peerplays_migrated/libraries/db/undo_database.cpp
pbattu123 0b280882af
Merge beatrice(GPOS changes) with master (#270)
* Created unit test for #325

* remove needless find()

* issue - 154: Don't allow to vote when vesting balance is 0

* Increase block creation timeout to 2500ms

* increase delay for node connection

* remove cache from cli get_account

* add cli tests framework

* Adjust newly merged code to new API

* Merged changes from Bitshares PR 1036

* GRPH-76 - Short-cut long sequences of missed blocks

Fixes database::update_global_dynamic_data to speed up counting missed blocks.
(This also fixes a minor issue with counting - the previous algorithm would skip missed blocks for the witness who signed the first block after the gap.)

* Improved resilience of block database against corruption

* Moved reindex logic into database / chain_database, make use of additional blocks in block_database

Fixed tests wrt db.open

* Enable undo + fork database for final blocks in a replay

Dont remove blocks from block db when popping blocks, handle edge case in replay wrt fork_db, adapted unit tests

* Log starting block number of replay

* Prevent unsigned integer underflow

* Fixed lock detection

* Dont leave _data_dir empty if db is locked

* Writing the object_database is now almost atomic

* Improved consistency check for block_log

* Cut back block_log index file if inconsistent

* Fixed undo_database

* Added test case for broken merge on empty undo_db

* exclude second undo_db.enable() call in some cases

* Add missing change

* change bitshares to core in message

* Merge pull request #938 from bitshares/fix-block-storing

Store correct block ID when switching forks

* Fixed integer overflow issue

* Fix for for history ID mismatch ( Bitshares PR #875 )

* Update the FC submodule with the changes for GRPH-4

* Merged Bitshares PR #1462 and compilation fixes

* Support/gitlab (#123)

* Updated gitlab process

* Fix undefined references in cli test

* Updated GitLab CI

* Fix #436 object_database created outside of witness data directory

* supplement more comments on database::_opened variable

* prevent segfault when destructing application obj

* Fixed test failures and compilation issue

* minor performance improvement

* Added comment

* Fix compilation in debug mode

* Fixed duplicate ops returned from get_account_history

* Fixed account_history_pagination test

* Removed unrelated comment

* Update to fixed version of fc

* Skip auth check when pushing self-generated blocks

* Extract public keys before pushing a transaction

* Dereference chain_database shared_ptr

* Updated transaction::signees to mutable

and
* updated get_signature_keys() to return a const reference,
* get_signature_keys() will update signees on first call,
* modified test cases and wallet.cpp accordingly,
* no longer construct a new signed_transaction object before pushing

* Added get_asset_count API

* No longer extract public keys before pushing a trx

and removed unused new added constructor and _get_signature_keys() function from signed_transaction struct

* changes to withdraw_vesting feature(for both cdd and GPOS)

* Comments update

* update to GPOS hardfork ref

* Remove leftover comment from merge

* fix for get_vesting_balance API call

* braces update

* Allow sufficient space for new undo_session

* Throw for deep nesting

* node.cpp: Check the attacker/buggy client before updating items ids

The peer is an attacker or buggy, which means the item_hashes_received is
not correct.

Move the check before updating items ids to save some time in this case.

* Create .gitlab-ci.yml

* Added cli_test to CI

* fixing build errors (#150)

* fixing build errors

vest type correction

* fixing build errors

vest type correction

* fixes 

new Dockerfile

* vesting_balance_type correction

vesting_balance_type changed to normal

* gcc5 support to Dockerfile

gcc5 support to Dockerfile

* use random port numbers in app_test (#154)

* Changes to compiple with GCC 7(Ubuntu 18.04)

* proposal fail_reason bug fixed (#157)

* Added Sonarcloud code_quality to CI (#159)

* Added sonarcloud analysis (#158)

* changes to have separate methods and single withdrawl fee for multiple vest objects

* 163-fix, Return only non-zero vesting balances

* Support/gitlab develop (#168)

* Added code_quality to CI

* Update .gitlab-ci.yml

* Point to PBSA/peerplays-fc commit f13d063 (#167)

* [GRPH-3] Additional cli tests (#155)

* Additional cli tests

* Compatible with latest fc changes

* Fixed Spacing issues

* [GRPH-106] Added voting tests (#136)

* Added more voting tests

* Added additional option

* Adjust p2p log level (#180)

* merge gpos to develop (#186)

* issue - 154: Don't allow to vote when vesting balance is 0

* changes to withdraw_vesting feature(for both cdd and GPOS)

* Comments update

* update to GPOS hardfork ref

* fix for get_vesting_balance API call

* braces update

* Create .gitlab-ci.yml

* fixing build errors (#150)

* fixing build errors

vest type correction

* fixing build errors

vest type correction

* fixes 

new Dockerfile

* vesting_balance_type correction

vesting_balance_type changed to normal

* gcc5 support to Dockerfile

gcc5 support to Dockerfile

* Changes to compiple with GCC 7(Ubuntu 18.04)

* changes to have separate methods and single withdrawl fee for multiple vest objects

* 163-fix, Return only non-zero vesting balances

* Revert "Revert "GPOS protocol""

This reverts commit 67616417b7.

* add new line needed to gpos hardfork file

* comment temporally cli_vote_for_2_witnesses until refactor or delete

* fix gpos tests

* fix gitlab-ci conflict

* Fixed few error messages

* error message corrections at other places

* Updated FC repository to peerplays-network/peerplays-fc (#189)

Point to fc commit hash 6096e94 [latest-fc branch]

* Project name update in Doxyfile (#146)

* changes to allow user to vote in each sub-period

* Fixed GPOS vesting factor issue when proxy is set

* Added unit test for proxy voting

* Review changes

* changes to update last voting time

* resolve merge conflict

* unit test changes and also separated GPOS test suite

* delete unused variables

* removed witness check

* eliminate time gap between two consecutive vesting periods

* deleted GPOS specific test suite and updated gpos tests

* updated GPOS hf

* Fixed dividend distribution issue and added test case

* fix flag

* clean newlines gpos_tests

* adapt gpos_tests to changed flag

* Fix to roll in GPOS rules, carry votes from 6th sub-period

* check was already modified

* comments updated

* updated comments to the benefit of reviewer

* Added token symbol name in error messages

* Added token symbol name in error messages (#204)

* case 1: Fixed last voting time issue

* get_account bug fixed

* Fixed flag issue

* Fixed spelling issue

* remove non needed gcc5 changes to dockerfile

* GRPH134- High CPU Issue, websocket changes (#213)

* update submodule branch to refer to the latest commit on latest-fc branch (#214)

* Improve account maintenance performance (#130)

* Improve account maintenance performance

* merge fixes

* Fixed merge issue

* Fixed indentations and extra ';'

* Update CI for syncing gitmodules (#216)

* Added logging for the old update_expired_feeds bug

The old bug is https://github.com/cryptonomex/graphene/issues/615 .

Due to the bug, `update_median_feeds()` and `check_call_orders()`
will be called when a feed is not actually expired, normally this
should not affect consensus since calling them should not change
any data in the state.

However, the logging indicates that `check_call_orders()` did
change some data under certain circumstances, specifically, when
multiple limit order matching issue (#453) occurred at same block.
* https://github.com/bitshares/bitshares-core/issues/453

* Minor performance improvement for price::is_null()

* Use static refs in db_getter for immutable objects

* Minor performance improvement for db_maint

* Minor code updates for asset_evaluator.cpp

* changed an `assert()` to `FC_ASSERT()`
* replaced one `db.get(asset_id_type())` with `db.get_core_asset()`
* capture only required variables for lambda

* Improve update_expired_feeds performance #1093

* Change static refs to member pointers of db class

* Added getter for witness schedule object

* Added getter for core dynamic data object

* Use getters

* Removed unused variable

* Add comments for update_expired_feeds in db_block

* Minor refactory asset_create_evaluator::do_apply()

* Added FC_ASSERT for dynamic data id of core asset

* Added header inclusions in db_management.cpp

* fix global objects usage during replay

* Logging config parsing issue

* added new files

* compilation fix

* Simplified code in database::pay_workers()

* issue with withdrawl

* Added unit test for empty account history

* set extensions default values

* Update GPOS hardfork date and don't allow GPOS features before hardfork time

* refer to latest commit of latest-fc branch (#224)

* account name or id support in all database api

* asset id or name support in all asset APIs

* Fixed compilation issues

* Fixed alignment issues

* Externalized some API templates

* Externalize serialization of blocks, tx, ops

* Externalized db objects

* Externalized genesis serialization

* Externalized serialization in protocol library

* Undo superfluous change

* remove default value for extension parameter

* fix compilation issues

* GRPH-46-Quit_command_cliwallet

* removed multiple function definition

* Fixed chainparameter update proposal issue

* Move GPOS withdraw logic to have single transaction(also single fee) and update API

* Added log for authorization failure of proposal operations

* Votes consideration on GPOS activation

* bump fc version

* fix gpos tests

* Bump fc version

* Updated gpos/voting_tests

* Fixed withdraw vesting bug

* Added unit test

* Update hardfork date for TESTNET, sync fc module and update logs

* avoid wlog as it filling up space

* Beatrice hot fix(sync issue fix)

* gpos tests fix

* Set hardfork date to Jan5th on TESTNET

Co-authored-by: Peter Conrad <github.com@quisquis.de>
Co-authored-by: John M. Jones <jmjatlanta@gmail.com>
Co-authored-by: obucinac <obucinac@users.noreply.github.com>
Co-authored-by: Bobinson K B <bobinson@gmail.com>
Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
Co-authored-by: Miha Čančula <miha@noughmad.eu>
Co-authored-by: Abit <abitmore@users.noreply.github.com>
Co-authored-by: Roshan Syed <r.syed@pbsa.info>
Co-authored-by: Sandip Patel <sandip@knackroot.com>
Co-authored-by: RichardWeiYang <richard.weiyang@gmail.com>
Co-authored-by: gladcow <jahr@yandex.ru>
Co-authored-by: satyakoneru <satyakoneru.iiith@gmail.com>
2020-02-07 21:23:08 +05:30

294 lines
9.9 KiB
C++

/*
* Copyright (c) 2015 Cryptonomex, Inc., and contributors.
*
* The MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <graphene/db/object_database.hpp>
#include <graphene/db/undo_database.hpp>
#include <fc/reflect/variant.hpp>
namespace graphene { namespace db {
void undo_database::enable() { _disabled = false; }
void undo_database::disable() { _disabled = true; }
undo_database::session undo_database::start_undo_session( bool force_enable )
{
if( _disabled && !force_enable ) return session(*this);
bool disable_on_exit = _disabled && force_enable;
if( force_enable )
_disabled = false;
while( size() > max_size() )
_stack.pop_front();
_stack.emplace_back();
++_active_sessions;
return session(*this, disable_on_exit );
}
void undo_database::on_create( const object& obj )
{
if( _disabled ) return;
if( _stack.empty() )
_stack.emplace_back();
auto& state = _stack.back();
auto index_id = object_id_type( obj.id.space(), obj.id.type(), 0 );
auto itr = state.old_index_next_ids.find( index_id );
if( itr == state.old_index_next_ids.end() )
state.old_index_next_ids[index_id] = obj.id;
state.new_ids.insert(obj.id);
}
void undo_database::on_modify( const object& obj )
{
if( _disabled ) return;
if( _stack.empty() )
_stack.emplace_back();
auto& state = _stack.back();
if( state.new_ids.find(obj.id) != state.new_ids.end() )
return;
auto itr = state.old_values.find(obj.id);
if( itr != state.old_values.end() ) return;
state.old_values[obj.id] = obj.clone();
}
void undo_database::on_remove( const object& obj )
{
if( _disabled ) return;
if( _stack.empty() )
_stack.emplace_back();
undo_state& state = _stack.back();
if( state.new_ids.count(obj.id) )
{
state.new_ids.erase(obj.id);
return;
}
if( state.old_values.count(obj.id) )
{
state.removed[obj.id] = std::move(state.old_values[obj.id]);
state.old_values.erase(obj.id);
return;
}
if( state.removed.count(obj.id) ) return;
state.removed[obj.id] = obj.clone();
}
void undo_database::undo()
{ try {
FC_ASSERT( !_disabled );
FC_ASSERT( _active_sessions > 0 );
disable();
auto& state = _stack.back();
for( auto& item : state.old_values )
{
_db.modify( _db.get_object( item.second->id ), [&]( object& obj ){ obj.move_from( *item.second ); } );
}
for( auto ritr = state.new_ids.begin(); ritr != state.new_ids.end(); ++ritr )
{
_db.remove( _db.get_object(*ritr) );
}
for( auto& item : state.old_index_next_ids )
{
_db.get_mutable_index( item.first.space(), item.first.type() ).set_next_id( item.second );
}
for( auto& item : state.removed )
_db.insert( std::move(*item.second) );
_stack.pop_back();
enable();
--_active_sessions;
} FC_CAPTURE_AND_RETHROW() }
void undo_database::merge()
{
FC_ASSERT( _active_sessions > 0 );
if( _active_sessions == 1 && _stack.size() == 1 )
{
_stack.pop_back();
--_active_sessions;
return;
}
FC_ASSERT( _stack.size() >=2 );
auto& state = _stack.back();
auto& prev_state = _stack[_stack.size()-2];
// An object's relationship to a state can be:
// in new_ids : new
// in old_values (was=X) : upd(was=X)
// in removed (was=X) : del(was=X)
// not in any of above : nop
//
// When merging A=prev_state and B=state we have a 4x4 matrix of all possibilities:
//
// |--------------------- B ----------------------|
//
// +------------+------------+------------+------------+
// | new | upd(was=Y) | del(was=Y) | nop |
// +------------+------------+------------+------------+------------+
// / | new | N/A | new A| nop C| new A|
// | +------------+------------+------------+------------+------------+
// | | upd(was=X) | N/A | upd(was=X)A| del(was=X)C| upd(was=X)A|
// A +------------+------------+------------+------------+------------+
// | | del(was=X) | N/A | N/A | N/A | del(was=X)A|
// | +------------+------------+------------+------------+------------+
// \ | nop | new B| upd(was=Y)B| del(was=Y)B| nop AB|
// +------------+------------+------------+------------+------------+
//
// Each entry was composed by labelling what should occur in the given case.
//
// Type A means the composition of states contains the same entry as the first of the two merged states for that object.
// Type B means the composition of states contains the same entry as the second of the two merged states for that object.
// Type C means the composition of states contains an entry different from either of the merged states for that object.
// Type N/A means the composition of states violates causal timing.
// Type AB means both type A and type B simultaneously.
//
// The merge() operation is defined as modifying prev_state in-place to be the state object which represents the composition of
// state A and B.
//
// Type A (and AB) can be implemented as a no-op; prev_state already contains the correct value for the merged state.
// Type B (and AB) can be implemented by copying from state to prev_state.
// Type C needs special case-by-case logic.
// Type N/A can be ignored or assert(false) as it can only occur if prev_state and state have illegal values
// (a serious logic error which should never happen).
//
// We can only be outside type A/AB (the nop path) if B is not nop, so it suffices to iterate through B's three containers.
// *+upd
for( auto& obj : state.old_values )
{
if( prev_state.new_ids.find(obj.second->id) != prev_state.new_ids.end() )
{
// new+upd -> new, type A
continue;
}
if( prev_state.old_values.find(obj.second->id) != prev_state.old_values.end() )
{
// upd(was=X) + upd(was=Y) -> upd(was=X), type A
continue;
}
// del+upd -> N/A
assert( prev_state.removed.find(obj.second->id) == prev_state.removed.end() );
// nop+upd(was=Y) -> upd(was=Y), type B
prev_state.old_values[obj.second->id] = std::move(obj.second);
}
// *+new, but we assume the N/A cases don't happen, leaving type B nop+new -> new
for( auto id : state.new_ids )
prev_state.new_ids.insert(id);
// old_index_next_ids can only be updated, iterate over *+upd cases
for( auto& item : state.old_index_next_ids )
{
if( prev_state.old_index_next_ids.find( item.first ) == prev_state.old_index_next_ids.end() )
{
// nop+upd(was=Y) -> upd(was=Y), type B
prev_state.old_index_next_ids[item.first] = item.second;
continue;
}
else
{
// upd(was=X)+upd(was=Y) -> upd(was=X), type A
// type A implementation is a no-op, as discussed above, so there is no code here
continue;
}
}
// *+del
for( auto& obj : state.removed )
{
if( prev_state.new_ids.find(obj.second->id) != prev_state.new_ids.end() )
{
// new + del -> nop (type C)
prev_state.new_ids.erase(obj.second->id);
continue;
}
auto it = prev_state.old_values.find(obj.second->id);
if( it != prev_state.old_values.end() )
{
// upd(was=X) + del(was=Y) -> del(was=X)
prev_state.removed[obj.second->id] = std::move(it->second);
prev_state.old_values.erase(obj.second->id);
continue;
}
// del + del -> N/A
assert( prev_state.removed.find( obj.second->id ) == prev_state.removed.end() );
// nop + del(was=Y) -> del(was=Y)
prev_state.removed[obj.second->id] = std::move(obj.second);
}
_stack.pop_back();
--_active_sessions;
}
void undo_database::commit()
{
FC_ASSERT( _active_sessions > 0 );
--_active_sessions;
}
void undo_database::pop_commit()
{
FC_ASSERT( _active_sessions == 0 );
FC_ASSERT( !_stack.empty() );
disable();
try {
auto& state = _stack.back();
for( auto& item : state.old_values )
{
_db.modify( _db.get_object( item.second->id ), [&]( object& obj ){ obj.move_from( *item.second ); } );
}
for( auto ritr = state.new_ids.begin(); ritr != state.new_ids.end(); ++ritr )
{
_db.remove( _db.get_object(*ritr) );
}
for( auto& item : state.old_index_next_ids )
{
_db.get_mutable_index( item.first.space(), item.first.type() ).set_next_id( item.second );
}
for( auto& item : state.removed )
_db.insert( std::move(*item.second) );
_stack.pop_back();
}
catch ( const fc::exception& e )
{
elog( "error popping commit ${e}", ("e", e.to_detail_string() ) );
enable();
throw;
}
enable();
}
const undo_state& undo_database::head()const
{
FC_ASSERT( !_stack.empty() );
return _stack.back();
}
} } // graphene::db