use std::rc::Rc;
use rusqlite::{params, Connection};
use snapshot_utils::get_ttl;
use soroban_env_host::storage::{EntryWithLiveUntil, SnapshotSource};
use soroban_env_host::xdr::{
AccountEntry, LedgerEntry, LedgerEntryExt, LedgerKey, Limits, PublicKey, ReadXdr,
SequenceNumber, Thresholds, WriteXdr,
};
use soroban_env_host::HostError;
use soroban_simulation::SnapshotSourceWithArchive;
use stellar_xdr::next::Uint256;
pub struct DynamicSnapshot {}
pub mod snapshot_utils {
use rusqlite::{params, Connection};
use sha2::{Digest, Sha256};
use soroban_env_host::xdr::{
Hash, LedgerEntry, LedgerEntryData, LedgerKey, Limits, ReadXdr, WriteXdr,
};
pub fn get_current_ledger_sequence() -> (i32, i64) {
let conn = Connection::open("/tmp/rs_ingestion_temp/stellar.db").unwrap();
let query_string = format!(
"SELECT ledgerseq, closetime FROM ledgerheaders ORDER BY ledgerseq DESC LIMIT 1"
);
let mut stmt = conn.prepare(&query_string).unwrap();
let mut entries = stmt.query(params![]).unwrap();
let row = entries.next().unwrap();
if row.is_none() {
return (0, 0);
}
(
row.unwrap().get(0).unwrap_or(0),
row.unwrap().get(1).unwrap_or(0),
)
}
pub fn get_ttl(key: LedgerKey) -> u32 {
let mut hasher = Sha256::new();
hasher.update(key.to_xdr(Limits::none()).unwrap());
let result = {
let hashed = hasher.finalize().as_slice().try_into().unwrap();
Hash(hashed).to_xdr_base64(Limits::none()).unwrap()
};
let conn = Connection::open("/tmp/rs_ingestion_temp/stellar.db").unwrap();
let query_string = format!("SELECT ledgerentry FROM ttl WHERE keyhash = ?1");
let mut stmt = conn.prepare(&query_string).unwrap();
let mut entries = stmt.query(params![result]).unwrap();
let row = entries.next().unwrap();
if row.is_none() {
return 0;
}
let entry = {
let string: String = row.unwrap().get(0).unwrap();
LedgerEntry::from_xdr_base64(&string, Limits::none()).unwrap()
};
let LedgerEntryData::Ttl(ttl) = entry.data else {
return 0;
};
ttl.live_until_ledger_seq
}
}
impl SnapshotSourceWithArchive for DynamicSnapshot {
fn get_including_archived(
&self,
key: &Rc<LedgerKey>,
) -> std::result::Result<Option<EntryWithLiveUntil>, soroban_env_host::HostError> {
let LedgerKey::ConfigSetting(setting) = key.as_ref() else {
return Err(HostError::from(
soroban_env_host::Error::from_contract_error(0),
));
};
let conn = Connection::open("/tmp/rs_ingestion_temp/stellar.db").unwrap();
let query_string =
format!("SELECT ledgerentry FROM configsettings WHERE configsettingid = ?1");
let mut stmt = conn.prepare(&query_string).unwrap();
let mut entries = stmt
.query(params![setting.config_setting_id as i32])
.unwrap();
let row = entries.next().unwrap();
if row.is_none() {
return Err(HostError::from(
soroban_env_host::Error::from_contract_error(0),
));
}
let entry = {
let string: String = row.unwrap().get(0).unwrap();
LedgerEntry::from_xdr_base64(&string, Limits::none()).unwrap()
};
Ok(Some((Rc::new(entry), Some(u32::MAX))))
}
}
pub fn snapshot_get_universal(
key: Vec<u8>,
) -> Result<Option<(Vec<u8>, Option<u32>)>, soroban_env_host::HostError> {
let key = LedgerKey::from_xdr(key, Limits::none())
.map_err(|_| soroban_env_host::xdr::Error::Invalid)?;
let entry: Option<EntryWithLiveUntil> = match key {
LedgerKey::Trustline(trustline) => {
let PublicKey::PublicKeyTypeEd25519(Uint256(bytes)) = trustline.account_id.0;
let account_id = stellar_strkey::ed25519::PublicKey(bytes).to_string();
let asset_xdr = trustline.asset.to_xdr_base64(Limits::none()).unwrap();
let conn = Connection::open("/tmp/rs_ingestion_temp/stellar.db").unwrap();
let query_string =
format!("SELECT ledgerentry FROM trustlines where accountid = ?1 AND asset = ?2");
let mut stmt = conn.prepare(&query_string).unwrap();
let mut entries = stmt.query(params![account_id, asset_xdr]).unwrap();
let row = entries.next().unwrap();
if row.is_none() {
return Ok(None);
}
let row = row.unwrap();
let xdr_entry: String = row.get(0).unwrap();
let xdr_entry = LedgerEntry::from_xdr_base64(xdr_entry, Limits::none()).unwrap();
Some((Rc::new(xdr_entry), None))
}
LedgerKey::Account(key) => {
let PublicKey::PublicKeyTypeEd25519(ed25519) = key.account_id.0.clone();
let id = stellar_strkey::ed25519::PublicKey(ed25519.0).to_string();
let conn = Connection::open("/tmp/rs_ingestion_temp/stellar.db").unwrap();
let query_string = format!("SELECT balance FROM accounts where accountid = ?1");
let mut stmt = conn.prepare(&query_string).unwrap();
let mut entries = stmt.query(params![id]).unwrap();
let row = entries.next().unwrap();
if row.is_none() {
return Ok(None);
}
let row = row.unwrap();
let entry = LedgerEntry {
last_modified_ledger_seq: 0,
ext: LedgerEntryExt::V0,
data: soroban_env_host::xdr::LedgerEntryData::Account(AccountEntry {
account_id: key.account_id.clone(),
balance: row.get(0).unwrap(),
seq_num: SequenceNumber(0),
num_sub_entries: 0,
inflation_dest: None,
flags: 0,
home_domain: Default::default(),
thresholds: Thresholds([0; 4]),
signers: vec![].try_into().unwrap(),
ext: soroban_env_host::xdr::AccountEntryExt::V0,
}),
};
Some((Rc::new(entry), None))
}
LedgerKey::ContractCode(key) => {
let hash = key.hash.clone();
let conn = Connection::open("/tmp/rs_ingestion_temp/stellar.db").unwrap();
let query_string = format!("SELECT ledgerentry FROM contractcode where hash = ?1");
let mut stmt = conn.prepare(&query_string).unwrap();
let mut entries = stmt
.query(params![hash.to_xdr_base64(Limits::none()).unwrap()])
.unwrap();
let row = entries.next().unwrap();
if row.is_none() {
return Ok(None);
}
let row = row.unwrap();
let xdr_entry: String = row.get(0).unwrap();
let xdr_entry = LedgerEntry::from_xdr_base64(xdr_entry, Limits::none()).unwrap();
Some((
Rc::new(xdr_entry),
Some(get_ttl(LedgerKey::ContractCode(key.clone()))),
))
}
LedgerKey::ContractData(key) => {
let contract = key.contract.clone();
let scval = key.key.clone();
let conn = Connection::open("/tmp/rs_ingestion_temp/stellar.db").unwrap();
let query_string =
format!("SELECT ledgerentry FROM contractdata where contractid = ?1 AND key = ?2");
let mut stmt = conn.prepare(&query_string).unwrap();
let mut entries = stmt
.query(params![
contract.to_xdr_base64(Limits::none()).unwrap(),
scval.to_xdr_base64(Limits::none()).unwrap()
])
.unwrap();
let row = entries.next().unwrap();
if row.is_none() {
return Ok(None);
}
let row = row.unwrap();
let xdr_entry: String = row.get(0).unwrap();
let xdr_entry = LedgerEntry::from_xdr_base64(xdr_entry, Limits::none()).unwrap();
Some((
Rc::new(xdr_entry),
Some(get_ttl(LedgerKey::ContractData(key.clone()))),
))
}
_ => None,
};
if let Some(key) = entry {
Ok(Some((key.0.to_xdr(Limits::none())?, key.1)))
} else {
Ok(None)
}
}
impl SnapshotSource for DynamicSnapshot {
fn get(
&self,
key: &std::rc::Rc<soroban_env_host::xdr::LedgerKey>,
) -> Result<Option<soroban_env_host::storage::EntryWithLiveUntil>, soroban_env_host::HostError>
{
let xdred = snapshot_get_universal(key.as_ref().to_xdr(Limits::none()).unwrap())?;
if let Some(xdr_key) = xdred {
Ok(Some((
Rc::new(LedgerEntry::from_xdr(xdr_key.0, Limits::none())?),
xdr_key.1,
)))
} else {
Ok(None)
}
}
}