diff --git a/programs/vote_api/src/vote_instruction.rs b/programs/vote_api/src/vote_instruction.rs index 11b74ee04f..778a13eabc 100644 --- a/programs/vote_api/src/vote_instruction.rs +++ b/programs/vote_api/src/vote_instruction.rs @@ -10,6 +10,7 @@ use solana_metrics::datapoint_warn; use solana_sdk::account::KeyedAccount; use solana_sdk::instruction::{AccountMeta, Instruction, InstructionError}; use solana_sdk::pubkey::Pubkey; +use solana_sdk::syscall::slot_hashes; use solana_sdk::system_instruction; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] @@ -93,7 +94,10 @@ pub fn vote( authorized_voter_id: &Pubkey, recent_votes: Vec, ) -> Instruction { - let account_metas = metas_for_authorized_signer(from_id, vote_id, authorized_voter_id); + let mut account_metas = metas_for_authorized_signer(from_id, vote_id, authorized_voter_id); + + // request slot_hashes syscall account after vote_id + account_metas.insert(2, AccountMeta::new(slot_hashes::id(), false)); Instruction::new(id(), &VoteInstruction::Vote(recent_votes), account_metas) } @@ -114,7 +118,7 @@ pub fn process_instruction( } // 0th index is the guy who paid for the transaction - let (me, other_signers) = &mut keyed_accounts.split_at_mut(2); + let (me, rest) = &mut keyed_accounts.split_at_mut(2); let me = &mut me[1]; // TODO: data-driven unpack and dispatch of KeyedAccounts @@ -123,33 +127,13 @@ pub fn process_instruction( vote_state::initialize_account(me, &node_id, commission) } VoteInstruction::AuthorizeVoter(voter_id) => { - vote_state::authorize_voter(me, other_signers, &voter_id) + vote_state::authorize_voter(me, rest, &voter_id) } VoteInstruction::Vote(votes) => { datapoint_warn!("vote-native", ("count", 1, i64)); - // TODO: remove me when Bank does this - let valid_votes = votes - .iter() - .map(|vote| (vote.slot, vote.hash)) - .collect::>(); - - use bincode::serialized_size; - use solana_sdk::account::Account; - use solana_sdk::account_utils::State; - use solana_sdk::syscall::slot_hashes; - let mut valid_votes_account = Account::new( - 0, - serialized_size(&valid_votes).unwrap() as usize, - &Pubkey::default(), - ); - valid_votes_account.set_state(&valid_votes).unwrap(); - // END TODO: remove me when Bank does this - vote_state::process_votes( - me, - &mut KeyedAccount::new(&slot_hashes::id(), false, &mut valid_votes_account), - other_signers, - &votes, - ) + let (slot_hashes, other_signers) = rest.split_at_mut(1); + let slot_hashes = &mut slot_hashes[0]; + vote_state::process_votes(me, slot_hashes, other_signers, &votes) } } } diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index f9edd1ff45..5877fa9935 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -24,6 +24,7 @@ use solana_sdk::hash::{extend_and_hash, Hash}; use solana_sdk::native_loader; use solana_sdk::pubkey::Pubkey; use solana_sdk::signature::{Keypair, Signature}; +use solana_sdk::syscall::slot_hashes::{self, SlotHashes}; use solana_sdk::system_transaction; use solana_sdk::timing::{duration_as_ms, duration_as_us, MAX_RECENT_BLOCKHASHES}; use solana_sdk::transaction::{Result, Transaction, TransactionError}; @@ -198,7 +199,19 @@ impl Bank { *self.hash.read().unwrap() != Hash::default() } - pub fn freeze(&self) { + fn update_slot_hashes(&self) { + let mut account = self + .get_account(&slot_hashes::id()) + .unwrap_or_else(|| slot_hashes::create_account(1)); + + let mut slot_hashes = SlotHashes::from(&account).unwrap(); + slot_hashes.add(self.slot(), self.hash()); + slot_hashes.to(&mut account).unwrap(); + + self.store(&slot_hashes::id(), &account); + } + + fn set_hash(&self) { let mut hash = self.hash.write().unwrap(); if *hash == Hash::default() { @@ -207,6 +220,11 @@ impl Bank { } } + pub fn freeze(&self) { + self.set_hash(); + self.update_slot_hashes(); + } + pub fn epoch_schedule(&self) -> &EpochSchedule { &self.epoch_schedule } @@ -1467,17 +1485,22 @@ mod tests { fn test_bank_hash_internal_state_squash() { let collector_id = Pubkey::default(); let bank0 = Arc::new(Bank::new(&create_genesis_block(10).0)); + let hash0 = bank0.hash_internal_state(); + // save hash0 because new_from_parent + // updates syscall entries + let bank1 = Bank::new_from_parent(&bank0, &collector_id, 1); // no delta in bank1, hashes match - assert_eq!(bank0.hash_internal_state(), bank1.hash_internal_state()); + assert_eq!(hash0, bank1.hash_internal_state()); // remove parent bank1.squash(); assert!(bank1.parents().is_empty()); - // hash should still match - assert_eq!(bank0.hash(), bank1.hash()); + // hash should still match, + // can't use hash_internal_state() after a freeze()... + assert_eq!(hash0, bank1.hash()); } /// Verifies that last ids and accounts are correctly referenced from parent diff --git a/sdk/src/syscall/mod.rs b/sdk/src/syscall/mod.rs index 4cc764e05f..4e6c39e636 100644 --- a/sdk/src/syscall/mod.rs +++ b/sdk/src/syscall/mod.rs @@ -6,13 +6,17 @@ pub mod slot_hashes; /// "Sysca11111111111111111111111111111111111111" /// owner pubkey for syscall accounts -const SYSCALL_PROGRAM_ID: [u8; 32] = [ +const ID: [u8; 32] = [ 6, 167, 211, 138, 69, 216, 137, 185, 198, 189, 33, 204, 111, 12, 217, 220, 229, 201, 34, 52, 253, 202, 87, 144, 232, 16, 195, 192, 0, 0, 0, 0, ]; pub fn id() -> Pubkey { - Pubkey::new(&SYSCALL_PROGRAM_ID) + Pubkey::new(&ID) +} + +pub fn check_id(id: &Pubkey) -> bool { + id.as_ref() == ID } #[cfg(test)] @@ -26,5 +30,6 @@ mod tests { // dbg!((name, bs58::decode(name).into_vec().unwrap())); // }); assert!(ids.iter().all(|(name, id)| *name == id.to_string())); + assert!(check_id(&id())); } } diff --git a/sdk/src/syscall/slot_hashes.rs b/sdk/src/syscall/slot_hashes.rs index 35085be81f..eee7912ece 100644 --- a/sdk/src/syscall/slot_hashes.rs +++ b/sdk/src/syscall/slot_hashes.rs @@ -3,56 +3,97 @@ //! this account carries the Bank's most recent blockhashes for some N parents //! use crate::account::Account; +use crate::account_utils::State; use crate::hash::Hash; use crate::pubkey::Pubkey; +use crate::syscall; +use bincode::serialized_size; +use std::ops::Deref; /// "Sysca11SlotHashes11111111111111111111111111" /// slot hashes account pubkey -const SYSCALL_SLOT_HASHES_ID: [u8; 32] = [ +const ID: [u8; 32] = [ 6, 167, 211, 138, 69, 219, 186, 157, 48, 170, 46, 66, 2, 146, 193, 59, 39, 59, 245, 188, 30, 60, 130, 78, 86, 27, 113, 191, 208, 0, 0, 0, ]; pub fn id() -> Pubkey { - Pubkey::new(&SYSCALL_SLOT_HASHES_ID) + Pubkey::new(&ID) } pub fn check_id(pubkey: &Pubkey) -> bool { - pubkey.as_ref() == SYSCALL_SLOT_HASHES_ID + pubkey.as_ref() == ID } -use crate::account_utils::State; -use std::ops::{Deref, DerefMut}; -#[derive(Serialize, Deserialize)] -pub struct SlotHashes(Vec<(u64, Hash)>); +pub const MAX_SLOT_HASHES: usize = 512; // 512 slots to get your vote in + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +pub struct SlotHashes { + // non-pub to keep control of size + inner: Vec<(u64, Hash)>, +} impl SlotHashes { pub fn from(account: &Account) -> Option { account.state().ok() } + pub fn to(&self, account: &mut Account) -> Option<()> { + account.set_state(self).ok() + } + + pub fn size_of() -> usize { + serialized_size(&SlotHashes { + inner: vec![(0, Hash::default()); MAX_SLOT_HASHES], + }) + .unwrap() as usize + } + pub fn add(&mut self, slot: u64, hash: Hash) { + self.inner.insert(0, (slot, hash)); + self.inner.truncate(MAX_SLOT_HASHES); + } } + impl Deref for SlotHashes { type Target = Vec<(u64, Hash)>; fn deref(&self) -> &Self::Target { - &self.0 + &self.inner } } -impl DerefMut for SlotHashes { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } + +pub fn create_account(lamports: u64) -> Account { + Account::new(lamports, SlotHashes::size_of(), &syscall::id()) } #[cfg(test)] mod tests { use super::*; + use crate::hash::hash; + #[test] - fn test_syscall_ids() { + fn test_slot_hashes_id() { let ids = [("Sysca11S1otHashes11111111111111111111111111", id())]; // to get the bytes above: // ids.iter().for_each(|(name, _)| { // dbg!((name, bs58::decode(name).into_vec().unwrap())); // }); assert!(ids.iter().all(|(name, id)| *name == id.to_string())); + assert!(check_id(&id())); + } + + #[test] + fn test_slot_hashes_create_account() { + let lamports = 42; + let account = create_account(lamports); + let slot_hashes = SlotHashes::from(&account); + assert_eq!(slot_hashes, Some(SlotHashes { inner: vec![] })); + let mut slot_hashes = slot_hashes.unwrap(); + for i in 0..MAX_SLOT_HASHES + 1 { + slot_hashes.add( + i as u64, + hash(&[(i >> 24) as u8, (i >> 16) as u8, (i >> 8) as u8, i as u8]), + ); + } + assert_eq!(slot_hashes[0].0, MAX_SLOT_HASHES as u64); + assert_eq!(slot_hashes.len(), MAX_SLOT_HASHES); } }