Drop matrix-sdk-sled

pull/2006/head
Jonas Platte 2023-06-01 11:21:55 +02:00 committed by Jonas Platte
parent 7cd8898f5f
commit 3e2bc3a514
18 changed files with 9 additions and 4120 deletions

95
Cargo.lock generated
View File

@ -538,7 +538,6 @@ dependencies = [
"matrix-sdk",
"matrix-sdk-base",
"matrix-sdk-crypto",
"matrix-sdk-sled",
"matrix-sdk-sqlite",
"matrix-sdk-test",
"pprof",
@ -1167,7 +1166,7 @@ dependencies = [
"hashbrown 0.12.3",
"lock_api",
"once_cell",
"parking_lot_core 0.9.7",
"parking_lot_core",
]
[[package]]
@ -1634,16 +1633,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541"
[[package]]
name = "fs2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "fs_extra"
version = "1.3.0"
@ -1770,15 +1759,6 @@ dependencies = [
"slab",
]
[[package]]
name = "fxhash"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
dependencies = [
"byteorder",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@ -2778,7 +2758,6 @@ dependencies = [
"http",
"matrix-sdk-common",
"matrix-sdk-crypto",
"matrix-sdk-sled",
"matrix-sdk-sqlite",
"napi",
"napi-build",
@ -2882,31 +2861,6 @@ dependencies = [
"vodozemac",
]
[[package]]
name = "matrix-sdk-sled"
version = "0.2.0"
dependencies = [
"assert_matches",
"async-trait",
"fs_extra",
"futures-core",
"futures-util",
"glob",
"matrix-sdk-base",
"matrix-sdk-crypto",
"matrix-sdk-store-encryption",
"matrix-sdk-test",
"once_cell",
"ruma",
"serde",
"serde_json",
"sled",
"tempfile",
"thiserror",
"tokio",
"tracing",
]
[[package]]
name = "matrix-sdk-sqlite"
version = "0.1.0"
@ -3513,17 +3467,6 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
[[package]]
name = "parking_lot"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
"parking_lot_core 0.8.6",
]
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -3531,21 +3474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core 0.9.7",
]
[[package]]
name = "parking_lot_core"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
"libc",
"redox_syscall 0.2.16",
"smallvec",
"winapi",
"parking_lot_core",
]
[[package]]
@ -3835,7 +3764,7 @@ dependencies = [
"log",
"nix",
"once_cell",
"parking_lot 0.12.1",
"parking_lot",
"smallvec",
"symbolic-demangle",
"tempfile",
@ -4791,22 +4720,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "sled"
version = "0.34.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
dependencies = [
"crc32fast",
"crossbeam-epoch",
"crossbeam-utils",
"fs2",
"fxhash",
"libc",
"log",
"parking_lot 0.11.2",
]
[[package]]
name = "sliding-sync-integration-test"
version = "0.1.0"
@ -4879,7 +4792,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
dependencies = [
"new_debug_unreachable",
"once_cell",
"parking_lot 0.12.1",
"parking_lot",
"phf_shared 0.10.0",
"precomputed-hash",
"serde",

View File

@ -12,7 +12,6 @@ criterion = { version = "0.4.0", features = ["async", "async_tokio", "html_repor
matrix-sdk-base = { path = "../crates/matrix-sdk-base" }
matrix-sdk-crypto = { path = "../crates/matrix-sdk-crypto", version = "0.6.0"}
matrix-sdk-sqlite = { path = "../crates/matrix-sdk-sqlite", version = "0.1.0", default-features = false, features = ["crypto-store"] }
matrix-sdk-sled = { path = "../crates/matrix-sdk-sled", version = "0.2.0", features = ["crypto-store"] }
matrix-sdk-test = { path = "../testing/matrix-sdk-test", version = "0.6.0"}
matrix-sdk = { path = "../crates/matrix-sdk" }
ruma = { workspace = true }

View File

@ -2,7 +2,6 @@ use std::{ops::Deref, sync::Arc};
use criterion::*;
use matrix_sdk_crypto::{EncryptionSettings, OlmMachine};
use matrix_sdk_sled::SledCryptoStore;
use matrix_sdk_sqlite::SqliteCryptoStore;
use matrix_sdk_test::response_from_file;
use ruma::{
@ -90,18 +89,6 @@ pub fn keys_query(c: &mut Criterion) {
drop(machine);
}
// Benchmark (deprecated) sled store.
let dir = tempfile::tempdir().unwrap();
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
let machine =
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
group.bench_with_input(BenchmarkId::new("sled store", &name), &response, |b, response| {
b.to_async(&runtime)
.iter(|| async { machine.mark_request_as_sent(&txn_id, response).await.unwrap() })
});
group.finish()
}
@ -164,28 +151,6 @@ pub fn keys_claiming(c: &mut Criterion) {
)
});
group.bench_with_input(BenchmarkId::new("sled store", &name), &response, |b, response| {
b.iter_batched(
|| {
let dir = tempfile::tempdir().unwrap();
let store =
Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
let machine = runtime
.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store))
.unwrap();
runtime
.block_on(machine.mark_request_as_sent(&txn_id, &keys_query_response))
.unwrap();
(machine, &runtime, &txn_id)
},
move |(machine, runtime, txn_id)| {
runtime.block_on(machine.mark_request_as_sent(txn_id, response)).unwrap()
},
BatchSize::SmallInput,
)
});
group.finish()
}
@ -269,37 +234,6 @@ pub fn room_key_sharing(c: &mut Criterion) {
drop(machine);
}
// Benchmark (deprecated) sled store.
let dir = tempfile::tempdir().unwrap();
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
let machine =
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
runtime.block_on(machine.mark_request_as_sent(&txn_id, &keys_query_response)).unwrap();
runtime.block_on(machine.mark_request_as_sent(&txn_id, &response)).unwrap();
group.bench_function(BenchmarkId::new("sled store", &name), |b| {
b.to_async(&runtime).iter(|| async {
let requests = machine
.share_room_key(
room_id,
users.iter().map(Deref::deref),
EncryptionSettings::default(),
)
.await
.unwrap();
assert!(!requests.is_empty());
for request in requests {
machine.mark_request_as_sent(&request.txn_id, &to_device_response).await.unwrap();
}
machine.invalidate_group_session(room_id).await.unwrap();
})
});
group.finish()
}
@ -349,22 +283,6 @@ pub fn devices_missing_sessions_collecting(c: &mut Criterion) {
drop(machine);
}
// Benchmark (deprecated) sled store.
let dir = tempfile::tempdir().unwrap();
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
let machine =
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
runtime.block_on(machine.mark_request_as_sent(&txn_id, &response)).unwrap();
group.bench_function(BenchmarkId::new("sled store", &name), |b| {
b.to_async(&runtime).iter(|| async {
machine.get_missing_sessions(users.iter().map(Deref::deref)).await.unwrap()
})
});
group.finish()
}

