Skip to content

Commit

Permalink
Merge pull request #17 from soonlabs/feat/init-bank
Browse files Browse the repository at this point in the history
Fix init banks from blockstore not working properly error
  • Loading branch information
MichaelTang23 authored Aug 28, 2024
2 parents 86e5821 + c279c69 commit 38dd167
Show file tree
Hide file tree
Showing 9 changed files with 238 additions and 101 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ split-debuginfo = "packed"

[workspace]
members = ["example", "interface", "storage", "svm/cli", "svm/executor"]
resolver = "2"

[workspace.package]
version = "0.1.0"
Expand Down
18 changes: 11 additions & 7 deletions storage/src/accounts/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use rollups_interface::l2::{bank::BankInfo, storage::TransactionSet};
use solana_entry::entry::Entry;
use solana_runtime::{
bank::{Bank, ExecutedTransactionCounts, NewBankOptions, TotalAccountsStats},
installed_scheduler_pool::BankWithScheduler,
Expand Down Expand Up @@ -104,6 +105,7 @@ impl RollupStorage {
&mut self,
mut result: TransactionsResultWrapper,
batch: &CommitBatch,
entries: &[Entry],
) -> Result<()> {
// In order to avoid a race condition, leaders must get the last
// blockhash *before* recording transactions because recording
Expand All @@ -126,17 +128,19 @@ impl RollupStorage {
&mut result.output.execute_timings,
);

self.register_ticks();
Ok(())
self.register_ticks(entries)
}

fn register_ticks(&self) {
fn register_ticks(&self, entries: &[Entry]) -> Result<()> {
let fork = self.bank_forks.read().unwrap();
let bank_with_schedule = fork.working_bank_with_scheduler();
// TODO: register real ticks later if use scheduled bank
for _ in bank_with_schedule.tick_height()..bank_with_schedule.max_tick_height() {
bank_with_schedule.register_tick(&Default::default());
}

// Skip the first tick because it's the entry that contains transactions
entries.iter().skip(1).for_each(|entry| {
bank_with_schedule.register_tick(&entry.hash);
});

Ok(())
}

fn collect_execution_logs(
Expand Down
134 changes: 131 additions & 3 deletions storage/src/accounts/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Result;
use rollups_interface::l2::storage::StorageOperations;
use rollups_interface::l2::{bank::BankOperations, storage::StorageOperations};
use solana_accounts_db::{
accounts_db::{self, ACCOUNTS_DB_CONFIG_FOR_TESTING},
accounts_index::AccountSecondaryIndexes,
Expand All @@ -9,11 +9,139 @@ use solana_runtime::{
snapshot_bank_utils,
snapshot_utils::{self, create_tmp_accounts_dir_for_tests},
};
use solana_sdk::{clock::Slot, pubkey::Pubkey, system_transaction};
use solana_sdk::{
clock::Slot, pubkey::Pubkey, signature::Keypair, signer::Signer, system_transaction,
transaction::SanitizedTransaction,
};
use solana_svm::runtime_config::RuntimeConfig;
use std::time::{Duration, Instant};

use crate::{config::GlobalConfig, RollupStorage};
use crate::{
blockstore::txs::CommitBatch,
config::GlobalConfig,
execution::TransactionsResultWrapper,
init::default::{DEFAULT_MINT_LAMPORTS, DEFAULT_VALIDATOR_LAMPORTS},
tests::mock::processor::process_transfers_ex,
RollupStorage,
};

#[tokio::test]
async fn init_from_snapshot_works() -> Result<()> {
let ledger_path = tempfile::tempdir()?.into_path();
let mut config = GlobalConfig::new_temp(&ledger_path)?;
config
.storage
.snapshot_config
.full_snapshot_archive_interval_slots = 1; // set snapshot interval to 1
let mut store = RollupStorage::new(config)?;
store.init()?;
let keypairs = store.config.keypairs.clone();

store.set_snapshot_interval(1);
assert_eq!(store.current_height(), 0);

let alice = keypairs.mint_keypair.as_ref().unwrap().clone();
let bob = keypairs.validator_keypair.as_ref().unwrap().clone();
let charlie = Keypair::new().pubkey();
let dave = Keypair::new().pubkey();

const ALICE_INIT_BALANCE: u64 = DEFAULT_MINT_LAMPORTS;
const BOB_INIT_BALANCE: u64 = DEFAULT_VALIDATOR_LAMPORTS;
assert_eq!(store.balance(&alice.pubkey()), ALICE_INIT_BALANCE);
assert_eq!(store.balance(&bob.pubkey()), BOB_INIT_BALANCE);
assert_eq!(store.balance(&charlie), 0);
assert_eq!(store.balance(&dave), 0);

const TO_CHARLIE: u64 = 2000000;
const TO_DAVE: u64 = 1000000;
const FEE: u64 = 5000;

// 1. process transfers
store.bump()?;
let bank = store.bank.clone();

let raw_txs = vec![
system_transaction::transfer(&alice, &charlie, TO_CHARLIE, bank.last_blockhash()),
system_transaction::transfer(&bob, &dave, TO_DAVE, bank.last_blockhash()),
];
let origin_txs = raw_txs
.clone()
.into_iter()
.map(|tx| SanitizedTransaction::from_transaction_for_tests(tx))
.collect::<Vec<_>>();
let results = process_transfers_ex(&store, origin_txs.clone());

// 2. commit
store
.commit(
TransactionsResultWrapper { output: results },
&CommitBatch::new(origin_txs.clone().into()),
)
.await?;

let (bank_height, store_height) = store.get_mixed_heights()?;
assert_eq!(bank_height, 1);
assert_eq!(store_height, Some(1));
assert_eq!(
store.balance(&alice.pubkey()),
ALICE_INIT_BALANCE - TO_CHARLIE - FEE
);
assert_eq!(
store.balance(&bob.pubkey()),
BOB_INIT_BALANCE - TO_DAVE - FEE
);
assert_eq!(store.balance(&charlie), TO_CHARLIE);
assert_eq!(store.balance(&dave), TO_DAVE);

// 3. save and close
store.force_save().await?;
// TODO: sleep is needed here, improve later
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
store.close().await?;

// 4. open with snapshot
assert!(ledger_path.join("snapshots/1/1").exists());
let mut config = GlobalConfig::new(&ledger_path)?;
config.keypairs = keypairs.clone();
let mut store = RollupStorage::new(config)?;
store.init()?;

let (bank_height, store_height) = store.get_mixed_heights()?;
assert_eq!(bank_height, 1);
assert_eq!(store_height, Some(1));
assert_eq!(
store.balance(&alice.pubkey()),
ALICE_INIT_BALANCE - TO_CHARLIE - FEE
);
// TODO: check why bob balance is not `BOB_INIT_BALANCE - TO_DAVE - FEE` ?
assert_eq!(store.balance(&bob.pubkey()), BOB_INIT_BALANCE - TO_DAVE);
assert_eq!(store.balance(&charlie), TO_CHARLIE);
assert_eq!(store.balance(&dave), TO_DAVE);
store.close().await?;

// 5. open without snapshot
let mut config = GlobalConfig::new(&ledger_path)?;
config.keypairs = keypairs;
config.storage.snapshot_config = Default::default(); // disable snapshot
let mut store = RollupStorage::new(config)?;
store.init()?;

let (bank_height, store_height) = store.get_mixed_heights()?;
assert_eq!(bank_height, 1);
assert_eq!(store_height, Some(1));
// TODO: check why bob balance is not `ALICE_INIT_BALANCE - TO_CHARLIE` ?
assert_eq!(
store.balance(&alice.pubkey()),
ALICE_INIT_BALANCE - TO_CHARLIE
);
// TODO: check why bob balance is not `BOB_INIT_BALANCE - TO_DAVE - FEE` ?
assert_eq!(store.balance(&bob.pubkey()), BOB_INIT_BALANCE - TO_DAVE);
assert_eq!(store.balance(&charlie), TO_CHARLIE);
assert_eq!(store.balance(&dave), TO_DAVE);
store.close().await?;

Ok(())
}

#[tokio::test]
#[ignore = "Takes a long time to run"]
Expand Down
65 changes: 47 additions & 18 deletions storage/src/blockstore/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use process::EntriesProcessor;
use rollups_interface::l2::storage::TransactionsResult;
use solana_entry::entry::Entry;
use solana_entry::entry::{next_hash, Entry};
use solana_ledger::{blockstore_processor, shred::Shred};
use solana_sdk::transaction::{SanitizedTransaction, VersionedTransaction};
use solana_sdk::{hash::Hash, transaction::VersionedTransaction};

use crate::{execution::TransactionsResultWrapper, Error, Result, RollupStorage};
use crate::{error::BankError, Error, Result, RollupStorage};

pub mod process;
pub mod txs;

const DEFAULT_NUM_HASHES: u64 = 2;

impl RollupStorage {
pub(crate) fn aligne_blockstore_with_bank_forks(&self) -> Result<()> {
blockstore_processor::process_blockstore_from_root(
Expand All @@ -25,13 +26,8 @@ impl RollupStorage {
Ok(())
}

pub(crate) fn blockstore_save(
&self,
result: &TransactionsResultWrapper,
extras: &[SanitizedTransaction],
) -> Result<()> {
let executed_txs = result.success_txs(extras);
let (data_shreds, code_shreds) = self.transactions_to_shreds(executed_txs)?;
pub(crate) fn blockstore_save(&self, entries: Vec<Entry>) -> Result<()> {
let (data_shreds, code_shreds) = self.transactions_to_shreds(entries)?;
let _data_info =
self.blockstore
.insert_shreds(data_shreds, Some(&self.leader_schedule_cache), true)?;
Expand All @@ -43,9 +39,8 @@ impl RollupStorage {

pub(crate) fn transactions_to_shreds(
&self,
txs: Vec<VersionedTransaction>,
entries: Vec<Entry>,
) -> Result<(Vec<Shred>, Vec<Shred>)> {
let entries = self.transactions_to_entries(txs);
let mut processor = EntriesProcessor::new(Default::default());

processor.process(
Expand All @@ -62,11 +57,45 @@ impl RollupStorage {
)
}

fn transactions_to_entries(&self, transactions: Vec<VersionedTransaction>) -> Vec<Entry> {
let entry = Entry {
pub(crate) fn transactions_to_entries(
&self,
transactions: Vec<VersionedTransaction>,
) -> Result<Vec<Entry>> {
let mut start_hash = self
.bank
.parent()
.ok_or(BankError::BankNotExists(self.bank.parent_slot()))?
.last_blockhash();
let entry = self.new_entry(&start_hash, DEFAULT_NUM_HASHES, transactions);
start_hash = entry.hash;

let mut entries = vec![entry];
for _ in 0..self.config.genesis.ticks_per_slot {
let entry = self.new_entry(&start_hash, DEFAULT_NUM_HASHES, vec![]);
start_hash = entry.hash;
entries.push(entry);
}

Ok(entries)
}

fn new_entry(
&self,
prev_hash: &Hash,
mut num_hashes: u64,
transactions: Vec<VersionedTransaction>,
) -> Entry {
// If you passed in transactions, but passed in num_hashes == 0, then
// next_hash will generate the next hash and set num_hashes == 1
if num_hashes == 0 && !transactions.is_empty() {
num_hashes = 1;
}

let hash = next_hash(prev_hash, num_hashes, &transactions);
Entry {
num_hashes,
hash,
transactions,
..Default::default()
};
vec![entry]
}
}
}
6 changes: 6 additions & 0 deletions storage/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub enum Error {
#[error("Load blockstore failed: {0}")]
LoadBlockstoreFailed(String),

#[error("Init bank forks failed: {0}")]
InitBankForksFailed(String),

#[error("Init config failed: {0}")]
InitConfigFailed(String),

Expand Down Expand Up @@ -47,6 +50,9 @@ pub enum StorageError {

#[error("Invalid Merkle root, slot: {slot}, index: {index}")]
InvalidMerkleRoot { slot: Slot, index: u64 },

#[error("Empty entries hashes")]
EmptyEntriesHashes,
}

#[derive(Debug, Error)]
Expand Down
20 changes: 7 additions & 13 deletions storage/src/impls.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use rollups_interface::l2::{
bank::{BankInfo, BankOperations},
executor::Init,
storage::{StorageOperations, TransactionSet},
storage::{StorageOperations, TransactionSet, TransactionsResult},
};
use solana_gossip::cluster_info::ClusterInfo;
use solana_ledger::{
Expand Down Expand Up @@ -177,24 +177,18 @@ impl StorageOperations for RollupStorage {
origin: &Self::TransactionSet<'a>,
) -> Result<(), Self::Error> {
// TODO: make commit async
self.blockstore_save(&result, origin.transactions())?;
self.bank_commit(result, origin)?;
let executed_txs = result.success_txs(origin.transactions());
let entries = self.transactions_to_entries(executed_txs)?;

self.bank_commit(result, origin, &entries)?;
self.blockstore_save(entries)?;

Ok(())
}

async fn force_save(&mut self) -> Result<(), Self::Error> {
self.bank.freeze();
let _removed_banks = self
.bank_forks
.write()
.unwrap()
.set_root(
self.bank.slot(),
&self.background_service.accounts_background_request_sender,
None,
)
.map_err(|e| BankError::SetRootFailed(e.to_string()))?;
self.set_root(self.bank.slot(), None)?;
Ok(())
}

Expand Down
Loading

0 comments on commit 38dd167

Please sign in to comment.