Skip to content

Commit 2f48e6b

Browse files
committed
Merge remote-tracking branch 'origin' into release/v25-preview
2 parents 4e43bfe + a60b7e8 commit 2f48e6b

File tree

166 files changed

+10588
-13524
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

166 files changed

+10588
-13524
lines changed

CONTRIBUTING.md

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
1-
# Contributing
1+
# How to contribute
22

3-
Contributions are welcome to the soroban-sdk. Please discuss issues to be solved
4-
and potential solutions on issues ahead of opening a pull request.
3+
Check out the [Stellar Contribution Guide](https://github.com/stellar/.github/blob/master/CONTRIBUTING.md) that applies to all Stellar projects.
4+
5+
## Contributing to soroban-sdk
6+
7+
Please discuss issues to be solved and potential solutions on issues ahead of opening a pull request.
8+
9+
For more information about Soroban, see the [Soroban documentation]. For API
10+
documentation, see [docs.rs].
11+
12+
[Soroban documentation]: https://soroban.stellar.org
13+
[docs.rs]: https://docs.rs/soroban-sdk
14+
15+
## Repository Structure
16+
17+
This repository is a Cargo workspace containing multiple crates. The `soroban-sdk`
18+
crate is the main SDK that everything revolves around. The other crates in the
19+
workspace either support `soroban-sdk` or serve smaller specialized roles.
20+
21+
## Test Vectors
22+
23+
The `tests/` directory contains small sample contracts called "test vectors" that
24+
are used to test the SDK. Each subdirectory is a minimal contract that exercises
25+
specific SDK features. These contracts are compiled to WASM and used in integration
26+
tests. Binary stability is maintained by building test vectors with the minimum
27+
supported Rust version (MSRV).
28+
29+
The `tests-expanded/` directory contains expanded macro output from the test vector
30+
contracts, generated by `make expand-tests`. This is useful for reviewing changes
31+
to macro-generated code and helps surface breaking changes in code generation.
532

633
## Development Environment Setup
734

@@ -51,7 +78,27 @@ Run tests:
5178
make test
5279
```
5380

54-
Build and test on changes:
81+
Clean build artifacts:
82+
```
83+
make clean
84+
```
85+
86+
Print the minimum supported Rust version:
87+
```
88+
make msrv
89+
```
90+
91+
Expand macros in test contracts to review generated code:
92+
```
93+
make expand-tests
94+
```
95+
96+
Build fuzz tests (requires nightly):
97+
```
98+
make build-fuzz
99+
```
100+
101+
Run tests with MIRI for memory safety checking (requires nightly):
55102
```
56-
make watch
103+
make miri
57104
```

soroban-ledger-snapshot/src/lib.rs

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use serde_with::serde_as;
1+
use serde::Deserialize;
2+
use serde_with::{serde_as, DeserializeAs, SerializeAs};
23
use std::{
34
fs::{create_dir_all, File},
4-
io::{self, Read, Write},
5+
io::{self, BufReader, Read, Write},
56
path::Path,
67
rc::Rc,
78
};
@@ -12,6 +13,9 @@ use soroban_env_host::{
1213
HostError, LedgerInfo,
1314
};
1415

16+
#[cfg(test)]
17+
mod tests;
18+
1519
#[derive(thiserror::Error, Debug)]
1620
pub enum Error {
1721
#[error("io")]
@@ -35,11 +39,80 @@ pub struct LedgerSnapshot {
3539
pub min_persistent_entry_ttl: u32,
3640
pub min_temp_entry_ttl: u32,
3741
pub max_entry_ttl: u32,
42+
#[serde_as(as = "LedgerEntryVec")]
3843
pub ledger_entries: Vec<(Box<LedgerKey>, (Box<LedgerEntry>, Option<u32>))>,
3944
}
4045

46+
/// Extended ledger entry that includes the live util ledger sequence. Provides a more compact
47+
/// form of the tuple used in [`LedgerSnapshot::ledger_entries`], to reduce the size of the snapshot
48+
/// when serialized to JSON.
49+
#[derive(Debug, Clone, serde::Deserialize)]
50+
struct LedgerEntryExt {
51+
entry: Box<LedgerEntry>,
52+
live_until: Option<u32>,
53+
}
54+
55+
/// Extended ledger entry that includes the live util ledger sequence, and the entry by reference.
56+
/// Used to reduce memory usage during serialization.
57+
#[derive(serde::Serialize)]
58+
struct LedgerEntryExtRef<'a> {
59+
entry: &'a Box<LedgerEntry>, // Reference = no clone
60+
live_until: Option<u32>,
61+
}
62+
63+
struct LedgerEntryVec;
64+
65+
impl<'a> SerializeAs<Vec<(Box<LedgerKey>, (Box<LedgerEntry>, Option<u32>))>> for LedgerEntryVec {
66+
fn serialize_as<S>(
67+
source: &Vec<(Box<LedgerKey>, (Box<LedgerEntry>, Option<u32>))>,
68+
serializer: S,
69+
) -> Result<S::Ok, S::Error>
70+
where
71+
S: serde::Serializer,
72+
{
73+
use serde::ser::SerializeSeq;
74+
let mut seq = serializer.serialize_seq(Some(source.len()))?;
75+
for (_, (entry, live_until)) in source {
76+
seq.serialize_element(&LedgerEntryExtRef {
77+
entry,
78+
live_until: *live_until,
79+
})?;
80+
}
81+
seq.end()
82+
}
83+
}
84+
85+
impl<'de> DeserializeAs<'de, Vec<(Box<LedgerKey>, (Box<LedgerEntry>, Option<u32>))>>
86+
for LedgerEntryVec
87+
{
88+
fn deserialize_as<D>(
89+
deserializer: D,
90+
) -> Result<Vec<(Box<LedgerKey>, (Box<LedgerEntry>, Option<u32>))>, D::Error>
91+
where
92+
D: serde::Deserializer<'de>,
93+
{
94+
#[derive(serde::Deserialize)]
95+
#[serde(untagged)]
96+
enum Format {
97+
V2(Vec<LedgerEntryExt>),
98+
V1(Vec<(Box<LedgerKey>, (Box<LedgerEntry>, Option<u32>))>),
99+
}
100+
101+
match Format::deserialize(deserializer)? {
102+
Format::V2(entries) => Ok(entries
103+
.into_iter()
104+
.map(|LedgerEntryExt { entry, live_until }| {
105+
let key = Box::new(entry.to_key());
106+
(key, (entry, live_until))
107+
})
108+
.collect()),
109+
Format::V1(entries) => Ok(entries),
110+
}
111+
}
112+
}
113+
41114
impl LedgerSnapshot {
42-
// Create a ledger snapshot from ledger info and a set of entries.
115+
/// Create a [`LedgerSnapshot`] from [`LedgerInfo`] and a set of entries.
43116
pub fn from<'a>(
44117
info: LedgerInfo,
45118
entries: impl IntoIterator<Item = (&'a Box<LedgerKey>, (&'a Box<LedgerEntry>, Option<u32>))>,
@@ -65,7 +138,7 @@ impl LedgerSnapshot {
65138
self.update_entries(&host.get_stored_entries().unwrap());
66139
}
67140

68-
// Get the ledger info in the snapshot.
141+
/// Get the ledger info in the snapshot.
69142
pub fn ledger_info(&self) -> LedgerInfo {
70143
LedgerInfo {
71144
protocol_version: self.protocol_version,
@@ -136,22 +209,23 @@ impl LedgerSnapshot {
136209
}
137210

138211
impl LedgerSnapshot {
139-
// Read in a [`LedgerSnapshot`] from a reader.
212+
/// Read in a [`LedgerSnapshot`] from a reader.
140213
pub fn read(r: impl Read) -> Result<LedgerSnapshot, Error> {
141214
Ok(serde_json::from_reader::<_, LedgerSnapshot>(r)?)
142215
}
143216

144-
// Read in a [`LedgerSnapshot`] from a file.
217+
/// Read in a [`LedgerSnapshot`] from a file.
145218
pub fn read_file(p: impl AsRef<Path>) -> Result<LedgerSnapshot, Error> {
146-
Self::read(File::open(p)?)
219+
let reader = BufReader::new(File::open(p)?);
220+
Self::read(reader)
147221
}
148222

149-
// Write a [`LedgerSnapshot`] to a writer.
223+
/// Write a [`LedgerSnapshot`] to a writer.
150224
pub fn write(&self, w: impl Write) -> Result<(), Error> {
151225
Ok(serde_json::to_writer_pretty(w, self)?)
152226
}
153227

154-
// Write a [`LedgerSnapshot`] to file.
228+
/// Write a [`LedgerSnapshot`] to file.
155229
pub fn write_file(&self, p: impl AsRef<Path>) -> Result<(), Error> {
156230
let p = p.as_ref();
157231
if let Some(dir) = p.parent() {
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use soroban_env_host::{
2+
storage::SnapshotSource,
3+
xdr::{LedgerKey, Limits, ReadXdr},
4+
};
5+
6+
use crate::LedgerSnapshot;
7+
use std::rc::Rc;
8+
9+
// Tuple of (ledger key in xdr base64, live_until_ledger_seq u32) for the entries in the test snapshots
10+
const TEST_SNAPSHOT_XDR: [(&str, u32); 4] = [
11+
("AAAABgAAAAEltPzYWa7C+mNIQ4xImzw8EMmLbSG+T9PLMMtolT75dwAAABQAAAAB", 10000000),
12+
("AAAABgAAAAEltPzYWa7C+mNIQ4xImzw8EMmLbSG+T9PLMMtolT75dwAAABAAAAABAAAAAgAAAA8AAAAHQmFsYW5jZQAAAAASAAAAAa3vzlmu5Slo92Bh1JTCUlt1ZZ+kKWpl9JnvKeVkd+SWAAAAAQ==", 10000001),
13+
("AAAACAAAAAA=", 10000002),
14+
("AAAAAAAAAAA7mRE4Dv6Yi6CokA6xz+RPNm99vpRr7QdyQPf2JN8VxQ==", 10000003)
15+
];
16+
17+
#[test]
18+
fn test_snapshot_from_v1() {
19+
let snapshot = LedgerSnapshot::read_file("./test_data/snapshot_v1.json").unwrap();
20+
21+
for (xdr_key, live_until_ledger_seq) in TEST_SNAPSHOT_XDR.iter() {
22+
let ledger_key = LedgerKey::from_xdr_base64(xdr_key, Limits::none()).unwrap();
23+
let entry = snapshot.get(&Rc::new(ledger_key)).unwrap();
24+
assert!(entry.is_some());
25+
assert_eq!(entry.unwrap().1, Some(live_until_ledger_seq.clone()));
26+
}
27+
28+
let expected_str = std::fs::read_to_string("./test_data/snapshot_v2.json").unwrap();
29+
let mut written = Vec::new();
30+
snapshot.write(&mut written).unwrap();
31+
let written_str = String::from_utf8(written).unwrap();
32+
33+
let expected_normalized = expected_str.replace("\r\n", "\n");
34+
let written_normalized = written_str.replace("\r\n", "\n");
35+
assert_eq!(written_normalized, expected_normalized);
36+
}
37+
38+
#[test]
39+
fn test_snapshot_roundtrip() {
40+
let snapshot = LedgerSnapshot::read_file("./test_data/snapshot_v2.json").unwrap();
41+
42+
for (xdr_key, live_until_ledger_seq) in TEST_SNAPSHOT_XDR.iter() {
43+
let ledger_key = LedgerKey::from_xdr_base64(xdr_key, Limits::none()).unwrap();
44+
let entry = snapshot.get(&Rc::new(ledger_key)).unwrap();
45+
assert!(entry.is_some());
46+
assert_eq!(entry.unwrap().1, Some(live_until_ledger_seq.clone()));
47+
}
48+
49+
let expected_str = std::fs::read_to_string("./test_data/snapshot_v2.json").unwrap();
50+
let mut written = Vec::new();
51+
snapshot.write(&mut written).unwrap();
52+
let written_str = String::from_utf8(written).unwrap();
53+
54+
let expected_normalized = expected_str.replace("\r\n", "\n");
55+
let written_normalized = written_str.replace("\r\n", "\n");
56+
assert_eq!(written_normalized, expected_normalized);
57+
}

0 commit comments

Comments
 (0)