blockify 0.0.9

A general purpose blockchain library
Documentation
use diesel::prelude::*;
use diesel::sql_types::Text;
use diesel::sqlite::Sqlite;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;

use crate::data::{Nonce, Position, Timestamp};
use crate::io::SerdeError;
use crate::{block::Block, record::Record};
use crate::{Hash, SqliteChainError, TempInstance};

use super::WrapperMut;

table! {
    records {
        id -> Integer,
        jsonvalues -> Text,
    }
}

table! {
    metadata {
        id -> Integer,
        timestamp -> Text,
        hash -> Text,
        merkle_root -> Text,
        nonce -> Text,
        prev_hash -> Text,
        position -> Text,
    }
}


pub struct SqliteBlock<X> {
    con: WrapperMut<SqliteConnection>,
    _data: PhantomData<X>,
}

#[derive(Debug)]
pub enum SqliteBlockError {
    ConnectionError(ConnectionError),
    SerdeError(SerdeError),
    ConnectionFailed,
}

impl Into<SqliteChainError> for SqliteBlockError {
    fn into(self) -> SqliteChainError {
        match self {
            SqliteBlockError::ConnectionError(ce) => SqliteChainError::ConnectionError(ce),
            Self::ConnectionFailed => SqliteChainError::ConnectionFailed,
            Self::SerdeError(sd) => SqliteChainError::SerdeError(sd),
        }
    }
}

impl std::fmt::Display for SqliteBlockError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        std::fmt::Debug::fmt(self, f)
    }
}

impl From<ConnectionError> for SqliteBlockError {
    fn from(value: ConnectionError) -> Self {
        SqliteBlockError::ConnectionError(value)
    }
}

impl<X: Record + Serialize> SqliteBlock<X> {
    pub fn new(url: &str) -> Result<Self, SqliteBlockError> {
        let con = SqliteConnection::establish(url)?;
        let val = Self {
            con: WrapperMut::new(con),
            _data: PhantomData,
        };
        Ok(val)
    }

    fn create_tables(con: &mut SqliteConnection) -> Result<(), SqliteBlockError> {
        diesel::sql_query(
            "
        CREATE TABLE IF NOT EXISTS records (
            id INTEGER PRIMARY KEY,
            jsonvalues TEXT
        )
        ",
        )
        .execute(con)
        .map_err(|_| SqliteBlockError::ConnectionFailed)?;

        diesel::sql_query(
            "
        CREATE TABLE IF NOT EXISTS metadata (
            id INTEGER PRIMARY KEY,
            timestamp TEXT,
            hash TEXT,
            merkle_root TEXT,
            nonce TEXT,
            prev_hash TEXT,
            position TEXT
        )",
        )
        .execute(con)
        .map_err(|_| SqliteBlockError::ConnectionFailed)?;

        Ok(())
    }

    pub fn build(
        url: &str,
        records: &[SignedRecord<X>],
        cc: &TempInstance,
    ) -> Result<Self, SqliteBlockError> {
        let TempInstance {
            nonce,
            position,
            hash,
            prev_hash,
            merkle_root,
            timestamp,
        } = cc;
        let val = Self::new(url)?;
        Self::create_tables(val.con.get_mut())?;

        let timestamp = serde_json::to_string(timestamp).unwrap();

        let hash = serde_json::to_string(hash).unwrap();

        let prev_hash = serde_json::to_string(prev_hash).unwrap();

        let merkle_root = { serde_json::to_string(merkle_root).unwrap() };

        let nonce = { serde_json::to_string(nonce).unwrap() };

        let position = serde_json::to_string(position).unwrap();

        let smt = diesel::insert_into(metadata::table).values((
            metadata::timestamp.eq(timestamp),
            metadata::hash.eq(hash),
            metadata::merkle_root.eq(merkle_root),
            metadata::nonce.eq(nonce),
            metadata::prev_hash.eq(prev_hash),
            metadata::position.eq(position),
        ));

        for record in records {
            let smt = diesel::insert_into(records::table)
                .values(records::jsonvalues.eq(serde_json::to_string(record).unwrap()));
            smt.execute(val.con.get_mut()).unwrap();
        }

        smt.execute(val.con.get_mut()).unwrap();

        Ok(val)
    }
}

use crate::block::BlockError;
use crate::record::SignedRecord;
use records::dsl::records as rq;

#[derive(Deserialize)]
struct RecordValue<X> {
    s: SignedRecord<X>,
}

impl<X> RecordValue<X> {
    fn new(s: SignedRecord<X>) -> Self {
        Self { s }
    }
}

impl<X: for<'a> Deserialize<'a>> Queryable<Text, Sqlite> for RecordValue<X> {
    type Row = String;
    fn build(row: Self::Row) -> diesel::deserialize::Result<Self> {
        let value = serde_json::from_str(&row)?;
        Ok(RecordValue::new(value))
    }
}

impl<X> From<RecordValue<X>> for SignedRecord<X> {
    fn from(value: RecordValue<X>) -> Self {
        value.s
    }
}

impl<X: Record + for<'a> Deserialize<'a> + 'static> Block for SqliteBlock<X> {
    type RecordType = X;
    fn records(&self) -> Result<Box<[SignedRecord<X>]>, BlockError> {
        let res = rq
            .select(records::jsonvalues)
            .load::<RecordValue<X>>(self.con.get_mut())
            .unwrap();
        let res = res
            .into_iter()
            .map(|record_val| record_val.into())
            .collect::<Vec<SignedRecord<X>>>();
        Ok(res.into_boxed_slice())
    }

    fn hash(&self) -> Result<Hash, crate::block::BlockError> {
        let res = metadata::table
            .select(metadata::hash)
            .first::<String>(self.con.get_mut())
            .unwrap();
        let res = serde_json::from_str::<Hash>(&res).unwrap();
        Ok(res)
    }

    fn merkle_root(&self) -> Result<crate::Hash, crate::block::BlockError> {
        let res = metadata::table
            .select(metadata::merkle_root)
            .first::<String>(self.con.get_mut())
            .unwrap();
        let res = serde_json::from_str::<Hash>(&res).unwrap();
        Ok(res)
    }

    fn nonce(&self) -> Result<Nonce, crate::block::BlockError> {
        let res = metadata::table
            .select(metadata::nonce)
            .first::<String>(self.con.get_mut())
            .unwrap();
        let res = serde_json::from_str::<Nonce>(&res).unwrap();
        Ok(res)
    }

    fn prev_hash(&self) -> Result<Hash, BlockError> {
        let res = metadata::table
            .select(metadata::prev_hash)
            .first::<String>(self.con.get_mut())
            .unwrap();
        let res = serde_json::from_str::<Hash>(&res).unwrap();
        Ok(res)
    }

    fn position(&self) -> Result<Position, BlockError> {
        let res = metadata::table
            .select(metadata::position)
            .first::<String>(self.con.get_mut())
            .unwrap();
        let res = serde_json::from_str::<Position>(&res).unwrap();
        Ok(res)
    }

    fn timestamp(&self) -> Result<Timestamp, BlockError> {
        let res = metadata::table
            .select(metadata::timestamp)
            .first::<String>(self.con.get_mut())
            .unwrap();
        let res = serde_json::from_str::<Timestamp>(&res).unwrap();
        Ok(res)
    }
}