Skip to content

Commit 77f7d6c

Browse files
authored
Cl generic event decoder rebase (#52)
implemented listening generically for events based on data obtained from the metadata added example_generic_event_callback modified contract example to listen generically. -> contract example is fixed now. closes #26 .
1 parent c93ccd6 commit 77f7d6c

12 files changed

+935
-247
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ hex = { version = "0.4", default-features=false, optional = true }
1111
log = { version = "0.4", optional = true }
1212
serde = { version = "1.0", optional = true, features = ["derive"] }
1313
serde_json = { version = "1.0", optional = true }
14+
thiserror = "1.0"
1415
primitive-types = { version = "0.6", default-features = false, features = ["codec"] }
1516

1617
[dependencies.sp-core]
@@ -165,6 +166,10 @@ path = "src/examples/example_custom_storage_struct.rs"
165166
name = "example_compose_extrinsic_offline"
166167
path = "src/examples/example_compose_extrinsic_offline.rs"
167168

169+
[[example]]
170+
name = "example_generic_event_callback"
171+
path = "src/examples/example_generic_event_callback.rs"
172+
168173
[[example]]
169174
name = "example_sudo"
170175
path = "src/examples/example_sudo.rs"

src/events.rs

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
// Copyright 2019 Parity Technologies (UK) Ltd.
2+
// This file is part of substrate-subxt.
3+
//
4+
// subxt is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// subxt is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with substrate-subxt. If not, see <http://www.gnu.org/licenses/>.
16+
17+
use std::{
18+
collections::{
19+
HashMap,
20+
HashSet,
21+
},
22+
convert::TryFrom,
23+
marker::{
24+
// PhantomData,
25+
Send,
26+
},
27+
};
28+
29+
use codec::{
30+
Codec,
31+
Compact,
32+
Decode,
33+
Encode,
34+
Error as CodecError,
35+
Input,
36+
Output,
37+
};
38+
use sp_runtime::DispatchError;
39+
use support::weights::DispatchInfo;
40+
use system::Phase;
41+
42+
use crate::{
43+
node_metadata::{
44+
EventArg,
45+
Metadata,
46+
MetadataError,
47+
},
48+
};
49+
50+
/// Event for the System module.
51+
#[derive(Clone, Debug, Decode)]
52+
pub enum SystemEvent {
53+
/// An extrinsic completed successfully.
54+
ExtrinsicSuccess(DispatchInfo),
55+
/// An extrinsic failed.
56+
ExtrinsicFailed(DispatchError, DispatchInfo),
57+
}
58+
59+
/// Top level Event that can be produced by a substrate runtime
60+
#[derive(Debug)]
61+
pub enum RuntimeEvent {
62+
System(SystemEvent),
63+
Raw(RawEvent),
64+
}
65+
66+
/// Raw bytes for an Event
67+
#[derive(Debug)]
68+
pub struct RawEvent {
69+
/// The name of the module from whence the Event originated
70+
pub module: String,
71+
/// The name of the Event
72+
pub variant: String,
73+
/// The raw Event data
74+
pub data: Vec<u8>,
75+
}
76+
77+
#[derive(Debug, thiserror::Error)]
78+
pub enum EventsError {
79+
#[error("Scale codec error: {0:?}")]
80+
CodecError(#[from] CodecError),
81+
#[error("Metadata error: {0:?}")]
82+
Metadata(#[from] MetadataError),
83+
#[error("Type Sizes Unavailable: {0:?}")]
84+
TypeSizeUnavailable(String),
85+
}
86+
87+
pub struct EventsDecoder {
88+
metadata: Metadata,
89+
type_sizes: HashMap<String, usize>,
90+
// marker: PhantomData<fn() -> T>,
91+
}
92+
93+
impl TryFrom<Metadata> for EventsDecoder {
94+
type Error = EventsError;
95+
96+
fn try_from(metadata: Metadata) -> Result<Self, Self::Error> {
97+
let mut decoder = Self {
98+
metadata,
99+
type_sizes: HashMap::new(),
100+
// marker: PhantomData,
101+
};
102+
// register default event arg type sizes for dynamic decoding of events
103+
decoder.register_type_size::<bool>("bool")?;
104+
decoder.register_type_size::<u32>("ReferendumIndex")?;
105+
decoder.register_type_size::<[u8; 16]>("Kind")?;
106+
decoder.register_type_size::<[u8; 32]>("AuthorityId")?;
107+
decoder.register_type_size::<u8>("u8")?;
108+
decoder.register_type_size::<u32>("u32")?;
109+
decoder.register_type_size::<u64>("u64")?;
110+
decoder.register_type_size::<u32>("AccountIndex")?;
111+
decoder.register_type_size::<u32>("SessionIndex")?;
112+
decoder.register_type_size::<u32>("PropIndex")?;
113+
decoder.register_type_size::<u32>("ProposalIndex")?;
114+
decoder.register_type_size::<u32>("AuthorityIndex")?;
115+
decoder.register_type_size::<u64>("AuthorityWeight")?;
116+
decoder.register_type_size::<u32>("MemberCount")?;
117+
decoder.register_type_size::<node_primitives::AccountId>("AccountId")?;
118+
decoder.register_type_size::<node_primitives::BlockNumber>("BlockNumber")?;
119+
decoder.register_type_size::<node_primitives::Hash>("Hash")?;
120+
decoder.register_type_size::<node_primitives::Balance>("Balance")?;
121+
// VoteThreshold enum index
122+
decoder.register_type_size::<u8>("VoteThreshold")?;
123+
124+
Ok(decoder)
125+
}
126+
}
127+
128+
impl EventsDecoder {
129+
pub fn register_type_size<U>(&mut self, name: &str) -> Result<usize, EventsError>
130+
where
131+
U: Default + Codec + Send + 'static,
132+
{
133+
let size = U::default().encode().len();
134+
if size > 0 {
135+
self.type_sizes.insert(name.to_string(), size);
136+
Ok(size)
137+
} else {
138+
Err(EventsError::TypeSizeUnavailable(name.to_owned()))
139+
}
140+
}
141+
142+
pub fn check_missing_type_sizes(&self) {
143+
let mut missing = HashSet::new();
144+
for module in self.metadata.modules_with_events() {
145+
for event in module.events() {
146+
for arg in event.arguments() {
147+
for primitive in arg.primitives() {
148+
if module.name() != "System"
149+
&& !self.type_sizes.contains_key(&primitive)
150+
&& !primitive.contains("PhantomData")
151+
{
152+
missing.insert(format!(
153+
"{}::{}::{}",
154+
module.name(),
155+
event.name,
156+
primitive
157+
));
158+
}
159+
}
160+
}
161+
}
162+
}
163+
if missing.len() > 0 {
164+
log::warn!(
165+
"The following primitive types do not have registered sizes: {:?} \
166+
If any of these events are received, an error will occur since we cannot decode them",
167+
missing
168+
);
169+
}
170+
}
171+
172+
fn decode_raw_bytes<I: Input, W: Output>(
173+
&self,
174+
args: &[EventArg],
175+
input: &mut I,
176+
output: &mut W,
177+
) -> Result<(), EventsError> {
178+
for arg in args {
179+
match arg {
180+
EventArg::Vec(arg) => {
181+
let len = <Compact<u32>>::decode(input)?;
182+
len.encode_to(output);
183+
for _ in 0..len.0 {
184+
self.decode_raw_bytes(&[*arg.clone()], input, output)?
185+
}
186+
}
187+
EventArg::Tuple(args) => self.decode_raw_bytes(args, input, output)?,
188+
EventArg::Primitive(name) => {
189+
if name.contains("PhantomData") {
190+
// PhantomData is size 0
191+
return Ok(())
192+
}
193+
if let Some(size) = self.type_sizes.get(name) {
194+
let mut buf = vec![0; *size];
195+
input.read(&mut buf)?;
196+
output.write(&buf);
197+
} else {
198+
return Err(EventsError::TypeSizeUnavailable(name.to_owned()))
199+
}
200+
}
201+
}
202+
}
203+
Ok(())
204+
}
205+
206+
pub fn decode_events(
207+
&self,
208+
input: &mut &[u8],
209+
) -> Result<Vec<(Phase, RuntimeEvent)>, EventsError> {
210+
log::debug!("Decoding compact len: {:?}", input);
211+
let compact_len = <Compact<u32>>::decode(input)?;
212+
let len = compact_len.0 as usize;
213+
214+
let mut r = Vec::new();
215+
for _ in 0..len {
216+
// decode EventRecord
217+
log::debug!("Decoding phase: {:?}", input);
218+
let phase = Phase::decode(input)?;
219+
let module_variant = input.read_byte()?;
220+
221+
let module = self.metadata.module_with_events(module_variant)?;
222+
let event = if module.name() == "System" {
223+
log::debug!("Decoding system event, intput: {:?}", input);
224+
let system_event = SystemEvent::decode(input)?;
225+
log::debug!("Decoding successful, system_event: {:?}", system_event);
226+
RuntimeEvent::System(system_event)
227+
} else {
228+
let event_variant = input.read_byte()?;
229+
let event_metadata = module.event(event_variant)?;
230+
log::debug!("decoding event '{}::{}'", module.name(), event_metadata.name);
231+
232+
let mut event_data = Vec::<u8>::new();
233+
self.decode_raw_bytes(
234+
&event_metadata.arguments(),
235+
input,
236+
&mut event_data,
237+
)?;
238+
239+
log::debug!(
240+
"received event '{}::{}', raw bytes: {}",
241+
module.name(),
242+
event_metadata.name,
243+
hex::encode(&event_data),
244+
);
245+
246+
RuntimeEvent::Raw(RawEvent {
247+
module: module.name().to_string(),
248+
variant: event_metadata.name.clone(),
249+
data: event_data,
250+
})
251+
};
252+
253+
// topics come after the event data in EventRecord
254+
log::debug!("Phase {:?}, Event: {:?}", phase, event);
255+
256+
log::debug!("Decoding topics {:?}", input);
257+
let _topics = Vec::<node_primitives::Hash>::decode(input)?;
258+
r.push((phase, event));
259+
}
260+
Ok(r)
261+
}
262+
}

0 commit comments

Comments
 (0)