Skip to content

Commit

Permalink
feat(stats): added miner and p2pool block stats (#26)
Browse files Browse the repository at this point in the history
Description
---
We need to know whether how many blocks we sent to both p2pool and the
target network.

Motivation and Context
---

How Has This Been Tested?
---

- Normally start mining with p2pool
- Check stats at http://127.0.0.1:19000/stats

Example output:

```json
{
  "connected": true,
  "connected_since": 1722856949,
  "num_of_miners": 2,
  "last_block_won": {
    "hash": "fbdbf4a36888c6f5ad92c67f40987ab9cc1313809ea98859bc42eda68d54c832",
    "height": 5403,
    "timestamp": 1722857161,
    "miner_wallet_address": "f27mEvFXUZJCNFQM1MbkRwBnNJHwsK6JBxzrwVyYNm6bgGHpL1xDj7Gjevwj9caFrp23iLGUcysK6decdDL87sQCtL2"
  },
  "share_chain_height": 5406,
  "pool_hash_rate": [4235994],
  "pool_total_earnings": 55525928731,
  "pool_total_estimated_earnings": {
    "1min": 12123240,
    "1h": 727394400,
    "1d": 17457465600,
    "1w": 122202259200,
    "30d": 523723968000
  },
  "total_earnings": {
    "f2CrtWaTZE3xWSxCkR1mR9Bszx321Ar2S1fPcBLNSNWiLBVwqkeankFjeTmdxYLuyeHg8oM4vSgsV1tjL4GSKEuy9pk": 9716846540,
    "f27mEvFXUZJCNFQM1MbkRwBnNJHwsK6JBxzrwVyYNm6bgGHpL1xDj7Gjevwj9caFrp23iLGUcysK6decdDL87sQCtL2": 58426310963
  },
  "estimated_earnings": {
    "f2CrtWaTZE3xWSxCkR1mR9Bszx321Ar2S1fPcBLNSNWiLBVwqkeankFjeTmdxYLuyeHg8oM4vSgsV1tjL4GSKEuy9pk": {
      "1min": 1728660,
      "1h": 103719600,
      "1d": 2489270400,
      "1w": 17424892800,
      "30d": 74678112000
    },
    "f27mEvFXUZJCNFQM1MbkRwBnNJHwsK6JBxzrwVyYNm6bgGHpL1xDj7Gjevwj9caFrp23iLGUcysK6decdDL87sQCtL2": {
      "1min": 10394520,
      "1h": 623671200,
      "1d": 14968108800,
      "1w": 104776761600,
      "30d": 449043264000
    }
  },
  "miner_block_stats": {
    "accepted": 154,
    "rejected": 0,
    "submitted": 154
  },
  "p2pool_block_stats": {
    "accepted": 2,
    "rejected": 77,
    "submitted": 79
  }
}
```

What process can a PR reviewer use to test or verify this change?
---
Check previous section.


Breaking Changes
---

- [x] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [ ] Other - Please specify
  • Loading branch information
ksrichard authored Aug 5, 2024
1 parent 62198a0 commit 4363494
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 10 deletions.
43 changes: 35 additions & 8 deletions src/server/grpc/p2pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ use tari_utilities::hex::Hex;
use tokio::sync::Mutex;
use tonic::{Request, Response, Status};

use crate::server::http::stats::{
MINER_STAT_ACCEPTED_BLOCKS_COUNT, MINER_STAT_REJECTED_BLOCKS_COUNT, P2POOL_STAT_ACCEPTED_BLOCKS_COUNT,
P2POOL_STAT_REJECTED_BLOCKS_COUNT,
};
use crate::server::stats_store::StatsStore;
use crate::{
server::{
grpc::{error::Error, util},
Expand All @@ -35,6 +40,8 @@ where
p2p_client: p2p::ServiceClient,
/// Current share chain
share_chain: Arc<S>,
/// Stats store
stats_store: Arc<StatsStore>,
}

impl<S> ShaP2PoolGrpc<S>
Expand All @@ -45,24 +52,37 @@ where
base_node_address: String,
p2p_client: p2p::ServiceClient,
share_chain: Arc<S>,
stats_store: Arc<StatsStore>,
) -> Result<Self, Error> {
Ok(Self {
client: Arc::new(Mutex::new(util::connect_base_node(base_node_address).await?)),
p2p_client,
share_chain,
stats_store,
})
}

/// Submits a new block to share chain and broadcasts to the p2p network.
pub async fn submit_share_chain_block(&self, block: &Block) -> Result<(), Status> {
if let Err(error) = self.share_chain.submit_block(block).await {
warn!(target: LOG_TARGET, "Failed to add new block: {error:?}");
match self.share_chain.submit_block(block).await {
Ok(_) => {
self.stats_store
.inc(&MINER_STAT_ACCEPTED_BLOCKS_COUNT.to_string(), 1)
.await;
info!(target: LOG_TARGET, "Broadcast new block: {:?}", block.hash().to_hex());
self.p2p_client
.broadcast_block(block)
.await
.map_err(|error| Status::internal(error.to_string()))
},
Err(error) => {
warn!(target: LOG_TARGET, "Failed to add new block: {error:?}");
self.stats_store
.inc(&MINER_STAT_REJECTED_BLOCKS_COUNT.to_string(), 1)
.await;
Ok(())
},
}
info!(target: LOG_TARGET, "Broadcast new block: {:?}", block.hash().to_hex());
self.p2p_client
.broadcast_block(block)
.await
.map_err(|error| Status::internal(error.to_string()))
}
}

Expand Down Expand Up @@ -176,12 +196,19 @@ where
let grpc_request = Request::from_parts(metadata, extensions, grpc_request_payload);
match self.client.lock().await.submit_block(grpc_request).await {
Ok(resp) => {
self.stats_store
.inc(&P2POOL_STAT_ACCEPTED_BLOCKS_COUNT.to_string(), 1)
.await;
info!("💰 New matching block found and sent to network!");
block.set_sent_to_main_chain(true);
self.submit_share_chain_block(&block).await?;
Ok(resp)
},
Err(_) => {
Err(error) => {
warn!("Failed to submit block to Tari network: {error:?}");
self.stats_store
.inc(&P2POOL_STAT_REJECTED_BLOCKS_COUNT.to_string(), 1)
.await;
block.set_sent_to_main_chain(false);
self.submit_share_chain_block(&block).await?;
Ok(Response::new(SubmitBlockResponse {
Expand Down
24 changes: 23 additions & 1 deletion src/server/http/stats/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: BSD-3-Clause

use std::collections::HashMap;
use std::sync::Arc;

use axum::extract::State;
use axum::http::StatusCode;
Expand All @@ -13,8 +14,13 @@ use tari_core::consensus::ConsensusManager;
use tari_core::transactions::tari_amount::MicroMinotari;
use tari_utilities::epoch_time::EpochTime;

use crate::server::http::stats::models::{EstimatedEarnings, Stats};
use crate::server::http::stats::models::{BlockStats, EstimatedEarnings, Stats};
use crate::server::http::stats::server::AppState;
use crate::server::http::stats::{
MINER_STAT_ACCEPTED_BLOCKS_COUNT, MINER_STAT_REJECTED_BLOCKS_COUNT, P2POOL_STAT_ACCEPTED_BLOCKS_COUNT,
P2POOL_STAT_REJECTED_BLOCKS_COUNT,
};
use crate::server::stats_store::StatsStore;
use crate::sharechain::SHARE_COUNT;

const LOG_TARGET: &str = "p2pool::server::stats::get";
Expand Down Expand Up @@ -136,5 +142,21 @@ pub async fn handle_get_stats(State(state): State<AppState>) -> Result<Json<Stat
pool_total_estimated_earnings: EstimatedEarnings::new(MicroMinotari::from(pool_total_estimated_earnings_1m)),
total_earnings: miners_with_rewards,
estimated_earnings,
miner_block_stats: miner_block_stats(state.stats_store.clone()).await,
p2pool_block_stats: p2pool_block_stats(state.stats_store.clone()).await,
}))
}

async fn miner_block_stats(stats_store: Arc<StatsStore>) -> BlockStats {
BlockStats::new(
stats_store.get(&MINER_STAT_ACCEPTED_BLOCKS_COUNT.to_string()).await,
stats_store.get(&MINER_STAT_REJECTED_BLOCKS_COUNT.to_string()).await,
)
}

async fn p2pool_block_stats(stats_store: Arc<StatsStore>) -> BlockStats {
BlockStats::new(
stats_store.get(&P2POOL_STAT_ACCEPTED_BLOCKS_COUNT.to_string()).await,
stats_store.get(&P2POOL_STAT_REJECTED_BLOCKS_COUNT.to_string()).await,
)
}
5 changes: 5 additions & 0 deletions src/server/http/stats/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// Copyright 2024 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

pub const MINER_STAT_ACCEPTED_BLOCKS_COUNT: &str = "miner_accepted_blocks_count";
pub const MINER_STAT_REJECTED_BLOCKS_COUNT: &str = "miner_rejected_blocks_count";
pub const P2POOL_STAT_ACCEPTED_BLOCKS_COUNT: &str = "p2pool_accepted_blocks_count";
pub const P2POOL_STAT_REJECTED_BLOCKS_COUNT: &str = "p2pool_rejected_blocks_count";

pub mod handlers;
pub mod models;
pub mod server;
19 changes: 19 additions & 0 deletions src/server/http/stats/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ impl EstimatedEarnings {
}
}

#[derive(Serialize, Deserialize)]
pub struct BlockStats {
pub accepted: u64,
pub rejected: u64,
pub submitted: u64,
}

impl BlockStats {
pub fn new(accepted: u64, rejected: u64) -> Self {
Self {
accepted,
rejected,
submitted: accepted + rejected,
}
}
}

#[derive(Serialize, Deserialize)]
pub struct Stats {
pub connected: bool,
Expand All @@ -68,4 +85,6 @@ pub struct Stats {
pub pool_total_estimated_earnings: EstimatedEarnings,
pub total_earnings: HashMap<String, u64>,
pub estimated_earnings: HashMap<String, EstimatedEarnings>,
pub miner_block_stats: BlockStats,
pub p2pool_block_stats: BlockStats,
}
7 changes: 6 additions & 1 deletion src/server/http/stats/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use tokio::io;

use crate::server::http::stats::handlers;
use crate::server::p2p::peer_store::PeerStore;
use crate::server::stats_store::StatsStore;
use crate::sharechain::ShareChain;

const LOG_TARGET: &str = "p2pool::server::stats";
Expand Down Expand Up @@ -42,23 +43,26 @@ where
{
share_chain: Arc<S>,
peer_store: Arc<PeerStore>,
stats_store: Arc<StatsStore>,
port: u16,
}

#[derive(Clone)]
pub struct AppState {
pub share_chain: Arc<dyn ShareChain>,
pub peer_store: Arc<PeerStore>,
pub stats_store: Arc<StatsStore>,
}

impl<S> StatsServer<S>
where
S: ShareChain,
{
pub fn new(share_chain: Arc<S>, peer_store: Arc<PeerStore>, port: u16) -> Self {
pub fn new(share_chain: Arc<S>, peer_store: Arc<PeerStore>, stats_store: Arc<StatsStore>, port: u16) -> Self {
Self {
share_chain,
peer_store,
stats_store,
port,
}
}
Expand All @@ -70,6 +74,7 @@ where
.with_state(AppState {
share_chain: self.share_chain.clone(),
peer_store: self.peer_store.clone(),
stats_store: self.stats_store.clone(),
})
}

Expand Down
1 change: 1 addition & 0 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ mod server;
pub mod grpc;
pub mod http;
pub mod p2p;
pub mod stats_store;
4 changes: 4 additions & 0 deletions src/server/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use thiserror::Error;

use crate::server::http::stats::server::StatsServer;
use crate::server::p2p::peer_store::PeerStore;
use crate::server::stats_store::StatsStore;
use crate::{
server::{
config, grpc,
Expand Down Expand Up @@ -56,6 +57,7 @@ where
let share_chain = Arc::new(share_chain);
let sync_in_progress = Arc::new(AtomicBool::new(true));
let peer_store = Arc::new(PeerStore::new(&config.peer_store));
let stats_store = Arc::new(StatsStore::new());

let mut p2p_service: p2p::Service<S> = p2p::Service::new(
&config,
Expand All @@ -78,6 +80,7 @@ where
config.base_node_address.clone(),
p2p_service.client(),
share_chain.clone(),
stats_store.clone(),
)
.await
.map_err(Error::Grpc)?;
Expand All @@ -88,6 +91,7 @@ where
Some(Arc::new(StatsServer::new(
share_chain.clone(),
peer_store.clone(),
stats_store.clone(),
config.stats_server.port,
)))
} else {
Expand Down
39 changes: 39 additions & 0 deletions src/server/stats_store.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2024 The Tari Project
// SPDX-License-Identifier: BSD-3-Clause

use std::collections::HashMap;

use tokio::sync::RwLock;

pub struct StatsStore {
stats: RwLock<HashMap<String, u64>>,
}

impl StatsStore {
pub fn new() -> Self {
Self {
stats: RwLock::new(HashMap::new()),
}
}

/// Returns one stat by [`key`].
pub async fn get(&self, key: &String) -> u64 {
let read_lock = self.stats.read().await;
read_lock.get(key).copied().unwrap_or(0)
}

/// Increments stat with given key.
/// If the value is not found by key, simply create new value.
pub async fn inc(&self, key: &String, by: u64) {
let mut write_lock = self.stats.write().await;
match write_lock.get(key) {
Some(stat) => {
let value = stat + by;
write_lock.insert(key.clone(), value);
},
None => {
write_lock.insert(key.clone(), by);
},
}
}
}

0 comments on commit 4363494

Please sign in to comment.