paritytech / parity-common

Collection of crates used in Parity projects
https://www.paritytech.io/
Apache License 2.0
288 stars 218 forks source link

Issues wrapping ethereum types and deriving scale-codec methods #679

Open drewstone opened 2 years ago

drewstone commented 2 years ago

I'm trying to implement wrapper types for ethereum_types types in order to provide other traits necessary for making my code compatible with https://github.com/sigp/lighthouse dependencies and Substrate. The following snippet demonstrates my intention.

macro_rules! uint_declare_wrapper_and_serde {
    ($name: ident, $len: expr) => {
        #[derive(
            Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Add, Sub, Mul, Div, Rem,
            AddAssign, SubAssign, MulAssign, DivAssign, RemAssign, Display, From, Into,
            Encode, EncodeLike, Decode, MaxEncodedLen, TypeInfo,
        )]
        // #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
        pub struct $name(pub ethereum_types::$name);

        impl Encodable for $name {
            fn rlp_append(&self, s: &mut RlpStream) {
                <ethereum_types::$name>::rlp_append(&self.0, s);
            }
        }

        impl Decodable for $name {
            fn decode(rlp: &Rlp) -> Result<Self, RlpDecoderError> {
                Ok($name(<ethereum_types::$name>::rlp_derive(rlp)?))
            }
        }
    };
}

uint_declare_wrapper_and_serde!(U64, 1);
uint_declare_wrapper_and_serde!(U128, 2);
uint_declare_wrapper_and_serde!(U256, 4);

Unfortunately, I keep fighting with the compiler who doesn't think that these types can implement Encode/Decode even though I am importing the package with those features. My TOML is below:

ethereum-types = { version = "0.12.1", features = ["codec", "rlp", "num-traits"], default-features = false }

Errors

error[E0599]: no function or associated item named `max_encoded_len` found for struct `ethereum_types::U64` in the current scope
  --> src/lib.rs:62:30
   |
62 |         pub struct $name(pub ethereum_types::$name);
   |                              ^^^^^^^^^^^^^^ function or associated item not found in `ethereum_types::U64`
...
78 | uint_declare_wrapper_and_serde!(U64, 1);
   | --------------------------------------- in this macro invocation
   |
   = help: items from traits can only be used if the trait is in scope
   = note: this error originates in the macro `uint_declare_wrapper_and_serde` (in Nightly builds, run with -Z macro-backtrace for more info)
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
   |
4  | use parity_scale_codec::max_encoded_len::MaxEncodedLen;
   |
drewstone commented 2 years ago

A similar thing happens with a trait for HXX types like H64 and H256, sometimes referencing traits not being present even though they are in the lib.rs.

error[E0599]: no function or associated item named `max_encoded_len` found for struct `ethereum_types::U64` in the current scope
  --> src/lib.rs:62:30
   |
62 |         pub struct $name(pub ethereum_types::$name);
   |                              ^^^^^^^^^^^^^^ function or associated item not found in `ethereum_types::U64`
...
78 | uint_declare_wrapper_and_serde!(U64, 1);
   | --------------------------------------- in this macro invocation
   |
   = help: items from traits can only be used if the trait is in scope
   = note: this error originates in the macro `uint_declare_wrapper_and_serde` (in Nightly builds, run with -Z macro-backtrace for more info)
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
   |
4  | use parity_scale_codec::max_encoded_len::MaxEncodedLen;
   |

If I try implementing the trait myself:

impl Encode for $name {
    fn encode(&self) -> Vec<u8> {
        self.0.encode()
    }
}

I get the following error as well:

error[E0599]: the method `encode` exists for struct `ethereum_types::H64`, but its trait bounds were not satisfied
   --> src/macros.rs:199:24
    |
199 |                   self.0.encode()
    |                          ^^^^^^ method cannot be called on `ethereum_types::H64` due to unsatisfied trait bounds
    |
   ::: src/lib.rs:27:1
    |
27  |   arr_ethereum_types_wrapper_impl!(H64, 8);
    |   ---------------------------------------- in this macro invocation
    |
   ::: /Users/drew/.cargo/registry/src/github.com-1ecc6299db9ec823/ethereum-types-0.12.1/src/hash.rs:33:1
    |
33  | / construct_fixed_hash! {
34  | |     #[cfg_attr(feature = "codec", derive(scale_info::TypeInfo))]
35  | |     pub struct H64(8);
36  | | }
    | | -
    | | |
    | | doesn't satisfy `<ethereum_types::H64 as Deref>::Target = _`
    | |_doesn't satisfy `ethereum_types::H64: WrapperTypeEncode`
    |   doesn't satisfy `ethereum_types::H64: parity_scale_codec::Encode`
    |
    = note: the following trait bounds were not satisfied:
            `<ethereum_types::H64 as Deref>::Target = _`
            which is required by `ethereum_types::H64: parity_scale_codec::Encode`
            `ethereum_types::H64: WrapperTypeEncode`
            which is required by `ethereum_types::H64: parity_scale_codec::Encode`
    = help: items from traits can only be used if the trait is in scope
    = note: this error originates in the macro `arr_ethereum_types_wrapper_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
   --> |src/lib.rs:4:1
    |
4   | use parity_scale_codec::codec::Encode;
    |
ordian commented 2 years ago

From what I can see, this is a general rust question. The compiler is your friend and it tells you what's wrong:

help: the following trait is implemented but not in scope; perhaps add a `use` for it:
   --> |src/lib.rs:4:1
    |
4   | use parity_scale_codec::codec::Encode;

In your implementation of the macro, you assume that Encode and Decode are in scope. Generally, that's not a good idea as it leads to the subpar developer experience, as you are seeing directly. Unless you're only planning to use this macro only where it's defined, then you'll have to import the required traits in the scope.

Take a look at how codec is implemented in https://github.com/paritytech/parity-common/blob/master/primitive-types/impls/codec/src/lib.rs. Note the $crate::codec::Encode and

#[doc(hidden)]
pub use parity_scale_codec as codec;