Skip to content

Commit

Permalink
Store decompressed BlsPublicKeys in IndexedDB
Browse files Browse the repository at this point in the history
  • Loading branch information
fiaxh committed Nov 28, 2024
1 parent eca46dc commit 6796f04
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 14 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions web-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ workspace = true
crate-type = ["cdylib"]

[dependencies]
ark-serialize = "0.4"
futures = { workspace = true }
gloo-timers = { version = "0.3", features = ["futures"] }
hex = "0.4"
js-sys = "0.3"
log = { workspace = true }
rand_core = "0.6.4"
idb = "0.6.4"
serde = "1.0"
serde_json = "1.0"
serde-wasm-bindgen = "0.6"
tokio = { version = "1.41", features = ["sync"] }
tsify = { git = "https://github.com/sisou/tsify", branch = "sisou/comments", default-features = false, features = ["js"] }
Expand Down
93 changes: 93 additions & 0 deletions web-client/src/client/bls_cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress};
use idb::{
Database, DatabaseEvent, Error, Factory, IndexParams, KeyPath, ObjectStore, ObjectStoreParams,
TransactionMode,
};
use nimiq_bls::{G2Projective, LazyPublicKey, PublicKey};
use nimiq_serde::{Deserialize, Serialize};
use std::time::{SystemTime, UNIX_EPOCH};

/// Caches decompressed BlsPublicKeys in an IndexedDB
pub(crate) struct BlsCache {
db: Option<Database>,
keys: Vec<LazyPublicKey>,
}

#[derive(Serialize, Deserialize, Debug)]
struct BlsKeyEntry {
compressed_key: String,
public_key: String,
}

