Skip to content

Commit

Permalink
Merge pull request #403 from Evalir/evalir/fastrlp-04
Browse files Browse the repository at this point in the history
feat: add support for [email protected]
  • Loading branch information
prestwich authored Nov 4, 2024
2 parents b81aa0a + 947d0f5 commit 107f9d0
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Support for sqlx @ 0.8 ([#400])
- Support for fastrlp @ 0.4 ([#401])
- Added support for [`subtle`](https://docs.rs/subtle) and [`der`](https://docs.rs/der) ([#399])

### Removed
Expand All @@ -21,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

[#399]: https://github.com/recmo/uint/pull/399
[#400]: https://github.com/recmo/uint/pull/400
[#401]: https://github.com/recmo/uint/pull/401

## [1.12.3] - 2024-06-03

Expand Down
11 changes: 8 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ arbitrary = { version = "1", optional = true, default-features = false }
ark-ff-03 = { version = "0.3.0", package = "ark-ff", optional = true, default-features = false }
ark-ff-04 = { version = "0.4.0", package = "ark-ff", optional = true, default-features = false }
bn-rs = { version = "0.2", optional = true, default-features = true }
fastrlp = { version = "0.3", optional = true, default-features = false, features = [
fastrlp-03 = { version = "0.3", package = "fastrlp", optional = true, default-features = false, features = [
"alloc",
] }
fastrlp-04 = { version = "0.4", package = "fastrlp", optional = true, default-features = false, features = [
"alloc",
] }
num-bigint = { version = "0.4", optional = true, default-features = false }
Expand Down Expand Up @@ -110,7 +113,8 @@ std = [
"ark-ff-03?/std",
"ark-ff-04?/std",
"bytes?/std",
"fastrlp?/std",
"fastrlp-03?/std",
"fastrlp-04?/std",
"num-bigint?/std",
"num-integer?/std",
"num-traits?/std",
Expand Down Expand Up @@ -144,7 +148,8 @@ ark-ff-04 = ["dep:ark-ff-04"]
bn-rs = ["dep:bn-rs", "std"]
bytemuck = ["dep:bytemuck"]
der = ["dep:der", "alloc"] # TODO: also have alloc free der impls.
fastrlp = ["dep:fastrlp", "alloc"]
fastrlp = ["dep:fastrlp-03", "alloc"]
fastrlp-04 = ["dep:fastrlp-04", "alloc"]
num-bigint = ["dep:num-bigint", "alloc"]
num-integer = ["dep:num-integer", "num-traits", "alloc"]
num-traits = ["dep:num-traits", "alloc"]
Expand Down
2 changes: 1 addition & 1 deletion src/support/fastrlp.rs → src/support/fastrlp_03.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#![cfg_attr(docsrs, doc(cfg(feature = "fastrlp")))]

use crate::Uint;
use fastrlp::{
use fastrlp_03::{
length_of_length, BufMut, Decodable, DecodeError, Encodable, Header, MaxEncodedLen,
MaxEncodedLenAssoc, EMPTY_STRING_CODE,
};
Expand Down
171 changes: 171 additions & 0 deletions src/support/fastrlp_04.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//! Support for the [`fastrlp`](https://crates.io/crates/fastrlp) crate.
#![cfg(feature = "fastrlp-04")]
#![cfg_attr(docsrs, doc(cfg(feature = "fastrlp-04")))]

use crate::Uint;
use fastrlp_04::{
length_of_length, BufMut, Decodable, DecodeError, Encodable, Header, MaxEncodedLen,
MaxEncodedLenAssoc, EMPTY_STRING_CODE,
};

const MAX_BITS: usize = 55 * 8;

/// Allows a [`Uint`] to be serialized as RLP.
///
/// See <https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/>
impl<const BITS: usize, const LIMBS: usize> Encodable for Uint<BITS, LIMBS> {
#[inline]
fn length(&self) -> usize {
let bits = self.bit_len();
if bits <= 7 {
1
} else {
let bytes = (bits + 7) / 8;
bytes + length_of_length(bytes)
}
}

#[inline]
fn encode(&self, out: &mut dyn BufMut) {
// fast paths, avoiding allocation due to `to_be_bytes_vec`
match LIMBS {
0 => return out.put_u8(EMPTY_STRING_CODE),
1 => return self.limbs[0].encode(out),
#[allow(clippy::cast_lossless)]
2 => return (self.limbs[0] as u128 | ((self.limbs[1] as u128) << 64)).encode(out),
_ => {}
}

match self.bit_len() {
0 => out.put_u8(EMPTY_STRING_CODE),
1..=7 => {
#[allow(clippy::cast_possible_truncation)] // self < 128
out.put_u8(self.limbs[0] as u8);
}
bits => {
// avoid heap allocation in `to_be_bytes_vec`
// SAFETY: we don't re-use `copy`
#[cfg(target_endian = "little")]
let mut copy = *self;
#[cfg(target_endian = "little")]
let bytes = unsafe { copy.as_le_slice_mut() };
#[cfg(target_endian = "little")]
bytes.reverse();

#[cfg(target_endian = "big")]
let bytes = self.to_be_bytes_vec();

let leading_zero_bytes = Self::BYTES - (bits + 7) / 8;
let trimmed = &bytes[leading_zero_bytes..];
if bits > MAX_BITS {
trimmed.encode(out);
} else {
#[allow(clippy::cast_possible_truncation)] // bytes.len() < 56 < 256
out.put_u8(EMPTY_STRING_CODE + trimmed.len() as u8);
out.put_slice(trimmed);
}
}
}
}
}

/// Allows a [`Uint`] to be deserialized from RLP.
///
/// See <https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/>
impl<const BITS: usize, const LIMBS: usize> Decodable for Uint<BITS, LIMBS> {
#[inline]
fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
// let bytes = Header::decode_bytes(buf, false)?;
let header = Header::decode(buf)?;
if header.list {
return Err(DecodeError::UnexpectedList);
}

let bytes = &buf[..header.payload_length];
*buf = &buf[header.payload_length..];

// The RLP spec states that deserialized positive integers with leading zeroes
// get treated as invalid.
//
// See:
// https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
//
// To check this, we only need to check if the first byte is zero to make sure
// there are no leading zeros
if !bytes.is_empty() && bytes[0] == 0 {
return Err(DecodeError::LeadingZero);
}

Self::try_from_be_slice(bytes).ok_or(DecodeError::Overflow)
}
}

#[cfg(feature = "generic_const_exprs")]
unsafe impl<const BITS: usize, const LIMBS: usize>
MaxEncodedLen<{ Self::BYTES + length_of_length(Self::BYTES) }> for Uint<BITS, LIMBS>
{
}

#[cfg(not(feature = "generic_const_exprs"))]
const _: () = {
crate::const_for!(BITS in [0, 1, 2, 8, 16, 32, 64, 128, 160, 192, 256, 384, 512, 4096] {
const LIMBS: usize = crate::nlimbs(BITS);
const BYTES: usize = Uint::<BITS, LIMBS>::BYTES;
unsafe impl MaxEncodedLen<{ BYTES + length_of_length(BYTES) }> for Uint<BITS, LIMBS> {}
});
};

unsafe impl<const BITS: usize, const LIMBS: usize> MaxEncodedLenAssoc for Uint<BITS, LIMBS> {
const LEN: usize = Self::BYTES + length_of_length(Self::BYTES);
}

#[cfg(test)]
mod test {
use super::*;
use crate::{
aliases::{U0, U256},
const_for, nlimbs,
};
use hex_literal::hex;
use proptest::proptest;

fn encode<T: Encodable>(value: T) -> Vec<u8> {
let mut buf = vec![];
value.encode(&mut buf);
buf
}

#[test]
fn test_rlp() {
// See <https://github.com/paritytech/parity-common/blob/436cb0827f0e3238ccb80d7d453f756d126c0615/rlp/tests/tests.rs#L214>
assert_eq!(encode(U0::from(0))[..], hex!("80"));
assert_eq!(encode(U256::from(0))[..], hex!("80"));
assert_eq!(encode(U256::from(15))[..], hex!("0f"));
assert_eq!(encode(U256::from(1024))[..], hex!("820400"));
assert_eq!(encode(U256::from(0x1234_5678))[..], hex!("8412345678"));
}

#[test]
fn test_roundtrip() {
const_for!(BITS in SIZES {
const LIMBS: usize = nlimbs(BITS);
proptest!(|(value: Uint<BITS, LIMBS>)| {
let serialized = encode(value);

#[cfg(feature = "rlp")]
{
use rlp::Encodable as _;
let serialized_rlp = value.rlp_bytes();
assert_eq!(serialized, serialized_rlp.freeze()[..]);
}

assert_eq!(serialized.len(), value.length());
let mut reader = &serialized[..];
let deserialized = Uint::decode(&mut reader).unwrap();
assert_eq!(reader.len(), 0);
assert_eq!(value, deserialized);
});
});
}
}
3 changes: 2 additions & 1 deletion src/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ mod ark_ff_04;
mod bn_rs;
mod bytemuck;
mod der;
mod fastrlp;
mod fastrlp_03;
mod fastrlp_04;
mod num_bigint;
mod num_integer;
mod num_traits;
Expand Down

0 comments on commit 107f9d0

Please sign in to comment.