Skip to content

Commit

Permalink
feat(querydb): block decoding and storing
Browse files Browse the repository at this point in the history
  • Loading branch information
MaicoLeberle committed Mar 21, 2024
1 parent 8aff933 commit 0bf1bb9
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 10 deletions.
57 changes: 55 additions & 2 deletions src/querydb/prelude.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,58 @@
use redb::DatabaseError;
use pallas;
use redb::{MultimapTableDefinition, TableDefinition};

// Given a block, table "block" maps its hash to its CBOR representation
pub type BlockKeyType<'a> = &'a [u8; 32];
pub type BlockValueType<'a> = &'a [u8];
pub const BLOCK_TABLE: TableDefinition<BlockKeyType, BlockValueType> =
TableDefinition::new("block");
// "chain_tip" stores the hash of the last applied block
pub type ChainTipKeyType = u64;
pub type ChainTipValueType<'a> = &'a [u8; 32];
pub const CHAIN_TIP_TABLE: TableDefinition<ChainTipKeyType, ChainTipValueType> =
TableDefinition::new("chain_tip");
// Given a transaction, table "tx" maps its hash to an encoding representation
// of it
// NOTE: transactions don't have a precise CBOR representation, so we use
// a library encoded representation instead
pub type TxTableKeyType<'a> = &'a [u8; 32];
pub type TxTableValueType<'a> = &'a [u8];
pub const TX_TABLE: TableDefinition<TxTableKeyType, TxTableValueType> = TableDefinition::new("tx");
// Given a UTxO, table "utxo" maps its output reference (a pair composed of the
// hash of the transaction that produced the UTxO and the index in the list of
// transaction outputs corresponding to it) to the result of encoding said UTxO
// NOTE: Just like transactions, UTxO's don't have a precise CBOR
// representation.
pub type UTxOKeyType<'a> = (&'a [u8], u8);
pub type UTxOValueType<'a> = &'a [u8];
pub const UTXO_TABLE: TableDefinition<UTxOKeyType, UTxOValueType> = TableDefinition::new("utxo");
// Given an address, table "utxo_by_addr" maps it to a list of pairs of a tx
// hash and an (output) index (each one representing a UTxO sitting at that
// address)
pub type UTxOByAddrKeyType<'a> = &'a [u8];
pub type UTxOByAddrValueType<'a> = (&'a [u8], u8);
pub const UTXO_BY_ADDR_TABLE: MultimapTableDefinition<UTxOByAddrKeyType, UTxOByAddrValueType> =
MultimapTableDefinition::new("utxo_by_addr");
// Given a minting policy, table "utxo_by_beacon" maps it to a list of pairs of
// a tx hash and an (output) index (each one representing a UTxO containing a
// token of that policy)
pub type UTxOByBeaconKeyType<'a> = &'a [u8; 28];
pub type UTxOByBeaconValueType<'a> = (&'a [u8], u8);
pub const UTXO_BY_BEACON_TABLE: MultimapTableDefinition<
UTxOByBeaconKeyType,
UTxOByBeaconValueType,
> = MultimapTableDefinition::new("utxo_by_beacon");

pub enum StoreError {
DatabaseInitilization(DatabaseError),
AddressDecoding(pallas::ledger::addresses::Error),
BlockDecoding(pallas::ledger::traverse::Error),
ReDBError(ReDBError),
}

pub enum ReDBError {
CommitError(redb::CommitError),
DatabaseInitilization(redb::DatabaseError),
InsertError(redb::StorageError),
TableError(redb::TableError),
TransactionError(redb::TransactionError),
}
106 changes: 98 additions & 8 deletions src/querydb/store.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,110 @@
use crate::querydb::prelude::StoreError;
use pallas::crypto::hash::Hash;
use pallas::ledger::traverse::MultiEraOutput;
use redb::Database;
use std::path::Path;
use crate::querydb::prelude::*;
use pallas::{
crypto::hash::Hash,
ledger::traverse::{MultiEraBlock, MultiEraOutput, MultiEraPolicyAssets, MultiEraTx},
};
use redb::{Database, MultimapTable, ReadableTable, Table, WriteTransaction};
use std::{ops::Deref, path::Path};

pub struct Store {
_inner_store: Database,
inner_store: Database,
}

