2015-06-17 16:33:38 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2015, Cryptonomex, Inc.
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* This source code is provided for evaluation in private test networks only, until September 8, 2015. After this date, this license expires and
|
|
|
|
|
* the code may not be used, modified or distributed for any purpose. Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
|
* are permitted until September 8, 2015, provided that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* 1. The code and/or derivative works are used only for private test networks consisting of no more than 10 P2P nodes.
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2015-06-16 19:56:13 +00:00
|
|
|
#include <graphene/chain/block_database.hpp>
|
2015-07-08 20:39:23 +00:00
|
|
|
#include <graphene/chain/protocol/fee_schedule.hpp>
|
|
|
|
|
#include <fc/io/raw.hpp>
|
2015-07-20 18:57:08 +00:00
|
|
|
#include <fc/smart_ref_impl.hpp>
|
2015-06-16 19:56:13 +00:00
|
|
|
|
|
|
|
|
namespace graphene { namespace chain {
|
|
|
|
|
|
|
|
|
|
struct index_entry
|
|
|
|
|
{
|
|
|
|
|
uint64_t block_pos = 0;
|
|
|
|
|
uint32_t block_size = 0;
|
|
|
|
|
block_id_type block_id;
|
|
|
|
|
};
|
2015-06-17 16:33:38 +00:00
|
|
|
}}
|
2015-06-16 22:45:33 +00:00
|
|
|
FC_REFLECT( graphene::chain::index_entry, (block_pos)(block_size)(block_id) );
|
|
|
|
|
|
|
|
|
|
namespace graphene { namespace chain {
|
2015-06-16 19:56:13 +00:00
|
|
|
|
|
|
|
|
void block_database::open( const fc::path& dbdir )
|
2015-06-16 22:45:33 +00:00
|
|
|
{ try {
|
|
|
|
|
fc::create_directories(dbdir);
|
|
|
|
|
_block_num_to_pos.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
|
|
|
|
_blocks.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
|
|
|
|
|
|
|
|
|
if( !fc::exists( dbdir/"index" ) )
|
|
|
|
|
{
|
|
|
|
|
_block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
|
|
|
|
|
_blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out | std::fstream::trunc);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_block_num_to_pos.open( (dbdir/"index").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
|
|
|
|
|
_blocks.open( (dbdir/"blocks").generic_string().c_str(), std::fstream::binary | std::fstream::in | std::fstream::out );
|
|
|
|
|
}
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (dbdir) ) }
|
2015-06-16 19:56:13 +00:00
|
|
|
|
|
|
|
|
bool block_database::is_open()const
|
|
|
|
|
{
|
|
|
|
|
return _blocks.is_open();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void block_database::close()
|
|
|
|
|
{
|
|
|
|
|
_blocks.close();
|
|
|
|
|
_block_num_to_pos.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void block_database::flush()
|
|
|
|
|
{
|
|
|
|
|
_blocks.flush();
|
|
|
|
|
_block_num_to_pos.flush();
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-21 17:16:17 +00:00
|
|
|
void block_database::store( const block_id_type& _id, const signed_block& b )
|
2015-06-16 19:56:13 +00:00
|
|
|
{
|
2015-08-21 17:16:17 +00:00
|
|
|
block_id_type id = _id;
|
|
|
|
|
if( id == block_id_type() )
|
|
|
|
|
{
|
|
|
|
|
id = b.id();
|
|
|
|
|
elog( "id argument of block_database::store() was not initialized for block ${id}", ("id", id) );
|
|
|
|
|
}
|
2015-06-16 19:56:13 +00:00
|
|
|
auto num = block_header::num_from_id(id);
|
|
|
|
|
_block_num_to_pos.seekp( sizeof( index_entry ) * num );
|
|
|
|
|
index_entry e;
|
|
|
|
|
_blocks.seekp( 0, _blocks.end );
|
|
|
|
|
auto vec = fc::raw::pack( b );
|
|
|
|
|
e.block_pos = _blocks.tellp();
|
|
|
|
|
e.block_size = vec.size();
|
|
|
|
|
e.block_id = id;
|
|
|
|
|
_blocks.write( vec.data(), vec.size() );
|
|
|
|
|
_block_num_to_pos.write( (char*)&e, sizeof(e) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void block_database::remove( const block_id_type& id )
|
2015-06-16 22:45:33 +00:00
|
|
|
{ try {
|
2015-06-16 19:56:13 +00:00
|
|
|
index_entry e;
|
|
|
|
|
auto index_pos = sizeof(e)*block_header::num_from_id(id);
|
|
|
|
|
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
2015-06-25 14:33:39 +00:00
|
|
|
if ( _block_num_to_pos.tellg() <= index_pos )
|
|
|
|
|
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block ${id} not contained in block database", ("id", id));
|
2015-06-16 19:56:13 +00:00
|
|
|
|
|
|
|
|
_block_num_to_pos.seekg( index_pos );
|
|
|
|
|
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
|
|
|
|
|
2015-06-16 22:45:33 +00:00
|
|
|
if( e.block_id == id )
|
|
|
|
|
{
|
|
|
|
|
e.block_size = 0;
|
|
|
|
|
_block_num_to_pos.seekp( sizeof(e)*block_header::num_from_id(id) );
|
|
|
|
|
_block_num_to_pos.write( (char*)&e, sizeof(e) );
|
|
|
|
|
}
|
|
|
|
|
} FC_CAPTURE_AND_RETHROW( (id) ) }
|
2015-06-16 19:56:13 +00:00
|
|
|
|
2015-06-30 20:42:41 +00:00
|
|
|
bool block_database::contains( const block_id_type& id )const
|
2015-06-16 19:56:13 +00:00
|
|
|
{
|
2015-08-21 17:18:47 +00:00
|
|
|
if( id == block_id_type() )
|
|
|
|
|
return false;
|
|
|
|
|
|
2015-06-16 19:56:13 +00:00
|
|
|
index_entry e;
|
|
|
|
|
auto index_pos = sizeof(e)*block_header::num_from_id(id);
|
|
|
|
|
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
2015-06-25 14:33:39 +00:00
|
|
|
if ( _block_num_to_pos.tellg() <= index_pos )
|
2015-06-30 20:42:41 +00:00
|
|
|
return false;
|
2015-06-16 19:56:13 +00:00
|
|
|
_block_num_to_pos.seekg( index_pos );
|
|
|
|
|
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
|
|
|
|
|
|
|
|
|
return e.block_id == id;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-30 20:42:41 +00:00
|
|
|
block_id_type block_database::fetch_block_id( uint32_t block_num )const
|
2015-06-16 19:56:13 +00:00
|
|
|
{
|
2015-08-21 17:16:17 +00:00
|
|
|
assert( block_num != 0 );
|
2015-06-16 19:56:13 +00:00
|
|
|
index_entry e;
|
|
|
|
|
auto index_pos = sizeof(e)*block_num;
|
|
|
|
|
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
2015-06-25 14:33:39 +00:00
|
|
|
if ( _block_num_to_pos.tellg() <= index_pos )
|
|
|
|
|
FC_THROW_EXCEPTION(fc::key_not_found_exception, "Block number ${block_num} not contained in block database", ("block_num", block_num));
|
2015-06-16 19:56:13 +00:00
|
|
|
|
|
|
|
|
_block_num_to_pos.seekg( index_pos );
|
|
|
|
|
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
|
|
|
|
|
2015-08-21 17:16:17 +00:00
|
|
|
FC_ASSERT( e.block_id != block_id_type(), "Empty block_id in block_database (maybe corrupt on disk?)" );
|
2015-06-16 19:56:13 +00:00
|
|
|
return e.block_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
optional<signed_block> block_database::fetch_optional( const block_id_type& id )const
|
2015-06-18 18:47:53 +00:00
|
|
|
{
|
2015-06-30 20:42:41 +00:00
|
|
|
try
|
2015-06-18 18:47:53 +00:00
|
|
|
{
|
|
|
|
|
index_entry e;
|
|
|
|
|
auto index_pos = sizeof(e)*block_header::num_from_id(id);
|
|
|
|
|
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
2015-06-25 14:33:39 +00:00
|
|
|
if ( _block_num_to_pos.tellg() <= index_pos )
|
2015-06-30 20:42:41 +00:00
|
|
|
return {};
|
|
|
|
|
|
2015-06-18 18:47:53 +00:00
|
|
|
_block_num_to_pos.seekg( index_pos );
|
|
|
|
|
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
2015-06-30 20:42:41 +00:00
|
|
|
|
2015-06-18 18:47:53 +00:00
|
|
|
if( e.block_id != id ) return optional<signed_block>();
|
2015-06-30 20:42:41 +00:00
|
|
|
|
2015-06-18 18:47:53 +00:00
|
|
|
vector<char> data( e.block_size );
|
|
|
|
|
_blocks.seekg( e.block_pos );
|
2015-08-21 23:53:35 +00:00
|
|
|
if (e.block_size)
|
|
|
|
|
_blocks.read( data.data(), e.block_size );
|
2015-06-18 18:47:53 +00:00
|
|
|
auto result = fc::raw::unpack<signed_block>(data);
|
|
|
|
|
FC_ASSERT( result.id() == e.block_id );
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
catch (const fc::exception&)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception&)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
return optional<signed_block>();
|
|
|
|
|
}
|
2015-06-16 19:56:13 +00:00
|
|
|
|
|
|
|
|
optional<signed_block> block_database::fetch_by_number( uint32_t block_num )const
|
2015-06-18 18:47:53 +00:00
|
|
|
{
|
2015-06-30 20:42:41 +00:00
|
|
|
try
|
2015-06-18 18:47:53 +00:00
|
|
|
{
|
|
|
|
|
index_entry e;
|
|
|
|
|
auto index_pos = sizeof(e)*block_num;
|
|
|
|
|
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
2015-06-25 14:33:39 +00:00
|
|
|
if ( _block_num_to_pos.tellg() <= index_pos )
|
2015-06-30 20:42:41 +00:00
|
|
|
return {};
|
2015-06-16 19:56:13 +00:00
|
|
|
|
2015-06-18 18:47:53 +00:00
|
|
|
_block_num_to_pos.seekg( index_pos, _block_num_to_pos.beg );
|
|
|
|
|
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
2015-06-16 19:56:13 +00:00
|
|
|
|
2015-06-18 18:47:53 +00:00
|
|
|
vector<char> data( e.block_size );
|
|
|
|
|
_blocks.seekg( e.block_pos );
|
|
|
|
|
_blocks.read( data.data(), e.block_size );
|
|
|
|
|
auto result = fc::raw::unpack<signed_block>(data);
|
|
|
|
|
FC_ASSERT( result.id() == e.block_id );
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
catch (const fc::exception&)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception&)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
return optional<signed_block>();
|
|
|
|
|
}
|
2015-06-16 19:56:13 +00:00
|
|
|
|
|
|
|
|
optional<signed_block> block_database::last()const
|
|
|
|
|
{
|
2015-06-30 20:42:41 +00:00
|
|
|
try
|
2015-06-18 18:47:53 +00:00
|
|
|
{
|
|
|
|
|
index_entry e;
|
|
|
|
|
_block_num_to_pos.seekg( 0, _block_num_to_pos.end );
|
2015-06-16 19:56:13 +00:00
|
|
|
|
2015-06-18 18:47:53 +00:00
|
|
|
if( _block_num_to_pos.tellp() < sizeof(index_entry) )
|
|
|
|
|
return optional<signed_block>();
|
2015-06-16 19:56:13 +00:00
|
|
|
|
2015-06-18 18:47:53 +00:00
|
|
|
_block_num_to_pos.seekg( -sizeof(index_entry), _block_num_to_pos.end );
|
2015-06-16 22:45:33 +00:00
|
|
|
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
2015-09-03 21:42:52 +00:00
|
|
|
uint64_t pos = _block_num_to_pos.tellg();
|
|
|
|
|
while( e.block_size == 0 && pos > 0 )
|
2015-06-18 18:47:53 +00:00
|
|
|
{
|
2015-09-03 21:42:52 +00:00
|
|
|
pos -= sizeof(index_entry);
|
|
|
|
|
_block_num_to_pos.seekg( pos );
|
2015-06-18 18:47:53 +00:00
|
|
|
_block_num_to_pos.read( (char*)&e, sizeof(e) );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( e.block_size == 0 )
|
|
|
|
|
return optional<signed_block>();
|
|
|
|
|
|
|
|
|
|
vector<char> data( e.block_size );
|
|
|
|
|
_blocks.seekg( e.block_pos );
|
|
|
|
|
_blocks.read( data.data(), e.block_size );
|
|
|
|
|
auto result = fc::raw::unpack<signed_block>(data);
|
|
|
|
|
return result;
|
2015-06-16 22:45:33 +00:00
|
|
|
}
|
2015-06-18 18:47:53 +00:00
|
|
|
catch (const fc::exception&)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
catch (const std::exception&)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
return optional<signed_block>();
|
2015-06-16 19:56:13 +00:00
|
|
|
}
|
|
|
|
|
} }
|