1use anyhow::{anyhow, Result};
2use soroban_env_host::ledger_info::get_key_durability;
3use soroban_env_host::storage::EntryWithLiveUntil;
4use soroban_env_host::xdr::{
5 AccountEntry, AccountEntryExt, AccountEntryExtensionV1, AccountEntryExtensionV1Ext,
6 AccountEntryExtensionV2, AccountEntryExtensionV2Ext, AccountEntryExtensionV3,
7 ContractDataDurability, ExtensionPoint, LedgerEntryData, Liabilities, ScErrorCode, ScErrorType,
8 SponsorshipDescriptor, TimePoint,
9};
10use soroban_env_host::LedgerInfo;
11use soroban_env_host::{storage::SnapshotSource, xdr::LedgerKey, HostError};
12use std::cell::RefCell;
13use std::collections::{BTreeMap, BTreeSet};
14use std::rc::Rc;
15
16use crate::simulation::{
17 simulate_restore_op, RestoreOpSimulationResult, SimulationAdjustmentConfig,
18};
19use crate::NetworkConfig;
20
21pub struct AutoRestoringSnapshotSource<T: SnapshotSource> {
37 snapshot_source: Rc<T>,
38 min_persistent_live_until_ledger: u32,
39 current_ledger_sequence: u32,
40 restored_ledger_keys: RefCell<BTreeSet<Rc<LedgerKey>>>,
41}
42
43impl<T: SnapshotSource> AutoRestoringSnapshotSource<T> {
44 pub fn new(snapshot_source: Rc<T>, ledger_info: &LedgerInfo) -> Result<Self, anyhow::Error> {
45 Ok(Self {
46 snapshot_source,
47 min_persistent_live_until_ledger: ledger_info.min_live_until_ledger_checked(ContractDataDurability::Persistent).ok_or_else(|| anyhow!("minimum persistent live until ledger overflows - ledger info is misconfigured"))?,
48 current_ledger_sequence: ledger_info.sequence_number,
49 restored_ledger_keys: RefCell::new(Default::default()),
50 })
51 }
52
53 pub fn reset_restored_keys(&self) {
55 self.restored_ledger_keys.borrow_mut().clear();
56 }
57
58 pub fn simulate_restore_keys_op(
61 &self,
62 network_config: &NetworkConfig,
63 adjustment_config: &SimulationAdjustmentConfig,
64 ledger_info: &LedgerInfo,
65 ) -> Result<Option<RestoreOpSimulationResult>> {
66 let restored_keys = self.restored_ledger_keys.borrow();
67 if restored_keys.is_empty() {
68 return Ok(None);
69 }
70 simulate_restore_op(
71 self.snapshot_source.as_ref(),
72 network_config,
73 adjustment_config,
74 ledger_info,
75 restored_keys
76 .iter()
77 .map(|k| k.as_ref().clone())
78 .collect::<Vec<LedgerKey>>()
79 .as_ref(),
80 )
81 .map(|res| Some(res))
82 }
83}
84
85impl<T: SnapshotSource> SnapshotSource for AutoRestoringSnapshotSource<T> {
86 fn get(&self, key: &Rc<LedgerKey>) -> Result<Option<EntryWithLiveUntil>, HostError> {
87 let entry_with_live_until = self.snapshot_source.get(key)?;
88 if let Some((entry, live_until)) = entry_with_live_until {
89 if let Some(durability) = get_key_durability(key.as_ref()) {
90 let live_until = live_until.ok_or_else(|| {
91 HostError::from((ScErrorType::Storage, ScErrorCode::InternalError))
93 })?;
94 if live_until < self.current_ledger_sequence {
95 return match durability {
96 ContractDataDurability::Temporary => Ok(None),
97 ContractDataDurability::Persistent => {
98 let mut restored_ledger_keys =
99 self.restored_ledger_keys.try_borrow_mut().map_err(|_| {
100 HostError::from((
101 ScErrorType::Context,
102 ScErrorCode::InternalError,
103 ))
104 })?;
105 restored_ledger_keys.insert(key.clone());
106 Ok(Some((entry, Some(self.min_persistent_live_until_ledger))))
107 }
108 };
109 }
110 }
111 Ok(Some((entry, live_until)))
112 } else {
113 Ok(None)
114 }
115 }
116}
117
118#[derive(Default)]
119struct LedgerEntryUpdater {
120 updated_entries_cache: BTreeMap<Rc<LedgerKey>, Option<EntryWithLiveUntil>>,
121}
122
123impl LedgerEntryUpdater {
124 fn maybe_update_entry(
125 &mut self,
126 key: &Rc<LedgerKey>,
127 entry: Option<EntryWithLiveUntil>,
128 ) -> Option<EntryWithLiveUntil> {
129 if let Some(e) = self.updated_entries_cache.get(key) {
130 return e.clone();
131 }
132 if let Some((entry, live_until)) = &entry {
133 match &entry.data {
134 LedgerEntryData::Account(_) => {
135 let mut updated_entry = (**entry).clone();
136 match &mut updated_entry.data {
137 LedgerEntryData::Account(acc) => {
138 update_account_entry(acc);
139 }
140 _ => (),
141 }
142 let entry_with_live_until = Some((Rc::new(updated_entry), *live_until));
143 self.updated_entries_cache
144 .insert(key.clone(), entry_with_live_until.clone());
145 return entry_with_live_until;
146 }
147 _ => (),
148 }
149 }
150 entry
151 }
152}
153
154enum SnapshotSourceHolder<'a> {
155 Ref(&'a dyn SnapshotSource),
156 Rc(Rc<dyn SnapshotSource>),
157}
158
159impl SnapshotSource for SnapshotSourceHolder<'_> {
160 fn get(&self, key: &Rc<LedgerKey>) -> Result<Option<EntryWithLiveUntil>, HostError> {
161 match self {
162 SnapshotSourceHolder::Ref(r) => r.get(key),
163 SnapshotSourceHolder::Rc(r) => r.get(key),
164 }
165 }
166}
167
168pub(crate) struct SimulationSnapshotSource<'a> {
174 inner_snapshot: SnapshotSourceHolder<'a>,
175 entry_updater: RefCell<LedgerEntryUpdater>,
176}
177
178impl<'a> SimulationSnapshotSource<'a> {
179 pub(crate) fn new(snapshot: &'a dyn SnapshotSource) -> Self {
180 Self {
181 inner_snapshot: SnapshotSourceHolder::Ref(snapshot),
182 entry_updater: RefCell::new(Default::default()),
183 }
184 }
185
186 pub(crate) fn new_from_rc(snapshot: Rc<dyn SnapshotSource>) -> Self {
187 Self {
188 inner_snapshot: SnapshotSourceHolder::Rc(snapshot),
189 entry_updater: RefCell::new(Default::default()),
190 }
191 }
192}
193
194impl SnapshotSource for SimulationSnapshotSource<'_> {
195 fn get(&self, key: &Rc<LedgerKey>) -> Result<Option<EntryWithLiveUntil>, HostError> {
196 Ok(self
197 .entry_updater
198 .borrow_mut()
199 .maybe_update_entry(key, self.inner_snapshot.get(key)?))
200 }
201}
202
203fn update_account_entry(account_entry: &mut AccountEntry) {
204 match &mut account_entry.ext {
205 AccountEntryExt::V0 => {
206 let mut ext = AccountEntryExtensionV1 {
207 liabilities: Liabilities {
208 buying: 0,
209 selling: 0,
210 },
211 ext: AccountEntryExtensionV1Ext::V0,
212 };
213 fill_account_ext_v2(&mut ext, account_entry.signers.len());
214 account_entry.ext = AccountEntryExt::V1(ext);
215 }
216 AccountEntryExt::V1(ext) => {
217 fill_account_ext_v2(ext, account_entry.signers.len());
218 }
219 }
220}
221
222fn fill_account_ext_v2(account_ext_v1: &mut AccountEntryExtensionV1, signers_count: usize) {
223 match &mut account_ext_v1.ext {
224 AccountEntryExtensionV1Ext::V0 => {
225 let mut ext = AccountEntryExtensionV2 {
226 num_sponsored: 0,
227 num_sponsoring: 0,
228 signer_sponsoring_i_ds: vec![SponsorshipDescriptor(None); signers_count]
229 .try_into()
230 .unwrap_or_default(),
231 ext: AccountEntryExtensionV2Ext::V0,
232 };
233 fill_account_ext_v3(&mut ext);
234 account_ext_v1.ext = AccountEntryExtensionV1Ext::V2(ext);
235 }
236 AccountEntryExtensionV1Ext::V2(ext) => fill_account_ext_v3(ext),
237 }
238}
239
240fn fill_account_ext_v3(account_ext_v2: &mut AccountEntryExtensionV2) {
241 match account_ext_v2.ext {
242 AccountEntryExtensionV2Ext::V0 => {
243 account_ext_v2.ext = AccountEntryExtensionV2Ext::V3(AccountEntryExtensionV3 {
244 ext: ExtensionPoint::V0,
245 seq_ledger: 0,
246 seq_time: TimePoint(0),
247 });
248 }
249 AccountEntryExtensionV2Ext::V3(_) => (),
250 }
251}