impl Store {
pub fn new(path: impl AsRef<Path>) -> Result<Self, StoreError> {
Ok(Store {
_inner_store: Database::create(path).map_err(StoreError::DatabaseInitilization)?,
inner_store: Database::create(path)
.map_err(|e| StoreError::ReDBError(ReDBError::DatabaseInitilization(e)))?,
})
}
pub fn apply_block(&self, _block_cbor: &[u8]) {}
pub fn apply_block(&self, block_cbor: &[u8]) -> Result<(), StoreError> {
let write_tx: WriteTransaction = self
.inner_store
.begin_write()
.map_err(|e| StoreError::ReDBError(ReDBError::TransactionError(e)))?;
let block: MultiEraBlock = Self::store_block(&write_tx, block_cbor)?;
Self::store_txs(&write_tx, &block)?;
write_tx
.commit()
.map_err(|e| StoreError::ReDBError(ReDBError::CommitError(e)))?;
Ok(())
}

fn store_block<'a>(
write_tx: &'a WriteTransaction,
block_cbor: &'a [u8],
) -> Result<MultiEraBlock<'a>, StoreError> {
let block: MultiEraBlock =
MultiEraBlock::decode(block_cbor).map_err(StoreError::BlockDecoding)?;
let mut block_table: Table<BlockKeyType, BlockValueType> = write_tx
.open_table(BLOCK_TABLE)
.map_err(|e| StoreError::ReDBError(ReDBError::TableError(e)))?;
let _ = block_table.insert(block.hash().deref(), block_cbor);
let mut chain_tip_table: Table<ChainTipKeyType, ChainTipValueType> = write_tx
.open_table(CHAIN_TIP_TABLE)
.map_err(|e| StoreError::ReDBError(ReDBError::TableError(e)))?;
let _ = chain_tip_table.insert(
chain_tip_table
.len()
.map_err(|e| StoreError::ReDBError(ReDBError::InsertError(e)))?
+ 1,
block.hash().deref(),
);
Ok(block)
}

fn store_txs(write_tx: &WriteTransaction, block: &MultiEraBlock) -> Result<(), StoreError> {
let mut tx_table: Table<TxTableKeyType, TxTableValueType> =
write_tx
.open_table(TX_TABLE)
.map_err(|e| StoreError::ReDBError(ReDBError::TableError(e)))?;
for tx in block.txs() {
tx_table
.insert(tx.hash().deref(), tx.encode().as_slice())
.map_err(|e| StoreError::ReDBError(ReDBError::InsertError(e)))?;
Self::store_utxos(write_tx, &tx)?;
}
Ok(())
}

fn store_utxos(write_tx: &WriteTransaction, tx: &MultiEraTx) -> Result<(), StoreError> {
let mut utxo_table: Table<UTxOKeyType, UTxOValueType> = write_tx
.open_table(UTXO_TABLE)
.map_err(|e| StoreError::ReDBError(ReDBError::TableError(e)))?;
let mut utxo_by_addr_table: MultimapTable<UTxOByAddrKeyType, UTxOByAddrValueType> =
write_tx
.open_multimap_table(UTXO_BY_ADDR_TABLE)
.map_err(|e| StoreError::ReDBError(ReDBError::TableError(e)))?;
let mut utxo_by_beacon_table: MultimapTable<UTxOByBeaconKeyType, UTxOByBeaconValueType> =
write_tx
.open_multimap_table(UTXO_BY_BEACON_TABLE)
.map_err(|e| StoreError::ReDBError(ReDBError::TableError(e)))?;
for (index, output) in tx.outputs().iter().enumerate() {
utxo_table
.insert(
(tx.hash().deref().as_slice(), index as u8),
output.encode().as_slice(),
)
.map_err(|e| StoreError::ReDBError(ReDBError::InsertError(e)))?;
let addr: Vec<u8> = output
.address()
.map_err(StoreError::AddressDecoding)?
.to_vec();
utxo_by_addr_table
.insert(addr.as_slice(), (tx.hash().deref().as_slice(), index as u8))
.map_err(|e| StoreError::ReDBError(ReDBError::InsertError(e)))?;
for policy in output
.non_ada_assets()
.iter()
.map(MultiEraPolicyAssets::policy)
{
utxo_by_beacon_table
.insert(policy.deref(), (tx.hash().deref().as_slice(), index as u8))
.map_err(|e| StoreError::ReDBError(ReDBError::InsertError(e)))?;
}
}
Ok(())
}

pub fn get_chain_tip(&self) -> &[u8] {
unimplemented!()
Expand Down

0 comments on commit 0bf1bb9

Please sign in to comment.