-
Notifications
You must be signed in to change notification settings - Fork 248
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
Static Decoding of Signed Extensions: Simple Approach #1235
Static Decoding of Signed Extensions: Simple Approach #1235
Conversation
subxt/src/blocks/extrinsic_types.rs
Outdated
/// Returns an iterator over each of the signed extension details of the extrinsic. | ||
/// If the decoding of any signed extension fails, an error item is yielded and the iterator stops. | ||
pub fn iter(&self) -> impl Iterator<Item = Result<ExtrinsicSignedExtension<'a>, Error>> { | ||
pub fn iter(&'a self) -> impl Iterator<Item = Result<ExtrinsicSignedExtension<'a, T>, Error>> { |
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.
I think you added this &'a self
lifetime back because metadata is no longer referenced, but you should be able to get rid of it (esp since you self.metadata.clone()
below still).
If Metadata
needs to be owned in ExtrinsicSignedExtensions
(even though other bits are borrowed) then you could:
let metadata = self.metadata.clone()
at the top- then don't reference
self
in theiter::from_fn
below.
But since everything else is borrowed, I'd borrow the Metadata in ExtrinsicSignedExtensions
and then all of the data has the 'a
lifetime and that lifetime flows into the iterated stuff too. (Basically, we should either fully commit to lifetimes or avoid them completely here I reckon, and fully committing seems like the good way to go unless we run into some case where they are a pain)
subxt/src/blocks/extrinsic_types.rs
Outdated
})) | ||
}) | ||
} | ||
|
||
fn find_by_name(&self, name: impl AsRef<str>) -> Option<ExtrinsicSignedExtension<'_, T>> { |
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.
Could this take &str
instead of impl AsRef<str>
?
subxt/src/blocks/extrinsic_types.rs
Outdated
/// DecodedValueThunk representing the type of the extra of this signed extension. | ||
fn decoded(&self) -> Result<DecodedValueThunk, Error> { | ||
let decoded_value_thunk = DecodedValueThunk::decode_with_metadata( | ||
&mut &self.bytes[..], | ||
self.ty_id, | ||
&self.metadata, | ||
)?; | ||
Ok(decoded_value_thunk) | ||
} |
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.
It looks like some things, like this and the lifetime stuff on ExtrinsicSignedExtensions
were lost again compared to the base PR that these build off; would be good to merge the underlying branch or rebase :)
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.
You are right, some stuff was lost in the merge, sorry. But this decoded
function I kept on purpose. It is just private now, but used in the public as_type()
and value()
functions.
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.
In the other PR I'd updated to directly use Value::decode_as_type(..)
which should be better than DecodedValueThunk::decode_with_metadata(..)
, and also I'd remove the value()
function and just return a Value
from this one (I'd have a look at the other PR because that was good here :))
/// Tip | ||
pub tip: Compact<u128>, | ||
/// Asset Id | ||
pub asset_id: Option<u32>, |
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.
here and below I think I wouldn't make thesepub
and instead expose tip()
and asset_id()
getters where tip
returns u128
and the Compact
thing is hidden away (it's an implementation detail) :)
(self.tip, self.asset_id).encode_to(v); | ||
self.encode_to(v); |
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.
Personally I prefer the prev approach and not making the struct Encode
(it feels like an implementation detail that's better to not expose). Same as below
pub struct ChargeTransactionPayment { | ||
tip: Compact<u128>, | ||
/// Tip | ||
pub tip: Compact<u128>, |
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.
As above I'd expose a tip()
getter that returns u128
to hide the Compact
implementation detail, and wouldn't have this be Encode
assert_eq!(tip2, 5678); | ||
|
||
assert_eq!(tip2, tip2_static); |
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.
it'd be really good I think to also test getting the mortality (just because that returns an enum and would be good to check that it decodes OK! I'm not sure off the top of my head whether the encoded impl is consistent with this or not))
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.
I added a check at the end of the test to verify Mortality decodes and is Era::Immortal
.
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.
Nice, thank you! That's reassuring :)
Nice! Yeah, I think I prefer this to the other way of doing things; one less trait, and the changes are confined to the Also just generally less code and no weird indirection from the new trait to the |
subxt/src/blocks/extrinsic_types.rs
Outdated
/// DecodedValueThunk representing the type of the extra of this signed extension. | ||
fn decoded(&self) -> Result<DecodedValueThunk, Error> { | ||
let decoded_value_thunk = DecodedValueThunk::decode_with_metadata( | ||
&mut &self.bytes[..], | ||
self.ty_id, | ||
self.metadata, | ||
)?; | ||
Ok(decoded_value_thunk) | ||
} | ||
|
||
/// Signed Extension as a [`scale_value::Value`] | ||
pub fn value(&self) -> Result<DecodedValue, Error> { | ||
let value = | ||
DecodedValue::decode_as_type(&mut &self.bytes[..], self.ty_id, self.metadata.types())?; | ||
self.decoded()?.to_value() | ||
} | ||
|
||
Ok(value) | ||
/// Decodes the `extra` bytes of this Signed Extension into a static type. | ||
pub fn as_type<E: DecodeAsType>(&self) -> Result<E, Error> { | ||
self.decoded()?.as_type::<E>().map_err(Into::into) |
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.
Please revert these changes here to master :)
subxt/src/blocks/extrinsic_types.rs
Outdated
pub fn as_type<E: DecodeAsType>(&self) -> Result<E, Error> { | ||
let value = E::decode_as_type(&mut &self.bytes[..], self.ty_id, self.metadata.types())?; | ||
Ok(value) | ||
} |
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.
So for eg events we have pub fn as_event<E: StaticEvent>(&self) -> Result<Option<E>, Error>
, and then for extrinsics we have pub fn as_extrinsic<E: StaticExtrinsic>(&self) -> Result<Option<E>, Error>
.
I'd be inclined to add something here like:
pub fn as_signed_extra<S: SignedExtension>(&self) -> Result<Option<S::Decoded>, Error> {
// Have sanity check that identifier lines up with `SignedExtension` given and then the
// usual decode logic, `Ok(None)` if name doesn't match (like the others).
}
This would be consistent with the above, and also be a little stricter than as_type
, only allowing something that looks like the correct signed extension type to even be tried in the first place. I have no real objections to keeping as_type
around too, but probably would remove it for now in favour of people defining and using more proper SignedExtension
impls here :)
All looks great now; just one comment to change |
subxt/src/blocks/extrinsic_types.rs
Outdated
@@ -669,7 +669,7 @@ impl<'a, T: Config> ExtrinsicSignedExtensions<'a, T> { | |||
/// If the Signed Extension is found, but decoding failed, `Some(Err(err))` is returned. | |||
pub fn find<S: SignedExtension<T>>(&self) -> Option<Result<S::Decoded, Error>> { |
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.
Nit: Maybe this should be Result<Option<..>
instead, actually? I'm never sure; just looking at some of the other find
methods we have around :)
// But both should start with a compact encoded u128, so this decoding is fine. | ||
let tip = Compact::<u128>::decode(&mut tip.bytes()).ok()?.0; | ||
Some(tip) | ||
// Note: the overhead of iterating twice should be negligible. |
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.
We could probably modify the find_by_name
function to take in a filter instead of the name, but yea we have a few extras anyway
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.
LGTM! Nice one 👍
Since #1227 merged, I am having some trouble implementing It tells me, that |
Maybe it's not documented (I didn't check) but I think (at least it looks like it supports this based on the Edit: ah here we go; see the bottom example: https://docs.rs/scale-decode/latest/scale_decode/derive.DecodeAsType.html |
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
* Update lightclient Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Fix typo Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Update cargo.toml Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Add tracing logs to improve debugging Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Add socket buffers module for `PlatformRef` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Update `SubxtPlatform` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cargo: Add lightclient dependencies Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update cargo.lock of wasm tests Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Add constant for with-buffer module Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Replace rand crate with getrandom Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * example: Update cargo lock file Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * examples: Update deps Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: Tadeo Hepperle <62739623+tadeohepperle@users.noreply.github.com>
…lt impl (#1227) * Asset Id in Config trait * add example configuring the config * fmt * fix Default trait bound * merge examples, fix default again * adjust config in examples * Update subxt/src/config/mod.rs Co-authored-by: James Wilson <james@jsdw.me> --------- Co-authored-by: James Wilson <james@jsdw.me>
// check that era decodes: | ||
for extensions in [&extensions1, &extensions2] { | ||
let era: Era = extensions | ||
.find::<CheckMortality<SubstrateConfig>>() |
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.
It's a shame to have to write the Config
bound in like this, but unavoidable as it stands!
(We can probably come up with something to avoid this at some point, but all good for now!)
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.
I agree.
Instead of introducing a new trait, like in #1226, this PR just adds an associated
type Decoded: DecodeAsType
to the SignedExtension trait.Results should be the same as in #1226.