-
Notifications
You must be signed in to change notification settings - Fork 665
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
FRAME: Reintroduce TransactionExtension
as a replacement for SignedExtension
#3685
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
TransactionExtension
as a replacement for SignedExtension
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
Signed-off-by: georgepisaltu <george.pisaltu@parity.io>
/// Type to represent the version of the [Extension](TransactionExtension) used in this extrinsic. | ||
pub type ExtensionVersion = u8; | ||
/// Type to represent the extrinsic format version which defines an [UncheckedExtrinsic]. | ||
pub type ExtrinsicVersion = u8; |
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.
These two versions should go to the metadata (I propose Version
type, found at System.Version
constant).
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.
The extrinsic version (soon to be a vec of supported versions in V16 metadata) is already in there.
The extension version we should add in some way. I believe it changes when the set of transaction extensions in a runtime changes? In which case we either want to add one extension version to the metadata to go with the list of "current" transaction extensions, or we could to have a vec of (extension_version, list_of_extensions)
to reflect the current sets of extensions that the runtime supports and the version of each, if applicable.
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.
Probably adding it to System.Version
is the way to proceed. Afterwards we can add it to V16 metadata as you propose.
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.
@gui1117 @georgepisaltu thoughts here?
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.
The extension version we should add in some way. I believe it changes when the set of transaction extensions in a runtime changes?
The extension version here doesn't change if a runtime add or remove some transaction extensions.
It is 0 for all runtime currently.
If a transaction extension get added or removed, I think we should bump the transaction version in the runtime version (RuntimeVersion
struct, transaction_version
field), because it changes the execution of the extrinsic.
I don't exactly know when we would bump it but IIUC extension version is associated with the way transaction extensions are used in an extrinsic, in the implementation in sp-runtime.
I see UncheckedExtrinsic::new_transaction
assume extension version is 0, I guess putting it into parameter would make more sense.
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.
RFC99 defines the transaction extension version. This PR only adds encoding/decoding support for this to not break the format twice (as is menitoned in the RFC as well). Full support for the extension version is out of scope for this PR and will be added later. Metadata work is also a follow-up on this PR.
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 still think the extension version should be part of the inherited implication, so it can be signed in some way.
Otherwise supporting multiple version can lead to extrinsic being valid in some non-intended context.
See : #3685 (comment)
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.
Replied here and I think it has merit but I stand by my earlier comment above about this being out of scope for this PR.
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.
Yes I agree we should do in a follow-up.
// Construct the payload that the signature will be validated against. The inherited | ||
// implication contains the encoded bytes of the call and all of the extension data of the | ||
// extensions that follow in the `TransactionExtension` pipeline. | ||
// | ||
// In other words: | ||
// - extensions that precede this extension are ignored in terms of signature validation; | ||
// - extensions that follow this extension are included in the payload to be signed (as if | ||
// they were the entire `SignedExtension` pipeline in the traditional signed transaction | ||
// model). | ||
// | ||
// The encoded bytes of the payload are then hashed using `blake2_256`. | ||
let msg = inherited_implication.using_encoded(blake2_256); |
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.
Until now the payloads were only hashed if they exceed 256 bytes of length. Changing this now to always hash might be messy for tooling, signers, etc.
If done at least it has to be spec-ed like https://spec.polkadot.network/id-extrinsics#defn-extrinsic-signature or in an RFC.
Is there any reason why this should be changed now? I don't see any particular advantage.
WDYT @bkchr?
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.
An extrinsic format version bump always involves a breaking change, so tooling has to make an active effort to support new versions anyways. V5 extrinsics also introduce the extension version byte, so this would have been a breaking change even without the change to payload construction, we just bundled them together.
Even so, we provide compatibility with v4 extrinsics regarding encoding/decoding, so tools that don't yet offer support for this should use v4 extrinsics.
Before we release, we will introduce a new entry in the spec for v5 extrinsics, in addition to the one in v4 that you linked.
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.
Until now the payloads were only hashed if they exceed 256 bytes of length. Changing this now to always hash might be messy for tooling, signers, etc.
This was overseen in the RFC and should still be added in some extra RFC to clarify this.
@josepot and @carlosala raised a couple of good points in a call earlier re this PR, which I want to raise here: Extrinsic version number in metadata: keep it at 4Currently, the metadata (eg V14 and V15) has an The reason for this is for backward compatibility: after merging this PR, any apps that check that field in order to know which extrinsic version is supported by the chain will still see "4", and will therefore still be happy to continue submitting V4 extrinsics. If we update the number to 5, then there is a chance that apps that only know how to construct V4 extrinsics will assume that they no longer can submit them and start failing. When V16 metadata lands, we should support a vec of versions, and so can properly communicate to apps all of the versions that are supported. The Transaction Extension version field: keep it at 0 until V16 metadata lands.My understanding (and please correct if wrong) is that the transaction extension version will be incremented each time the set of transaction extensions changes. In V14 and V15 metadata, we currently only see the "latest" set of transaciton extensions. This means that if I submit an extrinsic using eg v0 transaction extensions when we are currently on v1, then I will not know how to decode this extrinsic using the metadata: I can decode the v1 list of extensions but do not know what the v0 list looks like. As long as the extension version stays at 0, we are ok, because there is only one possible list of extensions that is valid in an extrinsic anyway, and this list is available in the metadata. When V16 metadata lands, the I hope both of those points are reasonable; please @josepot and @carlosala feel free to elaborate on anything I might have missed! \cc @lexnv |
About supporting multiple set of transaction extension and having in the metadata: The reason is that 2 extrinsic could be valid for 2 set of transaction extension. Also we should probably use a compact and not a u8. |
This is already covered by the current set of extensions that we're running, namely The point of the extension version, as described in the RFC, is to enable the runtime to introduce changes to the extensions used while supporting the old one in a backwards compatible way. |
As mentioned in other comments, the extension version is:
There will be another PR altogether adding full support for versioned extensions and the version will not be hardcoded for all The rest of the points about the metadata changes/additions should probably be discussed separately, maybe on the metadata v16 issue or a new one (@lexnv). We should focus on the core runtime functionality aspect in this PR and get it through, and address the metadata stuff in follow up efforts, according to the release timeline stated earlier. |
check genesis is signing for a blockchain, check spec version is signing for a runtime specification. We are talking about supporting multiple set of transaction extension in one runtime of one blockchain. Example: in one runtime we have:
I send my transaction. How can I ensure my tip goes to the correct destination, and nobody can change the meaning of my intended signature. I know such situation will be rare, but better to avoid automatically, rather than adding another burden on runtime developer. |
Absolutely. Since that |
@gui1117 I see now what corner case you're talking about. I personally think this would be extremely improbable, but if we want to address it, we should just provide an extension at the |
I was worried it would be difficult to get access to the extension version. But we can always leverage So OK, let's do in follow-up |
If we were to use a |
Yeah generally the extension_version stuff should only be added by this pr to the format of the extrinsic and then later implemented properly. |
Regarding the signing of the extension version. I would not want to have an extra extension for this... For signed transactions it isn't such a problem and we could directly include it into the signature. However, when it comes to general transaction we will have a problem. Or we pass it down as inherited implication? |
We can definitely come up with something to include it in the inherited implication. First idea, carry the version over from The problems if we do it like this are:
I like the extension solution and I like the inherited implication solution, but we need to check with the Another idea would be to merge this as is with the current |
I think before all of that; we need to come up with a structure that allows the changes proposed in this PR without having flaws in terms of security, ability to decode, etc.
If needed I can work on the first draft of the RFC. |
The solutions proposed have different payload schemes, different encoding and different assumptions built into them. This is not just for Signed extrinsics, all extrinsic types need to be captured within this solution, regardless of the approach we choose. V4 is still supported but doesn't change. We need one RFC (for v5), and we need to agree on an approach first before being able to write anything, which is the point of this comment, because the RFC needs to define the encoding format which is dependent on the chosen solution.
As mentioned multiple times, this PR only introduces the encode/decode logic for the extension version. Full support is to be added later in subsequent PRs.
Again the remaining metadata work is out of scope for this PR. Metadata v16 is a separate effort, but with the same release schedule. Moreover, both this PR with extrinsic v5 as well as metadata v16 are unreleased and they will release together. Extrinsic Horizon should be an incremental effort and the code in this PR doesn't break any tests. We should track the metadata related issues in the metadata v16 meta issue, not here. It's good that we're identifying issues and things we need to improve before the release, but we need to stop the scope creep in this PR. |
Let's ensure Westend and/or Rococo do not get released with these new changes then 👍🏻 |
About metadata can we just put extrinsic version 4 in the metadata, and not have anything else until metadata v16. About signature of extension version. Having new transaction extension seems hard. We would need to pass the extension version as an argument in Without transaction extension, IMO the solution is to have in the inherited implication (other solution is to have it in a parameter in |
As already proposed here multiple times, the metadata should return version 4. This way we don't need any fixes in pjs right now until the stuff is sorted out. This is also much better, because it allows us to still change some things with version 5. TBH, I don't see this getting anywhere ready for having extrinsics with version 5 being stable until december. They go into the release, but we will continue to use version 4 "by default".
I don't get the problem? General transactions will be backwards compatible like signed transactions when it comes to extensions. Or do you mean how to ensure that
Still think this is the best way. Any kind of general transaction that will need some kind of authorization will use a signature anyway that will then also include the extension version in the signed data, like we should do it for signed transactions.
Signed transactions are getting deprecated?
The extension version should be put next to the already existing extrinsic version. Don't see a reason to include this in |
// TODO: Remove below once `ValidateUnsigned` is deprecated and the only `Bare` | ||
// extrinsics left are inherents. | ||
#[allow(deprecated)] | ||
Extension::post_dispatch_bare_compat(info, &post_info, len, &pd_res)?; |
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.
Is there something wrong here?
Formerly signed extensions post_dispatch
was executed for unsigned extrinsics. This is important for CheckWeight
which registers the used weight inside the post dispatch.
But now it no longer executes it.
AFAICT nothing register the weight for unsigned transactions and inherents.
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 polkadot-sdk only CheckWeight
and StorageWeightReclaim
needs to execute some logic in before and after dispatch of inherents and unsigned transactions. AFAICT.
Maybe we can move both in frame-system or frame-executive.
Like have some extrinsic hook with pre-dispatch and post-dispatch.
EDIT: Or the other direction: runtime provides an instance of transaction extension to be used for inherents and unsigned transaction. And they are validated, prepared and post-dispatch is executed.
The validation for unsigned transaction is simply the ValidateUnsigned logic.
The validation for inherent is: source is local or in-block and also ProvideInherent::is_inherent
returns true.
I meant it could be replace with a general transaction with a transaction extension which contains the payload. In the far future. |
Just discussed this with @georgepisaltu via DM. Generally I think we should directly remove it. There is no real benefit in adding support for it and then removing it again. Changing the format is quite hard in the ecosystem and we should not do this again. This would also require extrinsic version 6. We need support for extrinsic version and then we could remove signed transactions directly from v5. Nevertheless, the pr can be merged as it is right now as long as we expose v4 in the metadata. |
With general transactions, we don't have a way to specify which
If we keep version 4 in metadata v14/15 I agree. If version 5 gets ever to metadata v14/15 then we need to add that byte somewhere, otherwise we can't construct extrinsics from outside of the runtime. |
Original PR #2280 reverted in #3665
This PR reintroduces the reverted functionality with additional changes, related effort here. Description is copied over from the original PR
First part of Extrinsic Horizon
Introduces a new trait
TransactionExtension
to replaceSignedExtension
. Introduce the idea of transactions which obey the runtime's extensions and have according Extension data (né Extra data) yet do not have hard-coded signatures.Deprecate the terminology of "Unsigned" when used for transactions/extrinsics owing to there now being "proper" unsigned transactions which obey the extension framework and "old-style" unsigned which do not. Instead we have General for the former and Bare for the latter. (Ultimately, the latter will be phased out as a type of transaction, and Bare will only be used for Inherents.)
Types of extrinsic are now therefore:
ValidateUnsigned
(deprecated) and the_bare_compat
bits ofTransactionExtension
(deprecated).ProvideInherent
.TransactionExtension
.TransactionExtension
differs fromSignedExtension
because:pre_dispatch
is renamed toprepare
and need not contain the checks present invalidate
.validate
andprepare
is passed anOrigin
rather than aAccountId
.validate
may pass arbitrary information intoprepare
via a new user-specifiable typeVal
.AdditionalSigned
/additional_signed
is renamed toImplicit
/implicit
. It is encoded for the entire transaction and passed in to each extension as a new argument tovalidate
. This facilitates the ability of extensions to acts as underlying crypto.There is a new
DispatchTransaction
trait which contains only default function impls and is impl'ed for anyTransactionExtension
impler. It provides several utility functions which reduce some of the tedium from usingTransactionExtension
(indeed, none of its regular functions should now need to be called directly).Three transaction version discriminator ("versions") are now permissible (RFC here) in extrinsic version 5:
For the New-school General Transaction, it becomes trivial for authors to publish extensions to the mechanism for authorizing an Origin, e.g. through new kinds of key-signing schemes, ZK proofs, pallet state, mutations over pre-authenticated origins or any combination of the above.
UncheckedExtrinsic
still maintains encode/decode backwards compatibility with extrinsic version 4, where the first byte was encoded as:Now,
UncheckedExtrinsic
contains aPreamble
and the actual call. ThePreamble
describes the type of extrinsic as follows:Code Migration
NOW: Getting it to build
Wrap your
SignedExtension
s inAsTransactionExtension
. This should be accompanied by renaming your aggregate type in line with the new terminology. E.g. Before:After:
You'll also need to alter any transaction building logic to add a
.into()
to make the conversion happen. E.g. Before:After:
SOON: Migrating to
TransactionExtension
Most
SignedExtension
s can be trivially converted to become aTransactionExtension
. There are a few things to know.SignedExtension
, you should now implement two traits individually:TransactionExtensionBase
andTransactionExtension
.fn weight
.TransactionExtensionBase
This trait takes care of anything which is not dependent on types specific to your runtime, most notably
Call
.AdditionalSigned
/additional_signed
is renamed toImplicit
/implicit
.weight
function. If your extension is associated with a pallet, you'll probably want to do this via the pallet's existing benchmarking infrastructure.TransactionExtension
Generally:
pre_dispatch
is nowprepare
and you should not reexecute thevalidate
functionality in there!AsSystemOriginSigner::as_system_origin_signer
.Pre
, calledVal
. This defines data which is passed fromvalidate
intoprepare
. This is important since you should not be duplicating logic fromvalidate
toprepare
, you need a way of passing your working from the former into the latter. This is it.Call
type parameter.Call
is the runtime call type which used to be an associated type; you can just move it to become a type parameter for your trait impl.AccountId
associated type any more. Just remove it.Regarding
validate
:validate
; all can be ignored when migrating fromSignedExtension
.validate
returns a tuple on success; the second item in the tuple is the new ticket typeSelf::Val
which gets passed in toprepare
. If you use any information extracted duringvalidate
(off-chain and on-chain, non-mutating) inprepare
(on-chain, mutating) then you can pass it through with this. For the tuple's last item, just return theorigin
argument.Regarding
prepare
:pre_dispatch
, but there is one change:validate
!!SignedExtension
which was required to run the same checks inpre_dispatch
as invalidate
.)Regarding
post_dispatch
:TransactionExtension
,Pre
is always defined, so the first parameter isSelf::Pre
rather thanOption<Self::Pre>
.If you make use of
SignedExtension::validate_unsigned
orSignedExtension::pre_dispatch_unsigned
, then:origin
isNone
.TransactionExtension
s' data.ValidateUnsigned
can still be used (for now) if you need to be able to construct transactions which contain none of the extension data, however these will be phased out in stage 2 of the Transactions Horizon, so you should consider moving to an extension-centric design.