Refactor - derive `CloneZeroed` (#28771)

* Adds proc_macro_derive(CloneZeroed).

* Switches over all use sites of clone_zeroed() and copy_field().

* Removes clone_zeroed() and copy_field().

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
This commit is contained in:
Alexander Meißner 2022-11-09 18:15:42 +01:00 committed by GitHub
parent 43e5c188c2
commit 93fc4edef8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 97 deletions

View File

@ -405,3 +405,38 @@ pub fn wasm_bindgen_stub(_attr: TokenStream, item: TokenStream) -> TokenStream {
}
.into()
}
// Sets padding in structures to zero explicitly.
// Otherwise padding could be inconsistent across the network and lead to divergence / consensus failures.
#[proc_macro_derive(CloneZeroed)]
pub fn derive_clone_zeroed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
match parse_macro_input!(input as syn::Item) {
syn::Item::Struct(item_struct) => {
let clone_statements = match item_struct.fields {
syn::Fields::Named(ref fields) => fields.named.iter().map(|f| {
let name = &f.ident;
quote! {
std::ptr::addr_of_mut!((*ptr).#name).write(self.#name);
}
}),
_ => unimplemented!(),
};
let name = &item_struct.ident;
quote! {
impl Clone for #name {
fn clone(&self) -> Self {
let mut value = std::mem::MaybeUninit::<Self>::uninit();
unsafe {
std::ptr::write_bytes(&mut value, 0, 1);
let ptr = value.as_mut_ptr();
#(#clone_statements)*
value.assume_init()
}
}
}
}
}
_ => unimplemented!(),
}
.into()
}

View File

