Browse Source

Add client state caching w/ seeding, update UI

Samir Menon 2 years ago
parent
commit
c320045943

+ 1 - 0
client/Cargo.toml

@@ -13,6 +13,7 @@ default = []
 [dependencies]
 spiral-rs = { path = "../spiral-rs" }
 rand = { version = "0.8.5" }
+rand_chacha = "0.3.1"
 wasm-bindgen = "0.2.74"
 
 # The `console_error_panic_hook` crate provides better debugging of panics by

+ 45 - 26
client/src/lib.rs

@@ -1,34 +1,32 @@
 mod utils;
-use rand::{rngs::ThreadRng, thread_rng};
+use std::convert::TryInto;
+
+use rand::{thread_rng, SeedableRng, RngCore};
+use rand_chacha::ChaCha20Rng;
 use spiral_rs::{client::*, discrete_gaussian::*, params::*, util::*};
 use wasm_bindgen::prelude::*;
 
 const UUID_V4_LEN: usize = 36;
 
 // console_log! macro
-// #[wasm_bindgen]
-// extern "C" {
-//     #[wasm_bindgen(js_namespace = console)]
-//     fn log(s: &str);
-// }
-// macro_rules! console_log {
-//     ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
-// }
+#[wasm_bindgen]
+extern "C" {
+    #[wasm_bindgen(js_namespace = console)]
+    fn log(s: &str);
+}
+macro_rules! console_log {
+    ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
+}
 
 // Container class for a static lifetime Client
 // Avoids a lifetime in the return signature of bound Rust functions
 #[wasm_bindgen]
 pub struct WrappedClient {
-    client: Client<'static, ThreadRng>,
+    client: &'static mut Client<'static, ChaCha20Rng>,
 }
 
-// Unsafe global with a static lifetime
-// Accessed unsafely only once, at load / setup
-static mut PARAMS: Params = get_empty_params();
-static mut RNG: Option<ThreadRng> = None;
-
 // Very simply test to ensure random generation is not obviously biased.
