zcash_client_sqlite: Add shard serialization & parsing
This commit is contained in:
parent
3e358bc1c9
commit
ed2e22b737
|
@ -17,6 +17,7 @@ rust-version = "1.65"
|
||||||
incrementalmerkletree = { version = "0.4", features = ["legacy-api"] }
|
incrementalmerkletree = { version = "0.4", features = ["legacy-api"] }
|
||||||
shardtree = { version = "0.0", features = ["legacy-api"] }
|
shardtree = { version = "0.0", features = ["legacy-api"] }
|
||||||
zcash_client_backend = { version = "0.9", path = "../zcash_client_backend" }
|
zcash_client_backend = { version = "0.9", path = "../zcash_client_backend" }
|
||||||
|
zcash_encoding = { version = "0.2", path = "../components/zcash_encoding" }
|
||||||
zcash_primitives = { version = "0.12", path = "../zcash_primitives", default-features = false }
|
zcash_primitives = { version = "0.12", path = "../zcash_primitives", default-features = false }
|
||||||
|
|
||||||
# Dependencies exposed in a public API:
|
# Dependencies exposed in a public API:
|
||||||
|
@ -28,7 +29,8 @@ hdwallet = { version = "0.4", optional = true }
|
||||||
# - Logging and metrics
|
# - Logging and metrics
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|
||||||
# - Protobuf interfaces
|
# - Serialization
|
||||||
|
byteorder = "1"
|
||||||
prost = "0.11"
|
prost = "0.11"
|
||||||
|
|
||||||
# - Secret management
|
# - Secret management
|
||||||
|
@ -49,6 +51,7 @@ uuid = "1.1"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_matches = "1.5"
|
assert_matches = "1.5"
|
||||||
incrementalmerkletree = { version = "0.4", features = ["legacy-api", "test-dependencies"] }
|
incrementalmerkletree = { version = "0.4", features = ["legacy-api", "test-dependencies"] }
|
||||||
|
shardtree = { version = "0.0", features = ["legacy-api", "test-dependencies"] }
|
||||||
proptest = "1.0.0"
|
proptest = "1.0.0"
|
||||||
rand_core = "0.6"
|
rand_core = "0.6"
|
||||||
regex = "1.4"
|
regex = "1.4"
|
||||||
|
|
|
@ -78,6 +78,7 @@ use {
|
||||||
|
|
||||||
pub mod chain;
|
pub mod chain;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
mod serialization;
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
|
|
||||||
/// The maximum number of blocks the wallet is allowed to rewind. This is
|
/// The maximum number of blocks the wallet is allowed to rewind. This is
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
//! Serialization formats for data stored as SQLite BLOBs
|
||||||
|
|
||||||
|
use byteorder::{ReadBytesExt, WriteBytesExt};
|
||||||
|
use core::ops::Deref;
|
||||||
|
use shardtree::{Node, PrunableTree, RetentionFlags, Tree};
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use zcash_encoding::Optional;
|
||||||
|
use zcash_primitives::merkle_tree::HashSer;
|
||||||
|
|
||||||
|
const SER_V1: u8 = 1;
|
||||||
|
|
||||||
|
const NIL_TAG: u8 = 0;
|
||||||
|
const LEAF_TAG: u8 = 1;
|
||||||
|
const PARENT_TAG: u8 = 2;
|
||||||
|
|
||||||
|
pub fn write_shard_v1<H: HashSer, W: Write>(
|
||||||
|
writer: &mut W,
|
||||||
|
tree: &PrunableTree<H>,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
fn write_inner<H: HashSer, W: Write>(
|
||||||
|
mut writer: &mut W,
|
||||||
|
tree: &PrunableTree<H>,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
match tree.deref() {
|
||||||
|
Node::Parent { ann, left, right } => {
|
||||||
|
writer.write_u8(PARENT_TAG)?;
|
||||||
|
Optional::write(&mut writer, ann.as_ref(), |w, h| {
|
||||||
|
<H as HashSer>::write(h, w)
|
||||||
|
})?;
|
||||||
|
write_inner(writer, left)?;
|
||||||
|
write_inner(writer, right)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Node::Leaf { value } => {
|
||||||
|
writer.write_u8(LEAF_TAG)?;
|
||||||
|
value.0.write(&mut writer)?;
|
||||||
|
writer.write_u8(value.1.bits())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Node::Nil => {
|
||||||
|
writer.write_u8(NIL_TAG)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_u8(SER_V1)?;
|
||||||
|
write_inner(writer, tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_shard_v1<H: HashSer, R: Read>(mut reader: &mut R) -> io::Result<PrunableTree<H>> {
|
||||||
|
match reader.read_u8()? {
|
||||||
|
PARENT_TAG => {
|
||||||
|
let ann = Optional::read(&mut reader, <H as HashSer>::read)?.map(Rc::new);
|
||||||
|
let left = read_shard_v1(reader)?;
|
||||||
|
let right = read_shard_v1(reader)?;
|
||||||
|
Ok(Tree::parent(ann, left, right))
|
||||||
|
}
|
||||||
|
LEAF_TAG => {
|
||||||
|
let value = <H as HashSer>::read(&mut reader)?;
|
||||||
|
let flags = reader.read_u8().and_then(|bits| {
|
||||||
|
RetentionFlags::from_bits(bits).ok_or_else(|| {
|
||||||
|
io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
format!(
|
||||||
|
"Byte value {} does not correspond to a valid set of retention flags",
|
||||||
|
bits
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
Ok(Tree::leaf((value, flags)))
|
||||||
|
}
|
||||||
|
NIL_TAG => Ok(Tree::empty()),
|
||||||
|
other => Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
format!("Node tag not recognized: {}", other),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_shard<H: HashSer, R: Read>(mut reader: R) -> io::Result<PrunableTree<H>> {
|
||||||
|
match reader.read_u8()? {
|
||||||
|
SER_V1 => read_shard_v1(&mut reader),
|
||||||
|
other => Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
format!("Shard serialization version not recognized: {}", other),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use incrementalmerkletree::frontier::testing::{arb_test_node, TestNode};
|
||||||
|
use proptest::prelude::*;
|
||||||
|
use shardtree::testing::arb_prunable_tree;
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
use super::{read_shard, write_shard_v1};
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#[test]
|
||||||
|
fn check_shard_roundtrip(
|
||||||
|
tree in arb_prunable_tree(arb_test_node(), 8, 32)
|
||||||
|
) {
|
||||||
|
let mut tree_data = vec![];
|
||||||
|
write_shard_v1(&mut tree_data, &tree).unwrap();
|
||||||
|
let cursor = Cursor::new(tree_data);
|
||||||
|
let tree_result = read_shard::<TestNode, _>(cursor).unwrap();
|
||||||
|
assert_eq!(tree, tree_result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue