Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add submitting txs to the cli #159

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
10 changes: 5 additions & 5 deletions apps/transfers/scripts/listen.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

ROOT=${ROOT:-$HOME}
ROOT=${ROOT:-$(git rev-parse --show-toplevel)}
DEFAULT_NODE="127.0.0.1:26657"
NODE_URL=${NODE_URL:-$DEFAULT_NODE}
# Use the QUARTZ_PORT environment variable if set, otherwise default to 11090
Expand Down Expand Up @@ -54,11 +54,11 @@ REPORT_SIG_FILE="/tmp/${USER}_datareportsig"
STATE=$($CMD query wasm contract-state raw $CONTRACT $(printf '%s' "state" | \
hexdump -ve '/1 "%02X"') -o json | jq -r .data | base64 -d)

cd "$ROOT/cycles-quartz/apps/transfers"
cd "$ROOT/apps/transfers"
export TRUSTED_HASH=$(cat trusted.hash)
export TRUSTED_HEIGHT=$(cat trusted.height)

cd $ROOT/cycles-quartz/utils/tm-prover
cd $ROOT/utils/tm-prover
export PROOF_FILE="light-client-proof.json"
if [ -f "$PROOF_FILE" ]; then
rm "$PROOF_FILE"
Expand All @@ -85,7 +85,7 @@ REPORT_SIG_FILE="/tmp/${USER}_datareportsig"
export REQUEST_MSG=$(jq --argjson msg "$ENCLAVE_REQUEST" '. + {msg: $msg}' <<< "$POP")
export PROTO_MSG=$(jq -nc --arg message "$REQUEST_MSG" '$ARGS.named')

cd $ROOT/cycles-quartz/apps/transfers/enclave
cd $ROOT/apps/transfers/enclave

echo "... executing transfer"
export ATTESTED_MSG=$(grpcurl -plaintext -import-path ./proto/ -proto transfers.proto \
Expand Down Expand Up @@ -135,7 +135,7 @@ REPORT_SIG_FILE="/tmp/${USER}_datareportsig"
--arg ephemeral_pubkey "$EPHEMERAL_PUBKEY" '$ARGS.named')
export REQUEST_MSG=$(jq -nc --arg message "$ENCLAVE_REQUEST" '$ARGS.named')

cd $ROOT/cycles-quartz/apps/transfers/enclave
cd $ROOT/apps/transfers/enclave