-fn dg_seems_okay() {
+fn _dg_seems_okay() {
     let params = get_test_params();
     let mut rng = thread_rng();
     let mut dg = DiscreteGaussian::init(&params, &mut rng);
@@ -48,8 +46,7 @@ fn dg_seems_okay() {
 
 // Initializes a client; can optionally take in a set of parameters
 #[wasm_bindgen]
-pub fn initialize(json_params: Option<String>) -> WrappedClient {
-    dg_seems_okay();
+pub fn initialize(json_params: Option<String>, seed: Box<[u8]>) -> WrappedClient {
     // spiral_rs::ntt::test::ntt_correct();
     let cfg = r#"
         {'n': 2,
@@ -69,21 +66,27 @@ pub fn initialize(json_params: Option<String>) -> WrappedClient {
     if json_params.is_some() {
         cfg = json_params.unwrap();
     }
-    let client;
 
-    // this minimal unsafe operation is need to initialize state
-    unsafe {
-        PARAMS = params_from_json(&cfg);
-        RNG = Some(thread_rng());
-        client = Client::init(&PARAMS, RNG.as_mut().unwrap());
-    }
+    let seed_buf = (*seed).try_into().unwrap();
+
+    let params = Box::leak(Box::new(params_from_json(&cfg)));
+    let rng = Box::leak(Box::new(ChaCha20Rng::from_seed(seed_buf)));
+
+    let client = Box::leak(Box::new(Client::init(params, rng)));
 
     WrappedClient { client }
 }
 
 #[wasm_bindgen]
 pub fn generate_public_parameters(c: &mut WrappedClient) -> Box<[u8]> {
-    c.client.generate_keys().serialize().into_boxed_slice()
+    let res = c.client.generate_keys().serialize().into_boxed_slice();
+
+    // important to re-seed here; only query and public key are deterministic
+    let mut rng = thread_rng();
+    let mut new_seed = [0u8; 32];
+    rng.fill_bytes(&mut new_seed);
+    *c.client.get_rng() = ChaCha20Rng::from_seed(new_seed);
+    res
 }
 
 #[wasm_bindgen]
@@ -100,3 +103,19 @@ pub fn generate_query(c: &mut WrappedClient, id: &str, idx_target: usize) -> Box
 pub fn decode_response(c: &mut WrappedClient, data: Box<[u8]>) -> Box<[u8]> {
     c.client.decode_response(&*data).into_boxed_slice()
 }
+
+#[cfg(test)]
+mod test {
+    use rand::{distributions::Standard, prelude::Distribution};
+
+    use super::*;
+
+    #[test]
+    fn chacha_is_correct() {
+        let mut rng1 = ChaCha20Rng::from_seed([1u8; 32]);
+        let mut rng2 = ChaCha20Rng::from_seed([1u8; 32]);
+        let val1: u64 = Standard.sample(&mut rng1);
+        let val2: u64 = Standard.sample(&mut rng2);
+        assert_eq!(val1, val2);
+    }
+}

+ 180 - 128
client/static/css/loading.css

@@ -3,133 +3,185 @@
  * Copyright 2015 Daniel Cardoso <@DanielCardoso>
  * Licensed under MIT
  */
- .la-ball-clip-rotate,
- .la-ball-clip-rotate > div {
-     position: relative;
-     -webkit-box-sizing: border-box;
-        -moz-box-sizing: border-box;
-             box-sizing: border-box;
- }
- .la-ball-clip-rotate {
-     display: block;
-     font-size: 0;
-     color: #fff;
- }
- .la-ball-clip-rotate.la-dark {
-     color: #333;
- }
- .la-ball-clip-rotate > div {
-     display: inline-block;
-     float: none;
-     background-color: currentColor;
-     border: 0 solid currentColor;
- }
- .la-ball-clip-rotate {
-     width: 32px;
-     height: 32px;
- }
- .la-ball-clip-rotate > div {
-     width: 32px;
-     height: 32px;
-     background: transparent;
-     border-width: 2px;
-     border-bottom-color: transparent;
-     border-radius: 100%;
-     -webkit-animation: ball-clip-rotate 1.0s linear infinite;
-        -moz-animation: ball-clip-rotate 1.0s linear infinite;
-          -o-animation: ball-clip-rotate 1.0s linear infinite;
-             animation: ball-clip-rotate 1.0s linear infinite;
- }
- .la-ball-clip-rotate.la-sm {
-     width: 16px;
-     height: 16px;
- }
- .la-ball-clip-rotate.la-sm > div {
-     width: 16px;
-     height: 16px;
-     border-width: 1px;
- }
- .la-ball-clip-rotate.la-2x {
-     width: 64px;
-     height: 64px;
- }
- .la-ball-clip-rotate.la-2x > div {
-     width: 64px;
-     height: 64px;
-     border-width: 4px;
- }
- .la-ball-clip-rotate.la-3x {
-     width: 96px;
-     height: 96px;
- }
- .la-ball-clip-rotate.la-3x > div {
-     width: 96px;
-     height: 96px;
-     border-width: 6px;
- }
- /*
+.la-ball-clip-rotate,
+.la-ball-clip-rotate>div {
+    position: relative;
+    -webkit-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+}
+
+.la-ball-clip-rotate {
+    display: block;
+    font-size: 0;
+    color: #fff;
+}
+
+.la-ball-clip-rotate.la-dark {
+    color: #333;
+}
+
+.la-ball-clip-rotate>div {
+    display: inline-block;
+    float: none;
+    background-color: currentColor;
+    border: 0 solid currentColor;
+}
+
+.la-ball-clip-rotate {
+    width: 32px;
+    height: 32px;
+}
+
+.la-ball-clip-rotate>div {
+    width: 32px;
+    height: 32px;
+    background: transparent;
+    border-width: 2px;
+    border-bottom-color: transparent;
+    border-radius: 100%;
+    -webkit-animation: ball-clip-rotate 1.0s linear infinite;
+    -moz-animation: ball-clip-rotate 1.0s linear infinite;
+    -o-animation: ball-clip-rotate 1.0s linear infinite;
+    animation: ball-clip-rotate 1.0s linear infinite;
+}
+
+.la-ball-clip-rotate.la-sm {
+    width: 16px;
+    height: 16px;
+}
+
+.la-ball-clip-rotate.la-sm>div {
+    width: 16px;
+    height: 16px;
+    border-width: 1px;
+}
+
+.la-ball-clip-rotate.la-2x {
+    width: 64px;
+    height: 64px;
+}
+
+.la-ball-clip-rotate.la-2x>div {
+    width: 64px;
+    height: 64px;
+    border-width: 4px;
+}
+
+.la-ball-clip-rotate.la-3x {
+    width: 96px;
+    height: 96px;
+}
+
+.la-ball-clip-rotate.la-3x>div {
+    width: 96px;
+    height: 96px;
+    border-width: 6px;
+}
+
+/*
   * Animation
   */
- @-webkit-keyframes ball-clip-rotate {
-     0% {
-         -webkit-transform: rotate(0deg);
-                 transform: rotate(0deg);
-     }
-     50% {
-         -webkit-transform: rotate(180deg);
-                 transform: rotate(180deg);
-     }
-     100% {
-         -webkit-transform: rotate(360deg);
-                 transform: rotate(360deg);
-     }
- }
- @-moz-keyframes ball-clip-rotate {
-     0% {
-         -moz-transform: rotate(0deg);
-              transform: rotate(0deg);
-     }
-     50% {
-         -moz-transform: rotate(180deg);
-              transform: rotate(180deg);
-     }
-     100% {
-         -moz-transform: rotate(360deg);
-              transform: rotate(360deg);
-     }
- }
- @-o-keyframes ball-clip-rotate {
-     0% {
-         -o-transform: rotate(0deg);
-            transform: rotate(0deg);
-     }
-     50% {
-         -o-transform: rotate(180deg);
-            transform: rotate(180deg);
-     }
-     100% {
-         -o-transform: rotate(360deg);
-            transform: rotate(360deg);
-     }
- }
- @keyframes ball-clip-rotate {
-     0% {
-         -webkit-transform: rotate(0deg);
-            -moz-transform: rotate(0deg);
-              -o-transform: rotate(0deg);
-                 transform: rotate(0deg);
-     }
-     50% {
-         -webkit-transform: rotate(180deg);
-            -moz-transform: rotate(180deg);
-              -o-transform: rotate(180deg);
-                 transform: rotate(180deg);
-     }
-     100% {
-         -webkit-transform: rotate(360deg);
-            -moz-transform: rotate(360deg);
-              -o-transform: rotate(360deg);
-                 transform: rotate(360deg);
-     }
- }
- 
+@-webkit-keyframes ball-clip-rotate {
+    0% {
+        -webkit-transform: rotate(0deg);
+        transform: rotate(0deg);
+    }
+
+    50% {
+        -webkit-transform: rotate(180deg);
+        transform: rotate(180deg);
+    }
+
+    100% {
+        -webkit-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}
+
+@-moz-keyframes ball-clip-rotate {
+    0% {
+        -moz-transform: rotate(0deg);
+        transform: rotate(0deg);
+    }
+
+    50% {
+        -moz-transform: rotate(180deg);
+        transform: rotate(180deg);
+    }
+
+    100% {
+        -moz-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}
+
+@-o-keyframes ball-clip-rotate {
+    0% {
+        -o-transform: rotate(0deg);
+        transform: rotate(0deg);
+    }
+
+    50% {
+        -o-transform: rotate(180deg);
+        transform: rotate(180deg);
+    }
+
+    100% {
+        -o-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}
+
+@keyframes ball-clip-rotate {
+    0% {
+        -webkit-transform: rotate(0deg);
+        -moz-transform: rotate(0deg);
+        -o-transform: rotate(0deg);
+        transform: rotate(0deg);
+    }
+
+    50% {
+        -webkit-transform: rotate(180deg);
+        -moz-transform: rotate(180deg);
+        -o-transform: rotate(180deg);
+        transform: rotate(180deg);
+    }
+
+    100% {
+        -webkit-transform: rotate(360deg);
+        -moz-transform: rotate(360deg);
+        -o-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}
+
+:root {
+    --delay: 0;
+}
+
+.progress {
+    position: relative;
+    width: 16px;
+    height: 16px;
+    background: conic-gradient(#333 80%, #fff 80%);
+    border-radius: 50%;
+
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.progress>div {
+    position: absolute;
+
+    height: 14px;
+    width: 14px;
+    background-color: #fff;
+
+    border-radius: 50%;
+}
+
+.off {
+    display: none !important;
+}

+ 11 - 11
client/static/css/style.css

@@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
 .fixedpanel {
   width: 100%;
-  height: 50px;
+  height: 64px;
   position: fixed;
   background-color: #FFF;
   padding: 16px;
@@ -74,8 +74,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 }
 
 .articles {
-  width: 800px;
-  max-width: 1200px;
+  min-width: 400px;
+  max-width: 800px;
   margin-left: 20px;
   flex-grow: 1;
   margin-right: 16px;
@@ -86,8 +86,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 } */
 
 .sidebar {
-  max-width: 500px;
-  padding: 16px;
+  max-width: 400px;
+  padding: 8px;
   font-family: Arial;
   background-color: #EEE;
   position: relative;
@@ -104,7 +104,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 .sidebar-header {
   display: flex;
   justify-content: space-between;
-  height: 48px;
+  height: 32px;
 }
 
 .sidebar-title {
@@ -114,7 +114,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 }
 
 .sidebar-content {
-  padding: 0 16px;
+  padding: 0 16px 16px 16px;
   margin-top: 16px;
 }
 
@@ -146,15 +146,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   transform: scaleY(-1);
 }
 
-@media (max-width: 1440px) {
+@media (max-width: 1080px) {
   .sidebar {
     order: -1;
-    max-width: 900px;
+    max-width: none;
     width: 100%;
   }
 }
 
-@media (max-width: 768px) {
+/* @media (max-width: 768px) {
   .fixedpanel {
     padding: 4px;
   }
@@ -165,7 +165,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     margin: 4px;
   }
 }
-
+ */
 .articles article {
   font-family: sans-serif;
   font-size: 14px;

+ 27 - 17
client/static/index.html

@@ -19,6 +19,7 @@
               <input type="text" class="searchbox" placeholder="Article title" />
               <input id="make_query" type="button" value="&gt;" />
               <div class="loading">
+                <div class="progress off"><div></div></div>
                 <div class="loading-icon hidden la-ball-clip-rotate la-dark la-sm"><div></div></div>
                 <div class="message inprogress"></div>
               </div>
@@ -32,25 +33,34 @@
       <div id="output" class="articles"></div>
 
       <div class="sidebar">
-        <p>
-          This is a page is a demo of the work featured in our paper, <a href="https://eprint.iacr.org/2022/368.pdf">"Spiral: Fast, High-Rate Single-Server PIR via FHE Composition"</a>. 
-          The code for our system is available <a href="https://github.com/menonsamir/spiral-rs">here</a>.
-        </p>
-
-        <p>
-          This demo allows private access to 6 GB (~30%) of English Wikipedia.
-          In theory, even if the server is malicious, it will be unable to learn which articles you request.
-          All article title searches are performed locally, and no images are available.
-        </p>
+        <div class="sidebar-header">
+          <div class="sidebar-title">FAQ</div>
+          <div class="sidebar-collapse-btn"></div>
+        </div>
+        <div class="sidebar-content">
+          <h2>What is this?</h2>
+          <p>
+            This is a page is a demo of the work featured in our paper, <a href="https://eprint.iacr.org/2022/368.pdf">"Spiral: Fast, High-Rate Single-Server PIR via FHE Composition"</a>. 
+            The code for our system is available <a href="https://github.com/menonsamir/spiral-rs">here</a>.
+          </p>
+          <p>
+            This demo allows private access to 6 GB (~30%) of English Wikipedia.
+            In theory, even if the server is malicious, it will be unable to learn which articles you request.
+            All article title searches are performed locally, and no images are available.
+          </p>
 
-        <p>
-          When making your first query, this demo will upload 18 MB of data; each later query requires only 14 KB of upload. The server response to each query is 250 KB.
-        </p>
+          <h2>What costs are associated with running the demo?</h2>
+          <p>
+            When making your first query, this demo will upload 18 MB of data; each later query requires only 14 KB of upload. 
+            The server response to each query is 250 KB.
+          </p>
 
-        <p>
-          This is research-quality software built for demonstration purposes; it is not intended to be 
-          side-channel resistant and has not undergone any kind fo security review. Don't use this code in production.
-        </p>
+          <h2>Should I use this in production?</h2>
+          <p>
+            No! This is research-quality software built for demonstration purposes; it is not intended to be 
+            side-channel resistant and has not undergone any kind fo security review. Don't use this code in production.
+          </p>
+          </div>
       </div>
     </div>
 

File diff suppressed because it is too large
+ 6 - 0
client/static/js/idb.js


+ 170 - 38
client/static/js/main.js

@@ -10,30 +10,64 @@ import './wtf_wikipedia.js';
 import './wtf-plugin-html.js';
 wtf.extend(wtfHtml);
 
-const API_URL = "";
+const API_URL = "/api";
+const CHECK_URL = "/check";
 const SETUP_URL = "/setup";
 const QUERY_URL = "/query";
 
 async function postData(url = '', data = {}, json = false) {
-    const response = await fetch(url, {
-      method: 'POST',
-      mode: 'cors',
-      cache: 'no-store',
-      credentials: 'omit',
-      headers: { 
-          'Content-Type': 'application/octet-stream',
-          'Content-Length': data.length
-      },
-      redirect: 'follow',
-      referrerPolicy: 'no-referrer',
-      body: data
+    // const response = await fetch(url, {
+    //   method: 'POST',
+    //   mode: 'cors',
+    //   cache: 'no-store',
+    //   credentials: 'omit',
+    //   headers: { 
+    //       'Content-Type': 'application/octet-stream',
+    //       'Content-Length': data.length
+    //   },
+    //   redirect: 'follow',
+    //   referrerPolicy: 'no-referrer',
+    //   body: data
+    // });
+    // if (json) {
+    //     return response.json();
+    // } else {
+    //     let data = await response.arrayBuffer();
+    //     return new Uint8Array(data);
+    // }
+
+    // Can't use Fetch API here since it lacks progress indication
+    const xhr = new XMLHttpRequest();
+    xhr.responseType = json ? 'json' : 'arraybuffer';
+    return await new Promise((resolve, reject) => {
+        xhr.upload.addEventListener("progress", (event) => {
+            if (event.lengthComputable) {
+                setProgress(Math.round(event.loaded / event.total * 100))
+            }
+        });
+        xhr.addEventListener("loadend", () => {
+            resolve(xhr.readyState === 4 && xhr.status === 200);
+        });
+        xhr.onload = function () {
+            if (xhr.status >= 200 && xhr.status < 300) {
+                resolve(xhr.response);
+            } else {
+                reject({
+                    status: xhr.status,
+                    statusText: xhr.statusText
+                });
+            }
+        };
+        xhr.onerror = function () {
+            reject({
+                status: xhr.status,
+                statusText: xhr.statusText
+            });
+        };
+        xhr.open("POST", url, true);
+        xhr.setRequestHeader("Content-Type", "application/octet-stream");
+        xhr.send(new Blob([data.buffer]));
     });
-    if (json) {
-        return response.json();
-    } else {
-        let data = await response.arrayBuffer();
-        return new Uint8Array(data);
-    }
 }
 
 async function getData(url = '', json = false) {
@@ -53,6 +87,7 @@ async function getData(url = '', json = false) {
 }
 
 const api = {
+    check: async (uuid) => getData(API_URL + CHECK_URL + "?uuid="+uuid, true),
     setup: async (data) => postData(API_URL + SETUP_URL, data, true),
     query: async (data) => postData(API_URL + QUERY_URL, data, false)
 }
@@ -162,10 +197,17 @@ function followRedirects(title) {
     }
 }
 
-function startLoading(message) {
+function startLoading(message, hasProgress) {
     window.loading = true;
     window.started_loading = Date.now();
-    document.querySelector(".loading-icon").classList.remove("hidden");
+    if (hasProgress) {
+        document.querySelector(".progress").classList.remove("off");
+        document.querySelector(".loading-icon").classList.add("off");
+    } else {
+        document.querySelector(".progress").classList.add("off");
+        document.querySelector(".loading-icon").classList.remove("off");
+        document.querySelector(".loading-icon").classList.remove("hidden");
+    }
     document.querySelector(".loading .message").innerHTML = message+"...";
     document.querySelector(".loading .message").classList.add("inprogress");
 }
@@ -207,22 +249,94 @@ function enableLinks(element) {
     })
 }
 
-async function query(targetIdx, title) {    
+const DB_NAME = 'spiralKey';
+const KEY_SIZE = 32;
+const MAX_VALID_TIME = 604800000; // 1 week
+
+async function arrayBufferToBase64(data) {
+    const base64url = await new Promise((r) => {
+        const reader = new FileReader()
+        reader.onload = () => r(reader.result)
+        reader.readAsDataURL(new Blob([data]))
+    })
+    return base64url.split(",", 2)[1]
+}
+
+function base64ToArrayBuffer(str) {
+    return Uint8Array.from(atob(str), c => c.charCodeAt(0));
+}
+
+async function storeState(key, uuid) {
+    console.log(key);
+    let dataToStore = {
+        "key": await arrayBufferToBase64(key),
+        "uuid": uuid,
+        "createdAt": Date.now()
+    }
+    window.localStorage[DB_NAME] = JSON.stringify(dataToStore);
+}
+
+function retrieveState() {
+    if (!window.localStorage || !window.localStorage[DB_NAME]) return false;
+    let state = JSON.parse(window.localStorage[DB_NAME]);
+    state["key"] = base64ToArrayBuffer(state["key"]);
+    return state;
+}
+
+function setStateFromKey(key) {
+    console.log("Initializing...");
+    window.key = key;
+    window.client = initialize(undefined, key);
+    console.log("done");
+    console.log("Generating public parameters...");
+    window.publicParameters = generate_public_parameters(window.client);
+    console.log(`done (${publicParameters.length} bytes)`);
+}
+
+async function isStateValid(state) {
+    console.log("Checking if cached state is still valid")
+    if (Date.now() - state.createdAt > MAX_VALID_TIME) return false;
+        
+    let isValidResponse = await api.check(state.uuid);
+    let isValid = isValidResponse.is_valid;
+    if (!isValid) return false;
+
+    return true;
+}
+
+async function setUpClient() {
+    let state = retrieveState();
+    if (state && await isStateValid(state)) {
+        console.log("Loading previous client state")
+        setStateFromKey(state.key);
+        window.id = state.uuid;
+        return true;
+    } else {
+        console.log("No state stored, generating new client state")
+        let key = new Uint8Array(KEY_SIZE);
+        self.crypto.getRandomValues(key);
+        setStateFromKey(key);
+        return false;
+    }
+}
+
+async function uploadState() {
+    startLoading("Uploading setup data", true);
+    console.log("Sending public parameters...");
+    let setup_resp = await api.setup(window.publicParameters);
+    console.log("sent.");
+    let id = setup_resp["id"];
+    stopLoading("Uploading setup data");
+    await storeState(window.key, id);
+    return id;
+}
+
+async function query(targetIdx, title) {
     if (!window.hasSetUp) {
-        startLoading("Uploading setup data");
-        console.log("Initializing...");
-        window.client = initialize();
-        console.log("done");
-        console.log("Generating public parameters...");
-        let publicParameters = generate_public_parameters(window.client);
-        console.log(`done (${publicParameters.length} bytes)`);
-        console.log("Sending public parameters...");
-        let setup_resp = await api.setup(new Blob([publicParameters.buffer]));
-        console.log("sent.");
-        console.log(setup_resp);
-        window.id = setup_resp["id"];
+        let id = await uploadState();
+        if (!id) return false;
         window.hasSetUp = true;
-        stopLoading("Uploading setup data");
+        window.id = id;
     }
 
     startLoading("Loading article");
@@ -231,11 +345,10 @@ async function query(targetIdx, title) {
     console.log(`done (${query.length} bytes)`);
 
     console.log("Sending query...");
-    let response = await api.query(query);
+    let response = new Uint8Array(await api.query(query));
     console.log("sent.");
 
     console.log(`done, got (${response.length} bytes)`);
-    console.log(response);
 
     console.log("Decoding result...");
     let result = decode_response(window.client, response)
@@ -268,10 +381,14 @@ async function run() {
 
     let makeQueryBtn = document.querySelector('#make_query');
     let searchBox = document.querySelector(".searchbox");
+    document.querySelector(".sidebar-collapse-btn").onclick = () => {
+        document.querySelector(".sidebar").classList.toggle("collapsed");
+    }
 
     startLoading("Loading article titles");
     let title_index_p = getData("data/enwiki-20220320-index.json", true);
     let redirect_backlinks_p = getData("data/redirects-old.json", true);
+    let setupClientResult = setUpClient();
 
     window.title_index = await title_index_p;
     let keys = Object.keys(window.title_index);
@@ -290,6 +407,8 @@ async function run() {
             window.redirects[redirect_srcs[j].toLowerCase()] = redirect_dest;
         }
     }
+
+    window.hasSetUp = await setupClientResult;
     stopLoading("Loading article titles");
 
     searchBox.addEventListener('input', (e) => {
@@ -313,4 +432,17 @@ async function run() {
         makeQueryBtn.disabled = false;
     }
 }
-run();
+run();
+
+function setProgress(progress) {
+    document.querySelector(".progress").style.background =
+        "conic-gradient(#333 " +
+        progress +
+        "%,#fff " +
+        progress +
+        "%)";
+
+    // document.getElementById("middle-circle").innerHTML =
+    //     progress.toString() + "%";
+}
+window.setProgress = setProgress;

+ 116 - 224
spiral-rs/Cargo.lock

@@ -16,7 +16,7 @@ dependencies = [
  "memchr",
  "pin-project-lite",
  "tokio",
- "tokio-util 0.7.1",
+ "tokio-util",
 ]
 
 [[package]]
@@ -34,29 +34,6 @@ dependencies = [
  "smallvec",
 ]
 
-[[package]]
-name = "actix-files"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d81bde9a79336aa51ebed236e91fc1a0528ff67cfdf4f68ca4c61ede9fd26fb5"
-dependencies = [
- "actix-http",
- "actix-service",
- "actix-utils",
- "actix-web",
- "askama_escape",
- "bitflags",
- "bytes",
- "derive_more",
- "futures-core",
- "http-range",
- "log",
- "mime",
- "mime_guess",
- "percent-encoding",
- "pin-project-lite",
-]
-
 [[package]]
 name = "actix-http"
 version = "3.0.4"
@@ -66,7 +43,6 @@ dependencies = [
  "actix-codec",
  "actix-rt",
  "actix-service",
- "actix-tls",
  "actix-utils",
  "ahash",
  "base64",
@@ -158,24 +134,6 @@ dependencies = [
  "pin-project-lite",
 ]
 
-[[package]]
-name = "actix-tls"
-version = "3.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297"
-dependencies = [
- "actix-codec",
- "actix-rt",
- "actix-service",
- "actix-utils",
- "futures-core",
- "log",
- "openssl",
- "pin-project-lite",
- "tokio-openssl",
- "tokio-util 0.7.1",
-]
-
 [[package]]
 name = "actix-utils"
 version = "3.0.0"
@@ -199,7 +157,6 @@ dependencies = [
  "actix-rt",
  "actix-server",
  "actix-service",
- "actix-tls",
  "actix-utils",
  "actix-web-codegen",
  "ahash",
@@ -298,12 +255,6 @@ dependencies = [
  "nodrop",
 ]
 
-[[package]]
-name = "askama_escape"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
-
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -323,15 +274,15 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "backtrace"
-version = "0.3.64"
+version = "0.3.65"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f"
+checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
 dependencies = [
  "addr2line",
  "cc",
  "cfg-if",
  "libc",
- "miniz_oxide 0.4.4",
+ "miniz_oxide",
  "object",
  "rustc-demangle",
 ]
@@ -344,9 +295,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
 
 [[package]]
 name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "block-buffer"
@@ -550,9 +501,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.5.2"
+version = "0.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
+checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
 dependencies = [
  "cfg-if",
  "crossbeam-utils",
@@ -571,10 +522,11 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-epoch"
-version = "0.9.7"
+version = "0.9.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
+checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
 dependencies = [
+ "autocfg",
  "cfg-if",
  "crossbeam-utils",
  "lazy_static",
@@ -584,9 +536,9 @@ dependencies = [
 
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.7"
+version = "0.8.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
+checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
 dependencies = [
  "cfg-if",
  "lazy_static",
@@ -664,9 +616,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
 [[package]]
 name = "encoding_rs"
-version = "0.8.30"
+version = "0.8.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
+checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
 dependencies = [
  "cfg-if",
 ]
@@ -695,7 +647,7 @@ dependencies = [
  "cfg-if",
  "crc32fast",
  "libc",
- "miniz_oxide 0.5.1",
+ "miniz_oxide",
 ]
 
 [[package]]
@@ -849,9 +801,9 @@ checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
 
 [[package]]
 name = "h2"
-version = "0.3.12"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b"
+checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57"
 dependencies = [
  "bytes",
  "fnv",
@@ -862,7 +814,7 @@ dependencies = [
  "indexmap",
  "slab",
  "tokio",
- "tokio-util 0.6.9",
+ "tokio-util",
  "tracing",
 ]
 
@@ -889,9 +841,9 @@ dependencies = [
 
 [[package]]
 name = "http"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03"
+checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb"
 dependencies = [
  "bytes",
  "fnv",
@@ -909,17 +861,11 @@ dependencies = [
  "pin-project-lite",
 ]
 
-[[package]]
-name = "http-range"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
-
 [[package]]
 name = "httparse"
-version = "1.6.0"
+version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4"
+checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c"
 
 [[package]]
 name = "httpdate"
@@ -977,9 +923,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.8.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
+checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
 dependencies = [
  "autocfg",
  "hashbrown",
@@ -1014,9 +960,9 @@ dependencies = [
 
 [[package]]
 name = "ipnet"
-version = "2.4.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c"
+checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
 
 [[package]]
 name = "itertools"
@@ -1050,9 +996,9 @@ dependencies = [
 
 [[package]]
 name = "js-sys"
-version = "0.3.56"
+version = "0.3.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
+checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -1071,9 +1017,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.122"
+version = "0.2.125"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
+checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
 
 [[package]]
 name = "local-channel"
@@ -1105,9 +1051,9 @@ dependencies = [
 
 [[package]]
 name = "log"
-version = "0.4.14"
+version = "0.4.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
 dependencies = [
  "cfg-if",
 ]
@@ -1120,9 +1066,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
 
 [[package]]
 name = "memchr"
-version = "2.4.1"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "memmap2"
@@ -1148,26 +1094,6 @@ version = "0.3.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
 
-[[package]]
-name = "mime_guess"
-version = "2.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
-dependencies = [
- "mime",
- "unicase",
-]
-
-[[package]]
-name = "miniz_oxide"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
-dependencies = [
- "adler",
- "autocfg",
-]
-
 [[package]]
 name = "miniz_oxide"
 version = "0.5.1"
@@ -1202,9 +1128,9 @@ dependencies = [
 
 [[package]]
 name = "native-tls"
-version = "0.2.9"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09bf6f32a3afefd0b587ee42ed19acd945c6d1f3b5424040f50b2f24ab16be77"
+checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9"
 dependencies = [
  "lazy_static",
  "libc",
@@ -1220,15 +1146,14 @@ dependencies = [
 
 [[package]]
 name = "nix"
-version = "0.20.2"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5e06129fb611568ef4e868c14b326274959aa70ff7776e9d55323531c374945"
+checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
 dependencies = [
  "bitflags",
  "cc",
  "cfg-if",
  "libc",
- "memoffset",
 ]
 
 [[package]]
@@ -1286,9 +1211,9 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.27.1"
+version = "0.28.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
+checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456"
 dependencies = [
  "memchr",
 ]
@@ -1356,7 +1281,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
 dependencies = [
  "lock_api",
- "parking_lot_core 0.9.2",
+ "parking_lot_core 0.9.3",
 ]
 
 [[package]]
@@ -1375,9 +1300,9 @@ dependencies = [
 
 [[package]]
 name = "parking_lot_core"
-version = "0.9.2"
+version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37"
+checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
 dependencies = [
  "cfg-if",
  "libc",
@@ -1400,9 +1325,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 [[package]]
 name = "pin-project-lite"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
 
 [[package]]
 name = "pin-utils"
@@ -1412,9 +1337,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
 [[package]]
 name = "pkg-config"
-version = "0.3.24"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
+checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
 
 [[package]]
 name = "plotters"
@@ -1471,9 +1396,9 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.36"
+version = "1.0.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
+checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
 dependencies = [
  "unicode-xid",
 ]
@@ -1489,9 +1414,9 @@ dependencies = [
 
 [[package]]
 name = "quote"
-version = "1.0.15"
+version = "1.0.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
 dependencies = [
  "proc-macro2",
 ]
@@ -1552,18 +1477,18 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.2.12"
+version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0"
+checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
 dependencies = [
  "bitflags",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.4"
+version = "1.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1684,9 +1609,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 
 [[package]]
 name = "security-framework"
-version = "2.3.1"
+version = "2.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467"
+checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
 dependencies = [
  "bitflags",
  "core-foundation",
@@ -1707,15 +1632,18 @@ dependencies = [
 
 [[package]]
 name = "semver"
-version = "1.0.6"
+version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d"
+checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4"
 
 [[package]]
 name = "serde"
-version = "1.0.136"
+version = "1.0.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
+checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
+dependencies = [
+ "serde_derive",
+]
 
 [[package]]
 name = "serde_cbor"
@@ -1729,9 +1657,9 @@ dependencies = [
 
 [[package]]
 name = "serde_derive"
-version = "1.0.136"
+version = "1.0.137"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
+checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1740,9 +1668,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.79"
+version = "1.0.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
+checksum = "f972498cf015f7c0746cac89ebe1d6ef10c293b94175a243a2d9442c163d9944"
 dependencies = [
  "itoa 1.0.1",
  "ryu",
@@ -1783,9 +1711,9 @@ dependencies = [
 
 [[package]]
 name = "slab"
-version = "0.4.5"
+version = "0.4.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
+checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
 
 [[package]]
 name = "smallvec"
@@ -1808,7 +1736,6 @@ name = "spiral-rs"
 version = "0.1.0"
 dependencies = [
  "actix-cors",
- "actix-files",
  "actix-http",
  "actix-server",
  "actix-service",
@@ -1816,11 +1743,11 @@ dependencies = [
  "criterion",
  "futures",
  "getrandom",
- "openssl",
  "pprof",
  "rand",
  "rayon",
  "reqwest",
+ "serde",
  "serde_json",
  "uuid 1.0.0",
 ]
@@ -1862,9 +1789,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "1.0.86"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
+checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1896,18 +1823,18 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.30"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.30"
+version = "1.0.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1944,9 +1871,9 @@ dependencies = [
 
 [[package]]
 name = "tinyvec"
-version = "1.5.1"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
 dependencies = [
  "tinyvec_macros",
 ]
@@ -1959,9 +1886,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "1.17.0"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
+checksum = "0f48b6d60512a392e34dbf7fd456249fd2de3c83669ab642e021903f4015185b"
 dependencies = [
  "bytes",
  "libc",
@@ -1986,32 +1913,6 @@ dependencies = [
  "tokio",
 ]
 
-[[package]]
-name = "tokio-openssl"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08f9ffb7809f1b20c1b398d92acf4cc719874b3b2b2d9ea2f09b4a80350878a"
-dependencies = [
- "futures-util",
- "openssl",
- "openssl-sys",
- "tokio",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.6.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "log",
- "pin-project-lite",
- "tokio",
-]
-
 [[package]]
 name = "tokio-util"
 version = "0.7.1"
@@ -2034,9 +1935,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6"
 
 [[package]]
 name = "tracing"
-version = "0.1.32"
+version = "0.1.34"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f"
+checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09"
 dependencies = [
  "cfg-if",
  "log",
@@ -2047,9 +1948,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-attributes"
-version = "0.1.20"
+version = "0.1.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b"
+checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2058,9 +1959,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-core"
-version = "0.1.23"
+version = "0.1.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c"
+checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f"
 dependencies = [
  "lazy_static",
 ]
@@ -2077,20 +1978,11 @@ version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
 
-[[package]]
-name = "unicase"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
-dependencies = [
- "version_check",
-]
-
 [[package]]
 name = "unicode-bidi"
-version = "0.3.7"
+version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
+checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
 
 [[package]]
 name = "unicode-normalization"
@@ -2187,9 +2079,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.79"
+version = "0.2.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
+checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -2197,9 +2089,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.79"
+version = "0.2.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
+checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
 dependencies = [
  "bumpalo",
  "lazy_static",
@@ -2212,9 +2104,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.29"
+version = "0.4.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
+checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -2224,9 +2116,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.79"
+version = "0.2.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
+checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -2234,9 +2126,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.79"
+version = "0.2.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
+checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2247,15 +2139,15 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.79"
+version = "0.2.80"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
+checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
 
 [[package]]
 name = "web-sys"
-version = "0.3.56"
+version = "0.3.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
+checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -2294,9 +2186,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
 name = "windows-sys"
-version = "0.34.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825"
+checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
 dependencies = [
  "windows_aarch64_msvc",
  "windows_i686_gnu",
@@ -2307,33 +2199,33 @@ dependencies = [
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.34.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
+checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.34.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
+checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.34.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
+checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.34.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
+checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.34.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
+checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
 
 [[package]]
 name = "winreg"

+ 12 - 5
spiral-rs/Cargo.toml

@@ -4,7 +4,13 @@ version = "0.1.0"
 edition = "2021"
 
 [features]
-server = ["actix-web", "actix-cors", "actix-files", "actix-server", "actix-http", "actix-service", "openssl", "futures", "uuid"]
+default = []
+client = ["reqwest"]
+server = ["actix-web", "actix-cors", "actix-server", "actix-http", "actix-service", "serde", "futures", "uuid"]
+
+[[bin]]
+name = "client"
+required-features = ["client"]
 
 [[bin]]
 name = "server"
@@ -13,16 +19,17 @@ required-features = ["server"]
 [dependencies]
 getrandom = { features = ["js"], version = "0.2.6" }
 rand = { version = "0.8.5", features = ["small_rng"] }
-reqwest = { version = "0.11", features = ["blocking"] }
 serde_json = "1.0"
 rayon = "1.5.2"
-actix-web = { version = "4.0.1", features = ["openssl"], optional = true }
+
+reqwest = { version = "0.11", features = ["blocking"], optional = true }
+
+serde = { version = "1.0", features = ["derive"], optional = true }
+actix-web = { version = "4.0.1", optional = true }
 actix-cors = { version = "0.6.1", optional = true }
-actix-files = { version = "0.6.0", optional = true }
 actix-server = { version = "2.1.1", optional = true }
 actix-http = { version = "3.0.4", optional = true }
 actix-service = { version = "2.0.2", optional = true }
-openssl = { version = "0.10", features = ["v110"], optional = true }
 futures = { version = "0.3", optional = true }
 uuid = { version = "1.0.0", features = ["v4"], optional = true }
 

+ 37 - 39
spiral-rs/src/bin/server.rs

@@ -5,29 +5,24 @@ use spiral_rs::params::*;
 use spiral_rs::server::*;
 use spiral_rs::util::*;
 use std::collections::HashMap;
+use std::collections::VecDeque;
 use std::env;
 use std::fs::File;
 use std::sync::Mutex;
 
 use actix_cors::Cors;
-use actix_files as fs;
 use actix_http::HttpServiceBuilder;
-use actix_server::{Server, ServerBuilder};
+use actix_server::Server;
 use actix_service::map_config;
-use actix_service::Service;
 use actix_web::error::PayloadError;
-use actix_web::{get, http, middleware, post, web, App, HttpServer};
-use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
-
-const CERT_FNAME: &str = "/etc/letsencrypt/live/spiralwiki.com/fullchain.pem";
-const KEY_FNAME: &str = "/etc/letsencrypt/live/spiralwiki.com/privkey.pem";
-
-const NO_CACHE_PATHS: [&str; 2] = ["/setup", "/query"];
+use actix_web::{get, http, middleware, post, web, App};
+use serde::Deserialize;
+const PUB_PARAMS_MAX: usize = 250;
 
 struct ServerState<'a> {
     params: &'a Params,
     db: AlignedMemory64,
-    pub_params_map: Mutex<HashMap<String, PublicParameters<'a>>>,
+    pub_params_map: Mutex<(VecDeque<String>, HashMap<String, PublicParameters<'a>>)>,
 }
 
 async fn get_request_bytes(
@@ -63,20 +58,43 @@ async fn index<'a>(data: web::Data<ServerState<'a>>) -> String {
     format!("Hello {} {}!", data.params.poly_len, data.db.as_slice()[5])
 }
 
+#[derive(Deserialize)]
+pub struct CheckUuid {
+    uuid: String,
+}
+
+#[get("/check")]
+async fn check<'a>(
+    web::Query(query_params): web::Query<CheckUuid>,
+    data: web::Data<ServerState<'a>>,
+) -> Result<String, http::Error> {
+    let pub_params_map = data.pub_params_map.lock().map_err(other_io_err)?;
+    let has_uuid = pub_params_map.1.contains_key(&query_params.uuid);
+    Ok(format!(
+        "{{\"uuid\":\"{}\", \"is_valid\":{}}}",
+        query_params.uuid, has_uuid
+    ))
+}
+
 #[post("/setup")]
 async fn setup<'a>(
     body: web::Bytes,
     data: web::Data<ServerState<'a>>,
 ) -> Result<String, http::Error> {
-    println!("/setup");
-
     // Parse the request
     let pub_params = PublicParameters::deserialize(data.params, &body);
 
     // Generate a UUID and store it
     let uuid = uuid::Uuid::new_v4();
     let mut pub_params_map = data.pub_params_map.lock().map_err(other_io_err)?;
-    pub_params_map.insert(uuid.to_string(), pub_params);
+    pub_params_map.0.push_back(uuid.to_string());
+    pub_params_map.1.insert(uuid.to_string(), pub_params);
+
+    // If too many public parameters, remove by LRU
+    if pub_params_map.1.len() > PUB_PARAMS_MAX {
+        let lru_uuid_str = pub_params_map.0.pop_front().ok_or(get_other_io_err())?;
+        pub_params_map.1.remove(&lru_uuid_str);
+    }
 
     Ok(format!("{{\"id\":\"{}\"}}", uuid.to_string()))
 }
@@ -88,8 +106,6 @@ async fn query<'a>(
     body: web::Payload,
     data: web::Data<ServerState<'a>>,
 ) -> Result<Vec<u8>, http::Error> {
-    println!("/query");
-
     // Parse the UUID
     let request_bytes =
         get_request_bytes(body, UUID_V4_STR_BYTES + data.params.query_bytes()).await?;
@@ -101,6 +117,7 @@ async fn query<'a>(
     // Look up UUID and get public parameters
     let pub_params_map = data.pub_params_map.lock().map_err(other_io_err)?;
     let pub_params = pub_params_map
+        .1
         .get(&uuid.to_string())
         .ok_or(get_not_found_err())?;
 
@@ -141,7 +158,7 @@ async fn main() -> std::io::Result<()> {
     let server_state = ServerState {
         params: params,
         db: db,
-        pub_params_map: Mutex::new(HashMap::new()),
+        pub_params_map: Mutex::new((VecDeque::new(), HashMap::new())),
     };
     let state = web::Data::new(server_state);
 
@@ -163,35 +180,16 @@ async fn main() -> std::io::Result<()> {
             .app_data(web::PayloadConfig::new(1 << 25))
             .service(setup)
             .service(query)
-            .service(fs::Files::new("/", "../client/static").index_file("index.html"))
-            .wrap_fn(|req, srv | {
-                let should_cache = !NO_CACHE_PATHS.contains(&req.path());
-                let fut = srv.call(req);
-                async move {
-                    let mut res = fut.await?;
-                    if should_cache {
-                        res.headers_mut()
-                            .insert(http::header::CACHE_CONTROL, http::header::HeaderValue::from_static("private, max-age=31536000"));
-                    }
-                    Ok(res)
-                }
-            })
+            .service(check)
     };
 
     Server::build()
-        .bind("http/1", "0.0.0.0:443", move || {
-            let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
-            builder
-                .set_private_key_file(KEY_FNAME, SslFiletype::PEM)
-                .unwrap();
-            builder.set_certificate_chain_file(CERT_FNAME).unwrap();
-            builder.set_alpn_protos(b"\x08http/1.1").unwrap();
-
+        .bind("http/1", "localhost:8088", move || {
             HttpServiceBuilder::default()
                 .h1(map_config(app_builder(), |_| {
                     actix_web::dev::AppConfig::default()
                 }))
-                .openssl(builder.build())
+                .tcp()
         })?
         .run()
         .await

Some files were not shown because too many files changed in this diff