impl BlsCache {
pub async fn new() -> Self {
let db = match Database::builder("nimiq_client_cache")
.version(1)
.add_object_store(
ObjectStore::builder("bls_keys")
.key_path(Some(KeyPath::new_single("compressed_key"))),
)
.build()
.await
{
Ok(db) => Some(db),
Err(err) => {
log::warn!("idb: Couldn't create database {}", err);
None
}
};

BlsCache { db, keys: vec![] }
}

/// Add the given keys into IndexedDB
pub async fn add_keys(&self, keys: Vec<LazyPublicKey>) -> Result<(), Error> {
if let Some(db) = &self.db {
let transaction = db.transaction(&["bls_keys"], TransactionMode::ReadWrite)?;
let bls_keys_store = transaction.object_store("bls_keys")?;

for key in keys {
let mut result = Vec::new();
key.uncompress()
.unwrap()
.public_key
.serialize_with_mode(&mut result, Compress::No)
.unwrap();
let public_key = hex::encode(&result);
let compressed_key = hex::encode(key.compressed().serialize_to_vec());

let entry = BlsKeyEntry {
compressed_key,
public_key,
};
let entry_js_value = serde_wasm_bindgen::to_value(&entry).unwrap();
bls_keys_store.put(&entry_js_value, None)?.await?;
}
}
Ok(())
}

/// Fetches all bls keys from the IndexedDB and stores them, which makes the decompressed keys
/// available in other places.
pub async fn init(&mut self) -> Result<(), Error> {
if let Some(db) = &self.db {
let transaction = db.transaction(&["bls_keys"], TransactionMode::ReadOnly)?;
let bls_keys_store = transaction.object_store("bls_keys")?;

let js_keys = bls_keys_store.get_all(None, None)?.await?;

for js_key in &js_keys {
let value: BlsKeyEntry = serde_wasm_bindgen::from_value(js_key.clone()).unwrap();
let public_key = PublicKey::new(
G2Projective::deserialize_uncompressed_unchecked(
&*hex::decode(value.public_key.clone()).unwrap(),
)
.unwrap(),
);
self.keys.push(LazyPublicKey::from(public_key));
}
transaction.await?;
}
Ok(())
}
}
67 changes: 53 additions & 14 deletions web-client/src/client/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ use futures::{
future::{select, Either},
StreamExt,
};
use idb::{
Database, DatabaseEvent, Error, Factory, IndexParams, KeyPath, ObjectStore, ObjectStoreParams,
TransactionMode,
};
use js_sys::{global, Array, Function, JsString};
use log::level_filters::LevelFilter;
use nimiq::client::ConsensusProxy;
Expand All @@ -22,15 +26,17 @@ pub use nimiq::{
},
extras::{panic::initialize_panic_reporting, web_logging::initialize_web_logging},
};
use nimiq_block::Block;
use nimiq_blockchain_interface::{AbstractBlockchain, BlockchainEvent};
use nimiq_bls::LazyPublicKey;
use nimiq_consensus::ConsensusEvent;
use nimiq_hash::Blake2bHash;
use nimiq_network_interface::{
network::{CloseReason, Network, NetworkEvent},
Multiaddr,
};
use nimiq_primitives::policy::Policy;
use nimiq_utils::spawn_local;
use nimiq_utils::{key_rng::SecureGenerate, spawn_local};
use tokio::sync::oneshot;
use tsify::Tsify;
use wasm_bindgen::{prelude::*, JsCast};
Expand All @@ -44,6 +50,7 @@ use crate::{
PlainValidatorType,
},
block::{PlainBlock, PlainBlockType},
bls_cache::BlsCache,
peer_info::{PlainPeerInfo, PlainPeerInfoArrayType},
},
common::{
Expand Down Expand Up @@ -114,6 +121,8 @@ pub struct Client {
/// Map from transaction hash as hex string to oneshot sender.
/// Used to await transaction events in `send_transaction`.
transaction_oneshots: Rc<RefCell<HashMap<String, oneshot::Sender<PlainTransactionDetails>>>>,

bls_cache: Rc<RefCell<BlsCache>>,
}

#[wasm_bindgen]
Expand Down Expand Up @@ -183,6 +192,8 @@ impl Client {
let zkp_component = client.take_zkp_component().unwrap();
spawn_local(zkp_component);

let bls_cache = BlsCache::new().await;

let client = Client {
inner: client,
network_id: from_network_id(web_config.network_id),
Expand All @@ -193,6 +204,7 @@ impl Client {
peer_changed_listeners: Rc::new(RefCell::new(HashMap::with_capacity(1))),
transaction_listeners: Rc::new(RefCell::new(HashMap::new())),
transaction_oneshots: Rc::new(RefCell::new(HashMap::new())),
bls_cache: Rc::new(RefCell::new(bls_cache)),
};

client.setup_offline_online_event_handlers();
Expand All @@ -201,6 +213,10 @@ impl Client {
client.setup_network_events();
client.setup_transaction_events().await;

if let Err(err) = client.bls_cache.borrow_mut().init().await {
log::warn!("Failed loading bls cache {}", err);
}

Ok(client)
}

Expand Down Expand Up @@ -1009,32 +1025,33 @@ impl Client {
let mut blockchain_events = blockchain.read().notifier_as_stream();

let block_listeners = Rc::clone(&self.head_changed_listeners);
let bls_cache = Rc::clone(&self.bls_cache);

spawn_local(async move {
loop {
let (hash, reason, reverted_blocks, adopted_blocks) =
let (hash, reason, reverted_blocks, adopted_blocks_hashs) =
match blockchain_events.next().await {
Some(BlockchainEvent::Extended(hash)) => {
let adopted_blocks = Array::new();
adopted_blocks.push(&hash.to_hex().into());
let mut adopted_blocks = Vec::new();
adopted_blocks.push(hash.clone());

(hash, "extended", Array::new(), adopted_blocks)
}
Some(BlockchainEvent::HistoryAdopted(hash)) => {
let adopted_blocks = Array::new();
adopted_blocks.push(&hash.to_hex().into());
let mut adopted_blocks = Vec::new();
adopted_blocks.push(hash.clone());

(hash, "history-adopted", Array::new(), adopted_blocks)
}
Some(BlockchainEvent::EpochFinalized(hash)) => {
let adopted_blocks = Array::new();
adopted_blocks.push(&hash.to_hex().into());
let mut adopted_blocks = Vec::new();
adopted_blocks.push(hash.clone());

(hash, "epoch-finalized", Array::new(), adopted_blocks)
}
Some(BlockchainEvent::Finalized(hash)) => {
let adopted_blocks = Array::new();
adopted_blocks.push(&hash.to_hex().into());
let mut adopted_blocks = Vec::new();
adopted_blocks.push(hash.clone());

(hash, "finalized", Array::new(), adopted_blocks)
}
Expand All @@ -1046,9 +1063,9 @@ impl Client {
reverted_blocks.push(&h.to_hex().into());
}

let adopted_blocks = Array::new();
let mut adopted_blocks = Vec::new();
for (h, _) in new_chain {
adopted_blocks.push(&h.to_hex().into());
adopted_blocks.push(h);
}

(
Expand All @@ -1059,23 +1076,45 @@ impl Client {
)
}
Some(BlockchainEvent::Stored(block)) => {
(block.hash(), "stored", Array::new(), Array::new())
(block.hash(), "stored", Array::new(), Vec::new())
}
None => {
break;
}
};

let adopted_blocks = Array::new();
for hash in &adopted_blocks_hashs {
adopted_blocks.push(&hash.to_hex().into());
}

let args = Array::new();
args.push(&hash.to_hex().into());
args.push(&reason.into());
args.push(&reverted_blocks);
args.push(&adopted_blocks);
args.push(&adopted_blocks.clone());

let this = JsValue::null();
for listener in block_listeners.borrow().values() {
let _ = listener.apply(&this, &args);
}

// Cache decompressed validator bls keys
for hash in &adopted_blocks_hashs {
if let Ok(Block::Macro(macro_block)) = blockchain.read().get_block(hash, false)
{
if let Some(validators) = macro_block.header.validators {
let bls_keys = validators
.validators
.iter()
.map(|validator| validator.voting_key.clone())
.collect::<Vec<LazyPublicKey>>();
if let Err(err) = bls_cache.borrow_mut().add_keys(bls_keys).await {
log::warn!("Failed caching bls keys {}", err);
}
}
}
}
}
});
}
Expand Down
1 change: 1 addition & 0 deletions web-client/src/client/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod account;
pub mod block;
mod bls_cache;
pub mod lib;
pub mod peer_info;

0 comments on commit 6796f04

Please sign in to comment.