Report validator rewards in getConfirmedBlock JSON RPC
This commit is contained in:
parent
0bbee9456f
commit
72b11081a4
|
@ -303,6 +303,9 @@ The result field will be an object with the following fields:
|
||||||
* `fee: <u64>` - fee this transaction was charged, as u64 integer
|
* `fee: <u64>` - fee this transaction was charged, as u64 integer
|
||||||
* `preBalances: <array>` - array of u64 account balances from before the transaction was processed
|
* `preBalances: <array>` - array of u64 account balances from before the transaction was processed
|
||||||
* `postBalances: <array>` - array of u64 account balances after the transaction was processed
|
* `postBalances: <array>` - array of u64 account balances after the transaction was processed
|
||||||
|
* `rewards: <array>` - an array of JSON objects containing:
|
||||||
|
* `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward
|
||||||
|
* `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64
|
||||||
|
|
||||||
#### Example:
|
#### Example:
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,14 @@ pub struct RpcBlockCommitment<T> {
|
||||||
pub total_stake: u64,
|
pub total_stake: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct RpcReward {
|
||||||
|
pub pubkey: String,
|
||||||
|
pub lamports: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RpcRewards = Vec<RpcReward>;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct RpcConfirmedBlock {
|
pub struct RpcConfirmedBlock {
|
||||||
|
@ -39,6 +47,7 @@ pub struct RpcConfirmedBlock {
|
||||||
pub blockhash: String,
|
pub blockhash: String,
|
||||||
pub parent_slot: Slot,
|
pub parent_slot: Slot,
|
||||||
pub transactions: Vec<RpcTransactionWithStatusMeta>,
|
pub transactions: Vec<RpcTransactionWithStatusMeta>,
|
||||||
|
pub rewards: RpcRewards,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
|
|
@ -36,6 +36,7 @@ pub mod repair_service;
|
||||||
pub mod replay_stage;
|
pub mod replay_stage;
|
||||||
mod result;
|
mod result;
|
||||||
pub mod retransmit_stage;
|
pub mod retransmit_stage;
|
||||||
|
pub mod rewards_recorder_service;
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
pub mod rpc_pubsub;
|
pub mod rpc_pubsub;
|
||||||
pub mod rpc_pubsub_service;
|
pub mod rpc_pubsub_service;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
||||||
consensus::{StakeLockout, Tower},
|
consensus::{StakeLockout, Tower},
|
||||||
poh_recorder::PohRecorder,
|
poh_recorder::PohRecorder,
|
||||||
result::Result,
|
result::Result,
|
||||||
|
rewards_recorder_service::RewardsRecorderSender,
|
||||||
rpc_subscriptions::RpcSubscriptions,
|
rpc_subscriptions::RpcSubscriptions,
|
||||||
};
|
};
|
||||||
use solana_ledger::{
|
use solana_ledger::{
|
||||||
|
@ -77,6 +78,7 @@ pub struct ReplayStageConfig {
|
||||||
pub snapshot_package_sender: Option<SnapshotPackageSender>,
|
pub snapshot_package_sender: Option<SnapshotPackageSender>,
|
||||||
pub block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
pub block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
||||||
pub transaction_status_sender: Option<TransactionStatusSender>,
|
pub transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
|
pub rewards_sender: Option<RewardsRecorderSender>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReplayStage {
|
pub struct ReplayStage {
|
||||||
|
@ -179,6 +181,7 @@ impl ReplayStage {
|
||||||
snapshot_package_sender,
|
snapshot_package_sender,
|
||||||
block_commitment_cache,
|
block_commitment_cache,
|
||||||
transaction_status_sender,
|
transaction_status_sender,
|
||||||
|
rewards_sender,
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
let (root_bank_sender, root_bank_receiver) = channel();
|
let (root_bank_sender, root_bank_receiver) = channel();
|
||||||
|
@ -219,6 +222,7 @@ impl ReplayStage {
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
&leader_schedule_cache,
|
&leader_schedule_cache,
|
||||||
&subscriptions,
|
&subscriptions,
|
||||||
|
rewards_sender.clone(),
|
||||||
);
|
);
|
||||||
datapoint_debug!(
|
datapoint_debug!(
|
||||||
"replay_stage-memory",
|
"replay_stage-memory",
|
||||||
|
@ -395,6 +399,7 @@ impl ReplayStage {
|
||||||
&poh_recorder,
|
&poh_recorder,
|
||||||
&leader_schedule_cache,
|
&leader_schedule_cache,
|
||||||
&subscriptions,
|
&subscriptions,
|
||||||
|
rewards_sender.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(bank) = poh_recorder.lock().unwrap().bank() {
|
if let Some(bank) = poh_recorder.lock().unwrap().bank() {
|
||||||
|
@ -468,6 +473,7 @@ impl ReplayStage {
|
||||||
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
poh_recorder: &Arc<Mutex<PohRecorder>>,
|
||||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||||
subscriptions: &Arc<RpcSubscriptions>,
|
subscriptions: &Arc<RpcSubscriptions>,
|
||||||
|
rewards_sender: Option<RewardsRecorderSender>,
|
||||||
) {
|
) {
|
||||||
// all the individual calls to poh_recorder.lock() are designed to
|
// all the individual calls to poh_recorder.lock() are designed to
|
||||||
// increase granularity, decrease contention
|
// increase granularity, decrease contention
|
||||||
|
@ -533,6 +539,7 @@ impl ReplayStage {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(Bank::new_from_parent(&parent, my_pubkey, poh_slot));
|
.insert(Bank::new_from_parent(&parent, my_pubkey, poh_slot));
|
||||||
|
|
||||||
|
Self::record_rewards(&tpu_bank, &rewards_sender);
|
||||||
poh_recorder.lock().unwrap().set_bank(&tpu_bank);
|
poh_recorder.lock().unwrap().set_bank(&tpu_bank);
|
||||||
} else {
|
} else {
|
||||||
error!("{} No next leader found", my_pubkey);
|
error!("{} No next leader found", my_pubkey);
|
||||||
|
@ -976,6 +983,7 @@ impl ReplayStage {
|
||||||
forks_lock: &RwLock<BankForks>,
|
forks_lock: &RwLock<BankForks>,
|
||||||
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
leader_schedule_cache: &Arc<LeaderScheduleCache>,
|
||||||
subscriptions: &Arc<RpcSubscriptions>,
|
subscriptions: &Arc<RpcSubscriptions>,
|
||||||
|
rewards_sender: Option<RewardsRecorderSender>,
|
||||||
) {
|
) {
|
||||||
// Find the next slot that chains to the old slot
|
// Find the next slot that chains to the old slot
|
||||||
let forks = forks_lock.read().unwrap();
|
let forks = forks_lock.read().unwrap();
|
||||||
|
@ -1011,10 +1019,10 @@ impl ReplayStage {
|
||||||
forks.root()
|
forks.root()
|
||||||
);
|
);
|
||||||
subscriptions.notify_slot(child_slot, parent_slot, forks.root());
|
subscriptions.notify_slot(child_slot, parent_slot, forks.root());
|
||||||
new_banks.insert(
|
|
||||||
child_slot,
|
let child_bank = Bank::new_from_parent(&parent_bank, &leader, child_slot);
|
||||||
Bank::new_from_parent(&parent_bank, &leader, child_slot),
|
Self::record_rewards(&child_bank, &rewards_sender);
|
||||||
);
|
new_banks.insert(child_slot, child_bank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drop(forks);
|
drop(forks);
|
||||||
|
@ -1025,6 +1033,16 @@ impl ReplayStage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn record_rewards(bank: &Bank, rewards_sender: &Option<RewardsRecorderSender>) {
|
||||||
|
if let Some(rewards_sender) = rewards_sender {
|
||||||
|
if let Some(ref rewards) = bank.rewards {
|
||||||
|
rewards_sender
|
||||||
|
.send((bank.slot(), rewards.iter().copied().collect()))
|
||||||
|
.unwrap_or_else(|err| warn!("rewards_sender failed: {:?}", err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn join(self) -> thread::Result<()> {
|
pub fn join(self) -> thread::Result<()> {
|
||||||
self.commitment_service.join()?;
|
self.commitment_service.join()?;
|
||||||
self.t_replay.join().map(|_| ())
|
self.t_replay.join().map(|_| ())
|
||||||
|
@ -1382,6 +1400,7 @@ pub(crate) mod tests {
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
&leader_schedule_cache,
|
&leader_schedule_cache,
|
||||||
&subscriptions,
|
&subscriptions,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert!(bank_forks.read().unwrap().get(1).is_some());
|
assert!(bank_forks.read().unwrap().get(1).is_some());
|
||||||
|
|
||||||
|
@ -1394,6 +1413,7 @@ pub(crate) mod tests {
|
||||||
&bank_forks,
|
&bank_forks,
|
||||||
&leader_schedule_cache,
|
&leader_schedule_cache,
|
||||||
&subscriptions,
|
&subscriptions,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert!(bank_forks.read().unwrap().get(1).is_some());
|
assert!(bank_forks.read().unwrap().get(1).is_some());
|
||||||
assert!(bank_forks.read().unwrap().get(2).is_some());
|
assert!(bank_forks.read().unwrap().get(2).is_some());
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
|
||||||
|
use solana_client::rpc_response::RpcReward;
|
||||||
|
use solana_ledger::blockstore::Blockstore;
|
||||||
|
use solana_sdk::{clock::Slot, pubkey::Pubkey};
|
||||||
|
use std::{
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
thread::{self, Builder, JoinHandle},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type RewardsRecorderReceiver = Receiver<(Slot, Vec<(Pubkey, i64)>)>;
|
||||||
|
pub type RewardsRecorderSender = Sender<(Slot, Vec<(Pubkey, i64)>)>;
|
||||||
|
|
||||||
|
pub struct RewardsRecorderService {
|
||||||
|
thread_hdl: JoinHandle<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RewardsRecorderService {
|
||||||
|
#[allow(clippy::new_ret_no_self)]
|
||||||
|
pub fn new(
|
||||||
|
rewards_receiver: RewardsRecorderReceiver,
|
||||||
|
blockstore: Arc<Blockstore>,
|
||||||
|
exit: &Arc<AtomicBool>,
|
||||||
|
) -> Self {
|
||||||
|
let exit = exit.clone();
|
||||||
|
let thread_hdl = Builder::new()
|
||||||
|
.name("solana-rewards-writer".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
if exit.load(Ordering::Relaxed) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if let Err(RecvTimeoutError::Disconnected) =
|
||||||
|
Self::write_rewards(&rewards_receiver, &blockstore)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
Self { thread_hdl }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_rewards(
|
||||||
|
rewards_receiver: &RewardsRecorderReceiver,
|
||||||
|
blockstore: &Arc<Blockstore>,
|
||||||
|
) -> Result<(), RecvTimeoutError> {
|
||||||
|
let (slot, rewards) = rewards_receiver.recv_timeout(Duration::from_secs(1))?;
|
||||||
|
let rpc_rewards = rewards
|
||||||
|
.into_iter()
|
||||||
|
.map(|(pubkey, lamports)| RpcReward {
|
||||||
|
pubkey: pubkey.to_string(),
|
||||||
|
lamports,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
blockstore
|
||||||
|
.write_rewards(slot, rpc_rewards)
|
||||||
|
.expect("Expect database write to succeed");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn join(self) -> thread::Result<()> {
|
||||||
|
self.thread_hdl.join()
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ use crate::{
|
||||||
poh_recorder::PohRecorder,
|
poh_recorder::PohRecorder,
|
||||||
replay_stage::{ReplayStage, ReplayStageConfig},
|
replay_stage::{ReplayStage, ReplayStageConfig},
|
||||||
retransmit_stage::RetransmitStage,
|
retransmit_stage::RetransmitStage,
|
||||||
|
rewards_recorder_service::RewardsRecorderSender,
|
||||||
rpc_subscriptions::RpcSubscriptions,
|
rpc_subscriptions::RpcSubscriptions,
|
||||||
shred_fetch_stage::ShredFetchStage,
|
shred_fetch_stage::ShredFetchStage,
|
||||||
sigverify_shreds::ShredSigVerifier,
|
sigverify_shreds::ShredSigVerifier,
|
||||||
|
@ -86,6 +87,7 @@ impl Tvu {
|
||||||
cfg: Option<Arc<AtomicBool>>,
|
cfg: Option<Arc<AtomicBool>>,
|
||||||
shred_version: u16,
|
shred_version: u16,
|
||||||
transaction_status_sender: Option<TransactionStatusSender>,
|
transaction_status_sender: Option<TransactionStatusSender>,
|
||||||
|
rewards_sender: Option<RewardsRecorderSender>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let keypair: Arc<Keypair> = cluster_info
|
let keypair: Arc<Keypair> = cluster_info
|
||||||
.read()
|
.read()
|
||||||
|
@ -170,6 +172,7 @@ impl Tvu {
|
||||||
snapshot_package_sender,
|
snapshot_package_sender,
|
||||||
block_commitment_cache,
|
block_commitment_cache,
|
||||||
transaction_status_sender,
|
transaction_status_sender,
|
||||||
|
rewards_sender,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (replay_stage, root_bank_receiver) = ReplayStage::new(
|
let (replay_stage, root_bank_receiver) = ReplayStage::new(
|
||||||
|
@ -312,6 +315,7 @@ pub mod tests {
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
exit.store(true, Ordering::Relaxed);
|
exit.store(true, Ordering::Relaxed);
|
||||||
tvu.join().unwrap();
|
tvu.join().unwrap();
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
gossip_service::{discover_cluster, GossipService},
|
gossip_service::{discover_cluster, GossipService},
|
||||||
poh_recorder::PohRecorder,
|
poh_recorder::PohRecorder,
|
||||||
poh_service::PohService,
|
poh_service::PohService,
|
||||||
|
rewards_recorder_service::RewardsRecorderService,
|
||||||
rpc::JsonRpcConfig,
|
rpc::JsonRpcConfig,
|
||||||
rpc_pubsub_service::PubSubService,
|
rpc_pubsub_service::PubSubService,
|
||||||
rpc_service::JsonRpcService,
|
rpc_service::JsonRpcService,
|
||||||
|
@ -122,6 +123,7 @@ pub struct Validator {
|
||||||
validator_exit: Arc<RwLock<Option<ValidatorExit>>>,
|
validator_exit: Arc<RwLock<Option<ValidatorExit>>>,
|
||||||
rpc_service: Option<(JsonRpcService, PubSubService)>,
|
rpc_service: Option<(JsonRpcService, PubSubService)>,
|
||||||
transaction_status_service: Option<TransactionStatusService>,
|
transaction_status_service: Option<TransactionStatusService>,
|
||||||
|
rewards_recorder_service: Option<RewardsRecorderService>,
|
||||||
gossip_service: GossipService,
|
gossip_service: GossipService,
|
||||||
serve_repair_service: ServeRepairService,
|
serve_repair_service: ServeRepairService,
|
||||||
poh_recorder: Arc<Mutex<PohRecorder>>,
|
poh_recorder: Arc<Mutex<PohRecorder>>,
|
||||||
|
@ -268,6 +270,21 @@ impl Validator {
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (rewards_sender, rewards_recorder_service) =
|
||||||
|
if rpc_service.is_some() && !config.transaction_status_service_disabled {
|
||||||
|
let (rewards_sender, rewards_receiver) = unbounded();
|
||||||
|
(
|
||||||
|
Some(rewards_sender),
|
||||||
|
Some(RewardsRecorderService::new(
|
||||||
|
rewards_receiver,
|
||||||
|
blockstore.clone(),
|
||||||
|
&exit,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"Starting PoH: epoch={} slot={} tick_height={} blockhash={} leader={:?}",
|
"Starting PoH: epoch={} slot={} tick_height={} blockhash={} leader={:?}",
|
||||||
bank.epoch(),
|
bank.epoch(),
|
||||||
|
@ -388,6 +405,7 @@ impl Validator {
|
||||||
config.enable_partition.clone(),
|
config.enable_partition.clone(),
|
||||||
node.info.shred_version,
|
node.info.shred_version,
|
||||||
transaction_status_sender.clone(),
|
transaction_status_sender.clone(),
|
||||||
|
rewards_sender,
|
||||||
);
|
);
|
||||||
|
|
||||||
if config.dev_sigverify_disabled {
|
if config.dev_sigverify_disabled {
|
||||||
|
@ -416,6 +434,7 @@ impl Validator {
|
||||||
serve_repair_service,
|
serve_repair_service,
|
||||||
rpc_service,
|
rpc_service,
|
||||||
transaction_status_service,
|
transaction_status_service,
|
||||||
|
rewards_recorder_service,
|
||||||
tpu,
|
tpu,
|
||||||
tvu,
|
tvu,
|
||||||
poh_service,
|
poh_service,
|
||||||
|
@ -473,6 +492,10 @@ impl Validator {
|
||||||
transaction_status_service.join()?;
|
transaction_status_service.join()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(rewards_recorder_service) = self.rewards_recorder_service {
|
||||||
|
rewards_recorder_service.join()?;
|
||||||
|
}
|
||||||
|
|
||||||
self.gossip_service.join()?;
|
self.gossip_service.join()?;
|
||||||
self.serve_repair_service.join()?;
|
self.serve_repair_service.join()?;
|
||||||
self.tpu.join()?;
|
self.tpu.join()?;
|
||||||
|
|
|
@ -22,8 +22,8 @@ use rayon::{
|
||||||
};
|
};
|
||||||
use rocksdb::DBRawIterator;
|
use rocksdb::DBRawIterator;
|
||||||
use solana_client::rpc_response::{
|
use solana_client::rpc_response::{
|
||||||
RpcConfirmedBlock, RpcEncodedTransaction, RpcTransactionEncoding, RpcTransactionStatus,
|
RpcConfirmedBlock, RpcEncodedTransaction, RpcRewards, RpcTransactionEncoding,
|
||||||
RpcTransactionWithStatusMeta,
|
RpcTransactionStatus, RpcTransactionWithStatusMeta,
|
||||||
};
|
};
|
||||||
use solana_measure::measure::Measure;
|
use solana_measure::measure::Measure;
|
||||||
use solana_metrics::{datapoint_debug, datapoint_error};
|
use solana_metrics::{datapoint_debug, datapoint_error};
|
||||||
|
@ -86,6 +86,7 @@ pub struct Blockstore {
|
||||||
data_shred_cf: LedgerColumn<cf::ShredData>,
|
data_shred_cf: LedgerColumn<cf::ShredData>,
|
||||||
code_shred_cf: LedgerColumn<cf::ShredCode>,
|
code_shred_cf: LedgerColumn<cf::ShredCode>,
|
||||||
transaction_status_cf: LedgerColumn<cf::TransactionStatus>,
|
transaction_status_cf: LedgerColumn<cf::TransactionStatus>,
|
||||||
|
rewards_cf: LedgerColumn<cf::Rewards>,
|
||||||
last_root: Arc<RwLock<Slot>>,
|
last_root: Arc<RwLock<Slot>>,
|
||||||
insert_shreds_lock: Arc<Mutex<()>>,
|
insert_shreds_lock: Arc<Mutex<()>>,
|
||||||
pub new_shreds_signals: Vec<SyncSender<bool>>,
|
pub new_shreds_signals: Vec<SyncSender<bool>>,
|
||||||
|
@ -195,6 +196,7 @@ impl Blockstore {
|
||||||
let data_shred_cf = db.column();
|
let data_shred_cf = db.column();
|
||||||
let code_shred_cf = db.column();
|
let code_shred_cf = db.column();
|
||||||
let transaction_status_cf = db.column();
|
let transaction_status_cf = db.column();
|
||||||
|
let rewards_cf = db.column();
|
||||||
|
|
||||||
let db = Arc::new(db);
|
let db = Arc::new(db);
|
||||||
|
|
||||||
|
@ -219,6 +221,7 @@ impl Blockstore {
|
||||||
data_shred_cf,
|
data_shred_cf,
|
||||||
code_shred_cf,
|
code_shred_cf,
|
||||||
transaction_status_cf,
|
transaction_status_cf,
|
||||||
|
rewards_cf,
|
||||||
new_shreds_signals: vec![],
|
new_shreds_signals: vec![],
|
||||||
completed_slots_senders: vec![],
|
completed_slots_senders: vec![],
|
||||||
insert_shreds_lock: Arc::new(Mutex::new(())),
|
insert_shreds_lock: Arc::new(Mutex::new(())),
|
||||||
|
@ -346,6 +349,10 @@ impl Blockstore {
|
||||||
& self
|
& self
|
||||||
.db
|
.db
|
||||||
.delete_range_cf::<cf::TransactionStatus>(&mut write_batch, from_slot, to_slot)
|
.delete_range_cf::<cf::TransactionStatus>(&mut write_batch, from_slot, to_slot)
|
||||||
|
.unwrap_or(false)
|
||||||
|
& self
|
||||||
|
.db
|
||||||
|
.delete_range_cf::<cf::Rewards>(&mut write_batch, from_slot, to_slot)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
if let Err(e) = self.db.write(write_batch) {
|
if let Err(e) = self.db.write(write_batch) {
|
||||||
error!(
|
error!(
|
||||||
|
@ -398,6 +405,10 @@ impl Blockstore {
|
||||||
&& self
|
&& self
|
||||||
.transaction_status_cf
|
.transaction_status_cf
|
||||||
.compact_range(from_slot, to_slot)
|
.compact_range(from_slot, to_slot)
|
||||||
|
.unwrap_or(false)
|
||||||
|
&& self
|
||||||
|
.rewards_cf
|
||||||
|
.compact_range(from_slot, to_slot)
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
@ -1396,6 +1407,12 @@ impl Blockstore {
|
||||||
let blockhash = get_last_hash(slot_entries.iter())
|
let blockhash = get_last_hash(slot_entries.iter())
|
||||||
.unwrap_or_else(|| panic!("Rooted slot {:?} must have blockhash", slot));
|
.unwrap_or_else(|| panic!("Rooted slot {:?} must have blockhash", slot));
|
||||||
|
|
||||||
|
let rewards = self
|
||||||
|
.rewards_cf
|
||||||
|
.get(slot)
|
||||||
|
.expect("Expect rewards get to succeed")
|
||||||
|
.unwrap_or_else(|| vec![]);
|
||||||
|
|
||||||
let block = RpcConfirmedBlock {
|
let block = RpcConfirmedBlock {
|
||||||
previous_blockhash: previous_blockhash.to_string(),
|
previous_blockhash: previous_blockhash.to_string(),
|
||||||
blockhash: blockhash.to_string(),
|
blockhash: blockhash.to_string(),
|
||||||
|
@ -1405,6 +1422,7 @@ impl Blockstore {
|
||||||
encoding,
|
encoding,
|
||||||
slot_transaction_iterator,
|
slot_transaction_iterator,
|
||||||
),
|
),
|
||||||
|
rewards,
|
||||||
};
|
};
|
||||||
return Ok(block);
|
return Ok(block);
|
||||||
}
|
}
|
||||||
|
@ -1442,6 +1460,10 @@ impl Blockstore {
|
||||||
self.transaction_status_cf.put(index, status)
|
self.transaction_status_cf.put(index, status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_rewards(&self, index: Slot, rewards: RpcRewards) -> Result<()> {
|
||||||
|
self.rewards_cf.put(index, &rewards)
|
||||||
|
}
|
||||||
|
|
||||||
fn get_block_timestamps(&self, slot: Slot) -> Result<Vec<(Pubkey, (Slot, UnixTimestamp))>> {
|
fn get_block_timestamps(&self, slot: Slot) -> Result<Vec<(Pubkey, (Slot, UnixTimestamp))>> {
|
||||||
let slot_entries = self.get_slot_entries(slot, 0, None)?;
|
let slot_entries = self.get_slot_entries(slot, 0, None)?;
|
||||||
Ok(slot_entries
|
Ok(slot_entries
|
||||||
|
@ -2574,6 +2596,13 @@ pub mod tests {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.next()
|
.next()
|
||||||
.map(|((slot, _), _)| slot >= min_slot)
|
.map(|((slot, _), _)| slot >= min_slot)
|
||||||
|
.unwrap_or(true)
|
||||||
|
& blockstore
|
||||||
|
.db
|
||||||
|
.iter::<cf::Rewards>(IteratorMode::Start)
|
||||||
|
.unwrap()
|
||||||
|
.next()
|
||||||
|
.map(|(slot, _)| slot >= min_slot)
|
||||||
.unwrap_or(true);
|
.unwrap_or(true);
|
||||||
assert!(condition_met);
|
assert!(condition_met);
|
||||||
}
|
}
|
||||||
|
@ -4826,6 +4855,7 @@ pub mod tests {
|
||||||
parent_slot: slot - 1,
|
parent_slot: slot - 1,
|
||||||
blockhash: blockhash.to_string(),
|
blockhash: blockhash.to_string(),
|
||||||
previous_blockhash: Hash::default().to_string(),
|
previous_blockhash: Hash::default().to_string(),
|
||||||
|
rewards: vec![],
|
||||||
};
|
};
|
||||||
// The previous_blockhash of `expected_block` is default because its parent slot is a
|
// The previous_blockhash of `expected_block` is default because its parent slot is a
|
||||||
// root, but empty of entries. This is special handling for snapshot root slots.
|
// root, but empty of entries. This is special handling for snapshot root slots.
|
||||||
|
@ -4846,6 +4876,7 @@ pub mod tests {
|
||||||
parent_slot: slot,
|
parent_slot: slot,
|
||||||
blockhash: blockhash.to_string(),
|
blockhash: blockhash.to_string(),
|
||||||
previous_blockhash: blockhash.to_string(),
|
previous_blockhash: blockhash.to_string(),
|
||||||
|
rewards: vec![],
|
||||||
};
|
};
|
||||||
assert_eq!(confirmed_block, expected_block);
|
assert_eq!(confirmed_block, expected_block);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ use rocksdb::{
|
||||||
};
|
};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use solana_client::rpc_response::RpcTransactionStatus;
|
use solana_client::rpc_response::{RpcRewards, RpcTransactionStatus};
|
||||||
use solana_sdk::{clock::Slot, signature::Signature};
|
use solana_sdk::{clock::Slot, signature::Signature};
|
||||||
use std::{collections::HashMap, fs, marker::PhantomData, path::Path, sync::Arc};
|
use std::{collections::HashMap, fs, marker::PhantomData, path::Path, sync::Arc};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -38,6 +38,8 @@ const DATA_SHRED_CF: &str = "data_shred";
|
||||||
const CODE_SHRED_CF: &str = "code_shred";
|
const CODE_SHRED_CF: &str = "code_shred";
|
||||||
/// Column family for Transaction Status
|
/// Column family for Transaction Status
|
||||||
const TRANSACTION_STATUS_CF: &str = "transaction_status";
|
const TRANSACTION_STATUS_CF: &str = "transaction_status";
|
||||||
|
/// Column family for Rewards
|
||||||
|
const REWARDS_CF: &str = "rewards";
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum BlockstoreError {
|
pub enum BlockstoreError {
|
||||||
|
@ -105,6 +107,10 @@ pub mod columns {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
/// The transaction status column
|
/// The transaction status column
|
||||||
pub struct TransactionStatus;
|
pub struct TransactionStatus;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// The rewards column
|
||||||
|
pub struct Rewards;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -113,8 +119,8 @@ struct Rocks(rocksdb::DB);
|
||||||
impl Rocks {
|
impl Rocks {
|
||||||
fn open(path: &Path) -> Result<Rocks> {
|
fn open(path: &Path) -> Result<Rocks> {
|
||||||
use columns::{
|
use columns::{
|
||||||
DeadSlots, DuplicateSlots, ErasureMeta, Index, Orphans, Root, ShredCode, ShredData,
|
DeadSlots, DuplicateSlots, ErasureMeta, Index, Orphans, Rewards, Root, ShredCode,
|
||||||
SlotMeta, TransactionStatus,
|
ShredData, SlotMeta, TransactionStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
fs::create_dir_all(&path)?;
|
fs::create_dir_all(&path)?;
|
||||||
|
@ -139,6 +145,7 @@ impl Rocks {
|
||||||
ColumnFamilyDescriptor::new(ShredCode::NAME, get_cf_options());
|
ColumnFamilyDescriptor::new(ShredCode::NAME, get_cf_options());
|
||||||
let transaction_status_cf_descriptor =
|
let transaction_status_cf_descriptor =
|
||||||
ColumnFamilyDescriptor::new(TransactionStatus::NAME, get_cf_options());
|
ColumnFamilyDescriptor::new(TransactionStatus::NAME, get_cf_options());
|
||||||
|
let rewards_cf_descriptor = ColumnFamilyDescriptor::new(Rewards::NAME, get_cf_options());
|
||||||
|
|
||||||
let cfs = vec![
|
let cfs = vec![
|
||||||
meta_cf_descriptor,
|
meta_cf_descriptor,
|
||||||
|
@ -151,6 +158,7 @@ impl Rocks {
|
||||||
shred_data_cf_descriptor,
|
shred_data_cf_descriptor,
|
||||||
shred_code_cf_descriptor,
|
shred_code_cf_descriptor,
|
||||||
transaction_status_cf_descriptor,
|
transaction_status_cf_descriptor,
|
||||||
|
rewards_cf_descriptor,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Open the database
|
// Open the database
|
||||||
|
@ -161,8 +169,8 @@ impl Rocks {
|
||||||
|
|
||||||
fn columns(&self) -> Vec<&'static str> {
|
fn columns(&self) -> Vec<&'static str> {
|
||||||
use columns::{
|
use columns::{
|
||||||
DeadSlots, DuplicateSlots, ErasureMeta, Index, Orphans, Root, ShredCode, ShredData,
|
DeadSlots, DuplicateSlots, ErasureMeta, Index, Orphans, Rewards, Root, ShredCode,
|
||||||
SlotMeta, TransactionStatus,
|
ShredData, SlotMeta, TransactionStatus,
|
||||||
};
|
};
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
|
@ -176,6 +184,7 @@ impl Rocks {
|
||||||
ShredData::NAME,
|
ShredData::NAME,
|
||||||
ShredCode::NAME,
|
ShredCode::NAME,
|
||||||
TransactionStatus::NAME,
|
TransactionStatus::NAME,
|
||||||
|
Rewards::NAME,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,6 +325,14 @@ impl ColumnName for columns::TransactionStatus {
|
||||||
const NAME: &'static str = TRANSACTION_STATUS_CF;
|
const NAME: &'static str = TRANSACTION_STATUS_CF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SlotColumn for columns::Rewards {}
|
||||||
|
impl ColumnName for columns::Rewards {
|
||||||
|
const NAME: &'static str = REWARDS_CF;
|
||||||
|
}
|
||||||
|
impl TypedColumn for columns::Rewards {
|
||||||
|
type Type = RpcRewards;
|
||||||
|
}
|
||||||
|
|
||||||
impl Column for columns::ShredCode {
|
impl Column for columns::ShredCode {
|
||||||
type Index = (u64, u64);
|
type Index = (u64, u64);
|
||||||
|
|
||||||
|
|
|
@ -776,7 +776,7 @@ pub fn redeem_rewards(
|
||||||
vote_account: &mut Account,
|
vote_account: &mut Account,
|
||||||
point_value: f64,
|
point_value: f64,
|
||||||
stake_history: Option<&StakeHistory>,
|
stake_history: Option<&StakeHistory>,
|
||||||
) -> Result<u64, InstructionError> {
|
) -> Result<(u64, u64), InstructionError> {
|
||||||
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
if let StakeState::Stake(meta, mut stake) = stake_account.state()? {
|
||||||
let vote_state = vote_account.state()?;
|
let vote_state = vote_account.state()?;
|
||||||
|
|
||||||
|
@ -788,7 +788,7 @@ pub fn redeem_rewards(
|
||||||
|
|
||||||
stake_account.set_state(&StakeState::Stake(meta, stake))?;
|
stake_account.set_state(&StakeState::Stake(meta, stake))?;
|
||||||
|
|
||||||
Ok(stakers_reward + voters_reward)
|
Ok((stakers_reward, voters_reward))
|
||||||
} else {
|
} else {
|
||||||
Err(StakeError::NoCreditsToRedeem.into())
|
Err(StakeError::NoCreditsToRedeem.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -333,6 +333,10 @@ pub struct Bank {
|
||||||
/// Last time when the cluster info vote listener has synced with this bank
|
/// Last time when the cluster info vote listener has synced with this bank
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub last_vote_sync: AtomicU64,
|
pub last_vote_sync: AtomicU64,
|
||||||
|
|
||||||
|
/// Rewards that were paid out immediately after this bank was created
|
||||||
|
#[serde(skip)]
|
||||||
|
pub rewards: Option<Vec<(Pubkey, i64)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for BlockhashQueue {
|
impl Default for BlockhashQueue {
|
||||||
|
@ -429,6 +433,7 @@ impl Bank {
|
||||||
entered_epoch_callback: parent.entered_epoch_callback.clone(),
|
entered_epoch_callback: parent.entered_epoch_callback.clone(),
|
||||||
hard_forks: parent.hard_forks.clone(),
|
hard_forks: parent.hard_forks.clone(),
|
||||||
last_vote_sync: AtomicU64::new(parent.last_vote_sync.load(Ordering::Relaxed)),
|
last_vote_sync: AtomicU64::new(parent.last_vote_sync.load(Ordering::Relaxed)),
|
||||||
|
rewards: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
datapoint_debug!(
|
datapoint_debug!(
|
||||||
|
@ -614,15 +619,16 @@ impl Bank {
|
||||||
// years_elapsed = slots_elapsed / slots/year
|
// years_elapsed = slots_elapsed / slots/year
|
||||||
let period = self.epoch_schedule.get_slots_in_epoch(epoch) as f64 / self.slots_per_year;
|
let period = self.epoch_schedule.get_slots_in_epoch(epoch) as f64 / self.slots_per_year;
|
||||||
|
|
||||||
|
let (validator_rewards, storage_rewards) = {
|
||||||
let inflation = self.inflation.read().unwrap();
|
let inflation = self.inflation.read().unwrap();
|
||||||
|
|
||||||
let validator_rewards =
|
(
|
||||||
(*inflation).validator(year) * self.capitalization() as f64 * period;
|
(*inflation).validator(year) * self.capitalization() as f64 * period,
|
||||||
|
(*inflation).storage(year) * self.capitalization() as f64 * period,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let validator_points = self.stakes.write().unwrap().claim_points();
|
let validator_points = self.stakes.write().unwrap().claim_points();
|
||||||
|
|
||||||
let storage_rewards = (*inflation).storage(year) * self.capitalization() as f64 * period;
|
|
||||||
|
|
||||||
let storage_points = self.storage_accounts.write().unwrap().claim_points();
|
let storage_points = self.storage_accounts.write().unwrap().claim_points();
|
||||||
|
|
||||||
let (validator_point_value, storage_point_value) = self.check_point_values(
|
let (validator_point_value, storage_point_value) = self.check_point_values(
|
||||||
|
@ -634,7 +640,6 @@ impl Bank {
|
||||||
});
|
});
|
||||||
|
|
||||||
let validator_rewards = self.pay_validator_rewards(validator_point_value);
|
let validator_rewards = self.pay_validator_rewards(validator_point_value);
|
||||||
|
|
||||||
self.capitalization.fetch_add(
|
self.capitalization.fetch_add(
|
||||||
validator_rewards + storage_rewards as u64,
|
validator_rewards + storage_rewards as u64,
|
||||||
Ordering::Relaxed,
|
Ordering::Relaxed,
|
||||||
|
@ -643,9 +648,12 @@ impl Bank {
|
||||||
|
|
||||||
/// iterate over all stakes, redeem vote credits for each stake we can
|
/// iterate over all stakes, redeem vote credits for each stake we can
|
||||||
/// successfully load and parse, return total payout
|
/// successfully load and parse, return total payout
|
||||||
fn pay_validator_rewards(&self, point_value: f64) -> u64 {
|
fn pay_validator_rewards(&mut self, point_value: f64) -> u64 {
|
||||||
let stake_history = self.stakes.read().unwrap().history().clone();
|
let stake_history = self.stakes.read().unwrap().history().clone();
|
||||||
self.stake_delegations()
|
let mut validator_rewards = HashMap::new();
|
||||||
|
|
||||||
|
let total_validator_rewards = self
|
||||||
|
.stake_delegations()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(stake_pubkey, delegation)| {
|
.map(|(stake_pubkey, delegation)| {
|
||||||
match (
|
match (
|
||||||
|
@ -659,10 +667,22 @@ impl Bank {
|
||||||
point_value,
|
point_value,
|
||||||
Some(&stake_history),
|
Some(&stake_history),
|
||||||
);
|
);
|
||||||
if let Ok(rewards) = rewards {
|
if let Ok((stakers_reward, voters_reward)) = rewards {
|
||||||
self.store_account(&stake_pubkey, &stake_account);
|
self.store_account(&stake_pubkey, &stake_account);
|
||||||
self.store_account(&delegation.voter_pubkey, &vote_account);
|
self.store_account(&delegation.voter_pubkey, &vote_account);
|
||||||
rewards
|
|
||||||
|
if voters_reward > 0 {
|
||||||
|
*validator_rewards
|
||||||
|
.entry(delegation.voter_pubkey)
|
||||||
|
.or_insert(0i64) += voters_reward as i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stakers_reward > 0 {
|
||||||
|
*validator_rewards.entry(*stake_pubkey).or_insert(0i64) +=
|
||||||
|
stakers_reward as i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
stakers_reward + voters_reward
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"stake_state::redeem_rewards() failed for {}: {:?}",
|
"stake_state::redeem_rewards() failed for {}: {:?}",
|
||||||
|
@ -674,7 +694,11 @@ impl Bank {
|
||||||
(_, _) => 0,
|
(_, _) => 0,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.sum()
|
.sum();
|
||||||
|
|
||||||
|
assert_eq!(self.rewards, None);
|
||||||
|
self.rewards = Some(validator_rewards.drain().collect());
|
||||||
|
total_validator_rewards
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_recent_blockhashes(&self) {
|
pub fn update_recent_blockhashes(&self) {
|
||||||
|
@ -3003,6 +3027,7 @@ mod tests {
|
||||||
..GenesisConfig::default()
|
..GenesisConfig::default()
|
||||||
}));
|
}));
|
||||||
assert_eq!(bank.capitalization(), 42 * 1_000_000_000);
|
assert_eq!(bank.capitalization(), 42 * 1_000_000_000);
|
||||||
|
assert_eq!(bank.rewards, None);
|
||||||
|
|
||||||
let ((vote_id, mut vote_account), (stake_id, stake_account)) =
|
let ((vote_id, mut vote_account), (stake_id, stake_account)) =
|
||||||
crate::stakes::tests::create_staked_node_accounts(1_0000);
|
crate::stakes::tests::create_staked_node_accounts(1_0000);
|
||||||
|
@ -3061,6 +3086,16 @@ mod tests {
|
||||||
.abs()
|
.abs()
|
||||||
< 1.0 // rounding, truncating
|
< 1.0 // rounding, truncating
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// verify validator rewards show up in bank1.rewards vector
|
||||||
|
// (currently storage rewards will not show up)
|
||||||
|
assert_eq!(
|
||||||
|
bank1.rewards,
|
||||||
|
Some(vec![(
|
||||||
|
stake_id,
|
||||||
|
(rewards.validator_point_value * validator_points as f64) as i64
|
||||||
|
)])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that purging 0 lamports accounts works.
|
// Test that purging 0 lamports accounts works.
|
||||||
|
|
Loading…
Reference in New Issue