diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 5370e1791b2..602ff37d93d 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -708,7 +708,8 @@ void chain_controller::create_block_summary(const signed_block& next_block) { } void chain_controller::update_global_properties(const signed_block& b) { - // If we're at the end of a round, update the BlockchainConfiguration and producer schedule + // If we're at the end of a round, update the BlockchainConfiguration, producer schedule + // and "producers" special account authority if (b.block_num() % config::BlocksPerRound == 0) { auto schedule = calculate_next_round(b); auto config = _admin->get_blockchain_configuration(_db, schedule); @@ -718,6 +719,16 @@ void chain_controller::update_global_properties(const signed_block& b) { gpo.active_producers = std::move(schedule); gpo.configuration = std::move(config); }); + + auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {}); + for(auto& name : gpo.active_producers) { + active_producers_authority.accounts.push_back({{name, config::ActiveName}, 1}); + } + + auto& po = _db.get( boost::make_tuple(config::ProducersAccountName, config::ActiveName) ); + _db.modify(po,[active_producers_authority] (permission_object& po) { + po.auth = active_producers_authority; + }); } } diff --git a/libraries/chain/include/eos/chain/config.hpp b/libraries/chain/include/eos/chain/config.hpp index c81598e0db2..edd8ef5804a 100644 --- a/libraries/chain/include/eos/chain/config.hpp +++ b/libraries/chain/include/eos/chain/config.hpp @@ -22,8 +22,7 @@ * THE SOFTWARE. */ #pragma once -#include -#include +#include namespace eos { namespace config { using types::UInt32; @@ -31,12 +30,21 @@ using types::UInt64; using types::UInt128; using types::ShareType; using types::Asset; +using types::AccountName; +using types::PermissionName; const static char KeyPrefix[] = "EOS"; -const static char SystemContractName[] = "system"; -const static char EosContractName[] = "eos"; -const static char StakedBalanceContractName[] = "staked"; +const static AccountName SystemContractName = N(system); +const static AccountName EosContractName = N(eos); +const static AccountName StakedBalanceContractName = N(staked); + +const static AccountName NobodyAccountName = N(nobody); +const static AccountName AnybodyAccountName = N(anybody); +const static AccountName ProducersAccountName = N(producers); + +const static PermissionName ActiveName = N(active); +const static PermissionName OwnerName = N(owner); const static ShareType InitialTokenSupply = Asset::fromString("90000000.00000000 EOS").amount; @@ -53,6 +61,7 @@ const static ShareType DefaultElectedPay = Asset(100).amount; const static ShareType DefaultRunnerUpPay = Asset(75).amount; const static ShareType DefaultMinEosBalance = Asset(100).amount; const static UInt32 DefaultMaxTrxLifetime = 60*60; +const static UInt32 ProducersAuthorityThreshold = 14; const static int BlocksPerRound = 21; const static int VotedProducersPerRound = 20; diff --git a/libraries/native_contract/native_contract_chain_initializer.cpp b/libraries/native_contract/native_contract_chain_initializer.cpp index a9f816e238a..2b97e5baee6 100644 --- a/libraries/native_contract/native_contract_chain_initializer.cpp +++ b/libraries/native_contract/native_contract_chain_initializer.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -143,6 +144,36 @@ std::vector native_contract_chain_initializer::prepare_database( }); boost::copy(genesis.initial_producers | CreateProducer, std::back_inserter(messages_to_process)); + // Create special accounts + auto create_special_account = [this, &db](Name name, const auto& owner, const auto& active) { + db.create([this, &name](account_object& a) { + a.name = name; + a.creation_date = genesis.initial_timestamp; + }); + const auto& owner_permission = db.create([&owner, &name](permission_object& p) { + p.name = config::OwnerName; + p.parent = 0; + p.owner = name; + p.auth = std::move(owner); + }); + db.create([&active, &owner_permission](permission_object& p) { + p.name = config::ActiveName; + p.parent = owner_permission.id; + p.owner = owner_permission.owner; + p.auth = std::move(active); + }); + }; + + auto empty_authority = types::Authority(0, {}, {}); + auto active_producers_authority = types::Authority(config::ProducersAuthorityThreshold, {}, {}); + for(auto& p : genesis.initial_producers) { + active_producers_authority.accounts.push_back({{p.owner_name, config::ActiveName}, 1}); + } + + //CreateNativeAccount(config::AnybodyAccountName, 0); + create_special_account(config::NobodyAccountName, empty_authority, empty_authority); + create_special_account(config::ProducersAccountName, empty_authority, active_producers_authority); + return messages_to_process; } diff --git a/libraries/types/include/eos/types/native.hpp b/libraries/types/include/eos/types/native.hpp index 09971ac7435..8f8f2599d83 100644 --- a/libraries/types/include/eos/types/native.hpp +++ b/libraries/types/include/eos/types/native.hpp @@ -18,6 +18,8 @@ #include +#define N(X) eos::types::string_to_name(#X) + namespace eos { namespace types { using namespace boost::multiprecision; @@ -55,7 +57,33 @@ namespace eos { namespace types { using Int128 = boost::multiprecision::int128_t; using Int256 = boost::multiprecision::int256_t; using uint128_t = unsigned __int128; /// native clang/gcc 128 intrinisic + + static constexpr char char_to_symbol( char c ) { + if( c >= 'a' && c <= 'z' ) + return (c - 'a') + 1; + if( c >= '1' && c <= '5' ) + return (c - '1') + 26; + return 0; + } + + static constexpr uint64_t string_to_name( const char* str ) { + uint32_t len = 0; + while( str[len] ) ++len; + + uint64_t value = 0; + + for( uint32_t i = 0; i <= 12 && i < len; ++i ) { + value <<= 5; + value |= char_to_symbol( str[ len -1 - i ] ); + } + if( len == 13 ) { + value <<= 4; + value |= 0x0f & char_to_symbol( str[ 12 ] ); + } + return value; + } + struct Name { uint64_t value = 0; bool valid()const { return 0 == (value >> 60); } @@ -80,16 +108,6 @@ namespace eos { namespace types { } }FC_CAPTURE_AND_RETHROW( (str) ) } - - char char_to_symbol( char c ) const { - if( c >= 'a' && c <= 'z' ) - return (c - 'a') + 1; - if( c >= '1' && c <= '5' ) - return (c - '1') + 27; - //FC_ASSERT( c == '.', "invalid character '${c}' (${i}) in Name string", ("c", String(&c,1))("i",int(c)) ); - return 0; - } - Name( uint64_t v = 0 ):value(v){ // FC_ASSERT( !(v>>(5*12)), "invalid name id" ); } diff --git a/tests/tests/special_accounts_tests.cpp b/tests/tests/special_accounts_tests.cpp new file mode 100644 index 00000000000..31f554e1b1f --- /dev/null +++ b/tests/tests/special_accounts_tests.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +#include "../common/database_fixture.hpp" + +using namespace eos; +using namespace chain; + +BOOST_AUTO_TEST_SUITE(special_account_tests) + +//Check special accounts exits in genesis +BOOST_FIXTURE_TEST_CASE(accounts_exists, testing_fixture) +{ try { + + Make_Blockchain(chain); + + auto nobody = chain_db.find(config::NobodyAccountName); + BOOST_CHECK(nobody != nullptr); + const auto& nobody_active_authority = chain_db.get(boost::make_tuple(config::NobodyAccountName, config::ActiveName)); + BOOST_CHECK_EQUAL(nobody_active_authority.auth.threshold, 0); + BOOST_CHECK_EQUAL(nobody_active_authority.auth.accounts.size(), 0); + BOOST_CHECK_EQUAL(nobody_active_authority.auth.keys.size(), 0); + + const auto& nobody_owner_authority = chain_db.get(boost::make_tuple(config::NobodyAccountName, config::OwnerName)); + BOOST_CHECK_EQUAL(nobody_owner_authority.auth.threshold, 0); + BOOST_CHECK_EQUAL(nobody_owner_authority.auth.accounts.size(), 0); + BOOST_CHECK_EQUAL(nobody_owner_authority.auth.keys.size(), 0); + + // TODO: check for anybody account + //auto anybody = chain_db.find(config::AnybodyAccountName); + //BOOST_CHECK(anybody == nullptr); + + auto producers = chain_db.find(config::ProducersAccountName); + BOOST_CHECK(producers != nullptr); + + auto& gpo = chain_db.get(); + + const auto& producers_active_authority = chain_db.get(boost::make_tuple(config::ProducersAccountName, config::ActiveName)); + BOOST_CHECK_EQUAL(producers_active_authority.auth.threshold, config::ProducersAuthorityThreshold); + BOOST_CHECK_EQUAL(producers_active_authority.auth.accounts.size(), gpo.active_producers.size()); + BOOST_CHECK_EQUAL(producers_active_authority.auth.keys.size(), 0); + + std::vector active_auth; + for(auto& apw : producers_active_authority.auth.accounts) { + active_auth.emplace_back(apw.permission.account); + } + + std::vector diff; + std::set_difference( + active_auth.begin(), + active_auth.end(), + gpo.active_producers.begin(), + gpo.active_producers.end(), + std::inserter(diff, diff.begin()) + ); + + BOOST_CHECK_EQUAL(diff.size(), 0); + + const auto& producers_owner_authority = chain_db.get(boost::make_tuple(config::ProducersAccountName, config::OwnerName)); + BOOST_CHECK_EQUAL(producers_owner_authority.auth.threshold, 0); + BOOST_CHECK_EQUAL(producers_owner_authority.auth.accounts.size(), 0); + BOOST_CHECK_EQUAL(producers_owner_authority.auth.keys.size(), 0); + +} FC_LOG_AND_RETHROW() } + +//Check correct authority when new set of producers are elected +BOOST_FIXTURE_TEST_CASE(producers_authority, testing_fixture) +{ try { + + Make_Blockchain(chain) + chain.produce_blocks(); + + Make_Account(chain, alice); + Make_Account(chain, bob); + Make_Account(chain, charlie); + + Make_Account(chain, newproducer1); Make_Producer(chain, newproducer1); + Make_Account(chain, newproducer2); Make_Producer(chain, newproducer2); + Make_Account(chain, newproducer3); Make_Producer(chain, newproducer3); + + Approve_Producer(chain, alice, newproducer1, true); + Approve_Producer(chain, bob, newproducer2, true); + Approve_Producer(chain, charlie, newproducer3, true); + + chain.produce_blocks(config::BlocksPerRound - chain.head_block_num() ); + + auto& gpo = chain_db.get(); + + BOOST_REQUIRE(boost::find(gpo.active_producers, "newproducer1") != gpo.active_producers.end()); + BOOST_REQUIRE(boost::find(gpo.active_producers, "newproducer2") != gpo.active_producers.end()); + BOOST_REQUIRE(boost::find(gpo.active_producers, "newproducer3") != gpo.active_producers.end()); + + const auto& producers_active_authority = chain_db.get(boost::make_tuple(config::ProducersAccountName, config::ActiveName)); + BOOST_CHECK_EQUAL(producers_active_authority.auth.threshold, config::ProducersAuthorityThreshold); + BOOST_CHECK_EQUAL(producers_active_authority.auth.accounts.size(), gpo.active_producers.size()); + BOOST_CHECK_EQUAL(producers_active_authority.auth.keys.size(), 0); + + std::vector active_auth; + for(auto& apw : producers_active_authority.auth.accounts) { + active_auth.emplace_back(apw.permission.account); + } + + std::vector diff; + std::set_difference( + active_auth.begin(), + active_auth.end(), + gpo.active_producers.begin(), + gpo.active_producers.end(), + std::inserter(diff, diff.begin()) + ); + + BOOST_CHECK_EQUAL(diff.size(), 0); + + const auto& producers_owner_authority = chain_db.get(boost::make_tuple(config::ProducersAccountName, config::OwnerName)); + BOOST_CHECK_EQUAL(producers_owner_authority.auth.threshold, 0); + BOOST_CHECK_EQUAL(producers_owner_authority.auth.accounts.size(), 0); + BOOST_CHECK_EQUAL(producers_owner_authority.auth.keys.size(), 0); + +} FC_LOG_AND_RETHROW() } + + +BOOST_AUTO_TEST_SUITE_END()