View File

@ -1,7 +1,6 @@
use criterion::*;
use matrix_sdk::{config::StoreConfig, Client, RoomInfo, RoomState, Session, StateChanges};
use matrix_sdk_base::{store::MemoryStore, StateStore as _};
use matrix_sdk_sled::SledStateStore;
use matrix_sdk_sqlite::SqliteStateStore;
use ruma::{device_id, user_id, RoomId};
use tokio::runtime::Builder;
@ -74,36 +73,6 @@ pub fn restore_session(c: &mut Criterion) {
for encryption_password in [None, Some("hunter2")] {
let encrypted_suffix = if encryption_password.is_some() { "encrypted" } else { "clear" };
// Sled
let sled_path = tempfile::tempdir().unwrap().path().to_path_buf();
let mut sled_store_builder = SledStateStore::builder().path(sled_path);
if let Some(password) = encryption_password {
sled_store_builder = sled_store_builder.passphrase(password.to_owned());
}
let sled_store = sled_store_builder.build().expect("Can't create sled store");
runtime
.block_on(sled_store.save_changes(&changes))
.expect("initial filling of sled failed");
group.bench_with_input(
BenchmarkId::new(format!("sled store {encrypted_suffix}"), NAME),
&sled_store,
|b, store| {
b.to_async(&runtime).iter(|| async {
let client = Client::builder()
.homeserver_url("https://matrix.example.com")
.store_config(StoreConfig::new().state_store(store.clone()))
.build()
.await
.expect("Can't build client");
client
.restore_session(session.clone())
.await
.expect("couldn't restore session");
})
},
);
// Sqlite
let sqlite_dir = tempfile::tempdir().unwrap();
let sqlite_store = runtime

View File

@ -142,8 +142,8 @@ impl OlmMachine {
/// * `path` - The path where the state of the machine should be persisted.
///
/// * `passphrase` - The passphrase that should be used to encrypt the data
/// at rest in the Sled store. **Warning**, if no passphrase is given, the
/// store and all its data will remain unencrypted.
/// at rest in the crypto store. **Warning**, if no passphrase is given,
/// the store and all its data will remain unencrypted.
#[uniffi::constructor]
pub fn new(
user_id: String,

View File

@ -25,7 +25,6 @@ tracing = ["dep:tracing-subscriber"]
[dependencies]
matrix-sdk-common = { version = "0.6.0", path = "../../crates/matrix-sdk-common", features = ["js"] }
matrix-sdk-sled = { version = "0.2.0", path = "../../crates/matrix-sdk-sled", default-features = false, features = ["crypto-store"] }
matrix-sdk-sqlite = { version = "0.1.0", path = "../../crates/matrix-sdk-sqlite", features = ["crypto-store"] }
ruma = { workspace = true, features = ["rand"] }
napi = { version = "2.9.1", default-features = false, features = ["napi6", "tokio_rt"] }

View File

@ -59,11 +59,8 @@ impl Deref for OlmMachineInner {
#[derive(Default)]
#[napi]
pub enum StoreType {
/// Use `matrix-sdk-sled`.
#[default]
Sled,
/// Use `matrix-sdk-sqlite`.
#[default]
Sqlite,
}
@ -123,21 +120,6 @@ impl OlmMachine {
inner: OlmMachineInner::Opened(ManuallyDrop::new(match store_path {
Some(store_path) => {
let machine = match store_type.unwrap_or_default() {
StoreType::Sled => {
matrix_sdk_crypto::OlmMachine::with_store(
user_id,
device_id,
matrix_sdk_sled::SledCryptoStore::open(
store_path,
store_passphrase.as_deref(),
)
.await
.map(Arc::new)
.map_err(into_err)?,
)
.await
}
StoreType::Sqlite => {
matrix_sdk_crypto::OlmMachine::with_store(
user_id,

View File

@ -25,8 +25,7 @@ const fs = require("fs/promises");
describe("StoreType", () => {
test("has the correct variant values", () => {
expect(StoreType.Sled).toStrictEqual(0);
expect(StoreType.Sqlite).toStrictEqual(1);
expect(StoreType.Sqlite).toStrictEqual(0);
});
});
@ -53,7 +52,6 @@ describe(OlmMachine.name, () => {
describe("can be instantiated with a store", () => {
for (const [store_type, store_name] of [
[StoreType.Sled, "sled"],
[StoreType.Sqlite, "sqlite"],
[null, "default"],
]) {

View File

@ -13,7 +13,6 @@ coverage:
- "crates/matrix-sdk-common/"
- "crates/matrix-sdk-crypto/"
- "crates/matrix-sdk-qrcode/"
- "crates/matrix-sdk-sled/"
- "crates/matrix-sdk-sqlite/"
- "crates/matrix-sdk-store-encryption/"
# Coverage of wasm tests isn't supported at the moment,

View File

@ -1,54 +0,0 @@
[package]
name = "matrix-sdk-sled"
version = "0.2.0"
edition = "2021"
authors = ["Damir Jelić <poljar@termina.org.uk>"]
repository = "https://github.com/matrix-org/matrix-rust-sdk"
description = "Sled Storage backend for matrix-sdk for native environments"
license = "Apache-2.0"
rust-version = { workspace = true }
readme = "README.md"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["state-store"]
state-store = ["dep:matrix-sdk-base"]
crypto-store = [
"dep:matrix-sdk-base",
"dep:matrix-sdk-crypto",
"matrix-sdk-base?/e2e-encryption",
]
docsrs = [
"crypto-store",
]
[dependencies]
async-trait = { workspace = true }
fs_extra = "1.2.0"
futures-core = { workspace = true }
futures-util = { workspace = true }
matrix-sdk-base = { version = "0.6.0", path = "../matrix-sdk-base", optional = true }
matrix-sdk-crypto = { version = "0.6.0", path = "../matrix-sdk-crypto", optional = true }
matrix-sdk-store-encryption = { version = "0.2.0", path = "../matrix-sdk-store-encryption" }
ruma = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sled = "0.34.7"
thiserror = { workspace = true }
tokio = { workspace = true, features = ["fs"] }
tracing = { workspace = true }
[dev-dependencies]
assert_matches = { workspace = true }
glob = "0.3.0"
matrix-sdk-base = { path = "../matrix-sdk-base", features = ["testing"] }
matrix-sdk-crypto = { path = "../matrix-sdk-crypto", features = ["testing"] }
matrix-sdk-test = { path = "../../testing/matrix-sdk-test" }
once_cell = { workspace = true }
tempfile = "3.3.0"
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }

View File

@ -1,25 +0,0 @@
# matrix-sdk-sled
This crate implements a storage backend using [sled][sled] for native and mobile environments using the matrix-sdk-base primitives. When using **matrix-sdk** this is included by default.
_Note_: the future of [sled][sled] is unclear. While it is currently the default for mobile and native environments for matrix-rust-sdk, [we are actively looking at replacing it with a different storage backend](https://github.com/matrix-org/matrix-rust-sdk/issues/294).
## Crate Feature Flags
The following crate feature flags are available:
* `state-store`: (on by default) Enables the state store
* `crypto-store`: Enables the store for end-to-end encrypted data.
## Minimum Supported Rust Version (MSRV)
These crates are built with the Rust language version 2021 and require a minimum compiler version of `1.62`.
## License
[Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)
[sled]: https://sled.rs/

View File

@ -1,17 +0,0 @@
use std::{env, process};
fn main() {
let target_arch = env::var_os("CARGO_CFG_TARGET_ARCH");
if target_arch.map_or(false, |arch| arch == "wasm32") {
let err = "this crate does not support the target arch 'wasm32'";
eprintln!(
"\n\
{pad}\n\
error: {err} \n\
{pad}\n\
",
pad = "".repeat(err.len()),
);
process::exit(1);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,290 +0,0 @@
use std::{borrow::Cow, ops::Deref};
use matrix_sdk_store_encryption::StoreCipher;
use ruma::{
events::{
receipt::ReceiptType, secret::request::SecretName, GlobalAccountDataEventType,
RoomAccountDataEventType, StateEventType,
},
DeviceId, EventId, MxcUri, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, TransactionId,
UserId,
};
/// Hold any data to be used as an encoding key
/// without checking for the existence of `ENCODE_SEPARATOR` within
pub struct EncodeUnchecked<'a>(&'a [u8]);
#[cfg(feature = "state-store")]
impl<'a> EncodeUnchecked<'a> {
/// Wrap any `[u8]`
pub fn from(bytes: &'a [u8]) -> Self {
EncodeUnchecked(bytes)
}
}
impl<'a> EncodeKey for EncodeUnchecked<'a> {
fn encode_as_bytes(&self) -> Cow<'a, [u8]> {
(self.0).into()
}
}
pub const ENCODE_SEPARATOR: u8 = 0xff;
pub trait EncodeKey {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
unimplemented!()
}
fn encode(&self) -> Vec<u8> {
[self.encode_as_bytes().deref(), &[ENCODE_SEPARATOR]].concat()
}
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
let key = store_cipher.hash_key(table_name, &self.encode_as_bytes());
[key.as_slice(), &[ENCODE_SEPARATOR]].concat()
}
}
impl<T: EncodeKey + ?Sized> EncodeKey for &T {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
T::encode_as_bytes(self)
}
fn encode(&self) -> Vec<u8> {
T::encode(self)
}
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
T::encode_secure(self, table_name, store_cipher)
}
}
impl EncodeKey for str {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_bytes().into()
}
}
impl EncodeKey for String {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for DeviceId {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for EventId {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for OwnedEventId {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for RoomId {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for OwnedRoomId {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for TransactionId {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for MxcUri {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
let s: &str = self.as_ref();
s.as_bytes().into()
}
}
impl EncodeKey for SecretName {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
let s: &str = self.as_ref();
s.as_bytes().into()
}
}
impl EncodeKey for ReceiptType {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
let s: &str = self.as_ref();
s.as_bytes().into()
}
}
impl EncodeKey for RoomAccountDataEventType {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.to_string().as_bytes().to_vec().into()
}
}
impl EncodeKey for UserId {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for OwnedUserId {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.as_str().as_bytes().into()
}
}
impl EncodeKey for StateEventType {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.to_string().as_bytes().to_vec().into()
}
}
impl EncodeKey for GlobalAccountDataEventType {
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
self.to_string().as_bytes().to_vec().into()
}
}
impl<A, B> EncodeKey for (A, B)
where
A: EncodeKey,
B: EncodeKey,
{
fn encode(&self) -> Vec<u8> {
[
self.0.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.1.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
]
.concat()
}
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
[
store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
]
.concat()
}
}
impl<A, B, C> EncodeKey for (A, B, C)
where
A: EncodeKey,
B: EncodeKey,
C: EncodeKey,
{
fn encode(&self) -> Vec<u8> {
[
self.0.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.1.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.2.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
]
.concat()
}
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
[
store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.2.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
]
.concat()
}
}
impl<A, B, C, D> EncodeKey for (A, B, C, D)
where
A: EncodeKey,
B: EncodeKey,
C: EncodeKey,
D: EncodeKey,
{
fn encode(&self) -> Vec<u8> {
[
self.0.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.1.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.2.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.3.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
]
.concat()
}
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
[
store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.2.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.3.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
]
.concat()
}
}
impl<A, B, C, D, E> EncodeKey for (A, B, C, D, E)
where
A: EncodeKey,
B: EncodeKey,
C: EncodeKey,
D: EncodeKey,
E: EncodeKey,
{
fn encode(&self) -> Vec<u8> {
[
self.0.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.1.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.2.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.3.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
self.4.encode_as_bytes().deref(),
&[ENCODE_SEPARATOR],
]
.concat()
}
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
[
store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.2.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.3.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
store_cipher.hash_key(table_name, &self.4.encode_as_bytes()).as_slice(),
&[ENCODE_SEPARATOR],
]
.concat()
}
}

View File

@ -1,92 +0,0 @@
#[cfg(any(feature = "state-store", feature = "crypto-store"))]
use matrix_sdk_base::store::StoreConfig;
#[cfg(feature = "state-store")]
use matrix_sdk_base::store::StoreError;
#[cfg(feature = "crypto-store")]
use matrix_sdk_crypto::store::CryptoStoreError;
use sled::Error as SledError;
use thiserror::Error;
#[cfg(feature = "crypto-store")]
mod crypto_store;
mod encode_key;
#[cfg(feature = "state-store")]
mod state_store;
#[cfg(feature = "crypto-store")]
pub use crypto_store::SledCryptoStore;
#[cfg(feature = "state-store")]
pub use state_store::{MigrationConflictStrategy, SledStateStore, SledStateStoreBuilder};
/// All the errors that can occur when opening a sled store.
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum OpenStoreError {
/// An error occurred with the state store implementation.
#[cfg(feature = "state-store")]
#[error(transparent)]
State(#[from] StoreError),
/// An error occurred with the crypto store implementation.
#[cfg(feature = "crypto-store")]
#[error(transparent)]
Crypto(#[from] CryptoStoreError),
/// An error occurred with sled.
#[error(transparent)]
Sled(#[from] SledError),
}
/// Create a [`StoreConfig`] with an opened [`SledStateStore`] that uses the
/// given path and passphrase.
///
/// If the `e2e-encryption` Cargo feature is enabled, a [`SledCryptoStore`] with
/// the same parameters is also opened.
///
/// [`StoreConfig`]: #StoreConfig
#[cfg(any(feature = "state-store", feature = "crypto-store"))]
pub async fn make_store_config(
path: impl AsRef<std::path::Path>,
passphrase: Option<&str>,
) -> Result<StoreConfig, OpenStoreError> {
#[cfg(all(feature = "crypto-store", feature = "state-store"))]
{
let (state_store, crypto_store) = open_stores_with_path(path, passphrase).await?;
Ok(StoreConfig::new().state_store(state_store).crypto_store(crypto_store))
}
#[cfg(all(feature = "crypto-store", not(feature = "state-store")))]
{
let crypto_store = SledCryptoStore::open(path, passphrase).await?;
Ok(StoreConfig::new().crypto_store(crypto_store))
}
#[cfg(not(feature = "crypto-store"))]
{
let mut store_builder = SledStateStore::builder().path(path.as_ref().to_path_buf());
if let Some(passphrase) = passphrase {
store_builder = store_builder.passphrase(passphrase.to_owned());
}
let state_store = store_builder.build().map_err(StoreError::backend)?;
Ok(StoreConfig::new().state_store(state_store))
}
}
/// Create a [`StateStore`] and a [`CryptoStore`] that use the same database and
/// passphrase.
#[cfg(all(feature = "state-store", feature = "crypto-store"))]
async fn open_stores_with_path(
path: impl AsRef<std::path::Path>,
passphrase: Option<&str>,
) -> Result<(SledStateStore, SledCryptoStore), OpenStoreError> {
let mut store_builder = SledStateStore::builder().path(path.as_ref().to_path_buf());
if let Some(passphrase) = passphrase {
store_builder = store_builder.passphrase(passphrase.to_owned());
}
let state_store = store_builder.build().map_err(StoreError::backend)?;
let crypto_store = state_store.open_crypto_store().await?;
Ok((state_store, crypto_store))
}

View File

@ -1,825 +0,0 @@
// Copyright 2021 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use matrix_sdk_base::{
store::{Result as StoreResult, StoreError},
RoomInfo, StateStoreDataKey,
};
use ruma::{
events::{
room::member::{StrippedRoomMemberEvent, SyncRoomMemberEvent},
StateEventType,
},
serde::Raw,
};
use serde_json::value::{RawValue as RawJsonValue, Value as JsonValue};
use sled::{transaction::TransactionError, Batch, Transactional, Tree};
use tracing::debug;
use super::{keys, Result, RoomMember, SledStateStore, SledStoreError};
use crate::encode_key::EncodeKey;
const DATABASE_VERSION: u8 = 7;
const VERSION_KEY: &str = "state-store-version";
/// Sometimes Migrations can't proceed without having to drop existing
/// data. This allows you to configure, how these cases should be handled.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum MigrationConflictStrategy {
/// Just drop the data, we don't care that we have to sync again
Drop,
/// Raise a `SledStoreError::MigrationConflict` error with the path to the
/// DB in question. The caller then has to take care about what they want
/// to do and try again after.
Raise,
/// _Default_: The _entire_ database is backed up under
/// `$path.$timestamp.backup` (this includes the crypto store if they
/// are linked), before the state tables are dropped.
BackupAndDrop,
}
impl SledStateStore {
pub(super) fn upgrade(&mut self) -> Result<()> {
let old_version = self.db_version()?;
if old_version == 0 {
// we are fresh, let's write the current version
return self.set_db_version(DATABASE_VERSION);
}
if old_version == DATABASE_VERSION {
// current, we don't have to do anything
return Ok(());
};
debug!(old_version, new_version = DATABASE_VERSION, "Upgrading the Sled state store");
if old_version == 1 && self.store_cipher.is_some() {
// we stored some fields un-encrypted. Drop them to force re-creation
return Err(SledStoreError::MigrationConflict {
path: self.path.take().expect("Path must exist for a migration to fail"),
old_version: old_version.into(),
new_version: DATABASE_VERSION.into(),
});
}
if old_version < 3 {
self.migrate_to_v3()?;
}
if old_version < 4 {
self.migrate_to_v4()?;
}
if old_version < 5 {
self.migrate_to_v5()?;
return Ok(());
}
// Version 6 was dropped and migration is similar to v7.
if old_version < 7 {
self.migrate_to_v7()?;
return Ok(());
}
// FUTURE UPGRADE CODE GOES HERE
// can't upgrade from that version to the new one
Err(SledStoreError::MigrationConflict {
path: self.path.take().expect("Path must exist for a migration to fail"),
old_version: old_version.into(),
new_version: DATABASE_VERSION.into(),
})
}
/// Get the version of the database.
///
/// Returns `0` for a new database.
fn db_version(&self) -> Result<u8> {
Ok(self
.inner
.get(VERSION_KEY)?
.map(|v| {
let (version_bytes, _) = v.split_at(std::mem::size_of::<u8>());
u8::from_be_bytes(version_bytes.try_into().unwrap_or_default())
})
.unwrap_or_default())
}
fn set_db_version(&self, version: u8) -> Result<()> {
self.inner.insert(VERSION_KEY, version.to_be_bytes().as_ref())?;
self.inner.flush()?;
Ok(())
}
pub fn drop_v1_tables(self) -> StoreResult<()> {
for name in V1_DB_STORES {
self.inner.drop_tree(name).map_err(StoreError::backend)?;
}
self.inner.remove(VERSION_KEY).map_err(StoreError::backend)?;
Ok(())
}
fn v3_fix_tree(&self, tree: &Tree, batch: &mut Batch) -> Result<()> {
fn maybe_fix_json(raw_json: &RawJsonValue) -> Result<Option<JsonValue>> {
let json = raw_json.get();
if json.contains(r#""content":null"#) {
let mut value: JsonValue = serde_json::from_str(json)?;
if let Some(content) = value.get_mut("content") {
if matches!(content, JsonValue::Null) {
*content = JsonValue::Object(Default::default());
return Ok(Some(value));
}
}
}
Ok(None)
}
for entry in tree.iter() {
let (key, value) = entry?;
let raw_json: Box<RawJsonValue> = self.deserialize_value(&value)?;
if let Some(fixed_json) = maybe_fix_json(&raw_json)? {
batch.insert(key, self.serialize_value(&fixed_json)?);
}
}
Ok(())
}
fn migrate_to_v3(&self) -> Result<()> {
let mut room_info_batch = sled::Batch::default();
self.v3_fix_tree(&self.room_info, &mut room_info_batch)?;
let mut room_state_batch = sled::Batch::default();
self.v3_fix_tree(&self.room_state, &mut room_state_batch)?;
let ret: Result<(), TransactionError<SledStoreError>> = (&self.room_info, &self.room_state)
.transaction(|(room_info, room_state)| {
room_info.apply_batch(&room_info_batch)?;
room_state.apply_batch(&room_state_batch)?;
Ok(())
});
ret?;
self.set_db_version(3u8)
}
/// Replace the SYNC_TOKEN and SESSION trees by KV.
fn migrate_to_v4(&self) -> Result<()> {
{
let session = &self.inner.open_tree(old_keys::SESSION)?;
let mut batch = sled::Batch::default();
// Sync token
let sync_token = session.get(StateStoreDataKey::SYNC_TOKEN.encode())?;
if let Some(sync_token) = sync_token {
batch.insert(StateStoreDataKey::SYNC_TOKEN.encode(), sync_token);
}
// Filters
let key = self.encode_key(keys::SESSION, StateStoreDataKey::FILTER);
for res in session.scan_prefix(key) {
let (key, value) = res?;
batch.insert(key, value);
}
self.kv.apply_batch(batch)?;
}
// This was unused so we can just drop it.
self.inner.drop_tree(old_keys::SYNC_TOKEN)?;
self.inner.drop_tree(old_keys::SESSION)?;
self.set_db_version(4)
}
/// Move the member events with the other state events.
fn migrate_to_v5(&self) -> Result<()> {
{
let members = &self.inner.open_tree(old_keys::MEMBER)?;
let mut state_batch = sled::Batch::default();
for room_info in
self.room_info.iter().map(|r| self.deserialize_value::<RoomInfo>(&r?.1))
{
let room_info = room_info?;
let room_id = room_info.room_id();
let prefix = self.encode_key(old_keys::MEMBER, room_id);
for entry in members.scan_prefix(prefix) {
let (_, value) = entry?;
let raw_member_event =
self.deserialize_value::<Raw<SyncRoomMemberEvent>>(&value)?;
let state_key =
raw_member_event.get_field::<String>("state_key")?.unwrap_or_default();
let key = self.encode_key(
keys::ROOM_STATE,
(room_id, StateEventType::RoomMember, state_key),
);
state_batch.insert(key, value);
}
}
let stripped_members = &self.inner.open_tree(old_keys::STRIPPED_ROOM_MEMBER)?;
let mut stripped_state_batch = sled::Batch::default();
for room_info in
self.stripped_room_infos.iter().map(|r| self.deserialize_value::<RoomInfo>(&r?.1))
{
let room_info = room_info?;
let room_id = room_info.room_id();
let prefix = self.encode_key(old_keys::STRIPPED_ROOM_MEMBER, room_id);
for entry in stripped_members.scan_prefix(prefix) {
let (_, value) = entry?;
let raw_member_event =
self.deserialize_value::<Raw<StrippedRoomMemberEvent>>(&value)?;
let state_key =
raw_member_event.get_field::<String>("state_key")?.unwrap_or_default();
let key = self.encode_key(
keys::STRIPPED_ROOM_STATE,
(room_id, StateEventType::RoomMember, state_key),
);
stripped_state_batch.insert(key, value);
}
}
let ret: Result<(), TransactionError<SledStoreError>> =
(&self.room_state, &self.stripped_room_state).transaction(
|(room_state, stripped_room_state)| {
room_state.apply_batch(&state_batch)?;
stripped_room_state.apply_batch(&stripped_state_batch)?;
Ok(())
},
);
ret?;
}
self.inner.drop_tree(old_keys::MEMBER)?;
self.inner.drop_tree(old_keys::STRIPPED_ROOM_MEMBER)?;
self.set_db_version(5)
}
/// Remove the old user IDs stores and populate the new ones.
fn migrate_to_v7(&self) -> Result<()> {
{
// Reset v6 stores.
self.user_ids.clear()?;
self.stripped_user_ids.clear()?;
// We only have joined and invited user IDs in the old stores, so instead we
// use the room member events to populate the new stores.
let state = &self.inner.open_tree(keys::ROOM_STATE)?;
let mut user_ids_batch = sled::Batch::default();
for room_info in
self.room_info.iter().map(|r| self.deserialize_value::<RoomInfo>(&r?.1))
{
let room_info = room_info?;
let room_id = room_info.room_id();
let prefix =
self.encode_key(keys::ROOM_STATE, (room_id, StateEventType::RoomMember));
for entry in state.scan_prefix(prefix) {
let (_, value) = entry?;
let member_event = self
.deserialize_value::<Raw<SyncRoomMemberEvent>>(&value)?
.deserialize()?;
let key = self.encode_key(keys::USER_ID, (room_id, member_event.state_key()));
let value = self.serialize_value(&RoomMember::from(&member_event))?;
user_ids_batch.insert(key, value);
}
}
let stripped_state = &self.inner.open_tree(keys::STRIPPED_ROOM_STATE)?;
let mut stripped_user_ids_batch = sled::Batch::default();
for room_info in
self.stripped_room_infos.iter().map(|r| self.deserialize_value::<RoomInfo>(&r?.1))
{
let room_info = room_info?;
let room_id = room_info.room_id();
let prefix = self
.encode_key(keys::STRIPPED_ROOM_STATE, (room_id, StateEventType::RoomMember));
for entry in stripped_state.scan_prefix(prefix) {
let (_, value) = entry?;
let stripped_member_event = self
.deserialize_value::<Raw<StrippedRoomMemberEvent>>(&value)?
.deserialize()?;
let key = self.encode_key(
keys::STRIPPED_USER_ID,
(room_id, &stripped_member_event.state_key),
);
let value = self.serialize_value(&RoomMember::from(&stripped_member_event))?;
stripped_user_ids_batch.insert(key, value);
}
}
let ret: Result<(), TransactionError<SledStoreError>> =
(&self.user_ids, &self.stripped_user_ids).transaction(
|(user_ids, stripped_user_ids)| {
user_ids.apply_batch(&user_ids_batch)?;
stripped_user_ids.apply_batch(&stripped_user_ids_batch)?;
Ok(())
},
);
ret?;
}
self.inner.drop_tree(old_keys::JOINED_USER_ID)?;
self.inner.drop_tree(old_keys::INVITED_USER_ID)?;
self.inner.drop_tree(old_keys::STRIPPED_JOINED_USER_ID)?;
self.inner.drop_tree(old_keys::STRIPPED_INVITED_USER_ID)?;
self.set_db_version(7)
}
}
mod old_keys {
/// Old stores.
pub const SYNC_TOKEN: &str = "sync_token";
pub const SESSION: &str = "session";
pub const MEMBER: &str = "member";
pub const STRIPPED_ROOM_MEMBER: &str = "stripped-room-member";
pub const INVITED_USER_ID: &str = "invited-user-id";
pub const JOINED_USER_ID: &str = "joined-user-id";
pub const STRIPPED_INVITED_USER_ID: &str = "stripped-invited-user-id";
pub const STRIPPED_JOINED_USER_ID: &str = "stripped-joined-user-id";
}
pub const V1_DB_STORES: &[&str] = &[
keys::ACCOUNT_DATA,
old_keys::SYNC_TOKEN,
keys::DISPLAY_NAME,
old_keys::INVITED_USER_ID,
old_keys::JOINED_USER_ID,
keys::MEDIA,
old_keys::MEMBER,
keys::PRESENCE,
keys::PROFILE,
keys::ROOM_ACCOUNT_DATA,
keys::ROOM_EVENT_RECEIPT,
keys::ROOM_INFO,
keys::ROOM_STATE,
keys::ROOM_USER_RECEIPT,
keys::ROOM,
old_keys::SESSION,
old_keys::STRIPPED_INVITED_USER_ID,
old_keys::STRIPPED_JOINED_USER_ID,
keys::STRIPPED_ROOM_INFO,
old_keys::STRIPPED_ROOM_MEMBER,
keys::STRIPPED_ROOM_STATE,
keys::CUSTOM,
];
#[cfg(test)]
mod test {
use assert_matches::assert_matches;
use matrix_sdk_base::{
deserialized_responses::RawMemberEvent, RoomInfo, RoomMemberships, RoomState,
StateStoreDataKey,
};
use matrix_sdk_test::{async_test, test_json};
use ruma::{
events::{
room::{
member::{StrippedRoomMemberEvent, SyncRoomMemberEvent},
topic::RoomTopicEventContent,
},
AnySyncStateEvent, StateEventType,
},
room_id,
serde::Raw,
user_id,
};
use serde_json::json;
use tempfile::TempDir;
use super::{old_keys, MigrationConflictStrategy};