Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] implement range proof verification #693

Draft
wants to merge 52 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
9aa1af6
WIP: Initial stab at new-node-store-interface
rkuris Mar 31, 2024
910f983
WIP: more implementation of LinearStore
rkuris Apr 2, 2024
2425fe6
Beginnings of test framework
rkuris Apr 2, 2024
9354971
Lints
rkuris Apr 3, 2024
fe9080d
Merge branch 'main' into rkuris/new-node-store-interface
rkuris Apr 3, 2024
b753eda
Complete read path implementation for proposals
rkuris Apr 3, 2024
05310d5
WIP: Increase visibility of some members
rkuris Apr 5, 2024
19a810f
rename DiskAddress to LinearAddress (#627)
Apr 5, 2024
f746256
Appease linter
rkuris Apr 5, 2024
e4057e0
Merge branch 'rkuris/new-node-store-interface' of github.com:ava-labs…
rkuris Apr 5, 2024
d90e3be
Fix bug: offset_within not getting updated
rkuris Apr 5, 2024
4c27f59
Finish implementation of linear store write
rkuris Apr 8, 2024
7a79ad0
Linter
rkuris Apr 8, 2024
e6b88ea
Lint fixes, along with code cleanups
rkuris Apr 8, 2024
5f6518e
Merge branch 'rkuris/new-node-store-interface' of github.com:ava-labs…
rkuris Apr 8, 2024
e5a1914
Super simple benchmark
rkuris Apr 10, 2024
3a5a6c1
async part 1
rkuris Apr 12, 2024
9cd1749
Revert "async part 1"
rkuris Apr 17, 2024
aafd6e5
implement `ReadLinearStore` for `Historical` (#632)
Apr 18, 2024
ab36ec8
appease clippy and docs
Apr 18, 2024
d8ed821
appease clippy
Apr 18, 2024
8f554d8
Refactor linear storage types (#633)
Apr 22, 2024
65b0ef5
Remove shale (#635)
Aug 2, 2024
78f9038
move range proof definition to range_proof.rs
Aug 9, 2024
6b85b78
update _range_proof implementation
Aug 9, 2024
8a1e5cf
appease clippy
Aug 9, 2024
1e58d69
uncomment test
Aug 9, 2024
79b68fd
make RangeProof parameterize on Box not Vec
Aug 10, 2024
3d321c4
nits
Aug 10, 2024
56eda2c
nit
Aug 10, 2024
15eb1a1
into_boxed_slice() --> into()
Aug 10, 2024
f1a343a
first pass of verification
Aug 10, 2024
960185c
comments
Aug 10, 2024
02010b1
WIP implement RangeProof::verify
Aug 13, 2024
fa44490
Implement range proof generation (#698)
Aug 13, 2024
1b5ba03
Add helper method to get a node (#699)
Aug 13, 2024
3f04744
Merge remote-tracking branch 'origin/rkuris/new-node-store-interface'…
Aug 13, 2024
0d2e602
Merge remote-tracking branch 'origin/main' into implement-range-proof
Aug 13, 2024
67299ec
WIP implement RangeProof::verify
Aug 13, 2024
a3a7437
nit
Aug 13, 2024
8b0b952
WIP implement RangeProof::verify
Aug 13, 2024
eac65af
add (failing) test
Aug 14, 2024
da0d2f4
make range proof generation match merkledb
Aug 14, 2024
43243e3
Merge branch 'fix-range-proof-generation' into implement-range-proof
Aug 14, 2024
e5256b4
add to test
Aug 14, 2024
41bc69c
Merge branch 'main' into implement-range-proof
rkuris Aug 19, 2024
241cd62
Merge branch 'main' into implement-range-proof
rkuris Oct 11, 2024
bb711a3
Merge branch 'main' into implement-range-proof
rkuris Jan 17, 2025
732db01
Merge branch 'main' into implement-range-proof
rkuris Jan 24, 2025
f720f5e
Merge branch 'main' into implement-range-proof
rkuris Feb 19, 2025
f90be77
Merge conflict cleanups
rkuris Feb 19, 2025
ca7dd13
Fix tests and remove warnings
rkuris Feb 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Finish implementation of linear store write
Added lots of test cases
rkuris committed Apr 8, 2024
commit 4c27f595b83ef1a4ee2fb7185b4bd9c2538f70c0
190 changes: 186 additions & 4 deletions firewood/src/storage/linear/proposed.rs
Original file line number Diff line number Diff line change
@@ -113,9 +113,79 @@ pub(crate) struct Immutable;

impl<P: ReadLinearStore> WriteLinearStore for Proposed<P, Mutable> {
fn write(&mut self, offset: u64, object: &[u8]) -> Result<usize, Error> {
// TODO: naive implementation
self.new.insert(offset, Box::from(object));
// TODO: coalesce
debug_assert!(!object.is_empty());
// search the modifications in front of (or on) this one that can be merged
// into ours. If we merge, we move modify offset and object, so make mutable
// copies of them here
let mut updated_offset = offset;
let mut updated_object: Box<[u8]> = Box::from(object);

// we track areas to delete here, which happens when we combine
let mut merged_offsets_to_delete = Vec::new();

for existing in self.new.range(..=updated_offset).rev() {
let old_end = *existing.0 + existing.1.len() as u64;
// found smme--------- s=existing.0 e=old_end
// original ----smmme---- s=new_start e=new_start+object.len() [0 overlap]
// original --smmmme----- with overlap
// new smmmmmmme----
if updated_offset <= old_end {
// we have some overlap, so we'll have only one area when we're done
// mark the original area for deletion and recompute it
// TODO: can this be done with iter/zip?
let mut updated = vec![];
for offset in *existing.0.. {
updated.push(
if offset >= updated_offset
&& offset < updated_offset + updated_object.len() as u64
{
*updated_object
.get((offset - updated_offset) as usize)
.expect("bounds checked above")
} else if let Some(res) =
existing.1.get((offset - *existing.0) as usize)
{
*res
} else {
break;
},
);
}
updated_offset = *existing.0;
merged_offsets_to_delete.push(updated_offset);
updated_object = updated.into_boxed_slice();
}
}
// TODO: search for modifications after this one that can be merged
for existing in self.new.range(updated_offset + 1..) {
// existing ----smme---- s=existing.0 e=existing.0+existing.1.len()
// adding --smme------ s=updated_offset e=new_end
let new_end = updated_offset + updated_object.len() as u64;
if new_end >= *existing.0 {
// we have some overlap
let mut updated = vec![];
for offset in updated_offset.. {
updated.push(if offset >= updated_offset && offset < new_end {
*updated_object
.get((offset - updated_offset) as usize)
.expect("bounds checked above")
} else if let Some(res) =
existing.1.get((offset - *existing.0) as usize)
{
*res
} else {
break;
});
}
merged_offsets_to_delete.push(*existing.0);
updated_object = updated.into_boxed_slice();
}
}
for delete_offset in merged_offsets_to_delete {
self.new.remove(&delete_offset);
}

self.new.insert(updated_offset, updated_object);
Ok(object.len())
}
}
@@ -308,7 +378,119 @@ mod test {
}
let mut data = [0u8; ConstBacked::DATA.len()];
child.stream_from(0).unwrap().read_exact(&mut data).unwrap();
println!("{:?}", child);
assert_eq!(&data, b"r1ndom data");
}

// possible cases (o) represents unmodified data (m) is modified in first
// modification, M is new modification on top of old
// oommoo < original state, one modification at offset 2 length 2 (2,2)
//
// entire contents before first modified store, space between it
// oooooo original (this note is not repeated below)
// oommoo original with first modification (this note is not repeated below)
// M----- modification at offset 0, length 1
// Mommoo result: two modified areas (0, 1) and (2, 2)
#[test_case(&[(2, 2)], (0, 1), b"Mommoo", 2)]
// adjoints the first modified store, no overlap
// MM---- modification at offset 0, length 2
// MMmmoo result: one enlarged modified area (0, 4)
#[test_case(&[(2, 2)], (0, 2), b"MMmmoo", 1)]
// starts before and overlaps some of the first modified store
// MMM--- modification at offset 0, length 3
// MMMmoo result: one enlarged modified area (0, 4)
#[test_case(&[(2, 2)], (0, 3), b"MMMmoo", 1)]
// still starts before and overlaps, modifies all of it
// MMMM-- modification at offset 0, length 4
// MMMMoo same result (0, 4)
#[test_case(&[(2,2)], (0, 4), b"MMMMoo", 1)]
// starts at same offset, modifies some of it
// --M--- modification at offset 2, length 1
// ooMmoo result: one modified area (2, 2)
#[test_case(&[(2, 2)], (2, 1), b"ooMmoo", 1)]
// same offset, exact fit
// --MM-- modification at offset 2, length 2
// ooMMoo result: one modified area (2, 2)
#[test_case(&[(2, 2)], (2, 2), b"ooMMoo", 1)]
// starts at same offset, enlarges
// --MMM- modification at offset 2, length 3
// ooMMMo result: one enlarged modified area (2, 3)
#[test_case(&[(2, 2)], (2, 3), b"ooMMMo", 1)]
// within existing offset
// ---M-- modification at offset 3, length 1
// oomMoo result: one modified area (2, 2)
#[test_case(&[(2, 2)], (3, 1), b"oomMoo", 1)]
// starts within original offset, becomes larger
// ---MM- modification at offset 3, length 2
// oomMMo result: one modified area (2, 3)
#[test_case(&[(2, 2)], (3, 2), b"oomMMo", 1)]
// starts immediately after modified area
// ----M- modification at offset 4, length 1
// oommMo result: one modified area (2, 3)
#[test_case(&[(2, 2)], (4, 1), b"oommMo", 1)]
// disjoint from modified area
// -----M modification at offset 5, length 1
// oommoM result: two modified areas (2, 2) and (5, 1)
#[test_case(&[(2, 2)], (5, 1), b"oommoM", 2)]
// consume it all
// MMMMMM modification at offset 0, length 6
// MMMMMM result: one modified area
#[test_case(&[(2, 2)], (0, 6), b"MMMMMM", 1)]
// consume all
// starting mods are:
// omomom
// MMMMMM modification at offset 0 length 6
// MMMMMM result, one modified area
#[test_case(&[(1, 1),(3, 1), (5, 1)], (0, 6), b"MMMMMM", 1)]
// consume two
// MMMM-- modificatoin at offset 0, length 4
// MMMMom result, two modified areas
#[test_case(&[(1, 1),(3, 1), (5, 1)], (0, 4), b"MMMMom", 2)]
// consume all, just but don't modify the last one
#[test_case(&[(1, 1),(3, 1), (5, 1)], (0, 5), b"MMMMMm", 1)]

// extend store from within original area
#[test_case(&[(2, 2)], (5, 6), b"oommoMMMMMM", 2)]

// extend store beyond original area
#[test_case(&[(2, 2)], (6, 6), b"oommooMMMMMM", 2)]

// consume multiple before us
#[test_case(&[(1, 1), (3, 1)], (2, 3), b"omMMMo", 1)]

fn test_combiner(
original_mods: &[(u64, usize)],
new_mod: (u64, usize),
result: &'static [u8],
segments: usize,
) -> Result<(), Error> {
let mut proposal: Proposed<ConstBacked, Mutable> = ConstBacked::new(b"oooooo").into();
for mods in original_mods {
proposal.write(
mods.0,
(0..mods.1)
.map(|_| b'm')
.collect::<Vec<_>>()
.as_slice(),
)?;
}

proposal.write(
new_mod.0,
(0..new_mod.1).map(|_| b'M').collect::<Vec<_>>().as_slice(),
)?;

// this bleeds the implementation, but I this made debugging the tests way easier...
assert_eq!(proposal.new.len(), segments);

let mut data = vec![];
proposal
.stream_from(0)
.unwrap()
.read_to_end(&mut data)
.unwrap();

assert_eq!(data, result);

Ok(())
}
}
6 changes: 3 additions & 3 deletions firewood/src/storage/linear/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -42,14 +42,14 @@ impl From<ConstBacked> for Proposed<ConstBacked, Immutable> {
impl ReadLinearStore for ConstBacked {
fn stream_from(&self, addr: u64) -> Result<Box<dyn Read>, std::io::Error> {
Ok(Box::new(Cursor::new(
Self::DATA
self.data
.get(addr as usize..)
.expect("invalid offset in test"),
.unwrap_or(&[]),
)))
}

fn size(&self) -> Result<u64, Error> {
Ok(Self::DATA.len() as u64)
Ok(self.data.len() as u64)
}
}