Skip to content

Commit

Permalink
feat: create soroban-rpc crate (#21)
Browse files Browse the repository at this point in the history
* feat: create soroban-rpc crate
* chore: add doc strings
  • Loading branch information
willemneal committed Jan 27, 2024
1 parent 2af47af commit d1e5709
Show file tree
Hide file tree
Showing 12 changed files with 642 additions and 265 deletions.
496 changes: 247 additions & 249 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ rev = "e6c2c900ab82b5f6eec48f69cb2cb519e19819cb"
version = "20.2.0"
path = "cmd/soroban-cli"

[workspace.dependencies.soroban-rpc]
version = "20.2.0"
path = "cmd/crates/soroban-rpc"

[workspace.dependencies.stellar-xdr]
version = "=20.0.2"
default-features = true
Expand Down
59 changes: 59 additions & 0 deletions cmd/crates/soroban-rpc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[package]
name = "soroban-rpc"
description = "Soroban RPC client for rust"
homepage = "https://github.com/stellar/soroban-tools"
repository = "https://github.com/stellar/soroban-tools"
authors = ["Stellar Development Foundation <[email protected]>"]
license = "Apache-2.0"
readme = "README.md"
version.workspace = true
edition = "2021"
rust-version = "1.70"
autobins = false


[lib]
crate-type = ["rlib"]


[dependencies]
soroban-sdk = { workspace = true }
soroban-spec-tools = { workspace = true }

soroban-env-host = { workspace = true }
stellar-strkey = { workspace = true }
stellar-xdr = { workspace = true, features = ["curr", "std", "serde"] }
soroban-spec = { workspace = true }


termcolor = { workspace = true }
termcolor_output = { workspace = true }
clap = { workspace = true }

serde_json = { workspace = true }
serde-aux = { workspace = true }
itertools = { workspace = true }
ethnum = { workspace = true }
hex = { workspace = true }
wasmparser = { workspace = true }
base64 = { workspace = true }
thiserror = { workspace = true }
serde = { workspace = true }
tokio = { workspace = true }
sha2 = { workspace = true }
ed25519-dalek = { workspace = true }
tracing = { workspace = true }


# networking
jsonrpsee-http-client = { workspace = true }
jsonrpsee-core = { workspace = true }
http = { workspace = true }

# soroban-ledger-snapshot = { workspace = true }
# soroban-sdk = { workspace = true }
# sep5 = { workspace = true }


[dev-dependencies]
which = { workspace = true }
3 changes: 3 additions & 0 deletions cmd/crates/soroban-rpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# soroban-rpc

Tools and utilities for soroban rpc.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ mod txn;

pub use txn::*;

use soroban_spec_tools::contract::Spec as Contract;
use soroban_spec_tools::contract;

use crate::utils::contract_spec as contract;
const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION");

pub type LogEvents = fn(
Expand Down Expand Up @@ -179,6 +178,8 @@ impl TryInto<GetTransactionResponse> for GetTransactionResponseRaw {
}

impl GetTransactionResponse {
///
/// # Errors
pub fn return_value(&self) -> Result<xdr::ScVal, Error> {
if let Some(xdr::TransactionMeta::V3(xdr::TransactionMetaV3 {
soroban_meta: Some(xdr::SorobanTransactionMeta { return_value, .. }),
Expand All @@ -191,6 +192,8 @@ impl GetTransactionResponse {
}
}

///
/// # Errors
pub fn events(&self) -> Result<Vec<DiagnosticEvent>, Error> {
if let Some(meta) = self.result_meta.as_ref() {
Ok(extract_events(meta))
Expand All @@ -199,6 +202,8 @@ impl GetTransactionResponse {
}
}

///
/// # Errors
pub fn contract_events(&self) -> Result<Vec<DiagnosticEvent>, Error> {
Ok(self
.events()?
Expand Down Expand Up @@ -311,6 +316,8 @@ pub struct SimulateTransactionResponse {
}

impl SimulateTransactionResponse {
///
/// # Errors
pub fn results(&self) -> Result<Vec<SimulateHostFunctionResult>, Error> {
self.results
.iter()
Expand All @@ -332,13 +339,17 @@ impl SimulateTransactionResponse {
.collect()
}

///
/// # Errors
pub fn events(&self) -> Result<Vec<DiagnosticEvent>, Error> {
self.events
.iter()
.map(|e| Ok(DiagnosticEvent::from_xdr_base64(e, Limits::none())?))
.collect()
}

///
/// # Errors
pub fn transaction_data(&self) -> Result<SorobanTransactionData, Error> {
Ok(SorobanTransactionData::from_xdr_base64(
&self.transaction_data,
Expand Down Expand Up @@ -435,10 +446,13 @@ impl Display for Event {
}

impl Event {
///
/// # Errors
pub fn parse_cursor(&self) -> Result<(u64, i32), Error> {
parse_cursor(&self.id)
}

///
/// # Errors
pub fn pretty_print(&self) -> Result<(), Box<dyn std::error::Error>> {
let mut stdout = StandardStream::stdout(ColorChoice::Auto);
if !stdout.supports_color() {
Expand Down Expand Up @@ -540,6 +554,8 @@ pub struct Client {
}

impl Client {
///
/// # Errors
pub fn new(base_url: &str) -> Result<Self, Error> {
// Add the port to the base URL if there is no port explicitly included
// in the URL and the scheme allows us to infer a default port.
Expand Down Expand Up @@ -570,6 +586,8 @@ impl Client {
})
}

///
/// # Errors
fn client(&self) -> Result<HttpClient, Error> {
let url = self.base_url.clone();
let mut headers = HeaderMap::new();
Expand All @@ -581,6 +599,8 @@ impl Client {
.build(url)?)
}

///
/// # Errors
pub async fn friendbot_url(&self) -> Result<String, Error> {
let network = self.get_network().await?;
tracing::trace!("{network:#?}");
Expand All @@ -591,7 +611,8 @@ impl Client {
)
})
}

///
/// # Errors
pub async fn verify_network_passphrase(&self, expected: Option<&str>) -> Result<String, Error> {
let server = self.get_network().await?.passphrase;
if let Some(expected) = expected {
Expand All @@ -605,11 +626,15 @@ impl Client {
Ok(server)
}

///
/// # Errors
pub async fn get_network(&self) -> Result<GetNetworkResponse, Error> {
tracing::trace!("Getting network");
Ok(self.client()?.request("getNetwork", rpc_params![]).await?)
}

///
/// # Errors
pub async fn get_latest_ledger(&self) -> Result<GetLatestLedgerResponse, Error> {
tracing::trace!("Getting latest ledger");
Ok(self
Expand All @@ -618,6 +643,8 @@ impl Client {
.await?)
}

///
/// # Errors
pub async fn get_account(&self, address: &str) -> Result<AccountEntry, Error> {
tracing::trace!("Getting address {}", address);
let key = LedgerKey::Account(LedgerKeyAccount {
Expand Down Expand Up @@ -649,6 +676,8 @@ soroban config identity fund {address} --helper-url <url>"#
}
}

///
/// # Errors
pub async fn send_transaction(
&self,
tx: &TransactionEnvelope,
Expand Down Expand Up @@ -719,6 +748,8 @@ soroban config identity fund {address} --helper-url <url>"#
}
}

///
/// # Errors
pub async fn simulate_transaction(
&self,
tx: &TransactionEnvelope,
Expand All @@ -741,6 +772,8 @@ soroban config identity fund {address} --helper-url <url>"#
}
}

///
/// # Errors
pub async fn send_assembled_transaction(
&self,
txn: txn::Assembled,
Expand All @@ -762,6 +795,8 @@ soroban config identity fund {address} --helper-url <url>"#
self.send_transaction(&tx).await
}

///
/// # Errors
pub async fn prepare_and_send_transaction(
&self,
tx_without_preflight: &Transaction,
Expand All @@ -783,20 +818,26 @@ soroban config identity fund {address} --helper-url <url>"#
.await
}

///
/// # Errors
pub async fn create_assembled_transaction(
&self,
txn: &Transaction,
) -> Result<txn::Assembled, Error> {
txn::Assembled::new(txn, self).await
}

///
/// # Errors
pub async fn get_transaction(&self, tx_id: &str) -> Result<GetTransactionResponseRaw, Error> {
Ok(self
.client()?
.request("getTransaction", rpc_params![tx_id])
.await?)
}

///
/// # Errors
pub async fn get_ledger_entries(
&self,
keys: &[LedgerKey],
Expand All @@ -807,14 +848,16 @@ soroban config identity fund {address} --helper-url <url>"#
if base64_result.is_err() {
return Err(Error::Xdr(XdrError::Invalid));
}
base64_keys.push(k.to_xdr_base64(Limits::none()).unwrap());
base64_keys.push(k.to_xdr_base64(Limits::none())?);
}
Ok(self
.client()?
.request("getLedgerEntries", rpc_params![base64_keys])
.await?)
}

///
/// # Errors
pub async fn get_full_ledger_entries(
&self,
ledger_keys: &[LedgerKey],
Expand Down Expand Up @@ -855,7 +898,8 @@ soroban config identity fund {address} --helper-url <url>"#
latest_ledger,
})
}

///
/// # Errors
pub async fn get_events(
&self,
start: EventStart,
Expand Down Expand Up @@ -895,6 +939,8 @@ soroban config identity fund {address} --helper-url <url>"#
Ok(self.client()?.request("getEvents", oparams).await?)
}

///
/// # Errors
pub async fn get_contract_data(
&self,
contract_id: &[u8; 32],
Expand All @@ -918,6 +964,8 @@ soroban config identity fund {address} --helper-url <url>"#
}
}

///
/// # Errors
pub async fn get_remote_wasm(&self, contract_id: &[u8; 32]) -> Result<Vec<u8>, Error> {
match self.get_contract_data(contract_id).await? {
xdr::ContractDataEntry {
Expand All @@ -932,6 +980,8 @@ soroban config identity fund {address} --helper-url <url>"#
}
}

///
/// # Errors
pub async fn get_remote_wasm_from_hash(&self, hash: xdr::Hash) -> Result<Vec<u8>, Error> {
let code_key = LedgerKey::ContractCode(xdr::LedgerKeyContractCode { hash: hash.clone() });
let contract_data = self.get_ledger_entries(&[code_key]).await?;
Expand All @@ -948,7 +998,8 @@ soroban config identity fund {address} --helper-url <url>"#
scval => Err(Error::UnexpectedContractCodeDataType(scval)),
}
}

///
/// # Errors
pub async fn get_remote_contract_spec(
&self,
contract_id: &[u8; 32],
Expand All @@ -958,9 +1009,11 @@ soroban config identity fund {address} --helper-url <url>"#
xdr::ScVal::ContractInstance(xdr::ScContractInstance {
executable: xdr::ContractExecutable::Wasm(hash),
..
}) => Ok(Contract::new(&self.get_remote_wasm_from_hash(hash).await?)
.map_err(Error::CouldNotParseContractSpec)?
.spec),
}) => Ok(
contract::Spec::new(&self.get_remote_wasm_from_hash(hash).await?)
.map_err(Error::CouldNotParseContractSpec)?
.spec,
),
xdr::ScVal::ContractInstance(xdr::ScContractInstance {
executable: xdr::ContractExecutable::StellarAsset,
..
Expand Down Expand Up @@ -997,7 +1050,7 @@ fn extract_events(tx_meta: &TransactionMeta) -> Vec<DiagnosticEvent> {
}
}

pub fn parse_cursor(c: &str) -> Result<(u64, i32), Error> {
pub(crate) fn parse_cursor(c: &str) -> Result<(u64, i32), Error> {
let (toid_part, event_index) = c.split('-').collect_tuple().ok_or(Error::InvalidCursor)?;
let toid_part: u64 = toid_part.parse().map_err(|_| Error::InvalidCursor)?;
let start_index: i32 = event_index.parse().map_err(|_| Error::InvalidCursor)?;
Expand Down
File renamed without changes.
Loading

0 comments on commit d1e5709

Please sign in to comment.