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

Clone + Debug on Payloads/Addresses, and compare child storage results #1203

Merged
merged 9 commits into from
Oct 18, 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
3 changes: 3 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ jobs:
wasm_tests:
name: Test (WASM)
runs-on: ubuntu-latest
env:
# Set timeout for wasm tests to be much bigger than the default 20 secs.
WASM_BINDGEN_TEST_TIMEOUT: 300

steps:
- uses: actions/checkout@v4
Expand Down
21 changes: 3 additions & 18 deletions subxt/src/backend/legacy/rpc_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,20 @@ use crate::backend::rpc::{rpc_params, RpcClient, RpcSubscription};
use crate::metadata::Metadata;
use crate::{Config, Error};
use codec::Decode;
use derivative::Derivative;
use primitive_types::U256;
use serde::{Deserialize, Serialize};

/// An interface to call the legacy RPC methods. This interface is instantiated with
/// some `T: Config` trait which determines some of the types that the RPC methods will
/// take or hand back.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
pub struct LegacyRpcMethods<T> {
client: RpcClient,
_marker: std::marker::PhantomData<T>,
}

impl<T> Clone for LegacyRpcMethods<T> {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
_marker: self._marker,
}
}
}

impl<T> std::fmt::Debug for LegacyRpcMethods<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LegacyRpcMethods")
.field("client", &self.client)
.field("_marker", &self._marker)
.finish()
}
}

impl<T: Config> LegacyRpcMethods<T> {
/// Instantiate the legacy RPC method interface.
pub fn new(client: RpcClient) -> Self {
Expand Down
21 changes: 3 additions & 18 deletions subxt/src/backend/unstable/rpc_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use crate::backend::rpc::{rpc_params, RpcClient, RpcSubscription};
use crate::config::BlockHash;
use crate::{Config, Error};
use derivative::Derivative;
use futures::{Stream, StreamExt};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
Expand All @@ -17,29 +18,13 @@ use std::task::Poll;
/// An interface to call the unstable RPC methods. This interface is instantiated with
/// some `T: Config` trait which determines some of the types that the RPC methods will
/// take or hand back.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
pub struct UnstableRpcMethods<T> {
client: RpcClient,
_marker: std::marker::PhantomData<T>,
}

impl<T> Clone for UnstableRpcMethods<T> {
fn clone(&self) -> Self {
Self {
client: self.client.clone(),
_marker: self._marker,
}
}
}

impl<T> std::fmt::Debug for UnstableRpcMethods<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UnstableRpcMethods")
.field("client", &self.client)
.field("_marker", &self._marker)
.finish()
}
}

