mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 13:31:10 +00:00
remove embedded client
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,4 +8,3 @@ members = [
|
||||
"subxt",
|
||||
"test-runtime",
|
||||
]
|
||||
exclude = ["client"]
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
[package]
|
||||
name = "subxt-client"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"David Craven <david@craven.ch>",
|
||||
"Parity Technologies <admin@parity.io>",
|
||||
]
|
||||
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" }
|
||||
Binary file not shown.
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! 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<String>);
|
||||
|
||||
/// Receiving end
|
||||
pub struct Receiver(mpsc::UnboundedReceiver<String>);
|
||||
|
||||
#[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<String, Self::Error> {
|
||||
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<C: ChainSpec + 'static>(
|
||||
config: SubxtClientConfig<C>,
|
||||
builder: impl Fn(Configuration) -> Result<(TaskManager, RpcHandlers), ServiceError>,
|
||||
) -> Result<Self, ServiceError> {
|
||||
let config = config.into_service_config();
|
||||
let (task_manager, rpc_handlers) = (builder)(config)?;
|
||||
Ok(Self::new(task_manager, rpc_handlers))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SubxtClient> 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<Role> for sc_service::Role {
|
||||
fn from(role: Role) -> Self {
|
||||
match role {
|
||||
Role::Light => Self::Light,
|
||||
Role::Authority(_) => Self::Authority,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Role> for Option<String> {
|
||||
fn from(role: Role) -> Self {
|
||||
match role {
|
||||
Role::Light => None,
|
||||
Role::Authority(key) => Some(key.to_seed()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Client configuration.
|
||||
#[derive(Clone)]
|
||||
pub struct SubxtClientConfig<C: ChainSpec + 'static> {
|
||||
/// 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<u16>,
|
||||
/// 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<C: ChainSpec + 'static> SubxtClientConfig<C> {
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<DefaultConfig, DefaultExtra<_>> =
|
||||
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::<substrate::system::events::ExtrinsicSuccess>()
|
||||
.unwrap();
|
||||
|
||||
// verify that we receive events
|
||||
assert!(success);
|
||||
}
|
||||
+1
-1
@@ -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"] }
|
||||
|
||||
Reference in New Issue
Block a user