Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to set custom metadata etc on OnlineClient #794

Merged
merged 4 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions subxt/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ pub use online_client::{
Update,
UpgradeError,
};

#[cfg(any(
feature = "jsonrpsee-ws",
all(feature = "jsonrpsee-web", target_arch = "wasm32")
))]
pub use online_client::default_rpc_client;
86 changes: 77 additions & 9 deletions subxt/src/client/online_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ impl<T: Config> std::fmt::Debug for OnlineClient<T> {
}
}

/// The default RPC client that's used (based on [`jsonrpsee`]).
#[cfg(any(
feature = "jsonrpsee-ws",
all(feature = "jsonrpsee-web", target_arch = "wasm32")
))]
pub async fn default_rpc_client<U: AsRef<str>>(url: U) -> Result<impl RpcClientT, Error> {
let client = jsonrpsee_helpers::client(url.as_ref())
.await
.map_err(|e| crate::error::RpcError::ClientError(Box::new(e)))?;
Ok(client)
}

// The default constructors assume Jsonrpsee.
#[cfg(any(
feature = "jsonrpsee-ws",
Expand All @@ -77,9 +89,7 @@ impl<T: Config> OnlineClient<T> {

/// Construct a new [`OnlineClient`], providing a URL to connect to.
pub async fn from_url(url: impl AsRef<str>) -> Result<OnlineClient<T>, Error> {
let client = jsonrpsee_helpers::client(url.as_ref())
.await
.map_err(|e| crate::error::RpcError::ClientError(Box::new(e)))?;
let client = default_rpc_client(url).await?;
OnlineClient::from_rpc_client(Arc::new(client)).await
}
}
Expand All @@ -90,22 +100,47 @@ impl<T: Config> OnlineClient<T> {
pub async fn from_rpc_client<R: RpcClientT>(
rpc_client: Arc<R>,
) -> Result<OnlineClient<T>, Error> {
let rpc = Rpc::new(rpc_client);

let rpc = Rpc::<T>::new(rpc_client.clone());
let (genesis_hash, runtime_version, metadata) = future::join3(
rpc.genesis_hash(),
rpc.runtime_version(None),
rpc.metadata(None),
)
.await;

OnlineClient::from_rpc_client_with(
genesis_hash?,
runtime_version?,
metadata?,
rpc_client,
)
}

/// Construct a new [`OnlineClient`] by providing all of the underlying details needed
/// to make it work.
///
/// # Warning
///
/// This is considered the most primitive and also error prone way to
/// instantiate a client; the genesis hash, metadata and runtime version provided will
/// entirely determine which node and blocks this client will be able to interact with,
/// and whether it will be able to successfully do things like submit transactions.
///
/// If you're unsure what you're doing, prefer one of the alternate methods to instantiate
/// a client.
pub fn from_rpc_client_with<R: RpcClientT>(
genesis_hash: T::Hash,
runtime_version: RuntimeVersion,
metadata: Metadata,
rpc_client: Arc<R>,
) -> Result<OnlineClient<T>, Error> {
Ok(OnlineClient {
inner: Arc::new(RwLock::new(Inner {
genesis_hash: genesis_hash?,
runtime_version: runtime_version?,
metadata: metadata?,
genesis_hash,
runtime_version,
metadata,
})),
rpc,
rpc: Rpc::new(rpc_client),
})
}

Expand Down Expand Up @@ -160,18 +195,51 @@ impl<T: Config> OnlineClient<T> {
inner.metadata.clone()
}

/// Change the [`Metadata`] used in this client.
///
/// # Warning
///
/// Setting custom metadata may leave Subxt unable to work with certain blocks,
/// subscribe to latest blocks or submit valid transactions.
pub fn set_metadata(&self, metadata: Metadata) {
let mut inner = self.inner.write();
inner.metadata = metadata;
}

/// Return the genesis hash.
pub fn genesis_hash(&self) -> T::Hash {
let inner = self.inner.read();
inner.genesis_hash
}

/// Change the genesis hash used in this client.
///
/// # Warning
///
/// Setting a custom genesis hash may leave Subxt unable to
/// submit valid transactions.
pub fn set_genesis_hash(&self, genesis_hash: T::Hash) {
let mut inner = self.inner.write();
inner.genesis_hash = genesis_hash;
}

/// Return the runtime version.
pub fn runtime_version(&self) -> RuntimeVersion {
let inner = self.inner.read();
inner.runtime_version.clone()
}

/// Change the [`RuntimeVersion`] used in this client.
///
/// # Warning
///
/// Setting a custom runtime version may leave Subxt unable to
/// submit valid transactions.
pub fn set_runtime_version(&self, runtime_version: RuntimeVersion) {
Copy link
Contributor

@kylezs kylezs Jan 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I would have the runtime_version and metadata setting in one method, rather than one each. Makes it less likely they are put out of sync, and also means we only need to acquire the lock once, when we are updating both runtime the metadata (which we should be doing). But this would work for my use case

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did contemplate this, as I'm def aware of the extra locking and synchronisation things, but from an API perspective it just felt nicer to keep them separate and ignore the fact that it happens to lock under the hood (and the locking should be very trivial anyway in terms of time cost), and for your sake you probably only really ever need to update the Metadata and can ignore the rest :)

let mut inner = self.inner.write();
inner.runtime_version = runtime_version;
}

/// Return an RPC client to make raw requests with.
pub fn rpc(&self) -> &Rpc<T> {
&self.rpc
Expand Down