impl<T: Config> UnstableRpcMethods<T> {
/// Instantiate the legacy RPC method interface.
pub fn new(client: RpcClient) -> Self {
Expand Down
2 changes: 1 addition & 1 deletion subxt/src/client/light_client/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// see LICENSE for license details.

use super::{rpc::LightClientRpc, LightClient, LightClientError};
use crate::backend::{rpc::RpcClient, Backend};
use crate::backend::rpc::RpcClient;
use crate::{config::Config, error::Error, OnlineClient};
use std::num::NonZeroU32;
use subxt_lightclient::{AddChainConfig, AddChainConfigJsonRpc, ChainId};
Expand Down
3 changes: 3 additions & 0 deletions subxt/src/constants/constant_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// see LICENSE for license details.

use crate::{dynamic::DecodedValueThunk, metadata::DecodeWithMetadata};
use derivative::Derivative;
use std::borrow::Cow;

/// This represents a constant address. Anything implementing this trait
Expand All @@ -26,6 +27,8 @@ pub trait ConstantAddress {
}

/// This represents the address of a constant.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
pub struct Address<ReturnTy> {
pallet_name: Cow<'static, str>,
constant_name: Cow<'static, str>,
Expand Down
3 changes: 3 additions & 0 deletions subxt/src/custom_values/custom_value_address.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use derivative::Derivative;
use std::marker::PhantomData;

use crate::dynamic::DecodedValueThunk;
Expand Down Expand Up @@ -36,6 +37,8 @@ impl CustomValueAddress for str {
pub struct Yes;

/// A static address to a custom value.
#[derive(Derivative)]
#[derivative(Clone(bound = ""), Debug(bound = ""))]
pub struct StaticAddress<ReturnTy, IsDecodable> {
name: &'static str,
hash: Option<[u8; 32]>,
Expand Down
7 changes: 6 additions & 1 deletion subxt/src/runtime_api/runtime_payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// see LICENSE for license details.

use core::marker::PhantomData;
use derivative::Derivative;
use scale_encode::EncodeAsFields;
use scale_value::Composite;
use std::borrow::Cow;
Expand Down Expand Up @@ -65,7 +66,11 @@ pub trait RuntimeApiPayload {
///
/// This can be created from static values (ie those generated
/// via the `subxt` macro) or dynamic values via [`dynamic`].
#[derive(Clone, Debug)]
#[derive(Derivative)]
#[derivative(
Clone(bound = "ArgsData: Clone"),
Debug(bound = "ArgsData: std::fmt::Debug")
)]
pub struct Payload<ArgsData, ReturnTy> {
trait_name: Cow<'static, str>,
method_name: Cow<'static, str>,
Expand Down
6 changes: 6 additions & 0 deletions subxt/src/storage/storage_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::{
metadata::{DecodeWithMetadata, EncodeWithMetadata, Metadata},
utils::{Encoded, Static},
};
use derivative::Derivative;
use scale_info::TypeDef;
use std::borrow::Cow;
use subxt_metadata::{StorageEntryType, StorageHasher};
Expand Down Expand Up @@ -51,6 +52,11 @@ pub struct Yes;

/// A concrete storage address. This can be created from static values (ie those generated
/// via the `subxt` macro) or dynamic values via [`dynamic`].
#[derive(Derivative)]
#[derivative(
Clone(bound = "StorageKey: Clone"),
Debug(bound = "StorageKey: std::fmt::Debug")
)]
pub struct Address<StorageKey, ReturnTy, Fetchable, Defaultable, Iterable> {
pallet_name: Cow<'static, str>,
entry_name: Cow<'static, str>,
Expand Down
40 changes: 40 additions & 0 deletions testing/integration-tests/src/full_client/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,46 @@ async fn storage_iter() {
assert_eq!(len, 13);
}

#[tokio::test]
async fn storage_child_values_same_across_backends() {
let ctx = test_context().await;

let unstable_client = ctx.unstable_client().await;
let legacy_client = ctx.legacy_client().await;

let addr = node_runtime::storage().system().account_iter();
let block_ref = legacy_client
.blocks()
.at_latest()
.await
.unwrap()
.reference();

let a: Vec<_> = unstable_client
.storage()
.at(block_ref.clone())
.iter(addr.clone())
.await
.unwrap()
.collect()
.await;
let b: Vec<_> = legacy_client
.storage()
.at(block_ref.clone())
.iter(addr)
.await
.unwrap()
.collect()
.await;

for (a, b) in a.into_iter().zip(b.into_iter()) {
let a = a.unwrap();
let b = b.unwrap();

assert_eq!(a, b);
}
}

#[tokio::test]
async fn transaction_validation() {
let ctx = test_context().await;
Expand Down
101 changes: 76 additions & 25 deletions testing/integration-tests/src/utils/node_proc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

use std::cell::RefCell;
use std::ffi::{OsStr, OsString};
use std::sync::Arc;
use substrate_runner::SubstrateNode;
Expand All @@ -18,6 +19,12 @@ pub struct TestNodeProcess<R: Config> {
// Keep a handle to the node; once it's dropped the node is killed.
proc: SubstrateNode,

// Lazily construct these when asked for.
unstable_client: RefCell<Option<OnlineClient<R>>>,
legacy_client: RefCell<Option<OnlineClient<R>>>,

rpc_client: rpc::RpcClient,

#[cfg(not(feature = "unstable-light-client"))]
client: OnlineClient<R>,

Expand Down Expand Up @@ -49,14 +56,42 @@ where
unstable::UnstableRpcMethods::new(rpc_client)
}

async fn rpc_client(&self) -> rpc::RpcClient {
/// Hand back an RPC client connected to the test node.
pub async fn rpc_client(&self) -> rpc::RpcClient {
let url = format!("ws://127.0.0.1:{}", self.proc.ws_port());
rpc::RpcClient::from_url(url)
.await
.expect("Unable to connect RPC client to test node")
}

/// Returns the subxt client connected to the running node.
/// Always return a client using the unstable backend.
/// Only use for comparing backends; use [`TestNodeProcess::client()`] normally,
/// which enables us to run each test against both backends.
pub async fn unstable_client(&self) -> OnlineClient<R> {
if self.unstable_client.borrow().is_none() {
Copy link
Member

Choose a reason for hiding this comment

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

Not the prettiest code I have seen but works I guess.

I guess you can't use borrow_mut here because it can panic.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It was just using RefCell as a silly internal cache because Mutex wasn't needed really, so in tests you can ask for one of the clients and you'll get one back or it'll make one for you and give that back if it needs to :)

let c = build_unstable_client(self.rpc_client.clone())
.await
.unwrap();
self.unstable_client.replace(Some(c));
}
self.unstable_client.borrow().as_ref().unwrap().clone()
}

/// Always return a client using the legacy backend.
/// Only use for comparing backends; use [`TestNodeProcess::client()`] normally,
/// which enables us to run each test against both backends.
pub async fn legacy_client(&self) -> OnlineClient<R> {
if self.legacy_client.borrow().is_none() {
let c = build_legacy_client(self.rpc_client.clone()).await.unwrap();
self.legacy_client.replace(Some(c));
}
self.legacy_client.borrow().as_ref().unwrap().clone()
}

/// Returns the subxt client connected to the running node. This client
/// will use the legacy backend by default or the unstable backend if the
/// "unstable-backend-client" feature is enabled, so that we can run each
/// test against both.
#[cfg(not(feature = "unstable-light-client"))]
pub fn client(&self) -> OnlineClient<R> {
self.client.clone()
Expand Down Expand Up @@ -115,36 +150,57 @@ impl TestNodeProcessBuilder {
// Spawn the node and retrieve a URL to it:
let proc = node_builder.spawn().map_err(|e| e.to_string())?;
let ws_url = format!("ws://127.0.0.1:{}", proc.ws_port());
let rpc_client = build_rpc_client(&ws_url)
.await
.map_err(|e| format!("Failed to connect to node at {ws_url}: {e}"))?;

// Cache whatever client we build, and None for the other.
#[allow(unused_assignments, unused_mut)]
let mut unstable_client = None;
#[allow(unused_assignments, unused_mut)]
let mut legacy_client = None;

#[cfg(feature = "unstable-light-client")]
let client = build_light_client(&proc).await;
let client = build_light_client(&proc).await?;

#[cfg(feature = "unstable-backend-client")]
let client = build_unstable_client(&proc).await;
let client = {
let client = build_unstable_client(rpc_client.clone()).await?;
unstable_client = Some(client.clone());
client
};

#[cfg(all(
not(feature = "unstable-light-client"),
not(feature = "unstable-backend-client")
))]
let client = build_legacy_client(&proc).await;

match client {
Ok(client) => Ok(TestNodeProcess { proc, client }),
Err(err) => Err(format!("Failed to connect to node rpc at {ws_url}: {err}")),
}
let client = {
let client = build_legacy_client(rpc_client.clone()).await?;
legacy_client = Some(client.clone());
client
};

Ok(TestNodeProcess {
proc,
client,
legacy_client: RefCell::new(legacy_client),
unstable_client: RefCell::new(unstable_client),
rpc_client,
})
}
}

#[cfg(all(
not(feature = "unstable-light-client"),
not(feature = "unstable-backend-client")
))]
async fn build_legacy_client<T: Config>(proc: &SubstrateNode) -> Result<OnlineClient<T>, String> {
let ws_url = format!("ws://127.0.0.1:{}", proc.ws_port());

async fn build_rpc_client(ws_url: &str) -> Result<rpc::RpcClient, String> {
let rpc_client = rpc::RpcClient::from_url(ws_url)
.await
.map_err(|e| format!("Cannot construct RPC client: {e}"))?;

Ok(rpc_client)
}

async fn build_legacy_client<T: Config>(
rpc_client: rpc::RpcClient,
) -> Result<OnlineClient<T>, String> {
let backend = legacy::LegacyBackend::new(rpc_client);
let client = OnlineClient::from_backend(Arc::new(backend))
.await
Expand All @@ -153,14 +209,9 @@ async fn build_legacy_client<T: Config>(proc: &SubstrateNode) -> Result<OnlineCl
Ok(client)
}

#[cfg(feature = "unstable-backend-client")]
async fn build_unstable_client<T: Config>(proc: &SubstrateNode) -> Result<OnlineClient<T>, String> {
let ws_url = format!("ws://127.0.0.1:{}", proc.ws_port());

let rpc_client = rpc::RpcClient::from_url(ws_url)
.await
.map_err(|e| format!("Cannot construct RPC client: {e}"))?;

async fn build_unstable_client<T: Config>(
rpc_client: rpc::RpcClient,
) -> Result<OnlineClient<T>, String> {
let (backend, mut driver) = unstable::UnstableBackend::builder().build(rpc_client);

// The unstable backend needs driving:
Expand Down
Loading
Loading