#![allow(unsafe_op_in_unsafe_fn)]
use crate::anchor::AnchorSet;
use crate::bls::BlsPubkey;
use crate::checkpoint::Checkpoint;
use crate::error::{Error, check};
use crate::ffi;
use crate::merkle::{MerkleEntry, MerkleProof};
use crate::types::{Duration, MerkleHash, NodeId, Tstamp};
pub const STALENESS_WARNING_NS: Duration = 3600 * crate::types::SECONDS;
pub const STALENESS_REJECT_NS: Duration = 86400 * crate::types::SECONDS;
pub const IDENTITY_CACHE_TTL: Duration = 3600 * crate::types::SECONDS;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Staleness {
Fresh,
Warning,
Reject,
}
impl From<ffi::nwep_staleness> for Staleness {
fn from(s: ffi::nwep_staleness) -> Self {
match s {
ffi::nwep_staleness_NWEP_STALENESS_WARNING => Staleness::Warning,
ffi::nwep_staleness_NWEP_STALENESS_REJECT => Staleness::Reject,
_ => Staleness::Fresh,
}
}
}
#[derive(Clone, Debug)]
pub struct VerifiedIdentity {
pub node_id: NodeId,
pub pubkey: [u8; 32],
pub log_index: u64,
pub checkpoint_epoch: u64,
pub verified_at: Tstamp,
pub revoked: bool,
}
impl VerifiedIdentity {
pub(crate) fn from_ffi(v: &ffi::nwep_verified_identity) -> Self {
VerifiedIdentity {
node_id: NodeId(v.nodeid.data),
pubkey: v.pubkey,
log_index: v.log_index,
checkpoint_epoch: v.checkpoint_epoch,
verified_at: v.verified_at,
revoked: v.revoked != 0,
}
}
}
#[derive(Clone, Debug)]
pub struct Equivocation {
pub anchor: BlsPubkey,
pub epoch: u64,
pub root1: MerkleHash,
pub root2: MerkleHash,
}
impl Equivocation {
fn from_ffi(e: &ffi::nwep_equivocation) -> Self {
Equivocation {
anchor: BlsPubkey(e.anchor.data),
epoch: e.epoch,
root1: MerkleHash(e.root1.data),
root2: MerkleHash(e.root2.data),
}
}
}
pub struct TrustSettings {
pub staleness_warning_ns: Duration,
pub staleness_reject_ns: Duration,
pub identity_cache_ttl: Duration,
pub anchor_threshold: usize,
}
impl Default for TrustSettings {
fn default() -> Self {
TrustSettings {
staleness_warning_ns: STALENESS_WARNING_NS,
staleness_reject_ns: STALENESS_REJECT_NS,
identity_cache_ttl: IDENTITY_CACHE_TTL,
anchor_threshold: crate::types::DEFAULT_ANCHOR_THRESHOLD,
}
}
}
pub struct TrustStorage {
pub anchor_load: Option<Box<dyn Fn() -> Result<Vec<BlsPubkey>, Error> + Send>>,
pub anchor_save: Option<Box<dyn Fn(&[BlsPubkey]) -> Result<(), Error> + Send>>,
pub checkpoint_load: Option<Box<dyn Fn() -> Result<Vec<Checkpoint>, Error> + Send>>,
pub checkpoint_save: Option<Box<dyn Fn(&Checkpoint) -> Result<(), Error> + Send>>,
}
impl Default for TrustStorage {
fn default() -> Self {
TrustStorage {
anchor_load: None,
anchor_save: None,
checkpoint_load: None,
checkpoint_save: None,
}
}
}
struct StorageCallbacks {
storage: TrustStorage,
}
unsafe extern "C" fn anchor_load_cb(
user_data: *mut std::ffi::c_void,
anchors: *mut ffi::nwep_bls_pubkey,
max_anchors: usize,
) -> std::ffi::c_int {
let cb = &*(user_data as *const StorageCallbacks);
if let Some(f) = &cb.storage.anchor_load {
match f() {
Ok(pks) => {
let n = pks.len().min(max_anchors);
for (i, pk) in pks[..n].iter().enumerate() {
*anchors.add(i) = ffi::nwep_bls_pubkey { data: pk.0 };
}
n as i32
}
Err(e) => e.code,
}
} else {
0
}
}
unsafe extern "C" fn anchor_save_cb(
user_data: *mut std::ffi::c_void,
anchors: *const ffi::nwep_bls_pubkey,
count: usize,
) -> std::ffi::c_int {
let cb = &*(user_data as *const StorageCallbacks);
if let Some(f) = &cb.storage.anchor_save {
let pks: Vec<BlsPubkey> = (0..count)
.map(|i| BlsPubkey((*anchors.add(i)).data))
.collect();
match f(&pks) {
Ok(()) => 0,
Err(e) => e.code,
}
} else {
0
}
}
unsafe extern "C" fn checkpoint_load_cb(
user_data: *mut std::ffi::c_void,
checkpoints: *mut ffi::nwep_checkpoint,
max_checkpoints: usize,
) -> std::ffi::c_int {
let cb = &*(user_data as *const StorageCallbacks);
if let Some(f) = &cb.storage.checkpoint_load {
match f() {
Ok(cps) => {
let n = cps.len().min(max_checkpoints);
for (i, cp) in cps[..n].iter().enumerate() {
*checkpoints.add(i) = cp.to_ffi();
}
n as i32
}
Err(e) => e.code,
}
} else {
0
}
}
unsafe extern "C" fn checkpoint_save_cb(
user_data: *mut std::ffi::c_void,
cp: *const ffi::nwep_checkpoint,
) -> std::ffi::c_int {
let cb = &*(user_data as *const StorageCallbacks);
if let Some(f) = &cb.storage.checkpoint_save {
let checkpoint = Checkpoint::from_ffi(&*cp);
match f(&checkpoint) {
Ok(()) => 0,
Err(e) => e.code,
}
} else {
0
}
}
pub struct TrustStore {
ptr: *mut ffi::nwep_trust_store,
_callbacks: Option<Box<StorageCallbacks>>,
}
unsafe impl Send for TrustStore {}
impl TrustStore {
pub fn new(settings: TrustSettings) -> Result<Self, Error> {
let ffi_settings = ffi::nwep_trust_settings {
staleness_warning_ns: settings.staleness_warning_ns,
staleness_reject_ns: settings.staleness_reject_ns,
identity_cache_ttl: settings.identity_cache_ttl,
anchor_threshold: settings.anchor_threshold,
};
let mut ptr: *mut ffi::nwep_trust_store = std::ptr::null_mut();
check(unsafe { ffi::nwep_trust_store_new(&mut ptr, &ffi_settings, std::ptr::null()) })?;
Ok(TrustStore {
ptr,
_callbacks: None,
})
}
pub fn new_with_storage(settings: TrustSettings, storage: TrustStorage) -> Result<Self, Error> {
let ffi_settings = ffi::nwep_trust_settings {
staleness_warning_ns: settings.staleness_warning_ns,
staleness_reject_ns: settings.staleness_reject_ns,
identity_cache_ttl: settings.identity_cache_ttl,
anchor_threshold: settings.anchor_threshold,
};
let mut cb = Box::new(StorageCallbacks { storage });
let cb_ptr = cb.as_mut() as *mut _ as *mut std::ffi::c_void;
let ffi_storage = ffi::nwep_trust_storage {
anchor_load: Some(anchor_load_cb),
anchor_save: Some(anchor_save_cb),
checkpoint_load: Some(checkpoint_load_cb),
checkpoint_save: Some(checkpoint_save_cb),
user_data: cb_ptr,
};
let mut ptr: *mut ffi::nwep_trust_store = std::ptr::null_mut();
check(unsafe { ffi::nwep_trust_store_new(&mut ptr, &ffi_settings, &ffi_storage) })?;
Ok(TrustStore {
ptr,
_callbacks: Some(cb),
})
}
pub fn add_anchor(&mut self, pk: &BlsPubkey, builtin: bool) -> Result<(), Error> {
let ffi_pk = ffi::nwep_bls_pubkey { data: pk.0 };
check(unsafe { ffi::nwep_trust_store_add_anchor(self.ptr, &ffi_pk, builtin as i32) })
}
pub fn remove_anchor(&mut self, pk: &BlsPubkey) -> Result<(), Error> {
let ffi_pk = ffi::nwep_bls_pubkey { data: pk.0 };
check(unsafe { ffi::nwep_trust_store_remove_anchor(self.ptr, &ffi_pk) })
}
pub fn anchors(&self) -> &AnchorSet {
unsafe {
let ptr = ffi::nwep_trust_store_get_anchors(self.ptr);
&*(ptr as *const AnchorSet)
}
}
pub fn add_checkpoint(&mut self, cp: &Checkpoint) -> Result<(), Error> {
let ffi_cp = cp.to_ffi();
check(unsafe { ffi::nwep_trust_store_add_checkpoint(self.ptr, &ffi_cp) })
}
pub fn get_latest_checkpoint(&self) -> Result<Checkpoint, Error> {
let mut cp = unsafe { std::mem::zeroed::<ffi::nwep_checkpoint>() };
check(unsafe { ffi::nwep_trust_store_get_latest_checkpoint(self.ptr, &mut cp) })?;
Ok(Checkpoint::from_ffi(&cp))
}
pub fn get_checkpoint(&self, epoch: u64) -> Result<Checkpoint, Error> {
let mut cp = unsafe { std::mem::zeroed::<ffi::nwep_checkpoint>() };
check(unsafe { ffi::nwep_trust_store_get_checkpoint(self.ptr, epoch, &mut cp) })?;
Ok(Checkpoint::from_ffi(&cp))
}
pub fn checkpoint_count(&self) -> usize {
unsafe { ffi::nwep_trust_store_checkpoint_count(self.ptr) }
}
pub fn check_staleness(&self, now: Tstamp) -> Staleness {
Staleness::from(unsafe { ffi::nwep_trust_store_check_staleness(self.ptr, now) })
}
pub fn get_staleness_age(&self, now: Tstamp) -> Duration {
unsafe { ffi::nwep_trust_store_get_staleness_age(self.ptr, now) }
}
pub fn verify_identity(
&mut self,
entry: &MerkleEntry,
proof: &MerkleProof,
checkpoint: Option<&Checkpoint>,
now: Tstamp,
) -> Result<VerifiedIdentity, Error> {
let ffi_entry = entry.to_ffi();
let ffi_proof = proof.to_ffi();
let ffi_cp_owned;
let ffi_cp_ptr = match checkpoint {
Some(cp) => {
ffi_cp_owned = cp.to_ffi();
&ffi_cp_owned as *const _
}
None => std::ptr::null(),
};
let mut result = unsafe { std::mem::zeroed::<ffi::nwep_verified_identity>() };
check(unsafe {
ffi::nwep_trust_store_verify_identity(
self.ptr,
&ffi_entry,
&ffi_proof,
ffi_cp_ptr,
now,
&mut result,
)
})?;
Ok(VerifiedIdentity::from_ffi(&result))
}
pub fn cache_identity(&mut self, vi: &VerifiedIdentity) -> Result<(), Error> {
let ffi_vi = ffi::nwep_verified_identity {
nodeid: ffi::nwep_nodeid { data: vi.node_id.0 },
pubkey: vi.pubkey,
log_index: vi.log_index,
checkpoint_epoch: vi.checkpoint_epoch,
verified_at: vi.verified_at,
revoked: vi.revoked as i32,
};
check(unsafe { ffi::nwep_trust_store_cache_identity(self.ptr, &ffi_vi) })
}
pub fn lookup_identity(
&self,
node_id: &NodeId,
now: Tstamp,
) -> Result<VerifiedIdentity, Error> {
let ffi_nid = ffi::nwep_nodeid { data: node_id.0 };
let mut out = unsafe { std::mem::zeroed::<ffi::nwep_verified_identity>() };
check(unsafe { ffi::nwep_trust_store_lookup_identity(self.ptr, &ffi_nid, now, &mut out) })?;
Ok(VerifiedIdentity::from_ffi(&out))
}
pub fn check_equivocation(&mut self, cp: &Checkpoint) -> Result<Option<Equivocation>, Error> {
let ffi_cp = cp.to_ffi();
let mut out = unsafe { std::mem::zeroed::<ffi::nwep_equivocation>() };
let rc = unsafe { ffi::nwep_trust_store_check_equivocation(self.ptr, &ffi_cp, &mut out) };
if rc == 0 {
Ok(None)
} else if rc == crate::error::ERR_TRUST_EQUIVOCATION {
Ok(Some(Equivocation::from_ffi(&out)))
} else {
Err(Error::from_code(rc))
}
}
}
impl Drop for TrustStore {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { ffi::nwep_trust_store_free(self.ptr) }
}
}
}