echo "... executing query balance"
ATTESTED_MSG=$(grpcurl -plaintext -import-path ./proto/ -proto transfers.proto \
Expand Down
60 changes: 58 additions & 2 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# quartz CLI

A CLI tool to manage Quartz applications. The `quartz` CLI tool is designed to streamline the development and deployment
process of Quartz applications.
The `quartz` CLI tool is designed to streamline the development and deployment process of Quartz applications.

It provides helpful information about each command and its options. To get a list of all available subcommands and their
descriptions, use the `--help` flag:
Expand Down Expand Up @@ -74,3 +73,60 @@ apps/transfers/
├── frontend/
└── README.md
```

## Intsructions to quickly setup the transfers app
You can use these instructions to run the transfers app. For your own app, you will need to adjust the env vars and paths as needed.

> Note - Run all commands from within the `/cli` folder

```bash
# setup env vars in ALL THREE terminals
export MOCK_SGX=true
export NODE_URL=143.244.186.205:26657
export CHAIN_ID=testing

#-------------------------------------------------------------------------------
# TERMINAL 1 - setup enclave

# build enclave
cargo run -- enclave build --manifest-path "../apps/transfers/enclave/Cargo.toml"

# start enclave
cargo run -- enclave start --app-dir "../apps/transfers" --chain-id $CHAIN_ID

#-------------------------------------------------------------------------------
# TERMINAL 2 - After enclave is setup, setup contracts

# build contracts
cargo run -- --mock-sgx contract build --manifest-path "../apps/transfers/contracts/Cargo.toml"

# deploy contracts
cargo run -- \
--mock-sgx \
contract deploy \
--wasm-bin-path "../apps/transfers/contracts/target/wasm32-unknown-unknown/release/transfers_contract.wasm" \
--init-msg '{"denom": "ucosm"}'

# retrieve contract addr from output and store in env
export CONTRACT=<YOUR_CONTRACT_ADDR>

# handshake
cargo run -- --mock-sgx handshake --app-dir "../apps/transfers/" --contract $CONTRACT

# listen - NOTE - still using bash instead of cli
bash ../apps/transfers/scripts/listen.sh $CONTRACT

#-------------------------------------------------------------------------------
# TERMINAL 3 - After contracts are setup, interact with them

export CONTRACT=<YOUR_CONTRACT_ADDR>

## example 1
cargo run -- contract tx --msg "\"deposit\"" --amount 1000ucosm --gas 200000 --contract $CONTRACT

## example 2
cargo run -- \
contract tx \
--msg "{\"query_request\": {\"emphemeral_pubkey\": \"$EPHEMERAL_PUBKEY\"}}" \
--contract $CONTRACT
```
26 changes: 26 additions & 0 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,12 @@ pub enum Command {

#[derive(Debug, Clone, Subcommand)]
pub enum ContractCommand {
// Build the Quartz app's contract
Build {
#[clap(long)]
manifest_path: PathBuf,
},
// Deploy a contract to the chain
Deploy {
/// Json-formatted cosmwasm contract initialization message
#[clap(long, default_value = "{}")]
Expand All @@ -111,6 +113,30 @@ pub enum ContractCommand {
#[clap(long)]
wasm_bin_path: PathBuf,
},
/// Submit a transaction to the contract
Tx {
/// <host>:<port> to tendermint rpc interface for this chain
#[clap(long, default_value_t = default_node_url())]
node_url: String,
/// Contract account ID
#[arg(short, long, value_parser = wasmaddr_to_id)]
contract: AccountId,
/// The network chain ID
#[arg(long, default_value = "testing")]
chain_id: ChainId,
/// Gas to send with tx
#[arg(long, default_value = "900000000")]
gas: u64,
/// Name or address of private key with which to sign
#[arg(short, long, default_value = "admin")]
sender: String,
/// Method to call on the contract
#[arg(long)]
msg: String,
/// Amount of base coin to send with tx, i.e. "1000ucosm"
#[arg(long)]
amount: Option<String>,
},
}

#[derive(Debug, Clone, Subcommand)]
Expand Down
2 changes: 2 additions & 0 deletions cli/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod utils;
// commands
pub mod contract_build;
pub mod contract_deploy;
pub mod contract_tx;
pub mod enclave_build;
pub mod enclave_start;
pub mod handshake;
Expand All @@ -30,6 +31,7 @@ impl Handler for Request {
Request::Handshake(request) => request.handle(config).await,
Request::ContractBuild(request) => request.handle(config).await,
Request::ContractDeploy(request) => request.handle(config).await,
Request::ContractTx(request) => request.handle(config).await,
Request::EnclaveBuild(request) => request.handle(config).await,
Request::EnclaveStart(request) => request.handle(config).await,
}
Expand Down
71 changes: 71 additions & 0 deletions cli/src/handler/contract_tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use async_trait::async_trait;
use cycles_sync::wasmd_client::{CliWasmdClient, WasmdClient};
use reqwest::Url;
use serde_json::Value;
use tokio::time::{sleep, Duration};
use tracing::info;

use super::utils::types::WasmdTxResponse;
use crate::{
error::Error,
handler::Handler,
request::contract_tx::ContractTxRequest,
response::{contract_tx::ContractTxResponse, Response},
Config,
};

#[async_trait]
impl Handler for ContractTxRequest {
type Error = Error;
type Response = Response;

async fn handle(self, _: Config) -> Result<Self::Response, Self::Error> {
let tx_hash = tx(self)
.await
.map_err(|e| Error::GenericErr(e.to_string()))?;

Ok(ContractTxResponse { tx_hash }.into())
}
}

async fn tx(args: ContractTxRequest) -> Result<String, anyhow::Error> {
let httpurl = Url::parse(&format!("http://{}", args.node_url))?;
let wasmd_client = CliWasmdClient::new(Url::parse(httpurl.as_str())?);

info!("🚀 Submitting Tx with msg: {}", args.msg);

let tx_output: WasmdTxResponse = serde_json::from_str(&wasmd_client.tx_execute(
&args.contract,
&args.chain_id,
args.gas,
&args.sender,
args.msg,
args.amount,
)?)?;

let hash = tx_output.txhash.to_string();
info!("🚀 Successfully sent tx with hash {}", hash);
info!("Waiting 5 seconds for tx to be included in block.....");

// TODO - a more robust timeout mechanism with polling blocks
sleep(Duration::from_secs(5)).await;
Comment on lines +46 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A function with this polling mechanism exists in handler/utils/helpers.rs called block_tx_commit


// Query the transaction
let tx_result: Value = wasmd_client.query_tx(&hash)?;

// Check if the transaction was successful, otherwise return raw log or error
if let Some(code) = tx_result["code"].as_u64() {
if code == 0 {
info!("Transaction was successful!");
} else {
return Err(anyhow::anyhow!(
"Transaction failed. Inspect raw log: {}",
tx_result["raw_log"]
));
}
} else {
return Err(anyhow::anyhow!("Unable to determine transaction status"));
}

Ok(tx_output.txhash.to_string())
}
2 changes: 2 additions & 0 deletions cli/src/handler/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ async fn handshake(args: HandshakeRequest, mock_sgx: bool) -> Result<String, any
2000000,
&args.sender,
json!(res),
None,
)?
.as_str(),
)?;
Expand Down Expand Up @@ -132,6 +133,7 @@ async fn handshake(args: HandshakeRequest, mock_sgx: bool) -> Result<String, any
2000000,
&args.sender,
json!(res),
None,
)?
.as_str(),
)?;
Expand Down
2 changes: 1 addition & 1 deletion cli/src/handler/utils/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub fn run_relay<R: DeserializeOwned>(
Ok(query_result)
}

// Note: time until tx commit is empiraclly 800ms on DO wasmd chain.
// Note: time until tx commit is empirically 800ms on DO wasmd chain.
pub async fn block_tx_commit(client: &HttpClient, tx: Hash) -> Result<TmTxResponse, anyhow::Error> {
let re = Regex::new(r"tx \([A-F0-9]{64}\) not found")?;

Expand Down
24 changes: 22 additions & 2 deletions cli/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ use crate::{
error::Error,
request::{
contract_build::ContractBuildRequest, contract_deploy::ContractDeployRequest,
enclave_build::EnclaveBuildRequest, enclave_start::EnclaveStartRequest,
handshake::HandshakeRequest, init::InitRequest,
contract_tx::ContractTxRequest, enclave_build::EnclaveBuildRequest,
enclave_start::EnclaveStartRequest, handshake::HandshakeRequest, init::InitRequest,
},
};

pub mod contract_build;
pub mod contract_deploy;
pub mod contract_tx;
pub mod enclave_build;
pub mod enclave_start;
pub mod handshake;
Expand All @@ -23,6 +24,7 @@ pub enum Request {
Handshake(HandshakeRequest),
ContractBuild(ContractBuildRequest),
ContractDeploy(ContractDeployRequest),
ContractTx(ContractTxRequest),
EnclaveBuild(EnclaveBuildRequest),
EnclaveStart(EnclaveStartRequest),
}
Expand Down Expand Up @@ -105,6 +107,24 @@ impl TryFrom<ContractCommand> for Request {

Ok(ContractBuildRequest { manifest_path }.into())
}
ContractCommand::Tx {
node_url,
contract,
chain_id,
gas,
sender,
msg,
amount,
} => Ok(ContractTxRequest {
node_url,
contract,
chain_id,
gas,
sender,
msg,
amount,
}
.into()),
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions cli/src/request/contract_tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use cosmrs::{tendermint::chain::Id as ChainId, AccountId};

use crate::request::Request;

#[derive(Clone, Debug)]
pub struct ContractTxRequest {
pub node_url: String,
pub contract: AccountId,
pub chain_id: ChainId,
pub gas: u64,
pub sender: String,
pub msg: String,
pub amount: Option<String>,
}

impl From<ContractTxRequest> for Request {
fn from(request: ContractTxRequest) -> Self {
Self::ContractTx(request)
}
}
6 changes: 4 additions & 2 deletions cli/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ use serde::Serialize;

use crate::response::{
contract_build::ContractBuildResponse, contract_deploy::ContractDeployResponse,
enclave_build::EnclaveBuildResponse, enclave_start::EnclaveStartResponse,
handshake::HandshakeResponse, init::InitResponse,
contract_tx::ContractTxResponse, enclave_build::EnclaveBuildResponse,
enclave_start::EnclaveStartResponse, handshake::HandshakeResponse, init::InitResponse,
};

pub mod contract_build;
pub mod contract_deploy;
pub mod contract_tx;
pub mod enclave_build;
pub mod enclave_start;
pub mod handshake;
Expand All @@ -19,6 +20,7 @@ pub enum Response {
Handshake(HandshakeResponse),
ContractBuild(ContractBuildResponse),
ContractDeploy(ContractDeployResponse),
ContractTx(ContractTxResponse),
EnclaveBuild(EnclaveBuildResponse),
EnclaveStart(EnclaveStartResponse),
}
14 changes: 14 additions & 0 deletions cli/src/response/contract_tx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use serde::Serialize;

use crate::response::Response;

#[derive(Clone, Debug, Serialize, Default)]
pub struct ContractTxResponse {
pub tx_hash: String,
}

impl From<ContractTxResponse> for Response {
fn from(response: ContractTxResponse) -> Self {
Self::ContractTx(response)
}
}
9 changes: 8 additions & 1 deletion utils/cycles-sync/src/bin/submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,14 @@ async fn main() -> Result<(), anyhow::Error> {

let wasmd_client = CliWasmdClient::new(node_url);

wasmd_client.tx_execute(&cli.mtcs, &chain_id, 3000000, &cli.admin.to_string(), msg)?;
wasmd_client.tx_execute(
&cli.mtcs,
&chain_id,
3000000,
&cli.admin.to_string(),
msg,
None,
)?;

Ok(())
}
Expand Down
Loading
Loading