Skip to content

Commit 66633bf

Browse files
committed
Change missing_nodes to accept hypercore index
Add missing_nodes_from_merkle_tree_index for a possibility to evaluate also odd merkle tree indices. Also add replication example.
1 parent 1414986 commit 66633bf

File tree

6 files changed

+158
-11
lines changed

6 files changed

+158
-11
lines changed

Cargo.toml

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
[package]
22
name = "hypercore"
3-
version = "0.12.0-alpha.7"
3+
version = "0.12.0-alpha.8"
44
license = "MIT OR Apache-2.0"
55
description = "Secure, distributed, append-only log"
66
documentation = "https://docs.rs/hypercore"
77
repository = "https://github.com/datrs/hypercore"
88
readme = "README.md"
9-
authors = ["Yoshua Wuyts <[email protected]>"]
9+
authors = [
10+
"Yoshua Wuyts <[email protected]>",
11+
"Timo Tiuraniemi <[email protected]>"
12+
]
1013
keywords = ["dat", "p2p", "stream", "feed", "merkle"]
1114
categories = [
1215
"asynchronous",

examples/disk.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
#[cfg(feature = "async-std")]
22
use async_std::main as async_main;
3-
use hypercore::{HypercoreBuilder, Storage};
3+
use hypercore::{HypercoreBuilder, HypercoreError, Storage};
44
use tempfile::Builder;
55
#[cfg(feature = "tokio")]
66
use tokio::main as async_main;
77

8+
/// Example about using an in-memory hypercore.
89
#[async_main]
910
async fn main() {
1011
// For the purposes of this example, first create a
@@ -72,8 +73,16 @@ async fn main() {
7273
// Print the first three values, converting binary back to string
7374
println!(
7475
"{}{}{}",
75-
String::from_utf8(hypercore.get(0).await.unwrap().unwrap()).unwrap(),
76-
String::from_utf8(hypercore.get(1).await.unwrap().unwrap()).unwrap(),
77-
String::from_utf8(hypercore.get(2).await.unwrap().unwrap()).unwrap()
76+
format_res(hypercore.get(0).await),
77+
format_res(hypercore.get(1).await),
78+
format_res(hypercore.get(2).await)
7879
); // prints "Hello, from disk hypercore!"
7980
}
81+
82+
fn format_res(res: Result<Option<Vec<u8>>, HypercoreError>) -> String {
83+
match res {
84+
Ok(Some(bytes)) => String::from_utf8(bytes).expect("Shouldn't fail in example"),
85+
Ok(None) => "Got None in feed".to_string(),
86+
Err(e) => format!("Error getting value from feed, reason = {e:?}"),
87+
}
88+
}

examples/memory.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#[cfg(feature = "async-std")]
22
use async_std::main as async_main;
3-
use hypercore::{HypercoreBuilder, Storage};
3+
use hypercore::{HypercoreBuilder, HypercoreError, Storage};
44
#[cfg(feature = "tokio")]
55
use tokio::main as async_main;
66

7+
/// Example about using an in-memory hypercore.
78
#[async_main]
89
async fn main() {
910
// Create a memory storage
@@ -44,7 +45,15 @@ async fn main() {
4445
// Print values, converting binary back to string
4546
println!(
4647
"{}{}",
47-
String::from_utf8(hypercore.get(0).await.unwrap().unwrap()).unwrap(),
48-
String::from_utf8(hypercore.get(1).await.unwrap().unwrap()).unwrap()
48+
format_res(hypercore.get(0).await),
49+
format_res(hypercore.get(1).await)
4950
); // prints "Hello, from memory hypercore!"
5051
}
52+
53+
fn format_res(res: Result<Option<Vec<u8>>, HypercoreError>) -> String {
54+
match res {
55+
Ok(Some(bytes)) => String::from_utf8(bytes).expect("Shouldn't fail in example"),
56+
Ok(None) => "Got None in feed".to_string(),
57+
Err(e) => format!("Error getting value from feed, reason = {e:?}"),
58+
}
59+
}

examples/replication.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#[cfg(feature = "async-std")]
2+
use async_std::main as async_main;
3+
use hypercore::{
4+
Hypercore, HypercoreBuilder, HypercoreError, PartialKeypair, RequestBlock, RequestUpgrade,
5+
Storage,
6+
};
7+
use random_access_disk::RandomAccessDisk;
8+
use random_access_memory::RandomAccessMemory;
9+
use tempfile::Builder;
10+
#[cfg(feature = "tokio")]
11+
use tokio::main as async_main;
12+
13+
/// Example on how to replicate a (disk) hypercore to another (memory) hypercore.
14+
/// NB: The replication functions used here are low-level, built for use in the wire
15+
/// protocol.
16+
#[async_main]
17+
async fn main() {
18+
// For the purposes of this example, first create a
19+
// temporary directory to hold hypercore.
20+
let dir = Builder::new()
21+
.prefix("examples_replication")
22+
.tempdir()
23+
.unwrap()
24+
.into_path();
25+
26+
// Create a disk storage, overwriting existing values.
27+
let overwrite = true;
28+
let storage = Storage::new_disk(&dir, overwrite)
29+
.await
30+
.expect("Could not create disk storage");
31+
32+
// Build a new disk hypercore
33+
let mut origin_hypercore = HypercoreBuilder::new(storage)
34+
.build()
35+
.await
36+
.expect("Could not create disk hypercore");
37+
38+
// Append values to the hypercore
39+
let batch: &[&[u8]] = &[b"Hello, ", b"from ", b"replicated ", b"hypercore!"];
40+
origin_hypercore.append_batch(batch).await.unwrap();
41+
42+
// Store the public key
43+
let origin_public_key = origin_hypercore.key_pair().public;
44+
45+
// Create a peer of the origin hypercore using the public key
46+
let mut replicated_hypercore = HypercoreBuilder::new(
47+
Storage::new_memory()
48+
.await
49+
.expect("Could not create memory storage"),
50+
)
51+
.key_pair(PartialKeypair {
52+
public: origin_public_key,
53+
secret: None,
54+
})
55+
.build()
56+
.await
57+
.expect("Could not create memory hypercore");
58+
59+
// Replicate the four values in random order
60+
replicate_index(&mut origin_hypercore, &mut replicated_hypercore, 3).await;
61+
replicate_index(&mut origin_hypercore, &mut replicated_hypercore, 0).await;
62+
replicate_index(&mut origin_hypercore, &mut replicated_hypercore, 2).await;
63+
replicate_index(&mut origin_hypercore, &mut replicated_hypercore, 1).await;
64+
65+
// Print values from replicated hypercore, converting binary back to string
66+
println!(
67+
"{}{}{}{}",
68+
format_res(replicated_hypercore.get(0).await),
69+
format_res(replicated_hypercore.get(1).await),
70+
format_res(replicated_hypercore.get(2).await),
71+
format_res(replicated_hypercore.get(3).await)
72+
); // prints "Hello, from replicated hypercore!"
73+
}
74+
75+
async fn replicate_index(
76+
origin_hypercore: &mut Hypercore<RandomAccessDisk>,
77+
replicated_hypercore: &mut Hypercore<RandomAccessMemory>,
78+
request_index: u64,
79+
) {
80+
let missing_nodes = origin_hypercore
81+
.missing_nodes(request_index)
82+
.await
83+
.expect("Could not get missing nodes");
84+
let upgrade_start = replicated_hypercore.info().contiguous_length;
85+
let upgrade_length = origin_hypercore.info().contiguous_length - upgrade_start;
86+
87+
let proof = origin_hypercore
88+
.create_proof(
89+
Some(RequestBlock {
90+
index: request_index,
91+
nodes: missing_nodes,
92+
}),
93+
None,
94+
None,
95+
Some(RequestUpgrade {
96+
start: upgrade_start,
97+
length: upgrade_length,
98+
}),
99+
)
100+
.await
101+
.expect("Creating proof error")
102+
.expect("Could not get proof");
103+
// Then the proof is verified and applied to the replicated party.
104+
assert!(replicated_hypercore
105+
.verify_and_apply_proof(&proof)
106+
.await
107+
.expect("Verifying and applying proof failed"));
108+
}
109+
110+
fn format_res(res: Result<Option<Vec<u8>>, HypercoreError>) -> String {
111+
match res {
112+
Ok(Some(bytes)) => String::from_utf8(bytes).expect("Shouldn't fail in example"),
113+
Ok(None) => "Got None in feed".to_string(),
114+
Err(e) => format!("Error getting value from feed, reason = {e:?}"),
115+
}
116+
}

src/core.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,17 @@ where
550550
/// Used to fill the nodes field of a `RequestBlock` during
551551
/// synchronization.
552552
#[instrument(err, skip(self))]
553-
pub async fn missing_nodes(&mut self, merkle_tree_index: u64) -> Result<u64, HypercoreError> {
553+
pub async fn missing_nodes(&mut self, index: u64) -> Result<u64, HypercoreError> {
554+
self.missing_nodes_from_merkle_tree_index(index * 2).await
555+
}
556+
557+
/// Get missing nodes using a merkle tree index. Advanced variant of missing_nodex
558+
/// that allow for special cases of searching directly from the merkle tree.
559+
#[instrument(err, skip(self))]
560+
pub async fn missing_nodes_from_merkle_tree_index(
561+
&mut self,
562+
merkle_tree_index: u64,
563+
) -> Result<u64, HypercoreError> {
554564
match self.tree.missing_nodes(merkle_tree_index, None)? {
555565
Either::Right(value) => Ok(value),
556566
Either::Left(instructions) => {

src/tree/merkle_tree.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ impl MerkleTree {
328328

329329
/// Creates valueless proof from requests.
330330
/// TODO: This is now just a clone of javascript's
331-
/// https://github.com/hypercore-protocol/hypercore/blob/7e30a0fe353c70ada105840ec1ead6627ff521e7/lib/merkle-tree.js#L604
331+
/// https://github.com/holepunchto/hypercore/blob/9ce03363cb8938dbab53baba7d7cc9dde0508a7e/lib/merkle-tree.js#L1181
332332
/// The implementation should be rewritten to make it clearer.
333333
pub(crate) fn create_valueless_proof(
334334
&mut self,

0 commit comments

Comments
 (0)