Submit a vote timestamp every vote (#10630)
* Submit a timestamp for every vote * Submit at most one vote timestamp per second * Submit a timestamp for every new vote Co-authored-by: Tyera Eulberg <tyera@solana.com>
This commit is contained in:
parent
41868f8adb
commit
247f27af37
|
@ -13,9 +13,7 @@ use solana_sdk::{
|
||||||
};
|
};
|
||||||
use solana_vote_program::{
|
use solana_vote_program::{
|
||||||
vote_instruction,
|
vote_instruction,
|
||||||
vote_state::{
|
vote_state::{BlockTimestamp, Lockout, Vote, VoteState, MAX_LOCKOUT_HISTORY},
|
||||||
BlockTimestamp, Lockout, Vote, VoteState, MAX_LOCKOUT_HISTORY, TIMESTAMP_SLOT_INTERVAL,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap, HashSet},
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
|
@ -345,6 +343,20 @@ impl Tower {
|
||||||
last_vote
|
last_vote
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn maybe_timestamp(&mut self, current_slot: Slot) -> Option<UnixTimestamp> {
|
||||||
|
if current_slot > self.last_timestamp.slot {
|
||||||
|
let timestamp = Utc::now().timestamp();
|
||||||
|
if timestamp >= self.last_timestamp.timestamp {
|
||||||
|
self.last_timestamp = BlockTimestamp {
|
||||||
|
slot: current_slot,
|
||||||
|
timestamp,
|
||||||
|
};
|
||||||
|
return Some(timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root(&self) -> Option<Slot> {
|
pub fn root(&self) -> Option<Slot> {
|
||||||
self.lockouts.root_slot
|
self.lockouts.root_slot
|
||||||
}
|
}
|
||||||
|
@ -650,21 +662,6 @@ impl Tower {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_timestamp(&mut self, current_slot: Slot) -> Option<UnixTimestamp> {
|
|
||||||
if self.last_timestamp.slot == 0
|
|
||||||
|| self.last_timestamp.slot < (current_slot - (current_slot % TIMESTAMP_SLOT_INTERVAL))
|
|
||||||
{
|
|
||||||
let timestamp = Utc::now().timestamp();
|
|
||||||
self.last_timestamp = BlockTimestamp {
|
|
||||||
slot: current_slot,
|
|
||||||
timestamp,
|
|
||||||
};
|
|
||||||
Some(timestamp)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -691,12 +688,7 @@ pub mod test {
|
||||||
vote_state::{Vote, VoteStateVersions, MAX_LOCKOUT_HISTORY},
|
vote_state::{Vote, VoteStateVersions, MAX_LOCKOUT_HISTORY},
|
||||||
vote_transaction,
|
vote_transaction,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{collections::HashMap, rc::Rc, sync::RwLock};
|
||||||
collections::HashMap,
|
|
||||||
rc::Rc,
|
|
||||||
sync::RwLock,
|
|
||||||
{thread::sleep, time::Duration},
|
|
||||||
};
|
|
||||||
use trees::{tr, Tree, TreeWalk};
|
use trees::{tr, Tree, TreeWalk};
|
||||||
|
|
||||||
pub(crate) struct VoteSimulator {
|
pub(crate) struct VoteSimulator {
|
||||||
|
@ -1829,17 +1821,15 @@ pub mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_maybe_timestamp() {
|
fn test_maybe_timestamp() {
|
||||||
let mut tower = Tower::default();
|
let mut tower = Tower::default();
|
||||||
assert!(tower.maybe_timestamp(TIMESTAMP_SLOT_INTERVAL).is_some());
|
assert!(tower.maybe_timestamp(0).is_none());
|
||||||
let BlockTimestamp { slot, timestamp } = tower.last_timestamp;
|
assert!(tower.maybe_timestamp(1).is_some());
|
||||||
|
assert!(tower.maybe_timestamp(0).is_none()); // Refuse to timestamp an older slot
|
||||||
|
assert!(tower.maybe_timestamp(1).is_none()); // Refuse to timestamp the same slot twice
|
||||||
|
|
||||||
assert_eq!(tower.maybe_timestamp(1), None);
|
tower.last_timestamp.timestamp -= 1; // Move last_timestamp into the past
|
||||||
assert_eq!(tower.maybe_timestamp(slot), None);
|
assert!(tower.maybe_timestamp(2).is_some()); // slot 2 gets a timestamp
|
||||||
assert_eq!(tower.maybe_timestamp(slot + 1), None);
|
|
||||||
|
|
||||||
sleep(Duration::from_secs(1));
|
tower.last_timestamp.timestamp += 1_000_000; // Move last_timestamp well into the future
|
||||||
assert!(tower
|
assert!(tower.maybe_timestamp(3).is_none()); // slot 3 gets no timestamp
|
||||||
.maybe_timestamp(slot + TIMESTAMP_SLOT_INTERVAL + 1)
|
|
||||||
.is_some());
|
|
||||||
assert!(tower.last_timestamp.timestamp > timestamp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl From<RpcCustomError> for Error {
|
||||||
},
|
},
|
||||||
RpcCustomError::BlockNotAvailable { slot } => Self {
|
RpcCustomError::BlockNotAvailable { slot } => Self {
|
||||||
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_4),
|
code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_4),
|
||||||
message: format!("Block not available for slot {}", slot,),
|
message: format!("Block not available for slot {}", slot),
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ use solana_transaction_status::{
|
||||||
EncodedTransaction, Rewards, TransactionStatusMeta, TransactionWithStatusMeta,
|
EncodedTransaction, Rewards, TransactionStatusMeta, TransactionWithStatusMeta,
|
||||||
UiTransactionEncoding, UiTransactionStatusMeta,
|
UiTransactionEncoding, UiTransactionStatusMeta,
|
||||||
};
|
};
|
||||||
use solana_vote_program::{vote_instruction::VoteInstruction, vote_state::TIMESTAMP_SLOT_INTERVAL};
|
use solana_vote_program::vote_instruction::VoteInstruction;
|
||||||
use std::{
|
use std::{
|
||||||
cell::RefCell,
|
cell::RefCell,
|
||||||
cmp,
|
cmp,
|
||||||
|
@ -78,6 +78,7 @@ thread_local!(static PAR_THREAD_POOL_ALL_CPUS: RefCell<ThreadPool> = RefCell::ne
|
||||||
pub const MAX_COMPLETED_SLOTS_IN_CHANNEL: usize = 100_000;
|
pub const MAX_COMPLETED_SLOTS_IN_CHANNEL: usize = 100_000;
|
||||||
pub const MAX_TURBINE_PROPAGATION_IN_MS: u64 = 100;
|
pub const MAX_TURBINE_PROPAGATION_IN_MS: u64 = 100;
|
||||||
pub const MAX_TURBINE_DELAY_IN_TICKS: u64 = MAX_TURBINE_PROPAGATION_IN_MS / MS_PER_TICK;
|
pub const MAX_TURBINE_DELAY_IN_TICKS: u64 = MAX_TURBINE_PROPAGATION_IN_MS / MS_PER_TICK;
|
||||||
|
const TIMESTAMP_SLOT_INTERVAL: u64 = 4500;
|
||||||
const TIMESTAMP_SLOT_RANGE: usize = 16;
|
const TIMESTAMP_SLOT_RANGE: usize = 16;
|
||||||
|
|
||||||
// An upper bound on maximum number of data shreds we can handle in a slot
|
// An upper bound on maximum number of data shreds we can handle in a slot
|
||||||
|
|
|
@ -32,10 +32,6 @@ pub const INITIAL_LOCKOUT: usize = 2;
|
||||||
// smaller numbers makes
|
// smaller numbers makes
|
||||||
pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
|
pub const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
|
||||||
|
|
||||||
// Frequency of timestamp Votes. In v0.22.0, this is approximately 30min with cluster clock
|
|
||||||
// defaults, intended to limit block time drift to < 1hr
|
|
||||||
pub const TIMESTAMP_SLOT_INTERVAL: u64 = 4500;
|
|
||||||
|
|
||||||
#[frozen_abi(digest = "69hYtmmcuqPbhpc64ZaNJDidaUcg66CW6wzPFiuYZ3To")]
|
#[frozen_abi(digest = "69hYtmmcuqPbhpc64ZaNJDidaUcg66CW6wzPFiuYZ3To")]
|
||||||
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
|
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
|
||||||
pub struct Vote {
|
pub struct Vote {
|
||||||
|
|
Loading…
Reference in New Issue