Skip to content

Commit

Permalink
feat: Announcing immutable replicas
Browse files Browse the repository at this point in the history
  • Loading branch information
emmyoh committed Nov 25, 2024
1 parent bbf5d83 commit ccea806
Show file tree
Hide file tree
Showing 8 changed files with 534 additions and 245 deletions.
39 changes: 39 additions & 0 deletions src/database/core.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use super::dht::*;
use super::posts::*;
use super::users::*;
use crate::fs::FS_PATH;
use miette::IntoDiagnostic;
use native_db::*;
use std::{path::PathBuf, sync::LazyLock};

pub(crate) static DATABASE_PATH: LazyLock<PathBuf> =
LazyLock::new(|| PathBuf::from(FS_PATH).join("OKU_FS_DATABASE"));
/// An Oku node's database.
pub static DATABASE: LazyLock<OkuDatabase> = LazyLock::new(|| OkuDatabase::new().unwrap());
pub(crate) static MODELS: LazyLock<Models> = LazyLock::new(|| {
let mut models = Models::new();
models.define::<OkuUser>().unwrap();
models.define::<OkuPost>().unwrap();
models.define::<ReplicaAnnouncement>().unwrap();
models
});

/// The database used by Oku's protocol.
pub struct OkuDatabase {
pub(crate) database: Database<'static>,
}

impl OkuDatabase {
/// Open an existing Oku database, or create one if it does not exist.
///
/// # Returns
///
/// An Oku database.
pub fn new() -> miette::Result<Self> {
Ok(Self {
database: native_db::Builder::new()
.create(&MODELS, &*DATABASE_PATH)
.into_diagnostic()?,
})
}
}
134 changes: 134 additions & 0 deletions src/database/dht.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use super::core::*;
use miette::IntoDiagnostic;
use native_db::*;
use native_model::{native_model, Model};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, Clone)]
#[native_model(id = 3, version = 1)]
#[native_db(
primary_key(primary_key -> (Vec<u8>, Vec<u8>))
)]
/// A record of a replica announcement on the DHT.
pub struct ReplicaAnnouncement {
/// The public key of the announcement.
#[primary_key]
pub key: Vec<u8>,
/// The signature of the announcement.
pub signature: Vec<u8>,
}

impl OkuDatabase {
/// Insert or update a replica announcement record.
///
/// # Arguments
///
/// * `announcement` - A replica announcement record to upsert.
///
/// # Returns
///
/// The previous record of the announcement, if one existed.
pub fn upsert_announcement(
&self,
announcement: ReplicaAnnouncement,
) -> miette::Result<Option<ReplicaAnnouncement>> {
let rw = self.database.rw_transaction().into_diagnostic()?;
let old_value: Option<ReplicaAnnouncement> = rw.upsert(announcement).into_diagnostic()?;
rw.commit().into_diagnostic()?;
Ok(old_value)
}

/// Insert or update multiple replica announcement records.
///
/// # Arguments
///
/// * `announcements` - A list of replica announcement records to upsert.
///
/// # Returns
///
/// A list containing the previous record of each announcement, if one existed.
pub fn upsert_announcements(
&self,
announcements: Vec<ReplicaAnnouncement>,
) -> miette::Result<Vec<Option<ReplicaAnnouncement>>> {
let rw = self.database.rw_transaction().into_diagnostic()?;
let old_announcements: Vec<_> = announcements
.clone()
.into_iter()
.filter_map(|announcement| rw.upsert(announcement).ok())
.collect();
rw.commit().into_diagnostic()?;
Ok(old_announcements)
}

/// Delete a replica announcement record.
///
/// # Arguments
///
/// * `announcement` - A replica announcement record to delete.
///
/// # Returns
///
/// The deleted replica announcement record.
pub fn delete_announcement(
&self,
announcement: ReplicaAnnouncement,
) -> miette::Result<ReplicaAnnouncement> {
let rw = self.database.rw_transaction().into_diagnostic()?;
let removed_announcement = rw.remove(announcement).into_diagnostic()?;
rw.commit().into_diagnostic()?;
Ok(removed_announcement)
}

/// Delete multiple replica announcement records.
///
/// # Arguments
///
/// * `announcements` - A list of replica announcement records to delete.
///
/// # Returns
///
/// A list containing the deleted replica announcement records.
pub fn delete_announcements(
&self,
announcements: Vec<ReplicaAnnouncement>,
) -> miette::Result<Vec<ReplicaAnnouncement>> {
let rw = self.database.rw_transaction().into_diagnostic()?;
let removed_announcements = announcements
.into_iter()
.filter_map(|announcement| rw.remove(announcement).ok())
.collect();
rw.commit().into_diagnostic()?;
Ok(removed_announcements)
}

/// Gets the replica announcements recorded by this node.
///
/// # Returns
///
/// The replica announcements recorded by this node.
pub fn get_announcements(&self) -> miette::Result<Vec<ReplicaAnnouncement>> {
let r = self.database.r_transaction().into_diagnostic()?;
r.scan()
.primary()
.into_diagnostic()?
.all()
.into_diagnostic()?
.collect::<Result<Vec<_>, _>>()
.into_diagnostic()
}

/// Gets a replica announcement record by its public key.
///
/// # Arguments
///
/// * `key` - The public key of the DHT announcement.
///
/// # Returns
///
/// A replica announcement record.
pub fn get_announcement(&self, key: Vec<u8>) -> miette::Result<Option<ReplicaAnnouncement>> {
let r = self.database.r_transaction().into_diagnostic()?;
r.get().primary(key).into_diagnostic()
}
}
8 changes: 8 additions & 0 deletions src/database/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/// Core functionality of an OkuNet node's database.
pub mod core;
/// Database functionality relating to the DHT.
pub mod dht;
/// Database functionality relating to OkuNet posts.
pub mod posts;
/// Database functionality relating to OkuNet users.
pub mod users;
Loading

0 comments on commit ccea806

Please sign in to comment.