Skip to main content

Overview

The Storage struct is a wrapper around the sled embedded database, providing serialization helpers and key construction utilities for the Minichain blockchain.

Storage

Core database wrapper with automatic serialization/deserialization.

Constructor Methods

Storage::open
fn
Opens a database at the specified path.
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self>
path
P: AsRef<Path>
required
File system path where the database will be stored
Result
Result<Storage>
Returns a Storage instance or StorageError on failure
Example:
use minichain_storage::Storage;

let storage = Storage::open("./blockchain_data")?;
Storage::open_temporary
fn
Opens an in-memory database for testing.
pub fn open_temporary() -> Result<Self>
Result
Result<Storage>
Returns a temporary Storage instance that will be deleted when dropped
Example:
use minichain_storage::Storage;

let storage = Storage::open_temporary()?;
// Perfect for unit tests

Data Operations

Storage::put
fn
Stores a serializable value with automatic encoding.
pub fn put<K, V>(&self, key: K, value: &V) -> Result<()>
where
    K: AsRef<[u8]>,
    V: serde::Serialize
key
K: AsRef<[u8]>
required
Key for storage (string or byte slice)
value
&V: Serialize
required
Value to serialize and store
Example:
storage.put("balance", &1000u64)?;
storage.put(b"account:alice", &account)?;
Storage::get
fn
Retrieves and deserializes a value.
pub fn get<K, V>(&self, key: K) -> Result<Option<V>>
where
    K: AsRef<[u8]>,
    V: serde::de::DeserializeOwned
key
K: AsRef<[u8]>
required
Key to retrieve
Result
Result<Option<V>>
Returns Some(value) if found, None if missing, or error on deserialization failure
Example:
let balance: Option<u64> = storage.get("balance")?;
match balance {
    Some(b) => println!("Balance: {}", b),
    None => println!("No balance found"),
}
Storage::get_or_err
fn
Retrieves a value, returning error if not found.
pub fn get_or_err<K, V>(&self, key: K) -> Result<V>
where
    K: AsRef<[u8]> + std::fmt::Debug + Clone,
    V: serde::de::DeserializeOwned
key
K: AsRef<[u8]>
required
Key to retrieve (must be Debug + Clone for error reporting)
Result
Result<V>
Returns the value or StorageError::NotFound
Example:
// Fails with clear error if key doesn't exist
let balance: u64 = storage.get_or_err("balance")?;
Storage::delete
fn
Deletes a key from storage.
pub fn delete<K: AsRef<[u8]>>(&self, key: K) -> Result<()>
key
K: AsRef<[u8]>
required
Key to delete
Example:
storage.delete("old_key")?;
Storage::contains
fn
Checks if a key exists in storage.
pub fn contains<K: AsRef<[u8]>>(&self, key: K) -> Result<bool>
key
K: AsRef<[u8]>
required
Key to check
Example:
if storage.contains("balance")? {
    println!("Balance exists");
}

Batch Operations

Storage::batch
fn
Applies multiple operations atomically using sled’s write-ahead log.
pub fn batch(&self, operations: Vec<BatchOp>) -> Result<()>
operations
Vec<BatchOp>
required
Vector of insert or remove operations
Example:
use minichain_storage::BatchOp;

let ops = vec![
    BatchOp::Insert {
        key: b"key1".to_vec(),
        value: bincode::serialize(&100u64)?,
    },
    BatchOp::Remove {
        key: b"key2".to_vec(),
    },
];
storage.batch(ops)?;
Storage::flush
fn
Flushes all pending writes to disk.
pub fn flush(&self) -> Result<()>
Example:
storage.flush()?; // Ensure durability

Key Construction Helpers

These methods generate standardized keys for different data types.
Storage::account_key
fn
Creates a prefixed key for accounts.
pub fn account_key(address: &Address) -> Vec<u8>
Format: "account:" + address_bytesExample:
use minichain_core::Address;

let alice = Address([0xAA; 20]);
let key = Storage::account_key(&alice);
// key = b"account:\xAA\xAA\xAA..."
Storage::block_height_key
fn
Creates a prefixed key for blocks by height.
pub fn block_height_key(height: u64) -> Vec<u8>
Format: "block:height:{height}"Example:
let key = Storage::block_height_key(42);
// key = b"block:height:42"
Storage::block_hash_key
fn
Creates a prefixed key for blocks by hash.
pub fn block_hash_key(hash: &Hash) -> Vec<u8>
Format: "block:hash:" + hash_bytesExample:
use minichain_core::Hash;

let hash = Hash([0xBB; 32]);
let key = Storage::block_hash_key(&hash);
Storage::contract_storage_key
fn
Creates a prefixed key for contract storage slots.
pub fn contract_storage_key(contract: &Address, slot: &[u8]) -> Vec<u8>
Format: "storage:" + contract_address + ":" + slotExample:
let contract = Address([0xCC; 20]);
let slot = [0u8; 32];
let key = Storage::contract_storage_key(&contract, &slot);
Storage::code_key
fn
Creates a prefixed key for contract code.
pub fn code_key(code_hash: &Hash) -> String
Format: "code:{code_hash_hex}"Example:
let code_hash = hash(b"contract bytecode");
let key = Storage::code_key(&code_hash);
// key = "code:a1b2c3..."

Advanced Access

Storage::inner
fn
Gets the underlying sled database for advanced operations.
pub fn inner(&self) -> &Db
Example:
// Use sled's scan_prefix for iteration
for result in storage.inner().scan_prefix(b"account:") {
    let (key, value) = result?;
    // Process each account...
}

BatchOp

Represents a single operation in an atomic batch.
pub enum BatchOp {
    Insert { key: Vec<u8>, value: Vec<u8> },
    Remove { key: Vec<u8> },
}

Variants

Insert
variant
Inserts or updates a key-value pair
key
Vec<u8>
required
Key to insert
value
Vec<u8>
required
Pre-serialized value bytes
Remove
variant
Removes a key
key
Vec<u8>
required
Key to remove

StorageError

Errors that can occur during storage operations.
pub enum StorageError {
    Database(sled::Error),
    Serialization(bincode::Error),
    NotFound(String),
    InsufficientBalance { address: Address, required: u64, available: u64 },
    InvalidGenesis(String),
    InvalidStorageValue,
}

Variants

Database
variant
Underlying sled database error
Serialization
variant
Bincode serialization/deserialization error
NotFound
variant
Key not found in database
InsufficientBalance
variant
Account doesn’t have enough balance for operation
address
Address
Account address
required
u64
Amount required
available
u64
Amount available
InvalidGenesis
variant
Genesis block validation error
InvalidStorageValue
variant
Contract storage value has invalid format

Result Type

pub type Result<T> = std::result::Result<T, StorageError>;
Convenience type alias for storage operations.

Complete Example

use minichain_storage::{Storage, BatchOp};
use minichain_core::Address;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Open database
    let storage = Storage::open("./my_blockchain")?;

    // Store some data
    let alice = Address([0xAA; 20]);
    storage.put(Storage::account_key(&alice), &1000u64)?;

    // Retrieve it
    let balance: u64 = storage.get_or_err(Storage::account_key(&alice))?;
    println!("Alice's balance: {}", balance);

    // Batch operations
    let ops = vec![
        BatchOp::Insert {
            key: b"key1".to_vec(),
            value: bincode::serialize(&42u64)?,
        },
        BatchOp::Insert {
            key: b"key2".to_vec(),
            value: bincode::serialize(&100u64)?,
        },
    ];
    storage.batch(ops)?;

    // Flush to disk
    storage.flush()?;

    Ok(())
}

See Also

Build docs developers (and LLMs) love