@ -20,10 +20,7 @@
//!
//! [oracle]: https://docs.solana.com/implemented-proposals/validator-timestamp-oracle
use {
crate::{clone_zeroed, copy_field},
std::mem::MaybeUninit,
};
use solana_sdk_macro::CloneZeroed;
/// The default tick rate that the cluster attempts to achieve (160 per second).
///
@ -147,7 +144,7 @@ pub type UnixTimestamp = i64;
///
/// All members of `Clock` start from 0 upon network boot.
#[repr(C)]
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Debug, CloneZeroed, Default, PartialEq, Eq)]
pub struct Clock {
/// The current `Slot`.
pub slot: Slot,
@ -170,21 +167,6 @@ pub struct Clock {
pub unix_timestamp: UnixTimestamp,
}
impl Clone for Clock {
fn clone(&self) -> Self {
clone_zeroed(|cloned: &mut MaybeUninit<Self>| {
let ptr = cloned.as_mut_ptr();
unsafe {
copy_field!(ptr, self, slot);
copy_field!(ptr, self, epoch_start_timestamp);
copy_field!(ptr, self, epoch);
copy_field!(ptr, self, leader_schedule_epoch);
copy_field!(ptr, self, unix_timestamp);
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -12,10 +12,7 @@
//! epochs increasing in slots until they last for [`DEFAULT_SLOTS_PER_EPOCH`].
pub use crate::clock::{Epoch, Slot, DEFAULT_SLOTS_PER_EPOCH};
use {
crate::{clone_zeroed, copy_field},
std::mem::MaybeUninit,
};
use solana_sdk_macro::CloneZeroed;
/// The default number of slots before an epoch starts to calculate the leader schedule.
pub const DEFAULT_LEADER_SCHEDULE_SLOT_OFFSET: u64 = DEFAULT_SLOTS_PER_EPOCH;
@ -32,7 +29,7 @@ pub const MAX_LEADER_SCHEDULE_EPOCH_OFFSET: u64 = 3;
pub const MINIMUM_SLOTS_PER_EPOCH: u64 = 32;
#[repr(C)]
#[derive(Debug, Copy, PartialEq, Eq, Deserialize, Serialize, AbiExample)]
#[derive(Debug, CloneZeroed, Copy, PartialEq, Eq, Deserialize, Serialize, AbiExample)]
#[serde(rename_all = "camelCase")]
pub struct EpochSchedule {
/// The maximum number of slots in each epoch.
@ -66,21 +63,6 @@ impl Default for EpochSchedule {
}
}
impl Clone for EpochSchedule {
fn clone(&self) -> Self {
clone_zeroed(|cloned: &mut MaybeUninit<Self>| {
let ptr = cloned.as_mut_ptr();
unsafe {
copy_field!(ptr, self, slots_per_epoch);
copy_field!(ptr, self, leader_schedule_slot_offset);
copy_field!(ptr, self, warmup);
copy_field!(ptr, self, first_normal_epoch);
copy_field!(ptr, self, first_normal_slot);
}
})
}
}
impl EpochSchedule {
pub fn new(slots_per_epoch: u64) -> Self {
Self::custom(slots_per_epoch, slots_per_epoch, true)

View File

@ -742,25 +742,6 @@ macro_rules! unchecked_div_by_const {
}};
}
use std::{mem::MaybeUninit, ptr::write_bytes};
#[macro_export]
macro_rules! copy_field {
($ptr:expr, $self:ident, $field:ident) => {
std::ptr::addr_of_mut!((*$ptr).$field).write($self.$field)
};
}
pub fn clone_zeroed<T, F>(clone: F) -> T
where
F: Fn(&mut MaybeUninit<T>),
{
let mut value = MaybeUninit::<T>::uninit();
unsafe { write_bytes(&mut value, 0, 1) }
clone(&mut value);
unsafe { value.assume_init() }
}
// This module is purposefully listed after all other exports: because of an
// interaction within rustdoc between the reexports inside this module of
// `solana_program`'s top-level modules, and `solana_sdk`'s glob re-export of

View File

@ -4,14 +4,11 @@
#![allow(clippy::integer_arithmetic)]
use {
crate::{clock::DEFAULT_SLOTS_PER_EPOCH, clone_zeroed, copy_field},
std::mem::MaybeUninit,
};
use {crate::clock::DEFAULT_SLOTS_PER_EPOCH, solana_sdk_macro::CloneZeroed};
/// Configuration of network rent.
#[repr(C)]
#[derive(Serialize, Deserialize, PartialEq, Copy, Debug, AbiExample)]
#[derive(Serialize, Deserialize, PartialEq, CloneZeroed, Copy, Debug, AbiExample)]
pub struct Rent {
/// Rental rate in lamports/byte-year.
pub lamports_per_byte_year: u64,
@ -62,19 +59,6 @@ impl Default for Rent {
}
}
impl Clone for Rent {
fn clone(&self) -> Self {
clone_zeroed(|cloned: &mut MaybeUninit<Self>| {
let ptr = cloned.as_mut_ptr();
unsafe {
copy_field!(ptr, self, lamports_per_byte_year);
copy_field!(ptr, self, exemption_threshold);
copy_field!(ptr, self, burn_percent);
}
})
}
}
impl Rent {
/// Calculate how much rent to burn from the collected rent.
///

View File

@ -22,10 +22,9 @@
use {
crate::{
clone_zeroed, copy_field, fee_calculator::FeeCalculator, impl_sysvar_get,
program_error::ProgramError, sysvar::Sysvar,
fee_calculator::FeeCalculator, impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar,
},
std::mem::MaybeUninit,
solana_sdk_macro::CloneZeroed,
};
crate::declare_deprecated_sysvar_id!("SysvarFees111111111111111111111111111111111", Fees);
@ -36,22 +35,11 @@ crate::declare_deprecated_sysvar_id!("SysvarFees11111111111111111111111111111111
note = "Please do not use, will no longer be available in the future"
)]
#[repr(C)]
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)]
#[derive(Serialize, Deserialize, Debug, CloneZeroed, Default, PartialEq, Eq)]
pub struct Fees {
pub fee_calculator: FeeCalculator,
}
impl Clone for Fees {
fn clone(&self) -> Self {
clone_zeroed(|cloned: &mut MaybeUninit<Self>| {
let ptr = cloned.as_mut_ptr();
unsafe {
copy_field!(ptr, self, fee_calculator);
}
})
}
}
impl Fees {
pub fn new(fee_calculator: &FeeCalculator) -> Self {
#[allow(deprecated)]

View File

@ -13,11 +13,11 @@ pub use signer::signers;
pub use solana_program::program_stubs;
pub use solana_program::{
account_info, address_lookup_table_account, blake3, borsh, bpf_loader, bpf_loader_deprecated,
bpf_loader_upgradeable, clock, clone_zeroed, config, copy_field, custom_heap_default,
custom_panic_default, debug_account_data, declare_deprecated_sysvar_id, declare_sysvar_id,
decode_error, ed25519_program, epoch_schedule, fee_calculator, impl_sysvar_get, incinerator,
instruction, keccak, lamports, loader_instruction, loader_upgradeable_instruction, message,
msg, native_token, nonce, program, program_error, program_memory, program_option, program_pack,
bpf_loader_upgradeable, clock, config, custom_heap_default, custom_panic_default,
debug_account_data, declare_deprecated_sysvar_id, declare_sysvar_id, decode_error,
ed25519_program, epoch_schedule, fee_calculator, impl_sysvar_get, incinerator, instruction,
keccak, lamports, loader_instruction, loader_upgradeable_instruction, message, msg,
native_token, nonce, program, program_error, program_memory, program_option, program_pack,
rent, sanitize, sdk_ids, secp256k1_program, secp256k1_recover, serialize_utils, short_vec,
slot_hashes, slot_history, stake, stake_history, syscalls, system_instruction, system_program,
sysvar, unchecked_div_by_const, vote, wasm_bindgen,