15个稳定版本

1.5.0 2023年10月26日
1.3.2 2022年11月20日
1.2.1 2022年5月31日
1.1.1 2022年2月22日
1.0.0 2020年6月30日

#1458 in 神奇豆子

Download history 58949/week @ 2024-03-14 60178/week @ 2024-03-21 59415/week @ 2024-03-28 56747/week @ 2024-04-04 65077/week @ 2024-04-11 64712/week @ 2024-04-18 57521/week @ 2024-04-25 57353/week @ 2024-05-02 66816/week @ 2024-05-09 62030/week @ 2024-05-16 62647/week @ 2024-05-23 63712/week @ 2024-05-30 63897/week @ 2024-06-06 71929/week @ 2024-06-13 72845/week @ 2024-06-20 65282/week @ 2024-06-27

286,062每月下载量
用于 588 个crate(51直接使用)

MIT/Apache

320KB
6K SLoC

ethnum

此crate提供256位整数的实现,这是以太坊中的原生整数类型。这种实现旨在尽可能接近Rust原生整数类型,实现相同的方法和特性。

使用方法

将以下内容添加到您的 Cargo.toml

ethnum = "1"

API尽可能遵循Rust {i,u}N 原生类型。

此crate提供基于 const fn 的宏,用于256位整数字面量。这使得您可以指定大于最大原生整数字面量(对于有符号整数是 i128::MINi128::MAX,对于无符号整数是 u128::MAX)的256位有符号和无符号整数字面量(例如,可以用作 const)。

int!("-57896044618658097711785492504343953926634992332820282019728792003956564819968");
int!("57896044618658097711785492504343953926634992332820282019728792003956564819967");
uint!("115792089237316195423570985008687907853269984665640564039457584007913129639935");

请注意,这些字面量支持前缀(二进制:0b,八进制:0o,十六进制:0x)以及 _ 和空白分隔符。

int!("-0b1010101010101010101010101010101010101010101010101010101010101010
         0101010101010101010101010101010101010101010101010101010101010101");
int!("0o 0123 4567");
uint!("0xffff_ffff");

特性

使用 macros 功能原本可以通过过程宏启用 256 位整数字面量。然而,现在这个 crate 使用 const fn 实现这些宏,因此该功能已被弃用,并且宏现在是始终可用的。为了不破坏语义版本控制,该功能仍然存在,但将在版本 2 中被移除。

serde

serde 功能添加了对 serde 序列化和反序列化的支持。默认情况下,256 位整数类型被序列化为带前缀的十六进制字符串。还提供了各种序列化助手,以便更精细地控制序列化的执行方式。

内联函数

256 位整数使用基于两种实现的内联函数

原生 Rust 实现

整数内联函数使用标准 Rust 实现。更复杂的操作,如乘法和除法,是从 C 编译器的内联函数移植过来的,用于在 64 位系统(或 32 位系统上的 64 位操作)上实现等效的 128 位操作。一般来说,这些是从 Clang 的 compiler-rt 支持例程移植过来的。

这是 crate 使用的默认实现,一般来说优化得很好。在使用原生实现时,此 crate 不需要额外的依赖项。

LLVM 生成实现

或者,ethnum 可以使用 LLVM 生成的内联函数来执行基 256 位整数操作。这利用了 LLVM IR 支持任意大小的整数操作(例如,@llvm.uadd.with.overflow.i256 用于溢出无符号加法)。这将产生更优化的汇编代码,例如加法和乘法。

然而,使用 LLVM 生成的内联函数也有一些缺点。首先,需要 Clang 来编译 LLVM IR。此外,Rust 通常在编译和链接 Rust 代码时进行优化(而不是外部链接的代码),这意味着这些内联函数不能内联,这会在某些情况下增加额外的函数调用开销,使得其性能不如原生 Rust 实现,尽管汇编代码更优化。幸运的是,Rust 当前支持链接器插件 LTO,以便在链接步骤中进行优化,使得使用 Clang 编译的 LLVM IR 可以进行优化。

为了使用 LLVM 生成的内联函数,请启用 llvm-intrinsics 功能

ethnum = { version = "1", features = ["llvm-intrinsics"] }

并且,通常最好启用 linker-plugin-lto 以充分利用优化后的汇编代码

RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build

注意,必须使 clang 版本与 rustc LLVM 版本相匹配。如果不匹配,则在运行 ethnum-intrinsics 构建脚本时可能会遇到错误。您可以使用以下命令验证 rustc 使用的 LLVM 版本

rustc --version --verbose | grep LLVM

特别是,这影响了 macOS,它自带其自己的 clang 二进制文件。ethnum-intrinsics 构建脚本接受一个 CLANG 环境变量来指定要使用的特定 clang 可执行文件路径。使用上述命令中的主要 LLVM 版本

brew install llvm@${LLVM_VERSION}
CLANG=/opt/homebrew/opt/llvm@${LLVM_VERSION}/bin/clang cargo build

API 稳定性

内联函数在 ethnum::intrinsics 下导出。话虽如此,在直接使用这些内联函数时请谨慎。对于这些内联函数中的任何一项,都不能保证语义版本控制的 API 兼容性。

如果您要在项目中使用这些,建议使用严格的版本控制

[dependencies]
ethnum = "=x.y.z"

这将确保如cargo update之类的命令不会更改ethnum依赖项的版本。

基准测试

ethnum-bench crate实现了针对整数内建的criterion基准测试。

cargo bench -p ethnum-bench
RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo bench -p ethnum-bench --features llvm-intrinsics

模糊测试

ethnum-fuzz crate实现了AFL模糊测试目标(以及一些用于与cargo afl一起工作的实用工具)。内部,它将256位有符号整数类型转换为num::BigInt,并以其操作实现作为参考。

为了开始模糊测试

cargo install --force cargo-afl
cargo run -p ethnum-fuzz --bin init target/fuzz
cargo afl build -p ethnum-fuzz --bin fuzz
cargo afl fuzz -i target/fuzz/in -o target/fuzz/out target/debug/fuzz

为了重放崩溃

cargo run -p ethnum-fuzz --bin dump target/fuzz/out/default/crashes/FILE

依赖项

~175KB