Skip to content

Commit

Permalink
feat contract info: add impl for base64 xdr
Browse files Browse the repository at this point in the history
  • Loading branch information
Ifropc committed Aug 6, 2024
1 parent 327b1f2 commit 92d21c0
Show file tree
Hide file tree
Showing 11 changed files with 294 additions and 135 deletions.
33 changes: 24 additions & 9 deletions FULL_HELP_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -414,9 +414,9 @@ Outputs no data when no data is present in the contract.

###### **Options:**

* `--wasm <WASM>` — Wasm file to extract the meta from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the meta for
* `--id <CONTRACT_ID>` — Contract ID to get the meta for
* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>` — Contract id to get the data for
* `--output <OUTPUT>` — Format of the output

Default value: `xdr-base64`
Expand All @@ -429,6 +429,11 @@ Outputs no data when no data is present in the contract.
- `json-formatted`:
Formatted JSON output of the info entry

* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
* `--network <NETWORK>` — Name of network to use from config
* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."



Expand All @@ -446,9 +451,9 @@ Outputs no data when no data is present in the contract.

###### **Options:**

* `--wasm <WASM>` — Wasm file to extract the interface from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the interface for
* `--id <CONTRACT_ID>`Format of the output
* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>`Contract id to get the data for
* `--output <OUTPUT>` — Format of the output

Default value: `xdr-base64`
Expand All @@ -461,6 +466,11 @@ Outputs no data when no data is present in the contract.
- `json-formatted`:
Formatted JSON output of the info entry

* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
* `--network <NETWORK>` — Name of network to use from config
* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."



Expand All @@ -478,9 +488,9 @@ Outputs no data when no data is present in the contract.

###### **Options:**

