Skip to content

Commit

Permalink
Support more types in TypeWithDefault (paritytech#6411)
Browse files Browse the repository at this point in the history
# Description

When using `TypeWithDefault<u32, ..>` as the default nonce provider to
overcome the [replay
attack](https://wiki.polkadot.network/docs/transaction-attacks#replay-attack)
issue, it fails to compile due to `TypeWithDefault<u32, ..>:
TryFrom<u64>` is not satisfied (which is required by trait
`BaseArithmetic`).

This is because the blanket implementation `TryFrom<U> for T where U:
Into<T>` only impl `TryFrom<u16>` and `TryFrom<u8>` for `u32` since
`u32` only impl `Into` for `u16` and `u8` but not `u64`.

This PR fixes the issue by adding `TryFrom<u16/u32/u64/u128>` and
`From<u8/u16/u32/u64/u128>` impl (using macro) for
`TypeWithDefault<u8/u16/u32/u64/u128, ..>` and removing the blanket impl
(otherwise the compiler will complain about conflicting impl), such that
`TypeWithDefault<u8/u16/u32/u64/u128, ..>: AtLeast8/16/32Bit` is
satisfied.

## Integration

This PR adds support to more types to be used with `TypeWithDefault`,
existing code that used `u64` with `TypeWithDefault` should not be
affected, an unit test is added to ensure that.

## Review Notes

This PR simply makes `TypeWithDefault<u8/u16/u32/u64/u128, ..>:
AtLeast8/16/32Bit` satisfied

---------

Signed-off-by: linning <[email protected]>
  • Loading branch information
NingLin-P authored Nov 14, 2024
1 parent dbeea18 commit 5bc571b
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 30 deletions.
10 changes: 10 additions & 0 deletions prdoc/pr_6411.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
title: "Support more types in TypeWithDefault"

doc:
- audience: Runtime Dev
description: |
This PR supports more integer types to be used with `TypeWithDefault` and makes `TypeWithDefault<u8/u16/u32, ..>: BaseArithmetic` satisfied

crates:
- name: sp-runtime
bump: patch
123 changes: 93 additions & 30 deletions substrate/primitives/runtime/src/type_with_default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,6 @@ impl<T, D: Get<T>> Default for TypeWithDefault<T, D> {
}
}

impl<T: From<u16>, D: Get<T>> From<u16> for TypeWithDefault<T, D> {
fn from(value: u16) -> Self {
Self::new(value.into())
}
}

impl<T: From<u32>, D: Get<T>> From<u32> for TypeWithDefault<T, D> {
fn from(value: u32) -> Self {
Self::new(value.into())
}
}

impl<T: From<u64>, D: Get<T>> From<u64> for TypeWithDefault<T, D> {
fn from(value: u64) -> Self {
Self::new(value.into())
}
}

impl<T: CheckedNeg, D: Get<T>> CheckedNeg for TypeWithDefault<T, D> {
fn checked_neg(&self) -> Option<Self> {
self.0.checked_neg().map(Self::new)
Expand Down Expand Up @@ -205,24 +187,45 @@ impl<T: AddAssign, D: Get<T>> AddAssign for TypeWithDefault<T, D> {
}
}

impl<T: From<u8>, D: Get<T>> From<u8> for TypeWithDefault<T, D> {
fn from(value: u8) -> Self {
Self::new(value.into())
}
}

impl<T: Display, D: Get<T>> Display for TypeWithDefault<T, D> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.0)
}
}

impl<T: TryFrom<u128>, D: Get<T>> TryFrom<u128> for TypeWithDefault<T, D> {
type Error = <T as TryFrom<u128>>::Error;
fn try_from(n: u128) -> Result<TypeWithDefault<T, D>, Self::Error> {
T::try_from(n).map(Self::new)
}
}
macro_rules! impl_from {
($for_type:ty $(, $from_type:ty)*) => {
$(
impl<D: Get<$for_type>> From<$from_type> for TypeWithDefault<$for_type, D> {
fn from(value: $from_type) -> Self {
Self::new(value.into())
}
}
)*
}
}
impl_from!(u128, u128, u64, u32, u16, u8);
impl_from!(u64, u64, u32, u16, u8);
impl_from!(u32, u32, u16, u8);
impl_from!(u16, u16, u8);
impl_from!(u8, u8);

macro_rules! impl_try_from {
($for_type:ty $(, $try_from_type:ty)*) => {
$(
impl<D: Get<$for_type>> TryFrom<$try_from_type> for TypeWithDefault<$for_type, D> {
type Error = <$for_type as TryFrom<$try_from_type>>::Error;
fn try_from(n: $try_from_type) -> Result<TypeWithDefault<$for_type, D>, Self::Error> {
<$for_type as TryFrom<$try_from_type>>::try_from(n).map(Self::new)
}
}
)*
}
}
impl_try_from!(u8, u16, u32, u64, u128);
impl_try_from!(u16, u32, u64, u128);
impl_try_from!(u32, u64, u128);
impl_try_from!(u64, u128);

impl<T: TryFrom<usize>, D: Get<T>> TryFrom<usize> for TypeWithDefault<T, D> {
type Error = <T as TryFrom<usize>>::Error;
Expand Down Expand Up @@ -504,3 +507,63 @@ impl<T: HasCompact, D: Get<T>> CompactAs for TypeWithDefault<T, D> {
Ok(Self::new(val))
}
}

#[cfg(test)]
mod tests {
use super::TypeWithDefault;
use sp_arithmetic::traits::{AtLeast16Bit, AtLeast32Bit, AtLeast8Bit};
use sp_core::Get;

#[test]
#[allow(dead_code)]
fn test_type_with_default_impl_base_arithmetic() {
trait WrapAtLeast8Bit: AtLeast8Bit {}
trait WrapAtLeast16Bit: AtLeast16Bit {}
trait WrapAtLeast32Bit: AtLeast32Bit {}

struct Getu8;
impl Get<u8> for Getu8 {
fn get() -> u8 {
0
}
}
type U8WithDefault = TypeWithDefault<u8, Getu8>;
impl WrapAtLeast8Bit for U8WithDefault {}

struct Getu16;
impl Get<u16> for Getu16 {
fn get() -> u16 {
0
}
}
type U16WithDefault = TypeWithDefault<u16, Getu16>;
impl WrapAtLeast16Bit for U16WithDefault {}

struct Getu32;
impl Get<u32> for Getu32 {
fn get() -> u32 {
0
}
}
type U32WithDefault = TypeWithDefault<u32, Getu32>;
impl WrapAtLeast32Bit for U32WithDefault {}

struct Getu64;
impl Get<u64> for Getu64 {
fn get() -> u64 {
0
}
}
type U64WithDefault = TypeWithDefault<u64, Getu64>;
impl WrapAtLeast32Bit for U64WithDefault {}

struct Getu128;
impl Get<u128> for Getu128 {
fn get() -> u128 {
0
}
}
type U128WithDefault = TypeWithDefault<u128, Getu128>;
impl WrapAtLeast32Bit for U128WithDefault {}
}
}

0 comments on commit 5bc571b

Please sign in to comment.