Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ serde_json = "1.0"
serde_with = "3.16"
serial_test = "3.2"
sha3 = "0.10.8"
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "6eae52b" }
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "3ff1b2ea" }
solana-account-decoder = { version = "2.2" }
solana-account-decoder-client-types = { version = "2.2" }
solana-account-info = { version = "2.2" }
Expand Down Expand Up @@ -211,6 +211,10 @@ tonic-build = "0.9.2"
tracing = "0.1"
tracing-log = { version = "0.2", features = ["log-tracer"] }
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
twox-hash = { version = "2.1", default-features = false, features = [
"xxhash3_64",
"alloc",
] }
url = "2.5.0"

# SPL Token crates used across the workspace
Expand All @@ -231,7 +235,7 @@ version = "0.22.0"
# some solana dependencies have solana-storage-proto as dependency
# we need to patch them with our version, because they use protobuf-src v1.1.0
# and we use protobuf-src v2.1.1. Otherwise compilation fails
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "6eae52b" }
solana-account = { git = "https://github.com/magicblock-labs/solana-account.git", rev = "3ff1b2ea" }
solana-storage-proto = { path = "./storage-proto" }
solana-svm = { git = "https://github.com/magicblock-labs/magicblock-svm.git", rev = "bdbaac86" }
# Fork is used to enable `disable_manual_compaction` usage
Expand Down
3 changes: 3 additions & 0 deletions magicblock-accounts-db/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ thiserror = { workspace = true }
tracing = { workspace = true }
magicblock-config = { workspace = true }

# misc
twox-hash = { workspace = true }

[dev-dependencies]
tempfile = { workspace = true }
tracing-subscriber = { workspace = true }
Expand Down
23 changes: 22 additions & 1 deletion magicblock-accounts-db/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{fs, path::Path, sync::Arc, thread};
use std::{fs, hash::Hasher, path::Path, sync::Arc, thread};

use error::{AccountsDbError, LogErr};
use index::{
Expand All @@ -13,6 +13,7 @@ use solana_account::{
use solana_pubkey::Pubkey;
use storage::AccountsStorage;
use tracing::{error, info, warn};
use twox_hash::xxhash3_64;

use crate::{snapshot::SnapshotManager, traits::AccountsBank};

Expand Down Expand Up @@ -374,6 +375,26 @@ impl AccountsDb {
pub fn write_lock(&self) -> GlobalSyncLock {
self.write_lock.clone()
}

/// Computes a deterministic checksum of all active accounts.
///
/// Iterates all accounts in key-sorted order (via LMDB) and hashes both
/// pubkey and serialized account data using xxHash3. Returns a 64-bit hash
/// suitable for verifying state consistency across nodes.
///
/// Acquires the write lock to ensure a consistent snapshot of the state.
pub fn checksum(&self) -> u64 {
let _locked = self.write_lock.write();
let mut hasher = xxhash3_64::Hasher::new();
for (pubkey, acc) in self.iter_all() {
let Some(borrowed) = acc.as_borrowed() else {
continue;
};
hasher.write(pubkey.as_ref());
hasher.write(borrowed.buffer());
}
hasher.finish()
}
}

impl AccountsBank for AccountsDb {
Expand Down
5 changes: 2 additions & 3 deletions magicblock-accounts-db/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,9 @@ impl AccountsStorage {
Ok(())
}

/// Returns the total expected size of the file in bytes (Header + Data).
/// Returns the total occupied size of the storage file in bytes.
pub(crate) fn size_bytes(&self) -> u64 {
(self.header().capacity_blocks as u64 * self.block_size as u64)
+ METADATA_STORAGE_SIZE as u64
self.active_segment().len() as u64
}

pub(crate) fn block_size(&self) -> usize {
Expand Down
63 changes: 63 additions & 0 deletions magicblock-accounts-db/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,69 @@ fn test_database_reset() {
assert_eq!(adb_reset.account_count(), 0);
}

#[test]
fn test_checksum_deterministic_across_dbs() {
// Two independent DBs with identical accounts must produce identical checksums
let dir1 = tempfile::tempdir().unwrap();
let dir2 = tempfile::tempdir().unwrap();
let config = AccountsDbConfig::default();

let db1 = AccountsDb::new(&config, dir1.path(), 0).unwrap();
let db2 = AccountsDb::new(&config, dir2.path(), 0).unwrap();

// Insert same accounts into both DBs
for i in 0..50 {
let pubkey = Pubkey::new_unique();
let mut account = AccountSharedData::new(LAMPORTS, SPACE, &OWNER);
account.data_as_mut_slice()[..8]
.copy_from_slice(&(i as u64).to_le_bytes());
db1.insert_account(&pubkey, &account).unwrap();
db2.insert_account(&pubkey, &account).unwrap();
}

assert_eq!(
db1.checksum(),
db2.checksum(),
"checksums must match for identical state"
);
}

#[test]
fn test_checksum_detects_state_change() {
let env = TestEnv::new();

// Create initial state
let mut accounts: Vec<_> = (0..20)
.map(|_| {
let acc = env.create_and_insert_account();
(acc.pubkey, acc.account)
})
.collect();

let original_checksum = env.checksum();

// Modify a single account's data
accounts[5].1.data_as_mut_slice()[0] ^= 0xFF;
env.insert_account(&accounts[5].0, &accounts[5].1).unwrap();

assert_ne!(
env.checksum(),
original_checksum,
"checksum must detect single account modification"
);

// Modify lamports on a different account
accounts[10].1.set_lamports(1_000_000);
env.insert_account(&accounts[10].0, &accounts[10].1)
.unwrap();

assert_ne!(
env.checksum(),
original_checksum,
"checksum must detect lamport change"
);
}

// ==============================================================
// TEST UTILITIES
// ==============================================================
Expand Down
Loading
Loading