Use `u64` for the internal state of `Position` and `Address::index`
This commit is contained in:
parent
b4bebd497e
commit
34d9aa25bc
|
@ -1093,15 +1093,14 @@ mod tests {
|
||||||
where
|
where
|
||||||
G::Value: Hashable + Clone + Ord + Debug + 'static,
|
G::Value: Hashable + Clone + Ord + Debug + 'static,
|
||||||
{
|
{
|
||||||
proptest::collection::vec(arb_operation(item_gen, 0..max_count), 0..max_count).prop_map(
|
let pos_gen = (0..max_count).prop_map(|p| Position::try_from(p).unwrap());
|
||||||
|ops| {
|
proptest::collection::vec(arb_operation(item_gen, pos_gen), 0..max_count).prop_map(|ops| {
|
||||||
let mut tree: BridgeTree<G::Value, usize, 8> = BridgeTree::new(10, 0);
|
let mut tree: BridgeTree<G::Value, usize, 8> = BridgeTree::new(10, 0);
|
||||||
for (i, op) in ops.into_iter().enumerate() {
|
for (i, op) in ops.into_iter().enumerate() {
|
||||||
apply_operation(&mut tree, op.map_checkpoint_id(|_| i));
|
apply_operation(&mut tree, op.map_checkpoint_id(|_| i));
|
||||||
}
|
}
|
||||||
tree
|
tree
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
|
@ -1171,11 +1170,11 @@ mod tests {
|
||||||
let mut t = BridgeTree::<String, usize, 7>::new(10, 0);
|
let mut t = BridgeTree::<String, usize, 7>::new(10, 0);
|
||||||
let mut to_unmark = vec![];
|
let mut to_unmark = vec![];
|
||||||
let mut has_witness = vec![];
|
let mut has_witness = vec![];
|
||||||
for i in 0usize..100 {
|
for i in 0u64..100 {
|
||||||
let elem: String = format!("{},", i);
|
let elem: String = format!("{},", i);
|
||||||
assert!(t.append(elem), "Append should succeed.");
|
assert!(t.append(elem), "Append should succeed.");
|
||||||
if i % 5 == 0 {
|
if i % 5 == 0 {
|
||||||
t.checkpoint(i + 1);
|
t.checkpoint(usize::try_from(i).unwrap() + 1);
|
||||||
}
|
}
|
||||||
if i % 7 == 0 {
|
if i % 7 == 0 {
|
||||||
t.mark();
|
t.mark();
|
||||||
|
@ -1235,7 +1234,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn check_randomized_u64_ops(
|
fn check_randomized_u64_ops(
|
||||||
ops in proptest::collection::vec(
|
ops in proptest::collection::vec(
|
||||||
arb_operation((0..32u64).prop_map(SipHashable), 0usize..100),
|
arb_operation(
|
||||||
|
(0..32u64).prop_map(SipHashable),
|
||||||
|
(0u64..100).prop_map(Position::from)
|
||||||
|
),
|
||||||
1..100
|
1..100
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -1247,7 +1249,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn check_randomized_str_ops(
|
fn check_randomized_str_ops(
|
||||||
ops in proptest::collection::vec(
|
ops in proptest::collection::vec(
|
||||||
arb_operation((97u8..123).prop_map(|c| char::from(c).to_string()), 0usize..100),
|
arb_operation(
|
||||||
|
(97u8..123).prop_map(|c| char::from(c).to_string()),
|
||||||
|
(0u64..100).prop_map(Position::from)
|
||||||
|
),
|
||||||
1..100
|
1..100
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -18,6 +18,22 @@ and this project adheres to Rust's notion of
|
||||||
the `legacy-api` feature flag related to constructing witnesses for leaves
|
the `legacy-api` feature flag related to constructing witnesses for leaves
|
||||||
of a Merkle tree.
|
of a Merkle tree.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- `Position` has been made isomorphic to `u64` via introduction of `From`
|
||||||
|
implementations between these types.
|
||||||
|
- `Address::index` now returns `u64` instead of `usize`
|
||||||
|
- The `expected_ommers` field of `FrontierError::PositionMismatch` now
|
||||||
|
has type `u8` instead of `usize`.
|
||||||
|
- `Address::context` now also uses `u64` instead of `usize` for when it returns
|
||||||
|
a range of index values.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- The `From<usize>` impl for `Position` has been removed, as has
|
||||||
|
`From<Position> for usize`. In addition, `Add` and `Sub` impls
|
||||||
|
that previously allowed numeric operations between `usize` and
|
||||||
|
`Position` values have been removed in favor of similar operations
|
||||||
|
that instead allow computing with `u64`.
|
||||||
|
|
||||||
## [0.3.1] - 2023-02-28
|
## [0.3.1] - 2023-02-28
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -12,7 +12,7 @@ use {std::collections::VecDeque, std::iter::repeat};
|
||||||
pub enum FrontierError {
|
pub enum FrontierError {
|
||||||
/// An error representing that the number of ommers provided in frontier construction does not
|
/// An error representing that the number of ommers provided in frontier construction does not
|
||||||
/// the expected length of the ommers list given the position.
|
/// the expected length of the ommers list given the position.
|
||||||
PositionMismatch { expected_ommers: usize },
|
PositionMismatch { expected_ommers: u8 },
|
||||||
/// An error representing that the position and/or list of ommers provided to frontier
|
/// An error representing that the position and/or list of ommers provided to frontier
|
||||||
/// construction would result in a frontier that exceeds the maximum statically allowed depth
|
/// construction would result in a frontier that exceeds the maximum statically allowed depth
|
||||||
/// of the tree. `depth` is the minimum tree depth that would be required in order for that
|
/// of the tree. `depth` is the minimum tree depth that would be required in order for that
|
||||||
|
@ -43,7 +43,7 @@ impl<H> NonEmptyFrontier<H> {
|
||||||
/// Constructs a new frontier from its constituent parts.
|
/// Constructs a new frontier from its constituent parts.
|
||||||
pub fn from_parts(position: Position, leaf: H, ommers: Vec<H>) -> Result<Self, FrontierError> {
|
pub fn from_parts(position: Position, leaf: H, ommers: Vec<H>) -> Result<Self, FrontierError> {
|
||||||
let expected_ommers = position.past_ommer_count();
|
let expected_ommers = position.past_ommer_count();
|
||||||
if ommers.len() == expected_ommers {
|
if ommers.len() == expected_ommers.into() {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
position,
|
position,
|
||||||
leaf,
|
leaf,
|
||||||
|
@ -89,25 +89,25 @@ impl<H: Hashable + Clone> NonEmptyFrontier<H> {
|
||||||
let new_root_level = self.position.root_level();
|
let new_root_level = self.position.root_level();
|
||||||
|
|
||||||
let mut carry = Some((prior_leaf, 0.into()));
|
let mut carry = Some((prior_leaf, 0.into()));
|
||||||
let mut new_ommers = Vec::with_capacity(self.position.past_ommer_count());
|
let mut new_ommers = Vec::with_capacity(self.position.past_ommer_count().into());
|
||||||
for (addr, source) in prior_position.witness_addrs(new_root_level) {
|
for (addr, source) in prior_position.witness_addrs(new_root_level) {
|
||||||
if let Source::Past(i) = source {
|
if let Source::Past(i) = source {
|
||||||
if let Some((carry_ommer, carry_lvl)) = carry.as_ref() {
|
if let Some((carry_ommer, carry_lvl)) = carry.as_ref() {
|
||||||
if *carry_lvl == addr.level() {
|
if *carry_lvl == addr.level() {
|
||||||
carry = Some((
|
carry = Some((
|
||||||
H::combine(addr.level(), &self.ommers[i], carry_ommer),
|
H::combine(addr.level(), &self.ommers[usize::from(i)], carry_ommer),
|
||||||
addr.level() + 1,
|
addr.level() + 1,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// insert the carry at the first empty slot; then the rest of the
|
// insert the carry at the first empty slot; then the rest of the
|
||||||
// ommers will remain unchanged
|
// ommers will remain unchanged
|
||||||
new_ommers.push(carry_ommer.clone());
|
new_ommers.push(carry_ommer.clone());
|
||||||
new_ommers.push(self.ommers[i].clone());
|
new_ommers.push(self.ommers[usize::from(i)].clone());
|
||||||
carry = None;
|
carry = None;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// when there's no carry, just push on the ommer value
|
// when there's no carry, just push on the ommer value
|
||||||
new_ommers.push(self.ommers[i].clone());
|
new_ommers.push(self.ommers[usize::from(i)].clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,9 @@ impl<H: Hashable + Clone> NonEmptyFrontier<H> {
|
||||||
.fold(digest, |d, l| H::combine(l, &d, &H::empty_root(l)));
|
.fold(digest, |d, l| H::combine(l, &d, &H::empty_root(l)));
|
||||||
|
|
||||||
let res_digest = match source {
|
let res_digest = match source {
|
||||||
Source::Past(i) => H::combine(addr.level(), &self.ommers[i], &digest),
|
Source::Past(i) => {
|
||||||
|
H::combine(addr.level(), &self.ommers[usize::from(i)], &digest)
|
||||||
|
}
|
||||||
Source::Future => {
|
Source::Future => {
|
||||||
H::combine(addr.level(), &digest, &H::empty_root(addr.level()))
|
H::combine(addr.level(), &digest, &H::empty_root(addr.level()))
|
||||||
}
|
}
|
||||||
|
@ -162,7 +164,7 @@ impl<H: Hashable + Clone> NonEmptyFrontier<H> {
|
||||||
self.position()
|
self.position()
|
||||||
.witness_addrs(depth.into())
|
.witness_addrs(depth.into())
|
||||||
.map(|(addr, source)| match source {
|
.map(|(addr, source)| match source {
|
||||||
Source::Past(i) => Ok(self.ommers[i].clone()),
|
Source::Past(i) => Ok(self.ommers[usize::from(i)].clone()),
|
||||||
Source::Future => complement_nodes(addr).ok_or(addr),
|
Source::Future => complement_nodes(addr).ok_or(addr),
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
@ -370,14 +372,13 @@ impl<H: Hashable + Clone, const DEPTH: u8> CommitmentTree<H, DEPTH> {
|
||||||
(f.leaf().clone(), None)
|
(f.leaf().clone(), None)
|
||||||
};
|
};
|
||||||
|
|
||||||
let upos: usize = f.position().into();
|
|
||||||
Self {
|
Self {
|
||||||
left: Some(left),
|
left: Some(left),
|
||||||
right,
|
right,
|
||||||
parents: (1u8..DEPTH)
|
parents: (1u8..DEPTH)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
if upos & (1 << i) == 0 {
|
if u64::from(f.position()) & (1 << i) == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
ommers_iter.next()
|
ommers_iter.next()
|
||||||
|
@ -404,7 +405,7 @@ impl<H: Hashable + Clone, const DEPTH: u8> CommitmentTree<H, DEPTH> {
|
||||||
|
|
||||||
// If a frontier cannot be successfully constructed from the
|
// If a frontier cannot be successfully constructed from the
|
||||||
// parts of a commitment tree, it is a programming error.
|
// parts of a commitment tree, it is a programming error.
|
||||||
Frontier::from_parts((self.size() - 1).into(), leaf, ommers)
|
Frontier::from_parts((self.size() - 1).try_into().unwrap(), leaf, ommers)
|
||||||
.expect("Frontier should be constructable from CommitmentTree.")
|
.expect("Frontier should be constructable from CommitmentTree.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ impl<C> Retention<C> {
|
||||||
pub enum Source {
|
pub enum Source {
|
||||||
/// The sibling to the address can be derived from the incremental frontier
|
/// The sibling to the address can be derived from the incremental frontier
|
||||||
/// at the contained ommer index
|
/// at the contained ommer index
|
||||||
Past(usize),
|
Past(u8),
|
||||||
/// The sibling to the address must be obtained from values discovered by
|
/// The sibling to the address must be obtained from values discovered by
|
||||||
/// the addition of more nodes to the tree
|
/// the addition of more nodes to the tree
|
||||||
Future,
|
Future,
|
||||||
|
@ -61,7 +61,7 @@ pub enum Source {
|
||||||
struct WitnessAddrsIter {
|
struct WitnessAddrsIter {
|
||||||
root_level: Level,
|
root_level: Level,
|
||||||
current: Address,
|
current: Address,
|
||||||
ommer_count: usize,
|
ommer_count: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for WitnessAddrsIter {
|
impl Iterator for WitnessAddrsIter {
|
||||||
|
@ -91,7 +91,7 @@ impl Iterator for WitnessAddrsIter {
|
||||||
/// A type representing the position of a leaf in a Merkle tree.
|
/// A type representing the position of a leaf in a Merkle tree.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Position(usize);
|
pub struct Position(u64);
|
||||||
|
|
||||||
impl Position {
|
impl Position {
|
||||||
/// Return whether the position is odd-valued.
|
/// Return whether the position is odd-valued.
|
||||||
|
@ -102,15 +102,17 @@ impl Position {
|
||||||
/// Returns the minimum possible level of the root of a binary tree containing at least
|
/// Returns the minimum possible level of the root of a binary tree containing at least
|
||||||
/// `self + 1` nodes.
|
/// `self + 1` nodes.
|
||||||
pub fn root_level(&self) -> Level {
|
pub fn root_level(&self) -> Level {
|
||||||
Level((usize::BITS - self.0.leading_zeros()) as u8)
|
Level((u64::BITS - self.0.leading_zeros()) as u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of cousins and/or ommers required to construct an authentication
|
/// Returns the number of cousins and/or ommers required to construct an authentication
|
||||||
/// path to the root of a merkle tree that has `self + 1` nodes.
|
/// path to the root of a merkle tree that has `self + 1` nodes.
|
||||||
pub fn past_ommer_count(&self) -> usize {
|
pub fn past_ommer_count(&self) -> u8 {
|
||||||
(0..self.root_level().0)
|
(0..self.root_level().0)
|
||||||
.filter(|i| (self.0 >> i) & 0x1 == 1)
|
.filter(|i| (self.0 >> i) & 0x1 == 1)
|
||||||
.count()
|
.count()
|
||||||
|
.try_into()
|
||||||
|
.unwrap() // this is safe because we're counting within a `u8` range
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the binary tree having `self` as the position of the rightmost leaf
|
/// Returns whether the binary tree having `self` as the position of the rightmost leaf
|
||||||
|
@ -133,34 +135,34 @@ impl Position {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Position> for usize {
|
impl From<Position> for u64 {
|
||||||
fn from(p: Position) -> usize {
|
fn from(p: Position) -> Self {
|
||||||
p.0
|
p.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Position> for u64 {
|
impl From<u64> for Position {
|
||||||
fn from(p: Position) -> Self {
|
fn from(sz: u64) -> Self {
|
||||||
p.0 as u64
|
Self(sz)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<usize> for Position {
|
impl Add<u64> for Position {
|
||||||
type Output = Position;
|
type Output = Position;
|
||||||
fn add(self, other: usize) -> Self {
|
fn add(self, other: u64) -> Self {
|
||||||
Position(self.0 + other)
|
Position(self.0 + other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<usize> for Position {
|
impl AddAssign<u64> for Position {
|
||||||
fn add_assign(&mut self, other: usize) {
|
fn add_assign(&mut self, other: u64) {
|
||||||
self.0 += other
|
self.0 += other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub<usize> for Position {
|
impl Sub<u64> for Position {
|
||||||
type Output = Position;
|
type Output = Position;
|
||||||
fn sub(self, other: usize) -> Self {
|
fn sub(self, other: u64) -> Self {
|
||||||
if self.0 < other {
|
if self.0 < other {
|
||||||
panic!("position underflow");
|
panic!("position underflow");
|
||||||
}
|
}
|
||||||
|
@ -168,16 +170,17 @@ impl Sub<usize> for Position {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<usize> for Position {
|
impl TryFrom<usize> for Position {
|
||||||
fn from(sz: usize) -> Self {
|
type Error = TryFromIntError;
|
||||||
Self(sz)
|
fn try_from(sz: usize) -> Result<Self, Self::Error> {
|
||||||
|
<u64>::try_from(sz).map(Self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u64> for Position {
|
impl TryFrom<Position> for usize {
|
||||||
type Error = TryFromIntError;
|
type Error = TryFromIntError;
|
||||||
fn try_from(sz: u64) -> Result<Self, Self::Error> {
|
fn try_from(p: Position) -> Result<Self, Self::Error> {
|
||||||
<usize>::try_from(sz).map(Self)
|
<usize>::try_from(p.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +219,7 @@ impl From<Level> for u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supporting sub-8-bit platforms isn't on our
|
||||||
impl From<Level> for usize {
|
impl From<Level> for usize {
|
||||||
fn from(level: Level) -> usize {
|
fn from(level: Level) -> usize {
|
||||||
level.0 as usize
|
level.0 as usize
|
||||||
|
@ -245,12 +249,12 @@ impl Sub<u8> for Level {
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Address {
|
pub struct Address {
|
||||||
level: Level,
|
level: Level,
|
||||||
index: usize,
|
index: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
/// Construct a new address from its constituent parts.
|
/// Construct a new address from its constituent parts.
|
||||||
pub fn from_parts(level: Level, index: usize) -> Self {
|
pub fn from_parts(level: Level, index: u64) -> Self {
|
||||||
Address { level, index }
|
Address { level, index }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +276,7 @@ impl Address {
|
||||||
/// The index of an address is defined as the number of subtrees with their roots
|
/// The index of an address is defined as the number of subtrees with their roots
|
||||||
/// at the address's level that appear to the left of this address in a binary
|
/// at the address's level that appear to the left of this address in a binary
|
||||||
/// tree of arbitrary height > level * 2 + 1.
|
/// tree of arbitrary height > level * 2 + 1.
|
||||||
pub fn index(&self) -> usize {
|
pub fn index(&self) -> u64 {
|
||||||
self.index
|
self.index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,7 +358,7 @@ impl Address {
|
||||||
/// than or equal to that of this address) or the range of indices of root addresses of
|
/// than or equal to that of this address) or the range of indices of root addresses of
|
||||||
/// subtrees with roots at the given level contained within the tree with its root at this
|
/// subtrees with roots at the given level contained within the tree with its root at this
|
||||||
/// address otherwise.
|
/// address otherwise.
|
||||||
pub fn context(&self, level: Level) -> Either<Address, Range<usize>> {
|
pub fn context(&self, level: Level) -> Either<Address, Range<u64>> {
|
||||||
if level >= self.level {
|
if level >= self.level {
|
||||||
Either::Left(Address {
|
Either::Left(Address {
|
||||||
level,
|
level,
|
||||||
|
@ -541,7 +545,7 @@ pub(crate) mod tests {
|
||||||
assert!(Position(3).is_complete_subtree(Level(2)));
|
assert!(Position(3).is_complete_subtree(Level(2)));
|
||||||
assert!(!Position(4).is_complete_subtree(Level(2)));
|
assert!(!Position(4).is_complete_subtree(Level(2)));
|
||||||
assert!(Position(7).is_complete_subtree(Level(3)));
|
assert!(Position(7).is_complete_subtree(Level(3)));
|
||||||
assert!(Position(u32::MAX as usize).is_complete_subtree(Level(32)));
|
assert!(Position(u32::MAX as u64).is_complete_subtree(Level(32)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -147,11 +147,11 @@ pub fn append_str<C>(x: &str, retention: Retention<C>) -> Operation<String, C> {
|
||||||
Operation::Append(x.to_string(), retention)
|
Operation::Append(x.to_string(), retention)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unmark<H, C>(pos: usize) -> Operation<H, C> {
|
pub fn unmark<H, C>(pos: u64) -> Operation<H, C> {
|
||||||
Operation::Unmark(Position::from(pos))
|
Operation::Unmark(Position::from(pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn witness<H, C>(pos: usize, depth: usize) -> Operation<H, C> {
|
pub fn witness<H, C>(pos: u64, depth: usize) -> Operation<H, C> {
|
||||||
Operation::Witness(Position::from(pos), depth)
|
Operation::Witness(Position::from(pos), depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ pub fn arb_retention() -> impl Strategy<Value = Retention<()>> {
|
||||||
|
|
||||||
pub fn arb_operation<G: Strategy + Clone>(
|
pub fn arb_operation<G: Strategy + Clone>(
|
||||||
item_gen: G,
|
item_gen: G,
|
||||||
pos_gen: impl Strategy<Value = usize> + Clone,
|
pos_gen: impl Strategy<Value = Position> + Clone,
|
||||||
) -> impl Strategy<Value = Operation<G::Value, ()>>
|
) -> impl Strategy<Value = Operation<G::Value, ()>>
|
||||||
where
|
where
|
||||||
G::Value: Clone + 'static,
|
G::Value: Clone + 'static,
|
||||||
|
@ -230,17 +230,11 @@ where
|
||||||
Just(Operation::MarkedPositions),
|
Just(Operation::MarkedPositions),
|
||||||
],
|
],
|
||||||
Just(Operation::GarbageCollect),
|
Just(Operation::GarbageCollect),
|
||||||
pos_gen
|
pos_gen.clone().prop_map(Operation::MarkedLeaf),
|
||||||
.clone()
|
pos_gen.clone().prop_map(Operation::Unmark),
|
||||||
.prop_map(|i| Operation::MarkedLeaf(Position::from(i))),
|
|
||||||
pos_gen
|
|
||||||
.clone()
|
|
||||||
.prop_map(|i| Operation::Unmark(Position::from(i))),
|
|
||||||
Just(Operation::Checkpoint(())),
|
Just(Operation::Checkpoint(())),
|
||||||
Just(Operation::Rewind),
|
Just(Operation::Rewind),
|
||||||
pos_gen
|
pos_gen.prop_flat_map(|i| (0usize..10).prop_map(move |depth| Operation::Witness(i, depth))),
|
||||||
.prop_flat_map(|i| (0usize..10)
|
|
||||||
.prop_map(move |depth| Operation::Witness(Position::from(i), depth))),
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,19 +283,20 @@ pub fn check_operations<H: Hashable + Ord + Clone, C: Clone, T: Tree<H, C>>(
|
||||||
} else {
|
} else {
|
||||||
prop_assert_eq!(
|
prop_assert_eq!(
|
||||||
tree_size,
|
tree_size,
|
||||||
tree.current_position().map_or(0, |p| usize::from(p) + 1)
|
tree.current_position()
|
||||||
|
.map_or(0, |p| usize::try_from(p).unwrap() + 1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CurrentPosition => {
|
CurrentPosition => {
|
||||||
if let Some(pos) = tree.current_position() {
|
if let Some(pos) = tree.current_position() {
|
||||||
prop_assert!(tree_size > 0);
|
prop_assert!(tree_size > 0);
|
||||||
prop_assert_eq!(tree_size - 1, pos.into());
|
prop_assert_eq!(tree_size - 1, pos.try_into().unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MarkedLeaf(position) => {
|
MarkedLeaf(position) => {
|
||||||
if tree.get_marked_leaf(*position).is_some() {
|
if tree.get_marked_leaf(*position).is_some() {
|
||||||
prop_assert!(<usize>::from(*position) < tree_size);
|
prop_assert!(<usize>::try_from(*position).unwrap() < tree_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Unmark(position) => {
|
Unmark(position) => {
|
||||||
|
@ -322,7 +317,7 @@ pub fn check_operations<H: Hashable + Ord + Clone, C: Clone, T: Tree<H, C>>(
|
||||||
}
|
}
|
||||||
Witness(position, depth) => {
|
Witness(position, depth) => {
|
||||||
if let Some(path) = tree.witness(*position, *depth) {
|
if let Some(path) = tree.witness(*position, *depth) {
|
||||||
let value: H = tree_values[<usize>::from(*position)].clone();
|
let value: H = tree_values[<usize>::try_from(*position).unwrap()].clone();
|
||||||
let tree_root = tree.root(*depth);
|
let tree_root = tree.root(*depth);
|
||||||
|
|
||||||
if tree_checkpoints.len() >= *depth {
|
if tree_checkpoints.len() >= *depth {
|
||||||
|
@ -361,7 +356,7 @@ pub fn compute_root_from_witness<H: Hashable>(value: H, position: Position, path
|
||||||
for (i, v) in path
|
for (i, v) in path
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, v)| (((<usize>::from(position) >> i) & 1) == 1, v))
|
.map(|(i, v)| (((<u64>::from(position) >> i) & 1) == 1, v))
|
||||||
{
|
{
|
||||||
if i {
|
if i {
|
||||||
cur = H::combine(lvl, v, &cur);
|
cur = H::combine(lvl, v, &cur);
|
||||||
|
@ -719,7 +714,7 @@ pub fn check_witnesses<T: Tree<String, usize> + std::fmt::Debug, F: Fn(usize) ->
|
||||||
.map(|c| Append(c.to_string(), Marked))
|
.map(|c| Append(c.to_string(), Marked))
|
||||||
.chain(Some(Append('m'.to_string(), Ephemeral)))
|
.chain(Some(Append('m'.to_string(), Ephemeral)))
|
||||||
.chain(Some(Append('n'.to_string(), Ephemeral)))
|
.chain(Some(Append('n'.to_string(), Ephemeral)))
|
||||||
.chain(Some(Witness(11usize.into(), 0)))
|
.chain(Some(Witness(11u64.into(), 0)))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut tree = new_tree(100);
|
let mut tree = new_tree(100);
|
||||||
|
@ -770,7 +765,7 @@ pub fn check_witnesses<T: Tree<String, usize> + std::fmt::Debug, F: Fn(usize) ->
|
||||||
is_marked: false,
|
is_marked: false,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Witness(3usize.into(), 5),
|
Witness(3u64.into(), 5),
|
||||||
];
|
];
|
||||||
let mut tree = new_tree(100);
|
let mut tree = new_tree(100);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -963,23 +958,23 @@ pub fn check_rewind_remove_mark<T: Tree<String, usize>, F: Fn(usize) -> T>(new_t
|
||||||
tree.append("e".to_string(), Retention::Marked);
|
tree.append("e".to_string(), Retention::Marked);
|
||||||
tree.checkpoint(1);
|
tree.checkpoint(1);
|
||||||
assert!(tree.rewind());
|
assert!(tree.rewind());
|
||||||
assert!(tree.remove_mark(0usize.into()));
|
assert!(tree.remove_mark(0u64.into()));
|
||||||
|
|
||||||
// use a maximum number of checkpoints of 1
|
// use a maximum number of checkpoints of 1
|
||||||
let mut tree = new_tree(1);
|
let mut tree = new_tree(1);
|
||||||
assert!(tree.append("e".to_string(), Retention::Marked));
|
assert!(tree.append("e".to_string(), Retention::Marked));
|
||||||
assert!(tree.checkpoint(1));
|
assert!(tree.checkpoint(1));
|
||||||
assert!(tree.marked_positions().contains(&0usize.into()));
|
assert!(tree.marked_positions().contains(&0u64.into()));
|
||||||
assert!(tree.append("f".to_string(), Retention::Ephemeral));
|
assert!(tree.append("f".to_string(), Retention::Ephemeral));
|
||||||
// simulate a spend of `e` at `f`
|
// simulate a spend of `e` at `f`
|
||||||
assert!(tree.remove_mark(0usize.into()));
|
assert!(tree.remove_mark(0u64.into()));
|
||||||
// even though the mark has been staged for removal, it's not gone yet
|
// even though the mark has been staged for removal, it's not gone yet
|
||||||
assert!(tree.marked_positions().contains(&0usize.into()));
|
assert!(tree.marked_positions().contains(&0u64.into()));
|
||||||
assert!(tree.checkpoint(2));
|
assert!(tree.checkpoint(2));
|
||||||
// the newest checkpoint will have caused the oldest to roll off, and
|
// the newest checkpoint will have caused the oldest to roll off, and
|
||||||
// so the forgotten node will be unmarked
|
// so the forgotten node will be unmarked
|
||||||
assert!(!tree.marked_positions().contains(&0usize.into()));
|
assert!(!tree.marked_positions().contains(&0u64.into()));
|
||||||
assert!(!tree.remove_mark(0usize.into()));
|
assert!(!tree.remove_mark(0u64.into()));
|
||||||
|
|
||||||
// The following check_operations tests cover errors where the
|
// The following check_operations tests cover errors where the
|
||||||
// test framework itself previously did not correctly handle
|
// test framework itself previously did not correctly handle
|
||||||
|
|
|
@ -4,6 +4,8 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use crate::{testing::Tree, Hashable, Level, Position, Retention};
|
use crate::{testing::Tree, Hashable, Level, Position, Retention};
|
||||||
|
|
||||||
|
const MAX_COMPLETE_SIZE_ERROR: &str = "Positions of a `CompleteTree` must fit into the platform word size, because larger complete trees are not representable.";
|
||||||
|
|
||||||
pub(crate) fn root<H: Hashable + Clone>(leaves: &[H], depth: u8) -> H {
|
pub(crate) fn root<H: Hashable + Clone>(leaves: &[H], depth: u8) -> H {
|
||||||
let empty_leaf = H::empty_leaf();
|
let empty_leaf = H::empty_leaf();
|
||||||
let mut leaves = leaves
|
let mut leaves = leaves
|
||||||
|
@ -129,7 +131,9 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
|
||||||
if self.leaves.is_empty() {
|
if self.leaves.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some((self.leaves.len() - 1).into())
|
// this unwrap is safe because nobody is ever going to create a complete
|
||||||
|
// tree with more than 2^64 leaves
|
||||||
|
Some((self.leaves.len() - 1).try_into().unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +162,10 @@ impl<H: Hashable, C: Clone + Ord + core::fmt::Debug, const DEPTH: u8> CompleteTr
|
||||||
fn checkpoint(&mut self, id: C, pos: Option<Position>) {
|
fn checkpoint(&mut self, id: C, pos: Option<Position>) {
|
||||||
self.checkpoints.insert(
|
self.checkpoints.insert(
|
||||||
id,
|
id,
|
||||||
Checkpoint::at_length(pos.map_or_else(|| 0, |p| usize::from(p) + 1)),
|
Checkpoint::at_length(pos.map_or_else(
|
||||||
|
|| 0,
|
||||||
|
|p| usize::try_from(p).expect(MAX_COMPLETE_SIZE_ERROR) + 1,
|
||||||
|
)),
|
||||||
);
|
);
|
||||||
if self.checkpoints.len() > self.max_checkpoints {
|
if self.checkpoints.len() > self.max_checkpoints {
|
||||||
self.drop_oldest_checkpoint();
|
self.drop_oldest_checkpoint();
|
||||||
|
@ -226,7 +233,7 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D
|
||||||
fn get_marked_leaf(&self, position: Position) -> Option<&H> {
|
fn get_marked_leaf(&self, position: Position) -> Option<&H> {
|
||||||
if self.marks.contains(&position) {
|
if self.marks.contains(&position) {
|
||||||
self.leaves
|
self.leaves
|
||||||
.get(usize::from(position))
|
.get(usize::try_from(position).expect(MAX_COMPLETE_SIZE_ERROR))
|
||||||
.and_then(|opt: &Option<H>| opt.as_ref())
|
.and_then(|opt: &Option<H>| opt.as_ref())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -254,7 +261,7 @@ impl<H: Hashable + PartialEq + Clone, C: Ord + Clone + core::fmt::Debug, const D
|
||||||
} else {
|
} else {
|
||||||
let mut path = vec![];
|
let mut path = vec![];
|
||||||
|
|
||||||
let mut leaf_idx: usize = position.into();
|
let mut leaf_idx: usize = position.try_into().expect(MAX_COMPLETE_SIZE_ERROR);
|
||||||
for bit in 0..DEPTH {
|
for bit in 0..DEPTH {
|
||||||
leaf_idx ^= 1 << bit;
|
leaf_idx ^= 1 << bit;
|
||||||
path.push(if leaf_idx < leaves_len {
|
path.push(if leaf_idx < leaves_len {
|
||||||
|
|
|
@ -85,7 +85,8 @@ impl<H, const DEPTH: u8> IncrementalWitness<H, DEPTH> {
|
||||||
|
|
||||||
/// Returns the position of the witnessed leaf node in the commitment tree.
|
/// Returns the position of the witnessed leaf node in the commitment tree.
|
||||||
pub fn position(&self) -> Position {
|
pub fn position(&self) -> Position {
|
||||||
Position::from(self.tree.size() - 1)
|
Position::try_from(self.tree.size() - 1)
|
||||||
|
.expect("Commitment trees with more than 2^64 leaves are unsupported.")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the next "depth" of an unfilled subtree.
|
/// Finds the next "depth" of an unfilled subtree.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use core::convert::Infallible;
|
use core::convert::{Infallible, TryFrom};
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::ops::{Deref, Range};
|
use core::ops::{Deref, Range};
|
||||||
|
@ -1424,7 +1424,7 @@ impl<H> ShardStore<H> for Vec<LocatedPrunableTree<H>> {
|
||||||
type Error = Infallible;
|
type Error = Infallible;
|
||||||
|
|
||||||
fn get_shard(&self, shard_root: Address) -> Option<&LocatedPrunableTree<H>> {
|
fn get_shard(&self, shard_root: Address) -> Option<&LocatedPrunableTree<H>> {
|
||||||
self.get(shard_root.index())
|
self.get(usize::try_from(shard_root.index()).expect("SHARD_HEIGHT > 64 is unsupported"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_shard(&self) -> Option<&LocatedPrunableTree<H>> {
|
fn last_shard(&self) -> Option<&LocatedPrunableTree<H>> {
|
||||||
|
@ -1440,7 +1440,8 @@ impl<H> ShardStore<H> for Vec<LocatedPrunableTree<H>> {
|
||||||
root: Tree(Node::Nil),
|
root: Tree(Node::Nil),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
self[subtree_addr.index()] = subtree;
|
self[usize::try_from(subtree_addr.index()).expect("SHARD_HEIGHT > 64 is unsupported")] =
|
||||||
|
subtree;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1449,7 +1450,7 @@ impl<H> ShardStore<H> for Vec<LocatedPrunableTree<H>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn truncate(&mut self, from: Address) -> Result<(), Self::Error> {
|
fn truncate(&mut self, from: Address) -> Result<(), Self::Error> {
|
||||||
self.truncate(from.index());
|
self.truncate(usize::try_from(from.index()).expect("SHARD_HEIGHT > 64 is unsupported"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2741,7 +2742,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn check_randomized_u64_ops(
|
fn check_randomized_u64_ops(
|
||||||
ops in proptest::collection::vec(
|
ops in proptest::collection::vec(
|
||||||
arb_operation((0..32u64).prop_map(SipHashable), 0usize..100),
|
arb_operation(
|
||||||
|
(0..32u64).prop_map(SipHashable),
|
||||||
|
(0u64..100).prop_map(Position::from)
|
||||||
|
),
|
||||||
1..100
|
1..100
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -2753,7 +2757,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn check_randomized_str_ops(
|
fn check_randomized_str_ops(
|
||||||
ops in proptest::collection::vec(
|
ops in proptest::collection::vec(
|
||||||
arb_operation((97u8..123).prop_map(|c| char::from(c).to_string()), 0usize..100),
|
arb_operation(
|
||||||
|
(97u8..123).prop_map(|c| char::from(c).to_string()),
|
||||||
|
(0u64..100).prop_map(Position::from)
|
||||||
|
),
|
||||||
1..100
|
1..100
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
Loading…
Reference in New Issue