feat: support custom transaction extension values - #2273
Conversation
9d1ea0b to
ce9a555
Compare
| /// specific transaction. | ||
| pub struct DefaultTransactionExtensions<T: Config> { | ||
| known: KnownDefaultTransactionExtensions<T>, | ||
| custom: BTreeMap<String, CustomTransactionExtensionValue>, |
There was a problem hiding this comment.
If we misspell the extension name we can now produce a wrongly singed extrinsic
This takes an arbitrary string, maybe we can easily compare against metadata.extrinsic().transaction_extensions_to_use_for_encoding()?
There was a problem hiding this comment.
Good catch – a typo silently dropped the extension. DefaultTransactionExtensions::new now validates each custom name against metadata.extrinsic().transaction_extensions_to_use_for_encoding() and errors with "Custom transaction extension '{name}' is not present in the runtime metadata" if it is absent. Covered by custom_extension_absent_from_metadata_is_rejected
|
|
||
| fn is_authorization_extension(&self, name: &str) -> bool { | ||
| frame_decode::extrinsics::TransactionExtensions::is_authorization_extension( | ||
| &self.known, |
There was a problem hiding this comment.
nit: This forwards only to known entries, so chain specific exntesions return false here? Would be worth having a subxt issue about this one to tackle later
There was a problem hiding this comment.
Filed #2276 for this, and left a comment on is_authorization_extension pointing at it.
Worth noting it is not purely cosmetic: in V5 the signer payload excludes the last authorization extension and everything before it, so a chain declaring its own authorization extension and supplying it via custom_extension would sign over bytes the runtime excludes. Latent for now since VerifyMultiSignature is the only authorization extension Subxt knows about.
| custom: Vec<(String, CustomTransactionExtensionValue)>, | ||
| } | ||
|
|
||
| enum CustomTransactionExtensionValue { |
There was a problem hiding this comment.
Do we plan to extend this? Maybe its easier to colapse and use directly Value?
There was a problem hiding this comment.
No plans to extend it, collapsed to Value directly, so the maps are now BTreeMap<String, Value> / Vec<(String, Value)>.
lexnv
left a comment
There was a problem hiding this comment.
The shape looks good, just some tiny bits and pieces!
We could probably add some more V5 tests (ie around authorization_extension_check_is_forwarded and is_authorization_extension and probably some tests with a custom extension positioned before and after VerifyMultiSignature)
|
Added the V5 coverage you asked for. Since
The |
Allow callers to provide metadata-aware values for custom transaction extensions without implementing a new `Config`. The value is encoded against the extension type from runtime metadata, which fixes signing on chains such as Paseo Asset Hub. `DefaultTransactionExtensions<T>` and its `Params` become structs with private fields, so custom values can travel alongside the known ones. `known()`, `known_mut()` and `custom()` preserve access to them, and the tuples live on as `KnownDefaultTransactionExtensions<T>` and `KnownDefaultExtrinsicParams<T>`.
A custom extension name the runtime does not declare was silently ignored, so a typo produced a transaction signed without the extension the caller asked for. Reject it instead, naming the extension. Tests move to `CheckWeight` and `WeightReclaim`, which the bundled metadata actually declares.
f6cd585 to
62050a4
Compare
A V5 extrinsic carries every extension value, including custom ones on either side of the authorization extension. Pin those bytes, and record why a custom extension can never report itself as an authorization extension.
62050a4 to
882503c
Compare
Summary
Allow callers to provide metadata-aware values for custom transaction extensions without implementing a new
Config:The value is encoded against the extension type from runtime metadata. This fixes signing on chains such as Paseo Asset Hub, where
RestrictOriginscontains a barebool.Fixes #2265.
Implementation
Breaking changes
DefaultTransactionExtensions<T>and itsParamstype changed from public tuple aliases to structs with private fields, so that custom extension values can be carried alongside the known ones. The tuples live on under new names:KnownDefaultTransactionExtensions<T>andKnownDefaultExtrinsicParams<T>.Code which configures transactions via the builder and passes the params through opaquely is source-compatible and produces byte-identical transactions. Code which relied on the tuple shape of the params migrates as follows:
DefaultExtrinsicParams::from_known(tuple)params.5 = ...params.known_mut().5 = ...¶ms.7¶ms.known().7Given the above, this should land in 0.51 rather than a 0.50.x patch.
Notes
cargo fmtfix for two test asserts that landed unformatted on master.