Skip to content

Commit

Permalink
feat: primitive utils and improvements (#52)
Browse files Browse the repository at this point in the history
* feat: improve primitives

* feat: implement Arbitrary for Bytes and wrapped FixedBytes

* feat: improve primitives utils

* fix: hex breaking change

* fixes

* chore: clippy

* serde

* feat: add more serde utils and impls

* ignore test

* fix: RLP `Encode` impl for `FixedBytes` and `bool`

* proptest

* chore: clippy

* are you ok proptest?

* improve serde impls

* fix: Signed `to_{b,l}e_bytes`

* fix: alias exports

* fix: docs

* refactor: improve bloom implementation quality (#59)

* refactor: improve bloom implementation quality

* chore: fmt

* fixes: address PR review comments

* chore: move `create{,2}_address*` utils to assoc methods on `Address`

* test: `FixedBytes` serde

---------

Co-authored-by: James Prestwich <james@prestwi.ch>
  • Loading branch information
DaniPopes and prestwich authored Jun 1, 2023
1 parent 84b78ee commit 70edd50
Show file tree
Hide file tree
Showing 34 changed files with 1,422 additions and 554 deletions.
15 changes: 10 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,25 @@ ruint-macro = { version = "1.0.2", git = "https://github.com/recmo/uint", defaul

# serde
serde = { version = "1.0", default-features = false }
serde_json = { version = "1.0", default-features = false }
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }

# macros
proc-macro2 = "1.0"
quote = "1.0"
syn = "2.0"

derive_more = "0.99"
hex-literal = "0.4"
strum = { version = "0.24", features = ["derive"] }
num_enum = "0.6"
thiserror = "1.0"

# misc
arbitrary = { version = "1.3", default-features = false }
arrayvec = { version = "0.7.2", default-features = false }
bytes = { version = "1.4", default-features = false }
hex = { package = "const-hex", version = ">=1.3", default-features = false }
hex-literal = "0.4"
proptest = { version = "1.1", default-features = false }
hex = { package = "const-hex", version = ">=1.5", default-features = false, features = ["alloc"] }
once_cell = "1.17"
proptest = "1.1"
proptest-derive = "0.3"
thiserror = "1.0"
tiny-keccak = "2.0"
6 changes: 5 additions & 1 deletion crates/dyn-abi/src/eip712/typed_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,14 @@ pub struct TypedData {
/// the signature (e.g. the dapp, protocol, etc. that it's intended for).
/// This data is used to construct the domain seperator of the message.
pub domain: Eip712Domain,

/// The custom types used by this message.
pub resolver: Resolver,
#[serde(rename = "primaryType")]

/// The type of the message.
#[serde(rename = "primaryType")]
pub primary_type: String,

/// The message to be signed.
pub message: serde_json::Value,
}
Expand Down Expand Up @@ -233,6 +236,7 @@ mod tests {
use serde_json::json;

#[test]
#[ignore = "Uint Serde"]
fn test_full_domain() {
let json = json!({
"types": {
Expand Down
8 changes: 2 additions & 6 deletions crates/dyn-abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(not(feature = "std"), no_std)]
#![doc = include_str!("../README.md")]
#![warn(
missing_docs,
unreachable_pub,
Expand All @@ -17,11 +17,7 @@
clippy::missing_const_for_fn
)]
#![deny(unused_must_use, rust_2018_idioms)]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
))]
#![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]

#[macro_use]
extern crate alloc;
Expand Down
94 changes: 53 additions & 41 deletions crates/dyn-abi/src/value.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{no_std_prelude::*, Word};
use ethers_primitives::{aliases::*, Address, I256, U256};
use ethers_primitives::{Address, I256, U256};

/// This type represents a Solidity value that has been decoded into rust. It
/// is broadly similar to `serde_json::Value` in that it is an enum of possible
Expand Down Expand Up @@ -32,7 +32,7 @@ pub enum DynSolValue {
name: String,
/// The struct's prop names, in declaration order.
prop_names: Vec<String>,
/// A inner types.
/// The inner types.
tuple: Vec<DynSolValue>,
},
/// A user-defined value type.
Expand Down Expand Up @@ -66,7 +66,7 @@ impl DynSolValue {
/// Fallible cast to a single word. Will succeed for any single-word type.
pub fn as_word(&self) -> Option<Word> {
match self {
Self::Address(a) => Some((*a).into()),
Self::Address(a) => Some(a.into_word()),
Self::Bool(b) => Some({
let mut buf = [0u8; 32];
if *b {
Expand Down Expand Up @@ -185,12 +185,12 @@ impl DynSolValue {
/// Encodes the packed value and appends it to the end of a byte array.
pub fn encode_packed_to(&self, buf: &mut Vec<u8>) {
match self {
DynSolValue::Address(addr) => buf.extend_from_slice(addr.as_bytes()),
DynSolValue::Bool(b) => buf.push(*b as u8),
DynSolValue::Bytes(bytes) => buf.extend_from_slice(bytes),
DynSolValue::FixedBytes(word, size) => buf.extend_from_slice(&word.as_bytes()[..*size]),
DynSolValue::Int(num, size) => {
let mut bytes = num.to_be_bytes();
Self::Address(addr) => buf.extend_from_slice(addr.as_bytes()),
Self::Bool(b) => buf.push(*b as u8),
Self::Bytes(bytes) => buf.extend_from_slice(bytes),
Self::FixedBytes(word, size) => buf.extend_from_slice(&word.as_bytes()[..*size]),
Self::Int(num, size) => {
let mut bytes = num.to_be_bytes::<32>();
let start = 32 - *size;
if num.is_negative() {
bytes[start] |= 0x80;
Expand All @@ -199,17 +199,17 @@ impl DynSolValue {
}
buf.extend_from_slice(&bytes[start..])
}
DynSolValue::Uint(num, size) => {
Self::Uint(num, size) => {
buf.extend_from_slice(&num.to_be_bytes::<32>().as_slice()[(32 - *size)..])
}
DynSolValue::String(s) => buf.extend_from_slice(s.as_bytes()),
DynSolValue::Tuple(inner)
| DynSolValue::Array(inner)
| DynSolValue::FixedArray(inner)
| DynSolValue::CustomStruct { tuple: inner, .. } => {
inner.iter().for_each(|v| v.encode_packed_to(buf));
Self::String(s) => buf.extend_from_slice(s.as_bytes()),
Self::Tuple(inner)
| Self::Array(inner)
| Self::FixedArray(inner)
| Self::CustomStruct { tuple: inner, .. } => {
inner.iter().for_each(|v| v.encode_packed_to(buf))
}
DynSolValue::CustomValue { inner, .. } => buf.extend_from_slice(inner.as_bytes()),
Self::CustomValue { inner, .. } => buf.extend_from_slice(inner.as_bytes()),
}
}

Expand All @@ -222,65 +222,77 @@ impl DynSolValue {
}

impl From<Address> for DynSolValue {
#[inline]
fn from(value: Address) -> Self {
Self::Address(value)
}
}

impl From<bool> for DynSolValue {
#[inline]
fn from(value: bool) -> Self {
Self::Bool(value)
}
}

impl From<Vec<u8>> for DynSolValue {
#[inline]
fn from(value: Vec<u8>) -> Self {
Self::Bytes(value)
}
}

macro_rules! impl_from_int {
($size:ty) => {
impl From<$size> for DynSolValue {
fn from(value: $size) -> Self {
let bits = <$size>::BITS as usize;
let bytes = bits / 8;
($($t:ty),+) => {$(
impl From<$t> for DynSolValue {
#[inline]
fn from(value: $t) -> Self {
const BITS: usize = <$t>::BITS as usize;
const BYTES: usize = BITS / 8;
const _: () = assert!(BYTES <= 32);

let mut word = if value.is_negative() {
ethers_primitives::B256::repeat_byte(0xff)
} else {
ethers_primitives::B256::default()
};
word[32 - bytes..].copy_from_slice(&value.to_be_bytes());
word[32 - BYTES..].copy_from_slice(&value.to_be_bytes());

Self::Int(I256::from_be_bytes(word.into()), bits)
Self::Int(I256::from_be_bytes(word.0), BITS)
}
}
};
($($size:ty),+) => {
$(impl_from_int!($size);)+
};
)+};
}

impl_from_int!(
i8, i16, i32, i64, isize, i128, I24, I40, I48, I56, I72, I80, I88, I96, I104, I112, I120, I128,
I136, I144, I152, I160, I168, I176, I184, I192, I200, I208, I216, I224, I232, I240, I248, I256
);
impl_from_int!(i8, i16, i32, i64, isize, i128);

impl From<I256> for DynSolValue {
#[inline]
fn from(value: I256) -> Self {
Self::Int(value, 256)
}
}

macro_rules! impl_from_uint {
($size:ty) => {
impl From<$size> for DynSolValue {
fn from(value: $size) -> Self {
Self::Uint(U256::from(value), <$size>::BITS as usize)
($($t:ty),+) => {$(
impl From<$t> for DynSolValue {
#[inline]
fn from(value: $t) -> Self {
Self::Uint(U256::from(value), <$t>::BITS as usize)
}
}
};
($($size:ty),+) => {
$(impl_from_uint!($size);)+
};
)+};
}

// TODO: more?
impl_from_uint!(u8, u16, u32, u64, usize, u128, U256);
impl_from_uint!(u8, u16, u32, u64, usize, u128);

impl From<U256> for DynSolValue {
#[inline]
fn from(value: U256) -> Self {
Self::Uint(value, 256)
}
}

impl From<String> for DynSolValue {
fn from(value: String) -> Self {
Expand Down
32 changes: 18 additions & 14 deletions crates/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
description = "Fundamental ethereum types shared by revm, reth and ethers"
readme = "README.md"
keywords = ["ethereum", "ethers", "revm", "reth"]
categories = ["cryptography::cryptocurrencies"]
categories = ["data-structures", "cryptography::cryptocurrencies"]

edition.workspace = true
rust-version.workspace = true
Expand All @@ -15,34 +15,38 @@ repository.workspace = true

[dependencies]
# eth
ruint = { workspace = true, features = ["rlp", "serde"] }
ruint = { workspace = true, features = ["serde"] }

# utility
derive_more = "0.99"
tiny-keccak = { workspace = true, features = ["keccak"] }
bytes.workspace = true
getrandom = "0.2"
hex.workspace = true
itoa = "1"
tiny-keccak = { workspace = true, features = ["keccak"] }

# optional
serde = { workspace = true, features = ["derive"], optional = true }
# macros
derive_more.workspace = true

# rlp support
# rlp
ethers-rlp = { workspace = true, optional = true }
bytes = { workspace = true, optional = true }

# prop tests
# serde
serde = { workspace = true, optional = true }

# arbitrary
arbitrary = { workspace = true, features = ["derive"], optional = true }
proptest = { workspace = true, optional = true }
proptest-derive = { workspace = true, optional = true }

[dev-dependencies]
hex-literal = "0.4"
hex-literal.workspace = true
serde_json.workspace = true

[features]
default = ["std", "rlp", "serde", "hex/std"]
std = ["serde/std", "ethers-rlp?/std", "bytes?/std", "proptest?/std"]
rlp = ["dep:ethers-rlp", "dep:bytes"]
serde = ["dep:serde", "ruint/serde"]
default = ["std", "rlp", "serde"]
std = ["bytes/std", "hex/std", "ethers-rlp?/std", "proptest?/std", "serde/std"]
rlp = ["dep:ethers-rlp", "ruint/fastrlp"]
serde = ["dep:serde", "bytes/serde", "hex/serde", "ruint/serde"]
arbitrary = [
"ruint/arbitrary",
"ruint/proptest",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use super::Signed;
//! Type aliases for common primitive types.

use crate::{Signed, B256};

pub use ruint::aliases::{
U0, U1, U1024, U128, U16, U160, U192, U2048, U256, U32, U320, U384, U4096, U448, U512, U64, U8,
};

/// The 0-bit signed integer type, capable of representing 0.
pub type I0 = Signed<0, 0>;
Expand Down Expand Up @@ -101,3 +107,31 @@ pub type I248 = Signed<248, 4>;

/// 256-bit signed integer type.
pub type I256 = Signed<256, 4>;

/// A block hash.
pub type BlockHash = B256;

/// A block number.
pub type BlockNumber = u64;

/// A transaction hash is a kecack hash of an RLP encoded signed transaction.
pub type TxHash = B256;

/// The sequence number of all existing transactions.
pub type TxNumber = u64;

/// The index of transaction in a block.
pub type TxIndex = u64;

/// Chain identifier type (introduced in EIP-155).
pub type ChainId = u64;

/// An account storage key.
pub type StorageKey = B256;

/// An account storage value.
pub type StorageValue = U256;

/// Solidity contract functions are addressed using the first four byte of the
/// Keccak-256 hash of their signature
pub type Selector = [u8; 4];
Loading

0 comments on commit 70edd50

Please sign in to comment.