oasis_runtime_sdk_contracts/
store.rs

1//! Contract storage.
2use oasis_contract_sdk_types::storage::StoreKind;
3use oasis_runtime_sdk::{
4    context::Context,
5    dispatcher,
6    keymanager::{self, StateKey},
7    state::CurrentState,
8    storage::{self, Store},
9    subcall,
10};
11
12use crate::{state, types, Error, MODULE_NAME};
13
14/// Confidential store key pair ID domain separation context base.
15pub const CONFIDENTIAL_STORE_KEY_PAIR_ID_CONTEXT_BASE: &[u8] =
16    b"oasis-runtime-sdk/contracts: state";
17
18const CONTEXT_KEY_CONFIDENTIAL_STORE_INSTANCE_COUNT: &str = "contracts.ConfidentialStoreCounter";
19
20/// Run a closure with the contract instance store.
21///
22/// Confidential stores will only work when private key queries for the key
23/// manager are available. In others, an error will be returned describing the
24/// particular key manager failure.
25pub fn with_instance_store<C, F, R>(
26    ctx: &C,
27    instance_info: &types::Instance,
28    store_kind: StoreKind,
29    f: F,
30) -> Result<R, Error>
31where
32    C: Context,
33    F: FnOnce(&mut dyn Store) -> R,
34{
35    // subcall_count, instance_count, round are all used as nonce derivation context
36    // in the confidential store. Along with confidential_key, they all need ctx,
37    // which becomes unavailable after the first PrefixStore is created, since that
38    // keeps a mutable reference to it (via runtime_state()).
39    let subcall_count = if let StoreKind::Confidential = store_kind {
40        subcall::get_current_subcall_depth(ctx)
41    } else {
42        0
43    };
44    let instance_count: Option<usize> = if let StoreKind::Confidential = store_kind {
45        CurrentState::with(|state| {
46            let cnt = *state
47                .block_value(CONTEXT_KEY_CONFIDENTIAL_STORE_INSTANCE_COUNT)
48                .or_default();
49            state
50                .block_value(CONTEXT_KEY_CONFIDENTIAL_STORE_INSTANCE_COUNT)
51                .set(cnt + 1);
52            Some(cnt)
53        })
54    } else {
55        None
56    };
57    let round = ctx.runtime_header().round;
58    let confidential_key: Option<StateKey> = if let StoreKind::Confidential = store_kind {
59        let kmgr_client = ctx.key_manager().ok_or(Error::Unsupported)?;
60        let kid = keymanager::get_key_pair_id([
61            CONFIDENTIAL_STORE_KEY_PAIR_ID_CONTEXT_BASE,
62            &instance_info.id.to_storage_key(),
63        ]);
64        let kp = kmgr_client
65            .get_or_create_keys(kid)
66            .map_err(|err| Error::Abort(dispatcher::Error::KeyManagerFailure(err)))?;
67        Some(kp.state_key)
68    } else {
69        None
70    };
71
72    with_instance_raw_store(instance_info, store_kind, |contract_state| {
73        match store_kind {
74            // For public storage we use a hashed store using the Blake3 hash function.
75            StoreKind::Public => Ok(f(&mut storage::HashedStore::<_, blake3::Hasher>::new(
76                contract_state,
77            ))),
78
79            StoreKind::Confidential => {
80                let mut confidential_store = storage::ConfidentialStore::new_with_key(
81                    contract_state,
82                    confidential_key.unwrap().0,
83                    &[
84                        round.to_le_bytes().as_slice(),
85                        subcall_count.to_le_bytes().as_slice(),
86                        instance_count.unwrap().to_le_bytes().as_slice(),
87                    ],
88                );
89                Ok(f(&mut confidential_store))
90            }
91        }
92    })
93}
94
95/// Run a closure with the per-contract-instance raw (public) store.
96pub fn with_instance_raw_store<F, R>(
97    instance_info: &types::Instance,
98    store_kind: StoreKind,
99    f: F,
100) -> R
101where
102    F: FnOnce(&mut dyn Store) -> R,
103{
104    CurrentState::with_store(|store| {
105        let store = storage::PrefixStore::new(store, &MODULE_NAME);
106        let instance_prefix = instance_info.id.to_storage_key();
107        let contract_state = storage::PrefixStore::new(
108            storage::PrefixStore::new(store, &state::INSTANCE_STATE),
109            instance_prefix,
110        );
111
112        let mut store = storage::PrefixStore::new(contract_state, store_kind.prefix());
113        f(&mut store)
114    })
115}