Skip to content

Commit

Permalink
init new state layout
Browse files Browse the repository at this point in the history
  • Loading branch information
buffalojoec committed Apr 4, 2024
1 parent 9580741 commit b0c3bb1
Show file tree
Hide file tree
Showing 9 changed files with 547 additions and 0 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ solana-frozen-abi = "1.18.2"
solana-program = "1.18.2"
spl-program-error = "0.3.1"

[target.'cfg(not(target_os = "solana"))'.dev-dependencies]
arbitrary = { version = "1.3.2", features = ["derive"] }

[lib]
crate-type = ["cdylib", "lib"]

Expand Down
1 change: 1 addition & 0 deletions program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#[cfg(all(target_os = "solana", feature = "bpf-entrypoint"))]
mod entrypoint;
pub mod error;
pub mod state;

// [Core BPF]: TODO: Program-test will not overwrite existing built-ins.
// See https://github.com/solana-labs/solana/pull/35233.
Expand Down
72 changes: 72 additions & 0 deletions program/src/state/circ_buf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#[cfg(test)]
use arbitrary::{Arbitrary, Unstructured};
use {
serde::{Deserialize, Serialize},
solana_frozen_abi_macro::AbiExample,
};

// this is how many epochs a voter can be remembered for slashing
const MAX_ITEMS: usize = 32;

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, AbiExample)]
pub struct CircBuf<I> {
buf: [I; MAX_ITEMS],
/// next pointer
idx: usize,
is_empty: bool,
}

impl<I: Default + Copy> Default for CircBuf<I> {
fn default() -> Self {
Self {
buf: [I::default(); MAX_ITEMS],
idx: MAX_ITEMS
.checked_sub(1)
.expect("`MAX_ITEMS` should be positive"),
is_empty: true,
}
}
}

impl<I> CircBuf<I> {
pub fn append(&mut self, item: I) {
// remember prior delegate and when we switched, to support later slashing
self.idx = self
.idx
.checked_add(1)
.and_then(|idx| idx.checked_rem(MAX_ITEMS))
.expect("`self.idx` should be < `MAX_ITEMS` which should be non-zero");

self.buf[self.idx] = item;
self.is_empty = false;
}

pub fn buf(&self) -> &[I; MAX_ITEMS] {
&self.buf
}

pub fn last(&self) -> Option<&I> {
if !self.is_empty {
Some(&self.buf[self.idx])
} else {
None
}
}
}

#[cfg(test)]
impl<'a, I: Default + Copy> Arbitrary<'a> for CircBuf<I>
where
I: Arbitrary<'a>,
{
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let mut circbuf = Self::default();

let len = u.arbitrary_len::<I>()?;
for _ in 0..len {
circbuf.append(I::arbitrary(u)?);
}

Ok(circbuf)
}
}
96 changes: 96 additions & 0 deletions program/src/state/lockout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#[cfg(test)]
use arbitrary::Arbitrary;
use {
serde::{Deserialize, Serialize},
solana_frozen_abi_macro::AbiExample,
solana_program::clock::Slot,
};

// Maximum number of votes to keep around, tightly coupled with
// epoch_schedule::MINIMUM_SLOTS_PER_EPOCH
pub const MAX_LOCKOUT_HISTORY: usize = 31;
pub const INITIAL_LOCKOUT: usize = 2;

#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct Lockout {
slot: Slot,
confirmation_count: u32,
}

impl Lockout {
pub fn new(slot: Slot) -> Self {
Self::new_with_confirmation_count(slot, 1)
}

pub fn new_with_confirmation_count(slot: Slot, confirmation_count: u32) -> Self {
Self {
slot,
confirmation_count,
}
}

// The number of slots for which this vote is locked
pub fn lockout(&self) -> u64 {
(INITIAL_LOCKOUT as u64).pow(self.confirmation_count())
}

// The last slot at which a vote is still locked out. Validators should not
// vote on a slot in another fork which is less than or equal to this slot
// to avoid having their stake slashed.
pub fn last_locked_out_slot(&self) -> Slot {
self.slot.saturating_add(self.lockout())
}

pub fn is_locked_out_at_slot(&self, slot: Slot) -> bool {
self.last_locked_out_slot() >= slot
}

pub fn slot(&self) -> Slot {
self.slot
}

pub fn confirmation_count(&self) -> u32 {
self.confirmation_count
}

pub fn increase_confirmation_count(&mut self, by: u32) {
self.confirmation_count = self.confirmation_count.saturating_add(by)
}
}

#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
#[cfg_attr(test, derive(Arbitrary))]
pub struct LandedVote {
// Latency is the difference in slot number between the slot that was voted on (lockout.slot)
// and the slot in which the vote that added this Lockout landed. For votes which were
// cast before versions of the validator software which recorded vote latencies, latency is
// recorded as 0.
pub latency: u8,
pub lockout: Lockout,
}

impl LandedVote {
pub fn slot(&self) -> Slot {
self.lockout.slot
}

pub fn confirmation_count(&self) -> u32 {
self.lockout.confirmation_count
}
}

impl From<LandedVote> for Lockout {
fn from(landed_vote: LandedVote) -> Self {
landed_vote.lockout
}
}

impl From<Lockout> for LandedVote {
fn from(lockout: Lockout) -> Self {
Self {
latency: 0,
lockout,
}
}
}
7 changes: 7 additions & 0 deletions program/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Vote Program state types.
pub mod circ_buf;
pub mod lockout;
pub mod tower_sync;
pub mod vote;
pub mod vote_state_update;
Loading

0 comments on commit b0c3bb1

Please sign in to comment.