From bd42bdf26f79f8a8fa6acfbc4770300977b05bd2 Mon Sep 17 00:00:00 2001 From: Sam Batschelet Date: Fri, 25 Nov 2022 08:54:54 -0500 Subject: [PATCH 1/8] e2e: add client tests Signed-off-by: Sam Batschelet --- spaces-cli/src/bin/spaces-cli/main.rs | 35 +++------- spacesvm/src/api/client.rs | 97 ++++++++++++++++++++------- spacesvm/tests/vm/mod.rs | 5 +- tests/e2e/src/tests/mod.rs | 41 ++++++++++- 4 files changed, 124 insertions(+), 54 deletions(-) diff --git a/spaces-cli/src/bin/spaces-cli/main.rs b/spaces-cli/src/bin/spaces-cli/main.rs index d1736293..34804a91 100644 --- a/spaces-cli/src/bin/spaces-cli/main.rs +++ b/spaces-cli/src/bin/spaces-cli/main.rs @@ -3,11 +3,8 @@ use std::error; use clap::{Parser, Subcommand}; use jsonrpc_core::futures; use spacesvm::{ - api::{ - client::{claim_tx, delete_tx, get_or_create_pk, set_tx, Client, Uri}, - DecodeTxArgs, IssueTxArgs, ResolveArgs, - }, - chain::tx::{decoder, unsigned::TransactionData}, + api::client::{claim_tx, delete_tx, get_or_create_pk, set_tx, Client, Uri}, + chain::tx::unsigned::TransactionData, }; #[derive(Subcommand, Debug)] @@ -47,20 +44,16 @@ struct Cli { command: Command, } -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let cli = Cli::parse(); - let secret_key = get_or_create_pk(&cli.private_key_file)?; + let private_key = get_or_create_pk(&cli.private_key_file)?; let uri = cli.endpoint.parse::()?; - let mut client = Client::new(uri); + let mut client = Client::new(uri).set_private_key(private_key); if let Command::Get { space, key } = &cli.command { - let resp = futures::executor::block_on(client.resolve(ResolveArgs { - space: space.as_bytes().to_vec(), - key: key.as_bytes().to_vec(), - })) - .map_err(|e| e.to_string())?; + let resp = + futures::executor::block_on(client.resolve(space, key)).map_err(|e| e.to_string())?; log::debug!("resolve response: {:?}", resp); println!("{}", serde_json::to_string(&resp)?); @@ -76,21 +69,13 @@ async fn main() -> Result<(), Box> { // decode tx let tx_data = command_to_tx(cli.command)?; - let resp = futures::executor::block_on(client.decode_tx(DecodeTxArgs { tx_data })) - .map_err(|e| e.to_string())?; + let resp = futures::executor::block_on(client.decode_tx(tx_data)).map_err(|e| e.to_string())?; let typed_data = &resp.typed_data; - // create signature - let dh = decoder::hash_structured_data(typed_data)?; - let sig = secret_key.sign_digest(&dh.as_bytes())?; - // issue tx - let resp = futures::executor::block_on(client.issue_tx(IssueTxArgs { - typed_data: resp.typed_data, - signature: sig.to_bytes().to_vec(), - })) - .map_err(|e| e.to_string())?; + let resp = + futures::executor::block_on(client.issue_tx(typed_data)).map_err(|e| e.to_string())?; println!("{}", serde_json::to_string(&resp)?); Ok(()) diff --git a/spacesvm/src/api/client.rs b/spacesvm/src/api/client.rs index 9e85e02d..3e10cceb 100644 --- a/spacesvm/src/api/client.rs +++ b/spacesvm/src/api/client.rs @@ -1,6 +1,6 @@ use std::{ fs::File, - io::{Result, Write}, + io::{Error, ErrorKind, Result, Write}, path::Path, }; @@ -9,12 +9,19 @@ use crate::{ DecodeTxArgs, DecodeTxResponse, IssueTxArgs, IssueTxResponse, PingResponse, ResolveArgs, ResolveResponse, }, - chain::tx::{tx::TransactionType, unsigned::TransactionData}, + chain::tx::{ + decoder::{self, TypedData}, + tx::TransactionType, + unsigned::TransactionData, + }, +}; +use avalanche_types::key::{ + self, + secp256k1::{private_key::Key, signature::Sig}, }; -use avalanche_types::key; use http::{Method, Request}; use hyper::{body, client::HttpConnector, Body, Client as HyperClient}; -use jsonrpc_core::{Call, Id, MethodCall, Params, Version}; +use jsonrpc_core::{Call, Id, MethodCall, Params, Value, Version}; use serde::de; pub use http::Uri; @@ -23,13 +30,19 @@ pub use http::Uri; pub struct Client { id: u64, client: HyperClient, - pub uri: Uri, + endpoint: Uri, + private_key: Option, } impl Client { - pub fn new(uri: Uri) -> Self { + pub fn new(endpoint: Uri) -> Self { let client = HyperClient::new(); - Self { id: 0, client, uri } + Self { + id: 0, + client, + endpoint, + private_key: None, + } } } @@ -40,6 +53,16 @@ impl Client { Id::Num(id) } + pub fn set_endpoint(mut self, endpoint: Uri) -> Self { + self.endpoint = endpoint; + self + } + + pub fn set_private_key(mut self, private_key: Key) -> Self { + self.private_key = Some(private_key); + self + } + /// Returns a serialized json request as string and the request id. pub fn raw_request(&mut self, method: &str, params: &Params) -> (Id, String) { let id = self.next_id(); @@ -55,6 +78,14 @@ impl Client { ) } + /// Returns a recoverable signature from bytes. + pub fn sign_digest(&self, dh: &[u8]) -> Result { + if let Some(pk) = &self.private_key { + pk.sign_digest(dh)?; + } + Err(Error::new(ErrorKind::Other, "private key not set")) + } + /// Returns a PingResponse from client request. pub async fn ping(&mut self) -> Result { let (_id, json_request) = self.raw_request("ping", &Params::None); @@ -64,30 +95,37 @@ impl Client { } /// Returns a DecodeTxResponse from client request. - pub async fn decode_tx(&mut self, args: DecodeTxArgs) -> Result { - let arg_bytes = serde_json::to_vec(&args)?; - let params: Params = serde_json::from_slice(&arg_bytes)?; - let (_id, json_request) = self.raw_request("decodeTx", ¶ms); + pub async fn decode_tx(&mut self, tx_data: TransactionData) -> Result { + let arg_value = serde_json::to_value(&DecodeTxArgs { tx_data })?; + let (_id, json_request) = self.raw_request("decodeTx", &Params::Array(vec![arg_value])); let resp = self.post_de::(&json_request).await?; Ok(resp) } /// Returns a IssueTxResponse from client request. - pub async fn issue_tx(&mut self, args: IssueTxArgs) -> Result { - let arg_bytes = serde_json::to_vec(&args)?; - let params: Params = serde_json::from_slice(&arg_bytes)?; - let (_id, json_request) = self.raw_request("issueTx", ¶ms); + pub async fn issue_tx(&mut self, typed_data: &TypedData) -> Result { + let dh = decoder::hash_structured_data(typed_data)?; + let sig = self.sign_digest(&dh.as_bytes())?.to_bytes().to_vec(); + log::debug!("signature: {:?}", sig); + + let arg_value = serde_json::to_value(&IssueTxArgs { + typed_data: typed_data.to_owned(), + signature: sig, + })?; + let (_id, json_request) = self.raw_request("issueTx", &Params::Array(vec![arg_value])); let resp = self.post_de::(&json_request).await?; Ok(resp) } /// Returns a ResolveResponse from client request. - pub async fn resolve(&mut self, args: ResolveArgs) -> Result { - let arg_bytes = serde_json::to_vec(&args)?; - let params: Params = serde_json::from_slice(&arg_bytes)?; - let (_id, json_request) = self.raw_request("resolve", ¶ms); + pub async fn resolve(&mut self, space: &str, key: &str) -> Result { + let arg_value = serde_json::to_value(&ResolveArgs { + space: space.as_bytes().to_vec(), + key: key.as_bytes().to_vec(), + })?; + let (_id, json_request) = self.raw_request("issueTx", &Params::Array(vec![arg_value])); let resp = self.post_de::(&json_request).await?; Ok(resp) @@ -95,9 +133,10 @@ impl Client { /// Returns a deserialized response from client request. pub async fn post_de(&self, json: &str) -> Result { + println!("json: {}", json); let req = Request::builder() .method(Method::POST) - .uri(self.uri.to_string()) + .uri(self.endpoint.to_string()) .header("content-type", "application/json-rpc") .body(Body::from(json.to_owned())) .map_err(|e| { @@ -107,20 +146,30 @@ impl Client { ) })?; - let resp = self.client.request(req).await.map_err(|e| { + let mut resp = self.client.request(req).await.map_err(|e| { std::io::Error::new( std::io::ErrorKind::Other, format!("client post request failed: {}", e), ) })?; - let bytes = body::to_bytes(resp.into_body()) + let bytes = body::to_bytes(resp.body_mut()) .await .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; - let resp = serde_json::from_slice(&bytes).map_err(|e| { + + // deserialize bytes to value + let v: Value = serde_json::from_slice(&bytes).map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("failed to deserialize response to value: {}", e), + ) + })?; + + // deserialize result to T + let resp = serde_json::from_value(v["result"].to_owned()).map_err(|e| { std::io::Error::new( std::io::ErrorKind::Other, - format!("failed to create client request: {}", e), + format!("failed to deserialize response: {}", e), ) })?; diff --git a/spacesvm/tests/vm/mod.rs b/spacesvm/tests/vm/mod.rs index 4b7a378b..6397d59a 100644 --- a/spacesvm/tests/vm/mod.rs +++ b/spacesvm/tests/vm/mod.rs @@ -139,9 +139,10 @@ async fn test_api() { log::info!("ping response {}", body); let tx_data = claim_tx("test_claim".to_owned()); - let arg_bytes = serde_json::to_value(&DecodeTxArgs { tx_data }).unwrap(); + let arg_value = serde_json::to_value(&DecodeTxArgs { tx_data }).unwrap(); - let (_id, json_str) = client.raw_request("decodeTx", &Params::Array(vec![arg_bytes])); + let (_id, json_str) = client.raw_request("decodeTx", &Params::Array(vec![arg_value])); + log::info!("decodeTx request: {}", json_str); let req = http::request::Builder::new() .body(json_str.as_bytes().to_vec()) .unwrap(); diff --git a/tests/e2e/src/tests/mod.rs b/tests/e2e/src/tests/mod.rs index d85f549e..d781926a 100644 --- a/tests/e2e/src/tests/mod.rs +++ b/tests/e2e/src/tests/mod.rs @@ -7,7 +7,13 @@ use std::{ use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; use avalanche_types::subnet; -use spacesvm; +use spacesvm::{ + self, + api::{ + client::{claim_tx, get_or_create_pk, Uri}, + DecodeTxArgs, + }, +}; #[tokio::test] async fn e2e() { @@ -47,7 +53,7 @@ async fn e2e() { // keep this in sync with "proto" crate // ref. https://github.com/ava-labs/avalanchego/blob/v1.9.2/version/constants.go#L15-L17 let (exec_path, plugins_dir) = - avalanche_installer::avalanchego::download(None, None, Some("v1.9.2".to_string())) + avalanche_installer::avalanchego::download(None, None, Some("v1.9.3".to_string())) .await .unwrap(); avalanchego_exec_path = exec_path; @@ -92,7 +98,7 @@ async fn e2e() { .unwrap(), ), blockchain_specs: vec![BlockchainSpec { - vm_name: String::from("minikvvm"), + vm_name: String::from("spacesvm"), genesis: genesis_file_path.to_string(), ..Default::default() }], @@ -178,6 +184,35 @@ async fn e2e() { rpc_eps.push(iv.uri.clone()); } + let ep = format!( + "{}/{}", + rpc_eps[0].to_owned(), + spacesvm::vm::PUBLIC_API_ENDPOINT + ); + let private_key = get_or_create_pk("/tmp/.spacesvm-cli-pk").expect("generate new private key"); + + let mut scli = spacesvm::api::client::Client::new(ep.parse::().expect("valid endpoint")) + .set_private_key(private_key); + log::info!("ping request..."); + let resp = scli.ping().await.expect("ping success"); + log::info!("ping response from {}: {:?}", ep, resp); + + log::info!("decode claim tx request..."); + let resp = scli + .decode_tx(DecodeTxArgs { + tx_data: claim_tx("test".to_owned()), + }) + .await + .expect("decodeTx success"); + log::info!("decode claim response from {}: {:?}", ep, resp); + + log::info!("issue claim tx request..."); + let resp = scli + .issue_tx(&resp.typed_data) + .await + .expect("issue_tx success"); + log::info!("issue claim tx response from {}: {:?}", ep, resp); + if crate::get_network_runner_enable_shutdown() { log::info!("shutdown is enabled... stopping..."); let _resp = cli.stop().await.expect("failed stop"); From ef6f7d2b99f8d65a1dbe70fd29c7df41b6885882 Mon Sep 17 00:00:00 2001 From: Sam Batschelet Date: Fri, 25 Nov 2022 09:11:19 -0500 Subject: [PATCH 2/8] improve ux Signed-off-by: Sam Batschelet --- spaces-cli/src/bin/spaces-cli/main.rs | 6 +++--- spacesvm/src/api/client.rs | 18 +++++++++--------- spacesvm/tests/vm/mod.rs | 2 +- tests/e2e/src/tests/mod.rs | 9 ++------- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/spaces-cli/src/bin/spaces-cli/main.rs b/spaces-cli/src/bin/spaces-cli/main.rs index 34804a91..fccff812 100644 --- a/spaces-cli/src/bin/spaces-cli/main.rs +++ b/spaces-cli/src/bin/spaces-cli/main.rs @@ -84,9 +84,9 @@ fn main() -> Result<(), Box> { /// Takes a TX command and returns transaction data. fn command_to_tx(command: Command) -> std::io::Result { match command { - Command::Claim { space } => Ok(claim_tx(space)), - Command::Set { space, key, value } => Ok(set_tx(space, key, value.as_bytes().to_vec())), - Command::Delete { space, key } => Ok(delete_tx(space, key)), + Command::Claim { space } => Ok(claim_tx(&space)), + Command::Set { space, key, value } => Ok(set_tx(&space, &key, &value)), + Command::Delete { space, key } => Ok(delete_tx(&space, &key)), _ => Err(std::io::Error::new( std::io::ErrorKind::Other, "not a supported tx", diff --git a/spacesvm/src/api/client.rs b/spacesvm/src/api/client.rs index 3e10cceb..5fa06910 100644 --- a/spacesvm/src/api/client.rs +++ b/spacesvm/src/api/client.rs @@ -177,29 +177,29 @@ impl Client { } } -pub fn claim_tx(space: String) -> TransactionData { +pub fn claim_tx(space: &str) -> TransactionData { TransactionData { typ: TransactionType::Claim, - space, + space: space.to_owned(), key: String::new(), value: vec![], } } -pub fn set_tx(space: String, key: String, value: Vec) -> TransactionData { +pub fn set_tx(space: &str, key: &str, value: &str) -> TransactionData { TransactionData { typ: TransactionType::Set, - space, - key, - value, + space: space.to_owned(), + key: key.to_owned(), + value: value.as_bytes().to_vec(), } } -pub fn delete_tx(space: String, key: String) -> TransactionData { +pub fn delete_tx(space: &str, key: &str) -> TransactionData { TransactionData { typ: TransactionType::Delete, - space, - key, + space: space.to_owned(), + key: key.to_owned(), value: vec![], } } diff --git a/spacesvm/tests/vm/mod.rs b/spacesvm/tests/vm/mod.rs index 6397d59a..2ee2acd0 100644 --- a/spacesvm/tests/vm/mod.rs +++ b/spacesvm/tests/vm/mod.rs @@ -138,7 +138,7 @@ async fn test_api() { let body = std::str::from_utf8(&resp.body()).unwrap(); log::info!("ping response {}", body); - let tx_data = claim_tx("test_claim".to_owned()); + let tx_data = claim_tx("test_claim"); let arg_value = serde_json::to_value(&DecodeTxArgs { tx_data }).unwrap(); let (_id, json_str) = client.raw_request("decodeTx", &Params::Array(vec![arg_value])); diff --git a/tests/e2e/src/tests/mod.rs b/tests/e2e/src/tests/mod.rs index d781926a..dad76a58 100644 --- a/tests/e2e/src/tests/mod.rs +++ b/tests/e2e/src/tests/mod.rs @@ -9,10 +9,7 @@ use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRe use avalanche_types::subnet; use spacesvm::{ self, - api::{ - client::{claim_tx, get_or_create_pk, Uri}, - DecodeTxArgs, - }, + api::client::{claim_tx, get_or_create_pk, Uri}, }; #[tokio::test] @@ -199,9 +196,7 @@ async fn e2e() { log::info!("decode claim tx request..."); let resp = scli - .decode_tx(DecodeTxArgs { - tx_data: claim_tx("test".to_owned()), - }) + .decode_tx(claim_tx("test")) .await .expect("decodeTx success"); log::info!("decode claim response from {}: {:?}", ep, resp); From 478f099bd87b8ad3ff6a37df68ac0a5223e1c964 Mon Sep 17 00:00:00 2001 From: Sam Batschelet Date: Fri, 25 Nov 2022 09:31:49 -0500 Subject: [PATCH 3/8] bump deps Signed-off-by: Sam Batschelet --- spacesvm/Cargo.toml | 2 +- tests/e2e/Cargo.toml | 4 ++-- tests/e2e/src/tests/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spacesvm/Cargo.toml b/spacesvm/Cargo.toml index 71dcfb7e..e23b2cf8 100644 --- a/spacesvm/Cargo.toml +++ b/spacesvm/Cargo.toml @@ -24,7 +24,7 @@ dyn-clone = "1.0.9" ethereum-types = { version = "0.14.0" } clap = { version = "4.0.22", features = ["cargo", "derive"] } eip-712 = "0.1.0" -env_logger = "0.9.3" +env_logger = "0.10.0" hex = "0.4.3" http = "0.2.8" hyper = "0.14.23" diff --git a/tests/e2e/Cargo.toml b/tests/e2e/Cargo.toml index bbce91f8..b843cedf 100644 --- a/tests/e2e/Cargo.toml +++ b/tests/e2e/Cargo.toml @@ -13,8 +13,8 @@ homepage = "https://avax.network" [dev-dependencies] avalanche-installer = "0.0.8" avalanche-network-runner-sdk = "0.3.0" # https://crates.io/crates/avalanche-network-runner-sdk -avalanche-types = { version = "0.0.135", features = ["client", "subnet"] } # https://crates.io/crates/avalanche-types -env_logger = "0.9.1" +avalanche-types = { version = "0.0.140", features = ["client", "subnet"] } # https://crates.io/crates/avalanche-types +env_logger = "0.10.0" log = "0.4.17" random-manager = "0.0.1" serde_json = "1.0.87" # https://github.com/serde-rs/json/releases diff --git a/tests/e2e/src/tests/mod.rs b/tests/e2e/src/tests/mod.rs index dad76a58..3e9bbd68 100644 --- a/tests/e2e/src/tests/mod.rs +++ b/tests/e2e/src/tests/mod.rs @@ -50,7 +50,7 @@ async fn e2e() { // keep this in sync with "proto" crate // ref. https://github.com/ava-labs/avalanchego/blob/v1.9.2/version/constants.go#L15-L17 let (exec_path, plugins_dir) = - avalanche_installer::avalanchego::download(None, None, Some("v1.9.3".to_string())) + avalanche_installer::avalanchego::download(None, None, Some("v1.9.2".to_string())) .await .unwrap(); avalanchego_exec_path = exec_path; From a707fbd96aee4dd125c983b3022a0c6221473f4c Mon Sep 17 00:00:00 2001 From: Sam Batschelet Date: Fri, 25 Nov 2022 13:56:01 -0500 Subject: [PATCH 4/8] extend tests Signed-off-by: Sam Batschelet --- scripts/tests.e2e.sh | 8 ++-- spaces-cli/src/bin/spaces-cli/main.rs | 4 +- spacesvm/src/api/client.rs | 12 ++--- tests/e2e/src/tests/mod.rs | 68 ++++++++++++++++++++------- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/scripts/tests.e2e.sh b/scripts/tests.e2e.sh index 5c575c1b..73c61f37 100755 --- a/scripts/tests.e2e.sh +++ b/scripts/tests.e2e.sh @@ -1,17 +1,17 @@ #!/usr/bin/env bash set -e -# build spacesvm-rs binary +# build spacesvm binary # ./scripts/build.release.sh # # download from github, keep network running -# VM_PLUGIN_PATH=$(pwd)/target/release/spacesvm-rs ./scripts/tests.e2e.sh +# VM_PLUGIN_PATH=$(pwd)/target/release/spacesvm ./scripts/tests.e2e.sh # # download from github, shut down network -# NETWORK_RUNNER_ENABLE_SHUTDOWN=1 VM_PLUGIN_PATH=$(pwd)/target/release/spacesvm-rs ./scripts/tests.e2e.sh +# NETWORK_RUNNER_ENABLE_SHUTDOWN=1 VM_PLUGIN_PATH=$(pwd)/target/release/spacesvm ./scripts/tests.e2e.sh # # use custom avalanchego binary -# VM_PLUGIN_PATH=$(pwd)/target/release/timestampvm ./scripts/tests.e2e.sh ~/go/src/github.com/ava-labs/avalanchego/build/avalanchego +# VM_PLUGIN_PATH=$(pwd)/target/release/spacesvm ./scripts/tests.e2e.sh ~/go/src/github.com/ava-labs/avalanchego/build/avalanchego # if ! [[ "$0" =~ scripts/tests.e2e.sh ]]; then echo "must be run from repository root" diff --git a/spaces-cli/src/bin/spaces-cli/main.rs b/spaces-cli/src/bin/spaces-cli/main.rs index fccff812..067e3f07 100644 --- a/spaces-cli/src/bin/spaces-cli/main.rs +++ b/spaces-cli/src/bin/spaces-cli/main.rs @@ -43,8 +43,8 @@ struct Cli { #[command(subcommand)] command: Command, } - -fn main() -> Result<(), Box> { +#[tokio::main] +async fn main() -> Result<(), Box> { let cli = Cli::parse(); let private_key = get_or_create_pk(&cli.private_key_file)?; diff --git a/spacesvm/src/api/client.rs b/spacesvm/src/api/client.rs index 5fa06910..72376d3e 100644 --- a/spacesvm/src/api/client.rs +++ b/spacesvm/src/api/client.rs @@ -53,9 +53,8 @@ impl Client { Id::Num(id) } - pub fn set_endpoint(mut self, endpoint: Uri) -> Self { + pub fn set_endpoint(&mut self, endpoint: Uri) { self.endpoint = endpoint; - self } pub fn set_private_key(mut self, private_key: Key) -> Self { @@ -81,7 +80,7 @@ impl Client { /// Returns a recoverable signature from bytes. pub fn sign_digest(&self, dh: &[u8]) -> Result { if let Some(pk) = &self.private_key { - pk.sign_digest(dh)?; + return pk.sign_digest(dh); } Err(Error::new(ErrorKind::Other, "private key not set")) } @@ -125,7 +124,7 @@ impl Client { space: space.as_bytes().to_vec(), key: key.as_bytes().to_vec(), })?; - let (_id, json_request) = self.raw_request("issueTx", &Params::Array(vec![arg_value])); + let (_id, json_request) = self.raw_request("resolve", &Params::Array(vec![arg_value])); let resp = self.post_de::(&json_request).await?; Ok(resp) @@ -133,7 +132,6 @@ impl Client { /// Returns a deserialized response from client request. pub async fn post_de(&self, json: &str) -> Result { - println!("json: {}", json); let req = Request::builder() .method(Method::POST) .uri(self.endpoint.to_string()) @@ -221,8 +219,8 @@ pub fn get_or_create_pk(path: &str) -> Result .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) } -#[tokio::test] -async fn test_raw_request() { +#[test] +fn test_raw_request() { let mut cli = Client::new(Uri::from_static("http://test.url")); let (id, _) = cli.raw_request("ping", &Params::None); assert_eq!(id, jsonrpc_core::Id::Num(0)); diff --git a/tests/e2e/src/tests/mod.rs b/tests/e2e/src/tests/mod.rs index 3e9bbd68..e0390c3b 100644 --- a/tests/e2e/src/tests/mod.rs +++ b/tests/e2e/src/tests/mod.rs @@ -1,15 +1,16 @@ use std::{ fs, path::Path, + str::FromStr, thread, time::{Duration, Instant}, }; use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest}; -use avalanche_types::subnet; +use avalanche_types::{ids, subnet}; use spacesvm::{ self, - api::client::{claim_tx, get_or_create_pk, Uri}, + api::client::{claim_tx, get_or_create_pk, set_tx, Uri}, }; #[tokio::test] @@ -33,7 +34,7 @@ async fn e2e() { assert!(exists); assert!(Path::new(&vm_plugin_path).exists()); - let vm_id = subnet::vm_name_to_id("minikvvm").unwrap(); + let vm_id = subnet::vm_name_to_id("spacesvm").unwrap(); let (mut avalanchego_exec_path, _) = crate::get_avalanchego_path(); let plugins_dir = if !avalanchego_exec_path.is_empty() { @@ -50,7 +51,7 @@ async fn e2e() { // keep this in sync with "proto" crate // ref. https://github.com/ava-labs/avalanchego/blob/v1.9.2/version/constants.go#L15-L17 let (exec_path, plugins_dir) = - avalanche_installer::avalanchego::download(None, None, Some("v1.9.2".to_string())) + avalanche_installer::avalanchego::download(None, None, Some("v1.9.3".to_string())) .await .unwrap(); avalanchego_exec_path = exec_path; @@ -180,33 +181,68 @@ async fn e2e() { log::info!("{}: {}", node_name, iv.uri); rpc_eps.push(iv.uri.clone()); } + let mut blockchain_id = ids::Id::empty(); + for (k, v) in cluster_info.custom_chains.iter() { + log::info!("custom chain info: {}={:?}", k, v); + if v.chain_name == "spacesvm" { + blockchain_id = ids::Id::from_str(&v.chain_id).unwrap(); + break; + } + } + + log::info!("avalanchego RPC endpoints: {:?}", rpc_eps); - let ep = format!( - "{}/{}", - rpc_eps[0].to_owned(), - spacesvm::vm::PUBLIC_API_ENDPOINT - ); let private_key = get_or_create_pk("/tmp/.spacesvm-cli-pk").expect("generate new private key"); + let chain_url = format!("{}/ext/bc/{}/public", rpc_eps[0], blockchain_id); + let mut scli = + spacesvm::api::client::Client::new(chain_url.parse::().expect("valid endpoint")) + .set_private_key(private_key); + for ep in rpc_eps.iter() { + let chain_url = format!("{}/ext/bc/{}/public", ep, blockchain_id) + .parse::() + .expect("valid endpoint"); + scli.set_endpoint(chain_url); + let resp = scli.ping().await.unwrap(); + log::info!("ping response from {}: {:?}", ep, resp); + assert!(resp.success); + + thread::sleep(Duration::from_millis(300)); + } - let mut scli = spacesvm::api::client::Client::new(ep.parse::().expect("valid endpoint")) - .set_private_key(private_key); - log::info!("ping request..."); - let resp = scli.ping().await.expect("ping success"); - log::info!("ping response from {}: {:?}", ep, resp); + scli.set_endpoint(chain_url.parse::().expect("valid endpoint")); log::info!("decode claim tx request..."); let resp = scli .decode_tx(claim_tx("test")) .await .expect("decodeTx success"); - log::info!("decode claim response from {}: {:?}", ep, resp); + log::info!("decode claim response from {}: {:?}", chain_url, resp); log::info!("issue claim tx request..."); let resp = scli .issue_tx(&resp.typed_data) .await .expect("issue_tx success"); - log::info!("issue claim tx response from {}: {:?}", ep, resp); + log::info!("issue claim tx response from {}: {:?}", chain_url, resp); + + log::info!("decode set tx request..."); + let resp = scli + .decode_tx(set_tx("test", "foo", "bar")) + .await + .expect("decodeTx success"); + log::info!("decode set response from {}: {:?}", chain_url, resp); + + log::info!("issue set tx request..."); + let resp = scli + .issue_tx(&resp.typed_data) + .await + .expect("issue_tx success"); + log::info!("issue tx tx response from {}: {:?}", chain_url, resp); + + log::info!("issue resolve request..."); + let resp = scli.resolve("test", "foo").await.expect("resolve success"); + log::info!("resolve response from {}: {:?}", chain_url, resp); + assert_eq!(std::str::from_utf8(&resp.value).unwrap(), "bar"); if crate::get_network_runner_enable_shutdown() { log::info!("shutdown is enabled... stopping..."); From 9d5d3b90310230fbf826e5a05e1eb4fbd7b5b9e8 Mon Sep 17 00:00:00 2001 From: Sam Batschelet Date: Sun, 27 Nov 2022 22:22:27 -0500 Subject: [PATCH 5/8] async thread safe Signed-off-by: Sam Batschelet --- spaces-cli/src/bin/spaces-cli/main.rs | 17 ++++-- spacesvm/Cargo.toml | 2 +- spacesvm/src/api/client.rs | 87 ++++++++++++++++----------- spacesvm/tests/vm/mod.rs | 12 ++-- tests/e2e/src/tests/mod.rs | 11 ++-- 5 files changed, 77 insertions(+), 52 deletions(-) diff --git a/spaces-cli/src/bin/spaces-cli/main.rs b/spaces-cli/src/bin/spaces-cli/main.rs index 067e3f07..58f68856 100644 --- a/spaces-cli/src/bin/spaces-cli/main.rs +++ b/spaces-cli/src/bin/spaces-cli/main.rs @@ -49,11 +49,14 @@ async fn main() -> Result<(), Box> { let private_key = get_or_create_pk(&cli.private_key_file)?; let uri = cli.endpoint.parse::()?; - let mut client = Client::new(uri).set_private_key(private_key); + let client = Client::new(uri); + client.set_private_key(private_key).await; if let Command::Get { space, key } = &cli.command { - let resp = - futures::executor::block_on(client.resolve(space, key)).map_err(|e| e.to_string())?; + let resp = client + .resolve(space, key) + .await + .map_err(|e| e.to_string())?; log::debug!("resolve response: {:?}", resp); println!("{}", serde_json::to_string(&resp)?); @@ -61,7 +64,7 @@ async fn main() -> Result<(), Box> { } if let Command::Ping {} = &cli.command { - let resp = futures::executor::block_on(client.ping()).map_err(|e| e.to_string())?; + let resp = client.ping().await.map_err(|e| e.to_string())?; println!("{}", serde_json::to_string(&resp)?); return Ok(()); @@ -74,8 +77,10 @@ async fn main() -> Result<(), Box> { let typed_data = &resp.typed_data; // issue tx - let resp = - futures::executor::block_on(client.issue_tx(typed_data)).map_err(|e| e.to_string())?; + let resp = client + .issue_tx(typed_data) + .await + .map_err(|e| e.to_string())?; println!("{}", serde_json::to_string(&resp)?); Ok(()) diff --git a/spacesvm/Cargo.toml b/spacesvm/Cargo.toml index e23b2cf8..7300e1f3 100644 --- a/spacesvm/Cargo.toml +++ b/spacesvm/Cargo.toml @@ -15,7 +15,7 @@ name = "spacesvm" path = "src/bin/spaces/main.rs" [dependencies] -avalanche-types = { version = "0.0.140", features = ["subnet"] } +avalanche-types = { version = "0.0.143", features = ["subnet"] } byteorder = "1.4.3" chrono = "0.4.22" crossbeam-channel = "0.5.6" diff --git a/spacesvm/src/api/client.rs b/spacesvm/src/api/client.rs index 72376d3e..0cd35a0d 100644 --- a/spacesvm/src/api/client.rs +++ b/spacesvm/src/api/client.rs @@ -2,6 +2,7 @@ use std::{ fs::File, io::{Error, ErrorKind, Result, Write}, path::Path, + sync::Arc, }; use crate::{ @@ -25,9 +26,14 @@ use jsonrpc_core::{Call, Id, MethodCall, Params, Value, Version}; use serde::de; pub use http::Uri; +use tokio::sync::RwLock; -/// HTTP client for interacting with the API, assumes single threaded use. +/// HTTP client for interacting with the API. pub struct Client { + inner: Arc>>, +} + +pub struct ClientInner { id: u64, client: HyperClient, endpoint: Uri, @@ -38,33 +44,37 @@ impl Client { pub fn new(endpoint: Uri) -> Self { let client = HyperClient::new(); Self { - id: 0, - client, - endpoint, - private_key: None, + inner: Arc::new(RwLock::new(ClientInner { + id: 0, + client, + endpoint, + private_key: None, + })), } } } impl Client { - fn next_id(&mut self) -> Id { - let id = self.id; - self.id = id + 1; + async fn next_id(&self) -> Id { + let mut client = self.inner.write().await; + let id = client.id; + client.id = id + 1; Id::Num(id) } - pub fn set_endpoint(&mut self, endpoint: Uri) { - self.endpoint = endpoint; + pub async fn set_endpoint(&self, endpoint: Uri) { + let mut inner = self.inner.write().await; + inner.endpoint = endpoint; } - pub fn set_private_key(mut self, private_key: Key) -> Self { - self.private_key = Some(private_key); - self + pub async fn set_private_key(&self, private_key: Key) { + let mut inner = self.inner.write().await; + inner.private_key = Some(private_key); } /// Returns a serialized json request as string and the request id. - pub fn raw_request(&mut self, method: &str, params: &Params) -> (Id, String) { - let id = self.next_id(); + pub async fn raw_request(&self, method: &str, params: &Params) -> (Id, String) { + let id = self.next_id().await; let request = jsonrpc_core::Request::Single(Call::MethodCall(MethodCall { jsonrpc: Some(Version::V2), method: method.to_owned(), @@ -77,54 +87,61 @@ impl Client { ) } - /// Returns a recoverable signature from bytes. - pub fn sign_digest(&self, dh: &[u8]) -> Result { - if let Some(pk) = &self.private_key { + /// Returns a recoverable signature from 32 byte SHA256 message. + pub async fn sign_digest(&self, dh: &[u8]) -> Result { + let inner = self.inner.read().await; + if let Some(pk) = &inner.private_key { return pk.sign_digest(dh); } Err(Error::new(ErrorKind::Other, "private key not set")) } /// Returns a PingResponse from client request. - pub async fn ping(&mut self) -> Result { - let (_id, json_request) = self.raw_request("ping", &Params::None); + pub async fn ping(&self) -> Result { + let (_id, json_request) = self.raw_request("ping", &Params::None).await; let resp = self.post_de::(&json_request).await?; Ok(resp) } /// Returns a DecodeTxResponse from client request. - pub async fn decode_tx(&mut self, tx_data: TransactionData) -> Result { + pub async fn decode_tx(&self, tx_data: TransactionData) -> Result { let arg_value = serde_json::to_value(&DecodeTxArgs { tx_data })?; - let (_id, json_request) = self.raw_request("decodeTx", &Params::Array(vec![arg_value])); + let (_id, json_request) = self + .raw_request("decodeTx", &Params::Array(vec![arg_value])) + .await; let resp = self.post_de::(&json_request).await?; Ok(resp) } /// Returns a IssueTxResponse from client request. - pub async fn issue_tx(&mut self, typed_data: &TypedData) -> Result { + pub async fn issue_tx(&self, typed_data: &TypedData) -> Result { let dh = decoder::hash_structured_data(typed_data)?; - let sig = self.sign_digest(&dh.as_bytes())?.to_bytes().to_vec(); + let sig = self.sign_digest(&dh.as_bytes()).await?.to_bytes().to_vec(); log::debug!("signature: {:?}", sig); let arg_value = serde_json::to_value(&IssueTxArgs { typed_data: typed_data.to_owned(), signature: sig, })?; - let (_id, json_request) = self.raw_request("issueTx", &Params::Array(vec![arg_value])); + let (_id, json_request) = self + .raw_request("issueTx", &Params::Array(vec![arg_value])) + .await; let resp = self.post_de::(&json_request).await?; Ok(resp) } /// Returns a ResolveResponse from client request. - pub async fn resolve(&mut self, space: &str, key: &str) -> Result { + pub async fn resolve(&self, space: &str, key: &str) -> Result { let arg_value = serde_json::to_value(&ResolveArgs { space: space.as_bytes().to_vec(), key: key.as_bytes().to_vec(), })?; - let (_id, json_request) = self.raw_request("resolve", &Params::Array(vec![arg_value])); + let (_id, json_request) = self + .raw_request("resolve", &Params::Array(vec![arg_value])) + .await; let resp = self.post_de::(&json_request).await?; Ok(resp) @@ -132,9 +149,11 @@ impl Client { /// Returns a deserialized response from client request. pub async fn post_de(&self, json: &str) -> Result { + let inner = self.inner.read().await; + let req = Request::builder() .method(Method::POST) - .uri(self.endpoint.to_string()) + .uri(inner.endpoint.to_string()) .header("content-type", "application/json-rpc") .body(Body::from(json.to_owned())) .map_err(|e| { @@ -144,7 +163,7 @@ impl Client { ) })?; - let mut resp = self.client.request(req).await.map_err(|e| { + let mut resp = inner.client.request(req).await.map_err(|e| { std::io::Error::new( std::io::ErrorKind::Other, format!("client post request failed: {}", e), @@ -219,12 +238,12 @@ pub fn get_or_create_pk(path: &str) -> Result .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) } -#[test] -fn test_raw_request() { - let mut cli = Client::new(Uri::from_static("http://test.url")); - let (id, _) = cli.raw_request("ping", &Params::None); +#[tokio::test] +async fn test_raw_request() { + let cli = Client::new(Uri::from_static("http://test.url")); + let (id, _) = cli.raw_request("ping", &Params::None).await; assert_eq!(id, jsonrpc_core::Id::Num(0)); - let (id, req) = cli.raw_request("ping", &Params::None); + let (id, req) = cli.raw_request("ping", &Params::None).await; assert_eq!(id, jsonrpc_core::Id::Num(1)); assert_eq!( req, diff --git a/spacesvm/tests/vm/mod.rs b/spacesvm/tests/vm/mod.rs index 2ee2acd0..2c41ee24 100644 --- a/spacesvm/tests/vm/mod.rs +++ b/spacesvm/tests/vm/mod.rs @@ -26,10 +26,8 @@ async fn test_api() { // setup stop channel for grpc services. let (stop_ch_tx, stop_ch_rx): (Sender<()>, Receiver<()>) = tokio::sync::broadcast::channel(1); - let vm_server = avalanche_types::subnet::rpc::vm::server::Server::new( - Box::new(vm::ChainVm::new()), - stop_ch_tx, - ); + let vm_server = + avalanche_types::subnet::rpc::vm::server::Server::new(Box::new(vm::ChainVm::new()), stop_ch_tx); // start Vm service let vm_addr = utils::new_socket_addr(); @@ -115,7 +113,7 @@ async fn test_api() { let mut client = spacesvm::api::client::Client::new(http::Uri::from_static("http://test.url")); // ping - let (_id, json_str) = client.raw_request("ping", &Params::None); + let (_id, json_str) = client.raw_request("ping", &Params::None).await; let req = http::request::Builder::new() .body(json_str.as_bytes().to_vec()) .unwrap(); @@ -141,7 +139,9 @@ async fn test_api() { let tx_data = claim_tx("test_claim"); let arg_value = serde_json::to_value(&DecodeTxArgs { tx_data }).unwrap(); - let (_id, json_str) = client.raw_request("decodeTx", &Params::Array(vec![arg_value])); + let (_id, json_str) = client + .raw_request("decodeTx", &Params::Array(vec![arg_value])) + .await; log::info!("decodeTx request: {}", json_str); let req = http::request::Builder::new() .body(json_str.as_bytes().to_vec()) diff --git a/tests/e2e/src/tests/mod.rs b/tests/e2e/src/tests/mod.rs index e0390c3b..84af694a 100644 --- a/tests/e2e/src/tests/mod.rs +++ b/tests/e2e/src/tests/mod.rs @@ -194,14 +194,14 @@ async fn e2e() { let private_key = get_or_create_pk("/tmp/.spacesvm-cli-pk").expect("generate new private key"); let chain_url = format!("{}/ext/bc/{}/public", rpc_eps[0], blockchain_id); - let mut scli = - spacesvm::api::client::Client::new(chain_url.parse::().expect("valid endpoint")) - .set_private_key(private_key); + let scli = + spacesvm::api::client::Client::new(chain_url.parse::().expect("valid endpoint")); + scli.set_private_key(private_key).await; for ep in rpc_eps.iter() { let chain_url = format!("{}/ext/bc/{}/public", ep, blockchain_id) .parse::() .expect("valid endpoint"); - scli.set_endpoint(chain_url); + scli.set_endpoint(chain_url).await; let resp = scli.ping().await.unwrap(); log::info!("ping response from {}: {:?}", ep, resp); assert!(resp.success); @@ -209,7 +209,8 @@ async fn e2e() { thread::sleep(Duration::from_millis(300)); } - scli.set_endpoint(chain_url.parse::().expect("valid endpoint")); + scli.set_endpoint(chain_url.parse::().expect("valid endpoint")) + .await; log::info!("decode claim tx request..."); let resp = scli From b92aed3ba41b59d168082681add31af8a28f0c5d Mon Sep 17 00:00:00 2001 From: Sam Batschelet Date: Mon, 28 Nov 2022 09:30:20 -0500 Subject: [PATCH 6/8] nit Signed-off-by: Sam Batschelet --- spacesvm/tests/vm/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spacesvm/tests/vm/mod.rs b/spacesvm/tests/vm/mod.rs index 2c41ee24..c635a52b 100644 --- a/spacesvm/tests/vm/mod.rs +++ b/spacesvm/tests/vm/mod.rs @@ -26,8 +26,10 @@ async fn test_api() { // setup stop channel for grpc services. let (stop_ch_tx, stop_ch_rx): (Sender<()>, Receiver<()>) = tokio::sync::broadcast::channel(1); - let vm_server = - avalanche_types::subnet::rpc::vm::server::Server::new(Box::new(vm::ChainVm::new()), stop_ch_tx); + let vm_server = avalanche_types::subnet::rpc::vm::server::Server::new( + Box::new(vm::ChainVm::new()), + stop_ch_tx, + ); // start Vm service let vm_addr = utils::new_socket_addr(); From 3d137e5ee8e63e515ef70483832701363df99b51 Mon Sep 17 00:00:00 2001 From: Sam Batschelet Date: Mon, 28 Nov 2022 10:34:17 -0500 Subject: [PATCH 7/8] bump deps Signed-off-by: Sam Batschelet --- spaces-cli/Cargo.toml | 6 +++--- spacesvm/Cargo.toml | 16 ++++++++-------- spacesvm/src/bin/spaces/main.rs | 2 +- spacesvm/tests/vm/mod.rs | 8 +++----- tests/e2e/Cargo.toml | 4 ++-- 5 files changed, 17 insertions(+), 19 deletions(-) diff --git a/spaces-cli/Cargo.toml b/spaces-cli/Cargo.toml index 8b0dbe03..688f1837 100644 --- a/spaces-cli/Cargo.toml +++ b/spaces-cli/Cargo.toml @@ -17,7 +17,7 @@ clap = { version = "4.0", features = ["derive"] } hex = "0.4.3" jsonrpc-core = "18.0.0" log = "0.4.17" -serde = { version = "1.0.147", features = ["derive"] } -serde_json = "1.0.87" +serde = { version = "1.0.148", features = ["derive"] } +serde_json = "1.0.89" spacesvm = { path = "../spacesvm" } -tokio = { version = "1.22.0", features = ["full"] } +tokio = { version = "1.22.0", features = [] } diff --git a/spacesvm/Cargo.toml b/spacesvm/Cargo.toml index 7300e1f3..a2c4eee7 100644 --- a/spacesvm/Cargo.toml +++ b/spacesvm/Cargo.toml @@ -15,14 +15,14 @@ name = "spacesvm" path = "src/bin/spaces/main.rs" [dependencies] -avalanche-types = { version = "0.0.143", features = ["subnet"] } +avalanche-types = { version = "0.0.144", features = ["subnet"] } byteorder = "1.4.3" -chrono = "0.4.22" +chrono = "0.4.23" crossbeam-channel = "0.5.6" derivative = "2.2.0" dyn-clone = "1.0.9" ethereum-types = { version = "0.14.0" } -clap = { version = "4.0.22", features = ["cargo", "derive"] } +clap = { version = "4.0.27", features = ["cargo", "derive"] } eip-712 = "0.1.0" env_logger = "0.10.0" hex = "0.4.3" @@ -32,15 +32,15 @@ jsonrpc-core = "18.0.0" jsonrpc-core-client = { version = "18.0.0" } jsonrpc-derive = "18.0" log = "0.4.17" -lru = "0.8.0" +lru = "0.8.1" prost = "0.11.2" ripemd = "0.1.3" semver = "1.0.14" -serde = { version = "1.0.147", features = ["derive"] } -serde_json = "1.0.87" +serde = { version = "1.0.148", features = ["derive"] } +serde_json = "1.0.89" serde_yaml = "0.9.14" sha3 = "0.10.6" -tokio = { version = "1.21.2", features = ["fs", "rt-multi-thread"] } +tokio = { version = "1.22.0", features = ["fs", "rt-multi-thread"] } tokio-stream = { version = "0.1.11", features = ["net"] } tonic = { version = "0.8.2", features = ["gzip"] } tonic-health = "0.7" @@ -48,7 +48,7 @@ typetag = "0.2" [dev-dependencies] jsonrpc-tcp-server = "18.0.0" -futures-test = "0.3.24" +futures-test = "0.3.25" [[test]] name = "integration" diff --git a/spacesvm/src/bin/spaces/main.rs b/spacesvm/src/bin/spaces/main.rs index b784c814..125d4741 100644 --- a/spacesvm/src/bin/spaces/main.rs +++ b/spacesvm/src/bin/spaces/main.rs @@ -45,7 +45,7 @@ async fn main() -> Result<()> { ) = tokio::sync::broadcast::channel(1); info!("starting spacesvm-rs"); - let vm_server = subnet::rpc::vm::server::Server::new(Box::new(vm::ChainVm::new()), stop_ch_tx); + let vm_server = subnet::rpc::vm::server::Server::new(vm::ChainVm::new(), stop_ch_tx); subnet::rpc::plugin::serve(vm_server, stop_ch_rx) .await diff --git a/spacesvm/tests/vm/mod.rs b/spacesvm/tests/vm/mod.rs index c635a52b..003888eb 100644 --- a/spacesvm/tests/vm/mod.rs +++ b/spacesvm/tests/vm/mod.rs @@ -26,10 +26,8 @@ async fn test_api() { // setup stop channel for grpc services. let (stop_ch_tx, stop_ch_rx): (Sender<()>, Receiver<()>) = tokio::sync::broadcast::channel(1); - let vm_server = avalanche_types::subnet::rpc::vm::server::Server::new( - Box::new(vm::ChainVm::new()), - stop_ch_tx, - ); + let vm_server = + avalanche_types::subnet::rpc::vm::server::Server::new(vm::ChainVm::new(), stop_ch_tx); // start Vm service let vm_addr = utils::new_socket_addr(); @@ -112,7 +110,7 @@ async fn test_api() { .await .unwrap(); - let mut client = spacesvm::api::client::Client::new(http::Uri::from_static("http://test.url")); + let client = spacesvm::api::client::Client::new(http::Uri::from_static("http://test.url")); // ping let (_id, json_str) = client.raw_request("ping", &Params::None).await; diff --git a/tests/e2e/Cargo.toml b/tests/e2e/Cargo.toml index b843cedf..1ddf9cfb 100644 --- a/tests/e2e/Cargo.toml +++ b/tests/e2e/Cargo.toml @@ -13,11 +13,11 @@ homepage = "https://avax.network" [dev-dependencies] avalanche-installer = "0.0.8" avalanche-network-runner-sdk = "0.3.0" # https://crates.io/crates/avalanche-network-runner-sdk -avalanche-types = { version = "0.0.140", features = ["client", "subnet"] } # https://crates.io/crates/avalanche-types +avalanche-types = { version = "0.0.144", features = ["client", "subnet"] } # https://crates.io/crates/avalanche-types env_logger = "0.10.0" log = "0.4.17" random-manager = "0.0.1" serde_json = "1.0.87" # https://github.com/serde-rs/json/releases tempfile = "3.3.0" spacesvm = { path = "../../spacesvm" } -tokio = { version = "1.21.2", features = [] } # https://github.com/tokio-rs/tokio/releases \ No newline at end of file +tokio = { version = "1.22.0", features = [] } # https://github.com/tokio-rs/tokio/releases \ No newline at end of file From f56978e9a53bf3453bdd402a6db6252a406ed457 Mon Sep 17 00:00:00 2001 From: Sam Batschelet Date: Mon, 28 Nov 2022 11:29:15 -0500 Subject: [PATCH 8/8] nits Signed-off-by: Sam Batschelet --- spacesvm/src/api/client.rs | 28 ++++++++++++++++------------ spacesvm/tests/vm/mod.rs | 8 ++++++-- tests/e2e/src/tests/mod.rs | 6 +++--- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/spacesvm/src/api/client.rs b/spacesvm/src/api/client.rs index 0cd35a0d..d4c402ee 100644 --- a/spacesvm/src/api/client.rs +++ b/spacesvm/src/api/client.rs @@ -28,7 +28,7 @@ use serde::de; pub use http::Uri; use tokio::sync::RwLock; -/// HTTP client for interacting with the API. +/// Thread safe HTTP client for interacting with the API. pub struct Client { inner: Arc>>, } @@ -73,7 +73,7 @@ impl Client { } /// Returns a serialized json request as string and the request id. - pub async fn raw_request(&self, method: &str, params: &Params) -> (Id, String) { + pub async fn raw_request(&self, method: &str, params: &Params) -> Result<(Id, String)> { let id = self.next_id().await; let request = jsonrpc_core::Request::Single(Call::MethodCall(MethodCall { jsonrpc: Some(Version::V2), @@ -81,10 +81,14 @@ impl Client { params: params.to_owned(), id: id.clone(), })); - ( - id, - serde_json::to_string(&request).expect("jsonrpc request should be serializable"), - ) + let request = serde_json::to_string(&request).map_err(|e| { + Error::new( + ErrorKind::Other, + format!("jsonrpc request should be serializable: {}", e), + ) + })?; + + Ok((id, request)) } /// Returns a recoverable signature from 32 byte SHA256 message. @@ -98,7 +102,7 @@ impl Client { /// Returns a PingResponse from client request. pub async fn ping(&self) -> Result { - let (_id, json_request) = self.raw_request("ping", &Params::None).await; + let (_id, json_request) = self.raw_request("ping", &Params::None).await?; let resp = self.post_de::(&json_request).await?; Ok(resp) @@ -109,7 +113,7 @@ impl Client { let arg_value = serde_json::to_value(&DecodeTxArgs { tx_data })?; let (_id, json_request) = self .raw_request("decodeTx", &Params::Array(vec![arg_value])) - .await; + .await?; let resp = self.post_de::(&json_request).await?; Ok(resp) @@ -127,7 +131,7 @@ impl Client { })?; let (_id, json_request) = self .raw_request("issueTx", &Params::Array(vec![arg_value])) - .await; + .await?; let resp = self.post_de::(&json_request).await?; Ok(resp) @@ -141,7 +145,7 @@ impl Client { })?; let (_id, json_request) = self .raw_request("resolve", &Params::Array(vec![arg_value])) - .await; + .await?; let resp = self.post_de::(&json_request).await?; Ok(resp) @@ -241,9 +245,9 @@ pub fn get_or_create_pk(path: &str) -> Result #[tokio::test] async fn test_raw_request() { let cli = Client::new(Uri::from_static("http://test.url")); - let (id, _) = cli.raw_request("ping", &Params::None).await; + let (id, _) = cli.raw_request("ping", &Params::None).await.unwrap(); assert_eq!(id, jsonrpc_core::Id::Num(0)); - let (id, req) = cli.raw_request("ping", &Params::None).await; + let (id, req) = cli.raw_request("ping", &Params::None).await.unwrap(); assert_eq!(id, jsonrpc_core::Id::Num(1)); assert_eq!( req, diff --git a/spacesvm/tests/vm/mod.rs b/spacesvm/tests/vm/mod.rs index 003888eb..ecbc201d 100644 --- a/spacesvm/tests/vm/mod.rs +++ b/spacesvm/tests/vm/mod.rs @@ -113,7 +113,10 @@ async fn test_api() { let client = spacesvm::api::client::Client::new(http::Uri::from_static("http://test.url")); // ping - let (_id, json_str) = client.raw_request("ping", &Params::None).await; + let (_id, json_str) = client + .raw_request("ping", &Params::None) + .await + .expect("raw_request success"); let req = http::request::Builder::new() .body(json_str.as_bytes().to_vec()) .unwrap(); @@ -141,7 +144,8 @@ async fn test_api() { let (_id, json_str) = client .raw_request("decodeTx", &Params::Array(vec![arg_value])) - .await; + .await + .expect("raw_request success"); log::info!("decodeTx request: {}", json_str); let req = http::request::Builder::new() .body(json_str.as_bytes().to_vec()) diff --git a/tests/e2e/src/tests/mod.rs b/tests/e2e/src/tests/mod.rs index 84af694a..403d6ff3 100644 --- a/tests/e2e/src/tests/mod.rs +++ b/tests/e2e/src/tests/mod.rs @@ -217,7 +217,7 @@ async fn e2e() { .decode_tx(claim_tx("test")) .await .expect("decodeTx success"); - log::info!("decode claim response from {}: {:?}", chain_url, resp); + log::info!("decode claim tx response from {}: {:?}", chain_url, resp); log::info!("issue claim tx request..."); let resp = scli @@ -231,14 +231,14 @@ async fn e2e() { .decode_tx(set_tx("test", "foo", "bar")) .await .expect("decodeTx success"); - log::info!("decode set response from {}: {:?}", chain_url, resp); + log::info!("decode set tx response from {}: {:?}", chain_url, resp); log::info!("issue set tx request..."); let resp = scli .issue_tx(&resp.typed_data) .await .expect("issue_tx success"); - log::info!("issue tx tx response from {}: {:?}", chain_url, resp); + log::info!("issue set tx response from {}: {:?}", chain_url, resp); log::info!("issue resolve request..."); let resp = scli.resolve("test", "foo").await.expect("resolve success");