From d434dfb3a56afde239023685ca0a8d191355314b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Fri, 14 Jan 2022 11:40:49 +0100
Subject: [PATCH 01/16] feat: config option for rocksdb max open files

---
 src/database.rs                     |  6 ++++++
 src/database/abstraction/rocksdb.rs | 29 ++++++++++++++++++++++-------
 2 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/src/database.rs b/src/database.rs
index d688ff9f..fd7a1451 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -49,6 +49,8 @@ pub struct Config {
     database_path: String,
     #[serde(default = "default_db_cache_capacity_mb")]
     db_cache_capacity_mb: f64,
+    #[serde(default = "default_rocksdb_max_open_files")]
+    rocksdb_max_open_files: i32,
     #[serde(default = "default_pdu_cache_capacity")]
     pdu_cache_capacity: u32,
     #[serde(default = "default_cleanup_second_interval")]
@@ -127,6 +129,10 @@ fn default_db_cache_capacity_mb() -> f64 {
     10.0
 }
 
+fn default_rocksdb_max_open_files() -> i32 {
+    512
+}
+
 fn default_pdu_cache_capacity() -> u32 {
     1_000_000
 }
diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs
index 79a3d82a..adda6787 100644
--- a/src/database/abstraction/rocksdb.rs
+++ b/src/database/abstraction/rocksdb.rs
@@ -5,6 +5,7 @@ use std::{future::Future, pin::Pin, sync::Arc, sync::RwLock};
 pub struct Engine {
     rocks: rocksdb::DBWithThreadMode<rocksdb::MultiThreaded>,
     cache_capacity_bytes: usize,
+    max_open_files: i32,
     cache: rocksdb::Cache,
     old_cfs: Vec<String>,
 }
@@ -16,7 +17,11 @@ pub struct RocksDbEngineTree<'a> {
     write_lock: RwLock<()>,
 }
 
