Skip to content

Commit e594d36

Browse files
authored
Merge pull request #100 from datachainlab/fix-enclave-store
Implement `CacheKVS` and utilize it as enclave store Signed-off-by: Jun Kimura <jun.kimura@datachain.jp>
2 parents 6584db7 + 4e145e6 commit e594d36

File tree

9 files changed

+164
-72
lines changed

9 files changed

+164
-72
lines changed

enclave-modules/environment/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ edition = "2021"
66
[dependencies]
77
light-client = { path = "../../modules/light-client", default-features = false }
88
store = { path = "../../modules/store", default-features = false }
9-
enclave-store = { path = "../store", optional = true }
9+
host-api = { path = "../host-api", optional = true }
1010

1111
[features]
1212
default = ["environment_impl"]
13-
environment_impl = ["enclave-store"]
13+
environment_impl = ["host-api"]

enclave-modules/environment/src/environment_impl.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{prelude::*, Env};
22
use alloc::sync::Arc;
3-
use enclave_store::EnclaveStore;
3+
use host_api::store::new_enclave_store;
44
use light_client::{LightClient, LightClientResolver, MapLightClientRegistry};
55
use store::{KVStore, TxId};
66

@@ -24,7 +24,7 @@ impl LightClientResolver for Environment {
2424

2525
impl Env for Environment {
2626
fn new_store(&self, tx_id: TxId) -> Box<dyn KVStore> {
27-
Box::new(EnclaveStore::new(tx_id))
27+
new_enclave_store(tx_id)
2828
}
2929

3030
fn get_lc_registry(&self) -> Arc<dyn LightClientResolver> {

enclave-modules/host-api/src/store.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,41 @@
11
use crate::prelude::*;
22
use crate::{api::execute_command, Error};
33
use ocall_commands::{Command, CommandResult, StoreCommand, StoreResult};
4-
use store::TxId;
4+
use store::cache::CacheKVS;
5+
use store::{KVStore, TxId};
56

6-
pub fn get(tx_id: TxId, key: Vec<u8>) -> Result<Option<Vec<u8>>, Error> {
7+
/// The store guarantees that reads a value from the host store only once per key
8+
pub fn new_enclave_store(tx_id: TxId) -> Box<dyn KVStore> {
9+
Box::new(CacheKVS::new(TxStore::new(tx_id)))
10+
}
11+
12+
/// TxStore is a KVStore implementation that uses the ocall_commands to interact with the
13+
/// host's store.
14+
struct TxStore {
15+
tx_id: TxId,
16+
}
17+
18+
impl TxStore {
19+
pub fn new(tx_id: TxId) -> Self {
20+
Self { tx_id }
21+
}
22+
}
23+
24+
impl KVStore for TxStore {
25+
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
26+
get(self.tx_id, key.to_vec()).unwrap()
27+
}
28+
29+
fn set(&mut self, key: Vec<u8>, value: Vec<u8>) {
30+
set(self.tx_id, key.clone(), value.clone()).unwrap();
31+
}
32+
33+
fn remove(&mut self, key: &[u8]) {
34+
remove(self.tx_id, key.to_vec()).unwrap();
35+
}
36+
}
37+
38+
fn get(tx_id: TxId, key: Vec<u8>) -> Result<Option<Vec<u8>>, Error> {
739
let cmd = Command::Store(StoreCommand::Get(tx_id, key));
840
if let CommandResult::Store(StoreResult::Get(v)) = execute_command(cmd)? {
941
Ok(v)
@@ -12,7 +44,7 @@ pub fn get(tx_id: TxId, key: Vec<u8>) -> Result<Option<Vec<u8>>, Error> {
1244
}
1345
}
1446

15-
pub fn set(tx_id: TxId, key: Vec<u8>, value: Vec<u8>) -> Result<(), Error> {
47+
fn set(tx_id: TxId, key: Vec<u8>, value: Vec<u8>) -> Result<(), Error> {
1648
let cmd = Command::Store(StoreCommand::Set(tx_id, key, value));
1749
if let CommandResult::Store(StoreResult::Set) = execute_command(cmd)? {
1850
Ok(())
@@ -21,7 +53,7 @@ pub fn set(tx_id: TxId, key: Vec<u8>, value: Vec<u8>) -> Result<(), Error> {
2153
}
2254
}
2355

24-
pub fn remove(tx_id: TxId, key: Vec<u8>) -> Result<(), Error> {
56+
fn remove(tx_id: TxId, key: Vec<u8>) -> Result<(), Error> {
2557
let cmd = Command::Store(StoreCommand::Remove(tx_id, key));
2658
if let CommandResult::Store(StoreResult::Remove) = execute_command(cmd)? {
2759
Ok(())

enclave-modules/store/Cargo.toml

Lines changed: 0 additions & 8 deletions
This file was deleted.

enclave-modules/store/src/lib.rs

Lines changed: 0 additions & 23 deletions
This file was deleted.

enclave-modules/store/src/store.rs

Lines changed: 0 additions & 24 deletions
This file was deleted.

enclave/Cargo.lock

Lines changed: 1 addition & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

modules/store/src/cache.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use crate::prelude::*;
2+
use crate::KVStore;
3+
use alloc::collections::BTreeMap;
4+
use core::cell::RefCell;
5+
6+
/// A key-value store that caches reads in memory
7+
pub struct CacheKVS<S: KVStore> {
8+
parent: S,
9+
cache: RefCell<BTreeMap<Vec<u8>, Option<Vec<u8>>>>,
10+
}
11+
12+
impl<S: KVStore> CacheKVS<S> {
13+
pub fn new(parent: S) -> Self {
14+
Self {
15+
parent,
16+
cache: RefCell::new(BTreeMap::new()),
17+
}
18+
}
19+
}
20+
21+
impl<S: KVStore> KVStore for CacheKVS<S> {
22+
fn set(&mut self, key: Vec<u8>, value: Vec<u8>) {
23+
self.parent.set(key.clone(), value.clone());
24+
self.cache.borrow_mut().insert(key, Some(value));
25+
}
26+
27+
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
28+
let cache = self.cache.borrow();
29+
let res = cache.get(key);
30+
match res {
31+
Some(Some(v)) => Some(v.clone()),
32+
Some(None) => None,
33+
None => {
34+
drop(cache);
35+
let v = self.parent.get(key);
36+
self.cache.borrow_mut().insert(key.to_vec(), v.clone());
37+
v
38+
}
39+
}
40+
}
41+
42+
fn remove(&mut self, key: &[u8]) {
43+
self.parent.remove(key);
44+
self.cache.borrow_mut().insert(key.to_vec(), None);
45+
}
46+
}
47+
48+
#[cfg(test)]
49+
mod tests {
50+
use super::*;
51+
use crate::KVStore;
52+
use alloc::rc::Rc;
53+
54+
pub struct MockStore {
55+
db: BTreeMap<Vec<u8>, Vec<u8>>,
56+
}
57+
58+
impl MockStore {
59+
pub fn new() -> Self {
60+
Self {
61+
db: BTreeMap::new(),
62+
}
63+
}
64+
}
65+
66+
impl KVStore for MockStore {
67+
fn set(&mut self, key: Vec<u8>, value: Vec<u8>) {
68+
self.db.insert(key, value);
69+
}
70+
71+
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
72+
self.db.get(key).cloned()
73+
}
74+
75+
fn remove(&mut self, key: &[u8]) {
76+
self.db.remove(key);
77+
}
78+
}
79+
80+
#[allow(non_snake_case)]
81+
fn B(s: &str) -> Vec<u8> {
82+
s.as_bytes().to_vec()
83+
}
84+
85+
#[test]
86+
fn test_cache_kvs() {
87+
let mut mock = Rc::new(RefCell::new(MockStore::new()));
88+
mock.set(B("k1"), B("v1"));
89+
90+
let mut cache = CacheKVS::new(mock.clone());
91+
assert_eq!(cache.get(&B("k1")), Some(B("v1")));
92+
93+
cache.set(B("k1"), B("v2"));
94+
assert_eq!(cache.get(&B("k1")), Some(B("v2")));
95+
assert_eq!(mock.get(&B("k1")), Some(B("v2")));
96+
97+
mock.set(B("k1"), B("v3"));
98+
assert_eq!(mock.get(&B("k1")), Some(B("v3")));
99+
assert_eq!(cache.get(&B("k1")), Some(B("v2")));
100+
101+
mock.remove(&B("k1"));
102+
assert_eq!(mock.get(&B("k1")), None);
103+
assert_eq!(cache.get(&B("k1")), Some(B("v2")));
104+
105+
mock.set(B("k2"), B("v4"));
106+
assert_eq!(cache.get(&B("k2")), Some(B("v4")));
107+
108+
assert_eq!(cache.get(&B("k3")), None);
109+
mock.set(B("k3"), B("v5"));
110+
assert_eq!(mock.get(&B("k3")), Some(B("v5")));
111+
assert_eq!(cache.get(&B("k3")), None);
112+
cache.set(B("k3"), B("v6"));
113+
assert_eq!(cache.get(&B("k3")), Some(B("v6")));
114+
115+
cache.remove(&B("k4"));
116+
mock.set(B("k4"), B("v7"));
117+
assert_eq!(cache.get(&B("k4")), None);
118+
assert_eq!(mock.get(&B("k4")), Some(B("v7")));
119+
cache.set(B("k4"), B("v8"));
120+
assert_eq!(cache.get(&B("k4")), Some(B("v8")));
121+
}
122+
}

modules/store/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod prelude {
2222
pub use crate::errors::{Error, Result};
2323
pub use crate::store::{KVStore, TxId};
2424

25+
pub mod cache;
2526
mod errors;
2627
#[cfg(feature = "std")]
2728
pub mod host;

0 commit comments

Comments
 (0)