-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Implement ValidateUnsigned as SignedExtension #5006
Conversation
I def. like the idea better as now we don't have two thing flying around doing the same thing:
If we can find a nice way to be able to have the nice features of |
Exactly this, so it's definitely possible for every pallet to expose it's own I think that If ever anyone needs the additional info passed to |
@tomusdrw is this a draft or ready for review? |
To be frank, I'm not actively working on this. I was hoping that we will merge that after 2.0 is released, as I didn't put a lot of work into testing it's correctness (and was quite surprised that none of our tests broke :)). That said, we can review that and let @gnunicorn decide if we want to include in 2.0 or rather put on hold. IMHO this makes the |
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.
When you forget to add ValidateUnsigned
to the list of signed extensions, any unsigned transaction will be valid.
This is a huge security problem of this implementation.
0826aa8
to
fee0292
Compare
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.
Re-wrote using different approach of altering only pre_dispatch
. Still want to add tests tomorrow.
frame/support/src/unsigned.rs
Outdated
@@ -87,7 +91,7 @@ macro_rules! impl_outer_validate_unsigned { | |||
#[allow(unreachable_patterns)] | |||
match call { | |||
$( Call::$module(inner_call) => $module::validate_unsigned(source, inner_call), )* | |||
_ => $crate::unsigned::UnknownTransaction::NoUnsignedValidator.into(), | |||
_ => $crate::unsigned::InvalidTransaction::NotFullyValidated.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.
This is a breaking change. Previously if you made an unsigned call to non-existent module it would merely return UnknownTransaction
, which is interpreted by the txpool as "not valid now, might be valid at some point in the future". Which is sort of ok, but it has undersirable consequence of not banning the transaction - so basically you can spam the txpool with (one; the same) transaction to non-existent module to make it constantly re-verify it.
I think it's more wise to reject such transaction (pretty much the same would happen if you try to call a non-existent function (we return InvalidTransaction::Call
)
Tests added, should be ready to re-review. |
/// No validator has been able to fully verify this transaction. | ||
/// | ||
/// The transactions are disallowed from being included by default. Some validator | ||
/// (`SignedExtension`) needs to whitelist them first. This error usually means that either you |
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.
For signed transactions, SignedExtension
works by default-pass (blacklisting), not whitelisting.
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.
That's only partially true, since the ValidTransaction
produced will have no provides
tags, which will be rejected by the transaction pool anyway.
Now CheckedExtrinsic
will explicitly return InvalidTransaction
error if no SignedExtension
provides any tags in validate
call. I also unified that with pre_dispatch
now, so basically it's a whitelist now (in a sense that you need at least one SignedExtension
to provide some tags, don't need to whitelist specific calls or anything).
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.
Honestly, I'm not especially keen on this refactor.
I see almost no improvement in the API; the only advatage here that I can see is that we get to lose one generic param in Executive
(which could perhaps be lost in a better way by combining with AllModules
) and one generic param elsewhere in validate
/apply
.
There are, however, several costs; we have an additional param going into validate
and we have an additional datapath out of SignedExtension::pre_dispatch
with the (rather opaque concept of) IsFullyValidated
. We also have an additional item in SignedExtra
that must be there or random other stuff in a runtime will break.
With a refactoring that adds no functionality like this, the other indicator I look at is code size change. Admittedly here, there's an extra test module and plenty of docs, it's still clear that this overall adds code to the main implementation. Not a good sign.
It is highly questionable whether ValidateUnsigned
should be subjugated to SignedExtension
and whether the latter should see its API bloated in that cause. The more I consider it, the more I'm inclined to say simply that the two are actually quite orthogonal and ought to be sibling APIs. SignedExtension
s are individually opt-in (like, for example, the SignedExtension
which blocks parachains transactions currently in Kusama) that are not necessarily a part of a pallet. ValidateUnsigned
s are OTOH inexticable parts of pallets.
If we really want to combine |
The main improvement imho is unified validation pipeline for signed and unsigned transactions, and decoulping unsigned transactions from
I see that more as an improvement actually - having
Only added to address Basti's critique about unsigned transactions being valid by default, removed in new approach. However I think this is a moot point anyway, since even though
I agree this is suboptimal, I want to get rid of all
The point is that currently (Sorry for a wall of text above, tl;dr is below) @gavofyork I've made another attempt to reduce the surface of API changes:
If that approach is going to be rejected as well I will split the PR into smaller improvements/fixes that don't change the semantics, namely:
|
Happy for the incremental improvements. Also happy to continue with the approach to try to merge the APIs, but like I say in my previous comment, I think that the primary aspect of the approach must be to allow a pallet to state that a |
Closing. Will split into smaller PRs. |
A follow up on #5003
Related: #3419
Closes: paritytech/polkadot-sdk#365
This PR removes a special role
ValidateUnsigned
has inexecutive
. Instead we have aValidateUnsigned
extension defined inframe_system
, which dispatches the calls instead.This is not properly tested yet, and possibly not something we want to include for 2.0, but still wanted to make the PR visible.
Let me know what you think, CC @bkchr @jimmychu0807