From 65a1f1e9c98b4e56bdb92b7be284cadc9528f772 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Wed, 11 Dec 2024 04:52:22 -0700 Subject: [PATCH 01/28] chore: get_payload_works --- crates/pop-contracts/src/up.rs | 107 +++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs index 8ae38e40..cf3e9151 100644 --- a/crates/pop-contracts/src/up.rs +++ b/crates/pop-contracts/src/up.rs @@ -110,6 +110,7 @@ pub async fn set_up_upload( let upload_exec: UploadExec = UploadCommandBuilder::new(extrinsic_opts).done().await?; + Ok(upload_exec) } @@ -416,6 +417,12 @@ mod tests { run_contracts_node, }; use anyhow::Result; + use reqwest::get; + use std::{env, fs, process::Command}; + use subxt::{ + config::{substrate::BlakeTwo256, Hasher}, + utils::to_hex, + }; use pop_common::{find_free_port, set_executable_permission}; use std::{env, process::Command, time::Duration}; use tokio::time::sleep; @@ -471,6 +478,36 @@ mod tests { Ok(()) } + #[tokio::test] + async fn get_payload_works() -> Result<()> { + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + let up_opts = UpOpts { + path: Some(temp_dir.path().join("testing")), + constructor: "new".to_string(), + args: ["false".to_string()].to_vec(), + value: "1000".to_string(), + gas_limit: None, + proof_size: None, + salt: None, + url: Url::parse(CONTRACTS_NETWORK_URL)?, + suri: "//Alice".to_string(), + }; + let contract_code = get_contract_code(up_opts.path.as_ref()).await?; + let call_data = get_upload_payload(contract_code, CONTRACTS_NETWORK_URL).await?; + let payload_hash = BlakeTwo256::hash(&call_data).to_string(); + // We know that for the above opts the payload hash should be: + // 0x4e4ff6ad411346d4cd6cf9bcc9101360e4657f776b0af3b46bfe780b0413e819 + assert!(payload_hash.starts_with("0x4e4f")); + assert!(payload_hash.ends_with("e819")); + Ok(()) + } + #[tokio::test] async fn dry_run_gas_estimate_instantiate_works() -> Result<()> { let temp_dir = new_environment("testing")?; @@ -625,4 +662,74 @@ mod tests { Ok(()) } + + #[tokio::test] + async fn get_instantiate_payload_works() -> Result<()> { + let random_port = find_free_port(); + let localhost_url = format!("ws://127.0.0.1:{}", random_port); + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("./tests/files/testing.contract"), + current_dir.join("./tests/files/testing.json"), + )?; + let cache = temp_dir.path().join(""); + + let binary = contracts_node_generator(cache.clone(), None).await?; + binary.source(false, &(), true).await?; + let process = run_contracts_node(binary.path(), None, random_port).await?; + + let upload_exec = set_up_upload(UpOpts { + path: Some(temp_dir.path().join("testing")), + constructor: "new".to_string(), + args: [].to_vec(), + value: "1000".to_string(), + gas_limit: None, + proof_size: None, + salt: None, + url: Url::parse(&localhost_url)?, + suri: "//Alice".to_string(), + }) + .await?; + + // Only upload a Smart Contract + let upload_result = upload_smart_contract(&upload_exec).await?; + assert!(!upload_result.starts_with("0x0x")); + assert!(upload_result.starts_with("0x")); + //Error when Smart Contract has been already uploaded + assert!(matches!( + upload_smart_contract(&upload_exec).await, + Err(Error::UploadContractError(..)) + )); + + // Instantiate a Smart Contract + let instantiate_exec = set_up_deployment(UpOpts { + path: Some(temp_dir.path().join("testing")), + constructor: "new".to_string(), + args: ["false".to_string()].to_vec(), + value: "0".to_string(), + gas_limit: None, + proof_size: None, + salt: Some(Bytes::from(vec![0x00])), + url: Url::parse(&localhost_url)?, + suri: "//Alice".to_string(), + }) + .await?; + // First gas estimation + let weight = dry_run_gas_estimate_instantiate(&instantiate_exec).await?; + assert!(weight.ref_time() > 0); + assert!(weight.proof_size() > 0); + + let call_data = get_instantiate_payload(instantiate_exec, weight).await?; + //println!("{:?}", to_hex(call_data)); + + //Stop the process contracts-node + Command::new("kill") + .args(["-s", "TERM", &process.id().to_string()]) + .spawn()? + .wait()?; + + Ok(()) + } } From de25ba32763315487c26ff98583e5f424764049c Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:45:45 +0100 Subject: [PATCH 02/28] test(wallet-integration): up contract get payload works --- crates/pop-contracts/Cargo.toml | 1 + crates/pop-contracts/src/up.rs | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/pop-contracts/Cargo.toml b/crates/pop-contracts/Cargo.toml index 94a2c6e7..5a8f8180 100644 --- a/crates/pop-contracts/Cargo.toml +++ b/crates/pop-contracts/Cargo.toml @@ -42,3 +42,4 @@ pop-common = { path = "../pop-common", version = "0.5.0" } dirs.workspace = true mockito.workspace = true tokio-test.workspace = true +hex.workspace = true diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs index cf3e9151..b5569bb1 100644 --- a/crates/pop-contracts/src/up.rs +++ b/crates/pop-contracts/src/up.rs @@ -417,14 +417,13 @@ mod tests { run_contracts_node, }; use anyhow::Result; - use reqwest::get; - use std::{env, fs, process::Command}; + use hex::FromHex; + use pop_common::{find_free_port, set_executable_permission}; + use std::{env, process::Command, time::Duration}; use subxt::{ config::{substrate::BlakeTwo256, Hasher}, - utils::to_hex, + utils::{to_hex, H256}, }; - use pop_common::{find_free_port, set_executable_permission}; - use std::{env, process::Command, time::Duration}; use tokio::time::sleep; use url::Url; @@ -500,11 +499,16 @@ mod tests { }; let contract_code = get_contract_code(up_opts.path.as_ref()).await?; let call_data = get_upload_payload(contract_code, CONTRACTS_NETWORK_URL).await?; - let payload_hash = BlakeTwo256::hash(&call_data).to_string(); + let payload_hash = BlakeTwo256::hash(&call_data); // We know that for the above opts the payload hash should be: - // 0x4e4ff6ad411346d4cd6cf9bcc9101360e4657f776b0af3b46bfe780b0413e819 - assert!(payload_hash.starts_with("0x4e4f")); - assert!(payload_hash.ends_with("e819")); + // 0x98c24584107b3a01d12e8e02c0bb634d15dc86123c44d186206813ede42f478d + let expected_hash: H256 = H256::from( + <[u8; 32]>::from_hex( + "98c24584107b3a01d12e8e02c0bb634d15dc86123c44d186206813ede42f478d", + ) + .expect("Invalid hex string"), + ); + assert_eq!(expected_hash, payload_hash); Ok(()) } From 2197dbf1791b7383ae9c65c47eb33c33663adf61 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 05:25:51 -0700 Subject: [PATCH 03/28] get paylaod from server works --- crates/pop-cli/src/commands/up/contract.rs | 175 ++++++++++++------ crates/pop-contracts/tests/files/testing.wasm | Bin 3710 -> 0 bytes 2 files changed, 119 insertions(+), 56 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index dfb95380..f1e24146 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -456,11 +456,31 @@ fn display_contract_info(spinner: &ProgressBar, address: String, code_hash: Opti #[cfg(test)] mod tests { use super::*; - use duct::cmd; - use std::fs::{self, File}; - use subxt::{client::OfflineClientT, utils::to_hex}; + use crate::wallet_integration::TransactionData; + use pop_common::{find_free_port, set_executable_permission}; + use pop_contracts::{contracts_node_generator, mock_build_process, new_environment}; + use std::{env, process::Command, time::Duration}; + use subxt::utils::to_hex; + use subxt_signer::sr25519::dev; + use tokio::time::sleep; use url::Url; + use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload}; + // This struct implements the [`Payload`] trait and is used to submit + // pre-encoded SCALE call data directly, without the dynamic construction of transactions. + struct CallData(Vec); + + impl Payload for CallData { + fn encode_call_data_to( + &self, + _: &subxt::Metadata, + out: &mut Vec, + ) -> Result<(), subxt::ext::subxt_core::Error> { + out.extend_from_slice(&self.0); + Ok(()) + } + } + fn default_up_contract_command() -> UpContractCommand { UpContractCommand { path: None, @@ -479,6 +499,53 @@ mod tests { } } + // This is a helper function to test the waller integration server. + // Given some preconfigured UpContractCommand, the command is executed and the payload is + // requested to the wallet integration http server. + // After retrievign the payload will sign and send it back to the server in order to kill the + // process. + async fn run_server_and_retrive_payload( + up_config: UpContractCommand, + ) -> anyhow::Result { + const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; + + let task_handle = tokio::spawn(up_config.clone().execute()); + + // Wait a moment for the server to start + sleep(Duration::from_millis(10000)).await; + + let pre_sign_payload = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) + .await + .expect("Failed to get payload") + .json::() + .await + .expect("Failed to parse payload"); + + // Submit signed payload to kill the process. + + let rpc_client = subxt::backend::rpc::RpcClient::from_url(up_config.url).await?; + let client = + subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + let signer = dev::alice(); + + let payload = CallData(pre_sign_payload.call_data()); + let ext_params = Params::new().build(); + let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; + + let _ = reqwest::Client::new() + .post(&format!("{}/submit", WALLET_INT_URI)) + .json(&to_hex(signed.encoded())) + .send() + .await + .expect("Failed to submit payload") + .text() + .await + .expect("Failed to parse JSON response"); + + Ok(pre_sign_payload) + } + #[test] fn conversion_up_contract_command_to_up_opts_works() -> anyhow::Result<()> { let command = default_up_contract_command(); @@ -500,70 +567,69 @@ mod tests { Ok(()) } - #[test] - fn has_contract_been_built_works() -> anyhow::Result<()> { - let temp_dir = tempfile::tempdir()?; - let path = temp_dir.path(); - - // Standard rust project - let name = "hello_world"; - cmd("cargo", ["new", name]).dir(&path).run()?; - let contract_path = path.join(name); - assert!(!has_contract_been_built(Some(&contract_path))); - - cmd("cargo", ["build"]).dir(&contract_path).run()?; - // Mock build directory - fs::create_dir(&contract_path.join("target/ink"))?; - assert!(!has_contract_been_built(Some(&path.join(name)))); - // Create a mocked .contract file inside the target directory - File::create(contract_path.join(format!("target/ink/{}.contract", name)))?; - assert!(has_contract_been_built(Some(&path.join(name)))); - Ok(()) - } - - // TODO: delete this test. - // This is a helper test for an actual running pop CLI. - // It can serve as the "frontend" to query the payload, sign it - // and submit back to the CLI. #[tokio::test] - async fn sign_call_data() -> anyhow::Result<()> { - use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload}; - // This struct implements the [`Payload`] trait and is used to submit - // pre-encoded SCALE call data directly, without the dynamic construction of transactions. - struct CallData(Vec); - - impl Payload for CallData { - fn encode_call_data_to( - &self, - _: &subxt::Metadata, - out: &mut Vec, - ) -> Result<(), subxt::ext::subxt_core::Error> { - out.extend_from_slice(&self.0); - Ok(()) - } - } + async fn get_upload_call_data_works() -> anyhow::Result<()> { + let random_port = find_free_port(); + let localhost_url = format!("ws://127.0.0.1:{}", random_port); + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("../pop-contracts/tests/files/testing.contract"), + current_dir.join("../pop-contracts/tests/files/testing.json"), + )?; + + const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; + + let up_contract_opts = UpContractCommand { + path: Some(temp_dir.path().join("testing")), + constructor: "new".to_string(), + args: vec![], + value: "0".to_string(), + gas_limit: None, + proof_size: None, + salt: None, + url: Url::parse(&localhost_url).expect("default url is valid"), + suri: "//Alice".to_string(), + dry_run: false, + upload_only: true, + skip_confirm: true, + use_wallet: true, + }; + + let task_handle = tokio::spawn(up_contract_opts.clone().execute()); + + // Wait a moment for the server to start + sleep(Duration::from_millis(10000)).await; + + // Calculate the expected call data based on the above command options. + let (expected_call_data, _) = match up_contract_opts.get_contract_data().await { + Ok(data) => data, + Err(e) => { + error(format!("An error occurred getting the call data: {e}"))?; + return Err(e); + }, + }; - use subxt_signer::sr25519::dev; - let payload = reqwest::get(&format!("{}/payload", "http://127.0.0.1:9090")) + let pre_sign_payload = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) .await .expect("Failed to get payload") .json::() .await .expect("Failed to parse payload"); - let url = "ws://localhost:9944"; - let rpc_client = subxt::backend::rpc::RpcClient::from_url(url).await?; + let rpc_client = subxt::backend::rpc::RpcClient::from_url(&up_contract_opts.url).await?; let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; let signer = dev::alice(); - let payload = CallData(payload.call_data()); + let payload = CallData(pre_sign_payload.call_data()); let ext_params = Params::new().build(); let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; - let response = reqwest::Client::new() - .post(&format!("{}/submit", "http://localhost:9090")) + let _ = reqwest::Client::new() + .post(&format!("{}/submit", WALLET_INT_URI)) .json(&to_hex(signed.encoded())) .send() .await @@ -572,12 +638,9 @@ mod tests { .await .expect("Failed to parse JSON response"); - Ok(()) - } + assert_eq!(expected_call_data, pre_sign_payload.call_data()); - #[tokio::test] - async fn get_upload_call_data_works() -> anyhow::Result<()> { - todo!() + Ok(()) } async fn get_instantiate_call_data_works() -> anyhow::Result<()> { diff --git a/crates/pop-contracts/tests/files/testing.wasm b/crates/pop-contracts/tests/files/testing.wasm index 430888669464e770d310139f29e8af1ebf865ba4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 GIT binary patch literal 0 HcmV?d00001 literal 3710 zcmb_fO>ZPu6}|7Hx~pC8aXC1S84uE{iiI30?T;bxhzSyYkzz9%A+uqL>>j(uxZBen zSNAv>DN1{y2#GCQ#0H5i8-4|1#S$U0VGA2J2nk5ed9T|aN+LFl<*Daa@1A?_x#!;3 zE#2WWB_isb)`1?6<-m-`V>LbyIj~r9$(_kxa-hOX?BcK*>ro>aR^8Lh?6?~34@YNp z_qY-h_BN7}!Sfd*k?`03Z2099*z2C|*VX7nJrEnM<}bRZFRJ}f-5m^{R&`w+iI{3| z(RfxpJF8!6rE~MfVxsU*8i-r0G*gWXMC-<)87qHzX|u8Tf4@rjxcoH9n)BKoc27^` zn~kID`RUn9xjXaUrN!69DQ9-iF>FHZX2 zoR>+jFisVgZwwAP!(-anj>tn1XT9p-t>%NKnKQ28l0E=GJ}*;j0$#bKS0;e_0Jce? zBG3=pKyzwU%a%rT-u4QF$cLE~1m_Y$*tietQQ7oM=S2&#HTzsOorQ5>r*N=#*0nAI zW@%k>RDTU}1F;kc(;+~lt6c+01Q;9BQP zJgAPeA85lN+6ms3h;YK42}eV!npF4877Hr2Db zo1sVqd%(S+6dZ{Xi7JaWKpB-nVI%>cE9+891aw{lswp5C9Yw7emt0J>a*ge>9z@cj zRuWVRe?gRi06;+FfnDStV+^_9(9IMFNhU!hR7ew%LA;<2+=j*qX$FO$nQk`~_=L+q zAOduHLf{NG0m>?slmMGb2c>}f|JhX3Rhh-6g7pXu2A)#HWkXXXE}J%0fQwC)UNu$n zwM}(J6awT7LDNqe+G~@s8|1L5Ch3J%Z)RTUu^yXk1foA^tGm!0l!j3SPS{I)U*7pqR3A@Pcalxt>?~of6Z>3xev%T=q~D zUvjcc{Quy}|HOVv&+6@9Ka1wd?*zH5>e(nGAUK)9ct_o%b`_XbX5B9xmfqyPQZ}u9mW9#KT5f2sSZ%Fn% zW{TMk3i+;ps?gQoUWcPBpZ`zT*BPgR(<~@br|~at$=d1nFE7;toq|O`fqOO#+PSp( zyxyi%)3U+GY#G=L+J#yWBq+iFN3VcH#*YSN?Ub`QB?f?K2SA{(00|3>VnD*09Z7|g ze%6NW(I`f;G$fxdX20fj0=74%Y$Mbyy%J7}U%_X9TE;Wxrm8s z#1kk+nYtwZ29+1i8^3f>pzVErftnbfUK9=XI1HD1_&8ufn;XBr3k&1_D)IR@*+p&s zsdj5jZwvxIw~Ji3_||S+w=L34^BKBWMM#(tK>}|Rw<9a~G=kPw{*OS3<_`fB?SBKz zw!x@Tv%GQrpVais&*&{4`m^8=Mr~}k>u`A=gv6H+{{rI2^$_oPM6!fwh2f?c)dEtJ zSoYN^M=Kl{q*S45A#$|is(!oFY$2>x7(Oz?t&yMx=qimL=!DZPo!Gse;VlgqpFk1T<3Se(<8FPCwND(&g%kyOvL!IYK=m*JP zmn?rRu%GO+T7t~3Wx7;Cb352U zVE2gGk%gcx0Q?1J5#YDif+qqbh$}I~I=K&n)}dPr8cqZo|G0B3lr{xdIV0 z)*uXc#b5}-hXn@X=lNp3FeRT=fVksU_Ni86)@J`w>pvb=^>Dp=T7C4S>JHY|4!ia7 z*?L_apA1L!%k^P>xPCG?syR+XyDr;P_-vt)C6{4}aP{8LU5f@f6>v*6N{%lIJ4w6a3!6@BhXB^KN}Idf6G` hi&oV+JUglmJKtH`THCxIVhI~`eF@)v`23K*zW{;(I0gU! From da6c43bee9bada8b46140387a06ec0a1bca5941f Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:32:18 +0100 Subject: [PATCH 04/28] test(wallet-integration): retrieve upload call data works --- crates/pop-cli/src/commands/up/contract.rs | 78 +++++++--------------- 1 file changed, 24 insertions(+), 54 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index f1e24146..155ab85d 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -457,19 +457,19 @@ fn display_contract_info(spinner: &ProgressBar, address: String, code_hash: Opti mod tests { use super::*; use crate::wallet_integration::TransactionData; - use pop_common::{find_free_port, set_executable_permission}; - use pop_contracts::{contracts_node_generator, mock_build_process, new_environment}; - use std::{env, process::Command, time::Duration}; - use subxt::utils::to_hex; + use pop_common::find_free_port; + use pop_contracts::{mock_build_process, new_environment}; + use std::{env, time::Duration}; + use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload, utils::to_hex}; use subxt_signer::sr25519::dev; use tokio::time::sleep; use url::Url; - use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload}; + const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; + // This struct implements the [`Payload`] trait and is used to submit // pre-encoded SCALE call data directly, without the dynamic construction of transactions. struct CallData(Vec); - impl Payload for CallData { fn encode_call_data_to( &self, @@ -499,21 +499,9 @@ mod tests { } } - // This is a helper function to test the waller integration server. - // Given some preconfigured UpContractCommand, the command is executed and the payload is - // requested to the wallet integration http server. - // After retrievign the payload will sign and send it back to the server in order to kill the - // process. - async fn run_server_and_retrive_payload( - up_config: UpContractCommand, - ) -> anyhow::Result { - const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - - let task_handle = tokio::spawn(up_config.clone().execute()); - - // Wait a moment for the server to start - sleep(Duration::from_millis(10000)).await; - + // This is a helper function to test the wallet integration server. + // Signs the given call data and sends the signed payload to the server. + async fn send_signed_payload(call_data: Vec, node_url: &str) -> anyhow::Result<()> { let pre_sign_payload = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) .await .expect("Failed to get payload") @@ -522,8 +510,7 @@ mod tests { .expect("Failed to parse payload"); // Submit signed payload to kill the process. - - let rpc_client = subxt::backend::rpc::RpcClient::from_url(up_config.url).await?; + let rpc_client = subxt::backend::rpc::RpcClient::from_url(node_url).await?; let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; @@ -543,7 +530,7 @@ mod tests { .await .expect("Failed to parse JSON response"); - Ok(pre_sign_payload) + Ok(()) } #[test] @@ -579,8 +566,6 @@ mod tests { current_dir.join("../pop-contracts/tests/files/testing.json"), )?; - const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - let up_contract_opts = UpContractCommand { path: Some(temp_dir.path().join("testing")), constructor: "new".to_string(), @@ -589,7 +574,8 @@ mod tests { gas_limit: None, proof_size: None, salt: None, - url: Url::parse(&localhost_url).expect("default url is valid"), + //url: Url::parse(&localhost_url).expect("given url is valid"), + url: Url::parse("ws://localhost:9944").expect("default url is valid"), suri: "//Alice".to_string(), dry_run: false, upload_only: true, @@ -602,6 +588,14 @@ mod tests { // Wait a moment for the server to start sleep(Duration::from_millis(10000)).await; + // Request payload from server. + let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) + .await + .expect("Failed to get payload") + .json::() + .await + .expect("Failed to parse payload"); + // Calculate the expected call data based on the above command options. let (expected_call_data, _) = match up_contract_opts.get_contract_data().await { Ok(data) => data, @@ -611,34 +605,10 @@ mod tests { }, }; - let pre_sign_payload = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) - .await - .expect("Failed to get payload") - .json::() - .await - .expect("Failed to parse payload"); - - let rpc_client = subxt::backend::rpc::RpcClient::from_url(&up_contract_opts.url).await?; - let client = - subxt::OnlineClient::::from_rpc_client(rpc_client).await?; - - let signer = dev::alice(); - - let payload = CallData(pre_sign_payload.call_data()); - let ext_params = Params::new().build(); - let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; - - let _ = reqwest::Client::new() - .post(&format!("{}/submit", WALLET_INT_URI)) - .json(&to_hex(signed.encoded())) - .send() - .await - .expect("Failed to submit payload") - .text() - .await - .expect("Failed to parse JSON response"); + assert_eq!(expected_call_data, response.call_data()); - assert_eq!(expected_call_data, pre_sign_payload.call_data()); + // Send signed payload to kill server. + send_signed_payload(response.call_data(), &up_contract_opts.url.to_string()).await?; Ok(()) } From b6747ad0a0641a4e3ce19916a29a55f91c7a47e5 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:37:41 +0100 Subject: [PATCH 05/28] test(wallet-integration): retrieve instantiate call data works --- crates/pop-cli/src/commands/up/contract.rs | 57 +++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 155ab85d..558db053 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -613,8 +613,63 @@ mod tests { Ok(()) } + #[tokio::test] async fn get_instantiate_call_data_works() -> anyhow::Result<()> { - todo!() + let random_port = find_free_port(); + let localhost_url = format!("ws://127.0.0.1:{}", random_port); + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("../pop-contracts/tests/files/testing.contract"), + current_dir.join("../pop-contracts/tests/files/testing.json"), + )?; + + let up_contract_opts = UpContractCommand { + path: Some(temp_dir.path().join("testing")), + constructor: "new".to_string(), + args: vec!["false".to_string()], + value: "0".to_string(), + gas_limit: None, + proof_size: None, + salt: None, + //url: Url::parse(&localhost_url).expect("given url is valid"), + url: Url::parse("ws://localhost:9944").expect("default url is valid"), + suri: "//Alice".to_string(), + dry_run: false, + upload_only: false, + skip_confirm: true, + use_wallet: true, + }; + + let task_handle = tokio::spawn(up_contract_opts.clone().execute()); + + // Wait a moment for the server to start + sleep(Duration::from_millis(10000)).await; + + // Request payload from server. + let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) + .await + .expect("Failed to get payload") + .json::() + .await + .expect("Failed to parse payload"); + + // Calculate the expected call data based on the above command options. + let (expected_call_data, _) = match up_contract_opts.get_contract_data().await { + Ok(data) => data, + Err(e) => { + error(format!("An error occurred getting the call data: {e}"))?; + return Err(e); + }, + }; + + assert_eq!(expected_call_data, response.call_data()); + + // Send signed payload to kill server. + send_signed_payload(response.call_data(), &up_contract_opts.url.to_string()).await?; + + Ok(()) } async fn wait_for_signature_works() -> anyhow::Result<()> { From 60978e95bd944e810c1aceb6e0e58bab8350cfbb Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:45:05 +0100 Subject: [PATCH 06/28] style: better comments --- crates/pop-cli/src/commands/up/contract.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 558db053..59f78f14 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -583,8 +583,8 @@ mod tests { use_wallet: true, }; + // Execute the command. let task_handle = tokio::spawn(up_contract_opts.clone().execute()); - // Wait a moment for the server to start sleep(Duration::from_millis(10000)).await; @@ -642,8 +642,8 @@ mod tests { use_wallet: true, }; + // Execute the command. let task_handle = tokio::spawn(up_contract_opts.clone().execute()); - // Wait a moment for the server to start sleep(Duration::from_millis(10000)).await; From 4af7a70cd14cf44f46ae99678f40b26540c7a121 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:37:03 -0700 Subject: [PATCH 07/28] test: try higher wait times in CI --- Cargo.lock | 11 +-- crates/pop-cli/src/commands/up/contract.rs | 5 +- crates/pop-cli/src/common/wallet.rs | 99 ++++++++++++++++++++++ 3 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 crates/pop-cli/src/common/wallet.rs diff --git a/Cargo.lock b/Cargo.lock index 9887bd52..f332d5cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -2004,7 +2004,7 @@ checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08" [[package]] name = "contract-build" version = "5.0.1" -source = "git+https://github.com/use-ink/cargo-contract?branch=peter/chore-make-types-pub#7c8fc481912d7a6f416a0f72e37840123064f90d" +source = "git+https://github.com/use-ink/cargo-contract?branch=peter%2Fchore-make-types-pub#7c8fc481912d7a6f416a0f72e37840123064f90d" dependencies = [ "anyhow", "blake2", @@ -2044,7 +2044,7 @@ dependencies = [ [[package]] name = "contract-extrinsics" version = "5.0.1" -source = "git+https://github.com/use-ink/cargo-contract?branch=peter/chore-make-types-pub#7c8fc481912d7a6f416a0f72e37840123064f90d" +source = "git+https://github.com/use-ink/cargo-contract?branch=peter%2Fchore-make-types-pub#7c8fc481912d7a6f416a0f72e37840123064f90d" dependencies = [ "anyhow", "blake2", @@ -2076,7 +2076,7 @@ dependencies = [ [[package]] name = "contract-metadata" version = "5.0.1" -source = "git+https://github.com/use-ink/cargo-contract?branch=peter/chore-make-types-pub#7c8fc481912d7a6f416a0f72e37840123064f90d" +source = "git+https://github.com/use-ink/cargo-contract?branch=peter%2Fchore-make-types-pub#7c8fc481912d7a6f416a0f72e37840123064f90d" dependencies = [ "anyhow", "impl-serde 0.5.0", @@ -2089,7 +2089,7 @@ dependencies = [ [[package]] name = "contract-transcode" version = "5.0.1" -source = "git+https://github.com/use-ink/cargo-contract?branch=peter/chore-make-types-pub#7c8fc481912d7a6f416a0f72e37840123064f90d" +source = "git+https://github.com/use-ink/cargo-contract?branch=peter%2Fchore-make-types-pub#7c8fc481912d7a6f416a0f72e37840123064f90d" dependencies = [ "anyhow", "base58", @@ -9387,6 +9387,7 @@ dependencies = [ "duct", "flate2", "heck 0.5.0", + "hex", "ink_env", "mockito", "pop-common", diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 59f78f14..6d16e54a 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -466,6 +466,7 @@ mod tests { use url::Url; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; + const WAIT_TIME: u64 = 20000; // This struct implements the [`Payload`] trait and is used to submit // pre-encoded SCALE call data directly, without the dynamic construction of transactions. @@ -586,7 +587,7 @@ mod tests { // Execute the command. let task_handle = tokio::spawn(up_contract_opts.clone().execute()); // Wait a moment for the server to start - sleep(Duration::from_millis(10000)).await; + sleep(Duration::from_millis(WAIT_TIME)).await; // Request payload from server. let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) @@ -645,7 +646,7 @@ mod tests { // Execute the command. let task_handle = tokio::spawn(up_contract_opts.clone().execute()); // Wait a moment for the server to start - sleep(Duration::from_millis(10000)).await; + sleep(Duration::from_millis(WAIT_TIME)).await; // Request payload from server. let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs new file mode 100644 index 00000000..6d18460c --- /dev/null +++ b/crates/pop-cli/src/common/wallet.rs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-3.0 + +use crate::wallet_integration::{FrontendFromDir, TransactionData, WalletIntegrationManager}; +use cliclack::log; +use sp_core::bytes::to_hex; +use std::path::PathBuf; + +pub async fn wait_for_signature(call_data: Vec, url: String) -> anyhow::Result> { + // TODO: to be addressed in future PR. Should not use FromDir (or local path). + let ui = FrontendFromDir::new(PathBuf::from( + "/Users/alexbean/Documents/react-teleport-example/dist", + )); + + let transaction_data = TransactionData::new(url, call_data); + let call_data_bytes = to_hex(&transaction_data.call_data(), false); + println!("transaction_data: {:?}", call_data_bytes); + // starts server + let mut wallet = WalletIntegrationManager::new(ui, transaction_data); + log::step(format!("Wallet signing portal started at http://{}", wallet.rpc_url))?; + + log::step("Waiting for signature... Press Ctrl+C to terminate early.")?; + loop { + // Display error, if any. + if let Some(error) = wallet.take_error().await { + log::error(format!("Signing portal error: {error}"))?; + } + + let state = wallet.state.lock().await; + // If the payload is submitted we terminate the frontend. + if !wallet.is_running() || state.signed_payload.is_some() { + wallet.task_handle.await??; + break; + } + } + + let signed_payload = wallet.state.lock().await.signed_payload.clone(); + Ok(signed_payload) +} + +#[cfg(test)] +mod tests { + use super::*; + use subxt::utils::to_hex; + + // TODO: delete this test. + // This is a helper test for an actual running pop CLI. + // It can serve as the "frontend" to query the payload, sign it + // and submit back to the CLI. + #[ignore] + #[tokio::test] + async fn sign_call_data() -> anyhow::Result<()> { + use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload}; + // This struct implements the [`Payload`] trait and is used to submit + // pre-encoded SCALE call data directly, without the dynamic construction of transactions. + struct CallData(Vec); + + impl Payload for CallData { + fn encode_call_data_to( + &self, + _: &subxt::Metadata, + out: &mut Vec, + ) -> Result<(), subxt::ext::subxt_core::Error> { + out.extend_from_slice(&self.0); + Ok(()) + } + } + + use subxt_signer::sr25519::dev; + let payload = reqwest::get(&format!("{}/payload", "http://127.0.0.1:9090")) + .await + .expect("Failed to get payload") + .json::() + .await + .expect("Failed to parse payload"); + + let url = "ws://localhost:9944"; + let rpc_client = subxt::backend::rpc::RpcClient::from_url(url).await?; + let client = + subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + let signer = dev::alice(); + + let payload = CallData(payload.call_data()); + let ext_params = Params::new().build(); + let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; + + let response = reqwest::Client::new() + .post(&format!("{}/submit", "http://localhost:9090")) + .json(&to_hex(signed.encoded())) + .send() + .await + .expect("Failed to submit payload") + .text() + .await + .expect("Failed to parse JSON response"); + + Ok(()) + } +} From ce2b86118f4d6ded4a8cee821e0476b973260f63 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:27:08 -0700 Subject: [PATCH 08/28] test(wallet-integration): bump sleep time --- crates/pop-cli/src/commands/up/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 6d16e54a..17868528 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -466,7 +466,7 @@ mod tests { use url::Url; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_TIME: u64 = 20000; + const WAIT_TIME: u64 = 50000; // This struct implements the [`Payload`] trait and is used to submit // pre-encoded SCALE call data directly, without the dynamic construction of transactions. From b245a30a53f173a008e4a2c3951343599b27d7d0 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:43:24 +0100 Subject: [PATCH 09/28] test(wallet-integration): even more sleep time --- crates/pop-cli/src/commands/up/contract.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 17868528..f70141b7 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -466,7 +466,7 @@ mod tests { use url::Url; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_TIME: u64 = 50000; + const WAIT_SECS: u64 = 100; // This struct implements the [`Payload`] trait and is used to submit // pre-encoded SCALE call data directly, without the dynamic construction of transactions. @@ -587,7 +587,7 @@ mod tests { // Execute the command. let task_handle = tokio::spawn(up_contract_opts.clone().execute()); // Wait a moment for the server to start - sleep(Duration::from_millis(WAIT_TIME)).await; + sleep(Duration::from_secs(WAIT_SECS)).await; // Request payload from server. let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) @@ -646,7 +646,7 @@ mod tests { // Execute the command. let task_handle = tokio::spawn(up_contract_opts.clone().execute()); // Wait a moment for the server to start - sleep(Duration::from_millis(WAIT_TIME)).await; + sleep(Duration::from_secs(WAIT_SECS)).await; // Request payload from server. let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) From 3f9ad4bd0779d747eeba0e60e47d5426e0069b5f Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:59:26 +0100 Subject: [PATCH 10/28] test(wallet-integration): maybe a port problem ? --- crates/pop-cli/src/commands/up/contract.rs | 2 +- crates/pop-cli/src/wallet_integration.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index f70141b7..3ec7b973 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -465,7 +465,7 @@ mod tests { use tokio::time::sleep; use url::Url; - const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; + const WALLET_INT_URI: &str = "http://127.0.0.1:65535"; const WAIT_SECS: u64 = 100; // This struct implements the [`Payload`] trait and is used to submit diff --git a/crates/pop-cli/src/wallet_integration.rs b/crates/pop-cli/src/wallet_integration.rs index 57b2924e..72218343 100644 --- a/crates/pop-cli/src/wallet_integration.rs +++ b/crates/pop-cli/src/wallet_integration.rs @@ -71,7 +71,7 @@ impl WalletIntegrationManager { /// A `WalletIntegrationManager` instance, with access to the state and task handle for the /// server. pub fn new(frontend: F, payload: TransactionData) -> Self { - Self::new_with_address(frontend, payload, "127.0.0.1:9090") + Self::new_with_address(frontend, payload, "127.0.0.1:65535") } /// Same as `new`, but allows specifying the address to bind to. From 88b445eb736a3d089cca0f94b63c898dbd305031 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:12:55 +0100 Subject: [PATCH 11/28] revert 0075e94 --- crates/pop-cli/src/commands/up/contract.rs | 2 +- crates/pop-cli/src/wallet_integration.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 3ec7b973..f70141b7 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -465,7 +465,7 @@ mod tests { use tokio::time::sleep; use url::Url; - const WALLET_INT_URI: &str = "http://127.0.0.1:65535"; + const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; const WAIT_SECS: u64 = 100; // This struct implements the [`Payload`] trait and is used to submit diff --git a/crates/pop-cli/src/wallet_integration.rs b/crates/pop-cli/src/wallet_integration.rs index 72218343..57b2924e 100644 --- a/crates/pop-cli/src/wallet_integration.rs +++ b/crates/pop-cli/src/wallet_integration.rs @@ -71,7 +71,7 @@ impl WalletIntegrationManager { /// A `WalletIntegrationManager` instance, with access to the state and task handle for the /// server. pub fn new(frontend: F, payload: TransactionData) -> Self { - Self::new_with_address(frontend, payload, "127.0.0.1:65535") + Self::new_with_address(frontend, payload, "127.0.0.1:9090") } /// Same as `new`, but allows specifying the address to bind to. From aedee3985b6ccde286a29c242e91e040a61e868d Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:32:06 +0100 Subject: [PATCH 12/28] test(wallet-integration): better unit tests --- crates/pop-cli/Cargo.toml | 2 + crates/pop-cli/src/commands/up/contract.rs | 178 ++++++++------------- 2 files changed, 67 insertions(+), 113 deletions(-) diff --git a/crates/pop-cli/Cargo.toml b/crates/pop-cli/Cargo.toml index 6d2d6ef4..f03faa82 100644 --- a/crates/pop-cli/Cargo.toml +++ b/crates/pop-cli/Cargo.toml @@ -53,9 +53,11 @@ tower-http = { workspace = true, features = ["fs", "cors"] } [dev-dependencies] assert_cmd.workspace = true +contract-extrinsics.workspace = true predicates.workspace = true subxt.workspace = true subxt-signer.workspace = true +sp-weights.workspace = true [features] default = ["contract", "parachain", "telemetry"] diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index f70141b7..db534ff5 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -456,32 +456,16 @@ fn display_contract_info(spinner: &ProgressBar, address: String, code_hash: Opti #[cfg(test)] mod tests { use super::*; - use crate::wallet_integration::TransactionData; - use pop_common::find_free_port; - use pop_contracts::{mock_build_process, new_environment}; - use std::{env, time::Duration}; - use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload, utils::to_hex}; - use subxt_signer::sr25519::dev; - use tokio::time::sleep; + use pop_common::{find_free_port, set_executable_permission}; + use pop_contracts::{contracts_node_generator, mock_build_process, new_environment}; + use std::{ + env, + process::{Child, Command}, + }; + use subxt::{tx::Payload, SubstrateConfig}; + use tempfile::TempDir; use url::Url; - const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_SECS: u64 = 100; - - // This struct implements the [`Payload`] trait and is used to submit - // pre-encoded SCALE call data directly, without the dynamic construction of transactions. - struct CallData(Vec); - impl Payload for CallData { - fn encode_call_data_to( - &self, - _: &subxt::Metadata, - out: &mut Vec, - ) -> Result<(), subxt::ext::subxt_core::Error> { - out.extend_from_slice(&self.0); - Ok(()) - } - } - fn default_up_contract_command() -> UpContractCommand { UpContractCommand { path: None, @@ -500,37 +484,25 @@ mod tests { } } - // This is a helper function to test the wallet integration server. - // Signs the given call data and sends the signed payload to the server. - async fn send_signed_payload(call_data: Vec, node_url: &str) -> anyhow::Result<()> { - let pre_sign_payload = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) - .await - .expect("Failed to get payload") - .json::() - .await - .expect("Failed to parse payload"); - - // Submit signed payload to kill the process. - let rpc_client = subxt::backend::rpc::RpcClient::from_url(node_url).await?; - let client = - subxt::OnlineClient::::from_rpc_client(rpc_client).await?; - - let signer = dev::alice(); - - let payload = CallData(pre_sign_payload.call_data()); - let ext_params = Params::new().build(); - let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; - - let _ = reqwest::Client::new() - .post(&format!("{}/submit", WALLET_INT_URI)) - .json(&to_hex(signed.encoded())) - .send() - .await - .expect("Failed to submit payload") - .text() - .await - .expect("Failed to parse JSON response"); + async fn start_test_environment() -> anyhow::Result<(Child, u16, TempDir)> { + let random_port = find_free_port(); + let temp_dir = new_environment("testing")?; + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.path().join("testing"), + current_dir.join("../pop-contracts/tests/files/testing.contract"), + current_dir.join("../pop-contracts/tests/files/testing.json"), + )?; + let cache = temp_dir.path().join(""); + let binary = contracts_node_generator(cache.clone(), None).await?; + binary.source(false, &(), true).await?; + set_executable_permission(binary.path())?; + let process = run_contracts_node(binary.path(), None, random_port).await?; + Ok((process, random_port, temp_dir)) + } + fn stop_test_environment(id: &str) -> anyhow::Result<()> { + Command::new("kill").args(["-s", "TERM", id]).spawn()?.wait()?; Ok(()) } @@ -557,15 +529,8 @@ mod tests { #[tokio::test] async fn get_upload_call_data_works() -> anyhow::Result<()> { - let random_port = find_free_port(); - let localhost_url = format!("ws://127.0.0.1:{}", random_port); - let temp_dir = new_environment("testing")?; - let current_dir = env::current_dir().expect("Failed to get current directory"); - mock_build_process( - temp_dir.path().join("testing"), - current_dir.join("../pop-contracts/tests/files/testing.contract"), - current_dir.join("../pop-contracts/tests/files/testing.json"), - )?; + let (contracts_node_process, port, temp_dir) = start_test_environment().await?; + let localhost_url = format!("ws://127.0.0.1:{}", port); let up_contract_opts = UpContractCommand { path: Some(temp_dir.path().join("testing")), @@ -575,8 +540,7 @@ mod tests { gas_limit: None, proof_size: None, salt: None, - //url: Url::parse(&localhost_url).expect("given url is valid"), - url: Url::parse("ws://localhost:9944").expect("default url is valid"), + url: Url::parse(&localhost_url).expect("given url is valid"), suri: "//Alice".to_string(), dry_run: false, upload_only: true, @@ -584,21 +548,11 @@ mod tests { use_wallet: true, }; - // Execute the command. - let task_handle = tokio::spawn(up_contract_opts.clone().execute()); - // Wait a moment for the server to start - sleep(Duration::from_secs(WAIT_SECS)).await; - - // Request payload from server. - let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) - .await - .expect("Failed to get payload") - .json::() - .await - .expect("Failed to parse payload"); - - // Calculate the expected call data based on the above command options. - let (expected_call_data, _) = match up_contract_opts.get_contract_data().await { + let rpc_client = subxt::backend::rpc::RpcClient::from_url(&up_contract_opts.url).await?; + let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + // Retrieve call data based on the above command options. + let (retrieved_call_data, _) = match up_contract_opts.get_contract_data().await { Ok(data) => data, Err(e) => { error(format!("An error occurred getting the call data: {e}"))?; @@ -606,25 +560,31 @@ mod tests { }, }; - assert_eq!(expected_call_data, response.call_data()); + // Craft encoded call data for an upload code call. + let contract_code = get_contract_code(up_contract_opts.path.as_ref()).await?; + let storage_deposit_limit: Option = None; + let upload_code = contract_extrinsics::extrinsic_calls::UploadCode::new( + contract_code, + storage_deposit_limit, + contract_extrinsics::upload::Determinism::Enforced, + ); + let expected_call_data = upload_code.build(); + let mut encoded_expected_call_data = Vec::::new(); + expected_call_data + .encode_call_data_to(&client.metadata(), &mut encoded_expected_call_data)?; - // Send signed payload to kill server. - send_signed_payload(response.call_data(), &up_contract_opts.url.to_string()).await?; + // Retrieved call data and calculated match. + assert_eq!(retrieved_call_data, encoded_expected_call_data); + // Stop running contracts-node + stop_test_environment(&contracts_node_process.id().to_string())?; Ok(()) } #[tokio::test] async fn get_instantiate_call_data_works() -> anyhow::Result<()> { - let random_port = find_free_port(); - let localhost_url = format!("ws://127.0.0.1:{}", random_port); - let temp_dir = new_environment("testing")?; - let current_dir = env::current_dir().expect("Failed to get current directory"); - mock_build_process( - temp_dir.path().join("testing"), - current_dir.join("../pop-contracts/tests/files/testing.contract"), - current_dir.join("../pop-contracts/tests/files/testing.json"), - )?; + let (contracts_node_process, port, temp_dir) = start_test_environment().await?; + let localhost_url = format!("ws://127.0.0.1:{}", port); let up_contract_opts = UpContractCommand { path: Some(temp_dir.path().join("testing")), @@ -634,8 +594,7 @@ mod tests { gas_limit: None, proof_size: None, salt: None, - //url: Url::parse(&localhost_url).expect("given url is valid"), - url: Url::parse("ws://localhost:9944").expect("default url is valid"), + url: Url::parse(&localhost_url).expect("given url is valid"), suri: "//Alice".to_string(), dry_run: false, upload_only: false, @@ -643,21 +602,11 @@ mod tests { use_wallet: true, }; - // Execute the command. - let task_handle = tokio::spawn(up_contract_opts.clone().execute()); - // Wait a moment for the server to start - sleep(Duration::from_secs(WAIT_SECS)).await; - - // Request payload from server. - let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) - .await - .expect("Failed to get payload") - .json::() - .await - .expect("Failed to parse payload"); - - // Calculate the expected call data based on the above command options. - let (expected_call_data, _) = match up_contract_opts.get_contract_data().await { + let rpc_client = subxt::backend::rpc::RpcClient::from_url(&up_contract_opts.url).await?; + let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + // Retrieve call data based on the above command options. + let (retrieved_call_data, _) = match up_contract_opts.get_contract_data().await { Ok(data) => data, Err(e) => { error(format!("An error occurred getting the call data: {e}"))?; @@ -665,14 +614,17 @@ mod tests { }, }; - assert_eq!(expected_call_data, response.call_data()); - - // Send signed payload to kill server. - send_signed_payload(response.call_data(), &up_contract_opts.url.to_string()).await?; + // println!("retrieved_call_data: {:?}", retrieved_call_data); + // TODO: Can't use WasmCode for crafting the instantiation call data. + // Can't use `get_contract_code`. + // assert_eq!(retrieved_call_data, encoded_expected_call_data); + // Stop running contracts-node + stop_test_environment(&contracts_node_process.id().to_string())?; Ok(()) } + #[ignore] async fn wait_for_signature_works() -> anyhow::Result<()> { todo!() } From 02ff35801f08440561c4da7fc99828e09256b713 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 19:44:40 +0100 Subject: [PATCH 13/28] test(wallet-integration): wait for wallet signature --- crates/pop-cli/src/commands/up/contract.rs | 5 - crates/pop-cli/tests/contract.rs | 127 ++++++++++++++++++++- 2 files changed, 125 insertions(+), 7 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index db534ff5..13587229 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -623,9 +623,4 @@ mod tests { stop_test_environment(&contracts_node_process.id().to_string())?; Ok(()) } - - #[ignore] - async fn wait_for_signature_works() -> anyhow::Result<()> { - todo!() - } } diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index 77c8dba5..efad4c49 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -5,12 +5,52 @@ use assert_cmd::Command; use pop_common::{set_executable_permission, templates::Template}; use pop_contracts::{ contracts_node_generator, dry_run_gas_estimate_instantiate, instantiate_smart_contract, - run_contracts_node, set_up_deployment, Contract, UpOpts, + mock_build_process, run_contracts_node, set_up_deployment, Contract, UpOpts, +}; +use serde::{Deserialize, Serialize}; +use std::{ + env, + path::{Path, PathBuf}, + process::Command as Cmd, + time::Duration, }; -use std::{path::Path, process::Command as Cmd}; use strum::VariantArray; +use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload, utils::to_hex}; +use subxt_signer::sr25519::dev; +use tokio::time::sleep; use url::Url; +// This struct implements the [`Payload`] trait and is used to submit +// pre-encoded SCALE call data directly, without the dynamic construction of transactions. +struct CallData(Vec); +impl Payload for CallData { + fn encode_call_data_to( + &self, + _: &subxt::Metadata, + out: &mut Vec, + ) -> Result<(), subxt::ext::subxt_core::Error> { + out.extend_from_slice(&self.0); + Ok(()) + } +} + +// TransactionData has been copied from wallet_integration.rs +/// Transaction payload to be sent to frontend for signing. +#[derive(Serialize, Debug)] +#[cfg_attr(test, derive(Deserialize, Clone))] +pub struct TransactionData { + chain_rpc: String, + call_data: Vec, +} +impl TransactionData { + pub fn new(chain_rpc: String, call_data: Vec) -> Self { + Self { chain_rpc, call_data } + } + pub fn call_data(&self) -> Vec { + self.call_data.clone() + } +} + /// Test the contract lifecycle: new, build, up, call #[tokio::test] async fn contract_lifecycle() -> Result<()> { @@ -135,6 +175,89 @@ async fn contract_lifecycle() -> Result<()> { Ok(()) } +#[tokio::test] +async fn wait_for_wallet_signature_works() -> Result<()> { + const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9944"; + const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; + const WAIT_SECS: u64 = 10; + let temp = tempfile::tempdir()?; + let temp_dir = temp.path(); + //let temp_dir = Path::new("./"); //For testing locally + // pop new contract test_contract (default) + Command::cargo_bin("pop") + .unwrap() + .current_dir(&temp_dir) + .args(&["new", "contract", "test_contract"]) + .assert() + .success(); + assert!(temp_dir.join("test_contract").exists()); + + // Mock pop build --path ./test_contract --release + let current_dir = env::current_dir().expect("Failed to get current directory"); + mock_build_process( + temp_dir.join("test_contract"), + current_dir.join("../pop-contracts/tests/files/testing.contract"), + current_dir.join("../pop-contracts/tests/files/testing.json"), + )?; + // Verify that the directory target has been created + assert!(temp_dir.join("test_contract/target").exists()); + // Verify that all the artifacts has been generated + assert!(temp_dir.join("test_contract/target/ink/testing.contract").exists()); + assert!(temp_dir.join("test_contract/target/ink/testing.json").exists()); + + // Only upload the contract with --use-wallet + // pop up contract --upload-only --use-wallet + let current_dir: PathBuf = temp_dir.join("test_contract"); + Command::cargo_bin("pop") + .unwrap() + .current_dir(current_dir) + .args(&[ + "up", + "contract", + "--upload-only", + "--use-wallet", + "--skip-confirm", + "--suri", + "//Alice", + "--url", + DEFAULT_ENDPOINT, + ]) + .assert() + .success(); + // Wait a moment for node and server to be up. + sleep(Duration::from_secs(WAIT_SECS)).await; + + // Request payload from server. + let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)) + .await + .expect("Failed to get payload") + .json::() + .await + .expect("Failed to parse payload"); + + let rpc_client = subxt::backend::rpc::RpcClient::from_url(DEFAULT_ENDPOINT).await?; + let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; + + // Sign payload. + let signer = dev::alice(); + let payload = CallData(response.call_data()); + let ext_params = Params::new().build(); + let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; + + // Submit signed payload. This kills server. + let _ = reqwest::Client::new() + .post(&format!("{}/submit", WALLET_INT_URI)) + .json(&to_hex(signed.encoded())) + .send() + .await + .expect("Failed to submit payload") + .text() + .await + .expect("Failed to parse JSON response"); + + Ok(()) +} + fn generate_all_the_templates(temp_dir: &Path) -> Result<()> { for template in Contract::VARIANTS { let contract_name = format!("test_contract_{}", template).replace("-", "_"); From d64486feeb077ab33f9bb398bd808d464b48b29c Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 19:54:10 +0100 Subject: [PATCH 14/28] test(wallet-integration): assert on received payload --- crates/pop-cli/tests/contract.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index efad4c49..efeb4023 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -234,6 +234,8 @@ async fn wait_for_wallet_signature_works() -> Result<()> { .json::() .await .expect("Failed to parse payload"); + // We have received some payload. + assert!(!response.call_data().is_empty()); let rpc_client = subxt::backend::rpc::RpcClient::from_url(DEFAULT_ENDPOINT).await?; let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; From 63ad27c963e3db3ab06b695a1743333eac392d4e Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:09:48 +0100 Subject: [PATCH 15/28] test(wallet-integration): use tokio spawn --- crates/pop-cli/tests/contract.rs | 41 ++++++++++---------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index efeb4023..d5b9c264 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -5,15 +5,10 @@ use assert_cmd::Command; use pop_common::{set_executable_permission, templates::Template}; use pop_contracts::{ contracts_node_generator, dry_run_gas_estimate_instantiate, instantiate_smart_contract, - mock_build_process, run_contracts_node, set_up_deployment, Contract, UpOpts, + run_contracts_node, set_up_deployment, Contract, UpOpts, }; use serde::{Deserialize, Serialize}; -use std::{ - env, - path::{Path, PathBuf}, - process::Command as Cmd, - time::Duration, -}; +use std::{path::Path, process::Command as Cmd, time::Duration}; use strum::VariantArray; use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload, utils::to_hex}; use subxt_signer::sr25519::dev; @@ -179,7 +174,7 @@ async fn contract_lifecycle() -> Result<()> { async fn wait_for_wallet_signature_works() -> Result<()> { const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9944"; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_SECS: u64 = 10; + const WAIT_SECS: u64 = 120; let temp = tempfile::tempdir()?; let temp_dir = temp.path(); //let temp_dir = Path::new("./"); //For testing locally @@ -192,25 +187,9 @@ async fn wait_for_wallet_signature_works() -> Result<()> { .success(); assert!(temp_dir.join("test_contract").exists()); - // Mock pop build --path ./test_contract --release - let current_dir = env::current_dir().expect("Failed to get current directory"); - mock_build_process( - temp_dir.join("test_contract"), - current_dir.join("../pop-contracts/tests/files/testing.contract"), - current_dir.join("../pop-contracts/tests/files/testing.json"), - )?; - // Verify that the directory target has been created - assert!(temp_dir.join("test_contract/target").exists()); - // Verify that all the artifacts has been generated - assert!(temp_dir.join("test_contract/target/ink/testing.contract").exists()); - assert!(temp_dir.join("test_contract/target/ink/testing.json").exists()); - - // Only upload the contract with --use-wallet // pop up contract --upload-only --use-wallet - let current_dir: PathBuf = temp_dir.join("test_contract"); - Command::cargo_bin("pop") - .unwrap() - .current_dir(current_dir) + let _handler = tokio::process::Command::new("pop") + .current_dir(temp_dir.join("test_contract")) .args(&[ "up", "contract", @@ -222,8 +201,7 @@ async fn wait_for_wallet_signature_works() -> Result<()> { "--url", DEFAULT_ENDPOINT, ]) - .assert() - .success(); + .spawn()?; // Wait a moment for node and server to be up. sleep(Duration::from_secs(WAIT_SECS)).await; @@ -246,7 +224,7 @@ async fn wait_for_wallet_signature_works() -> Result<()> { let ext_params = Params::new().build(); let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; - // Submit signed payload. This kills server. + // Submit signed payload. This kills the wallet integration server. let _ = reqwest::Client::new() .post(&format!("{}/submit", WALLET_INT_URI)) .json(&to_hex(signed.encoded())) @@ -257,6 +235,11 @@ async fn wait_for_wallet_signature_works() -> Result<()> { .await .expect("Failed to parse JSON response"); + // Request payload from server after signed payload has been sent. + // Server should not be running! + let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)).await; + assert!(response.is_err()); + Ok(()) } From 99c25a8a73adfe75277e088701be51984ad8b40e Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:25:35 +0100 Subject: [PATCH 16/28] test(wallet-integration): add some waiting time --- crates/pop-cli/src/commands/up/contract.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 13587229..a87db927 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -462,8 +462,10 @@ mod tests { env, process::{Child, Command}, }; + use std::time::Duration; use subxt::{tx::Payload, SubstrateConfig}; use tempfile::TempDir; + use tokio::time::sleep; use url::Url; fn default_up_contract_command() -> UpContractCommand { @@ -531,6 +533,7 @@ mod tests { async fn get_upload_call_data_works() -> anyhow::Result<()> { let (contracts_node_process, port, temp_dir) = start_test_environment().await?; let localhost_url = format!("ws://127.0.0.1:{}", port); + sleep(Duration::from_secs(20)).await; let up_contract_opts = UpContractCommand { path: Some(temp_dir.path().join("testing")), @@ -559,6 +562,8 @@ mod tests { return Err(e); }, }; + // We have retrieved some payload. + assert!(!retrieved_call_data.is_empty()); // Craft encoded call data for an upload code call. let contract_code = get_contract_code(up_contract_opts.path.as_ref()).await?; @@ -581,10 +586,12 @@ mod tests { Ok(()) } + #[ignore] #[tokio::test] async fn get_instantiate_call_data_works() -> anyhow::Result<()> { let (contracts_node_process, port, temp_dir) = start_test_environment().await?; let localhost_url = format!("ws://127.0.0.1:{}", port); + sleep(Duration::from_secs(20)).await; let up_contract_opts = UpContractCommand { path: Some(temp_dir.path().join("testing")), @@ -613,6 +620,8 @@ mod tests { return Err(e); }, }; + // We have retrieved some payload. + assert!(!retrieved_call_data.is_empty()); // println!("retrieved_call_data: {:?}", retrieved_call_data); // TODO: Can't use WasmCode for crafting the instantiation call data. From 477554d721af7941c419c9bb7770acc5087c3f71 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 12:55:43 -0700 Subject: [PATCH 17/28] test(wallet-integration): use cargo run --- Cargo.toml | 7 +++++++ crates/pop-cli/tests/contract.rs | 12 +++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3effb7c2..141d54f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,6 +68,13 @@ walkdir = "2.5" indexmap = "2.2" toml_edit = { version = "0.22", features = ["serde"] } symlink = "0.1" +scale-info = { version = "2.11.3", default-features = false, features = [ + "derive", +] } +scale-value = { version = "0.16.2", default-features = false, features = [ + "from-string", + "parser-ss58", +] } serde_json = { version = "1.0", features = ["preserve_order"] } serde = { version = "1.0", features = ["derive"] } zombienet-sdk = "0.2.18" diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index d5b9c264..f6862878 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -174,7 +174,7 @@ async fn contract_lifecycle() -> Result<()> { async fn wait_for_wallet_signature_works() -> Result<()> { const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9944"; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_SECS: u64 = 120; + const WAIT_SECS: u64 = 360; let temp = tempfile::tempdir()?; let temp_dir = temp.path(); //let temp_dir = Path::new("./"); //For testing locally @@ -188,9 +188,13 @@ async fn wait_for_wallet_signature_works() -> Result<()> { assert!(temp_dir.join("test_contract").exists()); // pop up contract --upload-only --use-wallet - let _handler = tokio::process::Command::new("pop") - .current_dir(temp_dir.join("test_contract")) + // Using `cargo run --` as means for the CI to pass. + // Possibly there's room for improvement here. + let _handler = tokio::process::Command::new("cargo") + //.current_dir(temp_dir.join("test_contract")) .args(&[ + "run", + "--", "up", "contract", "--upload-only", @@ -200,6 +204,8 @@ async fn wait_for_wallet_signature_works() -> Result<()> { "//Alice", "--url", DEFAULT_ENDPOINT, + "-p", + temp_dir.join("test_contract").to_str().expect("to_str"), ]) .spawn()?; // Wait a moment for node and server to be up. From c3d1eaf1a1ef0742c3b33f7f034f594eab2dfe56 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:57:10 +0100 Subject: [PATCH 18/28] style: nightly fmt --- crates/pop-cli/src/commands/up/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index a87db927..02b34a00 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -461,8 +461,8 @@ mod tests { use std::{ env, process::{Child, Command}, + time::Duration, }; - use std::time::Duration; use subxt::{tx::Payload, SubstrateConfig}; use tempfile::TempDir; use tokio::time::sleep; From a473598c66f0f2759005b652fe1314d2707fbcdd Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:09:32 +0100 Subject: [PATCH 19/28] test(wallet-integration): 500s sleep time --- crates/pop-cli/tests/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index f6862878..bd0565a8 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -174,7 +174,7 @@ async fn contract_lifecycle() -> Result<()> { async fn wait_for_wallet_signature_works() -> Result<()> { const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9944"; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_SECS: u64 = 360; + const WAIT_SECS: u64 = 500; let temp = tempfile::tempdir()?; let temp_dir = temp.path(); //let temp_dir = Path::new("./"); //For testing locally From 2f41c3afc3655407e3b45802b258c4467f91c269 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:19:49 +0100 Subject: [PATCH 20/28] test(wallet-integration): ignore some tests --- crates/pop-cli/src/commands/up/contract.rs | 2 +- crates/pop-contracts/src/up.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 02b34a00..ae70b72f 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -586,8 +586,8 @@ mod tests { Ok(()) } - #[ignore] #[tokio::test] + #[ignore] async fn get_instantiate_call_data_works() -> anyhow::Result<()> { let (contracts_node_process, port, temp_dir) = start_test_environment().await?; let localhost_url = format!("ws://127.0.0.1:{}", port); diff --git a/crates/pop-contracts/src/up.rs b/crates/pop-contracts/src/up.rs index b5569bb1..29a08616 100644 --- a/crates/pop-contracts/src/up.rs +++ b/crates/pop-contracts/src/up.rs @@ -668,6 +668,7 @@ mod tests { } #[tokio::test] + #[ignore] async fn get_instantiate_payload_works() -> Result<()> { let random_port = find_free_port(); let localhost_url = format!("ws://127.0.0.1:{}", random_port); From 487434489de8730ee77d90fc1742f5f27b9f2286 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 21:41:51 +0100 Subject: [PATCH 21/28] test(wallet-integration): get instantiate call data --- crates/pop-cli/src/commands/up/contract.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index ae70b72f..4afbb0ec 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -587,7 +587,6 @@ mod tests { } #[tokio::test] - #[ignore] async fn get_instantiate_call_data_works() -> anyhow::Result<()> { let (contracts_node_process, port, temp_dir) = start_test_environment().await?; let localhost_url = format!("ws://127.0.0.1:{}", port); @@ -623,10 +622,14 @@ mod tests { // We have retrieved some payload. assert!(!retrieved_call_data.is_empty()); - // println!("retrieved_call_data: {:?}", retrieved_call_data); - // TODO: Can't use WasmCode for crafting the instantiation call data. - // Can't use `get_contract_code`. - // assert_eq!(retrieved_call_data, encoded_expected_call_data); + // Craft instantiate call data. + let weight = Weight::from_parts(0,0); + let expected_call_data = get_instantiate_payload( + set_up_deployment(up_contract_opts.into()).await?, + weight + ).await?; + // Retrieved call data matches the one crafted above. + assert_eq!(retrieved_call_data, expected_call_data); // Stop running contracts-node stop_test_environment(&contracts_node_process.id().to_string())?; From 82326112ebcca5253f979d022c14a2915ffe205d Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:25:01 +0100 Subject: [PATCH 22/28] test(wallet-integration): integration tests improvements --- crates/pop-cli/src/commands/up/contract.rs | 9 ++++----- crates/pop-cli/tests/contract.rs | 23 ++++++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index 4afbb0ec..e92a374a 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -623,11 +623,10 @@ mod tests { assert!(!retrieved_call_data.is_empty()); // Craft instantiate call data. - let weight = Weight::from_parts(0,0); - let expected_call_data = get_instantiate_payload( - set_up_deployment(up_contract_opts.into()).await?, - weight - ).await?; + let weight = Weight::from_parts(0, 0); + let expected_call_data = + get_instantiate_payload(set_up_deployment(up_contract_opts.into()).await?, weight) + .await?; // Retrieved call data matches the one crafted above. assert_eq!(retrieved_call_data, expected_call_data); diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index bd0565a8..d74a0c79 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -171,10 +171,11 @@ async fn contract_lifecycle() -> Result<()> { } #[tokio::test] -async fn wait_for_wallet_signature_works() -> Result<()> { - const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9944"; +async fn wait_for_wallet_signature() -> Result<()> { + const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9966"; + const DEFAULT_PORT: u16 = 9966; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_SECS: u64 = 500; + const WAIT_SECS: u64 = 320; let temp = tempfile::tempdir()?; let temp_dir = temp.path(); //let temp_dir = Path::new("./"); //For testing locally @@ -187,11 +188,16 @@ async fn wait_for_wallet_signature_works() -> Result<()> { .success(); assert!(temp_dir.join("test_contract").exists()); + let binary = contracts_node_generator(temp_dir.to_path_buf().clone(), None).await?; + binary.source(false, &(), true).await?; + set_executable_permission(binary.path())?; + let process = run_contracts_node(binary.path(), None, DEFAULT_PORT).await?; + sleep(Duration::from_secs(10)).await; + // pop up contract --upload-only --use-wallet // Using `cargo run --` as means for the CI to pass. // Possibly there's room for improvement here. let _handler = tokio::process::Command::new("cargo") - //.current_dir(temp_dir.join("test_contract")) .args(&[ "run", "--", @@ -200,8 +206,7 @@ async fn wait_for_wallet_signature_works() -> Result<()> { "--upload-only", "--use-wallet", "--skip-confirm", - "--suri", - "//Alice", + "--dry-run", "--url", DEFAULT_ENDPOINT, "-p", @@ -246,6 +251,12 @@ async fn wait_for_wallet_signature_works() -> Result<()> { let response = reqwest::get(&format!("{}/payload", WALLET_INT_URI)).await; assert!(response.is_err()); + // Stop the process contracts-node + Cmd::new("kill") + .args(["-s", "TERM", &process.id().to_string()]) + .spawn()? + .wait()?; + Ok(()) } From e8b0da3cd88e91e20e9300aaca7fe1d44fe052ca Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 22:42:36 +0100 Subject: [PATCH 23/28] test(wallet-integration): bump sleep time --- crates/pop-cli/tests/contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index d74a0c79..b8472e64 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -175,7 +175,7 @@ async fn wait_for_wallet_signature() -> Result<()> { const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9966"; const DEFAULT_PORT: u16 = 9966; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_SECS: u64 = 320; + const WAIT_SECS: u64 = 500; let temp = tempfile::tempdir()?; let temp_dir = temp.path(); //let temp_dir = Path::new("./"); //For testing locally From a15d1ffbba877587e911a6cd8834fd7ee0410569 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Fri, 13 Dec 2024 23:32:49 +0100 Subject: [PATCH 24/28] test(wallet-integration): merge integration tests --- crates/pop-cli/tests/contract.rs | 41 ++++---------------------------- 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index b8472e64..3befd888 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -50,6 +50,9 @@ impl TransactionData { #[tokio::test] async fn contract_lifecycle() -> Result<()> { const DEFAULT_PORT: u16 = 9944; + const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9944"; + const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; + const WAIT_SECS: u64 = 240; let temp = tempfile::tempdir().unwrap(); let temp_dir = temp.path(); //let temp_dir = Path::new("./"); //For testing locally @@ -161,43 +164,11 @@ async fn contract_lifecycle() -> Result<()> { .assert() .success(); - // Stop the process contracts-node - Cmd::new("kill") - .args(["-s", "TERM", &process.id().to_string()]) - .spawn()? - .wait()?; - - Ok(()) -} - -#[tokio::test] -async fn wait_for_wallet_signature() -> Result<()> { - const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9966"; - const DEFAULT_PORT: u16 = 9966; - const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; - const WAIT_SECS: u64 = 500; - let temp = tempfile::tempdir()?; - let temp_dir = temp.path(); - //let temp_dir = Path::new("./"); //For testing locally - // pop new contract test_contract (default) - Command::cargo_bin("pop") - .unwrap() - .current_dir(&temp_dir) - .args(&["new", "contract", "test_contract"]) - .assert() - .success(); - assert!(temp_dir.join("test_contract").exists()); - - let binary = contracts_node_generator(temp_dir.to_path_buf().clone(), None).await?; - binary.source(false, &(), true).await?; - set_executable_permission(binary.path())?; - let process = run_contracts_node(binary.path(), None, DEFAULT_PORT).await?; - sleep(Duration::from_secs(10)).await; - // pop up contract --upload-only --use-wallet + // Will run http server for wallet integration. // Using `cargo run --` as means for the CI to pass. // Possibly there's room for improvement here. - let _handler = tokio::process::Command::new("cargo") + let _ = tokio::process::Command::new("cargo") .args(&[ "run", "--", @@ -207,8 +178,6 @@ async fn wait_for_wallet_signature() -> Result<()> { "--use-wallet", "--skip-confirm", "--dry-run", - "--url", - DEFAULT_ENDPOINT, "-p", temp_dir.join("test_contract").to_str().expect("to_str"), ]) From 9cc6a195157c49f51a289bc59cb6ceb1fa1b5a94 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Sun, 15 Dec 2024 19:54:37 +0100 Subject: [PATCH 25/28] test(wallet-integration): remove sign_call_data test --- crates/pop-cli/src/common/wallet.rs | 61 ----------------------------- 1 file changed, 61 deletions(-) diff --git a/crates/pop-cli/src/common/wallet.rs b/crates/pop-cli/src/common/wallet.rs index 6d18460c..468c153f 100644 --- a/crates/pop-cli/src/common/wallet.rs +++ b/crates/pop-cli/src/common/wallet.rs @@ -36,64 +36,3 @@ pub async fn wait_for_signature(call_data: Vec, url: String) -> anyhow::Resu let signed_payload = wallet.state.lock().await.signed_payload.clone(); Ok(signed_payload) } - -#[cfg(test)] -mod tests { - use super::*; - use subxt::utils::to_hex; - - // TODO: delete this test. - // This is a helper test for an actual running pop CLI. - // It can serve as the "frontend" to query the payload, sign it - // and submit back to the CLI. - #[ignore] - #[tokio::test] - async fn sign_call_data() -> anyhow::Result<()> { - use subxt::{config::DefaultExtrinsicParamsBuilder as Params, tx::Payload}; - // This struct implements the [`Payload`] trait and is used to submit - // pre-encoded SCALE call data directly, without the dynamic construction of transactions. - struct CallData(Vec); - - impl Payload for CallData { - fn encode_call_data_to( - &self, - _: &subxt::Metadata, - out: &mut Vec, - ) -> Result<(), subxt::ext::subxt_core::Error> { - out.extend_from_slice(&self.0); - Ok(()) - } - } - - use subxt_signer::sr25519::dev; - let payload = reqwest::get(&format!("{}/payload", "http://127.0.0.1:9090")) - .await - .expect("Failed to get payload") - .json::() - .await - .expect("Failed to parse payload"); - - let url = "ws://localhost:9944"; - let rpc_client = subxt::backend::rpc::RpcClient::from_url(url).await?; - let client = - subxt::OnlineClient::::from_rpc_client(rpc_client).await?; - - let signer = dev::alice(); - - let payload = CallData(payload.call_data()); - let ext_params = Params::new().build(); - let signed = client.tx().create_signed(&payload, &signer, ext_params).await?; - - let response = reqwest::Client::new() - .post(&format!("{}/submit", "http://localhost:9090")) - .json(&to_hex(signed.encoded())) - .send() - .await - .expect("Failed to submit payload") - .text() - .await - .expect("Failed to parse JSON response"); - - Ok(()) - } -} From a3993cc93f65ecdc90badf4c926e042a057f81a7 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Sun, 15 Dec 2024 20:33:14 +0100 Subject: [PATCH 26/28] test(wallet-integration): use random free port --- crates/pop-cli/tests/contract.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/crates/pop-cli/tests/contract.rs b/crates/pop-cli/tests/contract.rs index 3befd888..6effb4d2 100644 --- a/crates/pop-cli/tests/contract.rs +++ b/crates/pop-cli/tests/contract.rs @@ -2,7 +2,7 @@ use anyhow::Result; use assert_cmd::Command; -use pop_common::{set_executable_permission, templates::Template}; +use pop_common::{find_free_port, set_executable_permission, templates::Template}; use pop_contracts::{ contracts_node_generator, dry_run_gas_estimate_instantiate, instantiate_smart_contract, run_contracts_node, set_up_deployment, Contract, UpOpts, @@ -49,10 +49,10 @@ impl TransactionData { /// Test the contract lifecycle: new, build, up, call #[tokio::test] async fn contract_lifecycle() -> Result<()> { - const DEFAULT_PORT: u16 = 9944; - const DEFAULT_ENDPOINT: &str = "ws://127.0.0.1:9944"; const WALLET_INT_URI: &str = "http://127.0.0.1:9090"; const WAIT_SECS: u64 = 240; + let endpoint_port = find_free_port(); + let default_endpoint: &str = &format!("ws://127.0.0.1:{}", endpoint_port); let temp = tempfile::tempdir().unwrap(); let temp_dir = temp.path(); //let temp_dir = Path::new("./"); //For testing locally @@ -84,14 +84,15 @@ async fn contract_lifecycle() -> Result<()> { let binary = contracts_node_generator(temp_dir.to_path_buf().clone(), None).await?; binary.source(false, &(), true).await?; set_executable_permission(binary.path())?; - let process = run_contracts_node(binary.path(), None, DEFAULT_PORT).await?; + let process = run_contracts_node(binary.path(), None, endpoint_port).await?; + sleep(Duration::from_secs(5)).await; // Only upload the contract // pop up contract --upload-only Command::cargo_bin("pop") .unwrap() .current_dir(&temp_dir.join("test_contract")) - .args(&["up", "contract", "--upload-only"]) + .args(&["up", "contract", "--upload-only", "--url", default_endpoint]) .assert() .success(); // Instantiate contract, only dry-run @@ -108,6 +109,8 @@ async fn contract_lifecycle() -> Result<()> { "--suri", "//Alice", "--dry-run", + "--url", + default_endpoint, ]) .assert() .success(); @@ -121,7 +124,7 @@ async fn contract_lifecycle() -> Result<()> { gas_limit: None, proof_size: None, salt: None, - url: Url::parse("ws://127.0.0.1:9944")?, + url: Url::parse(default_endpoint)?, suri: "//Alice".to_string(), }) .await?; @@ -141,6 +144,8 @@ async fn contract_lifecycle() -> Result<()> { "get", "--suri", "//Alice", + "--url", + default_endpoint, ]) .assert() .success(); @@ -160,6 +165,8 @@ async fn contract_lifecycle() -> Result<()> { "--suri", "//Alice", "-x", + "--url", + default_endpoint, ]) .assert() .success(); @@ -180,6 +187,8 @@ async fn contract_lifecycle() -> Result<()> { "--dry-run", "-p", temp_dir.join("test_contract").to_str().expect("to_str"), + "--url", + default_endpoint, ]) .spawn()?; // Wait a moment for node and server to be up. @@ -195,7 +204,7 @@ async fn contract_lifecycle() -> Result<()> { // We have received some payload. assert!(!response.call_data().is_empty()); - let rpc_client = subxt::backend::rpc::RpcClient::from_url(DEFAULT_ENDPOINT).await?; + let rpc_client = subxt::backend::rpc::RpcClient::from_url(default_endpoint).await?; let client = subxt::OnlineClient::::from_rpc_client(rpc_client).await?; // Sign payload. From 86b5a6197ffe8e072083446ca2a7ed779eb5faa3 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Andres <11448715+al3mart@users.noreply.github.com> Date: Sun, 15 Dec 2024 21:12:31 +0100 Subject: [PATCH 27/28] test(wallet-integration): define gas_limit & proof_size --- crates/pop-cli/src/commands/up/contract.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/pop-cli/src/commands/up/contract.rs b/crates/pop-cli/src/commands/up/contract.rs index e92a374a..03828e83 100644 --- a/crates/pop-cli/src/commands/up/contract.rs +++ b/crates/pop-cli/src/commands/up/contract.rs @@ -597,8 +597,8 @@ mod tests { constructor: "new".to_string(), args: vec!["false".to_string()], value: "0".to_string(), - gas_limit: None, - proof_size: None, + gas_limit: Some(200_000_000), + proof_size: Some(30_000), salt: None, url: Url::parse(&localhost_url).expect("given url is valid"), suri: "//Alice".to_string(), @@ -623,7 +623,7 @@ mod tests { assert!(!retrieved_call_data.is_empty()); // Craft instantiate call data. - let weight = Weight::from_parts(0, 0); + let weight = Weight::from_parts(200_000_000, 30_000); let expected_call_data = get_instantiate_payload(set_up_deployment(up_contract_opts.into()).await?, weight) .await?; From 0ac034d15c8f02c5cbeb6723c247df9ec907b63c Mon Sep 17 00:00:00 2001 From: Peter White Date: Sun, 15 Dec 2024 21:46:44 -0700 Subject: [PATCH 28/28] fix: merge issues --- Cargo.lock | 1 + Cargo.toml | 7 ------- crates/pop-cli/src/wallet_integration.rs | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f332d5cd..edd117df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9318,6 +9318,7 @@ dependencies = [ "clap", "cliclack", "console", + "contract-extrinsics", "dirs", "duct", "env_logger 0.11.5", diff --git a/Cargo.toml b/Cargo.toml index 141d54f3..3effb7c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,13 +68,6 @@ walkdir = "2.5" indexmap = "2.2" toml_edit = { version = "0.22", features = ["serde"] } symlink = "0.1" -scale-info = { version = "2.11.3", default-features = false, features = [ - "derive", -] } -scale-value = { version = "0.16.2", default-features = false, features = [ - "from-string", - "parser-ss58", -] } serde_json = { version = "1.0", features = ["preserve_order"] } serde = { version = "1.0", features = ["derive"] } zombienet-sdk = "0.2.18" diff --git a/crates/pop-cli/src/wallet_integration.rs b/crates/pop-cli/src/wallet_integration.rs index 57b2924e..b179f21c 100644 --- a/crates/pop-cli/src/wallet_integration.rs +++ b/crates/pop-cli/src/wallet_integration.rs @@ -360,7 +360,7 @@ mod tests { .await .expect("Failed to parse response"); - assert_eq!(response, json!({"status": "success"})); + assert_eq!(response, json!({"status": "success"}).to_string()); assert_eq!(wim.state.lock().await.signed_payload, Some("0xDEADBEEF".to_string())); assert_eq!(wim.is_running(), false);