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() .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 //! [oracle]: https://docs.solana.com/implemented-proposals/validator-timestamp-oracle
use { use solana_sdk_macro::CloneZeroed;
crate::{clone_zeroed, copy_field},
std::mem::MaybeUninit,
};
/// The default tick rate that the cluster attempts to achieve (160 per second). /// 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. /// All members of `Clock` start from 0 upon network boot.
#[repr(C)] #[repr(C)]
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)] #[derive(Serialize, Deserialize, Debug, CloneZeroed, Default, PartialEq, Eq)]
pub struct Clock { pub struct Clock {
/// The current `Slot`. /// The current `Slot`.
pub slot: Slot, pub slot: Slot,
@ -170,21 +167,6 @@ pub struct Clock {
pub unix_timestamp: UnixTimestamp, 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -12,10 +12,7 @@
//! epochs increasing in slots until they last for [`DEFAULT_SLOTS_PER_EPOCH`]. //! epochs increasing in slots until they last for [`DEFAULT_SLOTS_PER_EPOCH`].
pub use crate::clock::{Epoch, Slot, DEFAULT_SLOTS_PER_EPOCH}; pub use crate::clock::{Epoch, Slot, DEFAULT_SLOTS_PER_EPOCH};
use { use solana_sdk_macro::CloneZeroed;
crate::{clone_zeroed, copy_field},
std::mem::MaybeUninit,
};
/// The default number of slots before an epoch starts to calculate the leader schedule. /// 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; 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; pub const MINIMUM_SLOTS_PER_EPOCH: u64 = 32;
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, PartialEq, Eq, Deserialize, Serialize, AbiExample)] #[derive(Debug, CloneZeroed, Copy, PartialEq, Eq, Deserialize, Serialize, AbiExample)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct EpochSchedule { pub struct EpochSchedule {
/// The maximum number of slots in each epoch. /// 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 { impl EpochSchedule {
pub fn new(slots_per_epoch: u64) -> Self { pub fn new(slots_per_epoch: u64) -> Self {
Self::custom(slots_per_epoch, slots_per_epoch, true) 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 // This module is purposefully listed after all other exports: because of an
// interaction within rustdoc between the reexports inside this module of // interaction within rustdoc between the reexports inside this module of
// `solana_program`'s top-level modules, and `solana_sdk`'s glob re-export 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)] #![allow(clippy::integer_arithmetic)]
use { use {crate::clock::DEFAULT_SLOTS_PER_EPOCH, solana_sdk_macro::CloneZeroed};
crate::{clock::DEFAULT_SLOTS_PER_EPOCH, clone_zeroed, copy_field},
std::mem::MaybeUninit,
};
/// Configuration of network rent. /// Configuration of network rent.
#[repr(C)] #[repr(C)]
#[derive(Serialize, Deserialize, PartialEq, Copy, Debug, AbiExample)] #[derive(Serialize, Deserialize, PartialEq, CloneZeroed, Copy, Debug, AbiExample)]
pub struct Rent { pub struct Rent {
/// Rental rate in lamports/byte-year. /// Rental rate in lamports/byte-year.
pub lamports_per_byte_year: u64, 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 { impl Rent {
/// Calculate how much rent to burn from the collected rent. /// Calculate how much rent to burn from the collected rent.
/// ///

View File

@ -22,10 +22,9 @@
use { use {
crate::{ crate::{
clone_zeroed, copy_field, fee_calculator::FeeCalculator, impl_sysvar_get, fee_calculator::FeeCalculator, impl_sysvar_get, program_error::ProgramError, sysvar::Sysvar,
program_error::ProgramError, sysvar::Sysvar,
}, },
std::mem::MaybeUninit, solana_sdk_macro::CloneZeroed,
}; };
crate::declare_deprecated_sysvar_id!("SysvarFees111111111111111111111111111111111", Fees); 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" note = "Please do not use, will no longer be available in the future"
)] )]
#[repr(C)] #[repr(C)]
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Eq)] #[derive(Serialize, Deserialize, Debug, CloneZeroed, Default, PartialEq, Eq)]
pub struct Fees { pub struct Fees {
pub fee_calculator: FeeCalculator, 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 { impl Fees {
pub fn new(fee_calculator: &FeeCalculator) -> Self { pub fn new(fee_calculator: &FeeCalculator) -> Self {
#[allow(deprecated)] #[allow(deprecated)]

View File

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