Merge pull request #49 from nuttycom/workspace_refactor
Move common utilities to the incrementalmerkletree crate & turn into a workspace.
This commit is contained in:
commit
da9ab5a71d
26
Cargo.toml
26
Cargo.toml
|
@ -1,23 +1,5 @@
|
||||||
[package]
|
[workspace]
|
||||||
name = "bridgetree"
|
members = [
|
||||||
version = "0.2.0"
|
"incrementalmerkletree",
|
||||||
authors = [
|
"bridgetree",
|
||||||
"Kris Nuttycombe <kris@nutty.land>",
|
|
||||||
"Sean Bowe <ewillbefull@gmail.com>",
|
|
||||||
]
|
]
|
||||||
edition = "2018"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
description = "A space-efficient Merkle tree with witnessing of marked leaves, checkpointing & state restoration."
|
|
||||||
homepage = "https://github.com/zcash/bridgetree"
|
|
||||||
repository = "https://github.com/zcash/bridgetree"
|
|
||||||
categories = ["algorithms", "data-structures"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
proptest = { version = "1.0.0", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
proptest = "1.0.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
test-dependencies = ["proptest"]
|
|
||||||
|
|
|
@ -7,6 +7,11 @@ and this project adheres to Rust's notion of
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- The `testing` module has been removed in favor of depending on
|
||||||
|
`incrementalmerkletree::testing`.
|
||||||
|
|
||||||
## [bridgetree-v0.2.0] - 2022-05-10
|
## [bridgetree-v0.2.0] - 2022-05-10
|
||||||
|
|
||||||
The `bridgetree` crate is a fork of `incrementalmerkletree`, with the contents
|
The `bridgetree` crate is a fork of `incrementalmerkletree`, with the contents
|
||||||
|
@ -42,6 +47,8 @@ referred to by their new location.
|
||||||
- `BridgeTree::authentication_path` has been renamed to `BridgeTree::witness`
|
- `BridgeTree::authentication_path` has been renamed to `BridgeTree::witness`
|
||||||
- `BridgeTree::witnessed` has been renamed to `BridgeTree::marked`
|
- `BridgeTree::witnessed` has been renamed to `BridgeTree::marked`
|
||||||
- `BridgeTree::witnessed_indices` has been renamed to `BridgeTree::marked_indices`
|
- `BridgeTree::witnessed_indices` has been renamed to `BridgeTree::marked_indices`
|
||||||
|
- `BridgeTree::append` and `NonEmptyFrontier::append` now take ownership of the
|
||||||
|
value being appended instead of the value being passed by reference.
|
||||||
|
|
||||||
The following types have been moved from the `bridgetree` module of
|
The following types have been moved from the `bridgetree` module of
|
||||||
`incrementalmerkletree` to the crate root:
|
`incrementalmerkletree` to the crate root:
|
||||||
|
@ -84,7 +91,7 @@ The `Tree` interface reflects the renaming of `witness` to `mark` described abov
|
||||||
|
|
||||||
### Removed relative to `incrementalmerkletree-0.3.0`
|
### Removed relative to `incrementalmerkletree-0.3.0`
|
||||||
|
|
||||||
- `bridgetree::Leaf`
|
- `bridgetree::Leaf`
|
||||||
- `bridgetree::AuthFragment`
|
- `bridgetree::AuthFragment`
|
||||||
- `NonEmptyFrontier::size`
|
- `NonEmptyFrontier::size`
|
||||||
- `NonEmptyFrontier::max_altitude`
|
- `NonEmptyFrontier::max_altitude`
|
|
@ -0,0 +1,16 @@
|
||||||
|
# License
|
||||||
|
|
||||||
|
Licensed under either of
|
||||||
|
|
||||||
|
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
||||||
|
|
||||||
|
# Contribution
|
||||||
|
|
||||||
|
Unless you explicitly state otherwise, any contribution intentionally
|
||||||
|
submitted for inclusion in the work by you, as defined in the Apache-2.0
|
||||||
|
license, shall be dual licensed as above, without any additional terms or
|
||||||
|
conditions.
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
[package]
|
||||||
|
name = "bridgetree"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = [
|
||||||
|
"Kris Nuttycombe <kris@nutty.land>",
|
||||||
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
|
]
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "A space-efficient Merkle tree designed for linear appends with witnessing of marked leaves, checkpointing & state restoration."
|
||||||
|
homepage = "https://github.com/zcash/incrementalmerkletree"
|
||||||
|
repository = "https://github.com/zcash/incrementalmerkletree"
|
||||||
|
categories = ["algorithms", "data-structures"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
incrementalmerkletree = { version = "0.3", path = "../incrementalmerkletree" }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
proptest = { version = "1.0.0", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
incrementalmerkletree = { version = "0.3", path = "../incrementalmerkletree", features = ["test-dependencies"] }
|
||||||
|
proptest = "1.0.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test-dependencies = ["proptest"]
|
|
@ -0,0 +1,202 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2021 The Electric Coin Company
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -30,12 +30,6 @@
|
||||||
//! reset the state to.
|
//! reset the state to.
|
||||||
//!
|
//!
|
||||||
//! In this module, the term "ommer" is used as for the sibling of a parent node in a binary tree.
|
//! In this module, the term "ommer" is used as for the sibling of a parent node in a binary tree.
|
||||||
mod hashing;
|
|
||||||
mod position;
|
|
||||||
|
|
||||||
#[cfg(any(bench, test, feature = "test-dependencies"))]
|
|
||||||
pub mod testing;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -43,11 +37,7 @@ use std::fmt::Debug;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use crate::position::Source;
|
pub use incrementalmerkletree::{Address, Hashable, Level, Position};
|
||||||
pub use crate::{
|
|
||||||
hashing::Hashable,
|
|
||||||
position::{Address, Level, Position},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Validation errors that can occur during reconstruction of a Merkle frontier from
|
/// Validation errors that can occur during reconstruction of a Merkle frontier from
|
||||||
/// its constituent parts.
|
/// its constituent parts.
|
||||||
|
@ -84,6 +74,59 @@ pub enum WitnessingError {
|
||||||
BridgeAddressInvalid(Address),
|
BridgeAddressInvalid(Address),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
enum Source {
|
||||||
|
/// The sibling to the address can be derived from the incremental frontier
|
||||||
|
/// at the contained ommer index
|
||||||
|
Past(usize),
|
||||||
|
/// The sibling to the address must be obtained from values discovered by
|
||||||
|
/// the addition of more nodes to the tree
|
||||||
|
Future,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||||
|
struct WitnessAddrsIter {
|
||||||
|
root_level: Level,
|
||||||
|
current: Address,
|
||||||
|
ommer_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the addresses of nodes required to create a witness for this
|
||||||
|
/// position, beginning with the sibling of the leaf at this position and ending with the
|
||||||
|
/// sibling of the ancestor of the leaf at this position that is required to compute a root at
|
||||||
|
/// the specified level.
|
||||||
|
fn witness_addrs(position: Position, root_level: Level) -> impl Iterator<Item = (Address, Source)> {
|
||||||
|
WitnessAddrsIter {
|
||||||
|
root_level,
|
||||||
|
current: Address::from(position),
|
||||||
|
ommer_count: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for WitnessAddrsIter {
|
||||||
|
type Item = (Address, Source);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<(Address, Source)> {
|
||||||
|
if self.current.level() < self.root_level {
|
||||||
|
let current = self.current;
|
||||||
|
let source = if current.is_complete_node() {
|
||||||
|
Source::Past(self.ommer_count)
|
||||||
|
} else {
|
||||||
|
Source::Future
|
||||||
|
};
|
||||||
|
|
||||||
|
self.current = current.parent();
|
||||||
|
if matches!(source, Source::Past(_)) {
|
||||||
|
self.ommer_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some((current.sibling(), source))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A [`NonEmptyFrontier`] is a reduced representation of a Merkle tree, containing a single leaf
|
/// A [`NonEmptyFrontier`] is a reduced representation of a Merkle tree, containing a single leaf
|
||||||
/// value, along with the vector of hashes produced by the reduction of previously appended leaf
|
/// value, along with the vector of hashes produced by the reduction of previously appended leaf
|
||||||
/// values that will be required when producing a witness for the current leaf.
|
/// values that will be required when producing a witness for the current leaf.
|
||||||
|
@ -154,7 +197,7 @@ impl<H: Hashable + Clone> NonEmptyFrontier<H> {
|
||||||
|
|
||||||
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());
|
||||||
for (addr, source) in prior_position.witness_addrs(new_root_level) {
|
for (addr, source) in witness_addrs(prior_position, 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() {
|
||||||
|
@ -188,8 +231,7 @@ impl<H: Hashable + Clone> NonEmptyFrontier<H> {
|
||||||
/// Generate the root of the Merkle tree by hashing against empty subtree roots.
|
/// Generate the root of the Merkle tree by hashing against empty subtree roots.
|
||||||
pub fn root(&self, root_level: Option<Level>) -> H {
|
pub fn root(&self, root_level: Option<Level>) -> H {
|
||||||
let max_level = root_level.unwrap_or_else(|| self.position.root_level());
|
let max_level = root_level.unwrap_or_else(|| self.position.root_level());
|
||||||
self.position
|
witness_addrs(self.position, max_level)
|
||||||
.witness_addrs(max_level)
|
|
||||||
.fold(
|
.fold(
|
||||||
(self.leaf.clone(), Level::from(0)),
|
(self.leaf.clone(), Level::from(0)),
|
||||||
|(digest, complete_lvl), (addr, source)| {
|
|(digest, complete_lvl), (addr, source)| {
|
||||||
|
@ -220,8 +262,7 @@ impl<H: Hashable + Clone> NonEmptyFrontier<H> {
|
||||||
{
|
{
|
||||||
// construct a complete trailing edge that includes the data from
|
// construct a complete trailing edge that includes the data from
|
||||||
// the following frontier not yet included in the trailing edge.
|
// the following frontier not yet included in the trailing edge.
|
||||||
self.position()
|
witness_addrs(self.position(), 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[i].clone()),
|
||||||
Source::Future => {
|
Source::Future => {
|
||||||
|
@ -285,16 +326,16 @@ impl<H: Hashable + Clone, const DEPTH: u8> Frontier<H, DEPTH> {
|
||||||
/// Appends a new value to the frontier at the next available slot.
|
/// Appends a new value to the frontier at the next available slot.
|
||||||
/// Returns true if successful and false if the frontier would exceed
|
/// Returns true if successful and false if the frontier would exceed
|
||||||
/// the maximum allowed depth.
|
/// the maximum allowed depth.
|
||||||
pub fn append(&mut self, value: &H) -> bool {
|
pub fn append(&mut self, value: H) -> bool {
|
||||||
if let Some(frontier) = self.frontier.as_mut() {
|
if let Some(frontier) = self.frontier.as_mut() {
|
||||||
if frontier.position().is_complete_subtree(DEPTH.into()) {
|
if frontier.position().is_complete_subtree(DEPTH.into()) {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
frontier.append(value.clone());
|
frontier.append(value);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.frontier = Some(NonEmptyFrontier::new(value.clone()));
|
self.frontier = Some(NonEmptyFrontier::new(value));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -876,7 +917,7 @@ impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
|
||||||
/// Appends a new value to the tree at the next available slot.
|
/// Appends a new value to the tree at the next available slot.
|
||||||
/// Returns true if successful and false if the tree would exceed
|
/// Returns true if successful and false if the tree would exceed
|
||||||
/// the maximum allowed depth.
|
/// the maximum allowed depth.
|
||||||
pub fn append(&mut self, value: &H) -> bool {
|
pub fn append(&mut self, value: H) -> bool {
|
||||||
if let Some(bridge) = self.current_bridge.as_mut() {
|
if let Some(bridge) = self.current_bridge.as_mut() {
|
||||||
if bridge
|
if bridge
|
||||||
.frontier
|
.frontier
|
||||||
|
@ -885,11 +926,11 @@ impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
|
||||||
{
|
{
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
bridge.append(value.clone());
|
bridge.append(value);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.current_bridge = Some(MerkleBridge::new(value.clone()));
|
self.current_bridge = Some(MerkleBridge::new(value));
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1204,10 +1245,8 @@ impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
|
||||||
|
|
||||||
// Add the elements of the auth path to the set of addresses we should
|
// Add the elements of the auth path to the set of addresses we should
|
||||||
// continue to track and retain information for
|
// continue to track and retain information for
|
||||||
for (addr, source) in cur_bridge
|
for (addr, source) in
|
||||||
.frontier
|
witness_addrs(cur_bridge.frontier.position(), Level::from(DEPTH))
|
||||||
.position()
|
|
||||||
.witness_addrs(Level::from(DEPTH))
|
|
||||||
{
|
{
|
||||||
if source == Source::Future {
|
if source == Source::Future {
|
||||||
ommer_addrs.insert(addr);
|
ommer_addrs.insert(addr);
|
||||||
|
@ -1255,12 +1294,21 @@ impl<H: Hashable + Ord + Clone, const DEPTH: u8> BridgeTree<H, DEPTH> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::testing::{apply_operation, arb_operation, tests, Frontier, Tree};
|
use incrementalmerkletree::{
|
||||||
|
testing::{
|
||||||
|
apply_operation, arb_operation, check_checkpoint_rewind, check_operations,
|
||||||
|
check_rewind_remove_mark, check_rewind_remove_mark_consistency, check_root_hashes,
|
||||||
|
check_witnesses, complete_tree::CompleteTree, CombinedTree, Frontier, SipHashable,
|
||||||
|
Tree,
|
||||||
|
},
|
||||||
|
Hashable,
|
||||||
|
};
|
||||||
|
|
||||||
impl<H: Hashable + Clone, const DEPTH: u8> Frontier<H> for super::Frontier<H, DEPTH> {
|
impl<H: Hashable + Clone, const DEPTH: u8> Frontier<H> for super::Frontier<H, DEPTH> {
|
||||||
fn append(&mut self, value: &H) -> bool {
|
fn append(&mut self, value: H) -> bool {
|
||||||
super::Frontier::append(self, value)
|
super::Frontier::append(self, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1270,7 +1318,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: Hashable + Ord + Clone, const DEPTH: u8> Tree<H> for BridgeTree<H, DEPTH> {
|
impl<H: Hashable + Ord + Clone, const DEPTH: u8> Tree<H> for BridgeTree<H, DEPTH> {
|
||||||
fn append(&mut self, value: &H) -> bool {
|
fn append(&mut self, value: H) -> bool {
|
||||||
BridgeTree::append(self, value)
|
BridgeTree::append(self, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1315,6 +1363,46 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn position_witness_addrs() {
|
||||||
|
use Source::*;
|
||||||
|
let path_elem = |l, i, s| (Address::from_parts(Level::from(l), i), s);
|
||||||
|
assert_eq!(
|
||||||
|
vec![path_elem(0, 1, Future), path_elem(1, 1, Future)],
|
||||||
|
witness_addrs(Position::from(0), Level::from(2)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
vec![path_elem(0, 3, Future), path_elem(1, 0, Past(0))],
|
||||||
|
witness_addrs(Position::from(2), Level::from(2)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
path_elem(0, 2, Past(0)),
|
||||||
|
path_elem(1, 0, Past(1)),
|
||||||
|
path_elem(2, 1, Future)
|
||||||
|
],
|
||||||
|
witness_addrs(Position::from(3), Level::from(3)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
path_elem(0, 5, Future),
|
||||||
|
path_elem(1, 3, Future),
|
||||||
|
path_elem(2, 0, Past(0)),
|
||||||
|
path_elem(3, 1, Future)
|
||||||
|
],
|
||||||
|
witness_addrs(Position::from(4), Level::from(4)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
vec![
|
||||||
|
path_elem(0, 7, Future),
|
||||||
|
path_elem(1, 2, Past(0)),
|
||||||
|
path_elem(2, 0, Past(1)),
|
||||||
|
path_elem(3, 1, Future)
|
||||||
|
],
|
||||||
|
witness_addrs(Position::from(6), Level::from(4)).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nonempty_frontier_root() {
|
fn nonempty_frontier_root() {
|
||||||
let mut frontier = NonEmptyFrontier::new("a".to_string());
|
let mut frontier = NonEmptyFrontier::new("a".to_string());
|
||||||
|
@ -1340,13 +1428,13 @@ mod tests {
|
||||||
assert_eq!(frontier.root().len(), 16);
|
assert_eq!(frontier.root().len(), 16);
|
||||||
assert_eq!(frontier.root(), "________________");
|
assert_eq!(frontier.root(), "________________");
|
||||||
|
|
||||||
frontier.append(&"a".to_string());
|
frontier.append("a".to_string());
|
||||||
assert_eq!(frontier.root(), "a_______________");
|
assert_eq!(frontier.root(), "a_______________");
|
||||||
|
|
||||||
frontier.append(&"b".to_string());
|
frontier.append("b".to_string());
|
||||||
assert_eq!(frontier.root(), "ab______________");
|
assert_eq!(frontier.root(), "ab______________");
|
||||||
|
|
||||||
frontier.append(&"c".to_string());
|
frontier.append("c".to_string());
|
||||||
assert_eq!(frontier.root(), "abc_____________");
|
assert_eq!(frontier.root(), "abc_____________");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1374,9 +1462,9 @@ mod tests {
|
||||||
fn tree_depth() {
|
fn tree_depth() {
|
||||||
let mut tree = BridgeTree::<String, 3>::new(100);
|
let mut tree = BridgeTree::<String, 3>::new(100);
|
||||||
for c in 'a'..'i' {
|
for c in 'a'..'i' {
|
||||||
assert!(tree.append(&c.to_string()))
|
assert!(tree.append(c.to_string()))
|
||||||
}
|
}
|
||||||
assert!(!tree.append(&'i'.to_string()));
|
assert!(!tree.append('i'.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arb_bridgetree<G: Strategy + Clone>(
|
fn arb_bridgetree<G: Strategy + Clone>(
|
||||||
|
@ -1441,10 +1529,10 @@ mod tests {
|
||||||
fn drop_oldest_checkpoint() {
|
fn drop_oldest_checkpoint() {
|
||||||
let mut t = BridgeTree::<String, 6>::new(100);
|
let mut t = BridgeTree::<String, 6>::new(100);
|
||||||
t.checkpoint();
|
t.checkpoint();
|
||||||
t.append(&"a".to_string());
|
t.append("a".to_string());
|
||||||
t.mark();
|
t.mark();
|
||||||
t.append(&"b".to_string());
|
t.append("b".to_string());
|
||||||
t.append(&"c".to_string());
|
t.append("c".to_string());
|
||||||
assert!(
|
assert!(
|
||||||
t.drop_oldest_checkpoint(),
|
t.drop_oldest_checkpoint(),
|
||||||
"Checkpoint drop is expected to succeed"
|
"Checkpoint drop is expected to succeed"
|
||||||
|
@ -1454,22 +1542,22 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn root_hashes() {
|
fn root_hashes() {
|
||||||
tests::check_root_hashes(BridgeTree::<String, 4>::new);
|
check_root_hashes(BridgeTree::<String, 4>::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn witnesss() {
|
fn witnesss() {
|
||||||
tests::check_witnesss(BridgeTree::<String, 4>::new);
|
check_witnesses(BridgeTree::<String, 4>::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checkpoint_rewind() {
|
fn checkpoint_rewind() {
|
||||||
tests::check_checkpoint_rewind(BridgeTree::<String, 4>::new);
|
check_checkpoint_rewind(BridgeTree::<String, 4>::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rewind_remove_mark() {
|
fn rewind_remove_mark() {
|
||||||
tests::check_rewind_remove_mark(BridgeTree::<String, 4>::new);
|
check_rewind_remove_mark(BridgeTree::<String, 4>::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1479,7 +1567,7 @@ mod tests {
|
||||||
let mut has_witness = vec![];
|
let mut has_witness = vec![];
|
||||||
for i in 0usize..100 {
|
for i in 0usize..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();
|
t.checkpoint();
|
||||||
}
|
}
|
||||||
|
@ -1522,7 +1610,7 @@ mod tests {
|
||||||
fn garbage_collect_idx() {
|
fn garbage_collect_idx() {
|
||||||
let mut tree: BridgeTree<String, 7> = BridgeTree::new(100);
|
let mut tree: BridgeTree<String, 7> = BridgeTree::new(100);
|
||||||
let empty_root = tree.root(0);
|
let empty_root = tree.root(0);
|
||||||
tree.append(&"a".to_string());
|
tree.append("a".to_string());
|
||||||
for _ in 0..100 {
|
for _ in 0..100 {
|
||||||
tree.checkpoint();
|
tree.checkpoint();
|
||||||
}
|
}
|
||||||
|
@ -1531,4 +1619,50 @@ mod tests {
|
||||||
tree.rewind();
|
tree.rewind();
|
||||||
assert!(tree.root(0) != empty_root);
|
assert!(tree.root(0) != empty_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Combined tree tests
|
||||||
|
fn new_combined_tree<H: Hashable + Ord + Clone + Debug>(
|
||||||
|
max_checkpoints: usize,
|
||||||
|
) -> CombinedTree<H, CompleteTree<H>, BridgeTree<H, 4>> {
|
||||||
|
CombinedTree::new(
|
||||||
|
CompleteTree::new(4, max_checkpoints),
|
||||||
|
BridgeTree::<H, 4>::new(max_checkpoints),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rewind_remove_mark() {
|
||||||
|
check_rewind_remove_mark(new_combined_tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rewind_remove_mark_consistency() {
|
||||||
|
check_rewind_remove_mark_consistency(new_combined_tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
#![proptest_config(ProptestConfig::with_cases(100000))]
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_randomized_u64_ops(
|
||||||
|
ops in proptest::collection::vec(
|
||||||
|
arb_operation((0..32u64).prop_map(SipHashable), 0usize..100),
|
||||||
|
1..100
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
let tree = new_combined_tree(100);
|
||||||
|
check_operations(tree, 4, &ops)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_randomized_str_ops(
|
||||||
|
ops in proptest::collection::vec(
|
||||||
|
arb_operation((97u8..123).prop_map(|c| char::from(c).to_string()), 0usize..100),
|
||||||
|
1..100
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
let tree = new_combined_tree(100);
|
||||||
|
check_operations(tree, 4, &ops)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -7,9 +7,46 @@ and this project adheres to Rust's notion of
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `Position::is_odd`
|
||||||
|
- `Position::ommer_index`
|
||||||
|
- `Position::root_level`
|
||||||
|
- `Position::past_ommer_count`
|
||||||
|
- `Address` A type used to uniquely identify node locations within a binary tree.
|
||||||
|
|
||||||
|
A `test-dependencies` feature has been added. This makes available a `testing`
|
||||||
|
module to users of this crate, which contains `proptest` generators for types
|
||||||
|
from this crate as well as a number of tools for comparison testing between
|
||||||
|
`Tree` implementations and a number of generalized example tests. The
|
||||||
|
`Frontier` and `Tree` traits have been moved to the `testing` module, as there
|
||||||
|
is not another good use case for polymorphism over tree implementations.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `Altitude` has been renamed to `Level`
|
||||||
|
- `Position::is_complete` has been renamed to `Position::is_complete_subtree`.
|
||||||
|
- `witness` is now used as the name of the operation to construct the witness for a leaf.
|
||||||
|
We now use `mark` to refer to the process of marking a node for which we may later wish
|
||||||
|
to construct a witness.
|
||||||
|
- `Tree::witness` has been renamed to `Tree::mark`
|
||||||
|
- `Tree::witnessed_positions` has been renamed to `Tree::marked_positions`
|
||||||
|
- `Tree::get_witnessed_leaf` has been renamed to `Tree::get_marked_leaf`
|
||||||
|
- `Tree::remove_witness` has been renamed to `Tree::remove_mark`
|
||||||
|
- `Tree::authentication_path` has been renamed to `Tree::witness`
|
||||||
|
- `Tree::append` now takes ownership of the value being appended instead of a value passed
|
||||||
|
by reference.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- The `bridgetree` module has been moved out a to a separate `bridgetree` crate.
|
- The `bridgetree` module has been moved out a to a separate `bridgetree` crate.
|
||||||
|
- `Position::witness_addrs`
|
||||||
|
- `Position::altitudes_required`
|
||||||
|
- `Position::all_altitudes_required`
|
||||||
|
- `Position::auth_path`
|
||||||
|
- `Position::max_altitude`
|
||||||
|
- `Position::ommer_altitudes`
|
||||||
|
- `impl Sub<u8> for Altitude`
|
||||||
|
|
||||||
## [0.3.0] - 2022-05-10
|
## [0.3.0] - 2022-05-10
|
||||||
|
|
||||||
|
@ -27,7 +64,7 @@ and this project adheres to Rust's notion of
|
||||||
addition to the previous behavior which only allowed computation of the path as of the
|
addition to the previous behavior which only allowed computation of the path as of the
|
||||||
most recent tree state. The provided `as_of_root` value must be equal to either the
|
most recent tree state. The provided `as_of_root` value must be equal to either the
|
||||||
current root of the tree, or to the root of the tree at a previous checkpoint that
|
current root of the tree, or to the root of the tree at a previous checkpoint that
|
||||||
contained a note at the given position.
|
contained a note at the given position.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -43,7 +80,7 @@ and this project adheres to Rust's notion of
|
||||||
witnessed leaves by their position in the tree.
|
witnessed leaves by their position in the tree.
|
||||||
- `Tree::witnessed_positions`, to allow a user to query for all positions that
|
- `Tree::witnessed_positions`, to allow a user to query for all positions that
|
||||||
have been witnessed.
|
have been witnessed.
|
||||||
- `Tree::garbage_collect`, to prune checkpoint and removed witness information
|
- `Tree::garbage_collect`, to prune checkpoint and removed witness information
|
||||||
that is no longer reachable by rewinds of the tree.
|
that is no longer reachable by rewinds of the tree.
|
||||||
- `incrementalmerkletree::bridgetree`:
|
- `incrementalmerkletree::bridgetree`:
|
||||||
- `NonEmptyFrontier::current_leaf`
|
- `NonEmptyFrontier::current_leaf`
|
||||||
|
@ -61,11 +98,11 @@ and this project adheres to Rust's notion of
|
||||||
- `Tree::current_leaf` and `Tree::witness` have both been changed to only return the leaf
|
- `Tree::current_leaf` and `Tree::witness` have both been changed to only return the leaf
|
||||||
value, instead of both the leaf value and the position.
|
value, instead of both the leaf value and the position.
|
||||||
- `incrementalmerkletree::bridgetree`:
|
- `incrementalmerkletree::bridgetree`:
|
||||||
- The type of `BridgeTree::saved` and `Checkpoint::forgotten` have been changed from
|
- The type of `BridgeTree::saved` and `Checkpoint::forgotten` have been changed from
|
||||||
`BTreeMap<(Position, H), usize>` to `BTreeMap<Position, usize>`. This change
|
`BTreeMap<(Position, H), usize>` to `BTreeMap<Position, usize>`. This change
|
||||||
is also reflected in the rturn type of the `BridgeTree::witnessed_indices` method.
|
is also reflected in the rturn type of the `BridgeTree::witnessed_indices` method.
|
||||||
- The `Checkpoint` type is no longer parameterized by `H`.
|
- The `Checkpoint` type is no longer parameterized by `H`.
|
||||||
- `BridgeTree::bridges` has been split into two parts:
|
- `BridgeTree::bridges` has been split into two parts:
|
||||||
- `BridgeTree::prior_bridges` now tracks past bridges not including the current frontier.
|
- `BridgeTree::prior_bridges` now tracks past bridges not including the current frontier.
|
||||||
- `BridgeTree::current_bridge` now tracks current mutable frontier.
|
- `BridgeTree::current_bridge` now tracks current mutable frontier.
|
||||||
- The signature of `BridgeTree::from_parts` has been modified to reflect these changes.
|
- The signature of `BridgeTree::from_parts` has been modified to reflect these changes.
|
||||||
|
@ -83,8 +120,8 @@ and this project adheres to Rust's notion of
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- A bug in `BridgeTree::garbage_collect` that caused garbage collection to in some
|
- A bug in `BridgeTree::garbage_collect` that caused garbage collection to in some
|
||||||
cases incorrectly rewrite checkpointed bridge lengths, resulting in a condition
|
cases incorrectly rewrite checkpointed bridge lengths, resulting in a condition
|
||||||
where a rewind could panic after a GC operation.
|
where a rewind could panic after a GC operation.
|
||||||
|
|
||||||
## [0.3.0-beta.1] - 2022-03-22
|
## [0.3.0-beta.1] - 2022-03-22
|
||||||
|
@ -133,7 +170,7 @@ and this project adheres to Rust's notion of
|
||||||
or `None` if the tree is empty, instead of a boolean value.
|
or `None` if the tree is empty, instead of a boolean value.
|
||||||
- `incrementalmerkletree::bridgetree`:
|
- `incrementalmerkletree::bridgetree`:
|
||||||
- Most `MerkleBridge` methods now require `H: Ord`.
|
- Most `MerkleBridge` methods now require `H: Ord`.
|
||||||
- Changed the return type of `MerkleBridge::auth_fragments` from
|
- Changed the return type of `MerkleBridge::auth_fragments` from
|
||||||
`HashMap<usize, AuthFragment>` to `BTreeMap<Position, AuthFragment>`; the
|
`HashMap<usize, AuthFragment>` to `BTreeMap<Position, AuthFragment>`; the
|
||||||
`saved` argument to `BridgeTree::from_parts` is similarly altered.
|
`saved` argument to `BridgeTree::from_parts` is similarly altered.
|
||||||
- `MerkleBridge::successor` now takes a boolean argument for tracking the most
|
- `MerkleBridge::successor` now takes a boolean argument for tracking the most
|
||||||
|
@ -157,7 +194,7 @@ and this project adheres to Rust's notion of
|
||||||
## [0.2.0] - 2022-03-22
|
## [0.2.0] - 2022-03-22
|
||||||
|
|
||||||
v0.2.0 is essentially a complete rewrite relative to v0.1.0, and should be considered
|
v0.2.0 is essentially a complete rewrite relative to v0.1.0, and should be considered
|
||||||
the first usable release.
|
the first usable release.
|
||||||
|
|
||||||
## [0.1.0] - 2021-06-23
|
## [0.1.0] - 2021-06-23
|
||||||
Initial release!
|
Initial release!
|
|
@ -0,0 +1,24 @@
|
||||||
|
[package]
|
||||||
|
name = "incrementalmerkletree"
|
||||||
|
version = "0.3.0"
|
||||||
|
authors = [
|
||||||
|
"Sean Bowe <ewillbefull@gmail.com>",
|
||||||
|
"Kris Nuttycombe <kris@nutty.land>",
|
||||||
|
]
|
||||||
|
edition = "2018"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
description = "Common types, interfaces, and utilities for Merkle tree data structures"
|
||||||
|
homepage = "https://github.com/zcash/incrementalmerkletree"
|
||||||
|
repository = "https://github.com/zcash/incrementalmerkletree"
|
||||||
|
categories = ["algorithms", "data-structures"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
either = "1.8"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
proptest = { version = "1.0.0", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
proptest = "1.0.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test-dependencies = ["proptest"]
|
|
@ -0,0 +1,202 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2021 The Electric Coin Company
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
|
@ -0,0 +1,6 @@
|
||||||
|
incrementalmerkletree
|
||||||
|
=====================
|
||||||
|
|
||||||
|
This crate contains common utility types and testing infrastructure used in the implementation
|
||||||
|
of the `bridgetree` and `shardtree` crates.
|
||||||
|
|
|
@ -1,10 +1,84 @@
|
||||||
//! Types that describe positions within a Merkle tree
|
//! Common types and utilities used in incremental Merkle tree implementations.
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::num::TryFromIntError;
|
use std::num::TryFromIntError;
|
||||||
use std::ops::{Add, AddAssign, Range};
|
use std::ops::{Add, AddAssign, Range};
|
||||||
|
|
||||||
|
#[cfg(feature = "test-dependencies")]
|
||||||
|
pub mod testing;
|
||||||
|
|
||||||
|
/// A type representing the position of a leaf in a Merkle tree.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Position(usize);
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
/// Return whether the position is odd-valued.
|
||||||
|
pub fn is_odd(&self) -> bool {
|
||||||
|
self.0 & 0x1 == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the minimum possible level of the root of a binary tree containing at least
|
||||||
|
/// `self + 1` nodes.
|
||||||
|
pub fn root_level(&self) -> Level {
|
||||||
|
Level(64 - self.0.leading_zeros() as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub fn past_ommer_count(&self) -> usize {
|
||||||
|
(0..self.root_level().0)
|
||||||
|
.filter(|i| (self.0 >> i) & 0x1 == 1)
|
||||||
|
.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the binary tree having `self` as the position of the rightmost leaf
|
||||||
|
/// contains a perfect balanced tree with a root at level `root_level` that contains the
|
||||||
|
/// aforesaid leaf.
|
||||||
|
pub fn is_complete_subtree(&self, root_level: Level) -> bool {
|
||||||
|
!(0..(root_level.0)).any(|l| self.0 & (1 << l) == 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Position> for usize {
|
||||||
|
fn from(p: Position) -> usize {
|
||||||
|
p.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Position> for u64 {
|
||||||
|
fn from(p: Position) -> Self {
|
||||||
|
p.0 as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<usize> for Position {
|
||||||
|
type Output = Position;
|
||||||
|
fn add(self, other: usize) -> Self {
|
||||||
|
Position(self.0 + other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddAssign<usize> for Position {
|
||||||
|
fn add_assign(&mut self, other: usize) {
|
||||||
|
self.0 += other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for Position {
|
||||||
|
fn from(sz: usize) -> Self {
|
||||||
|
Self(sz)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u64> for Position {
|
||||||
|
type Error = TryFromIntError;
|
||||||
|
fn try_from(sz: u64) -> Result<Self, Self::Error> {
|
||||||
|
<usize>::try_from(sz).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A type-safe wrapper for indexing into "levels" of a binary tree, such that
|
/// A type-safe wrapper for indexing into "levels" of a binary tree, such that
|
||||||
/// nodes at level `0` are leaves, nodes at level `1` are parents of nodes at
|
/// nodes at level `0` are leaves, nodes at level `1` are parents of nodes at
|
||||||
/// level `0`, and so forth. This type is capable of representing levels in
|
/// level `0`, and so forth. This type is capable of representing levels in
|
||||||
|
@ -46,92 +120,6 @@ impl From<Level> for usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type representing the position of a leaf in a Merkle tree.
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct Position(usize);
|
|
||||||
|
|
||||||
impl Position {
|
|
||||||
/// Return whether the position is odd-valued.
|
|
||||||
pub fn is_odd(&self) -> bool {
|
|
||||||
self.0 & 0x1 == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the minimum possible level of the root of a binary tree containing at least
|
|
||||||
/// `self + 1` nodes.
|
|
||||||
pub fn root_level(&self) -> Level {
|
|
||||||
Level(64 - self.0.leading_zeros() as u8)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
pub fn past_ommer_count(&self) -> usize {
|
|
||||||
(0..self.root_level().0)
|
|
||||||
.filter(|i| (self.0 >> i) & 0x1 == 1)
|
|
||||||
.count()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the binary tree having `self` as the position of the rightmost leaf
|
|
||||||
/// contains a perfect balanced tree with a root at level `root_level` that contains the
|
|
||||||
/// aforesaid leaf.
|
|
||||||
pub fn is_complete_subtree(&self, root_level: Level) -> bool {
|
|
||||||
!(0..(root_level.0)).any(|l| self.0 & (1 << l) == 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over the addresses of nodes required to create a witness for this
|
|
||||||
/// position, beginning with the sibling of the leaf at this position and ending with the
|
|
||||||
/// sibling of the ancestor of the leaf at this position that is required to compute a root at
|
|
||||||
/// the specified level.
|
|
||||||
pub(crate) fn witness_addrs(
|
|
||||||
&self,
|
|
||||||
root_level: Level,
|
|
||||||
) -> impl Iterator<Item = (Address, Source)> {
|
|
||||||
WitnessAddrsIter {
|
|
||||||
root_level,
|
|
||||||
current: Address::from(self),
|
|
||||||
ommer_count: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Position> for usize {
|
|
||||||
fn from(p: Position) -> usize {
|
|
||||||
p.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Position> for u64 {
|
|
||||||
fn from(p: Position) -> Self {
|
|
||||||
p.0 as u64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add<usize> for Position {
|
|
||||||
type Output = Position;
|
|
||||||
fn add(self, other: usize) -> Self {
|
|
||||||
Position(self.0 + other)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AddAssign<usize> for Position {
|
|
||||||
fn add_assign(&mut self, other: usize) {
|
|
||||||
self.0 += other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<usize> for Position {
|
|
||||||
fn from(sz: usize) -> Self {
|
|
||||||
Self(sz)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<u64> for Position {
|
|
||||||
type Error = TryFromIntError;
|
|
||||||
fn try_from(sz: u64) -> Result<Self, Self::Error> {
|
|
||||||
<usize>::try_from(sz).map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The address of an internal node of the Merkle tree.
|
/// The address of an internal node of the Merkle tree.
|
||||||
/// When `level == 0`, the index has the same value as the
|
/// When `level == 0`, the index has the same value as the
|
||||||
/// position.
|
/// position.
|
||||||
|
@ -141,16 +129,6 @@ pub struct Address {
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub(crate) enum Source {
|
|
||||||
/// The sibling to the address can be derived from the incremental frontier
|
|
||||||
/// at the contained ommer index
|
|
||||||
Past(usize),
|
|
||||||
/// The sibling to the address must be obtained from values discovered by
|
|
||||||
/// the addition of more nodes to the tree
|
|
||||||
Future,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Address {
|
impl Address {
|
||||||
pub fn from_parts(level: Level, index: usize) -> Self {
|
pub fn from_parts(level: Level, index: usize) -> Self {
|
||||||
Address { level, index }
|
Address { level, index }
|
||||||
|
@ -261,40 +239,23 @@ impl<'a> From<&'a Address> for Option<Position> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
/// A trait describing the operations that make a type suitable for use as
|
||||||
pub(crate) struct WitnessAddrsIter {
|
/// a leaf or node value in a merkle tree.
|
||||||
root_level: Level,
|
pub trait Hashable: Sized {
|
||||||
current: Address,
|
fn empty_leaf() -> Self;
|
||||||
ommer_count: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for WitnessAddrsIter {
|
fn combine(level: Level, a: &Self, b: &Self) -> Self;
|
||||||
type Item = (Address, Source);
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<(Address, Source)> {
|
fn empty_root(level: Level) -> Self {
|
||||||
if self.current.level() < self.root_level {
|
Level::from(0)
|
||||||
let current = self.current;
|
.iter_to(level)
|
||||||
let source = if current.is_complete_node() {
|
.fold(Self::empty_leaf(), |v, lvl| Self::combine(lvl, &v, &v))
|
||||||
Source::Past(self.ommer_count)
|
|
||||||
} else {
|
|
||||||
Source::Future
|
|
||||||
};
|
|
||||||
|
|
||||||
self.current = current.parent();
|
|
||||||
if matches!(source, Source::Past(_)) {
|
|
||||||
self.ommer_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((current.sibling(), source))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use super::{Address, Level, Position, Source};
|
use super::{Address, Level, Position};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn position_is_complete_subtree() {
|
fn position_is_complete_subtree() {
|
||||||
|
@ -350,44 +311,4 @@ pub(crate) mod tests {
|
||||||
assert_eq!(addr(1, 2), addr(0, 4).next_incomplete_parent());
|
assert_eq!(addr(1, 2), addr(0, 4).next_incomplete_parent());
|
||||||
assert_eq!(addr(3, 0), addr(1, 2).next_incomplete_parent());
|
assert_eq!(addr(3, 0), addr(1, 2).next_incomplete_parent());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn position_witness_addrs() {
|
|
||||||
use Source::*;
|
|
||||||
let path_elem = |l, i, s| (Address::from_parts(Level(l), i), s);
|
|
||||||
assert_eq!(
|
|
||||||
vec![path_elem(0, 1, Future), path_elem(1, 1, Future)],
|
|
||||||
Position(0).witness_addrs(Level(2)).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec![path_elem(0, 3, Future), path_elem(1, 0, Past(0))],
|
|
||||||
Position(2).witness_addrs(Level(2)).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec![
|
|
||||||
path_elem(0, 2, Past(0)),
|
|
||||||
path_elem(1, 0, Past(1)),
|
|
||||||
path_elem(2, 1, Future)
|
|
||||||
],
|
|
||||||
Position(3).witness_addrs(Level(3)).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec![
|
|
||||||
path_elem(0, 5, Future),
|
|
||||||
path_elem(1, 3, Future),
|
|
||||||
path_elem(2, 0, Past(0)),
|
|
||||||
path_elem(3, 1, Future)
|
|
||||||
],
|
|
||||||
Position(4).witness_addrs(Level(4)).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
vec![
|
|
||||||
path_elem(0, 7, Future),
|
|
||||||
path_elem(1, 2, Past(0)),
|
|
||||||
path_elem(2, 0, Past(1)),
|
|
||||||
path_elem(3, 1, Future)
|
|
||||||
],
|
|
||||||
Position(6).witness_addrs(Level(4)).collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,37 @@
|
||||||
//! Sample implementation of the Tree interface.
|
//! Sample implementation of the Tree interface.
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use super::{Frontier, Tree};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
hashing::Hashable,
|
testing::{Frontier, Tree},
|
||||||
position::{Level, Position},
|
Hashable, Level, Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub(crate) fn root<H: Hashable + Clone>(mut leaves: Vec<H>) -> H {
|
||||||
|
leaves.resize(leaves.len().next_power_of_two(), H::empty_leaf());
|
||||||
|
|
||||||
|
//leaves are always at level zero, so we start there.
|
||||||
|
let mut level = Level::from(0);
|
||||||
|
while leaves.len() != 1 {
|
||||||
|
leaves = leaves
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(i, _)| (i % 2) == 0)
|
||||||
|
.map(|(_, a)| a)
|
||||||
|
.zip(
|
||||||
|
leaves
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(i, _)| (i % 2) == 1)
|
||||||
|
.map(|(_, b)| b),
|
||||||
|
)
|
||||||
|
.map(|(a, b)| H::combine(level, a, b))
|
||||||
|
.collect();
|
||||||
|
level = level + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
leaves[0].clone()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TreeState<H: Hashable> {
|
pub struct TreeState<H: Hashable> {
|
||||||
leaves: Vec<H>,
|
leaves: Vec<H>,
|
||||||
|
@ -17,7 +42,6 @@ pub struct TreeState<H: Hashable> {
|
||||||
|
|
||||||
impl<H: Hashable + Clone> TreeState<H> {
|
impl<H: Hashable + Clone> TreeState<H> {
|
||||||
/// Creates a new, empty binary tree of specified depth.
|
/// Creates a new, empty binary tree of specified depth.
|
||||||
#[cfg(test)]
|
|
||||||
pub fn new(depth: usize) -> Self {
|
pub fn new(depth: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
leaves: vec![H::empty_leaf(); 1 << depth],
|
leaves: vec![H::empty_leaf(); 1 << depth],
|
||||||
|
@ -29,11 +53,11 @@ impl<H: Hashable + Clone> TreeState<H> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: Hashable + Clone> Frontier<H> for TreeState<H> {
|
impl<H: Hashable + Clone> Frontier<H> for TreeState<H> {
|
||||||
fn append(&mut self, value: &H) -> bool {
|
fn append(&mut self, value: H) -> bool {
|
||||||
if self.current_offset == (1 << self.depth) {
|
if self.current_offset == (1 << self.depth) {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
self.leaves[self.current_offset] = value.clone();
|
self.leaves[self.current_offset] = value;
|
||||||
self.current_offset += 1;
|
self.current_offset += 1;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -41,7 +65,7 @@ impl<H: Hashable + Clone> Frontier<H> for TreeState<H> {
|
||||||
|
|
||||||
/// Obtains the current root of this Merkle tree.
|
/// Obtains the current root of this Merkle tree.
|
||||||
fn root(&self) -> H {
|
fn root(&self) -> H {
|
||||||
lazy_root(self.leaves.clone())
|
root(self.leaves.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,9 +113,7 @@ impl<H: Hashable + PartialEq + Clone> TreeState<H> {
|
||||||
let mut leaf_idx: usize = position.into();
|
let mut leaf_idx: usize = position.into();
|
||||||
for bit in 0..self.depth {
|
for bit in 0..self.depth {
|
||||||
leaf_idx ^= 1 << bit;
|
leaf_idx ^= 1 << bit;
|
||||||
path.push(lazy_root::<H>(
|
path.push(root::<H>(self.leaves[leaf_idx..][0..(1 << bit)].to_vec()));
|
||||||
self.leaves[leaf_idx..][0..(1 << bit)].to_vec(),
|
|
||||||
));
|
|
||||||
leaf_idx &= usize::MAX << (bit + 1);
|
leaf_idx &= usize::MAX << (bit + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +140,6 @@ pub struct CompleteTree<H: Hashable> {
|
||||||
|
|
||||||
impl<H: Hashable + Clone> CompleteTree<H> {
|
impl<H: Hashable + Clone> CompleteTree<H> {
|
||||||
/// Creates a new, empty binary tree of specified depth.
|
/// Creates a new, empty binary tree of specified depth.
|
||||||
#[cfg(test)]
|
|
||||||
pub fn new(depth: usize, max_checkpoints: usize) -> Self {
|
pub fn new(depth: usize, max_checkpoints: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tree_state: TreeState::new(depth),
|
tree_state: TreeState::new(depth),
|
||||||
|
@ -158,7 +179,7 @@ impl<H: Hashable + PartialEq + Clone> CompleteTree<H> {
|
||||||
impl<H: Hashable + PartialEq + Clone + std::fmt::Debug> Tree<H> for CompleteTree<H> {
|
impl<H: Hashable + PartialEq + Clone + std::fmt::Debug> Tree<H> for CompleteTree<H> {
|
||||||
/// Appends a new value to the tree at the next available slot. Returns true
|
/// Appends a new value to the tree at the next available slot. Returns true
|
||||||
/// if successful and false if the tree is full.
|
/// if successful and false if the tree is full.
|
||||||
fn append(&mut self, value: &H) -> bool {
|
fn append(&mut self, value: H) -> bool {
|
||||||
self.tree_state.append(value)
|
self.tree_state.append(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,42 +247,17 @@ impl<H: Hashable + PartialEq + Clone + std::fmt::Debug> Tree<H> for CompleteTree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lazy_root<H: Hashable + Clone>(mut leaves: Vec<H>) -> H {
|
|
||||||
//leaves are always at level zero, so we start there.
|
|
||||||
let mut level = Level::from(0);
|
|
||||||
while leaves.len() != 1 {
|
|
||||||
leaves = leaves
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter(|(i, _)| (i % 2) == 0)
|
|
||||||
.map(|(_, a)| a)
|
|
||||||
.zip(
|
|
||||||
leaves
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter(|(i, _)| (i % 2) == 1)
|
|
||||||
.map(|(_, b)| b),
|
|
||||||
)
|
|
||||||
.map(|(a, b)| H::combine(level, a, b))
|
|
||||||
.collect();
|
|
||||||
level = level + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
leaves[0].clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use super::CompleteTree;
|
use super::CompleteTree;
|
||||||
use crate::{
|
use crate::{
|
||||||
hashing::Hashable,
|
|
||||||
position::{Level, Position},
|
|
||||||
testing::{
|
testing::{
|
||||||
tests::{self, compute_root_from_witness},
|
check_checkpoint_rewind, check_rewind_remove_mark, check_root_hashes, check_witnesses,
|
||||||
SipHashable, Tree,
|
compute_root_from_witness, SipHashable, Tree,
|
||||||
},
|
},
|
||||||
|
Hashable, Level, Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -283,9 +279,9 @@ mod tests {
|
||||||
|
|
||||||
let mut tree = CompleteTree::<SipHashable>::new(DEPTH, 100);
|
let mut tree = CompleteTree::<SipHashable>::new(DEPTH, 100);
|
||||||
for value in values {
|
for value in values {
|
||||||
assert!(tree.append(&value));
|
assert!(tree.append(value));
|
||||||
}
|
}
|
||||||
assert!(!tree.append(&SipHashable(0)));
|
assert!(!tree.append(SipHashable(0)));
|
||||||
|
|
||||||
let expected = SipHashable::combine(
|
let expected = SipHashable::combine(
|
||||||
Level::from(2),
|
Level::from(2),
|
||||||
|
@ -306,12 +302,12 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn root_hashes() {
|
fn root_hashes() {
|
||||||
tests::check_root_hashes(|max_c| CompleteTree::<String>::new(4, max_c));
|
check_root_hashes(|max_c| CompleteTree::<String>::new(4, max_c));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn witnesss() {
|
fn witnesss() {
|
||||||
tests::check_witnesss(|max_c| CompleteTree::<String>::new(4, max_c));
|
check_witnesses(|max_c| CompleteTree::<String>::new(4, max_c));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -321,10 +317,10 @@ mod tests {
|
||||||
|
|
||||||
let mut tree = CompleteTree::<SipHashable>::new(DEPTH, 100);
|
let mut tree = CompleteTree::<SipHashable>::new(DEPTH, 100);
|
||||||
for value in values {
|
for value in values {
|
||||||
assert!(tree.append(&value));
|
assert!(tree.append(value));
|
||||||
tree.mark();
|
tree.mark();
|
||||||
}
|
}
|
||||||
assert!(!tree.append(&SipHashable(0)));
|
assert!(!tree.append(SipHashable(0)));
|
||||||
|
|
||||||
let expected = SipHashable::combine(
|
let expected = SipHashable::combine(
|
||||||
<Level>::from(2),
|
<Level>::from(2),
|
||||||
|
@ -354,11 +350,11 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checkpoint_rewind() {
|
fn checkpoint_rewind() {
|
||||||
tests::check_checkpoint_rewind(|max_c| CompleteTree::<String>::new(4, max_c));
|
check_checkpoint_rewind(|max_c| CompleteTree::<String>::new(4, max_c));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rewind_remove_mark() {
|
fn rewind_remove_mark() {
|
||||||
tests::check_rewind_remove_mark(|max_c| CompleteTree::<String>::new(4, max_c));
|
check_rewind_remove_mark(|max_c| CompleteTree::<String>::new(4, max_c));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
use crate::position::Level;
|
|
||||||
|
|
||||||
/// A trait describing the operations that make a type suitable for use as
|
|
||||||
/// a leaf or node value in a merkle tree.
|
|
||||||
pub trait Hashable: Sized {
|
|
||||||
fn empty_leaf() -> Self;
|
|
||||||
|
|
||||||
fn combine(level: Level, a: &Self, b: &Self) -> Self;
|
|
||||||
|
|
||||||
fn empty_root(level: Level) -> Self {
|
|
||||||
Level::from(0)
|
|
||||||
.iter_to(level)
|
|
||||||
.fold(Self::empty_leaf(), |v, lvl| Self::combine(lvl, &v, &v))
|
|
||||||
}
|
|
||||||
}
|
|
1099
src/testing.rs
1099
src/testing.rs
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue