1个不稳定版本
0.7.0 | 2024年2月21日 |
---|
#2437 in 神奇豆子
115KB
2.5K SLoC
Groestlcoin切片
一个针对Groestlcoin数据结构(如bsl::Transaction
、bsl::Block
等)的零分配解析库,这些数据结构在[bsl
]模块中可用。
通过提供用户感兴趣的数据的Visitor
结构来访问数据。
// Calculate the amount of outputs in mainnet block 702861 in satoshi
use groestlcoin_slices::{bsl, Visit, Visitor};
struct Sum(pub u64);
impl Visitor for Sum {
fn visit_tx_out(&mut self, _vout: usize, tx_out: &bsl::TxOut) -> core::ops::ControlFlow<()> {
self.0 += tx_out.value();
core::ops::ControlFlow::Continue(())
}
}
let mut sum = Sum(0);
let block_bytes: &[u8] = bitcoin_test_data::blocks::mainnet_702861();
let block = bsl::Block::visit(block_bytes, &mut sum).unwrap();
assert_eq!(sum.0, 2_883_682_728_990)
数据结构是只读的,解析的数据必须在内存中,没有流式API。
权衡
在使用此库之前检查CON,如果它们对您的案例过于限制,请使用rust-groestlcoin。
优点
- 由于解析过程中没有进行分配,反序列化速度非常快。
- 由于结构中保留了序列化数据的一个切片,序列化是瞬间的。
- [
bsl
] 类型适合用作数据库键和值,实际上有一个特定的redb
特性 - 因为不需要重新序列化数据,散列速度略快。
- 没有强制依赖。
- 没有标准。
- 通过可选依赖
groestlcoin_hashes
或sha2
计算txid和区块哈希。 - 访问者模式仅访问您感兴趣的内容。
缺点
- 必须将全部数据放在内存中,没有流式(读/写)API。
- 数据结构是只读的,不能修改。
- 访问者模式需要用户自定义数据结构进行访问。
特性
散列
使用功能 sha2
或 groestlcoin_hashes
来计算区块和交易哈希。前者更快,后者更有可能在您使用 rust-groestlcoin 生态系统 crates 的情况下存在于您的树中。
redb
当激活了 redb
功能时,某些类型允许在 redb 数据库中用作值和键。groestlcoin 切片类型非常适合用作数据库中的键和值,因为从/到切片的转换是立即的。
#[cfg(feature = "redb")]
{
use groestlcoin_slices::{bsl, redb, Parse, redb::ReadableTable};
const UTXOS_TABLE: redb::TableDefinition<bsl::OutPoint, bsl::TxOut> = redb::TableDefinition::new("utxos");
let path = tempfile::NamedTempFile::new().unwrap().into_temp_path();
let db = redb::Database::create(path).unwrap();
let write_txn = db.begin_write().unwrap();
let tx_out_bytes = hex_lit::hex!("ffffffffffffffff0100");
let out_point_bytes = [0u8; 36];
let tx_out = bsl::TxOut::parse(&tx_out_bytes).unwrap().parsed_owned();
let out_point = bsl::OutPoint::parse(&out_point_bytes).unwrap().parsed_owned();
{
let mut table = write_txn.open_table(UTXOS_TABLE).unwrap();
table.insert(&out_point, &tx_out).unwrap();
}
write_txn.commit().unwrap();
let read_txn = db.begin_read().unwrap();
let table = read_txn.open_table(UTXOS_TABLE).unwrap();
assert_eq!(table.get(&out_point).unwrap().unwrap().value(), tx_out);
}
rust-groestlcoin
当激活了 groestlcoin
功能时,某些类型可以转换为 rust-groestlcoin
的对应类型:例如,bsl::TxOut
可以转换为 groestlcoin::TxOut
。您可能认为如果您需要 groestlcoin::TxOut
,您可以直接将其解码为字节而不使用此库,这通常是正确的,但有时使用这两种类型可能更方便,例如使用 groestlcoin 切片与数据库一起,但您可能需要比编写访问器更方便地访问字段,因此需要将其转换为 rust-groestlcoin 类型。此外,转换可以利用类型不变性和比从通用字节流开始更快。
#[cfg(feature = "groestlcoin")]
{
use groestlcoin_slices::{bsl, groestlcoin, Parse};
let tx_out_bytes = hex_lit::hex!("ffffffffffffffff0100");
let tx_out = bsl::TxOut::parse(&tx_out_bytes).unwrap().parsed_owned();
let tx_out_groestlcoin: groestlcoin::TxOut =
groestlcoin::consensus::deserialize(&tx_out_bytes[..]).unwrap();
let tx_out_back: groestlcoin::TxOut = tx_out.into();
assert_eq!(tx_out_back, tx_out_groestlcoin);
}
测试
cargo test
基准测试
RUSTFLAGS='--cfg=bench' cargo +nightly bench --all-features
test bsl::block::bench::block_deserialize ... bench: 289,421 ns/iter (+/- 46,179)
test bsl::block::bench::block_deserialize_groestlcoin ... bench: 2,719,666 ns/iter (+/- 459,186)
test bsl::block::bench::block_sum_outputs ... bench: 288,248 ns/iter (+/- 39,013)
test bsl::block::bench::block_sum_outputs_groestlcoin ... bench: 2,607,791 ns/iter (+/- 321,212)
test bsl::block::bench::find_tx ... bench: 1,012,297 ns/iter (+/- 6,278)
test bsl::block::bench::find_tx_groestlcoin ... bench: 8,632,416 ns/iter (+/- 89,751)
test bsl::block::bench::hash_block_txs ... bench: 8,406,341 ns/iter (+/- 938,119)
test bsl::block::bench::hash_block_txs_groestlcoin ... bench: 11,843,590 ns/iter (+/- 1,052,109)
test bsl::block::bench::hash_block_txs_sha2 ... bench: 7,891,956 ns/iter (+/- 1,047,439)
test bsl::block_header::bench::block_hash ... bench: 1,399 ns/iter (+/- 205)
test bsl::block_header::bench::block_hash_groestlcoin ... bench: 1,510 ns/iter (+/- 222)
test bsl::transaction::bench::tx_deserialize ... bench: 38 ns/iter (+/- 8)
test bsl::transaction::bench::tx_deserialize_groestlcoin ... bench: 219 ns/iter (+/- 30)
test bsl::transaction::bench::txid ... bench: 2,185 ns/iter (+/- 166)
test bsl::transaction::bench::txid_groestlcoin ... bench: 2,416 ns/iter (+/- 213)
test bsl::transaction::bench::txid_sha2 ... bench: 2,085 ns/iter (+/- 216)
- 以
_groestlcoin
结尾的基准测试使用rust-groestlcoin
- 以
_sha2
结尾的基准测试使用sha2
库而不是groestlcoin_hashes
与 rust-groestlcoin 的比较
block_deserialize
的速度几乎是 block_deserialize_groestlcoin
的 10 倍。这种比较可能不公平,因为例如,在 block_deserialize
的情况下,您不能从结果对象中迭代交易,但在查看使用访问器访问区块中每个输出的 sum_outputs
示例时,我们看到没有明显的差异。
散列
block_hash
和 block_hash_groestlcoin
使用相同的代码进行哈希,但是因为使用已经可用的切片而不是重新序列化数据,所以 groestlcoin_slice 大约快 7%。在 txid
和 txid_groestlcoin
之间也有类似的结果。性能提升在 hash_block_txs
和 hash_block_txs_groestlcoin
之间更为明显(30%)。
*_sha2
在虚拟 CI 机器上并不真正具有代表性,因为它们不是硬件加速的。
模糊测试
使用 cargo fuzz 将交易作为目标运行模糊测试。
cargo +nightly fuzz run transaction
在 fuzz/fuzz_targets
中可用的其他目标
最小化语料库
cargo +nightly fuzz cmin transaction
文档
要构建文档
RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --open
MSRV
此 crate 的最小支持 Rust 版本是 1.60.0,没有 redb
、slice_cache
功能(请与 CI 中运行的版本进行双检查)。带有 slice_cache
功能的 MSRV 是 1.64.0。带有 redb
功能的 MSRV 是 1.66.0。
以前的工作和致谢
依赖项
~0–1.9MB
~27K SLoC