Merge remote-tracking branch 'origin/master' into na-jsonrpsee-macros

This commit is contained in:
Niklas
2022-02-10 17:52:35 +01:00
20 changed files with 424 additions and 95 deletions
+5 -5
View File
@@ -1,6 +1,6 @@
[package]
name = "subxt"
version = "0.16.0"
version = "0.17.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
@@ -27,10 +27,10 @@ serde = { version = "1.0.124", features = ["derive"] }
serde_json = "1.0.64"
thiserror = "1.0.24"
subxt-macro = { version = "0.16.0", path = "../macro" }
subxt-macro = { version = "0.17.0", path = "../macro" }
sp-core = { version = "4.0.0", default-features = false }
sp-runtime = { version = "4.0.0", default-features = false }
sp-core = { version = "5.0.0", default-features = false }
sp-runtime = "5.0.0"
sp-version = "4.0.0"
frame-metadata = "14.0.0"
@@ -45,4 +45,4 @@ tempdir = "0.3.7"
wabt = "0.10.0"
which = "4.0.2"
test-runtime = { path = "../test-runtime" }
sp-keyring = "4.0.0"
sp-keyring = "5.0.0"
+1 -1
View File
@@ -90,7 +90,7 @@ impl ClientBuilder {
client
} else {
let url = self.url.as_deref().unwrap_or("ws://127.0.0.1:9944");
crate::rpc::build_ws_client(url).await?
crate::rpc::ws_client(url).await?
};
let rpc = Rpc::new(client);
let (metadata_bytes, genesis_hash, runtime_version, properties) = future::join4(
-12
View File
@@ -155,18 +155,6 @@ impl<E> RuntimeError<E> {
}
}
/// Module error.
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
#[error("{error} from {pallet}")]
pub struct PalletError {
/// The module where the error originated.
pub pallet: String,
/// The actual error code.
pub error: String,
/// The error description.
pub description: Vec<String>,
}
/// Transaction error.
#[derive(Clone, Debug, Eq, thiserror::Error, PartialEq)]
pub enum TransactionError {
+188
View File
@@ -373,10 +373,17 @@ pub enum EventsDecodingError {
mod tests {
use super::*;
use crate::{
error::GenericError::{
Codec,
EventsDecoding,
Other,
},
events::EventsDecodingError::UnsupportedPrimitive,
Config,
DefaultConfig,
Phase,
};
use assert_matches::assert_matches;
use codec::Encode;
use frame_metadata::{
v14::{
@@ -643,4 +650,185 @@ mod tests {
bitvec::bitvec![Msb0, u64; 0, 1, 1, 0, 1, 0, 1, 0, 0],
);
}
#[test]
fn decode_primitive() {
decode_and_consume_type_consumes_all_bytes(false);
decode_and_consume_type_consumes_all_bytes(true);
let dummy_data = vec![0u8];
let dummy_cursor = &mut &*dummy_data;
let (id, reg) = singleton_type_registry::<char>();
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(
res,
Err(EventsDecoding(UnsupportedPrimitive(TypeDefPrimitive::Char)))
);
decode_and_consume_type_consumes_all_bytes("str".to_string());
decode_and_consume_type_consumes_all_bytes(1u8);
decode_and_consume_type_consumes_all_bytes(1i8);
decode_and_consume_type_consumes_all_bytes(1u16);
decode_and_consume_type_consumes_all_bytes(1i16);
decode_and_consume_type_consumes_all_bytes(1u32);
decode_and_consume_type_consumes_all_bytes(1i32);
decode_and_consume_type_consumes_all_bytes(1u64);
decode_and_consume_type_consumes_all_bytes(1i64);
decode_and_consume_type_consumes_all_bytes(1u128);
decode_and_consume_type_consumes_all_bytes(1i128);
}
#[test]
fn decode_tuple() {
decode_and_consume_type_consumes_all_bytes(());
decode_and_consume_type_consumes_all_bytes((true,));
decode_and_consume_type_consumes_all_bytes((true, "str"));
// Incomplete bytes for decoding
let dummy_data = false.encode();
let dummy_cursor = &mut &*dummy_data;
let (id, reg) = singleton_type_registry::<(bool, &'static str)>();
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(res, Err(Codec(_)));
// Incomplete bytes for decoding, with invalid char type
let dummy_data = (false, "str", 0u8).encode();
let dummy_cursor = &mut &*dummy_data;
let (id, reg) = singleton_type_registry::<(bool, &'static str, char)>();
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(
res,
Err(EventsDecoding(UnsupportedPrimitive(TypeDefPrimitive::Char)))
);
// The last byte (0x0 u8) should not be consumed
assert_eq!(dummy_cursor.len(), 1);
}
#[test]
fn decode_array_and_seq() {
decode_and_consume_type_consumes_all_bytes([0]);
decode_and_consume_type_consumes_all_bytes([1, 2, 3, 4, 5]);
decode_and_consume_type_consumes_all_bytes([0; 500]);
decode_and_consume_type_consumes_all_bytes(["str", "abc", "cde"]);
decode_and_consume_type_consumes_all_bytes(vec![0]);
decode_and_consume_type_consumes_all_bytes(vec![1, 2, 3, 4, 5]);
decode_and_consume_type_consumes_all_bytes(vec!["str", "abc", "cde"]);
}
#[test]
fn decode_variant() {
#[derive(Clone, Encode, TypeInfo)]
enum EnumVar {
A,
B((&'static str, u8)),
C { named: i16 },
}
const INVALID_TYPE_ID: u32 = 1024;
decode_and_consume_type_consumes_all_bytes(EnumVar::A);
decode_and_consume_type_consumes_all_bytes(EnumVar::B(("str", 1)));
decode_and_consume_type_consumes_all_bytes(EnumVar::C { named: 1 });
// Invalid variant index
let dummy_data = 3u8.encode();
let dummy_cursor = &mut &*dummy_data;
let (id, reg) = singleton_type_registry::<EnumVar>();
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(res, Err(Other(_)));
// Valid index, incomplete data
let dummy_data = 2u8.encode();
let dummy_cursor = &mut &*dummy_data;
let res = decode_and_consume_type(id.id(), &reg, dummy_cursor);
assert_matches!(res, Err(Codec(_)));
let res = decode_and_consume_type(INVALID_TYPE_ID, &reg, dummy_cursor);
assert_matches!(res, Err(crate::error::GenericError::Metadata(_)));
}
#[test]
fn decode_composite() {
#[derive(Clone, Encode, TypeInfo)]
struct Composite {}
decode_and_consume_type_consumes_all_bytes(Composite {});
#[derive(Clone, Encode, TypeInfo)]
struct CompositeV2 {
id: u32,
name: String,
}
decode_and_consume_type_consumes_all_bytes(CompositeV2 {
id: 10,
name: "str".to_string(),
});
#[derive(Clone, Encode, TypeInfo)]
struct CompositeV3<T> {
id: u32,
extra: T,
}
decode_and_consume_type_consumes_all_bytes(CompositeV3 {
id: 10,
extra: vec![0, 1, 2],
});
decode_and_consume_type_consumes_all_bytes(CompositeV3 {
id: 10,
extra: bitvec::bitvec![Lsb0, u8; 0, 1, 1, 0, 1],
});
decode_and_consume_type_consumes_all_bytes(CompositeV3 {
id: 10,
extra: ("str", 1),
});
decode_and_consume_type_consumes_all_bytes(CompositeV3 {
id: 10,
extra: CompositeV2 {
id: 2,
name: "str".to_string(),
},
});
#[derive(Clone, Encode, TypeInfo)]
struct CompositeV4(u32, bool);
decode_and_consume_type_consumes_all_bytes(CompositeV4(1, true));
#[derive(Clone, Encode, TypeInfo)]
struct CompositeV5(u32);
decode_and_consume_type_consumes_all_bytes(CompositeV5(1));
}
#[test]
fn decode_compact() {
#[derive(Clone, Encode, TypeInfo)]
enum Compact {
A(#[codec(compact)] u32),
}
decode_and_consume_type_consumes_all_bytes(Compact::A(1));
#[derive(Clone, Encode, TypeInfo)]
struct CompactV2(#[codec(compact)] u32);
decode_and_consume_type_consumes_all_bytes(CompactV2(1));
#[derive(Clone, Encode, TypeInfo)]
struct CompactV3 {
#[codec(compact)]
val: u32,
}
decode_and_consume_type_consumes_all_bytes(CompactV3 { val: 1 });
#[derive(Clone, Encode, TypeInfo)]
struct CompactV4<T> {
#[codec(compact)]
val: T,
}
decode_and_consume_type_consumes_all_bytes(CompactV4 { val: 0u8 });
decode_and_consume_type_consumes_all_bytes(CompactV4 { val: 1u16 });
}
}
+2 -1
View File
@@ -81,7 +81,8 @@ pub use crate::{
error::{
BasicError,
Error,
PalletError,
GenericError,
RuntimeError,
TransactionError,
},
events::{
+5 -5
View File
@@ -203,6 +203,10 @@ pub trait SubxtRpcApi<Hash, Header, Xt: Serialize> {
#[method(name = "author_hasKey")]
async fn has_key(&self, public_key: Bytes, key_type: String) -> RpcResult<bool>;
/// Create and submit an extrinsic and return corresponding Hash if successful
#[method(name = "author_submitExtrinsic")]
async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<Hash>;
/// Subscribe to System Events that are imported into blocks.
///
/// *WARNING* these may not be included in the finalized chain, use
@@ -225,10 +229,6 @@ pub trait SubxtRpcApi<Hash, Header, Xt: Serialize> {
item = SubstrateTransactionStatus<Hash, Hash>
)]
fn watch_extrinsic<X: Encode>(&self, xt: Bytes) -> RpcResult<()>;
/// Create and submit an extrinsic and return corresponding Hash if successful
#[method(name = "author_submitExtrinsic")]
async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<Hash>;
}
/// A number type that can be serialized both as a number or a string that encodes a number in a
@@ -400,7 +400,7 @@ impl<T: Config> Rpc<T> {
}
/// Build WS RPC client from URL
pub async fn build_ws_client(url: &str) -> Result<RpcClient, RpcError> {
pub async fn ws_client(url: &str) -> Result<RpcClient, RpcError> {
let (sender, receiver) = ws_transport(url).await?;
Ok(RpcClientBuilder::default()
.max_notifs_per_subscription(4096)
+143 -20
View File
@@ -42,6 +42,16 @@ use sp_core::{
use sp_runtime::traits::Header;
use std::collections::VecDeque;
/// Raw bytes for an Event, including the block hash where it occurred and its
/// corresponding event index.
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Clone))]
pub struct EventContext<Hash> {
pub block_hash: Hash,
pub event_idx: usize,
pub event: RawEvent,
}
/// Event subscription simplifies filtering a storage change set stream for
/// events of interest.
pub struct EventSubscription<'a, T: Config> {
@@ -49,7 +59,7 @@ pub struct EventSubscription<'a, T: Config> {
block: Option<T::Hash>,
extrinsic: Option<usize>,
event: Option<(&'static str, &'static str)>,
events: VecDeque<RawEvent>,
events: VecDeque<EventContext<T::Hash>>,
finished: bool,
}
@@ -60,13 +70,19 @@ enum BlockReader<'a, T: Config> {
},
/// Mock event listener for unit tests
#[cfg(test)]
Mock(Box<dyn Iterator<Item = (T::Hash, Result<Vec<(Phase, RawEvent)>, BasicError>)>>),
Mock(
Box<
dyn Iterator<
Item = (T::Hash, Result<Vec<(Phase, usize, RawEvent)>, BasicError>),
>,
>,
),
}
impl<'a, T: Config> BlockReader<'a, T> {
async fn next(
&mut self,
) -> Option<(T::Hash, Result<Vec<(Phase, RawEvent)>, BasicError>)> {
) -> Option<(T::Hash, Result<Vec<(Phase, usize, RawEvent)>, BasicError>)> {
match self {
BlockReader::Decoder {
subscription,
@@ -81,7 +97,13 @@ impl<'a, T: Config> BlockReader<'a, T> {
})
.collect();
let flattened_events = events.map(|x| x.into_iter().flatten().collect());
let flattened_events = events.map(|x| {
x.into_iter()
.flatten()
.enumerate()
.map(|(event_idx, (phase, raw))| (phase, event_idx, raw))
.collect()
});
Some((change_set.block, flattened_events))
}
#[cfg(test)]
@@ -127,6 +149,15 @@ impl<'a, T: Config> EventSubscription<'a, T> {
/// Gets the next event.
pub async fn next(&mut self) -> Option<Result<RawEvent, BasicError>> {
self.next_context()
.await
.map(|res| res.map(|ctx| ctx.event))
}
/// Gets the next event with the associated block hash and its corresponding
/// event index.
pub async fn next_context(
&mut self,
) -> Option<Result<EventContext<T::Hash>, BasicError>> {
loop {
if let Some(raw_event) = self.events.pop_front() {
return Some(Ok(raw_event))
@@ -147,7 +178,7 @@ impl<'a, T: Config> EventSubscription<'a, T> {
match events {
Err(err) => return Some(Err(err)),
Ok(raw_events) => {
for (phase, raw) in raw_events {
for (phase, event_idx, raw) in raw_events {
if let Some(ext_index) = self.extrinsic {
if !matches!(phase, Phase::ApplyExtrinsic(i) if i as usize == ext_index)
{
@@ -159,7 +190,11 @@ impl<'a, T: Config> EventSubscription<'a, T> {
continue
}
}
self.events.push_back(raw);
self.events.push_back(EventContext {
block_hash: received_hash,
event_idx,
event: raw,
});
}
}
}
@@ -282,7 +317,7 @@ mod tests {
#[async_std::test]
/// test that filters work correctly, and are independent of each other
async fn test_filters() {
let mut events = vec![];
let mut events: Vec<(H256, Phase, usize, RawEvent)> = vec![];
// create all events
for block_hash in [H256::from([0; 32]), H256::from([1; 32])] {
for phase in [
@@ -291,14 +326,24 @@ mod tests {
Phase::ApplyExtrinsic(1),
Phase::Finalization,
] {
for event in [named_event("a"), named_event("b")] {
events.push((block_hash, phase.clone(), event))
}
[named_event("a"), named_event("b")]
.iter()
.enumerate()
.for_each(|(idx, event)| {
events.push((
block_hash,
phase.clone(),
// The event index
idx,
event.clone(),
))
});
}
}
// set variant index so we can uniquely identify the event
events.iter_mut().enumerate().for_each(|(idx, event)| {
event.2.variant_index = idx as u8;
event.3.variant_index = idx as u8;
});
let half_len = events.len() / 2;
@@ -315,8 +360,8 @@ mod tests {
Ok(events
.iter()
.take(half_len)
.map(|(_, phase, event)| {
(phase.clone(), event.clone())
.map(|(_, phase, idx, event)| {
(phase.clone(), *idx, event.clone())
})
.collect()),
),
@@ -325,8 +370,8 @@ mod tests {
Ok(events
.iter()
.skip(half_len)
.map(|(_, phase, event)| {
(phase.clone(), event.clone())
.map(|(_, phase, idx, event)| {
(phase.clone(), *idx, event.clone())
})
.collect()),
),
@@ -339,21 +384,24 @@ mod tests {
events: Default::default(),
finished: false,
};
let mut expected_events = events.clone();
let mut expected_events: Vec<(H256, Phase, usize, RawEvent)> =
events.clone();
if let Some(hash) = block_filter {
expected_events.retain(|(h, _, _)| h == &hash);
expected_events.retain(|(h, _, _, _)| h == &hash);
}
if let Some(idx) = extrinsic_filter {
expected_events.retain(|(_, phase, _)| matches!(phase, Phase::ApplyExtrinsic(i) if *i as usize == idx));
expected_events.retain(|(_, phase, _, _)| matches!(phase, Phase::ApplyExtrinsic(i) if *i as usize == idx));
}
if let Some(name) = event_filter {
expected_events.retain(|(_, _, event)| event.pallet == name.0);
expected_events.retain(|(_, _, _, event)| event.pallet == name.0);
}
for expected_event in expected_events {
assert_eq!(
subscription.next().await.unwrap().unwrap(),
expected_event.2
expected_event.3
);
}
assert!(subscription.next().await.is_none());
@@ -361,4 +409,79 @@ mod tests {
}
}
}
#[async_std::test]
async fn test_context() {
let mut events = vec![];
// create all events
for block_hash in [H256::from([0; 32]), H256::from([1; 32])] {
for phase in [
Phase::Initialization,
Phase::ApplyExtrinsic(0),
Phase::ApplyExtrinsic(1),
Phase::Finalization,
] {
[named_event("a"), named_event("b")]
.iter()
.enumerate()
.for_each(|(idx, event)| {
events.push((
phase.clone(),
EventContext {
block_hash,
event_idx: idx,
event: event.clone(),
},
));
});
}
}
// set variant index so we can uniquely identify the event
events.iter_mut().enumerate().for_each(|(idx, (_, ctx))| {
ctx.event.variant_index = idx as u8;
});
let half_len = events.len() / 2;
let mut subscription: EventSubscription<DefaultConfig> = EventSubscription {
block_reader: BlockReader::Mock(Box::new(
vec![
(
events[0].1.block_hash,
Ok(events
.iter()
.take(half_len)
.map(|(phase, ctx)| {
(phase.clone(), ctx.event_idx, ctx.event.clone())
})
.collect()),
),
(
events[half_len].1.block_hash,
Ok(events
.iter()
.skip(half_len)
.map(|(phase, ctx)| {
(phase.clone(), ctx.event_idx, ctx.event.clone())
})
.collect()),
),
]
.into_iter(),
)),
block: None,
extrinsic: None,
event: None,
events: Default::default(),
finished: false,
};
let expected_events = events.clone();
for exp in expected_events {
assert_eq!(subscription.next_context().await.unwrap().unwrap(), exp.1);
}
assert!(subscription.next().await.is_none());
}
}