Electroneum
blockchain_db.cpp
Go to the documentation of this file.
1 // Copyrights(c) 2017-2021, The Electroneum Project
2 // Copyrights(c) 2014-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include <boost/filesystem.hpp>
31 #include <boost/algorithm/string/predicate.hpp>
32 #include <cstdio>
33 #include <iostream>
34 #include <chrono>
35 #include <thread>
36 
37 #include "gtest/gtest.h"
38 
39 #include "string_tools.h"
42 #ifdef BERKELEY_DB
43 #include "blockchain_db/berkeleydb/db_bdb.h"
44 #endif
46 
47 using namespace cryptonote;
49 
50 #define ASSERT_HASH_EQ(a,b) ASSERT_EQ(pod_to_hex(a), pod_to_hex(b))
51 
52 namespace { // anonymous namespace
53 
54 const std::vector<std::string> t_blocks =
55  {
56  "0100d5adc49a053b8818b2b6023cd2d532c6774e164a8fcacd603651cb3ea0cb7f9340b28ec016b4bc4ca301aa0101ff6e08acbb2702eab03067870349139bee7eab2ca2e030a6bb73d4f68ab6a3b6ca937214054cdac0843d028bbe23b57ea9bae53f12da93bb57bf8a2e40598d9fccd10c2921576e987d93cd80b4891302468738e391f07c4f2b356f7957160968e0bfef6e907c3cee2d8c23cbf04b089680c6868f01025a0f41f063e195a966051e3a29e17130a9ce97d48f55285b9bb04bdd55a09ae78088aca3cf0202d0f26169290450fe17e08974789c3458910b4db18361cdc564f8f2d0bdd2cf568090cad2c60e02d6f3483ec45505cc3be841046c7a12bf953ac973939bc7b727e54258e1881d4d80e08d84ddcb0102dae6dfb16d3e28aaaf43e00170b90606b36f35f38f8a3dceb5ee18199dd8f17c80c0caf384a30202385d7e57a4daba4cdd9e550a92dcc188838386e7581f13f09de796cbed4716a42101c052492a077abf41996b50c1b2e67fd7288bcd8c55cdc657b4e22d0804371f6901beb76a82ea17400cd6d7f595f70e1667d2018ed8f5a78d1ce07484222618c3cd"
57  , "0100f9adc49a057d3113f562eac36f14afa08c22ae20bbbf8cffa31a4466d24850732cb96f80e9762365ee01ab0101ff6f08cc953502be76deb845c431f2ed9a4862457654b914003693b8cd672abc935f0d97b16380c08db7010291819f2873e3efbae65ecd5a736f5e8a26318b591c21e39a03fb536520ac63ba80dac40902439a10fde02e39e48e0b31e57cc084a07eedbefb8cbea0143aedd0442b189caa80c6868f010227b84449de4cd7a48cbdce8974baf0b6646e03384e32055e705c243a86bef8a58088aca3cf0202fa7bd15e4e7e884307ab130bb9d50e33c5fcea6546042a26f948efd5952459ee8090cad2c60e028695583dbb8f8faab87e3ef3f88fa827db097bbf51761d91924f5c5b74c6631780e08d84ddcb010279d2f247b54690e3b491e488acff16014a825fd740c23988a25df7c4670c1f2580c0caf384a302022599dfa3f8788b66295051d85937816e1c320cdb347a0fba5219e3fe60c83b2421010576509c5672025d28fd5d3f38efce24e1f9aaf65dd3056b2504e6e2b7f19f7800"
58  };
59 
60 const std::vector<size_t> t_sizes =
61  {
62  1122
63  , 347
64  };
65 
66 const std::vector<difficulty_type> t_diffs =
67  {
68  4003674
69  , 4051757
70  };
71 
72 const std::vector<uint64_t> t_coins =
73  {
74  1952630229575370
75  , 1970220553446486
76  };
77 
78 const std::vector<std::vector<std::string>> t_transactions =
79  {
80  {
81  "0100010280e08d84ddcb0106010401110701f254220bb50d901a5523eaed438af5d43f8c6d0e54ba0632eb539884f6b7c02008c0a8a50402f9c7cf807ae74e56f4ec84db2bd93cfb02c2249b38e306f5b54b6e05d00d543b8095f52a02b6abb84e00f47f0a72e37b6b29392d906a38468404c57db3dbc5e8dd306a27a880d293ad0302cfc40a86723e7d459e90e45d47818dc0e81a1f451ace5137a4af8110a89a35ea80b4c4c321026b19c796338607d5a2c1ba240a167134142d72d1640ef07902da64fed0b10cfc8088aca3cf02021f6f655254fee84161118b32e7b6f8c31de5eb88aa00c29a8f57c0d1f95a24dd80d0b8e1981a023321af593163cea2ae37168ab926efd87f195756e3b723e886bdb7e618f751c480a094a58d1d0295ed2b08d1cf44482ae0060a5dcc4b7d810a85dea8c62e274f73862f3d59f8ed80a0e5b9c2910102dc50f2f28d7ceecd9a1147f7106c8d5b4e08b2ec77150f52dd7130ee4f5f50d42101d34f90ac861d0ee9fe3891656a234ea86a8a93bf51a237db65baa00d3f4aa196a9e1d89bc06b40e94ea9a26059efc7ba5b2de7ef7c139831ca62f3fe0bb252008f8c7ee810d3e1e06313edf2db362fc39431755779466b635f12f9f32e44470a3e85e08a28fcd90633efc94aa4ae39153dfaf661089d045521343a3d63e8da08d7916753c66aaebd4eefcfe8e58e5b3d266b752c9ca110749fa33fce7c44270386fcf2bed4f03dd5dadb2dc1fd4c505419f8217b9eaec07521f0d8963e104603c926745039cf38d31de6ed95ace8e8a451f5a36f818c151f517546d55ac0f500e54d07b30ea7452f2e93fa4f60bdb30d71a0a97f97eb121e662006780fbf69002228224a96bff37893d47ec3707b17383906c0cd7d9e7412b3e6c8ccf1419b093c06c26f96e3453b424713cdc5c9575f81cda4e157052df11f4c40809edf420f88a3dd1f7909bbf77c8b184a933389094a88e480e900bcdbf6d1824742ee520fc0032e7d892a2b099b8c6edfd1123ce58a34458ee20cad676a7f7cfd80a28f0cb0888af88838310db372986bdcf9bfcae2324480ca7360d22bff21fb569a530e"
82  }
83  , {
84  }
85  };
86 
87 // if the return type (blobdata for now) of block_to_blob ever changes
88 // from std::string, this might break.
89 bool compare_blocks(const block& a, const block& b)
90 {
91  auto hash_a = pod_to_hex(get_block_hash(a));
92  auto hash_b = pod_to_hex(get_block_hash(b));
93 
94  return hash_a == hash_b;
95 }
96 
97 /*
98 void print_block(const block& blk, const std::string& prefix = "")
99 {
100  std::cerr << prefix << ": " << std::endl
101  << "\thash - " << pod_to_hex(get_block_hash(blk)) << std::endl
102  << "\tparent - " << pod_to_hex(blk.prev_id) << std::endl
103  << "\ttimestamp - " << blk.timestamp << std::endl
104  ;
105 }
106 
107 // if the return type (blobdata for now) of tx_to_blob ever changes
108 // from std::string, this might break.
109 bool compare_txs(const transaction& a, const transaction& b)
110 {
111  auto ab = tx_to_blob(a);
112  auto bb = tx_to_blob(b);
113 
114  return ab == bb;
115 }
116 */
117 
118 // convert hex string to string that has values based on that hex
119 // thankfully should automatically ignore null-terminator.
120 std::string h2b(const std::string& s)
121 {
122  bool upper = true;
123  std::string result;
124  unsigned char val = 0;
125  for (char c : s)
126  {
127  if (upper)
128  {
129  val = 0;
130  if (c <= 'f' && c >= 'a')
131  {
132  val = ((c - 'a') + 10) << 4;
133  }
134  else
135  {
136  val = (c - '0') << 4;
137  }
138  }
139  else
140  {
141  if (c <= 'f' && c >= 'a')
142  {
143  val |= (c - 'a') + 10;
144  }
145  else
146  {
147  val |= c - '0';
148  }
149  result += (char)val;
150  }
151  upper = !upper;
152  }
153  return result;
154 }
155 
156 template <typename T>
157 class BlockchainDBTest : public testing::Test
158 {
159 protected:
160  BlockchainDBTest() : m_db(new T()), m_hardfork(*m_db, 1, 0)
161  {
162  for (auto& i : t_blocks)
163  {
164  block bl;
165  blobdata bd = h2b(i);
167  m_blocks.push_back(std::make_pair(bl, bd));
168  }
169  for (auto& i : t_transactions)
170  {
171  std::vector<std::pair<transaction, blobdata>> txs;
172  for (auto& j : i)
173  {
174  transaction tx;
175  blobdata bd = h2b(j);
177  txs.push_back(std::make_pair(tx, bd));
178  }
179  m_txs.push_back(txs);
180  }
181  }
182 
183  ~BlockchainDBTest() {
184  delete m_db;
185  remove_files();
186  }
187 
188  BlockchainDB* m_db;
189  HardFork m_hardfork;
190  std::string m_prefix;
191  std::vector<std::pair<block, blobdata>> m_blocks;
192  std::vector<std::vector<std::pair<transaction, blobdata>>> m_txs;
193  std::vector<std::string> m_filenames;
194 
195  void init_hard_fork()
196  {
197  m_hardfork.init();
198  m_db->set_hard_fork(&m_hardfork);
199  }
200 
201  void get_filenames()
202  {
203  m_filenames = m_db->get_filenames();
204  for (auto& f : m_filenames)
205  {
206  std::cerr << "File created by test: " << f << std::endl;
207  }
208  }
209 
210  void remove_files()
211  {
212  // remove each file the db created, making sure it starts with fname.
213  for (auto& f : m_filenames)
214  {
215  if (boost::starts_with(f, m_prefix))
216  {
217  boost::filesystem::remove(f);
218  }
219  else
220  {
221  std::cerr << "File created by test not to be removed (for safety): " << f << std::endl;
222  }
223  }
224 
225  // remove directory if it still exists
226  boost::filesystem::remove_all(m_prefix);
227  }
228 
229  void set_prefix(const std::string& prefix)
230  {
231  m_prefix = prefix;
232  }
233 };
234 
235 using testing::Types;
236 
237 typedef Types<BlockchainLMDB
238 #ifdef BERKELEY_DB
239  , BlockchainBDB
240 #endif
241 > implementations;
242 
243 TYPED_TEST_CASE(BlockchainDBTest, implementations);
244 
245 TYPED_TEST(BlockchainDBTest, OpenAndClose)
246 {
247  boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
248  std::string dirPath = tempPath.string();
249 
250  this->set_prefix(dirPath);
251 
252  // make sure open does not throw
253  ASSERT_NO_THROW(this->m_db->open(dirPath));
254  this->get_filenames();
255 
256  // make sure open when already open DOES throw
257  ASSERT_THROW(this->m_db->open(dirPath), DB_OPEN_FAILURE);
258 
259  ASSERT_NO_THROW(this->m_db->close());
260 }
261 
262 TYPED_TEST(BlockchainDBTest, AddBlock)
263 {
264 
265  boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
266  std::string dirPath = tempPath.string();
267 
268  this->set_prefix(dirPath);
269 
270  // make sure open does not throw
271  ASSERT_NO_THROW(this->m_db->open(dirPath));
272  this->get_filenames();
273  this->init_hard_fork();
274 
275  db_wtxn_guard guard(this->m_db);
276 
277  // adding a block with no parent in the blockchain should throw.
278  // note: this shouldn't be possible, but is a good (and cheap) failsafe.
279  //
280  // TODO: need at least one more block to make this reasonable, as the
281  // BlockchainDB implementation should not check for parent if
282  // no blocks have been added yet (because genesis has no parent).
283  //ASSERT_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]), BLOCK_PARENT_DNE);
284 
285  ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]));
286  ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]));
287 
288  block b;
289  ASSERT_TRUE(this->m_db->block_exists(get_block_hash(this->m_blocks[0].first)));
290  ASSERT_NO_THROW(b = this->m_db->get_block(get_block_hash(this->m_blocks[0].first)));
291 
292  ASSERT_TRUE(compare_blocks(this->m_blocks[0].first, b));
293 
294  ASSERT_NO_THROW(b = this->m_db->get_block_from_height(0));
295 
296  ASSERT_TRUE(compare_blocks(this->m_blocks[0].first, b));
297 
298  // assert that we can't add the same block twice
299  ASSERT_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]), TX_EXISTS);
300 
301  for (auto& h : this->m_blocks[0].first.tx_hashes)
302  {
303  transaction tx;
304  ASSERT_TRUE(this->m_db->tx_exists(h));
305  ASSERT_NO_THROW(tx = this->m_db->get_tx(h));
306 
308  }
309 }
310 
311 TYPED_TEST(BlockchainDBTest, RetrieveBlockData)
312 {
313  boost::filesystem::path tempPath = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
314  std::string dirPath = tempPath.string();
315 
316  this->set_prefix(dirPath);
317 
318  // make sure open does not throw
319  ASSERT_NO_THROW(this->m_db->open(dirPath));
320  this->get_filenames();
321  this->init_hard_fork();
322 
323  db_wtxn_guard guard(this->m_db);
324 
325  ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[0], t_sizes[0], t_sizes[0], t_diffs[0], t_coins[0], this->m_txs[0]));
326 
327  ASSERT_EQ(t_sizes[0], this->m_db->get_block_weight(0));
328  ASSERT_EQ(t_diffs[0], this->m_db->get_block_cumulative_difficulty(0));
329  ASSERT_EQ(t_diffs[0], this->m_db->get_block_difficulty(0));
330  ASSERT_EQ(t_coins[0], this->m_db->get_block_already_generated_coins(0));
331 
332  ASSERT_NO_THROW(this->m_db->add_block(this->m_blocks[1], t_sizes[1], t_sizes[1], t_diffs[1], t_coins[1], this->m_txs[1]));
333  ASSERT_EQ(t_diffs[1] - t_diffs[0], this->m_db->get_block_difficulty(1));
334 
335  ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), this->m_db->get_block_hash_from_height(0));
336 
337  std::vector<block> blks;
338  ASSERT_NO_THROW(blks = this->m_db->get_blocks_range(0, 1));
339  ASSERT_EQ(2, blks.size());
340 
341  ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), get_block_hash(blks[0]));
342  ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1].first), get_block_hash(blks[1]));
343 
344  std::vector<crypto::hash> hashes;
345  ASSERT_NO_THROW(hashes = this->m_db->get_hashes_range(0, 1));
346  ASSERT_EQ(2, hashes.size());
347 
348  ASSERT_HASH_EQ(get_block_hash(this->m_blocks[0].first), hashes[0]);
349  ASSERT_HASH_EQ(get_block_hash(this->m_blocks[1].first), hashes[1]);
350 }
351 
352 } // anonymous namespace
const uint32_t T[512]
::std::string string
Definition: gtest-port.h:1097
thrown when a transaction exists, but shouldn&#39;t, namely when adding a block
void init()
initialize the object
Definition: hardfork.cpp:169
virtual std::vector< std::string > get_filenames() const =0
get all files used by the BlockchainDB (if any)
struct hash_func hashes[]
Holds cryptonote related classes and helpers.
Definition: ban.cpp:40
#define ASSERT_EQ(val1, val2)
Definition: gtest.h:1956
std::string pod_to_hex(const t_pod_type &s)
Definition: string_tools.h:317
bool get_block_hash(const block &b, crypto::hash &res)
#define ASSERT_NO_THROW(statement)
Definition: gtest.h:1851
#define ASSERT_THROW(statement, expected_exception)
Definition: gtest.h:1849
bool parse_and_validate_tx_from_blob(const blobdata &tx_blob, transaction &tx)
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1124
virtual void set_hard_fork(HardFork *hf)
std::string blobdata
Definition: blobdatatype.h:39
The BlockchainDB backing store interface declaration/contract.
#define ASSERT_TRUE(condition)
Definition: gtest.h:1865
crypto::hash get_transaction_hash(const transaction &t)
thrown when opening the BlockchainDB fails
#define ASSERT_HASH_EQ(a, b)
void h2b(bits amountb2, const key &test)
Definition: rctTypes.cpp:171
bool parse_and_validate_block_from_blob(const blobdata &b_blob, block &b, crypto::hash *block_hash)