Skip to content

Commit 13fc2d7

Browse files
update ttl extension guide to follow auto-restoration logic (#2189)
* update ttl extension guide to follow auto-restoration logic Fixes #2075 * Update docs/build/guides/archival/test-ttl-extension.mdx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs/build/guides/archival/test-ttl-extension.mdx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent a173435 commit 13fc2d7

1 file changed

Lines changed: 52 additions & 41 deletions

File tree

docs/build/guides/archival/test-ttl-extension.mdx

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ In order to test contracts that extend the contract data [TTL](../../../learn/fu
88

99
## Example
1010

11-
Follow along the [example](https://github.com/stellar/soroban-examples/blob/main/ttl/src/lib.rs) that tests TTL extensions. The example has extensive comments, this document just highlights the most important parts.
11+
Follow along the [example](https://github.com/stellar/soroban-examples/blob/v23.0.0/ttl/src/lib.rs) that tests TTL extensions. The example has extensive comments, this document just highlights the most important parts.
1212

1313
We use a very simple contract that only extends an entry for every Soroban storage type:
1414

@@ -51,30 +51,38 @@ The focus of the example is the tests, so the following code snippets come from
5151
It's a good idea to define the custom values of TTL-related network settings, since the defaults are defined by the SDK and aren't immediately obvious for the reader of the tests:
5252

5353
```rust
54-
env.ledger().with_mut(|li| {
55-
// Current ledger sequence - the TTL is the number of
56-
// ledgers from the `sequence_number` (exclusive) until
57-
// the last ledger sequence where entry is still considered
58-
// alive.
59-
li.sequence_number = 100_000;
60-
// Minimum TTL for persistent entries - new persistent (and instance)
61-
// entries will have this TTL when created.
62-
li.min_persistent_entry_ttl = 500;
63-
// Minimum TTL for temporary entries - new temporary
64-
// entries will have this TTL when created.
65-
li.min_temp_entry_ttl = 100;
66-
// Maximum TTL of any entry. Note, that entries can have their TTL
67-
// extended indefinitely, but each extension can be at most
68-
// `max_entry_ttl` ledger from the current `sequence_number`.
69-
li.max_entry_ttl = 15000;
70-
});
54+
/// Create an environment with specific values of network settings.
55+
fn create_env() -> Env {
56+
let env = Env::default();
57+
env.ledger().with_mut(|li| {
58+
// Current ledger sequence - the TTL is the number of
59+
// ledgers from the `sequence_number` (exclusive) until
60+
// the last ledger sequence where entry is still considered
61+
// alive.
62+
li.sequence_number = 100_000;
63+
// Minimum TTL for persistent entries - new persistent (and instance)
64+
// entries will have this TTL when created.
65+
li.min_persistent_entry_ttl = 500;
66+
// Minimum TTL for temporary entries - new temporary
67+
// entries will have this TTL when created.
68+
li.min_temp_entry_ttl = 100;
69+
// Maximum TTL of any entry. Note, that entries can have their TTL
70+
// extended indefinitely, but each extension can be at most
71+
// `max_entry_ttl` ledger from the current `sequence_number`.
72+
li.max_entry_ttl = 15000;
73+
});
74+
env
75+
}
7176
```
7277

7378
You could also use the current [network settings](../../../networks/resource-limits-fees.mdx#resource-fees) when setting up the tests, but keep in mind that these are subject to change, and the contract should be able to work with any values of these settings.
7479

75-
Now we run a test scenario that verifies the TTL extension logic (see [`test_extend_ttl_behavior`](https://github.com/stellar/soroban-examples/blob/f595fb5df06058ec0b9b829e9e4d0fe0513e0aa8/ttl/src/test.rs#L38) test for the full scenario). First, we setup the data and ensure that the initial TTL values correspond to the network settings we've defined above:
80+
Now we run a test scenario that verifies the TTL extension logic (see [`test_extend_ttl_behavior`](https://github.com/stellar/soroban-examples/blob/v23.0.0/ttl/src/test.rs#L38) test for the full scenario). First, we setup the data and ensure that the initial TTL values correspond to the network settings we've defined above:
7681

7782
```rust
83+
// Create initial entries and make sure their TTLs correspond to
84+
// `min_persistent_entry_ttl` and `min_temp_entry_ttl` values set in
85+
// `create_env()`.
7886
client.setup();
7987
env.as_contract(&contract_id, || {
8088
// Note, that TTL doesn't include the current ledger, but when entry
@@ -87,7 +95,7 @@ env.as_contract(&contract_id, || {
8795
});
8896
```
8997

90-
Notice, that we use `env.as_contract` in order to access the contract's storage.
98+
Notice, that we use `env.as_contract(...)` in order to access the contract's storage.
9199

92100
Then we call the TTL extension operations and verify that they behave as expected, for example:
93101

@@ -97,6 +105,8 @@ client.extend_persistent();
97105
env.as_contract(&contract_id, || {
98106
assert_eq!(env.storage().persistent().get_ttl(&DataKey::MyKey), 5000);
99107
});
108+
109+
// ... repeat with `client.extend_instance` and `client.extend_temporary` ...
100110
```
101111

102112
In order to test the extension thresholds (i.e. maximum current TTL that requires extension), we need to increase the ledger sequence number:
@@ -118,6 +128,7 @@ env.as_contract(&contract_id, || {
118128
Then we can extend the entries again and ensure that only entries that are below threshold have been extended (specifically, persistent and temporary entries in this example):
119129

120130
```rust
131+
// Extend TTL of all the entries.
121132
client.extend_persistent();
122133
client.extend_instance();
123134
client.extend_temporary();
@@ -131,42 +142,42 @@ env.as_contract(&contract_id, || {
131142
});
132143
```
133144

134-
Soroban SDK also emulates the behavior for the entries that have their TTL expired. Temporary entries behave 'as if' they were deleted (see [`test_temp_entry_removal`](https://github.com/stellar/soroban-examples/blob/f595fb5df06058ec0b9b829e9e4d0fe0513e0aa8/ttl/src/test.rs#L112) test for the full scenario):
145+
Soroban SDK also emulates the behavior for the entries that have their TTL expired. Temporary entries behave 'as if' they were deleted (see [`test_temp_entry_removal`](https://github.com/stellar/soroban-examples/blob/v23.0.0/ttl/src/test.rs#L112) test for the full scenario):
135146

136147
```rust
148+
// Extend the temporary entry TTL to 7000 ledgers.
137149
client.extend_temporary();
138150
// Bump the ledger sequence by 7001 ledgers (one ledger past TTL).
139151
env.ledger().with_mut(|li| {
140-
li.sequence_number = 100_000 + 7001;
152+
li.sequence_number += 7001;
141153
});
142154
// Now the entry is no longer present in the environment.
143155
env.as_contract(&contract_id, || {
144156
assert_eq!(env.storage().temporary().has(&DataKey::MyKey), false);
145157
});
146158
```
147159

148-
Persistent entries are more subtle: when a transaction that is executed on-chain contains a persistent entry that has been archived (i.e. it has its TTL expired) in the footprint, then the Soroban environment will not even be instantiated. Since this behavior is not directly reproducible in test environment, instead an irrecoverable 'internal' error will be produced as soon as an archived entry is accessed, and the test will `panic`:
160+
Persistent entries are more subtle: when a transaction that is executed on-chain contains a persistent entry that has been archived (i.e., it has its TTL expired) in the footprint, then the entry will be automatically restored. Automatic restoration is mostly transparent, the main side effect is the increased fees. (see [`test_persistent_entry_auto_restored`](https://github.com/stellar/soroban-examples/blob/v23.0.0/ttl/src/test.rs#L136) test for the full scenario):
149161

150162
```rust
151-
#[test]
152-
#[should_panic(expected = "[testing-only] Accessed contract instance key that has been archived.")]
153-
fn test_persistent_entry_archival() {
154-
let env = create_env();
155-
let contract_id = env.register(TtlContract, ());
156-
let client = TtlContractClient::new(&env, &contract_id);
157-
client.setup();
158-
// Extend the instance TTL to 10000 ledgers.
159-
client.extend_instance();
160-
// Bump the ledger sequence by 10001 ledgers (one ledger past TTL).
161-
env.ledger().with_mut(|li| {
162-
li.sequence_number = 100_000 + 10_001;
163-
});
164-
// Now any call involving the expired contract (such as `extend_instance`
165-
// call here) will panic as soon as that contract is accessed.
166-
client.extend_instance();
167-
}
163+
// Extend the persistent entry TTL to 5000 ledgers.
164+
client.extend_persistent();
165+
// Bump the ledger sequence by 5001 ledgers (one ledger past TTL).
166+
env.ledger().with_mut(|li| {
167+
li.sequence_number += 5001;
168+
});
169+
// Now any call involving the expired persistent data will cause automatic
170+
// restoration.
171+
client.extend_persistent();
172+
173+
// Notice that disk read bytes and write bytes are increased even though the
174+
// function itself is read-only.
175+
let resources = env.cost_estimate().resources();
176+
assert!(resources.disk_read_bytes > 0);
177+
assert!(resources.write_bytes > 0);
178+
assert_eq!(resources.write_entries, 1);
168179
```
169180

170181
## Testing TTL extension for other contract instances
171182

172-
Sometimes a contract may want to extend TTL of another contracts and/or their Wasm entries (usually that would happen in factory contracts). This logic may be covered in a similar fashion to the example above using `env.deployer().get_contract_instance_ttl(&contract)` to get TTL of any contract's instance, and `env.deployer().get_contract_code_ttl(&contract)` to get TTL of any contract's Wasm entry. You can find an example of using these function in the SDK [test suite](https://github.com/stellar/rs-soroban-sdk/blob/ff05c3d4cbed97db50142372e9d7a4fa4a8d1d5d/soroban-sdk/src/tests/storage_testutils.rs#L76).
183+
Sometimes a contract may want to extend TTL of another contracts and/or their Wasm entries (usually that would happen in factory contracts). This logic may be covered in a similar fashion to the example above using `env.deployer().get_contract_instance_ttl(&contract)` to get TTL of any contract's instance, and `env.deployer().get_contract_code_ttl(&contract)` to get TTL of any contract's Wasm entry. You can find an example of using these functions in the SDK [test suite](https://github.com/stellar/rs-soroban-sdk/blob/v23.4.1/soroban-sdk/src/tests/storage_testutils.rs#L76).

0 commit comments

Comments
 (0)