BitShares-Core  5.0.0
BitShares blockchain implementation and command-line interface software
block_database.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cryptonomex, Inc., and contributors.
3  *
4  * The MIT License
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
26 #include <fc/io/raw.hpp>
27 #include <boost/endian/buffers.hpp>
28 
29 namespace graphene { namespace chain {
30 
32 {
34  block_pos = 0;
35  block_size = 0;
36  };
37  boost::endian::little_uint64_buf_t block_pos;
38  boost::endian::little_uint32_buf_t block_size;
40 };
41  }}
43 
44 namespace graphene { namespace chain {
45 
46 void block_database::open( const fc::path& dbdir )
47 { try {
49  _block_num_to_pos.exceptions(std::ios_base::failbit | std::ios_base::badbit);
50  _blocks.exceptions(std::ios_base::failbit | std::ios_base::badbit);
51 
52  _index_filename = dbdir / "index";
53  if( !fc::exists( _index_filename ) )
54  {
55  _block_num_to_pos.open( _index_filename.generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
56  _blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
57  }
58  else
59  {
60  _block_num_to_pos.open( _index_filename.generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
61  _blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
62  }
63 } FC_CAPTURE_AND_RETHROW( (dbdir) ) }
64 
66 {
67  return _blocks.is_open();
68 }
69 
71 {
72  _blocks.close();
73  _block_num_to_pos.close();
74 }
75 
77 {
78  _blocks.flush();
79  _block_num_to_pos.flush();
80 }
81 
82 void block_database::store( const block_id_type& _id, const signed_block& b )
83 {
84  block_id_type id = _id;
85  if( id == block_id_type() )
86  {
87  id = b.id();
88  elog( "id argument of block_database::store() was not initialized for block ${id}", ("id", id) );
89  }
90  _block_num_to_pos.seekp( sizeof( index_entry ) * int64_t(block_header::num_from_id(id)) );
91  index_entry e;
92  _blocks.seekp( 0, _blocks.end );
93  auto vec = fc::raw::pack( b );
94  e.block_pos = _blocks.tellp();
95  e.block_size = vec.size();
96  e.block_id = id;
97  _blocks.write( vec.data(), vec.size() );
98  _block_num_to_pos.write( (char*)&e, sizeof(e) );
99 }
100 
102 { try {
103  index_entry e;
104  int64_t index_pos = sizeof(e) * int64_t(block_header::num_from_id(id));
105  _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
106  if ( _block_num_to_pos.tellg() <= index_pos )
107  FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block ${id} not contained in block database", ("id", id));
108 
109  _block_num_to_pos.seekg( index_pos );
110  _block_num_to_pos.read( (char*)&e, sizeof(e) );
111 
112  if( e.block_id == id )
113  {
114  e.block_size = 0;
115  _block_num_to_pos.seekp( sizeof(e) * int64_t(block_header::num_from_id(id)) );
116  _block_num_to_pos.write( (char*)&e, sizeof(e) );
117  }
118 } FC_CAPTURE_AND_RETHROW( (id) ) }
119 
121 {
122  if( id == block_id_type() )
123  return false;
124 
125  index_entry e;
126  int64_t index_pos = sizeof(e) * int64_t(block_header::num_from_id(id));
127  _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
128  if ( _block_num_to_pos.tellg() < int64_t(index_pos + sizeof(e)) )
129  return false;
130  _block_num_to_pos.seekg( index_pos );
131  _block_num_to_pos.read( (char*)&e, sizeof(e) );
132 
133  return e.block_id == id && e.block_size.value() > 0;
134 }
135 
137 {
138  assert( block_num != 0 );
139  index_entry e;
140  int64_t index_pos = sizeof(e) * int64_t(block_num);
141  _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
142  if ( _block_num_to_pos.tellg() <= index_pos )
143  FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block number ${block_num} not contained in block database", ("block_num", block_num));
144 
145  _block_num_to_pos.seekg( index_pos );
146  _block_num_to_pos.read( (char*)&e, sizeof(e) );
147 
148  FC_ASSERT( e.block_id != block_id_type(), "Empty block_id in block_database (maybe corrupt on disk?)" );
149  return e.block_id;
150 }
151 
153 {
154  try
155  {
156  index_entry e;
157  int64_t index_pos = sizeof(e) * int64_t(block_header::num_from_id(id));
158  _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
159  if ( _block_num_to_pos.tellg() <= index_pos )
160  return {};
161 
162  _block_num_to_pos.seekg( index_pos );
163  _block_num_to_pos.read( (char*)&e, sizeof(e) );
164 
165  if( e.block_id != id ) return optional<signed_block>();
166 
167  vector<char> data( e.block_size.value() );
168  _blocks.seekg( e.block_pos.value() );
169  if (e.block_size.value())
170  _blocks.read( data.data(), e.block_size.value() );
171  auto result = fc::raw::unpack<signed_block>(data);
172  FC_ASSERT( result.id() == e.block_id );
173  return result;
174  }
175  catch (const fc::exception&)
176  {
177  }
178  catch (const std::exception&)
179  {
180  }
181  return optional<signed_block>();
182 }
183 
185 {
186  try
187  {
188  index_entry e;
189  int64_t index_pos = sizeof(e) * int64_t(block_num);
190  _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
191  if ( _block_num_to_pos.tellg() <= index_pos )
192  return {};
193 
194  _block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg );
195  _block_num_to_pos.read( (char*)&e, sizeof(e) );
196 
197  vector<char> data( e.block_size.value() );
198  _blocks.seekg( e.block_pos.value() );
199  _blocks.read( data.data(), e.block_size.value() );
200  auto result = fc::raw::unpack<signed_block>(data);
201  FC_ASSERT( result.id() == e.block_id );
202  return result;
203  }
204  catch (const fc::exception&)
205  {
206  }
207  catch (const std::exception&)
208  {
209  }
210  return optional<signed_block>();
211 }
212 
213 optional<index_entry> block_database::last_index_entry()const {
214  try
215  {
216  index_entry e;
217 
218  _block_num_to_pos.seekg( 0, _block_num_to_pos.end );
219  std::streampos pos = _block_num_to_pos.tellg();
220  if( pos < long(sizeof(index_entry)) )
221  return optional<index_entry>();
222 
223  pos -= pos % sizeof(index_entry);
224 
225  _blocks.seekg( 0, _block_num_to_pos.end );
226  const std::streampos blocks_size = _blocks.tellg();
227  while( pos > 0 )
228  {
229  pos -= sizeof(index_entry);
230  _block_num_to_pos.seekg( pos );
231  _block_num_to_pos.read( (char*)&e, sizeof(e) );
232  if( _block_num_to_pos.gcount() == sizeof(e) && e.block_size.value() > 0
233  && int64_t(e.block_pos.value() + e.block_size.value()) <= blocks_size )
234  try
235  {
236  vector<char> data( e.block_size.value() );
237  _blocks.seekg( e.block_pos.value() );
238  _blocks.read( data.data(), e.block_size.value() );
239  if( _blocks.gcount() == long(e.block_size.value()) )
240  {
241  const signed_block block = fc::raw::unpack<signed_block>(data);
242  if( block.id() == e.block_id )
243  return e;
244  }
245  }
246  catch (const fc::exception&)
247  {
248  }
249  catch (const std::exception&)
250  {
251  }
252  fc::resize_file( _index_filename, pos );
253  }
254  }
255  catch (const fc::exception&)
256  {
257  }
258  catch (const std::exception&)
259  {
260  }
261  return optional<index_entry>();
262 }
263 
265 {
266  optional<index_entry> entry = last_index_entry();
267  if( entry.valid() ) return fetch_by_number( block_header::num_from_id(entry->block_id) );
268  return optional<signed_block>();
269 }
270 
272 {
273  optional<index_entry> entry = last_index_entry();
274  if( entry.valid() ) return entry->block_id;
275  return optional<block_id_type>();
276 }
277 
279 {
280  return (size_t)_blocks.tellg();
281 }
282 
284 {
285  _blocks.seekg( 0, _blocks.end );
286  return (size_t)_blocks.tellg();
287 }
288 
289 } }
bool exists(const path &p)
Definition: filesystem.cpp:209
void pack(Stream &s, const flat_set< T, A... > &value, uint32_t _max_depth)
Definition: flat.hpp:11
static uint32_t num_from_id(const block_id_type &id)
Definition: block.cpp:36
index_entry()
Definition: api.cpp:56
#define elog(FORMAT,...)
Definition: logger.hpp:129
boost::endian::little_uint64_buf_t block_pos
Used to generate a useful error report when an exception is thrown.At each level in the stack where t...
Definition: exception.hpp:56
bool valid() const
Definition: optional.hpp:186
FC_REFLECT(graphene::chain::index_entry,(block_pos)(block_size)(block_id))
void create_directories(const path &p)
Definition: filesystem.cpp:210
void open(const fc::path &dbdir)
fc::ripemd160 block_id_type
Definition: types.hpp:242
provides stack-based nullable value similar to boost::optional
Definition: optional.hpp:20
optional< block_id_type > last_id() const
block_id_type fetch_block_id(uint32_t block_num) const
block_id_type block_id
boost::endian::little_uint32_buf_t block_size
void remove(const block_id_type &id)
#define FC_CAPTURE_AND_RETHROW(...)
Definition: exception.hpp:478
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
Definition: exception.hpp:345
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
Definition: exception.hpp:378
optional< signed_block > fetch_optional(const block_id_type &id) const
bool contains(const block_id_type &id) const
void store(const block_id_type &id, const signed_block &b)
optional< signed_block > fetch_by_number(uint32_t block_num) const
const block_id_type & id() const
Definition: block.cpp:41
wraps boost::filesystem::path to provide platform independent path manipulation.
Definition: filesystem.hpp:28
optional< signed_block > last() const
void resize_file(const path &file, size_t s)
Definition: filesystem.cpp:258