// Copyright 2019 Cargill Incorporated // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! LMDB-backed tests for the merkle state implementation. use std::env; use std::fs::remove_file; use std::panic; use std::path::Path; use std::thread; use sawtooth::transact::{ database::{ btree::BTreeDatabase, lmdb::{LmdbContext, LmdbDatabase}, }, state::merkle::INDEXES, }; use super::*; fn new_lmdb_state_and_root(lmdb_path: &str) -> (MerkleState, String) { let lmdb_db = make_lmdb(lmdb_path); let merkle_state = MerkleState::new(lmdb_db.clone()); let merkle_db = MerkleRadixTree::new(lmdb_db, None) .expect("Could not overlay the merkle tree on the database"); let orig_root = merkle_db.get_merkle_root(); (merkle_state, orig_root) } #[test] fn merkle_trie_empty_changes() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_empty_changes(orig_root, state); }) } #[test] fn merkle_trie_root_advance() { run_test(|merkle_path| { let db = make_lmdb(&merkle_path); test_merkle_trie_root_advance(db); }) } #[test] fn merkle_trie_delete() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_delete(orig_root, state); }) } #[test] fn merkle_trie_multithread_read() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_multithread_read(orig_root, state); }) } #[test] fn merkle_trie_update_multiple_entries() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_update(orig_root, state); }) } #[test] fn merkle_trie_update_same_address_space() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_update_same_address_space(orig_root, state); }) } #[test] fn merkle_trie_update_same_address_space_with_no_children() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_update_same_address_space_with_no_children(orig_root, state); }) } #[test] fn merkle_trie_prune_parent() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_prune_parent(orig_root, state); }) } #[test] fn merkle_trie_pruning_parent() { run_test(|merkle_path| { let db = make_lmdb(&merkle_path); test_merkle_trie_pruning_parent(db); }) } #[test] fn merkle_trie_prune_successors() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_prune_successors(orig_root, state); }) } #[test] fn merkle_trie_pruning_successors() { run_test(|merkle_path| { let db = make_lmdb(&merkle_path); test_merkle_trie_pruning_successors(db); }) } #[test] fn merkle_trie_prune_duplicate_leaves() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_prune_duplicate_leaves(orig_root, state); }) } #[test] fn merkle_trie_pruning_duplicate_leaves() { run_test(|merkle_path| { let db = make_lmdb(&merkle_path); test_merkle_trie_pruning_duplicate_leaves(db); }) } #[test] fn merkle_trie_prune_successor_duplicate_leaves() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_merkle_trie_prune_successor_duplicate_leaves(orig_root, state); }) } #[test] fn merkle_trie_pruning_successor_duplicate_leaves() { run_test(|merkle_path| { let db = make_lmdb(&merkle_path); test_merkle_trie_pruning_successor_duplicate_leaves(db); }) } #[test] fn leaf_iteration() { run_test(|merkle_path| { let (state, orig_root) = new_lmdb_state_and_root(merkle_path); test_leaf_iteration(orig_root, state); }) } /// Verifies that a state tree backed by lmdb and btree give the same root hashes #[test] fn lmdb_btree_comparison() { run_test(|merkle_path| { let lmdb = make_lmdb(merkle_path); let btree_db = Box::new(BTreeDatabase::new(&INDEXES)); test_same_results(lmdb, btree_db); }) } #[test] fn btree_lmdb_comparison() { run_test(|merkle_path| { let lmdb = make_lmdb(merkle_path); let btree_db = Box::new(BTreeDatabase::new(&INDEXES)); test_same_results(btree_db, lmdb); }) } fn run_test(test: T) -> () where T: FnOnce(&str) -> () + panic::UnwindSafe, { let dbpath = temp_db_path(); let testpath = dbpath.clone(); let result = panic::catch_unwind(move || test(&testpath)); remove_file(dbpath).unwrap(); if let Err(err) = result { panic::resume_unwind(err); } } fn make_lmdb(merkle_path: &str) -> Box { let ctx = LmdbContext::new( Path::new(merkle_path), INDEXES.len(), Some(120 * 1024 * 1024), ) .map_err(|err| DatabaseError::InitError(format!("{}", err))) .unwrap(); Box::new( LmdbDatabase::new(ctx, &INDEXES) .map_err(|err| DatabaseError::InitError(format!("{}", err))) .unwrap(), ) } fn temp_db_path() -> String { let mut temp_dir = env::temp_dir(); let thread_id = thread::current().id(); temp_dir.push(format!("merkle-{:?}.lmdb", thread_id)); temp_dir.to_str().unwrap().to_string() }