Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clippy.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
avoid-breaking-exported-api = false
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added some new clippy lints to enforce.

### Changed

* `get_random_access` was renamed to `get_random_access_mut`.
* Bumped to 2024 edition.
* Structs that were shared with `hypercore-protocol` have been moved into a separate crate: `hypercore_schema`.
* Dependencies update
* Some function signatures were changed that were pass-by-value or pass-by-ref that didn't need to be.

### Removed



## [0.15.0] - 2025-12-25

### Added

* More impl's of `CompactEncoding`.

### Changed
Expand Down Expand Up @@ -480,5 +498,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
```

<!-- next-url -->
[Unreleased]: https://github.com/datrs/hypercore/compare/v0.14.0...HEAD
[Unreleased]: https://github.com/datrs/hypercore/compare/v0.15.0...HEAD
[0.15.0]: https://github.com/datrs/hypercore/compare/v0.14.0...v0.15.0
[0.14.0]: https://github.com/datrs/hypercore/compare/v0.14.0...v0.13.0
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "hypercore"
version = "0.14.0"
version = "0.15.0"
license = "MIT OR Apache-2.0"
description = "Secure, distributed, append-only log"
documentation = "https://docs.rs/hypercore"
Expand All @@ -18,7 +18,7 @@ categories = [
"data-structures",
"encoding",
]
edition = "2021"
edition = "2024"

[dependencies]
blake2 = "0.10"
Expand All @@ -42,6 +42,9 @@ moka = { version = "0.12", optional = true, features = ["sync"] }
async-broadcast = { version = "0.7.1", optional = true }
async-lock = {version = "3.4.0", optional = true }

[dependencies.hypercore_schema]
version = "0.2.0"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
random-access-disk = { version = "3", default-features = false }

Expand All @@ -61,7 +64,7 @@ test-log = { version = "0.2.11", default-features = false, features = ["trace"]
tracing-subscriber = { version = "0.3.16", features = ["env-filter", "fmt"] }

[features]
default = ["tokio", "sparse", "replication"]
default = ["tokio", "sparse", "replication", "cache"]
replication = ["dep:async-broadcast"]
shared-core = ["replication", "dep:async-lock"]
sparse = ["random-access-disk/sparse"]
Expand Down
2 changes: 1 addition & 1 deletion benches/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::time::{Duration, Instant};

#[cfg(feature = "async-std")]
use criterion::async_executor::AsyncStdExecutor;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use hypercore::{Hypercore, HypercoreBuilder, HypercoreError, Storage};
use tempfile::Builder as TempfileBuilder;

Expand Down
2 changes: 1 addition & 1 deletion benches/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::time::{Duration, Instant};

#[cfg(feature = "async-std")]
use criterion::async_executor::AsyncStdExecutor;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use criterion::{Criterion, black_box, criterion_group, criterion_main};
use hypercore::{Hypercore, HypercoreBuilder, HypercoreError, Storage};
use random_access_memory::RandomAccessMemory;

Expand Down
16 changes: 8 additions & 8 deletions examples/replication.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
#[cfg(feature = "async-std")]
use async_std::main as async_main;
use hypercore::{
Hypercore, HypercoreBuilder, HypercoreError, PartialKeypair, RequestBlock, RequestUpgrade,
Storage,
};
use hypercore::{Hypercore, HypercoreBuilder, HypercoreError, PartialKeypair, Storage};
use hypercore_schema::{RequestBlock, RequestUpgrade};
use tempfile::Builder;
#[cfg(feature = "tokio")]
use tokio::main as async_main;
Expand Down Expand Up @@ -99,10 +97,12 @@ async fn replicate_index(
.expect("Creating proof error")
.expect("Could not get proof");
// Then the proof is verified and applied to the replicated party.
assert!(replicated_hypercore
.verify_and_apply_proof(&proof)
.await
.expect("Verifying and applying proof failed"));
assert!(
replicated_hypercore
.verify_and_apply_proof(&proof)
.await
.expect("Verifying and applying proof failed")
);
}

fn format_res(res: Result<Option<Vec<u8>>, HypercoreError>) -> String {
Expand Down
45 changes: 22 additions & 23 deletions src/bitfield/dynamic.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::fixed::{FixedBitfield, FIXED_BITFIELD_BITS_LENGTH, FIXED_BITFIELD_LENGTH};
use super::fixed::{FIXED_BITFIELD_BITS_LENGTH, FIXED_BITFIELD_LENGTH, FixedBitfield};
use crate::{
common::{BitfieldUpdate, StoreInfo, StoreInfoInstruction, StoreInfoType},
Store,
common::{BitfieldUpdate, StoreInfo, StoreInfoInstruction, StoreInfoType},
};
use futures::future::Either;
use std::{cell::RefCell, convert::TryInto};
Expand Down Expand Up @@ -176,22 +176,22 @@ impl DynamicBitfield {
// not pages that don't exist, as they can't possibly contain the value.

// To keep the common case fast, first try the same page as the position
if let Some(p) = self.pages.get(first_page) {
if let Some(index) = p.borrow().index_of(value, first_index as u32) {
return Some(first_page * DYNAMIC_BITFIELD_PAGE_SIZE as u64 + index as u64);
};
}
if let Some(p) = self.pages.get(first_page)
&& let Some(index) = p.borrow().index_of(value, first_index as u32)
{
return Some(first_page * DYNAMIC_BITFIELD_PAGE_SIZE as u64 + index as u64);
};

// It wasn't found on the first page, now get the keys that are bigger
// than the given index and sort them.
let mut keys: Vec<&u64> = self.pages.keys().filter(|key| **key > first_page).collect();
keys.sort();
for key in keys {
if let Some(p) = self.pages.get(*key) {
if let Some(index) = p.borrow().index_of(value, 0) {
return Some(key * DYNAMIC_BITFIELD_PAGE_SIZE as u64 + index as u64);
};
}
if let Some(p) = self.pages.get(*key)
&& let Some(index) = p.borrow().index_of(value, 0)
{
return Some(key * DYNAMIC_BITFIELD_PAGE_SIZE as u64 + index as u64);
};
}
} else {
// Searching for the false value is easier as it is automatically hit on
Expand Down Expand Up @@ -223,11 +223,11 @@ impl DynamicBitfield {
// not pages that don't exist, as they can't possibly contain the value.

// To keep the common case fast, first try the same page as the position
if let Some(p) = self.pages.get(last_page) {
if let Some(index) = p.borrow().last_index_of(value, last_index as u32) {
return Some(last_page * DYNAMIC_BITFIELD_PAGE_SIZE as u64 + index as u64);
};
}
if let Some(p) = self.pages.get(last_page)
&& let Some(index) = p.borrow().last_index_of(value, last_index as u32)
{
return Some(last_page * DYNAMIC_BITFIELD_PAGE_SIZE as u64 + index as u64);
};

// It wasn't found on the last page, now get the keys that are smaller
// than the given index and sort them.
Expand All @@ -236,14 +236,13 @@ impl DynamicBitfield {
keys.reverse();

for key in keys {
if let Some(p) = self.pages.get(*key) {
if let Some(index) = p
if let Some(p) = self.pages.get(*key)
&& let Some(index) = p
.borrow()
.last_index_of(value, FIXED_BITFIELD_BITS_LENGTH as u32 - 1)
{
return Some(key * DYNAMIC_BITFIELD_PAGE_SIZE as u64 + index as u64);
};
}
{
return Some(key * DYNAMIC_BITFIELD_PAGE_SIZE as u64 + index as u64);
};
}
} else {
// Searching for the false value is easier as it is automatically hit on
Expand Down
2 changes: 1 addition & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use tracing::instrument;

#[cfg(feature = "cache")]
use crate::common::cache::CacheOptions;
use crate::{core::HypercoreOptions, Hypercore, HypercoreError, PartialKeypair, Storage};
use crate::{Hypercore, HypercoreError, PartialKeypair, Storage, core::HypercoreOptions};

/// Build CacheOptions.
#[cfg(feature = "cache")]
Expand Down
2 changes: 1 addition & 1 deletion src/common/cache.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use moka::sync::Cache;
use std::time::Duration;

use crate::Node;
use hypercore_schema::Node;

// Default to 1 year of cache
const DEFAULT_CACHE_TTL_SEC: u64 = 31556952;
Expand Down
4 changes: 0 additions & 4 deletions src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,8 @@ mod peer;
mod store;

pub use self::error::HypercoreError;
pub use self::node::Node;
pub(crate) use self::node::NodeByteRange;
pub(crate) use self::peer::ValuelessProof;
pub use self::peer::{
DataBlock, DataHash, DataSeek, DataUpgrade, Proof, RequestBlock, RequestSeek, RequestUpgrade,
};
pub use self::store::Store;
pub(crate) use self::store::{StoreInfo, StoreInfoInstruction, StoreInfoType};

Expand Down
149 changes: 0 additions & 149 deletions src/common/node.rs
Original file line number Diff line number Diff line change
@@ -1,155 +1,6 @@
use merkle_tree_stream::Node as NodeTrait;
use merkle_tree_stream::{NodeKind, NodeParts};
use pretty_hash::fmt as pretty_fmt;
use std::cmp::Ordering;
use std::convert::AsRef;
use std::fmt::{self, Display};

use crate::crypto::Hash;

/// Node byte range
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct NodeByteRange {
pub(crate) index: u64,
pub(crate) length: u64,
}

/// Nodes of the Merkle Tree that are persisted to disk.
// TODO: replace `hash: Vec<u8>` with `hash: Hash`. This requires patching /
// rewriting the Blake2b crate to support `.from_bytes()` to serialize from
// disk.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node {
/// This node's index in the Merkle tree
pub(crate) index: u64,
/// Hash of the data in this node
// TODO make this [u8; 32] like:
// https://github.com/holepunchto/hypercore/blob/d21ebdeca1b27eb4c2232f8af17d5ae939ee97f2/lib/messages.js#L246
pub(crate) hash: Vec<u8>,
/// Number of bytes in this [`Node::data`]
pub(crate) length: u64,
/// Index of this nodes parent
pub(crate) parent: u64,
/// Hypercore's data. Can be receieved after the rest of the node, so it's optional.
pub(crate) data: Option<Vec<u8>>,
pub(crate) blank: bool,
}

impl Node {
/// Create a new instance.
// TODO: ensure sizes are correct.
pub fn new(index: u64, hash: Vec<u8>, length: u64) -> Self {
let mut blank = true;
for byte in &hash {
if *byte != 0 {
blank = false;
break;
}
}
Self {
index,
hash,
length,
parent: flat_tree::parent(index),
data: Some(Vec::with_capacity(0)),
blank,
}
}

/// Creates a new blank node
pub fn new_blank(index: u64) -> Self {
Self {
index,
hash: vec![0, 32],
length: 0,
parent: 0,
data: None,
blank: true,
}
}
}

impl NodeTrait for Node {
#[inline]
fn index(&self) -> u64 {
self.index
}

#[inline]
fn hash(&self) -> &[u8] {
&self.hash
}

#[inline]
fn len(&self) -> u64 {
self.length
}

#[inline]
fn is_empty(&self) -> bool {
self.length == 0
}

#[inline]
fn parent(&self) -> u64 {
self.parent
}
}

impl AsRef<Node> for Node {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}

impl Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Node {{ index: {}, hash: {}, length: {} }}",
self.index,
pretty_fmt(&self.hash).unwrap(),
self.length
)
}
}

impl PartialOrd for Node {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for Node {
fn cmp(&self, other: &Self) -> Ordering {
self.index.cmp(&other.index)
}
}

impl From<NodeParts<Hash>> for Node {
fn from(parts: NodeParts<Hash>) -> Self {
let partial = parts.node();
let data = match partial.data() {
NodeKind::Leaf(data) => Some(data.clone()),
NodeKind::Parent => None,
};
let hash: Vec<u8> = parts.hash().as_bytes().into();
let mut blank = true;
for byte in &hash {
if *byte != 0 {
blank = false;
break;
}
}

Node {
index: partial.index(),
parent: partial.parent,
length: partial.len(),
hash,
data,
blank,
}
}
}
Loading
Loading