diff --git a/Cargo.toml b/Cargo.toml index 0f7e015..209090e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,9 @@ network = [] # An extension for casper network network_casper = ["casper-contract/wee_alloc", "casper-types", "network"] +# An extension for radix network +network_radix = ["radix-common", "scrypto", "network"] + # An extension for debug-printing of messages in the Casper extension. Not supported by Casper Contracts deployed to the network. print_debug = ["casper-contract/test-support"] @@ -32,6 +35,8 @@ helpers = ["hex/serde", "hex/alloc", "network"] [dependencies] casper-contract = { version = "^4.0.0", default-features = false, features = [], optional = true } casper-types = { version = "^4.0.1", default-features = false, features = [], optional = true } +radix-common = { version = "1.2.0", default-features = false, features = [], optional = true } +scrypto = { version = "1.2.0", optional = true } sha3 = { version = "^0.10.8", default-features = false, features = [], optional = true } k256 = { version = "^0.13.3", default-features = false, features = [], optional = true } secp256k1 = { version = "^0.29.0", default-features = false, features = [], optional = true } diff --git a/Makefile b/Makefile index aafe1e0..ed6424f 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,7 @@ CLIPPY=cargo clippy --release --fix --allow-dirty --allow-staged DOC=cargo doc --no-deps --document-private-items TEST=RUST_BACKTRACE=full cargo test --features="helpers" -FEATURE_SETS="crypto_k256" "crypto_k256,network_casper" "crypto_secp256k1" "crypto_secp256k1,network_casper" - +FEATURE_SETS="crypto_k256" "crypto_k256,network_casper" "crypto_secp256k1" "crypto_secp256k1,network_casper" "crypto_k256,network_radix" "crypto_secp256k1" "crypto_secp256k1,network_radix" prepare: @rustup target add wasm32-unknown-unknown diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..c3c8c37 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +imports_granularity = "Crate" diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 1bb66ff..a99765a 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,3 +1,2 @@ -pub(crate) mod recover; - +pub(crate)mod recover; mod keccak256; diff --git a/src/crypto/recover.rs b/src/crypto/recover.rs index 70c7ba0..24acb93 100644 --- a/src/crypto/recover.rs +++ b/src/crypto/recover.rs @@ -14,7 +14,7 @@ pub fn recover_address(message: Vec, signature: Vec) -> Vec { } #[cfg(feature = "crypto_secp256k1")] -pub mod crypto256 { +pub(crate) mod crypto256 { use crate::network::{assert::Unwrap, error::Error}; use secp256k1::{ecdsa::RecoverableSignature, Message, Secp256k1 as Secp256k1Curve}; @@ -40,11 +40,11 @@ pub mod crypto256 { } #[cfg(feature = "crypto_k256")] -pub mod crypto256 { +pub(crate) mod crypto256 { use crate::network::{assert::Unwrap, error::Error}; use k256::ecdsa::{RecoveryId, Signature, VerifyingKey}; - pub fn recover_public_key( + pub(crate) fn recover_public_key( message_hash: Box<[u8]>, signature_bytes: &[u8], recovery_byte: u8, @@ -64,8 +64,8 @@ pub mod crypto256 { } #[cfg(all(not(feature = "crypto_k256"), not(feature = "crypto_secp256k1")))] -pub mod crypto256 { - pub fn recover_public_key( +pub(crate) mod crypto256 { + pub(crate) fn recover_public_key( _message_hash: Box<[u8]>, _signature_bytes: &[u8], _recovery_byte: u8, diff --git a/src/helpers/hex.rs b/src/helpers/hex.rs index 055eff2..31dee35 100644 --- a/src/helpers/hex.rs +++ b/src/helpers/hex.rs @@ -1,6 +1,7 @@ -use crate::network::specific::{Bytes, FromBytesRepr, U256}; +use crate::network::specific::{Bytes, U256}; use hex::{decode, encode}; use std::{fs::File, io::Read}; +use crate::network::from_bytes_repr::FromBytesRepr; pub fn hex_to_bytes(hex_str: String) -> Vec { let trimmed_hex = hex_str.trim_start_matches("0x"); diff --git a/src/network/as_str.rs b/src/network/as_str.rs index 5384ac9..8c98635 100644 --- a/src/network/as_str.rs +++ b/src/network/as_str.rs @@ -8,6 +8,7 @@ pub trait AsHexStr { } impl AsHexStr for &[u8] { + #[allow(clippy::format_collect)] fn as_hex_str(&self) -> String { self.iter().map(|byte| format!("{:02x}", byte)).collect() } @@ -20,12 +21,27 @@ impl AsHexStr for casper_types::bytesrepr::Bytes { } } +#[cfg(not(feature = "network_radix"))] impl AsHexStr for U256 { fn as_hex_str(&self) -> String { format!("{:X}", self) } } +#[cfg(feature = "network_radix")] +impl AsHexStr for U256 { + fn as_hex_str(&self) -> String { + let digits = self.to_digits(); + let mut result = String::new(); + for &part in &digits { + if result.is_empty() || part != 0u64 { + result.push_str(&format!("{:02X}", part)); + } + } + result + } +} + impl AsHexStr for Vec { fn as_hex_str(&self) -> String { self.as_slice().as_hex_str() @@ -73,3 +89,27 @@ impl AsAsciiStr for U256 { bytes.as_ascii_str() } } + +#[cfg(test)] +mod tests { + use crate::network::as_str::{AsAsciiStr, AsHexStr}; + use crate::network::specific::U256; + + const ETH: u32 = 4543560u32; + + #[test] + fn test_as_hex_str() { + let u256 = U256::from(ETH); + let result = u256.as_hex_str(); + + assert_eq!(result, "455448"); + } + + #[test] + fn test_as_ascii_str() { + let u256 = U256::from(ETH); + let result = u256.as_ascii_str(); + + assert_eq!(result, "ETH"); + } +} \ No newline at end of file diff --git a/src/network/casper/from_bytes_repr.rs b/src/network/casper/from_bytes_repr.rs index 64ec573..9a2b821 100644 --- a/src/network/casper/from_bytes_repr.rs +++ b/src/network/casper/from_bytes_repr.rs @@ -1,7 +1,8 @@ -use crate::network::specific::{FromBytesRepr, U256}; +use crate::network::from_bytes_repr::{FromBytesRepr, Sanitized}; +use crate::network::specific::U256; impl FromBytesRepr> for U256 { fn from_bytes_repr(bytes: Vec) -> Self { - bytes.as_slice().into() + bytes.sanitized().as_slice().into() } } diff --git a/src/network/from_bytes_repr.rs b/src/network/from_bytes_repr.rs new file mode 100644 index 0000000..699a2c8 --- /dev/null +++ b/src/network/from_bytes_repr.rs @@ -0,0 +1,126 @@ +use crate::network::specific::{Bytes, VALUE_SIZE}; + +pub trait FromBytesRepr { + fn from_bytes_repr(bytes: T) -> Self; +} + +pub trait Sanitized { + fn sanitized(self) -> Self; +} + +impl Sanitized for Bytes { + fn sanitized(self) -> Self { + if self.len() <= VALUE_SIZE { + return self; + } + + let index = self.len().max(VALUE_SIZE) - VALUE_SIZE; + let remainder = &self[0..index]; + + if remainder != vec![0; index] { + panic!("Number to big: {:?} digits", self.len()) + } + + self[index..].into() + } +} + +#[cfg(test)] +mod tests { + use crate::network::from_bytes_repr::FromBytesRepr; + use crate::network::specific::{U256, VALUE_SIZE}; + + #[cfg(feature = "network_radix")] + use crate::network::radix::u256_ext::U256Ext; + + #[test] + fn test_from_bytes_repr_single() { + let vec = vec![1]; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::from(1u32)); + } + + #[test] + fn test_from_bytes_repr_double() { + let vec = vec![1, 2]; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::from(258u32)); + } + + #[test] + fn test_from_bytes_repr_simple() { + let vec = vec![1, 2, 3]; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::from(66051u32)); + } + + #[test] + fn test_from_bytes_repr_bigger() { + let vec = vec![101, 202, 255]; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::from(6671103u32)); + } + + #[test] + fn test_from_bytes_repr_empty() { + let result = U256::from_bytes_repr(Vec::new()); + + assert_eq!(result, U256::from(0u8)); + } + + #[test] + fn test_from_bytes_repr_trailing_zeroes() { + let vec = vec![1, 2, 3, 0]; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::from(16909056u32)); + } + + #[test] + fn test_from_bytes_repr_leading_zeroes() { + let vec = vec![0, 1, 2, 3]; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::from(66051u32)); + } + + #[allow(clippy::legacy_numeric_constants)] + #[test] + fn test_from_bytes_repr_max() { + let vec = vec![255; VALUE_SIZE]; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::max_value()); + } + + #[test] + fn test_from_bytes_repr_min() { + let vec = vec![0; VALUE_SIZE]; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::from(0u8)); + } + + #[should_panic(expected = "Number to big")] + #[test] + fn test_from_bytes_repr_too_long() { + let x = VALUE_SIZE as u8 + 1; + let vec = (1..=x).collect(); + + U256::from_bytes_repr(vec); + } + + #[allow(clippy::legacy_numeric_constants)] + #[test] + fn test_from_bytes_repr_too_long_but_zeroes() { + let mut vec = vec![255; VALUE_SIZE + 1]; + vec[0] = 0; + let result = U256::from_bytes_repr(vec); + + assert_eq!(result, U256::max_value()); + } +} \ No newline at end of file diff --git a/src/network/mod.rs b/src/network/mod.rs index f963421..189a14d 100644 --- a/src/network/mod.rs +++ b/src/network/mod.rs @@ -3,6 +3,7 @@ pub mod assert; pub mod error; pub mod print_debug; pub mod specific; +pub(crate) mod from_bytes_repr; #[cfg(feature = "network_casper")] pub mod casper; @@ -10,9 +11,15 @@ pub mod casper; #[cfg(feature = "network_casper")] pub type _Network = casper::Casper; +#[cfg(feature = "network_radix")] +pub mod radix; + +#[cfg(feature = "network_radix")] +pub type _Network = radix::Radix; + pub mod flattened; -#[cfg(not(feature = "network_casper"))] +#[cfg(all(not(feature = "network_casper"), not(feature = "network_radix")))] mod std; -#[cfg(not(feature = "network_casper"))] +#[cfg(all(not(feature = "network_casper"), not(feature = "network_radix")))] pub type _Network = std::Std; diff --git a/src/network/radix/from_bytes_repr.rs b/src/network/radix/from_bytes_repr.rs new file mode 100644 index 0000000..8ba7ae2 --- /dev/null +++ b/src/network/radix/from_bytes_repr.rs @@ -0,0 +1,18 @@ +use crate::network::from_bytes_repr::{FromBytesRepr, Sanitized}; +use crate::network::specific::{U256}; + +impl FromBytesRepr> for U256 { + fn from_bytes_repr(bytes: Vec) -> Self { + match bytes.len() { + 0 => U256::ZERO, + 1 => U256::from(bytes[0]), + _ => { + + // TODO: make it cheaper + let mut bytes_le = bytes.sanitized(); + bytes_le.reverse(); + + U256::from_le_bytes(bytes_le.as_slice()) + }} + } +} diff --git a/src/network/radix/mod.rs b/src/network/radix/mod.rs new file mode 100644 index 0000000..d275d1f --- /dev/null +++ b/src/network/radix/mod.rs @@ -0,0 +1,38 @@ +use crate::network::{error::Error, specific::NetworkSpecific}; + +mod from_bytes_repr; +pub mod u256_ext; + +pub struct Radix; + +impl NetworkSpecific for Radix { + type BytesRepr = Vec; + type ValueRepr = radix_common::math::bnum_integer::U256; + type _Self = Self; + + const VALUE_SIZE: usize = 32; + + fn print(_text: String) { + #[cfg(all(not(test), feature = "print_debug"))] + { + println!("{}", _text); + } + + #[cfg(test)] + { + println!("{}", _text); + } + } + + fn revert(error: Error) -> ! { + #[cfg(not(test))] + { + panic!("{}", error) + } + + #[cfg(test)] + { + panic!("{}", error) + } + } +} diff --git a/src/network/radix/u256_ext.rs b/src/network/radix/u256_ext.rs new file mode 100644 index 0000000..e68748c --- /dev/null +++ b/src/network/radix/u256_ext.rs @@ -0,0 +1,12 @@ + +use crate::network::specific::U256; + +pub trait U256Ext { + fn max_value() -> Self; +} + +impl U256Ext for U256 { + fn max_value() -> Self { + Self::MAX + } +} diff --git a/src/network/specific.rs b/src/network/specific.rs index 839af0b..35c1b37 100644 --- a/src/network/specific.rs +++ b/src/network/specific.rs @@ -1,8 +1,5 @@ use crate::network::{_Network, error::Error}; - -pub trait FromBytesRepr { - fn from_bytes_repr(bytes: T) -> Self; -} +use crate::network::from_bytes_repr::FromBytesRepr; pub trait NetworkSpecific { type BytesRepr: From> + Into>; @@ -15,14 +12,10 @@ pub trait NetworkSpecific { fn revert(error: Error) -> !; } +pub(crate) type Network = <_Network as NetworkSpecific>::_Self; pub type Bytes = <_Network as NetworkSpecific>::BytesRepr; pub type U256 = <_Network as NetworkSpecific>::ValueRepr; - -pub(crate) type Network = <_Network as NetworkSpecific>::_Self; - -#[cfg(test)] -#[allow(dead_code)] -pub(crate) const VALUE_SIZE: usize = Network::VALUE_SIZE; +pub const VALUE_SIZE: usize = <_Network as NetworkSpecific>::VALUE_SIZE; pub fn print(_text: String) { Network::print(_text) @@ -31,3 +24,4 @@ pub fn print(_text: String) { pub fn revert(error: Error) -> ! { Network::revert(error) } + diff --git a/src/network/std/from_bytes_repr.rs b/src/network/std/from_bytes_repr.rs index 3e4ff4b..a7589dd 100644 --- a/src/network/std/from_bytes_repr.rs +++ b/src/network/std/from_bytes_repr.rs @@ -1,11 +1,10 @@ -use crate::network::specific::FromBytesRepr; +use crate::network::from_bytes_repr::{FromBytesRepr, Sanitized}; + impl FromBytesRepr> for u128 { fn from_bytes_repr(bytes: Vec) -> u128 { - let bytes = bytes[(bytes.len().max(16) - 16)..].to_vec(); - let mut result: u128 = 0; - for &byte in bytes.iter() { + for &byte in bytes.sanitized().iter() { result = (result << 8) | byte as u128; } result diff --git a/src/protocol/data_package.rs b/src/protocol/data_package.rs index 00bf75e..0a44de6 100644 --- a/src/protocol/data_package.rs +++ b/src/protocol/data_package.rs @@ -71,7 +71,7 @@ impl Debug for DataPackage { mod tests { use crate::{ helpers::hex::hex_to_bytes, - network::specific::{FromBytesRepr, U256}, + network::specific::U256, protocol::{ constants::{ DATA_FEED_ID_BS, DATA_POINTS_COUNT_BS, DATA_POINT_VALUE_BYTE_SIZE_BS, SIGNATURE_BS, @@ -81,6 +81,7 @@ mod tests { data_point::DataPoint, }, }; + use crate::network::from_bytes_repr::FromBytesRepr; const DATA_PACKAGE_BYTES_1: &str = "4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000360cafc94e018d79bf0ba00000002000000151afa8c5c3caf6004b42c0fb17723e524f993b9ecbad3b9bce5ec74930fa436a3660e8edef10e96ee5f222de7ef5787c02ca467c0ec18daa2907b43ac20c63c11c"; const DATA_PACKAGE_BYTES_2: &str = "4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000360cdd851e018d79bf0ba000000020000001473fd9dc72e6814a7de719b403cf4c9eba08934a643fd0666c433b806b31e69904f2226ffd3c8ef75861b11b5e32a1fda4b1458e0da4605a772dfba2a812f3ee1b"; diff --git a/src/protocol/data_point.rs b/src/protocol/data_point.rs index 15145f4..4a580d2 100644 --- a/src/protocol/data_point.rs +++ b/src/protocol/data_point.rs @@ -3,12 +3,13 @@ use crate::{ as_str::{AsAsciiStr, AsHexStr}, assert::Assert, error::Error, - specific::{FromBytesRepr, U256}, + specific::U256, }, protocol::constants::DATA_FEED_ID_BS, utils::{trim::Trim, trim_zeros::TrimZeros}, }; use std::fmt::{Debug, Formatter}; +use crate::network::from_bytes_repr::FromBytesRepr; #[derive(Clone, PartialEq, Eq)] pub(crate) struct DataPoint { @@ -60,13 +61,14 @@ impl Debug for DataPoint { mod tests { use crate::{ helpers::hex::hex_to_bytes, - network::specific::{FromBytesRepr, U256, VALUE_SIZE}, + network::specific::{U256, VALUE_SIZE}, protocol::{ constants::DATA_FEED_ID_BS, data_point::{trim_data_point, trim_data_points, DataPoint}, }, }; use std::ops::Shr; + use crate::network::from_bytes_repr::FromBytesRepr; const DATA_POINT_BYTES_TAIL: &str = "4554480000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000360cafc94e"; const VALUE: u128 = 232141080910; @@ -119,7 +121,7 @@ mod tests { test_trim_data_point_of( &DATA_POINT_BYTES_TAIL[..DATA_POINT_BYTES_TAIL.len() - 2 * i], 32 - i, - U256::from(VALUE).shr(8 * i), + U256::from(VALUE).shr(8 * i as u32), ); } } diff --git a/src/utils/filter.rs b/src/utils/filter.rs index 9ee9c1a..1d434df 100644 --- a/src/utils/filter.rs +++ b/src/utils/filter.rs @@ -14,7 +14,7 @@ mod filter_some_tests { #[test] fn test_filter_some() { - let values = vec![None, Some(23u64), None, Some(12), Some(12), None, Some(23)]; + let values = [None, Some(23u64), None, Some(12), Some(12), None, Some(23)]; assert_eq!(values.filter_some(), vec![23, 12, 12, 23]) } diff --git a/src/utils/median.rs b/src/utils/median.rs index ed10b24..5702382 100644 --- a/src/utils/median.rs +++ b/src/utils/median.rs @@ -1,5 +1,6 @@ use crate::network::{assert::Assert, error::Error::ArrayIsEmpty}; use std::ops::{Add, Rem, Shr}; +use crate::network::specific::U256; pub(crate) trait Median { type Item; @@ -11,9 +12,26 @@ trait Avg { fn avg(self, other: Self) -> Self; } -impl Avg for T +trait Averageable: Add + Shr + From + Rem + Copy {} + +impl Averageable for i32 {} + +#[cfg(feature = "network_radix")] +impl Avg for U256 { + fn avg(self, other: Self) -> Self { + let one = 1u32; + let two = U256::from(2u8); + + self.shr(one) + other.shr(one) + (self % two + other % two).shr(one) + } +} + +#[cfg(not(feature = "network_radix"))] +impl Averageable for U256 {} + +impl Avg for T where - T: Add + Shr + From + Rem + Copy, + T: Averageable, { fn avg(self, other: Self) -> Self { let one = T::from(1); @@ -73,20 +91,26 @@ mod tests { use crate::network::specific::U256; use itertools::Itertools; use std::fmt::Debug; + + #[cfg(feature = "network_radix")] + use crate::network::radix::u256_ext::U256Ext; - const U256MAX: U256 = U256::max_value(); // 115792089237316195423570985008687907853269984665640564039457584007913129639935 - + #[allow(clippy::legacy_numeric_constants)] #[test] - fn test_casper_avg() { - assert_eq!(U256MAX.avg(U256::from(0u8)), U256MAX / 2); - assert_eq!(U256MAX.avg(U256::from(1u8)), U256MAX / 2 + 1); - assert_eq!(U256MAX.avg(U256MAX - 1), U256MAX - 1); - assert_eq!(U256MAX.avg(U256MAX), U256MAX); - - assert_eq!((U256MAX - 1).avg(U256::from(0u8)), U256MAX / 2); - assert_eq!((U256MAX - 1).avg(U256::from(1u8)), U256MAX / 2); - assert_eq!((U256MAX - 1).avg(U256MAX - 1), U256MAX - 1); - assert_eq!((U256MAX - 1).avg(U256MAX), U256MAX - 1); + fn test_avg() { + let u256 = U256::max_value(); // 115792089237316195423570985008687907853269984665640564039457584007913129639935 + let u256_max_sub_1 = u256 - U256::from(1u32); + let u256max_div_2 = u256 / U256::from(2u32); + + assert_eq!(u256.avg(U256::from(0u8)), u256max_div_2); + assert_eq!(u256.avg(U256::from(1u8)), u256max_div_2 + U256::from(1u8)); + assert_eq!(u256.avg(u256_max_sub_1), u256_max_sub_1); + assert_eq!(u256.avg(u256), u256); + + assert_eq!((u256_max_sub_1).avg(U256::from(0u8)), u256max_div_2); + assert_eq!((u256_max_sub_1).avg(U256::from(1u8)), u256max_div_2); + assert_eq!((u256_max_sub_1).avg(u256_max_sub_1), u256_max_sub_1); + assert_eq!((u256_max_sub_1).avg(u256), u256_max_sub_1); } #[test] diff --git a/src/utils/trim.rs b/src/utils/trim.rs index 7cd3ce0..1adbb30 100644 --- a/src/utils/trim.rs +++ b/src/utils/trim.rs @@ -1,8 +1,9 @@ use crate::network::{ assert::Unwrap, error::Error, - specific::{FromBytesRepr, U256}, + specific::U256, }; +use crate::network::from_bytes_repr::FromBytesRepr; pub trait Trim where