Skip to content

Commit

Permalink
Show info about connected peers in the gql api (#1524)
Browse files Browse the repository at this point in the history
closes: #649 

Show information about connected peers that may be useful for debugging
purposes via the gql api.

Note: in the future it may make sense to protect this API with a special
authentication token.

---------

Co-authored-by: xgreenx <xgreenx9999@gmail.com>
  • Loading branch information
Voxelot and xgreenx authored Dec 11, 2023
1 parent 673c91d commit 04c443f
Show file tree
Hide file tree
Showing 17 changed files with 348 additions and 14 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Description of the upcoming release here.

### Added

- [#1524](https://github.com/FuelLabs/fuel-core/pull/1524): Adds information about connected peers to the GQL API.
- [#1515](https://github.com/FuelLabs/fuel-core/pull/1515): Added support of `--version` command for `fuel-core-keygen` binary.
- [#1504](https://github.com/FuelLabs/fuel-core/pull/1504): A `Success` or `Failure` variant of `TransactionStatus` returned by a query now contains the associated receipts generated by transaction execution.

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bin/e2e-test-client/src/test_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ impl Wallet {
.transfer_tx(destination, transfer_amount, asset_id)
.await?;
let tx_id = tx.id(&self.consensus_params.chain_id);
println!("submitting tx... {:?}", tx_id);
let status = self.client.submit_and_await_commit(&tx).await?;

// we know the transferred coin should be output 0 from above
Expand Down
14 changes: 13 additions & 1 deletion crates/client/assets/schema.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ type ChainInfo {
name: String!
latestBlock: Block!
daHeight: U64!
peerCount: Int!
peers: [PeerInfo!]!
consensusParameters: ConsensusParameters!
gasCosts: GasCosts!
}
Expand Down Expand Up @@ -650,6 +650,18 @@ type PageInfo {
endCursor: String
}

type PeerInfo {
id: String!
addresses: [String!]!
clientVersion: String
blockHeight: U32
"""
The last heartbeat from this peer in unix epoch time ms
"""
lastHeartbeatMs: U64!
appScore: Float!
}

type PoAConsensus {
"""
Gets the signature of the block produced by `PoA` consensus.
Expand Down
13 changes: 12 additions & 1 deletion crates/client/src/client/schema/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,11 +322,22 @@ pub struct ChainQuery {
pub struct ChainInfo {
pub da_height: U64,
pub name: String,
pub peer_count: i32,
pub peers: Vec<PeerInfo>,
pub latest_block: Block,
pub consensus_parameters: ConsensusParameters,
}

#[derive(cynic::QueryFragment, Debug)]
#[cynic(schema_path = "./assets/schema.sdl", graphql_type = "PeerInfo")]
pub struct PeerInfo {
pub id: String,
pub addresses: Vec<String>,
pub client_version: Option<String>,
pub block_height: Option<U32>,
pub last_heartbeat_ms: U64,
pub app_score: f64,
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ query {
chain {
daHeight
name
peerCount
peers {
id
addresses
clientVersion
blockHeight
lastHeartbeatMs
appScore
}
latestBlock {
id
header {
Expand Down
37 changes: 34 additions & 3 deletions crates/client/src/client/types/chain_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,26 @@ use crate::client::{
schema,
types::Block,
};
use fuel_core_types::fuel_tx::ConsensusParameters;
use fuel_core_types::{
fuel_tx::ConsensusParameters,
services::p2p::{
HeartbeatData,
PeerId,
PeerInfo,
},
};
use std::{
str::FromStr,
time::{
Duration,
UNIX_EPOCH,
},
};

pub struct ChainInfo {
pub da_height: u64,
pub name: String,
pub peer_count: i32,
pub peers: Vec<PeerInfo>,
pub latest_block: Block,
pub consensus_parameters: ConsensusParameters,
}
Expand All @@ -19,9 +33,26 @@ impl From<schema::chain::ChainInfo> for ChainInfo {
Self {
da_height: value.da_height.into(),
name: value.name,
peer_count: value.peer_count,
peers: value.peers.into_iter().map(|info| info.into()).collect(),
latest_block: value.latest_block.into(),
consensus_parameters: value.consensus_parameters.into(),
}
}
}

impl From<schema::chain::PeerInfo> for PeerInfo {
fn from(info: schema::chain::PeerInfo) -> Self {
Self {
id: PeerId::from_str(info.id.as_str()).unwrap_or_default(),
peer_addresses: info.addresses.into_iter().collect(),
client_version: info.client_version,
heartbeat_data: HeartbeatData {
block_height: info.block_height.map(|h| h.0.into()),
last_heartbeat: UNIX_EPOCH
.checked_add(Duration::from_millis(info.last_heartbeat_ms.0))
.unwrap_or(UNIX_EPOCH),
},
app_score: info.app_score,
}
}
}
6 changes: 6 additions & 0 deletions crates/fuel-core/src/graphql_api/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ use fuel_core_types::{
},
services::{
graphql_api::ContractBalance,
p2p::PeerInfo,
txpool::{
InsertionResult,
TransactionStatus,
Expand Down Expand Up @@ -203,3 +204,8 @@ pub trait DatabaseMessageProof: Send + Sync {
commit_block_height: &BlockHeight,
) -> StorageResult<MerkleProof>;
}

#[async_trait::async_trait]
pub trait P2pPort: Send + Sync {
async fn all_peer_info(&self) -> anyhow::Result<Vec<PeerInfo>>;
}
4 changes: 4 additions & 0 deletions crates/fuel-core/src/graphql_api/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
BlockProducerPort,
ConsensusModulePort,
DatabasePort,
P2pPort,
TxPoolPort,
},
graphql_api::{
Expand Down Expand Up @@ -81,6 +82,7 @@ pub type BlockProducer = Box<dyn BlockProducerPort>;
// use only `Database` to receive all information about transactions.
pub type TxPool = Box<dyn TxPoolPort>;
pub type ConsensusModule = Box<dyn ConsensusModulePort>;
pub type P2pService = Box<dyn P2pPort>;

#[derive(Clone)]
pub struct SharedState {
Expand Down Expand Up @@ -165,6 +167,7 @@ pub fn new_service(
txpool: TxPool,
producer: BlockProducer,
consensus_module: ConsensusModule,
p2p_service: P2pService,
log_threshold_ms: Duration,
request_timeout: Duration,
) -> anyhow::Result<Service> {
Expand All @@ -176,6 +179,7 @@ pub fn new_service(
.data(txpool)
.data(producer)
.data(consensus_module)
.data(p2p_service)
.extension(async_graphql::extensions::Tracing)
.extension(MetricsExtension::new(log_threshold_ms))
.finish();
Expand Down
61 changes: 59 additions & 2 deletions crates/fuel-core/src/schema/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use async_graphql::{
Union,
};
use fuel_core_types::fuel_tx;
use std::time::UNIX_EPOCH;

pub struct ChainInfo;
pub struct ConsensusParameters(fuel_tx::ConsensusParameters);
Expand Down Expand Up @@ -658,6 +659,49 @@ impl GasCosts {
}
}

struct PeerInfo(fuel_core_types::services::p2p::PeerInfo);

#[Object]
impl PeerInfo {
/// The libp2p peer id
async fn id(&self) -> String {
self.0.id.to_string()
}

/// The advertised multi-addrs that can be used to connect to this peer
async fn addresses(&self) -> Vec<String> {
self.0.peer_addresses.iter().cloned().collect()
}

/// The self-reported version of the client the peer is using
async fn client_version(&self) -> Option<String> {
self.0.client_version.clone()
}

/// The last reported height of the peer
async fn block_height(&self) -> Option<U32> {
self.0
.heartbeat_data
.block_height
.map(|height| (*height).into())
}

/// The last heartbeat from this peer in unix epoch time ms
async fn last_heartbeat_ms(&self) -> U64 {
let time = self.0.heartbeat_data.last_heartbeat;
let time = time
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis();
U64(time.try_into().unwrap_or_default())
}

/// The internal fuel p2p reputation of this peer
async fn app_score(&self) -> f64 {
self.0.app_score
}
}

#[Object]
impl LightOperation {
async fn base(&self) -> U64 {
Expand Down Expand Up @@ -704,8 +748,21 @@ impl ChainInfo {
height.0.into()
}

async fn peer_count(&self) -> u16 {
0
async fn peers(&self, _ctx: &Context<'_>) -> anyhow::Result<Vec<PeerInfo>> {
#[cfg(feature = "p2p")]
{
let p2p: &crate::fuel_core_graphql_api::service::P2pService =
_ctx.data_unchecked();
let peer_info = p2p.all_peer_info().await?;
let peers = peer_info.into_iter().map(PeerInfo).collect();
Ok(peers)
}
#[cfg(not(feature = "p2p"))]
{
Err(anyhow::anyhow!(
"Peering is disabled in this build, try using the `p2p` feature flag."
))
}
}

async fn consensus_parameters(
Expand Down
46 changes: 44 additions & 2 deletions crates/fuel-core/src/service/adapters/graphql_api.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::BlockProducerAdapter;
use crate::{
database::{
transactions::OwnedTransactionIndexCursor,
Expand All @@ -14,9 +15,13 @@ use crate::{
DatabasePort,
DatabaseTransactions,
DryRunExecution,
P2pPort,
TxPoolPort,
},
service::adapters::TxPoolAdapter,
service::adapters::{
P2PAdapter,
TxPoolAdapter,
},
};
use async_trait::async_trait;
use fuel_core_services::stream::BoxStream;
Expand Down Expand Up @@ -60,6 +65,7 @@ use fuel_core_types::{
},
services::{
graphql_api::ContractBalance,
p2p::PeerInfo,
txpool::{
InsertionResult,
TransactionStatus,
Expand Down Expand Up @@ -264,4 +270,40 @@ impl DryRunExecution for BlockProducerAdapter {

impl BlockProducerPort for BlockProducerAdapter {}

use super::BlockProducerAdapter;
#[async_trait::async_trait]
impl P2pPort for P2PAdapter {
async fn all_peer_info(&self) -> anyhow::Result<Vec<PeerInfo>> {
#[cfg(feature = "p2p")]
{
use fuel_core_types::services::p2p::HeartbeatData;
if let Some(service) = &self.service {
let peers = service.get_all_peers().await?;
Ok(peers
.into_iter()
.map(|(peer_id, peer_info)| PeerInfo {
id: fuel_core_types::services::p2p::PeerId::from(
peer_id.to_bytes(),
),
peer_addresses: peer_info
.peer_addresses
.iter()
.map(|addr| addr.to_string())
.collect(),
client_version: None,
heartbeat_data: HeartbeatData {
block_height: peer_info.heartbeat_data.block_height,
last_heartbeat: peer_info.heartbeat_data.last_heartbeat_sys,
},
app_score: peer_info.score,
})
.collect())
} else {
Ok(vec![])
}
}
#[cfg(not(feature = "p2p"))]
{
Ok(vec![])
}
}
}
3 changes: 2 additions & 1 deletion crates/fuel-core/src/service/sub_services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ pub fn init_sub_services(
#[cfg(feature = "p2p")]
let sync = fuel_core_sync::service::new_service(
*last_block.header().height(),
p2p_adapter,
p2p_adapter.clone(),
importer_adapter.clone(),
verifier,
config.sync,
Expand Down Expand Up @@ -206,6 +206,7 @@ pub fn init_sub_services(
Box::new(tx_pool_adapter),
Box::new(producer_adapter),
Box::new(poa_adapter),
Box::new(p2p_adapter),
config.query_log_threshold_time,
config.api_request_timeout,
)?;
Expand Down
Loading

0 comments on commit 04c443f

Please sign in to comment.