Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Inbound FindBlocks and FindHeaders #1347

Merged
merged 29 commits into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
680d270
implement inbound `FindBlocks`
oxarbitrage Nov 20, 2020
88cd84b
Apply suggestions from code review
yaahc Nov 24, 2020
aed0170
Apply some suggestions from code review
oxarbitrage Nov 24, 2020
e81b823
fix build after suggestions
oxarbitrage Nov 24, 2020
33ee423
remove non needed iterator
oxarbitrage Nov 24, 2020
5f8c93c
fix stop parameter
oxarbitrage Nov 24, 2020
b3c7f9e
handle empty response
oxarbitrage Nov 24, 2020
20bc94d
remove redundant call in
yaahc Nov 24, 2020
e967c97
Update zebra-state/src/service.rs
oxarbitrage Nov 24, 2020
24904ce
Merge branch 'main' into issue1306
oxarbitrage Nov 24, 2020
9124315
rustfmt
oxarbitrage Nov 25, 2020
7a74fc1
Apply suggestions from code review
oxarbitrage Nov 25, 2020
187534d
handle request before having any chain tip
oxarbitrage Nov 25, 2020
b8ee598
fix known blocks matching with our chain
oxarbitrage Nov 25, 2020
190d7cf
remove known blocks empty check
oxarbitrage Nov 25, 2020
d5c126a
clippy
oxarbitrage Nov 25, 2020
973a85c
Split state height functions into "any chain" and "best chain"
teor2345 Nov 26, 2020
92940f4
Split `find_chain_hashes` into smaller functions
teor2345 Nov 26, 2020
c43c255
Refactor - use more iterator methods
teor2345 Nov 26, 2020
266278f
Rename `hash()` to `best_hash()`
teor2345 Nov 26, 2020
5d3723c
Document the only remaining use of an "any chain" method
teor2345 Nov 26, 2020
cd061fd
Rename the best chain block method to `best_block`
teor2345 Nov 26, 2020
76070a5
Improve some logs and comments
teor2345 Nov 26, 2020
a50ebfa
Handle inbound peer FindHeaders requests
teor2345 Nov 26, 2020
2813c1c
Move fmt utilities to zebra_chain::fmt
teor2345 Nov 26, 2020
6174d23
Summarise Debug for some Message variants
teor2345 Nov 26, 2020
adaf13f
Fix some comments
teor2345 Nov 26, 2020
8d96d43
Refactor to avoid an unwrap
teor2345 Nov 30, 2020
4d8bbd5
Clean up the rest of the expanded `derive(Debug)`
teor2345 Nov 30, 2020
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
14 changes: 14 additions & 0 deletions zebra-state/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,18 @@ pub enum Request {
/// Code making this request should apply a timeout layer to the service to
/// handle missing UTXOs.
AwaitUtxo(transparent::OutPoint),

/// Finds the intersection of the block hashes provided by a peer, and the local best chain.
/// Returns a list of block hashes, starting at the child of the intersection, and extending towards the tip.
///
/// Returns
///
/// [`Response::BlockHashes(Vec<block::Hash>)`](Response::Hashes) with max of 500 block hashes.
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
/// See https://en.bitcoin.it/wiki/Protocol_documentation#getblocks
FindBlockHashes {
/// Hashes of known blocks, ordered from highest height to lowest height.
known_blocks: Vec<block::Hash>,
/// Optionally, the last block hash to request.
stop: Option<block::Hash>,
},
}
5 changes: 4 additions & 1 deletion zebra-state/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ pub enum Response {
/// Response to [`Request::Block`] with the specified block.
Block(Option<Arc<Block>>),

/// The response to a `AwaitUtxo` request
/// The response to a `AwaitUtxo` request.
Utxo(Utxo),

/// The response to a `FindBlockHashes` request.
BlockHashes(Vec<block::Hash>),
}
103 changes: 103 additions & 0 deletions zebra-state/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,105 @@ impl StateService {
state: IterState::NonFinalized(hash),
}
}

