Expand description

An LMDB-backed trie store.

Usage

use casper_execution_engine::storage::store::Store;
use casper_execution_engine::storage::transaction_source::{Transaction, TransactionSource};
use casper_execution_engine::storage::transaction_source::lmdb::LmdbEnvironment;
use casper_execution_engine::storage::trie::{Pointer, PointerBlock, Trie};
use casper_execution_engine::storage::trie_store::lmdb::LmdbTrieStore;
use casper_hashing::Digest;
use casper_types::bytesrepr::{ToBytes, Bytes};
use lmdb::DatabaseFlags;
use tempfile::tempdir;

// Create some leaves
let leaf_1 = Trie::Leaf { key: Bytes::from(vec![0u8, 0, 0]), value: Bytes::from(b"val_1".to_vec()) };
let leaf_2 = Trie::Leaf { key: Bytes::from(vec![1u8, 0, 0]), value: Bytes::from(b"val_2".to_vec()) };

// Get their hashes
let leaf_1_hash = Digest::hash(&leaf_1.to_bytes().unwrap());
let leaf_2_hash = Digest::hash(&leaf_2.to_bytes().unwrap());

// Create a node
let node: Trie<Bytes, Bytes> = {
    let mut pointer_block = PointerBlock::new();
    pointer_block[0] = Some(Pointer::LeafPointer(leaf_1_hash));
    pointer_block[1] = Some(Pointer::LeafPointer(leaf_2_hash));
    let pointer_block = Box::new(pointer_block);
    Trie::Node { pointer_block }
};

// Get its hash
let node_hash = Digest::hash(&node.to_bytes().unwrap());

// Create the environment and the store. For both the in-memory and
// LMDB-backed implementations, the environment is the source of
// transactions.
let tmp_dir = tempdir().unwrap();
let map_size = 4096 * 2560;  // map size should be a multiple of OS page size
let max_readers = 512;
let env = LmdbEnvironment::new(&tmp_dir.path().to_path_buf(), map_size, max_readers, true).unwrap();
let store = LmdbTrieStore::new(&env, None, DatabaseFlags::empty()).unwrap();

// First let's create a read-write transaction, persist the values, but
// forget to commit the transaction.
{
    // Create a read-write transaction
    let mut txn = env.create_read_write_txn().unwrap();

    // Put the values in the store
    store.put(&mut txn, &leaf_1_hash, &leaf_1).unwrap();
    store.put(&mut txn, &leaf_2_hash, &leaf_2).unwrap();
    store.put(&mut txn, &node_hash, &node).unwrap();

    // Here we forget to commit the transaction before it goes out of scope
}

// Now let's check to see if the values were stored
{
    // Create a read transaction
    let txn = env.create_read_txn().unwrap();

    // Observe that nothing has been persisted to the store
    for hash in vec![&leaf_1_hash, &leaf_2_hash, &node_hash].iter() {
        // We need to use a type annotation here to help the compiler choose
        // a suitable FromBytes instance
        let maybe_trie: Option<Trie<Bytes, Bytes>> = store.get(&txn, hash).unwrap();
        assert!(maybe_trie.is_none());
    }

    // Commit the read transaction.  Not strictly necessary, but better to be hygienic.
    txn.commit().unwrap();
}

// Now let's try that again, remembering to commit the transaction this time
{
    // Create a read-write transaction
    let mut txn = env.create_read_write_txn().unwrap();

    // Put the values in the store
    store.put(&mut txn, &leaf_1_hash, &leaf_1).unwrap();
    store.put(&mut txn, &leaf_2_hash, &leaf_2).unwrap();
    store.put(&mut txn, &node_hash, &node).unwrap();

    // Commit the transaction.
    txn.commit().unwrap();
}

// Now let's check to see if the values were stored again
{
    // Create a read transaction
    let txn = env.create_read_txn().unwrap();

    // Get the values in the store
    assert_eq!(Some(leaf_1), store.get(&txn, &leaf_1_hash).unwrap());
    assert_eq!(Some(leaf_2), store.get(&txn, &leaf_2_hash).unwrap());
    assert_eq!(Some(node), store.get(&txn, &node_hash).unwrap());

    // Commit the read transaction.
    txn.commit().unwrap();
}

tmp_dir.close().unwrap();

Structs