-fn db_options(cache_capacity_bytes: usize, rocksdb_cache: &rocksdb::Cache) -> rocksdb::Options {
+fn db_options(
+    cache_capacity_bytes: usize,
+    max_open_files: i32,
+    rocksdb_cache: &rocksdb::Cache,
+) -> rocksdb::Options {
     let mut block_based_options = rocksdb::BlockBasedOptions::default();
     block_based_options.set_block_cache(rocksdb_cache);
 
@@ -36,7 +41,7 @@ fn db_options(cache_capacity_bytes: usize, rocksdb_cache: &rocksdb::Cache) -> ro
     //db_opts.set_use_direct_io_for_flush_and_compaction(true);
     db_opts.create_if_missing(true);
     db_opts.increase_parallelism(num_cpus::get() as i32);
-    db_opts.set_max_open_files(512);
+    db_opts.set_max_open_files(max_open_files);
     db_opts.set_compression_type(rocksdb::DBCompressionType::Zstd);
     db_opts.set_compaction_style(rocksdb::DBCompactionStyle::Level);
     db_opts.optimize_level_style_compaction(cache_capacity_bytes);
@@ -52,7 +57,11 @@ impl DatabaseEngine for Arc<Engine> {
         let cache_capacity_bytes = (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize;
         let rocksdb_cache = rocksdb::Cache::new_lru_cache(cache_capacity_bytes).unwrap();
 
-        let db_opts = db_options(cache_capacity_bytes, &rocksdb_cache);
+        let db_opts = db_options(
+            cache_capacity_bytes,
+            config.rocksdb_max_open_files,
+            &rocksdb_cache,
+        );
 
         let cfs = rocksdb::DBWithThreadMode::<rocksdb::MultiThreaded>::list_cf(
             &db_opts,
@@ -66,7 +75,11 @@ impl DatabaseEngine for Arc<Engine> {
             cfs.iter().map(|name| {
                 rocksdb::ColumnFamilyDescriptor::new(
                     name,
-                    db_options(cache_capacity_bytes, &rocksdb_cache),
+                    db_options(
+                        cache_capacity_bytes,
+                        config.rocksdb_max_open_files,
+                        &rocksdb_cache,
+                    ),
                 )
             }),
         )?;
@@ -74,6 +87,7 @@ impl DatabaseEngine for Arc<Engine> {
         Ok(Arc::new(Engine {
             rocks: db,
             cache_capacity_bytes,
+            max_open_files: config.rocksdb_max_open_files,
             cache: rocksdb_cache,
             old_cfs: cfs,
         }))
@@ -82,9 +96,10 @@ impl DatabaseEngine for Arc<Engine> {
     fn open_tree(&self, name: &'static str) -> Result<Arc<dyn Tree>> {
         if !self.old_cfs.contains(&name.to_owned()) {
             // Create if it didn't exist
-            let _ = self
-                .rocks
-                .create_cf(name, &db_options(self.cache_capacity_bytes, &self.cache));
+            let _ = self.rocks.create_cf(
+                name,
+                &db_options(self.cache_capacity_bytes, self.max_open_files, &self.cache),
+            );
         }
 
         Ok(Arc::new(RocksDbEngineTree {

From 10f1da12bfa17c05ae219913c411fd3c27dc3a29 Mon Sep 17 00:00:00 2001
From: Jonas Zohren <gitlab-jfowl-0ux98@sh14.de>
Date: Sun, 16 Jan 2022 20:57:23 +0000
Subject: [PATCH 02/16] CI: Fix cargo-test

---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f47327b8..73a1a928 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -218,7 +218,7 @@ test:cargo:
   before_script:
     #    - mkdir -p $CARGO_HOME
     - apt-get update -yqq
-    - apt-get install -yqq --no-install-recommends build-essential libssl-dev pkg-config
+    - apt-get install -yqq --no-install-recommends build-essential libssl-dev pkg-config libclang-dev
     - rustup component add clippy rustfmt
     - curl "https://faulty-storage.de/gitlab-report" --output ./gitlab-report && chmod +x ./gitlab-report
     # If provided, bring in caching through sccache, which uses an external S3 endpoint to store compilation results:

From ee8e72f7a809cfbe58697ad69aff437d35e1404f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Mon, 17 Jan 2022 14:35:38 +0100
Subject: [PATCH 03/16] feat: implement server ACLs

---
 Cargo.lock                          |  48 +++++++----
 Cargo.toml                          |   2 +-
 src/client_server/membership.rs     |   4 +-
 src/client_server/message.rs        |   4 +-
 src/client_server/state.rs          |   4 +-
 src/client_server/to_device.rs      |   4 +-
 src/database/abstraction/rocksdb.rs |   2 +-
 src/database/sending.rs             |   8 +-
 src/database/transaction_ids.rs     |   6 +-
 src/server_server.rs                | 126 +++++++++++++++++++++++-----
 10 files changed, 150 insertions(+), 58 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index d297102c..5be10f14 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2086,7 +2086,7 @@ dependencies = [
 [[package]]
 name = "ruma"
 version = "0.4.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "assign",
  "js_int",
@@ -2107,7 +2107,7 @@ dependencies = [
 [[package]]
 name = "ruma-api"
 version = "0.18.5"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "bytes",
  "http",
@@ -2123,7 +2123,7 @@ dependencies = [
 [[package]]
 name = "ruma-api-macros"
 version = "0.18.5"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -2134,7 +2134,7 @@ dependencies = [
 [[package]]
 name = "ruma-appservice-api"
 version = "0.4.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "ruma-api",
  "ruma-common",
@@ -2148,7 +2148,7 @@ dependencies = [
 [[package]]
 name = "ruma-client-api"
 version = "0.12.3"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "assign",
  "bytes",
@@ -2168,7 +2168,7 @@ dependencies = [
 [[package]]
 name = "ruma-common"
 version = "0.6.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "indexmap",
  "js_int",
@@ -2183,7 +2183,7 @@ dependencies = [
 [[package]]
 name = "ruma-events"
 version = "0.24.6"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "indoc",
  "js_int",
@@ -2194,12 +2194,13 @@ dependencies = [
  "serde",
  "serde_json",
  "thiserror",
+ "wildmatch",
 ]
 
 [[package]]
 name = "ruma-events-macros"
 version = "0.24.6"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -2210,7 +2211,7 @@ dependencies = [
 [[package]]
 name = "ruma-federation-api"
 version = "0.3.1"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "js_int",
  "ruma-api",
@@ -2225,7 +2226,7 @@ dependencies = [
 [[package]]
 name = "ruma-identifiers"
 version = "0.20.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "percent-encoding",
  "rand 0.8.4",
@@ -2234,12 +2235,13 @@ dependencies = [
  "ruma-serde",
  "ruma-serde-macros",
  "serde",
+ "uuid",
 ]
 
 [[package]]
 name = "ruma-identifiers-macros"
 version = "0.20.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "quote",
  "ruma-identifiers-validation",
@@ -2249,7 +2251,7 @@ dependencies = [
 [[package]]
 name = "ruma-identifiers-validation"
 version = "0.5.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "thiserror",
 ]
@@ -2257,7 +2259,7 @@ dependencies = [
 [[package]]
 name = "ruma-identity-service-api"
 version = "0.3.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "js_int",
  "ruma-api",
@@ -2270,7 +2272,7 @@ dependencies = [
 [[package]]
 name = "ruma-push-gateway-api"
 version = "0.3.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "js_int",
  "ruma-api",
@@ -2285,8 +2287,9 @@ dependencies = [
 [[package]]
 name = "ruma-serde"
 version = "0.5.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
+ "base64 0.13.0",
  "bytes",
  "form_urlencoded",
  "itoa 0.4.8",
@@ -2299,7 +2302,7 @@ dependencies = [
 [[package]]
 name = "ruma-serde-macros"
 version = "0.5.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -2310,7 +2313,7 @@ dependencies = [
 [[package]]
 name = "ruma-signatures"
 version = "0.9.0"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "base64 0.13.0",
  "ed25519-dalek",
@@ -2327,7 +2330,7 @@ dependencies = [
 [[package]]
 name = "ruma-state-res"
 version = "0.4.1"
-source = "git+https://github.com/ruma/ruma?rev=f8ba7f795765bf4aeb4db06849f9fdde9c162ac3#f8ba7f795765bf4aeb4db06849f9fdde9c162ac3"
+source = "git+https://github.com/ruma/ruma?rev=08d60b3d376b63462f769d4b9bd3bbfb560d501a#08d60b3d376b63462f769d4b9bd3bbfb560d501a"
 dependencies = [
  "itertools",
  "js_int",
@@ -3308,6 +3311,15 @@ dependencies = [
  "percent-encoding",
 ]
 
+[[package]]
+name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+dependencies = [
+ "getrandom 0.2.3",
+]
+
 [[package]]
 name = "vcpkg"
 version = "0.2.15"
diff --git a/Cargo.toml b/Cargo.toml
index c87d949c..29a090c7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -19,7 +19,7 @@ rocket = { version = "0.5.0-rc.1", features = ["tls"] } # Used to handle request
 
 # Used for matrix spec type definitions and helpers
 #ruma = { version = "0.4.0", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
-ruma = { git = "https://github.com/ruma/ruma", rev = "f8ba7f795765bf4aeb4db06849f9fdde9c162ac3", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
+ruma = { git = "https://github.com/ruma/ruma", rev = "08d60b3d376b63462f769d4b9bd3bbfb560d501a", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
 #ruma = { git = "https://github.com/timokoesters/ruma", rev = "50c1db7e0a3a21fc794b0cce3b64285a4c750c71", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
 #ruma = { path = "../ruma/crates/ruma", features = ["compat", "rand", "appservice-api-c", "client-api", "federation-api", "push-gateway-api-c", "state-res", "unstable-pre-spec", "unstable-exhaustive-types"] }
 
diff --git a/src/client_server/membership.rs b/src/client_server/membership.rs
index cede51f0..70352784 100644
--- a/src/client_server/membership.rs
+++ b/src/client_server/membership.rs
@@ -23,7 +23,7 @@ use ruma::{
         },
         EventType,
     },
-    serde::{to_canonical_value, CanonicalJsonObject, CanonicalJsonValue},
+    serde::{to_canonical_value, Base64, CanonicalJsonObject, CanonicalJsonValue},
     state_res::{self, RoomVersion},
     uint, EventId, RoomId, RoomVersionId, ServerName, UserId,
 };
@@ -787,7 +787,7 @@ async fn join_room_by_id_helper(
 fn validate_and_add_event_id(
     pdu: &RawJsonValue,
     room_version: &RoomVersionId,
-    pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, String>>>,
+    pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
     db: &Database,
 ) -> Result<(Box<EventId>, CanonicalJsonObject)> {
     let mut value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| {
diff --git a/src/client_server/message.rs b/src/client_server/message.rs
index 9705e4c0..36653fab 100644
--- a/src/client_server/message.rs
+++ b/src/client_server/message.rs
@@ -74,11 +74,11 @@ pub async fn send_message_event_route(
     }
 
     let mut unsigned = BTreeMap::new();
-    unsigned.insert("transaction_id".to_owned(), body.txn_id.clone().into());
+    unsigned.insert("transaction_id".to_owned(), body.txn_id.to_string().into());
 
     let event_id = db.rooms.build_and_append_pdu(
         PduBuilder {
-            event_type: EventType::from(&body.event_type),
+            event_type: EventType::from(&*body.event_type),
             content: serde_json::from_str(body.body.body.json().get())
                 .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
             unsigned: Some(unsigned),
diff --git a/src/client_server/state.rs b/src/client_server/state.rs
index e42694ae..c07d4825 100644
--- a/src/client_server/state.rs
+++ b/src/client_server/state.rs
@@ -44,7 +44,7 @@ pub async fn send_state_event_for_key_route(
         &db,
         sender_user,
         &body.room_id,
-        EventType::from(&body.event_type),
+        EventType::from(&*body.event_type),
         &body.body.body, // Yes, I hate it too
         body.state_key.to_owned(),
     )
@@ -86,7 +86,7 @@ pub async fn send_state_event_for_empty_key_route(
         &db,
         sender_user,
         &body.room_id,
-        EventType::from(&body.event_type),
+        EventType::from(&*body.event_type),
         &body.body.body,
         body.state_key.to_owned(),
     )
diff --git a/src/client_server/to_device.rs b/src/client_server/to_device.rs
index 177b1234..6e764deb 100644
--- a/src/client_server/to_device.rs
+++ b/src/client_server/to_device.rs
@@ -53,8 +53,8 @@ pub async fn send_event_to_device_route(
                     serde_json::to_vec(&federation::transactions::edu::Edu::DirectToDevice(
                         DirectDeviceContent {
                             sender: sender_user.clone(),
-                            ev_type: EventType::from(&body.event_type),
-                            message_id: body.txn_id.clone(),
+                            ev_type: EventType::from(&*body.event_type),
+                            message_id: body.txn_id.to_string(),
                             messages,
                         },
                     ))
diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs
index adda6787..15ea9f73 100644
--- a/src/database/abstraction/rocksdb.rs
+++ b/src/database/abstraction/rocksdb.rs
@@ -44,7 +44,7 @@ fn db_options(
     db_opts.set_max_open_files(max_open_files);
     db_opts.set_compression_type(rocksdb::DBCompressionType::Zstd);
     db_opts.set_compaction_style(rocksdb::DBCompactionStyle::Level);
-    db_opts.optimize_level_style_compaction(cache_capacity_bytes);
+    db_opts.optimize_level_style_compaction(10 * 1024 * 1024);
 
     let prefix_extractor = rocksdb::SliceTransform::create_fixed_prefix(1);
     db_opts.set_prefix_extractor(prefix_extractor);
diff --git a/src/database/sending.rs b/src/database/sending.rs
index 1e180d43..65284a4f 100644
--- a/src/database/sending.rs
+++ b/src/database/sending.rs
@@ -524,7 +524,7 @@ impl Sending {
                         .unwrap(), // TODO: handle error
                     appservice::event::push_events::v1::Request {
                         events: &pdu_jsons,
-                        txn_id: &base64::encode_config(
+                        txn_id: (&*base64::encode_config(
                             Self::calculate_hash(
                                 &events
                                     .iter()
@@ -534,7 +534,7 @@ impl Sending {
                                     .collect::<Vec<_>>(),
                             ),
                             base64::URL_SAFE_NO_PAD,
-                        ),
+                        )).into(),
                     },
                 )
                 .await
@@ -682,7 +682,7 @@ impl Sending {
                         pdus: &pdu_jsons,
                         edus: &edu_jsons,
                         origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
-                        transaction_id: &base64::encode_config(
+                        transaction_id: (&*base64::encode_config(
                             Self::calculate_hash(
                                 &events
                                     .iter()
@@ -692,7 +692,7 @@ impl Sending {
                                     .collect::<Vec<_>>(),
                             ),
                             base64::URL_SAFE_NO_PAD,
-                        ),
+                        )).into(),
                     },
                 )
                 .await
diff --git a/src/database/transaction_ids.rs b/src/database/transaction_ids.rs
index f3467572..d576083a 100644
--- a/src/database/transaction_ids.rs
+++ b/src/database/transaction_ids.rs
@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use crate::Result;
-use ruma::{DeviceId, UserId};
+use ruma::{DeviceId, UserId, identifiers::TransactionId};
 
 use super::abstraction::Tree;
 
@@ -14,7 +14,7 @@ impl TransactionIds {
         &self,
         user_id: &UserId,
         device_id: Option<&DeviceId>,
-        txn_id: &str,
+        txn_id: &TransactionId,
         data: &[u8],
     ) -> Result<()> {
         let mut key = user_id.as_bytes().to_vec();
@@ -32,7 +32,7 @@ impl TransactionIds {
         &self,
         user_id: &UserId,
         device_id: Option<&DeviceId>,
-        txn_id: &str,
+        txn_id: &TransactionId,
     ) -> Result<Option<Vec<u8>>> {
         let mut key = user_id.as_bytes().to_vec();
         key.push(0xff);
diff --git a/src/server_server.rs b/src/server_server.rs
index c76afd34..5cd43d81 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -42,6 +42,7 @@ use ruma::{
     events::{
         receipt::{ReceiptEvent, ReceiptEventContent},
         room::{
+            server_acl::RoomServerAclEventContent,
             create::RoomCreateEventContent,
             member::{MembershipState, RoomMemberEventContent},
         },
@@ -49,7 +50,7 @@ use ruma::{
     },
     int,
     receipt::ReceiptType,
-    serde::JsonObject,
+    serde::{Base64, JsonObject},
     signatures::{CanonicalJsonObject, CanonicalJsonValue},
     state_res::{self, RoomVersion, StateMap},
     to_device::DeviceIdOrAllDevices,
@@ -551,7 +552,7 @@ pub fn get_server_keys_route(db: DatabaseGuard) -> Json<String> {
             .try_into()
             .expect("found invalid server signing keys in DB"),
         VerifyKey {
-            key: base64::encode_config(db.globals.keypair().public_key(), base64::STANDARD_NO_PAD),
+            key: Base64::new(db.globals.keypair().public_key().to_vec()),
         },
     );
     let mut response = serde_json::from_slice(
@@ -740,6 +741,8 @@ pub async fn send_transaction_message_route(
             }
         };
 
+        acl_check(&body.origin, &room_id, &db)?;
+
         let mutex = Arc::clone(
             db.globals
                 .roomid_mutex_federation
@@ -854,7 +857,7 @@ pub async fn send_transaction_message_route(
                 // Check if this is a new transaction id
                 if db
                     .transaction_ids
-                    .existing_txnid(&sender, None, &message_id)?
+                    .existing_txnid(&sender, None, (&*message_id).into())?
                     .is_some()
                 {
                     continue;
@@ -902,7 +905,7 @@ pub async fn send_transaction_message_route(
 
                 // Save transaction id with empty data
                 db.transaction_ids
-                    .add_txnid(&sender, None, &message_id, &[])?;
+                    .add_txnid(&sender, None, (&*message_id).into(), &[])?;
             }
             Edu::_Custom(_) => {}
         }
@@ -948,7 +951,7 @@ pub(crate) async fn handle_incoming_pdu<'a>(
     value: BTreeMap<String, CanonicalJsonValue>,
     is_timeline_event: bool,
     db: &'a Database,
-    pub_key_map: &'a RwLock<BTreeMap<String, BTreeMap<String, String>>>,
+    pub_key_map: &'a RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
 ) -> Result<Option<Vec<u8>>, String> {
     match db.rooms.exists(room_id) {
         Ok(true) => {}
@@ -1123,7 +1126,7 @@ fn handle_outlier_pdu<'a>(
     room_id: &'a RoomId,
     value: BTreeMap<String, CanonicalJsonValue>,
     db: &'a Database,
-    pub_key_map: &'a RwLock<BTreeMap<String, BTreeMap<String, String>>>,
+    pub_key_map: &'a RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
 ) -> AsyncRecursiveType<'a, Result<(Arc<PduEvent>, BTreeMap<String, CanonicalJsonValue>), String>> {
     Box::pin(async move {
         // TODO: For RoomVersion6 we must check that Raw<..> is canonical do we anywhere?: https://matrix.org/docs/spec/rooms/v6#canonical-json
@@ -1285,7 +1288,7 @@ async fn upgrade_outlier_to_timeline_pdu(
     origin: &ServerName,
     db: &Database,
     room_id: &RoomId,
-    pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, String>>>,
+    pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
 ) -> Result<Option<Vec<u8>>, String> {
     if let Ok(Some(pduid)) = db.rooms.get_pdu_id(&incoming_pdu.event_id) {
         return Ok(Some(pduid));
@@ -1827,7 +1830,7 @@ pub(crate) fn fetch_and_handle_outliers<'a>(
     events: &'a [Arc<EventId>],
     create_event: &'a PduEvent,
     room_id: &'a RoomId,
-    pub_key_map: &'a RwLock<BTreeMap<String, BTreeMap<String, String>>>,
+    pub_key_map: &'a RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
 ) -> AsyncRecursiveType<'a, Vec<(Arc<PduEvent>, Option<BTreeMap<String, CanonicalJsonValue>>)>> {
     Box::pin(async move {
         let back_off = |id| match db.globals.bad_event_ratelimiter.write().unwrap().entry(id) {
@@ -1966,9 +1969,9 @@ pub(crate) async fn fetch_signing_keys(
     db: &Database,
     origin: &ServerName,
     signature_ids: Vec<String>,
-) -> Result<BTreeMap<String, String>> {
+) -> Result<BTreeMap<String, Base64>> {
     let contains_all_ids =
-        |keys: &BTreeMap<String, String>| signature_ids.iter().all(|id| keys.contains_key(id));
+        |keys: &BTreeMap<String, Base64>| signature_ids.iter().all(|id| keys.contains_key(id));
 
     let permit = db
         .globals
@@ -2355,8 +2358,11 @@ pub fn get_event_route(
     let room_id = <&RoomId>::try_from(room_id_str)
         .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
 
-    if !db.rooms.server_in_room(sender_servername, room_id)? {
-        return Err(Error::BadRequest(ErrorKind::NotFound, "Event not found."));
+    if !db.rooms.server_in_room(sender_servername, &room_id)? {
+        return Err(Error::BadRequest(
+            ErrorKind::Forbidden,
+            "Server is not in room",
+        ));
     }
 
     Ok(get_event::v1::Response {
@@ -2395,6 +2401,8 @@ pub fn get_missing_events_route(
         ));
     }
 
+    acl_check(sender_servername, &body.room_id, &db)?;
+
     let mut queued_events = body.latest_events.clone();
     let mut events = Vec::new();
 
@@ -2464,6 +2472,15 @@ pub fn get_event_authorization_route(
         .as_ref()
         .expect("server is authenticated");
 
+    if !db.rooms.server_in_room(sender_servername, &body.room_id)? {
+        return Err(Error::BadRequest(
+            ErrorKind::Forbidden,
+            "Server is not in room.",
+        ));
+    }
+
+    acl_check(sender_servername, &body.room_id, &db)?;
+
     let event = db
         .rooms
         .get_pdu_json(&body.event_id)?
@@ -2477,10 +2494,6 @@ pub fn get_event_authorization_route(
     let room_id = <&RoomId>::try_from(room_id_str)
         .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
 
-    if !db.rooms.server_in_room(sender_servername, room_id)? {
-        return Err(Error::BadRequest(ErrorKind::NotFound, "Event not found."));
-    }
-
     let auth_chain_ids = get_auth_chain(room_id, vec![Arc::from(&*body.event_id)], &db)?;
 
     Ok(get_event_authorization::v1::Response {
@@ -2520,6 +2533,8 @@ pub fn get_room_state_route(
         ));
     }
 
+    acl_check(sender_servername, &body.room_id, &db)?;
+
     let shortstatehash = db
         .rooms
         .pdu_shortstatehash(&body.event_id)?
@@ -2583,6 +2598,8 @@ pub fn get_room_state_ids_route(
         ));
     }
 
+    acl_check(sender_servername, &body.room_id, &db)?;
+
     let shortstatehash = db
         .rooms
         .pdu_shortstatehash(&body.event_id)?
@@ -2626,10 +2643,17 @@ pub fn create_join_event_template_route(
     if !db.rooms.exists(&body.room_id)? {
         return Err(Error::BadRequest(
             ErrorKind::NotFound,
-            "Server is not in room.",
+            "Room is unknown to this server.",
         ));
     }
 
+    let sender_servername = body
+        .sender_servername
+        .as_ref()
+        .expect("server is authenticated");
+
+    acl_check(sender_servername, &body.room_id, &db)?;
+
     let prev_events: Vec<_> = db
         .rooms
         .get_pdu_leaves(&body.room_id)?
@@ -2782,6 +2806,7 @@ pub fn create_join_event_template_route(
 
 async fn create_join_event(
     db: &DatabaseGuard,
+    sender_servername: &ServerName,
     room_id: &RoomId,
     pdu: &RawJsonValue,
 ) -> Result<RoomState> {
@@ -2789,6 +2814,15 @@ async fn create_join_event(
         return Err(Error::bad_config("Federation is disabled."));
     }
 
+    if !db.rooms.exists(room_id)? {
+        return Err(Error::BadRequest(
+            ErrorKind::NotFound,
+            "Room is unknown to this server.",
+        ));
+    }
+
+    acl_check(sender_servername, room_id, &db)?;
+
     // We need to return the state prior to joining, let's keep a reference to that here
     let shortstatehash = db
         .rooms
@@ -2888,7 +2922,12 @@ pub async fn create_join_event_v1_route(
     db: DatabaseGuard,
     body: Ruma<create_join_event::v1::Request<'_>>,
 ) -> ConduitResult<create_join_event::v1::Response> {
-    let room_state = create_join_event(&db, &body.room_id, &body.pdu).await?;
+    let sender_servername = body
+        .sender_servername
+        .as_ref()
+        .expect("server is authenticated");
+
+    let room_state = create_join_event(&db, sender_servername, &body.room_id, &body.pdu).await?;
 
     Ok(create_join_event::v1::Response { room_state }.into())
 }
@@ -2905,7 +2944,12 @@ pub async fn create_join_event_v2_route(
     db: DatabaseGuard,
     body: Ruma<create_join_event::v2::Request<'_>>,
 ) -> ConduitResult<create_join_event::v2::Response> {
-    let room_state = create_join_event(&db, &body.room_id, &body.pdu).await?;
+    let sender_servername = body
+        .sender_servername
+        .as_ref()
+        .expect("server is authenticated");
+
+    let room_state = create_join_event(&db, sender_servername, &body.room_id, &body.pdu).await?;
 
     Ok(create_join_event::v2::Response { room_state }.into())
 }
@@ -2926,6 +2970,13 @@ pub async fn create_invite_route(
         return Err(Error::bad_config("Federation is disabled."));
     }
 
+    let sender_servername = body
+        .sender_servername
+        .as_ref()
+        .expect("server is authenticated");
+
+    acl_check(sender_servername, &body.room_id, &db)?;
+
     if body.room_version != RoomVersionId::V5 && body.room_version != RoomVersionId::V6 {
         return Err(Error::BadRequest(
             ErrorKind::IncompatibleRoomVersion {
@@ -3199,7 +3250,7 @@ pub async fn claim_keys_route(
 #[tracing::instrument(skip(event, pub_key_map, db))]
 pub(crate) async fn fetch_required_signing_keys(
     event: &BTreeMap<String, CanonicalJsonValue>,
-    pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, String>>>,
+    pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
     db: &Database,
 ) -> Result<()> {
     let signatures = event
@@ -3253,7 +3304,7 @@ fn get_server_keys_from_cache(
     pdu: &RawJsonValue,
     servers: &mut BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, QueryCriteria>>,
     room_version: &RoomVersionId,
-    pub_key_map: &mut RwLockWriteGuard<'_, BTreeMap<String, BTreeMap<String, String>>>,
+    pub_key_map: &mut RwLockWriteGuard<'_, BTreeMap<String, BTreeMap<String, Base64>>>,
     db: &Database,
 ) -> Result<()> {
     let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| {
@@ -3306,7 +3357,7 @@ fn get_server_keys_from_cache(
         let signature_ids = signature_object.keys().cloned().collect::<Vec<_>>();
 
         let contains_all_ids =
-            |keys: &BTreeMap<String, String>| signature_ids.iter().all(|id| keys.contains_key(id));
+            |keys: &BTreeMap<String, Base64>| signature_ids.iter().all(|id| keys.contains_key(id));
 
         let origin = <&ServerName>::try_from(signature_server.as_str()).map_err(|_| {
             Error::BadServerResponse("Invalid servername in signatures of server response pdu.")
@@ -3339,7 +3390,7 @@ fn get_server_keys_from_cache(
 pub(crate) async fn fetch_join_signing_keys(
     event: &create_join_event::v2::Response,
     room_version: &RoomVersionId,
-    pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, String>>>,
+    pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
     db: &Database,
 ) -> Result<()> {
     let mut servers: BTreeMap<Box<ServerName>, BTreeMap<Box<ServerSigningKeyId>, QueryCriteria>> =
@@ -3439,6 +3490,35 @@ pub(crate) async fn fetch_join_signing_keys(
     Ok(())
 }
 
+/// Returns Ok if the acl allows the server
+fn acl_check(
+    server_name: &ServerName,
+    room_id: &RoomId,
+    db: &Database,
+) -> Result<()> {
+    let acl_event = match db
+        .rooms
+        .room_state_get(room_id, &EventType::RoomServerAcl, "")? {
+            Some(acl) => acl,
+            None => return Ok(()),
+        };
+
+    let acl_event_content: RoomServerAclEventContent = match
+        serde_json::from_str(acl_event.content.get()) {
+            Ok(content) => content,
+            Err(_) => {
+                warn!("Invalid ACL event");
+                return Ok(());
+            }
+        };
+
+    if acl_event_content.is_allowed(server_name) {
+        Ok(())
+    } else {
+        Err(Error::BadRequest(ErrorKind::Forbidden, "Server was denied by ACL"))
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::{add_port_to_hostname, get_ip_with_port, FedDest};

From 8c90e7adfb0d06164d17921e6e686cdaab0d8f1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Mon, 17 Jan 2022 14:39:37 +0100
Subject: [PATCH 04/16] refactor: fix warnings

---
 src/database/abstraction/rocksdb.rs | 27 ++++++---------------------
 src/database/sending.rs             |  6 ++++--
 src/database/transaction_ids.rs     |  2 +-
 src/server_server.rs                | 26 +++++++++++++-------------
 4 files changed, 24 insertions(+), 37 deletions(-)

diff --git a/src/database/abstraction/rocksdb.rs b/src/database/abstraction/rocksdb.rs
index 15ea9f73..d6157135 100644
--- a/src/database/abstraction/rocksdb.rs
+++ b/src/database/abstraction/rocksdb.rs
@@ -4,7 +4,6 @@ use std::{future::Future, pin::Pin, sync::Arc, sync::RwLock};
 
 pub struct Engine {
     rocks: rocksdb::DBWithThreadMode<rocksdb::MultiThreaded>,
-    cache_capacity_bytes: usize,
     max_open_files: i32,
     cache: rocksdb::Cache,
     old_cfs: Vec<String>,
@@ -17,11 +16,7 @@ pub struct RocksDbEngineTree<'a> {
     write_lock: RwLock<()>,
 }
 
-fn db_options(
-    cache_capacity_bytes: usize,
-    max_open_files: i32,
-    rocksdb_cache: &rocksdb::Cache,
-) -> rocksdb::Options {
+fn db_options(max_open_files: i32, rocksdb_cache: &rocksdb::Cache) -> rocksdb::Options {
     let mut block_based_options = rocksdb::BlockBasedOptions::default();
     block_based_options.set_block_cache(rocksdb_cache);
 
@@ -57,11 +52,7 @@ impl DatabaseEngine for Arc<Engine> {
         let cache_capacity_bytes = (config.db_cache_capacity_mb * 1024.0 * 1024.0) as usize;
         let rocksdb_cache = rocksdb::Cache::new_lru_cache(cache_capacity_bytes).unwrap();
 
-        let db_opts = db_options(
-            cache_capacity_bytes,
-            config.rocksdb_max_open_files,
-            &rocksdb_cache,
-        );
+        let db_opts = db_options(config.rocksdb_max_open_files, &rocksdb_cache);
 
         let cfs = rocksdb::DBWithThreadMode::<rocksdb::MultiThreaded>::list_cf(
             &db_opts,
@@ -75,18 +66,13 @@ impl DatabaseEngine for Arc<Engine> {
             cfs.iter().map(|name| {
                 rocksdb::ColumnFamilyDescriptor::new(
                     name,
-                    db_options(
-                        cache_capacity_bytes,
-                        config.rocksdb_max_open_files,
-                        &rocksdb_cache,
-                    ),
+                    db_options(config.rocksdb_max_open_files, &rocksdb_cache),
                 )
             }),
         )?;
 
         Ok(Arc::new(Engine {
             rocks: db,
-            cache_capacity_bytes,
             max_open_files: config.rocksdb_max_open_files,
             cache: rocksdb_cache,
             old_cfs: cfs,
@@ -96,10 +82,9 @@ impl DatabaseEngine for Arc<Engine> {
     fn open_tree(&self, name: &'static str) -> Result<Arc<dyn Tree>> {
         if !self.old_cfs.contains(&name.to_owned()) {
             // Create if it didn't exist
-            let _ = self.rocks.create_cf(
-                name,
-                &db_options(self.cache_capacity_bytes, self.max_open_files, &self.cache),
-            );
+            let _ = self
+                .rocks
+                .create_cf(name, &db_options(self.max_open_files, &self.cache));
         }
 
         Ok(Arc::new(RocksDbEngineTree {
diff --git a/src/database/sending.rs b/src/database/sending.rs
index 65284a4f..69f7c444 100644
--- a/src/database/sending.rs
+++ b/src/database/sending.rs
@@ -534,7 +534,8 @@ impl Sending {
                                     .collect::<Vec<_>>(),
                             ),
                             base64::URL_SAFE_NO_PAD,
-                        )).into(),
+                        ))
+                            .into(),
                     },
                 )
                 .await
@@ -692,7 +693,8 @@ impl Sending {
                                     .collect::<Vec<_>>(),
                             ),
                             base64::URL_SAFE_NO_PAD,
-                        )).into(),
+                        ))
+                            .into(),
                     },
                 )
                 .await
diff --git a/src/database/transaction_ids.rs b/src/database/transaction_ids.rs
index d576083a..12b838ba 100644
--- a/src/database/transaction_ids.rs
+++ b/src/database/transaction_ids.rs
@@ -1,7 +1,7 @@
 use std::sync::Arc;
 
 use crate::Result;
-use ruma::{DeviceId, UserId, identifiers::TransactionId};
+use ruma::{identifiers::TransactionId, DeviceId, UserId};
 
 use super::abstraction::Tree;
 
diff --git a/src/server_server.rs b/src/server_server.rs
index 5cd43d81..54ae0251 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -42,9 +42,9 @@ use ruma::{
     events::{
         receipt::{ReceiptEvent, ReceiptEventContent},
         room::{
-            server_acl::RoomServerAclEventContent,
             create::RoomCreateEventContent,
             member::{MembershipState, RoomMemberEventContent},
+            server_acl::RoomServerAclEventContent,
         },
         AnyEphemeralRoomEvent, EventType,
     },
@@ -3491,20 +3491,17 @@ pub(crate) async fn fetch_join_signing_keys(
 }
 
 /// Returns Ok if the acl allows the server
-fn acl_check(
-    server_name: &ServerName,
-    room_id: &RoomId,
-    db: &Database,
-) -> Result<()> {
+fn acl_check(server_name: &ServerName, room_id: &RoomId, db: &Database) -> Result<()> {
     let acl_event = match db
         .rooms
-        .room_state_get(room_id, &EventType::RoomServerAcl, "")? {
-            Some(acl) => acl,
-            None => return Ok(()),
-        };
+        .room_state_get(room_id, &EventType::RoomServerAcl, "")?
+    {
+        Some(acl) => acl,
+        None => return Ok(()),
+    };
 
-    let acl_event_content: RoomServerAclEventContent = match
-        serde_json::from_str(acl_event.content.get()) {
+    let acl_event_content: RoomServerAclEventContent =
+        match serde_json::from_str(acl_event.content.get()) {
             Ok(content) => content,
             Err(_) => {
                 warn!("Invalid ACL event");
@@ -3515,7 +3512,10 @@ fn acl_check(
     if acl_event_content.is_allowed(server_name) {
         Ok(())
     } else {
-        Err(Error::BadRequest(ErrorKind::Forbidden, "Server was denied by ACL"))
+        Err(Error::BadRequest(
+            ErrorKind::Forbidden,
+            "Server was denied by ACL",
+        ))
     }
 }
 

From 03b174335cfc472c3ecaba7068ead74f0e2268be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Mon, 17 Jan 2022 14:46:53 +0100
Subject: [PATCH 05/16] improvement: lower default pdu cache capacity

---
 src/database.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/database.rs b/src/database.rs
index fd7a1451..1997dc0a 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -134,7 +134,7 @@ fn default_rocksdb_max_open_files() -> i32 {
 }
 
 fn default_pdu_cache_capacity() -> u32 {
-    1_000_000
+    150_000
 }
 
 fn default_cleanup_second_interval() -> u32 {

From 53de3509087f46b6a45ca20d27e8fa2884269535 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Mon, 17 Jan 2022 23:24:27 +0100
Subject: [PATCH 06/16] fix: less load when lazy loading

---
 src/client_server/sync.rs | 53 ++++++++++++++++++++-------------------
 1 file changed, 27 insertions(+), 26 deletions(-)

diff --git a/src/client_server/sync.rs b/src/client_server/sync.rs
index bd2f48a3..14aac3a1 100644
--- a/src/client_server/sync.rs
+++ b/src/client_server/sync.rs
@@ -453,38 +453,39 @@ async fn sync_helper(
             let joined_since_last_sync = since_sender_member
                 .map_or(true, |member| member.membership != MembershipState::Join);
 
-            let current_state_ids = db.rooms.state_full_ids(current_shortstatehash)?;
-
-            let since_state_ids = db.rooms.state_full_ids(since_shortstatehash)?;
-
             let mut state_events = Vec::new();
             let mut lazy_loaded = HashSet::new();
 
-            for (key, id) in current_state_ids {
-                if body.full_state || since_state_ids.get(&key) != Some(&id) {
-                    let pdu = match db.rooms.get_pdu(&id)? {
-                        Some(pdu) => pdu,
-                        None => {
-                            error!("Pdu in state not found: {}", id);
-                            continue;
-                        }
-                    };
+            if since_shortstatehash != current_shortstatehash {
+                let current_state_ids = db.rooms.state_full_ids(current_shortstatehash)?;
+                let since_state_ids = db.rooms.state_full_ids(since_shortstatehash)?;
 
-                    if pdu.kind == EventType::RoomMember {
-                        match UserId::parse(
-                            pdu.state_key
-                                .as_ref()
-                                .expect("State event has state key")
-                                .clone(),
-                        ) {
-                            Ok(state_key_userid) => {
-                                lazy_loaded.insert(state_key_userid);
+                for (key, id) in current_state_ids {
+                    if body.full_state || since_state_ids.get(&key) != Some(&id) {
+                        let pdu = match db.rooms.get_pdu(&id)? {
+                            Some(pdu) => pdu,
+                            None => {
+                                error!("Pdu in state not found: {}", id);
+                                continue;
                             }
-                            Err(e) => error!("Invalid state key for member event: {}", e),
-                        }
-                    }
+                        };
 
-                    state_events.push(pdu);
+                        if pdu.kind == EventType::RoomMember {
+                            match UserId::parse(
+                                pdu.state_key
+                                    .as_ref()
+                                    .expect("State event has state key")
+                                    .clone(),
+                            ) {
+                                Ok(state_key_userid) => {
+                                    lazy_loaded.insert(state_key_userid);
+                                }
+                                Err(e) => error!("Invalid state key for member event: {}", e),
+                            }
+                        }
+
+                        state_events.push(pdu);
+                    }
                 }
             }
 

From 13a48c45776de19912ecd040a6434c75152802f7 Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Tue, 18 Jan 2022 21:04:44 +0100
Subject: [PATCH 07/16] Clean up mod and use statements in lib.rs and main.rs

---
 src/lib.rs  | 10 ++++++----
 src/main.rs | 22 ++++------------------
 2 files changed, 10 insertions(+), 22 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index 82b8f340..745eb394 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,21 +7,23 @@
 #![allow(clippy::suspicious_else_formatting)]
 #![deny(clippy::dbg_macro)]
 
-pub mod appservice_server;
-pub mod client_server;
+use std::ops::Deref;
+
 mod database;
 mod error;
 mod pdu;
 mod ruma_wrapper;
-pub mod server_server;
 mod utils;
 
+pub mod appservice_server;
+pub mod client_server;
+pub mod server_server;
+
 pub use database::{Config, Database};
 pub use error::{Error, Result};
 pub use pdu::PduEvent;
 pub use rocket::Config as RocketConfig;
 pub use ruma_wrapper::{ConduitResult, Ruma, RumaResponse};
-use std::ops::Deref;
 
 pub struct State<'r, T: Send + Sync + 'static>(pub &'r T);
 
diff --git a/src/main.rs b/src/main.rs
index 56faa3e7..d9bbc240 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,27 +7,9 @@
 #![allow(clippy::suspicious_else_formatting)]
 #![deny(clippy::dbg_macro)]
 
-pub mod appservice_server;
-pub mod client_server;
-pub mod server_server;
-
-mod database;
-mod error;
-mod pdu;
-mod ruma_wrapper;
-mod utils;
-
 use std::sync::Arc;
 
-use database::Config;
-pub use database::Database;
-pub use error::{Error, Result};
 use opentelemetry::trace::{FutureExt, Tracer};
-pub use pdu::PduEvent;
-pub use rocket::State;
-use ruma::api::client::error::ErrorKind;
-pub use ruma_wrapper::{ConduitResult, Ruma, RumaResponse};
-
 use rocket::{
     catch, catchers,
     figment::{
@@ -36,9 +18,13 @@ use rocket::{
     },
     routes, Request,
 };
+use ruma::api::client::error::ErrorKind;
 use tokio::sync::RwLock;
 use tracing_subscriber::{prelude::*, EnvFilter};
 
+pub use conduit::*; // Re-export everything from the library crate
+pub use rocket::State;
+
 fn setup_rocket(config: Figment, data: Arc<RwLock<Database>>) -> rocket::Rocket<rocket::Build> {
     rocket::custom(config)
         .manage(data)

From c6277c72a1f75d889b47708769adf376cac9d1ea Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Tue, 18 Jan 2022 21:05:40 +0100
Subject: [PATCH 08/16] Fix warnings in database::abstraction

---
 src/database/abstraction.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/database/abstraction.rs b/src/database/abstraction.rs
index 17bd971f..321b064f 100644
--- a/src/database/abstraction.rs
+++ b/src/database/abstraction.rs
@@ -23,12 +23,12 @@ pub trait DatabaseEngine: Send + Sync {
     where
         Self: Sized;
     fn open_tree(&self, name: &'static str) -> Result<Arc<dyn Tree>>;
-    fn flush(self: &Self) -> Result<()>;
-    fn cleanup(self: &Self) -> Result<()> {
+    fn flush(&self) -> Result<()>;
+    fn cleanup(&self) -> Result<()> {
         Ok(())
     }
-    fn memory_usage(self: &Self) -> Result<String> {
-        Ok("Current database engine does not support memory usage reporting.".to_string())
+    fn memory_usage(&self) -> Result<String> {
+        Ok("Current database engine does not support memory usage reporting.".to_owned())
     }
 }
 

From d4eb3e3295ee1b0947b66d1d45ef10bb4d152839 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Wed, 19 Jan 2022 07:09:25 +0100
Subject: [PATCH 09/16] fix: rocksdb does not use zstd compression unless we
 disable everything else

---
 Cargo.toml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index 29a090c7..32233305 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -78,7 +78,8 @@ crossbeam = { version = "0.8.1", optional = true }
 num_cpus = "1.13.0"
 threadpool = "1.8.1"
 heed = { git = "https://github.com/timokoesters/heed.git", rev = "f6f825da7fb2c758867e05ad973ef800a6fe1d5d", optional = true }
-rocksdb = { version = "0.17.0", features = ["multi-threaded-cf"], optional = true }
+rocksdb = { version = "0.17.0", default-features = false, features = ["multi-threaded-cf", "zstd"], optional = true }
+
 thread_local = "1.1.3"
 # used for TURN server authentication
 hmac = "0.11.0"

From a0fc5eba72a7b364cfe91d5b188b136fa555b7e1 Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Wed, 19 Jan 2022 23:56:55 +0100
Subject: [PATCH 10/16] Remove unnecessary Result

---
 src/database/uiaa.rs |  7 +++----
 src/ruma_wrapper.rs  | 13 +++++--------
 2 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/src/database/uiaa.rs b/src/database/uiaa.rs
index 5e11467e..b0c8d6dd 100644
--- a/src/database/uiaa.rs
+++ b/src/database/uiaa.rs
@@ -166,13 +166,12 @@ impl Uiaa {
         user_id: &UserId,
         device_id: &DeviceId,
         session: &str,
-    ) -> Result<Option<CanonicalJsonValue>> {
-        Ok(self
-            .userdevicesessionid_uiaarequest
+    ) -> Option<CanonicalJsonValue> {
+        self.userdevicesessionid_uiaarequest
             .read()
             .unwrap()
             .get(&(user_id.to_owned(), device_id.to_owned(), session.to_owned()))
-            .map(|j| j.to_owned()))
+            .map(|j| j.to_owned())
     }
 
     fn update_uiaa_session(
diff --git a/src/ruma_wrapper.rs b/src/ruma_wrapper.rs
index 4b8d5dea..1bd921d9 100644
--- a/src/ruma_wrapper.rs
+++ b/src/ruma_wrapper.rs
@@ -296,14 +296,11 @@ where
                 .and_then(|auth| auth.get("session"))
                 .and_then(|session| session.as_str())
                 .and_then(|session| {
-                    db.uiaa
-                        .get_uiaa_request(
-                            &user_id,
-                            &sender_device.clone().unwrap_or_else(|| "".into()),
-                            session,
-                        )
-                        .ok()
-                        .flatten()
+                    db.uiaa.get_uiaa_request(
+                        &user_id,
+                        &sender_device.clone().unwrap_or_else(|| "".into()),
+                        session,
+                    )
                 })
             {
                 for (key, value) in initial_request {

From 756a41f22d24c89682eea826e138f8c3896433fb Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Thu, 20 Jan 2022 00:10:39 +0100
Subject: [PATCH 11/16] Fix rustc / clippy warnings

---
 src/client_server/context.rs | 15 +++++++--------
 src/client_server/keys.rs    |  2 +-
 src/client_server/message.rs | 14 +++++++-------
 src/client_server/profile.rs |  4 ++--
 src/database.rs              | 30 ++++++++++++------------------
 src/database/admin.rs        |  2 +-
 src/database/rooms.rs        | 27 +++++++++++++--------------
 src/server_server.rs         |  6 +++---
 8 files changed, 46 insertions(+), 54 deletions(-)

diff --git a/src/client_server/context.rs b/src/client_server/context.rs
index 94a44e39..e1177661 100644
--- a/src/client_server/context.rs
+++ b/src/client_server/context.rs
@@ -3,8 +3,7 @@ use ruma::{
     api::client::{error::ErrorKind, r0::context::get_context},
     events::EventType,
 };
-use std::collections::HashSet;
-use std::convert::TryFrom;
+use std::{collections::HashSet, convert::TryFrom};
 
 #[cfg(feature = "conduit_bin")]
 use rocket::get;
@@ -55,8 +54,8 @@ pub async fn get_context_route(
         ))?;
 
     if !db.rooms.lazy_load_was_sent_before(
-        &sender_user,
-        &sender_device,
+        sender_user,
+        sender_device,
         &body.room_id,
         &base_event.sender,
     )? {
@@ -79,8 +78,8 @@ pub async fn get_context_route(
 
     for (_, event) in &events_before {
         if !db.rooms.lazy_load_was_sent_before(
-            &sender_user,
-            &sender_device,
+            sender_user,
+            sender_device,
             &body.room_id,
             &event.sender,
         )? {
@@ -112,8 +111,8 @@ pub async fn get_context_route(
 
     for (_, event) in &events_after {
         if !db.rooms.lazy_load_was_sent_before(
-            &sender_user,
-            &sender_device,
+            sender_user,
+            sender_device,
             &body.room_id,
             &event.sender,
         )? {
diff --git a/src/client_server/keys.rs b/src/client_server/keys.rs
index be0675d8..e7aec26b 100644
--- a/src/client_server/keys.rs
+++ b/src/client_server/keys.rs
@@ -272,7 +272,7 @@ pub async fn get_key_changes_route(
     device_list_updates.extend(
         db.users
             .keys_changed(
-                &sender_user.to_string(),
+                sender_user.as_str(),
                 body.from
                     .parse()
                     .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from`."))?,
diff --git a/src/client_server/message.rs b/src/client_server/message.rs
index 36653fab..7d904f90 100644
--- a/src/client_server/message.rs
+++ b/src/client_server/message.rs
@@ -139,7 +139,7 @@ pub async fn get_message_events_route(
     let to = body.to.as_ref().map(|t| t.parse());
 
     db.rooms
-        .lazy_load_confirm_delivery(&sender_user, &sender_device, &body.room_id, from)?;
+        .lazy_load_confirm_delivery(sender_user, sender_device, &body.room_id, from)?;
 
     // Use limit or else 10
     let limit = body.limit.try_into().map_or(10_usize, |l: u32| l as usize);
@@ -168,8 +168,8 @@ pub async fn get_message_events_route(
 
             for (_, event) in &events_after {
                 if !db.rooms.lazy_load_was_sent_before(
-                    &sender_user,
-                    &sender_device,
+                    sender_user,
+                    sender_device,
                     &body.room_id,
                     &event.sender,
                 )? {
@@ -205,8 +205,8 @@ pub async fn get_message_events_route(
 
             for (_, event) in &events_before {
                 if !db.rooms.lazy_load_was_sent_before(
-                    &sender_user,
-                    &sender_device,
+                    sender_user,
+                    sender_device,
                     &body.room_id,
                     &event.sender,
                 )? {
@@ -239,8 +239,8 @@ pub async fn get_message_events_route(
 
     if let Some(next_token) = next_token {
         db.rooms.lazy_load_mark_sent(
-            &sender_user,
-            &sender_device,
+            sender_user,
+            sender_device,
             &body.room_id,
             lazy_loaded,
             next_token,
diff --git a/src/client_server/profile.rs b/src/client_server/profile.rs
index 29b1ae87..71e61da3 100644
--- a/src/client_server/profile.rs
+++ b/src/client_server/profile.rs
@@ -52,7 +52,7 @@ pub async fn set_displayname_route(
                                 .room_state_get(
                                     &room_id,
                                     &EventType::RoomMember,
-                                    &sender_user.to_string(),
+                                    sender_user.as_str(),
                                 )?
                                 .ok_or_else(|| {
                                     Error::bad_database(
@@ -195,7 +195,7 @@ pub async fn set_avatar_url_route(
                                 .room_state_get(
                                     &room_id,
                                     &EventType::RoomMember,
-                                    &sender_user.to_string(),
+                                    sender_user.as_str(),
                                 )?
                                 .ok_or_else(|| {
                                     Error::bad_database(
diff --git a/src/database.rs b/src/database.rs
index 1997dc0a..7a4ddc66 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -212,28 +212,22 @@ impl Database {
             return Ok(());
         }
 
-        if sled_exists {
-            if config.database_backend != "sled" {
-                return Err(Error::bad_config(
-                    "Found sled at database_path, but is not specified in config.",
-                ));
-            }
+        if sled_exists && config.database_backend != "sled" {
+            return Err(Error::bad_config(
+                "Found sled at database_path, but is not specified in config.",
+            ));
         }
 
-        if sqlite_exists {
-            if config.database_backend != "sqlite" {
-                return Err(Error::bad_config(
-                    "Found sqlite at database_path, but is not specified in config.",
-                ));
-            }
+        if sqlite_exists && config.database_backend != "sqlite" {
+            return Err(Error::bad_config(
+                "Found sqlite at database_path, but is not specified in config.",
+            ));
         }
 
-        if rocksdb_exists {
-            if config.database_backend != "rocksdb" {
-                return Err(Error::bad_config(
-                    "Found rocksdb at database_path, but is not specified in config.",
-                ));
-            }
+        if rocksdb_exists && config.database_backend != "rocksdb" {
+            return Err(Error::bad_config(
+                "Found rocksdb at database_path, but is not specified in config.",
+            ));
         }
 
         Ok(())
diff --git a/src/database/admin.rs b/src/database/admin.rs
index 7d2301d9..bf38bd8c 100644
--- a/src/database/admin.rs
+++ b/src/database/admin.rs
@@ -118,7 +118,7 @@ impl Admin {
                                 if let Ok(response) = guard._db.memory_usage() {
                                     send_message(RoomMessageEventContent::text_plain(response), guard, &state_lock);
                                 } else {
-                                    send_message(RoomMessageEventContent::text_plain("Failed to get database memory usage.".to_string()), guard, &state_lock);
+                                    send_message(RoomMessageEventContent::text_plain("Failed to get database memory usage.".to_owned()), guard, &state_lock);
                                 }
                             }
                             AdminCommand::SendMessage(message) => {
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 0ba6c9ba..c9a3c202 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -2727,7 +2727,7 @@ impl Rooms {
             let state_lock = mutex_state.lock().await;
 
             let mut event: RoomMemberEventContent = serde_json::from_str(
-                self.room_state_get(room_id, &EventType::RoomMember, &user_id.to_string())?
+                self.room_state_get(room_id, &EventType::RoomMember, user_id.as_str())?
                     .ok_or(Error::BadRequest(
                         ErrorKind::BadState,
                         "Cannot leave a room you are not a member of.",
@@ -3462,8 +3462,7 @@ impl Rooms {
                 &key[0].to_be_bytes(),
                 &chain
                     .iter()
-                    .map(|s| s.to_be_bytes().to_vec())
-                    .flatten()
+                    .flat_map(|s| s.to_be_bytes().to_vec())
                     .collect::<Vec<u8>>(),
             )?;
         }
@@ -3484,11 +3483,11 @@ impl Rooms {
     ) -> Result<bool> {
         let mut key = user_id.as_bytes().to_vec();
         key.push(0xff);
-        key.extend_from_slice(&device_id.as_bytes());
+        key.extend_from_slice(device_id.as_bytes());
         key.push(0xff);
-        key.extend_from_slice(&room_id.as_bytes());
+        key.extend_from_slice(room_id.as_bytes());
         key.push(0xff);
-        key.extend_from_slice(&ll_user.as_bytes());
+        key.extend_from_slice(ll_user.as_bytes());
         Ok(self.lazyloadedids.get(&key)?.is_some())
     }
 
@@ -3528,14 +3527,14 @@ impl Rooms {
         )) {
             let mut prefix = user_id.as_bytes().to_vec();
             prefix.push(0xff);
-            prefix.extend_from_slice(&device_id.as_bytes());
+            prefix.extend_from_slice(device_id.as_bytes());
             prefix.push(0xff);
-            prefix.extend_from_slice(&room_id.as_bytes());
+            prefix.extend_from_slice(room_id.as_bytes());
             prefix.push(0xff);
 
             for ll_id in user_ids {
                 let mut key = prefix.clone();
-                key.extend_from_slice(&ll_id.as_bytes());
+                key.extend_from_slice(ll_id.as_bytes());
                 self.lazyloadedids.insert(&key, &[])?;
             }
         }
@@ -3546,15 +3545,15 @@ impl Rooms {
     #[tracing::instrument(skip(self))]
     pub fn lazy_load_reset(
         &self,
-        user_id: &Box<UserId>,
-        device_id: &Box<DeviceId>,
-        room_id: &Box<RoomId>,
+        user_id: &UserId,
+        device_id: &DeviceId,
+        room_id: &RoomId,
     ) -> Result<()> {
         let mut prefix = user_id.as_bytes().to_vec();
         prefix.push(0xff);
-        prefix.extend_from_slice(&device_id.as_bytes());
+        prefix.extend_from_slice(device_id.as_bytes());
         prefix.push(0xff);
-        prefix.extend_from_slice(&room_id.as_bytes());
+        prefix.extend_from_slice(room_id.as_bytes());
         prefix.push(0xff);
 
         for (key, _) in self.lazyloadedids.scan_prefix(prefix) {
diff --git a/src/server_server.rs b/src/server_server.rs
index 54ae0251..9129951b 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -1938,7 +1938,7 @@ pub(crate) fn fetch_and_handle_outliers<'a>(
                 match handle_outlier_pdu(
                     origin,
                     create_event,
-                    &next_id,
+                    next_id,
                     room_id,
                     value.clone(),
                     db,
@@ -2358,7 +2358,7 @@ pub fn get_event_route(
     let room_id = <&RoomId>::try_from(room_id_str)
         .map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
 
-    if !db.rooms.server_in_room(sender_servername, &room_id)? {
+    if !db.rooms.server_in_room(sender_servername, room_id)? {
         return Err(Error::BadRequest(
             ErrorKind::Forbidden,
             "Server is not in room",
@@ -2821,7 +2821,7 @@ async fn create_join_event(
         ));
     }
 
-    acl_check(sender_servername, room_id, &db)?;
+    acl_check(sender_servername, room_id, db)?;
 
     // We need to return the state prior to joining, let's keep a reference to that here
     let shortstatehash = db

From 6e322716caf6f9181bf21444b552bf05d5f5a774 Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Thu, 20 Jan 2022 12:29:10 +0100
Subject: [PATCH 12/16] Delete rust-toolchain file

---
 rust-toolchain | 1 -
 1 file changed, 1 deletion(-)
 delete mode 100644 rust-toolchain

diff --git a/rust-toolchain b/rust-toolchain
deleted file mode 100644
index 74df8b16..00000000
--- a/rust-toolchain
+++ /dev/null
@@ -1 +0,0 @@
-1.53

From 5afb27a5a9ae887dea042e3ca9f0ecef98feff47 Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Thu, 20 Jan 2022 12:29:24 +0100
Subject: [PATCH 13/16] Use latest stable for Docker image

---
 Dockerfile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index 5812fdf9..b629690d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # syntax=docker/dockerfile:1
-FROM docker.io/rust:1.53-alpine AS builder
+FROM docker.io/rust:1.58-alpine AS builder
 WORKDIR /usr/src/conduit
 
 # Install required packages to build Conduit and it's dependencies
@@ -38,7 +38,7 @@ FROM docker.io/alpine:3.15.0 AS runner
 # You still need to map the port when using the docker command or docker-compose.
 EXPOSE 6167
 
-# Note from @jfowl: I would like to remove this in the future and just have the Docker version be configured with envs. 
+# Note from @jfowl: I would like to remove this in the future and just have the Docker version be configured with envs.
 ENV CONDUIT_CONFIG="/srv/conduit/conduit.toml"
 
 # Conduit needs:
@@ -78,4 +78,4 @@ WORKDIR /srv/conduit
 
 # Run Conduit and print backtraces on panics
 ENV RUST_BACKTRACE=1
-ENTRYPOINT [ "/srv/conduit/conduit" ]
\ No newline at end of file
+ENTRYPOINT [ "/srv/conduit/conduit" ]

From ff5fec9e74b4ed12c4dae579344a94f1c1f22f29 Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Thu, 20 Jan 2022 12:29:52 +0100
Subject: [PATCH 14/16] Raise minimum supported Rust version to 1.56

---
 Cargo.toml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Cargo.toml b/Cargo.toml
index 29a090c7..b6a2a2b7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,8 @@ homepage = "https://conduit.rs"
 repository = "https://gitlab.com/famedly/conduit"
 readme = "README.md"
 version = "0.2.0"
-edition = "2018"
+rust-version = "1.56"
+edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 

From 6bb1081b7127a38cdc85614e4250f52b557753c8 Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Thu, 20 Jan 2022 13:13:14 +0100
Subject: [PATCH 15/16] Use BTreeMap::into_values

Stable under new MSRV.
---
 src/database/users.rs | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/database/users.rs b/src/database/users.rs
index c4fcee3d..69a277c6 100644
--- a/src/database/users.rs
+++ b/src/database/users.rs
@@ -531,11 +531,11 @@ impl Users {
         prefix.push(0xff);
 
         // Master key
-        let master_key_map = master_key
+        let mut master_key_ids = master_key
             .deserialize()
             .map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid master key"))?
-            .keys;
-        let mut master_key_ids = master_key_map.values();
+            .keys
+            .into_values();
 
         let master_key_id = master_key_ids.next().ok_or(Error::BadRequest(
             ErrorKind::InvalidParam,
@@ -560,13 +560,14 @@ impl Users {
 
         // Self-signing key
         if let Some(self_signing_key) = self_signing_key {
-            let self_signing_key_map = self_signing_key
+            let mut self_signing_key_ids = self_signing_key
                 .deserialize()
                 .map_err(|_| {
                     Error::BadRequest(ErrorKind::InvalidParam, "Invalid self signing key")
                 })?
-                .keys;
-            let mut self_signing_key_ids = self_signing_key_map.values();
+                .keys
+                .into_values();
+
             let self_signing_key_id = self_signing_key_ids.next().ok_or(Error::BadRequest(
                 ErrorKind::InvalidParam,
                 "Self signing key contained no key.",
@@ -593,13 +594,14 @@ impl Users {
 
         // User-signing key
         if let Some(user_signing_key) = user_signing_key {
-            let user_signing_key_map = user_signing_key
+            let mut user_signing_key_ids = user_signing_key
                 .deserialize()
                 .map_err(|_| {
                     Error::BadRequest(ErrorKind::InvalidParam, "Invalid user signing key")
                 })?
-                .keys;
-            let mut user_signing_key_ids = user_signing_key_map.values();
+                .keys
+                .into_values();
+
             let user_signing_key_id = user_signing_key_ids.next().ok_or(Error::BadRequest(
                 ErrorKind::InvalidParam,
                 "User signing key contained no key.",

From 8d81c1c0722ad2f608adea44d7b4ceb1a8f645ae Mon Sep 17 00:00:00 2001
From: Jonas Platte <jplatte+git@posteo.de>
Date: Thu, 20 Jan 2022 13:23:58 +0100
Subject: [PATCH 16/16] Use MSRV for build CI jobs

The test job will use the latest stable so all stable lints are included.
---
 .gitlab-ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 73a1a928..cdc1d4cb 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,7 +21,7 @@ variables:
     - if: '$CI_COMMIT_BRANCH == "next"'
     - if: "$CI_COMMIT_TAG"
   interruptible: true
-  image: "rust:latest"
+  image: "rust:1.56"
   tags: ["docker"]
   variables:
     CARGO_PROFILE_RELEASE_LTO: "true"