use crate::peer_handler::DumpError;
use ethrex_common::{H256, H512, U256, types::AccountState, utils::keccak};
use ethrex_rlp::encode::RLPEncode;
use secp256k1::{PublicKey, SecretKey};
use std::{
path::{Path, PathBuf},
time::{Duration, SystemTime, UNIX_EPOCH},
};
use tracing::error;
pub fn node_id(public_key: &H512) -> H256 {
keccak(public_key)
}
pub fn current_unix_time() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
pub fn get_msg_expiration_from_seconds(seconds: u64) -> u64 {
(SystemTime::now() + Duration::from_secs(seconds))
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
pub fn is_msg_expired(expiration: u64) -> bool {
(expiration as i64) < (current_unix_time() as i64)
}
pub fn public_key_from_signing_key(signer: &SecretKey) -> H512 {
let public_key = PublicKey::from_secret_key(secp256k1::SECP256K1, signer);
let encoded = public_key.serialize_uncompressed();
H512::from_slice(&encoded[1..])
}
pub fn delete_leaves_folder(datadir: &Path) {
let _ = std::fs::remove_dir_all(get_account_state_snapshots_dir(datadir));
let _ = std::fs::remove_dir_all(get_account_storages_snapshots_dir(datadir));
let _ = std::fs::remove_dir_all(get_code_hashes_snapshots_dir(datadir));
#[cfg(feature = "rocksdb")]
{
let _ = std::fs::remove_dir_all(get_rocksdb_temp_accounts_dir(datadir));
let _ = std::fs::remove_dir_all(get_rocksdb_temp_storage_dir(datadir));
};
}
pub fn get_account_storages_snapshots_dir(datadir: &Path) -> PathBuf {
datadir.join("account_storages_snapshots")
}
pub fn get_account_state_snapshots_dir(datadir: &Path) -> PathBuf {
datadir.join("account_state_snapshots")
}
pub fn get_rocksdb_temp_accounts_dir(datadir: &Path) -> PathBuf {
datadir.join("temp_acc_dir")
}
pub fn get_rocksdb_temp_storage_dir(datadir: &Path) -> PathBuf {
datadir.join("temp_storage_dir")
}
pub fn get_account_state_snapshot_file(directory: &Path, chunk_index: u64) -> PathBuf {
directory.join(format!("account_state_chunk.rlp.{chunk_index}"))
}
pub fn get_account_storages_snapshot_file(directory: &Path, chunk_index: u64) -> PathBuf {
directory.join(format!("account_storages_chunk.rlp.{chunk_index}"))
}
#[cfg(feature = "rocksdb")]
pub fn dump_accounts_to_rocks_db(
path: &Path,
mut contents: Vec<(H256, AccountState)>,
) -> Result<(), rocksdb::Error> {
if contents.is_empty() {
return Ok(());
}
contents.sort_by_key(|(k, _)| *k);
contents.dedup_by_key(|(k, _)| {
let mut buf = [0u8; 32];
buf[..32].copy_from_slice(&k.0);
buf
});
let mut buffer: Vec<u8> = Vec::new();
let writer_options = rocksdb::Options::default();
let mut writer = rocksdb::SstFileWriter::create(&writer_options);
writer.open(std::path::Path::new(&path))?;
for (key, account) in contents {
buffer.clear();
account.encode(&mut buffer);
writer.put(key.0.as_ref(), buffer.as_slice())?;
}
writer.finish()
}
#[cfg(feature = "rocksdb")]
pub fn dump_storages_to_rocks_db(
path: &Path,
mut contents: Vec<(H256, H256, U256)>,
) -> Result<(), rocksdb::Error> {
if contents.is_empty() {
return Ok(());
}
contents.sort();
contents.dedup_by_key(|(k0, k1, _)| {
let mut buffer = [0_u8; 64];
buffer[0..32].copy_from_slice(&k0.0);
buffer[32..64].copy_from_slice(&k1.0);
buffer
});
let writer_options = rocksdb::Options::default();
let mut writer = rocksdb::SstFileWriter::create(&writer_options);
let mut buffer_key = [0_u8; 64];
let mut buffer_storage: Vec<u8> = Vec::new();
writer.open(std::path::Path::new(&path))?;
for (account, slot_hash, slot_value) in contents {
buffer_key[0..32].copy_from_slice(&account.0);
buffer_key[32..64].copy_from_slice(&slot_hash.0);
buffer_storage.clear();
slot_value.encode(&mut buffer_storage);
writer.put(buffer_key.as_ref(), buffer_storage.as_slice())?;
}
writer.finish()
}
pub fn get_code_hashes_snapshots_dir(datadir: &Path) -> PathBuf {
datadir.join("bytecode_hashes_snapshots")
}
pub fn get_code_hashes_snapshot_file(directory: &Path, chunk_index: u64) -> PathBuf {
directory.join(format!("bytecode_hashes_chunk.rlp.{chunk_index}"))
}
pub fn dump_to_file(path: &Path, contents: Vec<u8>) -> Result<(), DumpError> {
std::fs::write(path, &contents)
.inspect_err(|err| error!(%err, ?path, "Failed to dump snapshot to file"))
.map_err(|err| DumpError {
path: path.to_path_buf(),
error: err.kind(),
})
}
pub fn dump_accounts_to_file(
path: &Path,
accounts: Vec<(H256, AccountState)>,
) -> Result<(), DumpError> {
#[cfg(feature = "rocksdb")]
return dump_accounts_to_rocks_db(path, accounts)
.inspect_err(|err| error!("RocksDB SST write error: {err:?}"))
.map_err(|_| DumpError {
path: path.to_path_buf(),
error: std::io::ErrorKind::Other,
});
#[cfg(not(feature = "rocksdb"))]
dump_to_file(path, accounts.encode_to_vec())
}
pub struct AccountsWithStorage {
pub accounts: Vec<H256>,
pub storages: Vec<(H256, U256)>,
}
pub fn dump_storages_to_file(
path: &Path,
storages: Vec<AccountsWithStorage>,
) -> Result<(), DumpError> {
#[cfg(feature = "rocksdb")]
return dump_storages_to_rocks_db(
path,
storages
.into_iter()
.flat_map(|accounts_with_slots| {
accounts_with_slots
.accounts
.into_iter()
.map(|hash| {
accounts_with_slots
.storages
.iter()
.map(move |(slot_hash, slot_value)| (hash, *slot_hash, *slot_value))
.collect::<Vec<_>>()
})
.collect::<Vec<_>>()
})
.flatten()
.collect::<Vec<_>>(),
)
.inspect_err(|err| error!("RocksDB SST write error: {err:?}"))
.map_err(|_| DumpError {
path: path.to_path_buf(),
error: std::io::ErrorKind::Other,
});
#[cfg(not(feature = "rocksdb"))]
dump_to_file(
path,
storages
.into_iter()
.map(|accounts_with_slots| (accounts_with_slots.accounts, accounts_with_slots.storages))
.collect::<Vec<_>>()
.encode_to_vec(),
)
}
pub fn distance(node_id_1: &H256, node_id_2: &H256) -> usize {
let xor = node_id_1 ^ node_id_2;
let distance = U256::from_big_endian(xor.as_bytes());
distance.bits()
}