diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index df6b4b9a9f..8062a677dc 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -26,12 +26,11 @@ jobs: mkdir -p ~/.local/bin mv substrate ~/.local/bin - - name: Install Rust nightly toolchain + - name: Install Rust stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly - target: wasm32-unknown-unknown + toolchain: stable override: true - name: Rust Cache diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index d9e687c69d..73078643f0 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,12 +32,11 @@ jobs: mkdir -p ~/.local/bin mv substrate ~/.local/bin - - name: Install Rust nightly toolchain + - name: Install Rust stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2021-12-15 - target: wasm32-unknown-unknown + toolchain: stable override: true - name: Rust Cache @@ -60,7 +59,7 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2021-12-15 + toolchain: nightly override: true components: rustfmt @@ -114,12 +113,11 @@ jobs: mkdir -p ~/.local/bin mv substrate ~/.local/bin - - name: Install Rust nightly toolchain + - name: Install Rust stable toolchain uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: nightly-2021-12-15 - target: wasm32-unknown-unknown + toolchain: stable override: true - name: Rust Cache diff --git a/Cargo.toml b/Cargo.toml index 0d212a1cd3..84243dd992 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,3 @@ members = [ "subxt", "test-runtime", ] -exclude = ["client"] diff --git a/client/Cargo.toml b/client/Cargo.toml deleted file mode 100644 index 3a45c21cf1..0000000000 --- a/client/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "subxt-client" -version = "0.1.0" -authors = [ - "David Craven ", - "Parity Technologies ", -] -edition = "2018" - -license = "GPL-3.0" -repository = "https://github.com/paritytech/substrate-subxt" -documentation = "https://docs.rs/substrate-subxt-client" -homepage = "https://www.parity.io/" -description = "Embed a substrate node into your subxt application." -keywords = ["parity", "substrate", "blockchain"] - -[dependencies] -async-std = { version = "1.8.0", features = ["tokio1"] } -codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive", "full", "bit-vec"] } -futures = "0.3.9" -jsonrpsee = { version = "0.8.0", features = ["async-client"] } -log = "0.4.13" -serde_json = "1" -subxt = { path = "../subxt" } -thiserror = "1.0.23" - -sc-client-db = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0" } -sp-keyring = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0" } -sc-network = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0" } -sc-service = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0" } - -tokio = { version = "1.16", features = ["rt-multi-thread"] } - -[target.'cfg(target_arch="x86_64")'.dependencies] -sc-service = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0", features = ["wasmtime"] } - -[dev-dependencies] -async-std = { version = "1.8.0", features = ["attributes"] } -env_logger = "0.9" -tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } -node-cli = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0", default-features = false } -tempdir = "0.3.7" - -[patch.crates-io] -sp-core = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0" } -sp-runtime = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0" } -sp-version = { git = "https://github.com/paritytech/substrate.git", tag = "publish-sp-version-v4.0.0" } diff --git a/client/metadata.scale b/client/metadata.scale deleted file mode 100644 index f3b1d45b56..0000000000 Binary files a/client/metadata.scale and /dev/null differ diff --git a/client/src/lib.rs b/client/src/lib.rs deleted file mode 100644 index 868ddbd6aa..0000000000 --- a/client/src/lib.rs +++ /dev/null @@ -1,297 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of subxt. -// -// subxt is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// subxt is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with subxt. If not, see . - -//! Client for embedding substrate nodes. - -#[cfg(test)] -mod tests; - -#[subxt::subxt(runtime_metadata_path = "metadata.scale")] -pub mod substrate {} - -use async_std::task; -use futures::{ - channel::mpsc, - future::{ - select, - FutureExt, - }, - sink::SinkExt, - stream::StreamExt, -}; -use jsonrpsee::core::{ - async_trait, - client::{ - Client as JsonRpcClient, - ClientBuilder as JsonRpcClientBuilder, - TransportReceiverT, - TransportSenderT, - }, -}; -use sc_network::config::TransportConfig; -pub use sc_service::{ - config::{ - DatabaseSource, - KeystoreConfig, - WasmExecutionMethod, - }, - Error as ServiceError, -}; -use sc_service::{ - config::{ - NetworkConfiguration, - TelemetryEndpoints, - }, - ChainSpec, - Configuration, - KeepBlocks, - RpcHandlers, - RpcSession, - TaskManager, -}; -use thiserror::Error; - -/// Error thrown by the client. -#[derive(Debug, Error)] -pub enum SubxtClientError { - /// Failed to parse json rpc message. - #[error("{0}")] - Json(#[from] serde_json::Error), - /// Channel closed. - #[error("{0}")] - Mpsc(#[from] mpsc::SendError), -} - -/// Sending end. -pub struct Sender(mpsc::UnboundedSender); - -/// Receiving end -pub struct Receiver(mpsc::UnboundedReceiver); - -#[async_trait] -impl TransportSenderT for Sender { - type Error = SubxtClientError; - - async fn send(&mut self, msg: String) -> Result<(), Self::Error> { - log::info!("send: {:?}", msg); - self.0.send(msg).await?; - Ok(()) - } -} - -#[async_trait] -impl TransportReceiverT for Receiver { - type Error = SubxtClientError; - - async fn receive(&mut self) -> Result { - let msg = self.0.next().await.expect("channel should be open"); - log::info!("rx: {:?}", msg); - Ok(msg) - } -} - -/// Client for an embedded substrate node. -pub struct SubxtClient { - sender: Sender, - receiver: Receiver, -} - -impl SubxtClient { - /// Create a new client. - pub fn new(mut task_manager: TaskManager, rpc: RpcHandlers) -> Self { - let (to_back, from_front) = mpsc::unbounded(); - let (to_front, from_back) = mpsc::unbounded(); - - let session = RpcSession::new(to_front.clone()); - task::spawn( - select( - Box::pin(from_front.for_each(move |message: String| { - let rpc = rpc.clone(); - let session = session.clone(); - let mut to_front = to_front.clone(); - async move { - let response = rpc.rpc_query(&session, &message).await; - if let Some(response) = response { - to_front.send(response).await.ok(); - } - } - })), - Box::pin(async move { - task_manager.future().await.ok(); - }), - ) - .map(drop), - ); - - Self { - sender: Sender(to_back), - receiver: Receiver(from_back), - } - } - - /// Creates a new client from a config. - pub fn from_config( - config: SubxtClientConfig, - builder: impl Fn(Configuration) -> Result<(TaskManager, RpcHandlers), ServiceError>, - ) -> Result { - let config = config.into_service_config(); - let (task_manager, rpc_handlers) = (builder)(config)?; - Ok(Self::new(task_manager, rpc_handlers)) - } -} - -impl From for JsonRpcClient { - fn from(client: SubxtClient) -> Self { - JsonRpcClientBuilder::default() - .request_timeout(std::time::Duration::from_secs(5 * 60)) - .build(client.sender, client.receiver) - } -} - -/// Role of the node. -#[derive(Clone, Copy, Debug)] -pub enum Role { - /// Light client. - Light, - /// A full node (mainly used for testing purposes). - Authority(sp_keyring::AccountKeyring), -} - -impl From for sc_service::Role { - fn from(role: Role) -> Self { - match role { - Role::Light => Self::Light, - Role::Authority(_) => Self::Authority, - } - } -} - -impl From for Option { - fn from(role: Role) -> Self { - match role { - Role::Light => None, - Role::Authority(key) => Some(key.to_seed()), - } - } -} - -/// Client configuration. -#[derive(Clone)] -pub struct SubxtClientConfig { - /// Name of the implementation. - pub impl_name: &'static str, - /// Version of the implementation. - pub impl_version: &'static str, - /// Author of the implementation. - pub author: &'static str, - /// Copyright start year. - pub copyright_start_year: i32, - /// Database configuration. - pub db: DatabaseSource, - /// Keystore configuration. - pub keystore: KeystoreConfig, - /// Chain specification. - pub chain_spec: C, - /// Role of the node. - pub role: Role, - /// Enable telemetry on the given port. - pub telemetry: Option, - /// Wasm execution method - pub wasm_method: WasmExecutionMethod, - /// Handle to the tokio runtime. Will be used to spawn futures by the task manager. - pub tokio_handle: tokio::runtime::Handle, -} - -impl SubxtClientConfig { - /// Creates a service configuration. - pub fn into_service_config(self) -> Configuration { - let mut network = NetworkConfiguration::new( - format!("{} (subxt client)", self.chain_spec.name()), - "unknown", - Default::default(), - None, - ); - network.boot_nodes = self.chain_spec.boot_nodes().to_vec(); - network.transport = TransportConfig::Normal { - enable_mdns: true, - allow_private_ipv4: true, - }; - let telemetry_endpoints = if let Some(port) = self.telemetry { - let endpoints = TelemetryEndpoints::new(vec![( - format!("/ip4/127.0.0.1/tcp/{}/ws", port), - 0, - )]) - .expect("valid config; qed"); - Some(endpoints) - } else { - None - }; - let service_config = Configuration { - network, - impl_name: self.impl_name.to_string(), - impl_version: self.impl_version.to_string(), - chain_spec: Box::new(self.chain_spec), - role: self.role.into(), - database: self.db, - keystore: self.keystore, - max_runtime_instances: 8, - announce_block: true, - dev_key_seed: self.role.into(), - telemetry_endpoints, - tokio_handle: self.tokio_handle, - default_heap_pages: Default::default(), - disable_grandpa: true, - execution_strategies: Default::default(), - force_authoring: true, - keep_blocks: KeepBlocks::All, - keystore_remote: Default::default(), - offchain_worker: Default::default(), - prometheus_config: Default::default(), - rpc_cors: Default::default(), - rpc_http: Default::default(), - rpc_ipc: Default::default(), - rpc_ws: Default::default(), - rpc_ws_max_connections: Default::default(), - rpc_methods: Default::default(), - rpc_max_payload: Default::default(), - runtime_cache_size: Default::default(), - state_cache_child_ratio: Default::default(), - state_cache_size: Default::default(), - tracing_receiver: Default::default(), - tracing_targets: Default::default(), - transaction_pool: Default::default(), - wasm_method: self.wasm_method, - base_path: Default::default(), - informant_output_format: Default::default(), - state_pruning: Default::default(), - transaction_storage: sc_client_db::TransactionStorageMode::BlockBody, - wasm_runtime_overrides: Default::default(), - ws_max_out_buffer_capacity: Default::default(), - }; - - log::info!("{}", service_config.impl_name); - log::info!("✌️ version {}", service_config.impl_version); - log::info!("❤️ by {}, {}", self.author, self.copyright_start_year); - log::info!( - "📋 Chain specification: {}", - service_config.chain_spec.name() - ); - log::info!("🏷 Node name: {}", service_config.network.node_name); - log::info!("👤 Role: {:?}", self.role); - - service_config - } -} diff --git a/client/src/tests.rs b/client/src/tests.rs deleted file mode 100644 index f96bd0e984..0000000000 --- a/client/src/tests.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of subxt. -// -// subxt is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// subxt is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with subxt. If not, see . - -use crate::{ - substrate, - DatabaseSource, - KeystoreConfig, - Role, - SubxtClient, - SubxtClientConfig, - WasmExecutionMethod, -}; -use node_cli::service::NewFullBase; -use sp_keyring::AccountKeyring; -use subxt::{ - ClientBuilder, - DefaultConfig, - DefaultExtra, - PairSigner, -}; -use tempdir::TempDir; - -#[async_std::test] -pub async fn test_embedded_client() { - tracing_subscriber::FmtSubscriber::builder() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .try_init() - .expect("setting default subscriber failed"); - - let tmp = TempDir::new("subxt-").expect("failed to create tempdir"); - let config = SubxtClientConfig { - impl_name: "full-client", - impl_version: "0.0.1", - author: "", - copyright_start_year: 2020, - db: DatabaseSource::RocksDb { - path: tmp.path().join("db"), - cache_size: 16, - }, - keystore: KeystoreConfig::Path { - path: tmp.path().join("keystore"), - password: None, - }, - chain_spec: node_cli::chain_spec::development_config(), - role: Role::Authority(AccountKeyring::Alice), - telemetry: None, - wasm_method: WasmExecutionMethod::Interpreted, - tokio_handle: tokio::runtime::Handle::current(), - }; - - let NewFullBase { - task_manager, - rpc_handlers, - .. - } = node_cli::service::new_full_base(config.into_service_config(), |_, _| ()) - .unwrap(); - - let client = SubxtClient::new(task_manager, rpc_handlers); - - let ext_client = ClientBuilder::new() - .set_client(client) - .build() - .await - .unwrap(); - - let api: substrate::RuntimeApi> = - ext_client.clone().to_runtime_api(); - - // verify that we can read storage - api.storage() - .system() - .account(AccountKeyring::Alice.to_account_id().into(), None) - .await - .unwrap(); - - let alice = PairSigner::new(AccountKeyring::Alice.pair()); - let bob_address = AccountKeyring::Bob.to_account_id().into(); - - // verify that we can call dispatchable functions - let success = api - .tx() - .balances() - .transfer(bob_address, 100_000) - .sign_and_submit_then_watch(&alice) - .await - .unwrap() - .wait_for_finalized_success() - .await - .unwrap() - .has_event::() - .unwrap(); - - // verify that we receive events - assert!(success); -} diff --git a/subxt/Cargo.toml b/subxt/Cargo.toml index a73deed4ce..97f5652fe0 100644 --- a/subxt/Cargo.toml +++ b/subxt/Cargo.toml @@ -21,7 +21,7 @@ chameleon = "0.1.0" scale-info = { version = "1.0.0", features = ["bit-vec"] } futures = "0.3.13" hex = "0.4.3" -jsonrpsee = { version = "0.8.0", features = ["macros", "async-client", "client-ws-transport"] } +jsonrpsee = { version = "0.8.0", features = ["async-client", "client-ws-transport"] } log = "0.4.14" num-traits = { version = "0.2.14", default-features = false } serde = { version = "1.0.124", features = ["derive"] }