1use serde_with::serde_as;
2use std::{
3 fs::{create_dir_all, File},
4 io::{self, Read, Write},
5 path::Path,
6 rc::Rc,
7};
8
9use soroban_env_host::{
10 storage::SnapshotSource,
11 xdr::{LedgerEntry, LedgerKey},
12 HostError, LedgerInfo,
13};
14
15#[derive(thiserror::Error, Debug)]
16pub enum Error {
17 #[error("io")]
18 Io(#[from] io::Error),
19 #[error("serde")]
20 Serde(#[from] serde_json::Error),
21}
22
23#[serde_as]
26#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub struct LedgerSnapshot {
29 pub protocol_version: u32,
30 pub sequence_number: u32,
31 pub timestamp: u64,
32 #[serde_as(as = "serde_with::hex::Hex")]
33 pub network_id: [u8; 32],
34 pub base_reserve: u32,
35 pub min_persistent_entry_ttl: u32,
36 pub min_temp_entry_ttl: u32,
37 pub max_entry_ttl: u32,
38 pub ledger_entries: Vec<(Box<LedgerKey>, (Box<LedgerEntry>, Option<u32>))>,
39}
40
41impl LedgerSnapshot {
42 pub fn from<'a>(
44 info: LedgerInfo,
45 entries: impl IntoIterator<Item = (&'a Box<LedgerKey>, (&'a Box<LedgerEntry>, Option<u32>))>,
46 ) -> Self {
47 let mut s = Self::default();
48 s.set_ledger_info(info);
49 s.set_entries(entries);
50 s
51 }
52
53 #[cfg(feature = "testutils")]
60 pub fn update(&mut self, host: &soroban_env_host::Host) {
61 let _result = host.with_ledger_info(|li| {
62 self.set_ledger_info(li.clone());
63 Ok(())
64 });
65 self.update_entries(&host.get_stored_entries().unwrap());
66 }
67
68 pub fn ledger_info(&self) -> LedgerInfo {
70 LedgerInfo {
71 protocol_version: self.protocol_version,
72 sequence_number: self.sequence_number,
73 timestamp: self.timestamp,
74 network_id: self.network_id,
75 base_reserve: self.base_reserve,
76 min_persistent_entry_ttl: self.min_persistent_entry_ttl,
77 min_temp_entry_ttl: self.min_temp_entry_ttl,
78 max_entry_ttl: self.max_entry_ttl,
79 }
80 }
81
82 pub fn set_ledger_info(&mut self, info: LedgerInfo) {
84 self.protocol_version = info.protocol_version;
85 self.sequence_number = info.sequence_number;
86 self.timestamp = info.timestamp;
87 self.network_id = info.network_id;
88 self.base_reserve = info.base_reserve;
89 self.min_persistent_entry_ttl = info.min_persistent_entry_ttl;
90 self.min_temp_entry_ttl = info.min_temp_entry_ttl;
91 self.max_entry_ttl = info.max_entry_ttl;
92 }
93
94 pub fn entries(
96 &self,
97 ) -> impl IntoIterator<Item = (&Box<LedgerKey>, &(Box<LedgerEntry>, Option<u32>))> {
98 self.ledger_entries.iter().map(|(k, v)| (k, v))
99 }
100
101 pub fn set_entries<'a>(
103 &mut self,
104 entries: impl IntoIterator<Item = (&'a Box<LedgerKey>, (&'a Box<LedgerEntry>, Option<u32>))>,
105 ) {
106 self.ledger_entries.clear();
107 for (k, e) in entries {
108 self.ledger_entries.push((k.clone(), (e.0.clone(), e.1)));
109 }
110 }
111
112 pub fn update_entries<'a>(
116 &mut self,
117 entries: impl IntoIterator<Item = &'a (Rc<LedgerKey>, Option<(Rc<LedgerEntry>, Option<u32>)>)>,
118 ) {
119 for (k, e) in entries {
120 let i = self.ledger_entries.iter().position(|(ik, _)| **ik == **k);
121 if let Some((entry, live_until_ledger)) = e {
122 let new = (
123 Box::new((**k).clone()),
124 (Box::new((**entry).clone()), *live_until_ledger),
125 );
126 if let Some(i) = i {
127 self.ledger_entries[i] = new;
128 } else {
129 self.ledger_entries.push(new);
130 }
131 } else if let Some(i) = i {
132 self.ledger_entries.swap_remove(i);
133 }
134 }
135 }
136}
137
138impl LedgerSnapshot {
139 pub fn read(r: impl Read) -> Result<LedgerSnapshot, Error> {
141 Ok(serde_json::from_reader::<_, LedgerSnapshot>(r)?)
142 }
143
144 pub fn read_file(p: impl AsRef<Path>) -> Result<LedgerSnapshot, Error> {
146 Self::read(File::open(p)?)
147 }
148
149 pub fn write(&self, w: impl Write) -> Result<(), Error> {
151 Ok(serde_json::to_writer_pretty(w, self)?)
152 }
153
154 pub fn write_file(&self, p: impl AsRef<Path>) -> Result<(), Error> {
156 let p = p.as_ref();
157 if let Some(dir) = p.parent() {
158 if !dir.exists() {
159 create_dir_all(dir)?;
160 }
161 }
162 self.write(File::create(p)?)
163 }
164}
165
166impl Default for LedgerSnapshot {
167 fn default() -> Self {
168 Self {
169 protocol_version: 23,
170 sequence_number: Default::default(),
171 timestamp: Default::default(),
172 network_id: Default::default(),
173 base_reserve: Default::default(),
174 ledger_entries: Vec::default(),
175 min_persistent_entry_ttl: Default::default(),
176 min_temp_entry_ttl: Default::default(),
177 max_entry_ttl: Default::default(),
178 }
179 }
180}
181
182impl SnapshotSource for &LedgerSnapshot {
183 fn get(
184 &self,
185 key: &Rc<LedgerKey>,
186 ) -> Result<Option<(Rc<LedgerEntry>, Option<u32>)>, HostError> {
187 match self.ledger_entries.iter().find(|(k, _)| **k == **key) {
188 Some((_, v)) => Ok(Some((Rc::new(*v.0.clone()), v.1))),
189 None => Ok(None),
190 }
191 }
192}
193
194impl SnapshotSource for LedgerSnapshot {
195 fn get(
196 &self,
197 key: &Rc<LedgerKey>,
198 ) -> Result<Option<(Rc<LedgerEntry>, Option<u32>)>, HostError> {
199 <_ as SnapshotSource>::get(&self, key)
200 }
201}