/// Return a list of block hashes in the best chain, following the first matching hash in `known_blocks`.
///
/// Starts from the first matching hash in the best chain, ignoring all other hashes in `known_blocks`.
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
/// Only matches and returns hashes from the best chain, including non-finalized blocks.
/// Stops the list of hashes after:
/// * adding the non-finalized best tip,
/// * adding the stop hash to the list, if it is in the best chain, or
/// * adding 500 hashes to the list.
pub fn find_chain_hashes(
&self,
known_blocks: Vec<block::Hash>,
stop: Option<block::Hash>,
) -> Vec<block::Hash> {
let mut res: Vec<block::Hash> = Vec::new();

// Known blocks can't be empty
if known_blocks.is_empty() {
return res;
}

oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
// Get chain tip
let (chain_tip_height, ..) = self.tip().expect("tip must always be available");
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved

// Get requested tip
let locator_tip_hash = known_blocks.first().expect("we must have a tip hash");
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
let locator_tip_height = self
.height_by_hash(*locator_tip_hash)
.expect("tip hash must have a height");
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
let chain = self.chain(*locator_tip_hash);

// Requested tip must be below our chain tip
if locator_tip_height >= chain_tip_height {
return res;
}
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved

tracing::info!(
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
"REQUESTED TIP: {:?} {:?}",
locator_tip_height,
locator_tip_hash
);

// Block locator are not continuous but we need to make sure all the blocks
// provided are in our chain and in the right order.
let mut collect_block_locator: Vec<block::Hash> = Vec::new();
let mut index = 0;
for block in chain {
// We get out of the loop as soon as we collect a vector of equal
// lenght of the provided.
if collect_block_locator.len() == known_blocks.len() {
break;
}

if block.hash() == known_blocks[index] {
collect_block_locator.push(block.hash());
index += 1
}
}

// If the 2 vectors are not equal we cant continue.
if collect_block_locator != known_blocks {
return res;
}

const MAX_FIND_BLOCK_HASHES_RESULTS: usize = 500;

// Compute a new tip, make sure it is below our chain tip.
let new_tip_height = block::Height(std::cmp::min(
locator_tip_height.0 + MAX_FIND_BLOCK_HASHES_RESULTS as u32,
chain_tip_height.0,
));
let new_tip_hash = self.hash(new_tip_height).expect("new tip must have a hash");

// Get a new chain starting at the new "requested" tip.
let new_chain = self.chain(new_tip_hash);

for block in new_chain {
// If we get the requested tip we are over
if block.hash() == *locator_tip_hash {
break;
}

res.push(block.hash());

// Check the stop parameter
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
if stop == Some(block.hash()) {
break;
}

tracing::info!(
teor2345 marked this conversation as resolved.
Show resolved Hide resolved
"RESPONSE: {:?} {:?}",
self.height_by_hash(block.hash())
.expect("if hash is in the state then it should have an associated height"),
block.hash()
);
}
res.reverse();
res
}
}

struct Iter<'a> {
Expand Down Expand Up @@ -452,6 +551,10 @@ impl Service<Request> for StateService {

fut.boxed()
}
Request::FindBlockHashes { known_blocks, stop } => {
let res = self.find_chain_hashes(known_blocks, stop);
async move { Ok(Response::BlockHashes(res)) }.boxed()
}
}
}
}
Expand Down
17 changes: 14 additions & 3 deletions zebrad/src/components/inbound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,20 @@ impl Service<zn::Request> for Inbound {
debug!("ignoring unimplemented request");
async { Ok(zn::Response::Nil) }.boxed()
}
zn::Request::FindBlocks { .. } => {
debug!("ignoring unimplemented request");
async { Ok(zn::Response::Nil) }.boxed()
zn::Request::FindBlocks { known_blocks, stop } => {
let request = zs::Request::FindBlockHashes { known_blocks, stop };
self.state.call(request).map_ok(|resp| match resp {
zs::Response::BlockHashes(hashes) => {
if hashes.is_empty() {
zn::Response::Nil
}
else {
zn::Response::BlockHashes(hashes)
}
},
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
_ => unreachable!("zebra-state should always respond to a `FindBlocks` request with a `Vec<block::Hash>` response"),
oxarbitrage marked this conversation as resolved.
Show resolved Hide resolved
})
.boxed()
}
zn::Request::FindHeaders { .. } => {
debug!("ignoring unimplemented request");
Expand Down