-
Notifications
You must be signed in to change notification settings - Fork 14
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
V14 Decoding #45
Merged
Merged
V14 Decoding #45
Changes from 1 commit
Commits
Show all changes
55 commits
Select commit
Hold shift + click to select a range
a3e7d11
Begin scaffolding out crate for v14 decoding
jsdw e300f9a
Ongoing work to port metadata over
jsdw 9ed8410
Comment out metadata stuff to start fresh
jsdw d18eead
test binary to play with metadata stuff
jsdw f113916
Start working out how metadata will be laid out
jsdw dfec7d1
Basic decoding of metadata to handle calls
jsdw 5cfbc7d
Probably don't need extrinsic info in metadata, actually
jsdw 6e6b90d
allow type resolution via metadata; we'll need that for decoding
jsdw 7133c32
re-write SubstrateType to be derived from scale_info::Type
jsdw 1ace2ec
Tidy up Metadata bits now that we have new SubstrateType
jsdw 154947c
Rewrite SubstrateValue and get things compiling again
jsdw 6241c72
Remove now-unused code
jsdw 0f98fd4
Cargo fmt and a little tidy up
jsdw 46400f2
Wee tidy up
jsdw a4cc70f
Update Cargo.lock
jsdw e38451e
Scaffold out some of the decode logic
jsdw 07b7959
cargo fmt
jsdw aa097b5
Decode signature (untested) and scaffold out arg decoding methods
jsdw a9f1f20
Add a comment about mut ptr to slice
jsdw 2c8dd13
Remove substrate_type; we _can_ construct scale_info Types ourselves.…
jsdw bca25c2
First pass over decoding logic done
jsdw caea294
Tidy up, and make v14_test bin do something a little more useful
jsdw 1f41c16
Some tidy up and error on decoding if not all bytes consumed
jsdw 51a3aa4
Comment lowercasing
jsdw 4b80064
Add copyright notice on v14 code
jsdw 0f6a566
Add missing copyright header
jsdw d1c8968
Add a couple of comments in the decode logic
jsdw 1d1daeb
remove some commented-out code
jsdw a2cb527
Use signed extension metadata for dynamic signed extension decoding, …
jsdw 14d882c
Unit test decoding, and some simplifications and removing a couple of…
jsdw 76ab0a0
Remove lifetime from DecodeError and make sure LateEof is correct w.r…
jsdw e96deab
Cargo fmt
jsdw 2098c40
differentiate between decoding extrinsics prefixed with length and no…
jsdw 5a0abd0
Tweak a comment
jsdw 8daeb91
Rename SubstrateValue to Value and expose from crate. Add first integ…
jsdw f989aa1
remove slightly-redundant 'Value' suffix from inner enum types
jsdw 02f7318
Pallet/call indexes don't necessarily align with their positions; nee…
jsdw 2b4e55d
Support compact-encoded wrapper-structs and add a couple more integra…
jsdw d487d27
More integration tests
jsdw 6a72b06
cargo fmt
jsdw 72d49e3
Add a couple more integration tests
jsdw 9bd6564
A little renaming, and remove unwanted DS_Store
jsdw b2b87cf
Add docs and fix/test ExtrinsicBytes
jsdw cd4deb0
More docs
jsdw 5831844
cargo clippy
jsdw 3f10df4
More documenting
jsdw 5f2f917
Remove Sequence value type: It's identical to Composite, and potentia…
jsdw 79b282d
cargo fmt
jsdw deafb08
more doc tweaks
jsdw fe608de
Cargo clippy (tabs in doc comments keep coming back..)
jsdw 859145c
add sqlx-data for offline mode
jsdw 4111d6c
Update core_v14/src/decoder/extrinsic_bytes.rs
jsdw 827a56d
newline in gitignore
jsdw 4da194c
Doc comment fixes
jsdw b0374da
More doc comment fixes
jsdw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "core_v14" | ||
version = "0.1.0" | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
log = "0.4" | ||
thiserror = "1.0.22" | ||
primitives = { package = "sp-core", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9" } | ||
runtime_primitives = { package = "sp-runtime", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9" } | ||
frame_metadata = { package = "frame-metadata", git = "https://github.com/paritytech/frame-metadata", features = ["v14"] } | ||
serde = { version = "1", features = ["derive"] } | ||
serde_json = { version = "1", features = ["preserve_order"] } | ||
codec = { version = "2", package = "parity-scale-codec" } | ||
hex = "0.4.3" | ||
pallet_democracy = { package = "pallet-democracy", git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.9" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
use crate::metadata::Metadata; | ||
use crate::generic_extrinsic::GenericExtrinsic; | ||
use super::utils::{ scaled_extrinsic_bytes, ExtrinsicBytesError }; | ||
|
||
pub struct Decoder { | ||
metadata: Metadata | ||
} | ||
|
||
#[derive(Clone,Debug,thiserror::Error)] | ||
pub enum DecodeError { | ||
#[error("Failed to parse the provided vector of extrinsics: {0}")] | ||
UnexpectedExtrinsicsShape(#[from] ExtrinsicBytesError) | ||
} | ||
|
||
impl Decoder { | ||
|
||
/// Create a new decoder using the provided metadata. | ||
pub fn with_metadata<M: Into<Metadata>>(metadata: M) -> Decoder { | ||
Decoder { | ||
metadata: metadata.into() | ||
} | ||
} | ||
|
||
/// Decode a SCALE encoded vector of extrinsics against the metadata provided | ||
pub fn decode_extrinsics(&self, data: &[u8]) -> Result<Vec<GenericExtrinsic>, DecodeError> { | ||
let extrinsic_bytes = scaled_extrinsic_bytes(data)?; | ||
log::trace!("Decoding {} Total Extrinsics.", extrinsic_bytes.len()); | ||
|
||
let mut out = Vec::with_capacity(extrinsic_bytes.len()); | ||
for (idx, res) in extrinsic_bytes.iter().enumerate() { | ||
let bytes = res?; | ||
log::trace!("Extrinsic {}:{:?}", idx, bytes); | ||
out.push(self.decode_extrinsic(bytes)?); | ||
} | ||
Ok(out) | ||
} | ||
|
||
/// Decode a SCALE encoded extrinsic against the metadata provided | ||
pub fn decode_extrinsic(&self, data: &[u8]) -> Result<GenericExtrinsic, DecodeError> { | ||
todo!() | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
mod decoder; | ||
mod utils; | ||
|
||
pub use decoder::Decoder; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/// Iterate over a SCALE encoded vector of extrinsics and return the bytes associated with each one. | ||
/// Conceptually, the vector of extrinsics is encoded in the form: | ||
/// | ||
/// `Vec<(Compact<u32>, Extrinsic)>` | ||
/// | ||
/// Where Each extrinsic is prefixed with a compact encoding of its length in bytes. This is because | ||
/// extrinsics are themselves just treated as opaque vectors of btyes when they are encoded. | ||
/// | ||
/// On each iteration, we return the extrinsic bytes, or a `usize` denoting the position at | ||
pub fn scaled_extrinsic_bytes(data: &[u8]) -> Result<ExtrinsicBytes, ExtrinsicBytesError> { | ||
let (vec_len, vec_len_bytes) = match decode_compact_u32(data) { | ||
Some(res) => res, | ||
None => return Err(ExtrinsicBytesError { index: 0 }) | ||
}; | ||
|
||
Ok(ExtrinsicBytes { | ||
len: vec_len, | ||
data: &data[vec_len_bytes..] | ||
}) | ||
} | ||
|
||
/// A structure representing a set of extrinsics in terms of their raw SCALE encoded bytes. | ||
#[derive(Clone, Copy)] | ||
pub struct ExtrinsicBytes<'a> { | ||
len: usize, | ||
data: &'a [u8] | ||
} | ||
|
||
impl <'a> ExtrinsicBytes<'a> { | ||
/// How many extrinsics are there? | ||
pub fn len(&self) -> usize { | ||
self.len | ||
} | ||
|
||
/// Iterate over the bytes, returning each extrinsic found in the form of its bytes, | ||
/// or an error if we cannot decode the bytes as expected. | ||
pub fn iter(&self) -> ExtrinsicBytesIter<'a> { | ||
ExtrinsicBytesIter { data: &self.data, cursor: 0 } | ||
} | ||
} | ||
|
||
/// An iterator that returns the set of bytes representing each extrinsic found. | ||
pub struct ExtrinsicBytesIter<'a> { | ||
data: &'a [u8], | ||
cursor: usize | ||
} | ||
|
||
impl <'a> Iterator for ExtrinsicBytesIter<'a> { | ||
type Item = Result<&'a [u8], ExtrinsicBytesError>; | ||
fn next(&mut self) -> Option<Self::Item> { | ||
if self.data.is_empty() { | ||
return None | ||
} | ||
|
||
let (vec_len, vec_len_bytes) = match decode_compact_u32(&self.data) { | ||
Some(res) => res, | ||
None => return Some(Err(ExtrinsicBytesError { index: self.cursor })) | ||
}; | ||
log::trace!("Length {}, Prefix: {}", vec_len, vec_len_bytes); | ||
|
||
let res = &self.data[(self.cursor + vec_len_bytes) .. (self.cursor + vec_len + vec_len_bytes)]; | ||
self.cursor += vec_len + vec_len_bytes; | ||
Some(Ok(res)) | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Copy, thiserror::Error)] | ||
#[error("Expected a compact encoded u32 at byte index {index}, but did not find one")] | ||
pub struct ExtrinsicBytesError { | ||
pub index: usize | ||
} | ||
|
||
/// Given a SCALE encoded `Compact<u32>` (which prefixes a SCALE encoded vector, for instance), | ||
/// return a tuple of the length of the vector, and the number of input bytes used to represent | ||
/// this length. | ||
fn decode_compact_u32(mut data: &[u8]) -> Option<(usize, usize)> { | ||
use codec::{Compact, CompactLen, Decode}; | ||
use std::convert::TryFrom; | ||
|
||
// alternative to `DecodeLength` trait, to avoid casting from a trait | ||
let length = u32::from(Compact::<u32>::decode(&mut data).ok()?); | ||
let prefix = Compact::<u32>::compact_len(&length); | ||
let length = usize::try_from(length).ok()?; | ||
Some((length, prefix)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
// Copyright 2019 Parity Technologies (UK) Ltd. | ||
jsdw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// This file is part of substrate-desub. | ||
// | ||
// substrate-desub 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. // | ||
// substrate-desub 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 substrate-desub. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
//! Generic Extrinsic Type and Functions | ||
|
||
use crate::substrate_value::SubstrateValue; | ||
use serde::Serialize; | ||
use std::fmt; | ||
|
||
#[derive(Debug, Serialize)] | ||
pub struct ExtrinsicArgument { | ||
pub name: String, | ||
pub arg: SubstrateValue, | ||
} | ||
|
||
impl fmt::Display for ExtrinsicArgument { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, " arg: {}, Ty: {} ", self.name, self.arg) | ||
} | ||
} | ||
|
||
#[derive(Debug, Serialize)] | ||
pub struct GenericCall { | ||
name: String, | ||
module: String, | ||
args: Vec<ExtrinsicArgument>, | ||
} | ||
|
||
impl fmt::Display for GenericCall { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
let mut s = String::from(""); | ||
s.push_str(&self.name); | ||
s.push_str(": "); | ||
for val in self.args.iter() { | ||
s.push_str(&format!("{}", val)); | ||
} | ||
write!(f, "{}", s) | ||
} | ||
} | ||
|
||
/// Generic Extrinsic Type | ||
#[derive(Debug, Serialize)] | ||
pub struct GenericExtrinsic { | ||
signature: Option<GenericSignature>, | ||
call: GenericCall, | ||
} | ||
|
||
impl fmt::Display for GenericExtrinsic { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
let mut s = String::from(""); | ||
if let Some(v) = &self.signature { | ||
s.push_str(&format!("{}", v)); | ||
} else { | ||
s.push_str(&"None".to_string()); | ||
} | ||
s.push('\n'); | ||
s.push_str("CALL"); | ||
s.push('\n'); | ||
s.push_str(&format!("{}", self.call)); | ||
write!(f, "{}", s) | ||
} | ||
} | ||
|
||
impl GenericExtrinsic { | ||
/// create a new generic extrinsic type | ||
pub fn new(sig: Option<SubstrateValue>, call: Vec<(String, SubstrateValue)>, name: String, module: String) -> Self { | ||
let call = | ||
call.into_iter().map(|c| ExtrinsicArgument { name: c.0, arg: c.1 }).collect::<Vec<ExtrinsicArgument>>(); | ||
let call = GenericCall { name, module, args: call }; | ||
Self { signature: sig.map(GenericSignature::new), call } | ||
} | ||
|
||
pub fn is_signed(&self) -> bool { | ||
self.signature.is_some() | ||
} | ||
|
||
pub fn signature(&self) -> Option<&GenericSignature> { | ||
self.signature.as_ref() | ||
} | ||
|
||
pub fn call(&self) -> &GenericCall { | ||
&self.call | ||
} | ||
|
||
pub fn ext_module(&self) -> &str { | ||
&self.call.module | ||
} | ||
|
||
pub fn ext_call(&self) -> &str { | ||
&self.call.name | ||
} | ||
|
||
pub fn args(&self) -> &[ExtrinsicArgument] { | ||
&self.call.args | ||
} | ||
} | ||
|
||
#[derive(Debug, Serialize)] | ||
pub struct GenericSignature { | ||
#[serde(serialize_with = "crate::util::as_substrate_address")] | ||
address: SubstrateValue, | ||
signature: SubstrateValue, | ||
extra: SubstrateValue, | ||
} | ||
|
||
impl GenericSignature { | ||
pub fn new(signature: SubstrateValue) -> Self { | ||
Self::split(signature) | ||
} | ||
|
||
/// returns address signature and extra as a tuple | ||
pub fn parts(&self) -> (&SubstrateValue, &SubstrateValue, &SubstrateValue) { | ||
(&self.address, &self.signature, &self.extra) | ||
} | ||
|
||
fn split(sig: SubstrateValue) -> Self { | ||
match sig { | ||
SubstrateValue::Composite(mut v) => { | ||
v.reverse(); | ||
Self { | ||
address: v.pop().expect("Address must must be present in signature"), | ||
signature: v.pop().expect("Signature must be present"), | ||
extra: v.pop().expect("Extra must be present"), | ||
} | ||
} | ||
_ => panic!("Signature should always be a tuple of Address, Signature, Extra"), | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for GenericSignature { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "Address {}\n Signature {}\n SignedExtra {}\n", self.address, self.signature, self.extra) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn should_serialize_generic_extrinsic() { | ||
let call = GenericCall { | ||
name: "set".to_string(), | ||
module: "Timestamp".to_string(), | ||
args: vec![ExtrinsicArgument { name: "Some Arg".to_string(), arg: SubstrateValue::U32(32) }], | ||
}; | ||
let ext = GenericExtrinsic { | ||
signature: Some(GenericSignature::new(SubstrateValue::Composite(vec![ | ||
SubstrateValue::Composite(vec![ | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
0u8.into(), | ||
]), | ||
SubstrateValue::U64(64), | ||
SubstrateValue::U128(128), | ||
]))), | ||
call, | ||
}; | ||
let serialized = serde_json::to_string(&ext).unwrap(); | ||
assert_eq!( | ||
serialized, | ||
r#"{"signature":{"address":"5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM","signature":64,"extra":128},"call":{"name":"set","module":"Timestamp","args":[{"name":"Some Arg","arg":32}]}}"# | ||
); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might not be the best name if this crate could one day be used on its own?
desub-metadata-v14
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely; the plan is to merge it with a crap name, and then do a PR to rename soem crates.. probably promoting this in place of the <=V13 logic, and moving that logic to be under something with legacy in the name (we'll need to work out the naming though when we get to that!)