Skip to content
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

Support multiple extension versions in V5 extrinsics #4

Merged
merged 5 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ The format is based on [Keep a Changelog].

[Keep a Changelog]: http://keepachangelog.com/en/1.0.0/

## 0.4.0 (2024-10-21)

- Split `ExtrinsicTypeInfo` trait to get signature and extensions info separately, and support being given an extension version in the latter.
- Remove support for V5 signed extrinsics, which are no longer a thing (see [#3685](https://github.com/paritytech/polkadot-sdk/pull/3685) for context).

## 0.3.0 (2024-09-30)

- Fix `extrinsic.call_range()` and `extensions.range()` functions, and clarify descriptions.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "frame-decode"
version = "0.3.0"
version = "0.4.0"
edition = "2021"
description = "Decode extrinsics and storage from Substrate based chains"
license = "Apache-2.0"
Expand Down
75 changes: 45 additions & 30 deletions src/decoding/extrinsic_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ pub enum ExtrinsicDecodeError {
},
NotEnoughBytes,
VersionNotSupported(u8),
VersionTypeNotSupported(u8),
ExtrinsicTypeNotSupported {
version: u8,
extrinsic_type: u8,
},
CannotGetInfo(ExtrinsicInfoError<'static>),
CannotDecodeSignature(DecodeErrorTrace),
CannotDecodePalletIndex(parity_scale_codec::Error),
Expand Down Expand Up @@ -72,10 +75,13 @@ impl core::fmt::Display for ExtrinsicDecodeError {
"This extrinsic version ({extrinsic_version}) is not supported."
)
}
ExtrinsicDecodeError::VersionTypeNotSupported(version_ty) => {
ExtrinsicDecodeError::ExtrinsicTypeNotSupported {
version,
extrinsic_type,
} => {
write!(
f,
"This extrinsic version type ({version_ty}) is not supported; only Bare, Signed and General types are supported."
"The extrinsic type 0b{extrinsic_type:02b} is not supported (given extrinsic version {version})."
)
}
ExtrinsicDecodeError::CannotGetInfo(extrinsic_info_error) => {
Expand Down Expand Up @@ -554,23 +560,31 @@ where
return Err(ExtrinsicDecodeError::VersionNotSupported(version));
}

// We know about the following types of extrinsic.
// We know about the following types of extrinsics:
// - "Bare": no signature or extensions. V4 inherents encode the same as V5 bare.
// - "Signed": an address, signature and extensions. Only exists in V4.
// - "General": no signature, just extensions (one of which can include sig). Only exists in V5.
let version_ty = match version_type {
0b00 => ExtrinsicType::Bare,
0b10 => ExtrinsicType::Signed,
0b01 => ExtrinsicType::General,
_ => return Err(ExtrinsicDecodeError::VersionTypeNotSupported(version_type)),
0b10 if version == 4 => ExtrinsicType::Signed,
0b01 if version == 5 => ExtrinsicType::General,
_ => {
return Err(ExtrinsicDecodeError::ExtrinsicTypeNotSupported {
version,
extrinsic_type: version_type,
})
}
};

let curr_idx = |cursor: &mut &[u8]| (bytes.len() - cursor.len()) as u32;

let signature_info = info
.get_signature_info()
.map_err(|e| ExtrinsicDecodeError::CannotGetInfo(e.into_owned()))?;

// Signature part. Present for V4 or V5 signed extrinsics
// Signature part. Present for V4 signed extrinsics
let signature = (version_ty == ExtrinsicType::Signed)
.then(|| {
let signature_info = info
.get_signature_info()
.map_err(|e| ExtrinsicDecodeError::CannotGetInfo(e.into_owned()))?;

let address_start_idx = curr_idx(cursor);
decode_with_error_tracing(
cursor,
Expand Down Expand Up @@ -600,19 +614,20 @@ where
})
.transpose()?;

// Transaction extensions part. Present for Signed or General extrinsics.
// "General" extensions now have a single byte representing the extension version.
let extension_version = (version_ty == ExtrinsicType::General)
.then(|| u8::decode(cursor).map_err(ExtrinsicDecodeError::CannotDecodeExtensionsVersion))
.transpose()?;

// Signed and General extrinsics both now have a set of transaction extensions.
let extensions = (version_ty == ExtrinsicType::General || version_ty == ExtrinsicType::Signed)
.then(|| {
let transaction_extensions_version = if version_ty == ExtrinsicType::General
|| version == 5
{
u8::decode(cursor).map_err(ExtrinsicDecodeError::CannotDecodeExtensionsVersion)?
} else {
0
};
let extension_info = info
.get_extension_info(extension_version)
.map_err(|e| ExtrinsicDecodeError::CannotGetInfo(e.into_owned()))?;

let mut transaction_extensions = vec![];
for ext in signature_info.transaction_extension_ids {
for ext in extension_info.extension_ids {
let start_idx = curr_idx(cursor);
decode_with_error_tracing(
cursor,
Expand All @@ -634,24 +649,24 @@ where
}

Ok::<_, ExtrinsicDecodeError>(ExtrinsicExtensions {
transaction_extensions_version,
transaction_extensions_version: extension_version.unwrap_or(0),
transaction_extensions,
})
})
.transpose()?;

// Call data part
// All extrinsics now have the encoded call data.
let pallet_index_idx = curr_idx(cursor);
let pallet_index: u8 =
Decode::decode(cursor).map_err(ExtrinsicDecodeError::CannotDecodePalletIndex)?;
let call_index: u8 =
Decode::decode(cursor).map_err(ExtrinsicDecodeError::CannotDecodeCallIndex)?;
let extrinsic_info = info
.get_extrinsic_info(pallet_index, call_index)
let call_info = info
.get_call_info(pallet_index, call_index)
.map_err(|e| ExtrinsicDecodeError::CannotGetInfo(e.into_owned()))?;

let mut call_data = vec![];
for arg in extrinsic_info.args {
for arg in call_info.args {
let start_idx = curr_idx(cursor);
decode_with_error_tracing(
cursor,
Expand All @@ -660,8 +675,8 @@ where
scale_decode::visitor::IgnoreVisitor::new(),
)
.map_err(|e| ExtrinsicDecodeError::CannotDecodeCallData {
pallet_name: extrinsic_info.pallet_name.to_string(),
call_name: extrinsic_info.call_name.to_string(),
pallet_name: call_info.pallet_name.to_string(),
call_name: call_info.call_name.to_string(),
argument_name: arg.name.to_string(),
reason: e,
})?;
Expand All @@ -684,10 +699,10 @@ where
byte_len: bytes.len() as u32,
signature,
extensions,
pallet_name: extrinsic_info.pallet_name,
pallet_name: call_info.pallet_name,
pallet_index,
pallet_index_idx,
call_name: extrinsic_info.call_name,
call_name: call_info.call_name,
call_index,
call_data,
};
Expand Down
Loading
Loading