From 581317f7ce9ff4b2ed6305c06fd61e41006b2c8d Mon Sep 17 00:00:00 2001 From: theoreticalbts Date: Wed, 10 Aug 2016 16:11:19 -0400 Subject: [PATCH] Convert target -> difficulty #256 --- libraries/chain/database.cpp | 19 +++++++++ .../chain/include/steemit/chain/database.hpp | 2 + .../chain/protocol/steem_operations.hpp | 7 ++-- .../include/steemit/chain/protocol/types.hpp | 4 +- .../include/steemit/chain/witness_objects.hpp | 27 ++++++++++-- libraries/chain/protocol/steem_operations.cpp | 41 ++++++++++++++++-- libraries/chain/steem_evaluator.cpp | 42 +++++++++---------- libraries/plugins/witness/witness.cpp | 21 +++++++--- 8 files changed, 123 insertions(+), 40 deletions(-) diff --git a/libraries/chain/database.cpp b/libraries/chain/database.cpp index 4e33a3f3bd..2dfb64fc0c 100644 --- a/libraries/chain/database.cpp +++ b/libraries/chain/database.cpp @@ -1030,6 +1030,12 @@ fc::sha256 database::get_pow_target()const return target; } +uint32_t database::get_difficulty_target()const +{ + const dynamic_global_property_object& dgp = get_dynamic_global_properties(); + return ((dgp.num_pow_witnesses/4)+4); +} + void database::update_witness_schedule4() { vector active_witnesses; @@ -2648,6 +2654,7 @@ void database::_apply_block( const signed_block& next_block ) create_block_summary(next_block); clear_expired_transactions(); clear_expired_orders(); + clear_work_nonces(); update_witness_schedule(); update_median_feed(); @@ -3243,6 +3250,18 @@ void database::clear_expired_orders() } } +void database::clear_work_nonces() +{ + auto& idx = get_index_type().indices().get(); + while( true ) + { + auto it = idx.begin(); + if( it == idx.end() ) + break; + remove(*it); + } +} + void database::adjust_balance( const account_object& a, const asset& delta ) { modify( a, [&]( account_object& acnt ) diff --git a/libraries/chain/include/steemit/chain/database.hpp b/libraries/chain/include/steemit/chain/database.hpp index 4bbfa35a9d..33cbc71cf7 100644 --- a/libraries/chain/include/steemit/chain/database.hpp +++ b/libraries/chain/include/steemit/chain/database.hpp @@ -93,6 +93,7 @@ namespace steemit { namespace chain { bool is_known_block( const block_id_type& id )const; bool is_known_transaction( const transaction_id_type& id )const; fc::sha256 get_pow_target()const; + uint32_t get_difficulty_target()const; block_id_type get_block_id_for_num( uint32_t block_num )const; optional fetch_block_by_id( const block_id_type& id )const; optional fetch_block_by_number( uint32_t num )const; @@ -396,6 +397,7 @@ namespace steemit { namespace chain { void update_last_irreversible_block(); void clear_expired_transactions(); void clear_expired_orders(); + void clear_work_nonces(); void process_header_extensions( const signed_block& next_block ); void reset_virtual_schedule_time(); diff --git a/libraries/chain/include/steemit/chain/protocol/steem_operations.hpp b/libraries/chain/include/steemit/chain/protocol/steem_operations.hpp index e6d78f1488..e4b3f41b25 100644 --- a/libraries/chain/include/steemit/chain/protocol/steem_operations.hpp +++ b/libraries/chain/include/steemit/chain/protocol/steem_operations.hpp @@ -597,7 +597,7 @@ namespace steemit { namespace chain { string worker_account; block_id_type prev_block; uint64_t nonce = 0; - fc::sha256 work; + uint8_t difficulty = 0; void create( const block_id_type& prev_block, const string& account_name, uint64_t nonce ); void validate()const; @@ -606,11 +606,10 @@ namespace steemit { namespace chain { struct pow2_operation : public base_operation { static_variant work; - optional new_owner_key; + optional new_owner_key; chain_properties props; void validate()const; - const string& get_worker_account()const { return work.get().worker_account; } /** there is no need to verify authority, the proof of work is sufficient */ void get_required_active_authorities( flat_set& a )const { @@ -778,7 +777,7 @@ FC_REFLECT( steemit::chain::report_over_production_operation, (reporter)(first_b FC_REFLECT( steemit::chain::convert_operation, (owner)(requestid)(amount) ) FC_REFLECT( steemit::chain::feed_publish_operation, (publisher)(exchange_rate) ) FC_REFLECT( steemit::chain::pow, (worker)(input)(signature)(work) ) -FC_REFLECT( steemit::chain::pow2, (worker_account)(prev_block)(nonce)(work) ) +FC_REFLECT( steemit::chain::pow2, (worker_account)(prev_block)(nonce)(difficulty) ) FC_REFLECT( steemit::chain::chain_properties, (account_creation_fee)(maximum_block_size)(sbd_interest_rate) ); FC_REFLECT( steemit::chain::pow_operation, (worker_account)(block_id)(nonce)(work)(props) ) diff --git a/libraries/chain/include/steemit/chain/protocol/types.hpp b/libraries/chain/include/steemit/chain/protocol/types.hpp index 11bc95a414..00ecee6df3 100644 --- a/libraries/chain/include/steemit/chain/protocol/types.hpp +++ b/libraries/chain/include/steemit/chain/protocol/types.hpp @@ -118,7 +118,8 @@ namespace steemit { namespace chain { impl_owner_authority_history_object_type, impl_account_recovery_request_object_type, impl_change_recovery_account_request_object_type, - impl_escrow_object_type + impl_escrow_object_type, + impl_work_nonce_object_type }; class operation_object; @@ -302,6 +303,7 @@ FC_REFLECT_ENUM( steemit::chain::impl_object_type, (impl_account_recovery_request_object_type) (impl_change_recovery_account_request_object_type) (impl_escrow_object_type) + (impl_work_nonce_object_type) ) FC_REFLECT_TYPENAME( steemit::chain::share_type ) diff --git a/libraries/chain/include/steemit/chain/witness_objects.hpp b/libraries/chain/include/steemit/chain/witness_objects.hpp index da4b626238..8a5b6de9da 100644 --- a/libraries/chain/include/steemit/chain/witness_objects.hpp +++ b/libraries/chain/include/steemit/chain/witness_objects.hpp @@ -85,8 +85,6 @@ namespace steemit { namespace chain { fc::uint128 virtual_scheduled_time = fc::uint128::max_value(); ///@} - digest_type last_work; - /** * This field represents the Steem blockchain version the witness is running. */ @@ -122,6 +120,14 @@ namespace steemit { namespace chain { version majority_version; }; + class work_nonce_object : public graphene::db::abstract_object + { + public: + static const uint8_t space_id = implementation_ids; + static const uint8_t type_id = impl_work_nonce_object_type; + + uint64_t nonce = 0; + }; struct by_vote_name; struct by_name; @@ -135,7 +141,6 @@ namespace steemit { namespace chain { witness_object, indexed_by< ordered_unique< tag, member< object, object_id_type, &object::id > >, - ordered_non_unique< tag, member >, ordered_unique< tag, member >, ordered_non_unique< tag, member >, ordered_unique< tag, @@ -177,9 +182,18 @@ namespace steemit { namespace chain { > // indexed_by > witness_vote_multi_index_type; + struct by_nonce; + typedef multi_index_container< + work_nonce_object, + indexed_by< + ordered_unique< tag, member< object, object_id_type, &object::id > >, + ordered_unique< tag, member< work_nonce_object, uint64_t, &work_nonce_object::nonce > > + > + > work_nonce_multi_index_type; typedef generic_index< witness_object, witness_multi_index_type> witness_index; typedef generic_index< witness_vote_object, witness_vote_multi_index_type > witness_vote_index; + typedef generic_index< work_nonce_object, work_nonce_multi_index_type > work_nonce_index; } } FC_REFLECT_DERIVED( steemit::chain::witness_object, (graphene::db::object), @@ -189,7 +203,6 @@ FC_REFLECT_DERIVED( steemit::chain::witness_object, (graphene::db::object), (last_aslot)(last_confirmed_block_num)(pow_worker)(signing_key) (props) (sbd_exchange_rate)(last_sbd_exchange_update) - (last_work) (running_version) (hardfork_version_vote)(hardfork_time_vote) ) @@ -201,3 +214,9 @@ FC_REFLECT_DERIVED( (current_virtual_time)(next_shuffle_block_num)(current_shuffled_witnesses)(median_props) (majority_version) ) + +FC_REFLECT_DERIVED( + steemit::chain::work_nonce_object, + (graphene::db::object), + (nonce) +) diff --git a/libraries/chain/protocol/steem_operations.cpp b/libraries/chain/protocol/steem_operations.cpp index 4b97c92371..d5cf46040c 100644 --- a/libraries/chain/protocol/steem_operations.cpp +++ b/libraries/chain/protocol/steem_operations.cpp @@ -190,6 +190,39 @@ namespace steemit { namespace chain { work.get().validate(); } + uint16_t clz_sha256( const fc::sha256& x ) + { + const char* data = x.data(); + size_t size = x.data_size(); + size_t lzbits = 0; + static const uint8_t char2lzbits[] = { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + size_t i = 0; + + while( true ) + { + uint8_t c = uint8_t( data[i] ); + lzbits += char2lzbits[c]; + if( c != 0 ) + break; + ++i; + if( i >= size ) + return 0x100; + } + + return lzbits; + } + void pow::create( const fc::ecc::private_key& w, const digest_type& i ) { input = i; @@ -205,7 +238,6 @@ namespace steemit { namespace chain { worker_account = account_name; prev_block = prev; nonce = n; - work = fc::sha256(); auto prv_key = fc::sha256::hash( *this ); auto input = fc::sha256::hash( prv_key ); @@ -214,7 +246,10 @@ namespace steemit { namespace chain { auto sig_hash = fc::sha256::hash( signature ); public_key_type recover = fc::ecc::public_key( signature, sig_hash ); - work = fc::sha256::hash(std::make_pair(input,recover)); + fc::sha256 work = fc::sha256::hash(std::make_pair(input,recover)); + uint16_t lzbits = clz_sha256( work ); + lzbits = (lzbits >= 0x100) ? 0xFF : lzbits; + difficulty = uint8_t( lzbits ); } void pow::validate()const @@ -230,7 +265,7 @@ namespace steemit { namespace chain { { FC_ASSERT( is_valid_account_name( worker_account ) ); pow2 tmp; tmp.create( prev_block, worker_account, nonce ); - FC_ASSERT( work == tmp.work ); + FC_ASSERT( difficulty <= tmp.difficulty ); } void feed_publish_operation::validate()const diff --git a/libraries/chain/steem_evaluator.cpp b/libraries/chain/steem_evaluator.cpp index 196826720a..5d24f40f6a 100644 --- a/libraries/chain/steem_evaluator.cpp +++ b/libraries/chain/steem_evaluator.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #ifndef IS_LOW_MEM #include @@ -1171,17 +1172,14 @@ void pow_apply( database& db, Operation o ) { FC_ASSERT( db.head_block_time() > STEEMIT_MINING_TIME, "Mining cannot start until ${t}", ("t",STEEMIT_MINING_TIME) ); - if( db.is_producing() || db.has_hardfork( STEEMIT_HARDFORK_0_5__59 ) ) { - const auto& witness_by_work = db.get_index_type().indices().get(); - auto work_itr = witness_by_work.find( o.work.work ); - if( work_itr != witness_by_work.end() ) { - FC_ASSERT( !"DUPLICATE WORK DISCOVERED", "${w} ${witness}",("w",o)("wit",*work_itr) ); - } - } - if( !db.has_hardfork( STEEMIT_HARDFORK_0_12__179 ) ) FC_ASSERT( o.props.maximum_block_size >= STEEMIT_MIN_BLOCK_SIZE_LIMIT * 2 ); + db.create< work_nonce_object >( [&]( work_nonce_object& wno ) + { + wno.nonce = o.nonce; + } ); + const auto& accounts_by_name = db.get_index_type().indices().get(); auto itr = accounts_by_name.find(o.get_worker_account()); if(itr == accounts_by_name.end()) { @@ -1227,7 +1225,6 @@ void pow_apply( database& db, Operation o ) { db.modify(*cur_witness, [&]( witness_object& w ){ w.props = o.props; w.pow_worker = dgp.total_pow; - w.last_work = o.work.work; }); } else { db.create( [&]( witness_object& w ) @@ -1236,7 +1233,6 @@ void pow_apply( database& db, Operation o ) { w.props = o.props; w.signing_key = o.work.worker; w.pow_worker = dgp.total_pow; - w.last_work = o.work.work; }); } /// POW reward depends upon whether we are before or after MINER_VOTING kicks in @@ -1265,26 +1261,30 @@ void pow2_evaluator::do_apply( const pow2_operation& o ) { database& db = this->db(); - fc::sha256 target = db.get_pow_target(); - FC_ASSERT( work.work < target, "work lacks sufficient difficulty" ); + uint32_t target = db.get_difficulty_target(); + FC_ASSERT( work.prev_block == db.head_block_id() ); + FC_ASSERT( work.difficulty == target, "incorrect work difficulty" ); FC_ASSERT( o.props.maximum_block_size >= STEEMIT_MIN_BLOCK_SIZE_LIMIT * 2 ); - const auto& witness_by_work = db.get_index_type().indices().get(); - auto work_itr = witness_by_work.find( work.work ); - if( work_itr != witness_by_work.end() ) { - FC_ASSERT( !"DUPLICATE WORK DISCOVERED", "${w} ${witness}",("w",o)("wit",*work_itr) ); - } + // We discard duplicate PoW by enforcing uniqueness of nonce through a unique index, + // which we clear on every block (older work cannot be re-used because it will fail block_id check). + // If two workers are unlucky enough to discover work with the same nonce on the same block, + // one of them will be rejected -- too bad, it's an extremely rare unlucky situation. + db.create< work_nonce_object >( [&]( work_nonce_object& wno ) + { + wno.nonce = work.nonce; + } ); const auto& dgp = db.get_dynamic_global_properties(); const auto& accounts_by_name = db.get_index_type().indices().get(); - auto itr = accounts_by_name.find(o.get_worker_account()); + auto itr = accounts_by_name.find( work.worker_account ); if(itr == accounts_by_name.end()) { FC_ASSERT( o.new_owner_key.valid() ); db.create< account_object >( [&]( account_object& acc ) { - acc.name = o.get_worker_account(); + acc.name = work.worker_account; acc.owner = authority( 1, *o.new_owner_key, 1); acc.active = acc.owner; acc.posting = acc.owner; @@ -1311,16 +1311,14 @@ void pow2_evaluator::do_apply( const pow2_operation& o ) { db.modify(*cur_witness, [&]( witness_object& w ){ w.props = o.props; w.pow_worker = dgp.total_pow; - w.last_work = work.work; }); } else { db.create( [&]( witness_object& w ) { - w.owner = o.get_worker_account(); + w.owner = work.worker_account; w.props = o.props; w.signing_key = *o.new_owner_key; w.pow_worker = dgp.total_pow; - w.last_work = work.work; }); } /// POW reward depends upon whether we are before or after MINER_VOTING kicks in diff --git a/libraries/plugins/witness/witness.cpp b/libraries/plugins/witness/witness.cpp index c2001b336b..b16ca9fcfc 100644 --- a/libraries/plugins/witness/witness.cpp +++ b/libraries/plugins/witness/witness.cpp @@ -463,7 +463,6 @@ void witness_plugin::start_mining( const fc::ecc::public_key& pub, const fc::ecc auto block_id = b.id(); - auto target = db.get_pow_target(); fc::thread* mainthread = &fc::thread::current(); _total_hashes = 0; @@ -473,8 +472,11 @@ void witness_plugin::start_mining( const fc::ecc::public_key& pub, const fc::ecc uint32_t thread_num = 0; uint32_t num_threads = _mining_threads; - for( auto& t : _thread_pool ) { - if( db.has_hardfork( STEEMIT_HARDFORK_0_13 ) ) { + if( db.has_hardfork( STEEMIT_HARDFORK_0_13 ) ) + { + uint32_t target = db.get_difficulty_target(); + for( auto& t : _thread_pool ) + { t->async( [=](){ chain::pow2_operation op; chain::pow2 work; @@ -499,11 +501,12 @@ void witness_plugin::start_mining( const fc::ecc::public_key& pub, const fc::ecc work.nonce += num_threads; work.create( block_id, miner, work.nonce ); - if( work.work < target ) + if( work.difficulty < target ) { ++this->_head_block_num; /// signal other workers to stop chain::signed_transaction trx; + work.difficulty = target; op.work = work; op.new_owner_key = pub; trx.operations.push_back(op); @@ -523,7 +526,13 @@ void witness_plugin::start_mining( const fc::ecc::public_key& pub, const fc::ecc } } }); - } else { /// TODO: after Hardfork 13, remove this branch + } + } + else + { /// TODO: after Hardfork 13, remove this branch + auto target = db.get_pow_target(); + for( auto& t : _thread_pool ) + { t->async( [=](){ chain::pow_operation op; op.block_id = block_id; @@ -570,8 +579,8 @@ void witness_plugin::start_mining( const fc::ecc::public_key& pub, const fc::ecc } } }); + ++thread_num; } - ++thread_num; } }