* `--wasm <WASM>` — Wasm file to extract the meta from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the meta for
* `--id <CONTRACT_ID>`Format of the output
* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>`Contract id to get the data for
* `--output <OUTPUT>` — Format of the output

Default value: `xdr-base64`
Expand All @@ -493,6 +503,11 @@ Outputs no data when no data is present in the contract.
- `json-formatted`:
Formatted JSON output of the info entry

* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
* `--network <NETWORK>` — Name of network to use from config
* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."



Expand Down
21 changes: 14 additions & 7 deletions cmd/crates/soroban-spec-tools/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,11 @@ impl Spec {
vec![]
};

let mut spec_base64 = None;
let spec = if let Some(spec) = spec {
spec_base64 = Some(base64.encode(spec));
let cursor = Cursor::new(spec);
let mut read = Limited::new(cursor, Limits::none());
ScSpecEntry::read_xdr_iter(&mut read).collect::<Result<Vec<_>, xdr::Error>>()?
let (spec_base64, spec) = if let Some(spec) = spec {
let (spec_base64, spec) = Spec::spec_to_base64(spec)?;
(Some(spec_base64), spec)
} else {
vec![]
(None, vec![])
};

Ok(Spec {
Expand All @@ -106,6 +103,16 @@ impl Spec {
.join(",\n");
Ok(format!("[{spec}]"))
}

pub fn spec_to_base64(spec: &[u8]) -> Result<(String, Vec<ScSpecEntry>), Error> {
let spec_base64 = base64.encode(spec);
let cursor = Cursor::new(spec);
let mut read = Limited::new(cursor, Limits::none());
Ok((
spec_base64,
ScSpecEntry::read_xdr_iter(&mut read).collect::<Result<Vec<_>, xdr::Error>>()?,
))
}
}

impl Display for Spec {
Expand Down
44 changes: 4 additions & 40 deletions cmd/soroban-cli/src/commands/contract/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,13 @@ use std::str::FromStr;
use std::{fmt::Debug, fs, io};

use clap::{arg, command, Parser};
use stellar_xdr::curr::{ContractDataEntry, ContractExecutable, ScVal};

use crate::commands::contract::fetch::Error::{ContractIsStellarAsset, UnexpectedContractToken};
use crate::commands::{global, NetworkRunnable};
use crate::config::{
self, locator,
network::{self, Network},
};
use crate::utils::rpc::get_remote_wasm_from_hash;
use crate::{
rpc::{self, Client},
Pwd,
};
use crate::{wasm, Pwd};

#[derive(Parser, Debug, Default, Clone)]
#[allow(clippy::struct_excessive_bools)]
Expand Down Expand Up @@ -53,8 +47,6 @@ impl Pwd for Cmd {

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Rpc(#[from] rpc::Error),
#[error(transparent)]
Config(#[from] config::Error),
#[error(transparent)]
Expand All @@ -67,13 +59,8 @@ pub enum Error {
Network(#[from] network::Error),
#[error("cannot create contract directory for {0:?}")]
CannotCreateContractDir(PathBuf),
#[error("unexpected contract data {0:?}")]
UnexpectedContractToken(ContractDataEntry),
#[error(
"cannot fetch wasm for contract because the contract is \
a network built-in asset contract that does not have a downloadable code binary"
)]
ContractIsStellarAsset(),
#[error(transparent)]
Wasm(#[from] wasm::Error),
}

impl From<Infallible> for Error {
Expand Down Expand Up @@ -110,14 +97,6 @@ impl Cmd {
pub fn network(&self) -> Result<Network, Error> {
Ok(self.network.get(&self.locator)?)
}

fn contract_id(&self) -> Result<[u8; 32], Error> {
let network = self.network()?;
Ok(self
.locator
.resolve_contract_id(&self.contract_id, &network.network_passphrase)?
.0)
}
}

#[async_trait::async_trait]
Expand All @@ -130,21 +109,6 @@ impl NetworkRunnable for Cmd {
config: Option<&config::Args>,
) -> Result<Vec<u8>, Error> {
let network = config.map_or_else(|| self.network(), |c| Ok(c.get_network()?))?;
tracing::trace!(?network);
let contract_id = self.contract_id()?;
let client = Client::new(&network.rpc_url)?;
client
.verify_network_passphrase(Some(&network.network_passphrase))
.await?;
let data_entry = client.get_contract_data(&contract_id).await?;
if let ScVal::ContractInstance(contract) = &data_entry.val {
return match &contract.executable {
ContractExecutable::Wasm(hash) => {
Ok(get_remote_wasm_from_hash(&client, hash).await?)
}
ContractExecutable::StellarAsset => Err(ContractIsStellarAsset()),
};
}
return Err(UnexpectedContractToken(data_entry));
return Ok(wasm::fetch_from_contract(&self.contract_id, &network, &self.locator).await?);
}
}
1 change: 1 addition & 0 deletions cmd/soroban-cli/src/commands/contract/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::fmt::Debug;
pub mod env_meta;
pub mod interface;
pub mod meta;
mod shared;

#[derive(Debug, clap::Subcommand)]
pub enum Cmd {
Expand Down
66 changes: 40 additions & 26 deletions cmd/soroban-cli/src/commands/contract/info/env_meta.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,55 @@
use std::{fmt::Debug, path::PathBuf};
use std::fmt::Debug;

use crate::commands::contract::info::env_meta::Error::{NoEnvMetaPresent, NoSACEnvMeta};
use crate::commands::contract::info::shared;
use crate::commands::contract::info::shared::fetch_wasm;
use clap::{command, Parser};
use soroban_spec_tools::contract;
use soroban_spec_tools::contract::Spec;

use crate::commands::contract::InfoOutput;

#[derive(Parser, Debug, Clone)]
#[command(group(
clap::ArgGroup::new("src")
.required(true)
.args(& ["wasm", "wasm_hash", "contract_id"]),
))]
#[group(skip)]
pub struct Cmd {
/// Wasm file to extract the meta from
#[arg(
long,
conflicts_with = "wasm_hash",
conflicts_with = "contract_id",
group = "src"
)]
pub wasm: Option<PathBuf>,
/// Wasm hash to get the meta for
#[arg(long = "wasm-hash", group = "src")]
pub wasm_hash: Option<String>,
/// Contract ID to get the meta for
#[arg(long = "id", env = "STELLAR_CONTRACT_ID", group = "src")]
pub contract_id: Option<String>,
/// Format of the output
#[arg(long, default_value = "xdr-base64")]
output: InfoOutput,
#[command(flatten)]
pub common: shared::Args,
}

#[derive(thiserror::Error, Debug)]
pub enum Error {}
pub enum Error {
#[error(transparent)]
Wasm(#[from] shared::Error),
#[error(transparent)]
Spec(#[from] contract::Error),
#[error("Stellar asset contract doesn't contain meta information")]
NoSACEnvMeta(),
#[error("no meta present in provided WASM file")]
NoEnvMetaPresent(),
}

impl Cmd {
pub async fn run(&self) -> Result<String, Error> {
Ok("env_meta".to_string()) // TODO
let bytes = fetch_wasm(&self.common).await?;

if bytes.is_none() {
return Err(NoSACEnvMeta());
}
let spec = Spec::new(&bytes.unwrap())?;

if spec.env_meta_base64.is_none() {
return Err(NoEnvMetaPresent());
}

let res = match self.common.output {
InfoOutput::XdrBase64 => spec.env_meta_base64.unwrap(),
InfoOutput::Json => {
unreachable!("TODO")
}
InfoOutput::JsonFormatted => {
unreachable!("TODO")
}
};

Ok(res)
}
}
68 changes: 42 additions & 26 deletions cmd/soroban-cli/src/commands/contract/info/interface.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,58 @@
use std::fmt::Debug;
use std::path::PathBuf;

use crate::commands::contract::info::interface::Error::NoInterfacePresent;
use crate::commands::contract::info::shared;
use crate::commands::contract::info::shared::fetch_wasm;
use clap::{command, Parser};
use soroban_spec_tools::contract;
use soroban_spec_tools::contract::Spec;

use crate::commands::contract::InfoOutput;

#[derive(Parser, Debug, Clone)]
#[command(group(
clap::ArgGroup::new("src")
.required(true)
.args(& ["wasm", "wasm_hash", "contract_id"]),
))]
#[group(skip)]
pub struct Cmd {
/// Wasm file to extract the interface from
#[arg(
long,
conflicts_with = "wasm_hash",
conflicts_with = "contract_id",
group = "src"
)]
pub wasm: Option<PathBuf>,
/// Wasm hash to get the interface for
#[arg(long = "wasm-hash", group = "src")]
pub wasm_hash: Option<String>,
/// Format of the output
#[arg(long = "id", env = "STELLAR_CONTRACT_ID", group = "src")]
pub contract_id: Option<String>,
/// Format of the output
#[arg(long, default_value = "xdr-base64")]
output: InfoOutput,
#[command(flatten)]
pub common: shared::Args,
}

#[derive(thiserror::Error, Debug)]
pub enum Error {}
pub enum Error {
#[error(transparent)]
Wasm(#[from] shared::Error),
#[error(transparent)]
Spec(#[from] contract::Error),
#[error("no interface present in provided WASM file")]
NoInterfacePresent(),
}

impl Cmd {
pub async fn run(&self) -> Result<String, Error> {
Ok("interface".to_string()) // TODO
let bytes = fetch_wasm(&self.common).await?;

let base64 = if bytes.is_none() {
let res = Spec::spec_to_base64(&soroban_sdk::token::StellarAssetSpec::spec_xdr())?;

res.0
} else {
let spec = Spec::new(&bytes.unwrap())?;

if spec.env_meta_base64.is_none() {
return Err(NoInterfacePresent());
}

spec.spec_base64.unwrap()
};

let res = match self.common.output {
InfoOutput::XdrBase64 => base64,
InfoOutput::Json => {
unreachable!("TODO")
}
InfoOutput::JsonFormatted => {
unreachable!("TODO")
}
};

Ok(res)
}
}
Loading

0 comments on commit 92d21c0

Please sign in to comment.