Skip to content

Commit

Permalink
change(rpc): Add getpeerinfo RPC method (#5951)
Browse files Browse the repository at this point in the history
* adds ValidateBlock request to state

* adds `Request` enum in block verifier

skips solution check for BlockProposal requests

calls CheckBlockValidity instead of Commit block for BlockProposal requests

* uses new Request in references to chain verifier

* adds getblocktemplate proposal mode response type

* makes getblocktemplate-rpcs feature in zebra-consensus select getblocktemplate-rpcs in zebra-state

* Adds PR review revisions

* adds info log in CheckBlockProposalValidity

* Reverts replacement of match statement

* adds `GetBlockTemplate::capabilities` fn

* conditions calling checkpoint verifier on !request.is_proposal

* updates references to validate_and_commit_non_finalized

* adds snapshot test, updates test vectors

* adds `should_count_metrics` to NonFinalizedState

* Returns an error from chain verifier for block proposal requests below checkpoint height

adds feature flags

* adds "proposal" to GET_BLOCK_TEMPLATE_CAPABILITIES_FIELD

* adds back block::Request to zebra-consensus lib

* updates snapshots

* Removes unnecessary network arg

* skips req in tracing intstrument for read state

* Moves out block proposal validation to its own fn

* corrects `difficulty_threshold_is_valid` docs

adds/fixes some comments, adds TODOs

general cleanup from a self-review.

* Update zebra-state/src/service.rs

* Apply suggestions from code review

Co-authored-by: teor <[email protected]>

* Update zebra-rpc/src/methods/get_block_template_rpcs.rs

Co-authored-by: teor <[email protected]>

* check best chain tip

* Update zebra-state/src/service.rs

Co-authored-by: teor <[email protected]>

* Applies cleanup suggestions from code review

* updates gbt acceptance test to make a block proposal

* fixes json parsing mistake

* adds retries

* returns reject reason if there are no retries left

* moves result deserialization to RPCRequestClient method, adds docs, moves jsonrpc_core to dev-dependencies

* moves sleep(EXPECTED_TX_TIME) out of loop

* updates/adds info logs in retry loop

* Revert "moves sleep(EXPECTED_TX_TIME) out of loop"

This reverts commit f7f0926.

* adds `allow(dead_code)`

* tests with curtime, mintime, & maxtime

* Fixes doc comment

* Logs error responses from chain_verifier CheckProposal requests

* Removes retry loop, adds num_txs log

* removes verbose info log

* sorts mempool_txs before generating merkle root

* Make imports conditional on a feature

* Disable new CI tests until bugs are fixed

* adds support for getpeerinfo RPC

* adds vector test

* Always passes address_book to RpcServer

* Update zebra-network/src/address_book.rs

Co-authored-by: teor <[email protected]>

* Renames PeerObserver to AddressBookPeers

* adds getpeerinfo acceptance test

* Asserts that addresses parse into SocketAddr

* adds launches_lightwalletd field to TestType::LaunchWithEmptyState and uses it from the get_peer_info test

* renames peer_observer mod

* uses SocketAddr as `addr` field type in PeerInfo

Co-authored-by: teor <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 17, 2023
1 parent dcf3067 commit 1bb8a9c
Show file tree
Hide file tree
Showing 18 changed files with 370 additions and 46 deletions.
55 changes: 33 additions & 22 deletions zebra-network/src/address_book.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
//! The `AddressBook` manages information about what peers exist, when they were
//! seen, and what services they provide.
use std::{cmp::Reverse, iter::Extend, net::SocketAddr, time::Instant};
use std::{
cmp::Reverse,
iter::Extend,
net::SocketAddr,
sync::{Arc, Mutex},
time::Instant,
};

use chrono::Utc;
use ordered_map::OrderedMap;
Expand All @@ -12,9 +18,8 @@ use zebra_chain::parameters::Network;

use crate::{
constants, meta_addr::MetaAddrChange, protocol::external::canonical_socket_addr,
types::MetaAddr, PeerAddrState,
types::MetaAddr, AddressBookPeers, PeerAddrState,
};

#[cfg(test)]
mod tests;

Expand Down Expand Up @@ -288,7 +293,7 @@ impl AddressBook {
?updated,
?previous,
total_peers = self.by_addr.len(),
recent_peers = self.recently_live_peers(chrono_now).count(),
recent_peers = self.recently_live_peers(chrono_now).len(),
"calculated updated address book entry",
);

Expand Down Expand Up @@ -317,7 +322,7 @@ impl AddressBook {
?updated,
?previous,
total_peers = self.by_addr.len(),
recent_peers = self.recently_live_peers(chrono_now).count(),
recent_peers = self.recently_live_peers(chrono_now).len(),
"updated address book entry",
);

Expand All @@ -340,7 +345,7 @@ impl AddressBook {
surplus = ?surplus_peer,
?updated,
total_peers = self.by_addr.len(),
recent_peers = self.recently_live_peers(chrono_now).count(),
recent_peers = self.recently_live_peers(chrono_now).len(),
"removed surplus address book entry",
);
}
Expand Down Expand Up @@ -370,7 +375,7 @@ impl AddressBook {
trace!(
?removed_addr,
total_peers = self.by_addr.len(),
recent_peers = self.recently_live_peers(chrono_now).count(),
recent_peers = self.recently_live_peers(chrono_now).len(),
);

if let Some(entry) = self.by_addr.remove(&removed_addr) {
Expand Down Expand Up @@ -452,20 +457,6 @@ impl AddressBook {
.cloned()
}

/// Return an iterator over peers we've seen recently,
/// in reconnection attempt order.
pub fn recently_live_peers(
&'_ self,
now: chrono::DateTime<Utc>,
) -> impl Iterator<Item = MetaAddr> + DoubleEndedIterator + '_ {
let _guard = self.span.enter();

self.by_addr
.descending_values()
.filter(move |peer| peer.was_recently_live(now))
.cloned()
}

/// Returns the number of entries in this address book.
pub fn len(&self) -> usize {
self.by_addr.len()
Expand Down Expand Up @@ -501,7 +492,7 @@ impl AddressBook {
let failed = self.state_peers(PeerAddrState::Failed).count();
let attempt_pending = self.state_peers(PeerAddrState::AttemptPending).count();

let recently_live = self.recently_live_peers(now).count();
let recently_live = self.recently_live_peers(now).len();
let recently_stopped_responding = responded
.checked_sub(recently_live)
.expect("all recently live peers must have responded");
Expand Down Expand Up @@ -597,6 +588,26 @@ impl AddressBook {
}
}

impl AddressBookPeers for AddressBook {
fn recently_live_peers(&self, now: chrono::DateTime<Utc>) -> Vec<MetaAddr> {
let _guard = self.span.enter();

self.by_addr
.descending_values()
.filter(|peer| peer.was_recently_live(now))
.cloned()
.collect()
}
}

impl AddressBookPeers for Arc<Mutex<AddressBook>> {
fn recently_live_peers(&self, now: chrono::DateTime<Utc>) -> Vec<MetaAddr> {
self.lock()
.expect("panic in a previous thread that was holding the mutex")
.recently_live_peers(now)
}
}

impl Extend<MetaAddrChange> for AddressBook {
fn extend<T>(&mut self, iter: T)
where
Expand Down
17 changes: 17 additions & 0 deletions zebra-network/src/address_book_peers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//! A AddressBookPeers trait for getting the [`MetaAddr`] of recently live peers.
use chrono::Utc;

use crate::meta_addr::MetaAddr;

#[cfg(any(test, feature = "proptest-impl"))]
pub mod mock;

#[cfg(any(test, feature = "proptest-impl"))]
pub use mock::MockAddressBookPeers;

/// Method signatures for getting [`MetaAddr`]s of recently live peers.
pub trait AddressBookPeers {
/// Return an Vec of peers we've seen recently, in reconnection attempt order.
fn recently_live_peers(&self, now: chrono::DateTime<Utc>) -> Vec<MetaAddr>;
}
25 changes: 25 additions & 0 deletions zebra-network/src/address_book_peers/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! Mock [`AddressBookPeers`] for use in tests.
use crate::{meta_addr::MetaAddr, AddressBookPeers};

/// A mock [`AddressBookPeers`] implementation that's always empty.
#[derive(Default, Clone)]
pub struct MockAddressBookPeers {
/// Return value for mock `recently_live_peers` method.
recently_live_peers: Vec<MetaAddr>,
}

impl MockAddressBookPeers {
/// Creates a new [`MockAddressBookPeers`]
pub fn new(recently_live_peers: Vec<MetaAddr>) -> Self {
Self {
recently_live_peers,
}
}
}

impl AddressBookPeers for MockAddressBookPeers {
fn recently_live_peers(&self, _now: chrono::DateTime<chrono::Utc>) -> Vec<MetaAddr> {
self.recently_live_peers.clone()
}
}
2 changes: 2 additions & 0 deletions zebra-network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ extern crate bitflags;
pub type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;

mod address_book;
pub mod address_book_peers;
mod address_book_updater;
mod config;
pub mod constants;
Expand Down Expand Up @@ -171,6 +172,7 @@ pub use crate::isolated::{

pub use crate::{
address_book::AddressBook,
address_book_peers::AddressBookPeers,
config::Config,
isolated::{connect_isolated, connect_isolated_tcp_direct},
meta_addr::PeerAddrState,
Expand Down
40 changes: 34 additions & 6 deletions zebra-rpc/src/methods/get_block_template_rpcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use zebra_chain::{
transparent,
};
use zebra_consensus::VerifyChainError;
use zebra_network::AddressBookPeers;
use zebra_node_services::mempool;
use zebra_state::{ReadRequest, ReadResponse};

Expand All @@ -31,7 +32,7 @@ use crate::methods::{
},
types::{
get_block_template::GetBlockTemplate, get_mining_info, hex_data::HexData,
long_poll::LongPollInput, submit_block,
long_poll::LongPollInput, peer_info::PeerInfo, submit_block,
},
},
height_from_signed_int, GetBlockHash, MISSING_BLOCK_ERROR_CODE,
Expand Down Expand Up @@ -149,10 +150,16 @@ pub trait GetBlockTemplateRpc {
) -> BoxFuture<Result<u64>> {
self.get_network_sol_ps(num_blocks, height)
}

/// Returns data about each connected network node.
///
/// zcashd reference: [`getpeerinfo`](https://zcash.github.io/rpc/getpeerinfo.html)
#[rpc(name = "getpeerinfo")]
fn get_peer_info(&self) -> BoxFuture<Result<Vec<PeerInfo>>>;
}

/// RPC method implementations.
pub struct GetBlockTemplateRpcImpl<Mempool, State, Tip, ChainVerifier, SyncStatus>
pub struct GetBlockTemplateRpcImpl<Mempool, State, Tip, ChainVerifier, SyncStatus, AddressBook>
where
Mempool: Service<
mempool::Request,
Expand All @@ -170,6 +177,7 @@ where
+ Sync
+ 'static,
SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
AddressBook: AddressBookPeers,
{
// Configuration
//
Expand Down Expand Up @@ -197,10 +205,13 @@ where

/// The chain sync status, used for checking if Zebra is likely close to the network chain tip.
sync_status: SyncStatus,

/// Address book of peers, used for `getpeerinfo`.
address_book: AddressBook,
}

impl<Mempool, State, Tip, ChainVerifier, SyncStatus>
GetBlockTemplateRpcImpl<Mempool, State, Tip, ChainVerifier, SyncStatus>
impl<Mempool, State, Tip, ChainVerifier, SyncStatus, AddressBook>
GetBlockTemplateRpcImpl<Mempool, State, Tip, ChainVerifier, SyncStatus, AddressBook>
where
Mempool: Service<
mempool::Request,
Expand All @@ -222,8 +233,10 @@ where
+ Sync
+ 'static,
SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
{
/// Create a new instance of the handler for getblocktemplate RPCs.
#[allow(clippy::too_many_arguments)]
pub fn new(
network: Network,
mining_config: config::Config,
Expand All @@ -232,6 +245,7 @@ where
latest_chain_tip: Tip,
chain_verifier: ChainVerifier,
sync_status: SyncStatus,
address_book: AddressBook,
) -> Self {
Self {
network,
Expand All @@ -241,12 +255,13 @@ where
latest_chain_tip,
chain_verifier,
sync_status,
address_book,
}
}
}

impl<Mempool, State, Tip, ChainVerifier, SyncStatus> GetBlockTemplateRpc
for GetBlockTemplateRpcImpl<Mempool, State, Tip, ChainVerifier, SyncStatus>
impl<Mempool, State, Tip, ChainVerifier, SyncStatus, AddressBook> GetBlockTemplateRpc
for GetBlockTemplateRpcImpl<Mempool, State, Tip, ChainVerifier, SyncStatus, AddressBook>
where
Mempool: Service<
mempool::Request,
Expand All @@ -271,6 +286,7 @@ where
+ 'static,
<ChainVerifier as Service<zebra_consensus::Request>>::Future: Send,
SyncStatus: ChainSyncStatus + Clone + Send + Sync + 'static,
AddressBook: AddressBookPeers + Clone + Send + Sync + 'static,
{
fn get_block_count(&self) -> Result<u32> {
best_chain_tip_height(&self.latest_chain_tip).map(|height| height.0)
Expand Down Expand Up @@ -704,6 +720,18 @@ where
}
.boxed()
}

fn get_peer_info(&self) -> BoxFuture<Result<Vec<PeerInfo>>> {
let address_book = self.address_book.clone();
async move {
Ok(address_book
.recently_live_peers(chrono::Utc::now())
.into_iter()
.map(PeerInfo::from)
.collect())
}
.boxed()
}
}

// Put support functions in a submodule, to keep this file small.
1 change: 1 addition & 0 deletions zebra-rpc/src/methods/get_block_template_rpcs/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ pub mod get_block_template;
pub mod get_mining_info;
pub mod hex_data;
pub mod long_poll;
pub mod peer_info;
pub mod submit_block;
pub mod transaction;
20 changes: 20 additions & 0 deletions zebra-rpc/src/methods/get_block_template_rpcs/types/peer_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! An array of [`PeerInfo`] is the output of the `getpeerinfo` RPC method.
use std::net::SocketAddr;

use zebra_network::types::MetaAddr;

/// Item of the `getpeerinfo` response
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct PeerInfo {
/// The IP address and port of the peer
pub addr: SocketAddr,
}

impl From<MetaAddr> for PeerInfo {
fn from(meta_addr: MetaAddr) -> Self {
Self {
addr: meta_addr.addr(),
}
}
}
Loading

0 comments on commit 1bb8a9c

Please sign in to comment.