freespek / solarkraft

Solarkraft: a runtime monitoring tool for Soroban, powered by TLA+ and Apalache
Apache License 2.0
12 stars 0 forks source link

[Epic] M1: Support temporary and persistent storage #54

Open konnov opened 5 months ago

konnov commented 5 months ago

Soroban has unusual features regarding data persistence. We are not accounting for that currently.


Some high-level issues; for the full list, see the milestone

andrey-kuprianov commented 5 months ago

It looks like this is going to be a problem

            durability: xdr.ContractDataDurability.persistent(),

The problem is that durability represents independent keyspaces in ledgers, see here https://developers.stellar.org/docs/learn/smart-contract-internals/state-archival. In particular:

const EXAMPLE_KEY: Symbol = symbol_short!("KEY");
env.storage().persistent().set(&EXAMPLE_KEY, 1);
env.storage().temporary().set(&EXAMPLE_KEY, 2);

env.storage().persistent().get(&EXAMPLE_KEY); // Returns Ok(1)
env.storage().temporary().get(&EXAMPLE_KEY); // Returns Ok(2)

i.e. persistent , temporary , and instance are all independent keyspaces, and they all are used in various combinations in contracts. Thus retrieving only the keys which are persistent would be a heavy mistake on our side.

As our setter contract uses instance storage, but it seems to work when being retrieved as persistent, then most probably persistent and instance are the same from the retrieval perspective... But the storage API still distinguishes between them; probably that's for setting the TTL. Otoh temporary should surely fail to be retrieved as the above code snippet demonstrates.

Here are the examples of usages of all three qualifiers:

One way to test that would be to deploy both Timelock and Token contracts, and to try fetcher on both of them. But I did have problems with the Token contract, specifically with the allowance part... So another way would be to extend the Setter contract with some storage variables of all three kinds, and test on it.

thpani commented 5 months ago

I've extended the setter contract like this:

env.storage().instance().set(&MY_U32, &zero_u32);
env.storage().temporary().set(&MY_U32, &1_u32);
env.storage().persistent().set(&MY_U32, &2_u32);

It appears that persistent, instance and temporary indeed are three separate address spaces:

vscode ➜ /workspaces/solarkraft/ContractExamples (th/qol-tests) $ soroban contract invoke --source-account alice --id CB3YKTHSSIZPVKHBFWSSFHMYU3M4V27GE2CJK6NZVBIXMT2HRZ2HXBJR --network testnet -- get_u32
0
vscode ➜ /workspaces/solarkraft/ContractExamples (th/qol-tests) $ soroban contract read --durability temporary --source-account alice --id CB3YKTHSSIZPVKHBFWSSFHMYU3M4V27GE2CJK6NZVBIXMT2HRZ2HXBJR --network testnet --key MY_U32
MY_U32,1,1954002,1971281
vscode ➜ /workspaces/solarkraft/ContractExamples (th/qol-tests) $ soroban contract read --durability persistent --source-account 
alice --id CB3YKTHSSIZPVKHBFWSSFHMYU3M4V27GE2CJK6NZVBIXMT2HRZ2HXBJR --network testnet --key MY_U32
MY_U32,2,1954002,4027601

Although, for some reason soroban contract read does not support instance durability, so I had to soroban contract invoke instead.

thpani commented 1 month ago

In addition to keying the storage by contract id (#121), we will need to key the storage by durability.