16 个不稳定版本 (7 个破坏性变更)
0.8.0 | 2024 年 4 月 27 日 |
---|---|
0.7.0 | 2023 年 11 月 1 日 |
0.6.2 | 2023 年 8 月 3 日 |
0.6.0 | 2023 年 7 月 28 日 |
0.4.3 | 2022 年 10 月 11 日 |
#1199 in 魔法豆
每月 789 次下载
用于 4 crates
110KB
2.5K SLoC
Bitcoin slices
为 Bitcoin 数据结构(如 bsl::Transaction
、bsl::Block
等)提供零分配解析库,这些数据结构在 [bsl
] 模块中可用。
通过提供用户感兴趣的数据的 Visitor
结构来访问数据。
// Calculate the amount of outputs in mainnet block 702861 in satoshi
use bitcoin_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-bitcoin。
优点
- 由于解析过程中没有进行分配,反序列化速度惊人。
- 由于结构中保留了序列化数据的切片,序列化是瞬时的。
- [
bsl
] 类型适合用作数据库键和值,实际上有一个特定的redb
功能 - 由于切片不需要重新序列化数据,散列速度略快。
- 没有强制依赖。
- 没有标准。
- 通过可选依赖
bitcoin_hashes
或sha2
计算交易 ID 和区块哈希。 - 访问者模式仅访问您感兴趣的内容。
缺点
- 所有数据必须驻留在内存中,没有流式(读取/写入)API。
- 数据结构为只读,不可修改。
- 访问者模式需要用户构建的数据结构进行访问。
功能
哈希
使用功能 sha2
或 bitcoin_hashes
来计算区块和交易哈希。前者更快,后者更有可能在您使用 rust-bitcoin 生态系统 crate 的情况下出现在您的树中。
redb
在激活了 redb
功能的情况下,一些类型可以用作 redb 数据库的值和键。Bitcoin 切片类型非常适合用作数据库中的键和值,因为从切片到切片的转换是立即的。
#[cfg(feature = "redb")]
{
use bitcoin_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-bitcoin
在激活了 bitcoin
功能的情况下,一些类型可以转换为其 rust-bitcoin
对应版本:例如 bsl::TxOut
可以转换为 bitcoin::TxOut
。您可能认为如果需要 bitcoin::TxOut
,您可以直接将其字节解码到它中而不使用此库,这通常是正确的,但有时可能更方便同时使用这两种类型,例如使用 bitcoin 切片与数据库,但您可能需要比编写访问者更方便地访问字段,因此将其转换为 rust-bitcoin 类型。此外,转换可以利用类型不变性并且可能比从通用字节流开始更快。
#[cfg(feature = "bitcoin")]
{
use bitcoin_slices::{bsl, bitcoin, Parse};
let tx_out_bytes = hex_lit::hex!("ffffffffffffffff0100");
let tx_out = bsl::TxOut::parse(&tx_out_bytes).unwrap().parsed_owned();
let tx_out_bitcoin: bitcoin::TxOut =
bitcoin::consensus::deserialize(&tx_out_bytes[..]).unwrap();
let tx_out_back: bitcoin::TxOut = tx_out.into();
assert_eq!(tx_out_back, tx_out_bitcoin);
}
测试
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_bitcoin ... 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_bitcoin ... 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_bitcoin ... 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_bitcoin ... 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_bitcoin ... bench: 1,510 ns/iter (+/- 222)
test bsl::transaction::bench::tx_deserialize ... bench: 38 ns/iter (+/- 8)
test bsl::transaction::bench::tx_deserialize_bitcoin ... bench: 219 ns/iter (+/- 30)
test bsl::transaction::bench::txid ... bench: 2,185 ns/iter (+/- 166)
test bsl::transaction::bench::txid_bitcoin ... bench: 2,416 ns/iter (+/- 213)
test bsl::transaction::bench::txid_sha2 ... bench: 2,085 ns/iter (+/- 216)
- 以
_bitcoin
结尾的基准使用rust-bitcoin
- 以
_sha2
结尾的基准使用sha2
库而不是bitcoin_hashes
与 rust-bitcoin 的比较
block_deserialize
比 block_deserialize_bitcoin
快近 10 倍。这种比较可能不公平,因为例如在 block_deserialize
的情况下,您无法迭代结果对象中的交易,但在查看使用访问者访问区块中每个输出的 sum_outputs
示例时,我们发现没有明显的差异。
哈希
block_hash
和 block_hash_bitcoin
使用相同的代码进行哈希,但是 bitcoin_slice 大约快 7%,因为它使用已提供的切片而不是重新序列化数据。类似的结果适用于 txid
和 txid_bitcoin
。性能提升在 hash_block_txs
和 hash_block_txs_bitcoin
之间更为明显(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
~28K SLoC