aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cryptonote_core/blockchain.cpp7
-rw-r--r--src/cryptonote_core/blockchain_db.h24
-rw-r--r--tests/unit_tests/BlockchainDB.cpp290
3 files changed, 315 insertions, 6 deletions
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index 828ad96e4..a35ce846c 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -547,11 +547,16 @@ bool Blockchain::get_block_by_hash(const crypto::hash &h, block &blk)
return false;
}
//------------------------------------------------------------------
+//FIXME: this function does not seem to be called from anywhere, but
+// if it ever is, should probably change std::list for std::vector
void Blockchain::get_all_known_block_ids(std::list<crypto::hash> &main, std::list<crypto::hash> &alt, std::list<crypto::hash> &invalid)
{
CRITICAL_REGION_LOCAL(m_blockchain_lock);
- main = m_db->get_hashes_range(0, m_db->height());
+ for (auto& a : m_db->get_hashes_range(0, m_db->height()))
+ {
+ main.push_back(a);
+ }
BOOST_FOREACH(blocks_ext_by_hash::value_type &v, m_alternative_chains)
alt.push_back(v.first);
diff --git a/src/cryptonote_core/blockchain_db.h b/src/cryptonote_core/blockchain_db.h
index 5359909f7..251f9272b 100644
--- a/src/cryptonote_core/blockchain_db.h
+++ b/src/cryptonote_core/blockchain_db.h
@@ -25,6 +25,8 @@
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#ifndef BLOCKCHAIN_DB_H
+#define BLOCKCHAIN_DB_H
#include <list>
#include <string>
@@ -70,6 +72,8 @@
* bool lock()
* unlock()
*
+ * vector<str> get_filenames()
+ *
* Blocks:
* bool block_exists(hash)
* height add_block(block, block_size, cumulative_difficulty, coins_generated, transactions)
@@ -364,6 +368,11 @@ private:
public:
+
+ // virtual dtor
+ virtual ~BlockchainDB() { };
+
+
// open the db at location <filename>, or create it if there isn't one.
virtual void open(const std::string& filename) = 0;
@@ -379,6 +388,9 @@ public:
// reset the db -- USE WITH CARE
virtual void reset() = 0;
+ // get all files used by this db (if any)
+ virtual std::vector<std::string> get_filenames() = 0;
+
// FIXME: these are just for functionality mocking, need to implement
// RAII-friendly and multi-read one-write friendly locking mechanism
@@ -435,11 +447,11 @@ public:
// return hash of block at height <height>
virtual crypto::hash get_block_hash_from_height(const uint64_t& height) = 0;
- // return list of blocks in range <h1,h2> of height.
- virtual std::list<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) = 0;
+ // return vector of blocks in range <h1,h2> of height (inclusively)
+ virtual std::vector<block> get_blocks_range(const uint64_t& h1, const uint64_t& h2) = 0;
- // return list of block hashes in range <h1, h2> of height
- virtual std::list<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) = 0;
+ // return vector of block hashes in range <h1, h2> of height (inclusively)
+ virtual std::vector<crypto::hash> get_hashes_range(const uint64_t& h1, const uint64_t& h2) = 0;
// return the hash of the top block on the chain
virtual crypto::hash top_block_hash() = 0;
@@ -479,7 +491,7 @@ public:
// return list of tx with hashes <hlist>.
// TODO: decide if a missing hash means return empty list
// or just skip that hash
- virtual std::list<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) = 0;
+ virtual std::vector<transaction> get_tx_list(const std::vector<crypto::hash>& hlist) = 0;
// returns height of block that contains transaction with hash <h>
virtual uint64_t get_tx_block_height(const crypto::hash& h) = 0;
@@ -511,3 +523,5 @@ public:
} // namespace cryptonote
+
+#endif // BLOCKCHAIN_DB_H
diff --git a/tests/unit_tests/BlockchainDB.cpp b/tests/unit_tests/BlockchainDB.cpp
new file mode 100644
index 000000000..ea2940f31
--- /dev/null
+++ b/tests/unit_tests/BlockchainDB.cpp
@@ -0,0 +1,290 @@
+// Copyright (c) 2014, The Monero Project
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without modification, are
+// permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this list of
+// conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice, this list
+// of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its contributors may be
+// used to endorse or promote products derived from this software without specific
+// prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+#include <cstdio>
+#include <iostream>
+
+#include "gtest/gtest.h"
+
+#include "cryptonote_core/blockchain_db.h"
+#include "cryptonote_core/BlockchainDB_impl/lmdb.h"
+#include "cryptonote_core/cryptonote_format_utils.h"
+
+using namespace cryptonote;
+using epee::string_tools::pod_to_hex;
+
+#define ASSERT_HASH_EQ(a,b) ASSERT_EQ(pod_to_hex(a), pod_to_hex(b))
+
+namespace { // anonymous namespace
+
+const std::vector<std::string> t_blocks =
+ {
+ "0100d5adc49a053b8818b2b6023cd2d532c6774e164a8fcacd603651cb3ea0cb7f9340b28ec016b4bc4ca301aa0101ff6e08acbb2702eab03067870349139bee7eab2ca2e030a6bb73d4f68ab6a3b6ca937214054cdac0843d028bbe23b57ea9bae53f12da93bb57bf8a2e40598d9fccd10c2921576e987d93cd80b4891302468738e391f07c4f2b356f7957160968e0bfef6e907c3cee2d8c23cbf04b089680c6868f01025a0f41f063e195a966051e3a29e17130a9ce97d48f55285b9bb04bdd55a09ae78088aca3cf0202d0f26169290450fe17e08974789c3458910b4db18361cdc564f8f2d0bdd2cf568090cad2c60e02d6f3483ec45505cc3be841046c7a12bf953ac973939bc7b727e54258e1881d4d80e08d84ddcb0102dae6dfb16d3e28aaaf43e00170b90606b36f35f38f8a3dceb5ee18199dd8f17c80c0caf384a30202385d7e57a4daba4cdd9e550a92dcc188838386e7581f13f09de796cbed4716a42101c052492a077abf41996b50c1b2e67fd7288bcd8c55cdc657b4e22d0804371f6901beb76a82ea17400cd6d7f595f70e1667d2018ed8f5a78d1ce07484222618c3cd"
+ , "0100f9adc49a057d3113f562eac36f14afa08c22ae20bbbf8cffa31a4466d24850732cb96f80e9762365ee01ab0101ff6f08cc953502be76deb845c431f2ed9a4862457654b914003693b8cd672abc935f0d97b16380c08db7010291819f2873e3efbae65ecd5a736f5e8a26318b591c21e39a03fb536520ac63ba80dac40902439a10fde02e39e48e0b31e57cc084a07eedbefb8cbea0143aedd0442b189caa80c6868f010227b84449de4cd7a48cbdce8974baf0b6646e03384e32055e705c243a86bef8a58088aca3cf0202fa7bd15e4e7e884307ab130bb9d50e33c5fcea6546042a26f948efd5952459ee8090cad2c60e028695583dbb8f8faab87e3ef3f88fa827db097bbf51761d91924f5c5b74c6631780e08d84ddcb010279d2f247b54690e3b491e488acff16014a825fd740c23988a25df7c4670c1f2580c0caf384a302022599dfa3f8788b66295051d85937816e1c320cdb347a0fba5219e3fe60c83b2421010576509c5672025d28fd5d3f38efce24e1f9aaf65dd3056b2504e6e2b7f19f7800"
+ };
+
+const std::vector<size_t> t_sizes =
+ {
+ 1122
+ , 347
+ };
+
+const std::vector<difficulty_type> t_diffs =
+ {
+ 4003674
+ , 4051757
+ };
+
+const std::vector<uint64_t> t_coins =
+ {
+ 1952630229575370
+ , 1970220553446486
+ };
+
+const std::vector<std::vector<std::string>> t_transactions =
+ {
+ {
+ "0100010280e08d84ddcb0106010401110701f254220bb50d901a5523eaed438af5d43f8c6d0e54ba0632eb539884f6b7c02008c0a8a50402f9c7cf807ae74e56f4ec84db2bd93cfb02c2249b38e306f5b54b6e05d00d543b8095f52a02b6abb84e00f47f0a72e37b6b29392d906a38468404c57db3dbc5e8dd306a27a880d293ad0302cfc40a86723e7d459e90e45d47818dc0e81a1f451ace5137a4af8110a89a35ea80b4c4c321026b19c796338607d5a2c1ba240a167134142d72d1640ef07902da64fed0b10cfc8088aca3cf02021f6f655254fee84161118b32e7b6f8c31de5eb88aa00c29a8f57c0d1f95a24dd80d0b8e1981a023321af593163cea2ae37168ab926efd87f195756e3b723e886bdb7e618f751c480a094a58d1d0295ed2b08d1cf44482ae0060a5dcc4b7d810a85dea8c62e274f73862f3d59f8ed80a0e5b9c2910102dc50f2f28d7ceecd9a1147f7106c8d5b4e08b2ec77150f52dd7130ee4f5f50d42101d34f90ac861d0ee9fe3891656a234ea86a8a93bf51a237db65baa00d3f4aa196a9e1d89bc06b40e94ea9a26059efc7ba5b2de7ef7c139831ca62f3fe0bb252008f8c7ee810d3e1e06313edf2db362fc39431755779466b635f12f9f32e44470a3e85e08a28fcd90633efc94aa4ae39153dfaf661089d045521343a3d63e8da08d7916753c66aaebd4eefcfe8e58e5b3d266b752c9ca110749fa33fce7c44270386fcf2bed4f03dd5dadb2dc1fd4c505419f8217b9eaec07521f0d8963e104603c926745039cf38d31de6ed95ace8e8a451f5a36f818c151f517546d55ac0f500e54d07b30ea7452f2e93fa4f60bdb30d71a0a97f97eb121e662006780fbf69002228224a96bff37893d47ec3707b17383906c0cd7d9e7412b3e6c8ccf1419b093c06c26f96e3453b424713cdc5c9575f81cda4e157052df11f4c40809edf420f88a3dd1f7909bbf77c8b184a933389094a88e480e900bcdbf6d1824742ee520fc0032e7d892a2b099b8c6edfd1123ce58a34458ee20cad676a7f7cfd80a28f0cb0888af88838310db372986bdcf9bfcae2324480ca7360d22bff21fb569a530e"
+ }
+ , {
+ }
+ };
+
+// if the return type (blobdata for now) of block_to_blob ever changes
+// from std::string, this might break.
+bool compare_blocks(const block& a, const block& b)
+{
+ auto ab = block_to_blob(a);
+ auto bb = block_to_blob(b);
+
+ return ab == bb;
+}
+
+// if the return type (blobdata for now) of tx_to_blob ever changes
+// from std::string, this might break.
+bool compare_txs(const transaction& a, const transaction& b)
+{
+ auto ab = tx_to_blob(a);
+ auto bb = tx_to_blob(b);
+
+ return ab == bb;
+}
+
+// convert hex string to string that has values based on that hex
+// thankfully should automatically ignore null-terminator.
+std::string h2b(const std::string& s)
+{
+ bool upper = true;
+ std::string result;
+ unsigned char val = 0;
+ for (char c : s)
+ {
+ if (upper)
+ {
+ val = 0;
+ if (c <= 'f' && c >= 'a')
+ {
+ val = ((c - 'a') + 10) << 4;
+ }
+ else
+ {
+ val = (c - '0') << 4;
+ }
+ }
+ else
+ {
+ if (c <= 'f' && c >= 'a')
+ {
+ val |= (c - 'a') + 10;
+ }
+ else
+ {
+ val |= c - '0';
+ }
+ result += (char)val;
+ }
+ upper = !upper;
+ }
+ return result;
+}
+
+template <typename T>
+class BlockchainDBTest : public testing::Test
+{
+protected:
+ BlockchainDBTest() : m_db(new T())
+ {
+ for (auto& i : t_blocks)
+ {
+ block bl;
+ blobdata bd = h2b(i);
+ parse_and_validate_block_from_blob(bd, bl);
+ m_blocks.push_back(bl);
+ }
+ for (auto& i : t_transactions)
+ {
+ std::vector<transaction> txs;
+ for (auto& j : i)
+ {
+ transaction tx;
+ blobdata bd = h2b(j);
+ parse_and_validate_tx_from_blob(bd, tx);
+ txs.push_back(tx);
+ }
+ m_txs.push_back(txs);
+ }
+ }
+
+ ~BlockchainDBTest() {
+ auto files = m_db->get_filenames();
+ delete m_db;
+ remove_files(files);
+ }
+
+ BlockchainDB* m_db;
+ std::string m_prefix;
+ std::vector<block> m_blocks;
+ std::vector<std::vector<transaction> > m_txs;
+
+ void remove_files(const std::vector<std::string>& files)
+ {
+ // remove each file the db created, making sure it starts with fname.
+ for (auto& f : files)
+ {
+ if (boost::starts_with(f, m_prefix))
+ {
+ boost::filesystem::remove(f);
+ }
+ else
+ {
+ std::cerr << "File created by test not to be removed (for safety): " << f << std::endl;
+ }
+ }
+ }
+
+ void set_prefix(const std::string& prefix)
+ {
+ m_prefix = prefix;
+ }
+};
+
+using testing::Types;
+
+typedef Types<> implementations;
+
+TYPED_TEST_CASE(BlockchainDBTest, implementations);
+
+TYPED_TEST(BlockchainDBTest, OpenAndClose)
+{
+ std::string fname(tmpnam(NULL));
+
+ this->set_prefix(fname);
+
+ // make sure open does not throw
+ ASSERT_NO_THROW(this->m_db->open(fname));
+
+ // make sure open when already open DOES throw
+ ASSERT_THROW(this->m_db->open(fname), DB_OPEN_FAILURE);
+
+ ASSERT_NO_THROW(this->m_db->close());
+}
+
+TYPED_TEST(BlockchainDBTest, AddBlock)
+{
+ std::string fname(tmpnam(NULL));
+
+ // make sure open does not throw
+ ASSERT_NO_THROW(this->m_db->open(fname));
+
+ // adding a block with no parent in the blockchain should throw.
+ // note: this shouldn't be possible, but is a good (and cheap) failsafe.
+ ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE);
+
+ ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]));
+ ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]));
+
+ block b;
+ ASSERT_TRUE(this->m_db->block_exists(get_block_hash(this->m_blocks[0])));
+ ASSERT_NO_THROW(b = this->m_db->get_block(get_block_hash(this->m_blocks[0])));
+
+ ASSERT_TRUE(compare_blocks(this->m_blocks[0], b));
+
+ ASSERT_NO_THROW(b = this->m_db->get_block_from_height(0));
+
+ ASSERT_TRUE(compare_blocks(this->m_blocks[0], b));
+
+ // assert that we can't add the same block twice
+ ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), BLOCK_EXISTS);
+
+ for (auto& h : this->m_blocks[0].tx_hashes)
+ {
+ transaction tx;
+ ASSERT_TRUE(this->m_db->tx_exists(h));
+ ASSERT_NO_THROW(tx = this->m_db->get_tx(h));
+
+ ASSERT_HASH_EQ(h, get_transaction_hash(tx));
+ }
+}
+
+TYPED_TEST(BlockchainDBTest, RetrieveBlockData)
+{
+ std::string fname(tmpnam(NULL));
+
+ // make sure open does not throw
+ ASSERT_NO_THROW(this->m_db->open(fname));
+
+ ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]));
+
+ ASSERT_EQ(t_sizes[0], this->m_db->get_block_size(0));
+ ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0));
+ ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0));
+ ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0));
+
+ ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]));
+ ASSERT_EQ(t_diffs[1] - t_diffs[0], this->m_db->get_block_difficulty(1));
+
+ ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0]), this->m_db->get_block_hash_from_height(0));
+
+ std::vector<block> blks;
+ ASSERT_NO_THROW(blks = this->m_db->get_blocks_range(0, 1));
+ ASSERT_EQ(2, blks.size());
+
+ ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0]), get_block_hash(blks[0]));
+ ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1]), get_block_hash(blks[1]));
+
+ std::vector<crypto::hash> hashes;
+ ASSERT_NO_THROW(hashes = this->m_db->get_hashes_range(0, 1));
+ ASSERT_EQ(2, hashes.size());
+
+ ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0]), hashes[0]);
+ ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1]), hashes[1]);
+}
+
+} // anonymous namespace