Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 100 additions & 100 deletions crates/pop-cli/src/assets/index.html
Comment thread
AlexD10S marked this conversation as resolved.

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions crates/pop-cli/src/commands/call/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,8 +583,7 @@ impl CallContractCommand {
.get_contract_data(&call_exec, storage_deposit_limit)
.map_err(|err| anyhow!("An error occurred getting the call data: {err}"))?;

let maybe_payload =
request_signature(call_data, self.url()?.to_string()).await?.signed_payload;
let maybe_payload = request_signature(call_data, self.url()?.to_string()).await?;
if let Some(payload) = maybe_payload {
cli.success("Signed payload received.")?;
let spinner = spinner();
Expand Down
12 changes: 4 additions & 8 deletions crates/pop-cli/src/commands/up/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ impl UpContractCommand {
},
};

let maybe_signature_request = request_signature(call_data, url.to_string()).await?;
if let Some(payload) = maybe_signature_request.signed_payload {
let maybe_payload = request_signature(call_data, url.to_string()).await?;
if let Some(payload) = maybe_payload {
Cli.success("Signed payload received.")?;
let spinner = spinner();
spinner.start(
Expand Down Expand Up @@ -290,12 +290,8 @@ impl UpContractCommand {
// Check if the account is already mapped, and prompt the user to perform the
// mapping if it's required.
map_account(instantiate_exec.opts(), &mut Cli).await?;
let contract_info = match instantiate_contract_signed(
maybe_signature_request.contract_address,
url.as_str(),
payload,
)
.await
let contract_info = match instantiate_contract_signed(url.as_str(), payload)
.await
{
Err(e) => {
spinner
Expand Down
14 changes: 5 additions & 9 deletions crates/pop-cli/src/common/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

use crate::{
cli::traits::{Cli, Confirm},
wallet_integration::{
FrontendFromString, SubmitRequest, TransactionData, WalletIntegrationManager,
},
wallet_integration::{FrontendFromString, TransactionData, WalletIntegrationManager},
};
use cliclack::{log, spinner};
#[cfg(feature = "chain")]
Expand All @@ -26,8 +24,8 @@ pub const USE_WALLET_PROMPT: &str = "Do you want to use your browser wallet to s
/// * `call_data` - The call data to be signed.
/// * `url` - Chain rpc.
/// # Returns
/// * The signed payload and the associated contract address, if provided by the wallet.
pub async fn request_signature(call_data: Vec<u8>, rpc: String) -> anyhow::Result<SubmitRequest> {
/// * The signed payload, if it exists.
pub async fn request_signature(call_data: Vec<u8>, rpc: String) -> anyhow::Result<Option<String>> {
let ui = FrontendFromString::new(include_str!("../assets/index.html").to_string());

let transaction_data = TransactionData::new(rpc, call_data);
Expand Down Expand Up @@ -59,9 +57,7 @@ pub async fn request_signature(call_data: Vec<u8>, rpc: String) -> anyhow::Resul
spinner.clear();

let signed_payload = wallet.state.lock().await.signed_payload.take();
let contract_address = wallet.state.lock().await.contract_address.take();

Ok(SubmitRequest { signed_payload, contract_address })
Ok(signed_payload)
}

/// Prompts the user to use the wallet for signing.
Expand Down Expand Up @@ -90,7 +86,7 @@ pub(crate) async fn submit_extrinsic(
call_data: Vec<u8>,
cli: &mut impl Cli,
) -> Result<ExtrinsicEvents<SubstrateConfig>> {
let maybe_payload = request_signature(call_data, url.to_string()).await?.signed_payload;
let maybe_payload = request_signature(call_data, url.to_string()).await?;
let payload = maybe_payload.ok_or_else(|| anyhow!("No signed payload received."))?;
cli.success("Signed payload received.")?;
let spinner = spinner();
Expand Down
43 changes: 11 additions & 32 deletions crates/pop-cli/src/wallet_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,30 +44,18 @@ impl TransactionData {

/// Shared state between routes. Serves two purposes:
/// - Maintains a channel to signal shutdown to the main app.
/// - Stores the signed payload and the contract address received from the wallet.
/// - Stores the signed payload received from the wallet.
#[derive(Default)]
pub struct StateHandler {
/// Channel to signal shutdown to the main app.
shutdown_tx: Option<oneshot::Sender<()>>,
/// Received from UI.
pub signed_payload: Option<String>,
/// Contract address received from UI.
pub contract_address: Option<String>,
/// Holds a single error message.
/// Only method for consuming error removes (takes) it from state.
error: Option<String>,
}

/// Payload submitted by the wallet after signing a transaction.
#[derive(serde::Deserialize, serde::Serialize, Debug)]
pub struct SubmitRequest {
/// Signed transaction returned from the wallet.
pub signed_payload: Option<String>,
/// Address of the deployed contract, included only when the transaction is a contract
/// deployment.
pub contract_address: Option<String>,
}

/// Manages the wallet integration for secure signing of transactions.
pub struct WalletIntegrationManager {
pub server_url: String,
Expand Down Expand Up @@ -115,7 +103,6 @@ impl WalletIntegrationManager {
let state = Arc::new(Mutex::new(StateHandler {
shutdown_tx: Some(tx),
signed_payload: None,
contract_address: None,
error: None,
}));

Expand Down Expand Up @@ -173,7 +160,7 @@ impl WalletIntegrationManager {
}

mod routes {
use super::{Arc, Mutex, StateHandler, SubmitRequest, TransactionData, terminate_helper};
use super::{Arc, Mutex, StateHandler, TransactionData, terminate_helper};
use anyhow::Error;
use axum::{
Json,
Expand Down Expand Up @@ -215,14 +202,13 @@ mod routes {
/// Will signal for shutdown on success.
pub(super) async fn submit_handler(
State(state): State<Arc<Mutex<StateHandler>>>,
Json(data): Json<SubmitRequest>,
Json(payload): Json<String>,
) -> Result<Json<serde_json::Value>, ApiError> {
// Signal shutdown.
let res = terminate_helper(&state).await;

let mut state_locked = state.lock().await;
state_locked.signed_payload = data.signed_payload;
state_locked.contract_address = data.contract_address;
state_locked.signed_payload = Some(payload);

res?;

Expand Down Expand Up @@ -301,7 +287,6 @@ impl Frontend for FrontendFromString {
#[cfg(test)]
mod tests {
use super::*;
use crate::wallet_integration::SubmitRequest;
use serde_json::json;

const TEST_HTML: &str = "<html><body>Hello, world!</body></html>";
Expand All @@ -324,7 +309,6 @@ mod tests {
assert!(wim.is_running());
assert!(wim.state.lock().await.shutdown_tx.is_some());
assert!(wim.state.lock().await.signed_payload.is_none());
assert!(wim.state.lock().await.contract_address.is_none());

// Terminate the server and make sure result is ok.
wim.terminate().await.expect("Termination should not fail.");
Expand All @@ -351,7 +335,6 @@ mod tests {
for mut server in servers.into_iter() {
assert!(server.state.lock().await.shutdown_tx.is_some());
assert!(server.state.lock().await.signed_payload.is_none());
assert!(server.state.lock().await.contract_address.is_none());
server.terminate().await.expect("Server termination should not fail");

let task_result = server.task_handle.await;
Expand Down Expand Up @@ -414,13 +397,9 @@ mod tests {
wait().await;

let addr = format!("http://{}", wim.server_url);
let request = SubmitRequest {
signed_payload: Some("0xDEADBEEF".to_string()),
contract_address: Some("0x1234567890abcdef".to_string()),
};
let response = reqwest::Client::new()
.post(format!("{addr}/submit"))
.json(&request)
.json(&"0xDEADBEEF")
.send()
.await
.expect("Failed to submit payload")
Expand All @@ -430,7 +409,6 @@ mod tests {

assert_eq!(response, json!({"status": "success"}).to_string());
assert_eq!(wim.state.lock().await.signed_payload, Some("0xDEADBEEF".to_string()));
assert_eq!(wim.state.lock().await.contract_address, Some("0x1234567890abcdef".to_string()));
assert!(!wim.is_running());

wim.terminate().await.expect("Termination should not fail");
Expand Down Expand Up @@ -573,12 +551,10 @@ mod tests {
assert_eq!(actual_payload.call_data, call_data_5mb);

let encoded_payload: String = call_data_5mb.iter().map(|b| format!("{:02x}", b)).collect();
let mut submit_request =
SubmitRequest { signed_payload: Some(encoded_payload), contract_address: None };
let client = reqwest::Client::new();
let response = client
.post(format!("{addr}/submit"))
.json(&submit_request)
.json(&encoded_payload)
.send()
.await
.expect("Failed to send large payload");
Expand All @@ -590,8 +566,11 @@ mod tests {
let call_data_15mb = vec![99u8; MAX_PAYLOAD_SIZE + 1];
let encoded_oversized_payload: String =
call_data_15mb.iter().map(|b| format!("{:02x}", b)).collect();
submit_request.signed_payload = Some(encoded_oversized_payload);
let response = client.post(format!("{addr}/submit")).json(&submit_request).send().await;
let response = client
.post(format!("{addr}/submit"))
.json(&encoded_oversized_payload)
.send()
.await;

assert!(
response.is_err() ||
Expand Down
5 changes: 2 additions & 3 deletions crates/pop-cli/tests/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,11 @@ async fn contract_lifecycle() -> Result<()> {
let ext_params = Params::new().build();
let signed = client.tx().create_signed(&payload, &signer, ext_params).await?;

let submit_request =
SubmitRequest { signed_payload: Some(to_hex(signed.encoded())), contract_address: None };
let signed_payload = Some(to_hex(signed.encoded()));
// Submit signed payload. This kills the wallet integration server.
let _ = reqwest::Client::new()
.post(format!("{}/submit", WALLET_INT_URI))
.json(&submit_request)
.json(&signed_payload)
.send()
.await
.expect("Failed to submit payload")
Expand Down
23 changes: 6 additions & 17 deletions crates/pop-contracts/src/up.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use contract_extrinsics::{
events::ContractInstantiated,
extrinsic_calls::{Instantiate, InstantiateWithCode},
};
use pop_common::{DefaultConfig, Keypair, account_id::parse_h160_account, create_signer};
use pop_common::{DefaultConfig, Keypair, create_signer};
use scale_info::scale::Encode;
use sp_core::bytes::{from_hex, to_hex};
use std::{
Expand Down Expand Up @@ -246,27 +246,16 @@ pub async fn upload_contract_signed(
/// * `url` - rpc for chain.
/// * `payload` - the signed payload to submit (encoded call data).
pub async fn instantiate_contract_signed(
maybe_contract_address: Option<String>,
url: &str,
payload: String,
) -> anyhow::Result<InstantiateExecResult<SubstrateConfig>> {
let events = submit_signed_payload(url, payload).await?;

let (code_hash, contract_address) = {
let contract_address = match maybe_contract_address {
Some(addr) => parse_h160_account(&addr)?,
None => {
let instantiated =
events.find_first::<ContractInstantiated>()?.ok_or_else(|| {
Error::InstantiateContractError(
"Failed to find Instantiated event".to_string(),
)
})?;
instantiated.contract
},
};
(None, contract_address)
};
let instantiated = events.find_first::<ContractInstantiated>()?.ok_or_else(|| {
Error::InstantiateContractError("Failed to find Instantiated event".to_string())
})?;
let contract_address = instantiated.contract;
let code_hash = None;

Ok(InstantiateExecResult { events, code_hash, contract_address })
}
Expand Down
Loading