Skip to content

Commit 9fb237c

Browse files
committed
custom rent get, pod for epoch sysvars
1 parent ff3c1f9 commit 9fb237c

File tree

7 files changed

+178
-136
lines changed

7 files changed

+178
-136
lines changed

epoch-rewards/src/lib.rs

Lines changed: 44 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,18 @@ extern crate std;
1919
use serde_derive::{Deserialize, Serialize};
2020
use {solana_hash::Hash, solana_sdk_macro::CloneZeroed};
2121

22-
#[repr(C)]
22+
#[repr(C, align(16))]
2323
#[cfg_attr(feature = "frozen-abi", derive(solana_frozen_abi_macro::AbiExample))]
2424
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
2525
#[derive(Debug, PartialEq, Eq, Default, CloneZeroed)]
2626
pub struct EpochRewards {
2727
/// The starting block height of the rewards distribution in the current
2828
/// epoch
29-
distribution_starting_block_height: [u8; 8],
29+
pub distribution_starting_block_height: u64,
3030

3131
/// Number of partitions in the rewards distribution in the current epoch,
3232
/// used to generate an EpochRewardsHasher
33-
num_partitions: [u8; 8],
33+
pub num_partitions: u64,
3434

3535
/// The blockhash of the parent block of the first block in the epoch, used
3636
/// to seed an EpochRewardsHasher
@@ -39,71 +39,72 @@ pub struct EpochRewards {
3939
/// The total rewards points calculated for the current epoch, where points
4040
/// equals the sum of (delegated stake * credits observed) for all
4141
/// delegations
42-
total_points: [u8; 16],
42+
pub total_points: u128,
4343

4444
/// The total rewards calculated for the current epoch. This may be greater
4545
/// than the total `distributed_rewards` at the end of the rewards period,
4646
/// due to rounding and inability to deliver rewards smaller than 1 lamport.
47-
total_rewards: [u8; 8],
47+
pub total_rewards: u64,
4848

4949
/// The rewards currently distributed for the current epoch, in lamports
50-
distributed_rewards: [u8; 8],
50+
pub distributed_rewards: u64,
5151

5252
/// Whether the rewards period (including calculation and distribution) is
5353
/// active
54-
pub active: u8,
54+
pub active: bool,
5555
}
5656

57-
impl EpochRewards {
58-
pub fn distribution_starting_block_height(&self) -> u64 {
59-
u64::from_le_bytes(self.distribution_starting_block_height)
60-
}
61-
62-
pub fn num_partitions(&self) -> u64 {
63-
u64::from_le_bytes(self.num_partitions)
64-
}
65-
66-
pub fn parent_blockhash(&self) -> &Hash {
67-
&self.parent_blockhash
68-
}
69-
70-
pub fn total_points(&self) -> u128 {
71-
u128::from_le_bytes(self.total_points)
72-
}
57+
/// Pod (Plain Old Data) representation of [`EpochRewards`] with no padding.
58+
///
59+
/// This type can be safely loaded via `sol_get_sysvar` without undefined behavior.
60+
#[repr(C)]
61+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62+
pub struct PodEpochRewards {
63+
pub distribution_starting_block_height: [u8; 8],
64+
pub num_partitions: [u8; 8],
65+
pub parent_blockhash: [u8; 32],
66+
pub total_points: [u8; 16],
67+
pub total_rewards: [u8; 8],
68+
pub distributed_rewards: [u8; 8],
69+
pub active: u8,
70+
}
7371

74-
pub fn total_rewards(&self) -> u64 {
75-
u64::from_le_bytes(self.total_rewards)
76-
}
72+
const _: () = assert!(core::mem::size_of::<PodEpochRewards>() == 81);
7773

78-
pub fn distributed_rewards(&self) -> u64 {
79-
u64::from_le_bytes(self.distributed_rewards)
80-
}
81-
82-
pub fn active(&self) -> bool {
83-
match self.active {
84-
0 => false,
85-
1 => true,
86-
_ => panic!("invalid active value"),
74+
impl From<PodEpochRewards> for EpochRewards {
75+
fn from(pod: PodEpochRewards) -> Self {
76+
Self {
77+
distribution_starting_block_height: u64::from_le_bytes(
78+
pod.distribution_starting_block_height,
79+
),
80+
num_partitions: u64::from_le_bytes(pod.num_partitions),
81+
parent_blockhash: Hash::new_from_array(pod.parent_blockhash),
82+
total_points: u128::from_le_bytes(pod.total_points),
83+
total_rewards: u64::from_le_bytes(pod.total_rewards),
84+
distributed_rewards: u64::from_le_bytes(pod.distributed_rewards),
85+
active: pod.active != 0,
8786
}
8887
}
88+
}
8989

90+
impl EpochRewards {
9091
pub fn new(
9192
total_rewards: u64,
9293
distributed_rewards: u64,
9394
distribution_starting_block_height: u64,
9495
) -> Self {
9596
Self {
96-
distribution_starting_block_height: distribution_starting_block_height.to_le_bytes(),
97-
total_rewards: total_rewards.to_le_bytes(),
98-
distributed_rewards: distributed_rewards.to_le_bytes(),
97+
distribution_starting_block_height,
98+
total_rewards,
99+
distributed_rewards,
99100
..Self::default()
100101
}
101102
}
102103

103104
pub fn distribute(&mut self, amount: u64) {
104-
let new_distributed_rewards = self.distributed_rewards().saturating_add(amount);
105-
assert!(new_distributed_rewards <= self.total_rewards());
106-
self.distributed_rewards = new_distributed_rewards.to_le_bytes();
105+
let new_distributed_rewards = self.distributed_rewards.saturating_add(amount);
106+
assert!(new_distributed_rewards <= self.total_rewards);
107+
self.distributed_rewards = new_distributed_rewards;
107108
}
108109
}
109110

@@ -116,8 +117,8 @@ mod tests {
116117
let mut epoch_rewards = EpochRewards::new(100, 0, 64);
117118
epoch_rewards.distribute(100);
118119

119-
assert_eq!(epoch_rewards.total_rewards(), 100);
120-
assert_eq!(epoch_rewards.distributed_rewards(), 100);
120+
assert_eq!(epoch_rewards.total_rewards, 100);
121+
assert_eq!(epoch_rewards.distributed_rewards, 100);
121122
}
122123

123124
#[test]

epoch-schedule/src/lib.rs

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -56,24 +56,51 @@ pub const MINIMUM_SLOTS_PER_EPOCH: u64 = 32;
5656
#[derive(Debug, CloneZeroed, PartialEq, Eq)]
5757
pub struct EpochSchedule {
5858
/// The maximum number of slots in each epoch.
59-
slots_per_epoch: [u8; 8],
59+
pub slots_per_epoch: u64,
6060

6161
/// A number of slots before beginning of an epoch to calculate
6262
/// a leader schedule for that epoch.
63-
leader_schedule_slot_offset: [u8; 8],
63+
pub leader_schedule_slot_offset: u64,
6464

6565
/// Whether epochs start short and grow.
66-
pub warmup: u8,
66+
pub warmup: bool,
6767

6868
/// The first epoch after the warmup period.
6969
///
7070
/// Basically: `log2(slots_per_epoch) - log2(MINIMUM_SLOTS_PER_EPOCH)`.
71-
first_normal_epoch: [u8; 8],
71+
pub first_normal_epoch: u64,
7272

7373
/// The first slot after the warmup period.
7474
///
7575
/// Basically: `MINIMUM_SLOTS_PER_EPOCH * (2.pow(first_normal_epoch) - 1)`.
76-
first_normal_slot: [u8; 8],
76+
pub first_normal_slot: u64,
77+
}
78+
79+
/// Pod (Plain Old Data) representation of [`EpochSchedule`] with no padding.
80+
///
81+
/// This type can be safely loaded via `sol_get_sysvar` without undefined behavior.
82+
#[repr(C)]
83+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84+
pub struct PodEpochSchedule {
85+
pub slots_per_epoch: [u8; 8],
86+
pub leader_schedule_slot_offset: [u8; 8],
87+
pub warmup: u8,
88+
pub first_normal_epoch: [u8; 8],
89+
pub first_normal_slot: [u8; 8],
90+
}
91+
92+
const _: () = assert!(core::mem::size_of::<PodEpochSchedule>() == 33);
93+
94+
impl From<PodEpochSchedule> for EpochSchedule {
95+
fn from(pod: PodEpochSchedule) -> Self {
96+
Self {
97+
slots_per_epoch: u64::from_le_bytes(pod.slots_per_epoch),
98+
leader_schedule_slot_offset: u64::from_le_bytes(pod.leader_schedule_slot_offset),
99+
warmup: pod.warmup != 0,
100+
first_normal_epoch: u64::from_le_bytes(pod.first_normal_epoch),
101+
first_normal_slot: u64::from_le_bytes(pod.first_normal_slot),
102+
}
103+
}
77104
}
78105

79106
impl Default for EpochSchedule {
@@ -87,40 +114,18 @@ impl Default for EpochSchedule {
87114
}
88115

89116
impl EpochSchedule {
90-
pub fn slots_per_epoch(&self) -> u64 {
91-
u64::from_le_bytes(self.slots_per_epoch)
92-
}
93-
94-
pub fn leader_schedule_slot_offset(&self) -> u64 {
95-
u64::from_le_bytes(self.leader_schedule_slot_offset)
96-
}
97-
98-
pub fn warmup(&self) -> bool {
99-
match self.warmup {
100-
0 => false,
101-
1 => true,
102-
_ => panic!("invalid warmup value"),
103-
}
104-
}
105-
106-
pub fn first_normal_epoch(&self) -> u64 {
107-
u64::from_le_bytes(self.first_normal_epoch)
108-
}
109-
110-
pub fn first_normal_slot(&self) -> u64 {
111-
u64::from_le_bytes(self.first_normal_slot)
112-
}
113-
114117
pub fn new(slots_per_epoch: u64) -> Self {
115118
Self::custom(slots_per_epoch, slots_per_epoch, true)
116119
}
120+
117121
pub fn without_warmup() -> Self {
118122
Self::custom(
119123
DEFAULT_SLOTS_PER_EPOCH,
120124
DEFAULT_LEADER_SCHEDULE_SLOT_OFFSET,
121125
false,
122126
)
123127
}
128+
124129
pub fn custom(slots_per_epoch: u64, leader_schedule_slot_offset: u64, warmup: bool) -> Self {
125130
assert!(slots_per_epoch >= MINIMUM_SLOTS_PER_EPOCH);
126131
let (first_normal_epoch, first_normal_slot) = if warmup {
@@ -137,40 +142,40 @@ impl EpochSchedule {
137142
(0, 0)
138143
};
139144
EpochSchedule {
140-
slots_per_epoch: slots_per_epoch.to_le_bytes(),
141-
leader_schedule_slot_offset: leader_schedule_slot_offset.to_le_bytes(),
142-
warmup: warmup as u8,
143-
first_normal_epoch: first_normal_epoch.to_le_bytes(),
144-
first_normal_slot: first_normal_slot.to_le_bytes(),
145+
slots_per_epoch,
146+
leader_schedule_slot_offset,
147+
warmup,
148+
first_normal_epoch,
149+
first_normal_slot,
145150
}
146151
}
147152

148153
/// get the length of the given epoch (in slots)
149154
pub fn get_slots_in_epoch(&self, epoch: u64) -> u64 {
150-
if epoch < self.first_normal_epoch() {
155+
if epoch < self.first_normal_epoch {
151156
2u64.saturating_pow(
152157
(epoch as u32).saturating_add(MINIMUM_SLOTS_PER_EPOCH.trailing_zeros()),
153158
)
154159
} else {
155-
self.slots_per_epoch()
160+
self.slots_per_epoch
156161
}
157162
}
158163

159164
/// get the epoch for which the given slot should save off
160165
/// information about stakers
161166
pub fn get_leader_schedule_epoch(&self, slot: u64) -> u64 {
162-
if slot < self.first_normal_slot() {
167+
if slot < self.first_normal_slot {
163168
// until we get to normal slots, behave as if leader_schedule_slot_offset == slots_per_epoch
164169
self.get_epoch_and_slot_index(slot).0.saturating_add(1)
165170
} else {
166-
let new_slots_since_first_normal_slot = slot.saturating_sub(self.first_normal_slot());
167-
let new_first_normal_leader_schedule_slot = new_slots_since_first_normal_slot
168-
.saturating_add(self.leader_schedule_slot_offset());
171+
let new_slots_since_first_normal_slot = slot.saturating_sub(self.first_normal_slot);
172+
let new_first_normal_leader_schedule_slot =
173+
new_slots_since_first_normal_slot.saturating_add(self.leader_schedule_slot_offset);
169174
let new_epochs_since_first_normal_leader_schedule =
170175
new_first_normal_leader_schedule_slot
171-
.checked_div(self.slots_per_epoch())
176+
.checked_div(self.slots_per_epoch)
172177
.unwrap_or(0);
173-
self.first_normal_epoch()
178+
self.first_normal_epoch
174179
.saturating_add(new_epochs_since_first_normal_leader_schedule)
175180
}
176181
}
@@ -182,7 +187,7 @@ impl EpochSchedule {
182187

183188
/// get epoch and offset into the epoch for the given slot
184189
pub fn get_epoch_and_slot_index(&self, slot: u64) -> (u64, u64) {
185-
if slot < self.first_normal_slot() {
190+
if slot < self.first_normal_slot {
186191
let epoch = slot
187192
.saturating_add(MINIMUM_SLOTS_PER_EPOCH)
188193
.saturating_add(1)
@@ -199,28 +204,28 @@ impl EpochSchedule {
199204
slot.saturating_sub(epoch_len.saturating_sub(MINIMUM_SLOTS_PER_EPOCH)),
200205
)
201206
} else {
202-
let normal_slot_index = slot.saturating_sub(self.first_normal_slot());
207+
let normal_slot_index = slot.saturating_sub(self.first_normal_slot);
203208
let normal_epoch_index = normal_slot_index
204-
.checked_div(self.slots_per_epoch())
209+
.checked_div(self.slots_per_epoch)
205210
.unwrap_or(0);
206-
let epoch = self.first_normal_epoch().saturating_add(normal_epoch_index);
211+
let epoch = self.first_normal_epoch.saturating_add(normal_epoch_index);
207212
let slot_index = normal_slot_index
208-
.checked_rem(self.slots_per_epoch())
213+
.checked_rem(self.slots_per_epoch)
209214
.unwrap_or(0);
210215
(epoch, slot_index)
211216
}
212217
}
213218

214219
pub fn get_first_slot_in_epoch(&self, epoch: u64) -> u64 {
215-
if epoch <= self.first_normal_epoch() {
220+
if epoch <= self.first_normal_epoch {
216221
2u64.saturating_pow(epoch as u32)
217222
.saturating_sub(1)
218223
.saturating_mul(MINIMUM_SLOTS_PER_EPOCH)
219224
} else {
220225
epoch
221-
.saturating_sub(self.first_normal_epoch())
222-
.saturating_mul(self.slots_per_epoch())
223-
.saturating_add(self.first_normal_slot())
226+
.saturating_sub(self.first_normal_epoch)
227+
.saturating_mul(self.slots_per_epoch)
228+
.saturating_add(self.first_normal_slot)
224229
}
225230
}
226231

@@ -295,8 +300,13 @@ mod tests {
295300

296301
#[test]
297302
fn test_clone() {
298-
let epoch_schedule =
299-
EpochSchedule::custom(MINIMUM_SLOTS_PER_EPOCH, MINIMUM_SLOTS_PER_EPOCH, true);
303+
let epoch_schedule = EpochSchedule {
304+
slots_per_epoch: 1,
305+
leader_schedule_slot_offset: 2,
306+
warmup: true,
307+
first_normal_epoch: 4,
308+
first_normal_slot: 5,
309+
};
300310
#[allow(clippy::clone_on_copy)]
301311
let cloned_epoch_schedule = epoch_schedule.clone();
302312
assert_eq!(cloned_epoch_schedule, epoch_schedule);

genesis-config/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub const UNUSED_DEFAULT: u64 = 1024;
4949
#[cfg_attr(
5050
feature = "frozen-abi",
5151
derive(AbiExample),
52-
frozen_abi(digest = "CR76YX4Y3eHSCoRzkBEaJv7p3vAShUT56fX8WERoLBAp")
52+
frozen_abi(digest = "3tUUJkZiUUGfuNCXbDuDR6KCQYPsh3m3cPw5vVUSt113")
5353
)]
5454
#[cfg_attr(
5555
feature = "serde",
@@ -244,8 +244,8 @@ impl fmt::Display for GenesisConfig {
244244
self.ticks_per_slot,
245245
self.poh_config.hashes_per_tick,
246246
self.poh_config.target_tick_duration,
247-
self.epoch_schedule.slots_per_epoch(),
248-
if self.epoch_schedule.warmup() {
247+
self.epoch_schedule.slots_per_epoch,
248+
if self.epoch_schedule.warmup {
249249
"en"
250250
} else {
251251
"dis"

0 commit comments

Comments
 (0)