From 8eb17ff19a2b1ed59b6b37f0fe0fcb4760584422 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 29 Jun 2017 10:00:36 -0500 Subject: [PATCH 1/3] Remove outdated comment --- .../chain/include/eos/chain/chain_administration_interface.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/chain/include/eos/chain/chain_administration_interface.hpp b/libraries/chain/include/eos/chain/chain_administration_interface.hpp index 35143c77140..acccfce87da 100644 --- a/libraries/chain/include/eos/chain/chain_administration_interface.hpp +++ b/libraries/chain/include/eos/chain/chain_administration_interface.hpp @@ -14,9 +14,6 @@ class chain_controller; * The @ref chain_controller is not responsible for scheduling block producers, calculating the current @ref * BlockchainConfiguration, but it needs to know the results of these calculations. This interface allows the @ref * chain_controller to invoke these calculations and retrieve their results when needed. - * - * @note This class uses the Curiously Recurring Template Pattern for static polymorphism: - * https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern */ class chain_administration_interface { public: From 692c49a845d128f1c87d8d34527ab29a72282757 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 29 Jun 2017 12:06:35 -0500 Subject: [PATCH 2/3] Cleanup in chain_controller Remove the node_property_object, and other cruft. --- libraries/chain/chain_controller.cpp | 47 +++++------------- .../include/eos/chain/chain_controller.hpp | 21 -------- .../eos/chain/node_property_object.hpp | 48 ------------------- 3 files changed, 12 insertions(+), 104 deletions(-) delete mode 100644 libraries/chain/include/eos/chain/node_property_object.hpp diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index 6ec2cc2c413..d64a094e83e 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -178,9 +178,9 @@ bool chain_controller::push_block(const signed_block& new_block, uint32_t skip) bool chain_controller::_push_block(const signed_block& new_block) { try { - uint32_t skip = get_node_properties().skip_flags; + uint32_t skip = _skip_flags; if (!(skip&skip_fork_db)) { - /// TODO: if the block is greater than the head block and before the next maitenance interval + /// TODO: if the block is greater than the head block and before the next maintenance interval // verify that the block signer is in the current set of active producers. shared_ptr new_head = _fork_db.push_block(new_block); @@ -259,10 +259,8 @@ bool chain_controller::_push_block(const signed_block& new_block) void chain_controller::push_transaction(const SignedTransaction& trx, uint32_t skip) { try { with_skip_flags(skip, [&]() { - with_producing([&](){ - _db.with_write_lock([&]() { - _push_transaction(trx); - }); + _db.with_write_lock([&]() { + _push_transaction(trx); }); }); } FC_CAPTURE_AND_RETHROW((trx)) } @@ -293,15 +291,13 @@ signed_block chain_controller::generate_block( uint32_t skip /* = 0 */ ) { try { - return with_producing( [&]() { - return with_skip_flags( skip, [&](){ - auto b = _db.with_write_lock( [&](){ - return _generate_block( when, producer, block_signing_private_key ); - }); - push_block(b); - return b; + return with_skip_flags( skip, [&](){ + auto b = _db.with_write_lock( [&](){ + return _generate_block( when, producer, block_signing_private_key ); }); - }); + push_block(b, skip); + return b; + }); } FC_CAPTURE_AND_RETHROW( (when) ) } signed_block chain_controller::_generate_block( @@ -311,7 +307,7 @@ signed_block chain_controller::_generate_block( ) { try { - uint32_t skip = get_node_properties().skip_flags; + uint32_t skip = _skip_flags; uint32_t slot_num = get_slot_at_time( when ); FC_ASSERT( slot_num > 0 ); AccountName scheduled_producer = get_scheduled_producer( slot_num ); @@ -454,11 +450,10 @@ void chain_controller::apply_block(const signed_block& next_block, uint32_t skip with_skip_flags(skip, [&](){ _apply_block(next_block); }); } - void chain_controller::_apply_block(const signed_block& next_block) { try { uint32_t next_block_num = next_block.block_num(); - uint32_t skip = get_node_properties().skip_flags; + uint32_t skip = _skip_flags; FC_ASSERT((skip & skip_merkle_check) || next_block.transaction_merkle_root == next_block.calculate_merkle_root(), "", ("next_block.transaction_merkle_root", next_block.transaction_merkle_root) @@ -752,18 +747,10 @@ types::AccountName chain_controller::head_block_producer() const { return {}; } -const node_property_object& chain_controller::get_node_properties()const { - return _node_property_object; -} - const producer_object& chain_controller::get_producer(const types::AccountName& ownerName) const { return _db.get(ownerName); } -node_property_object& chain_controller::node_properties() { - return _node_property_object; -} - uint32_t chain_controller::last_irreversible_block_num() const { return get_dynamic_global_properties().last_irreversible_block_num; } @@ -785,16 +772,6 @@ void chain_controller::initialize_chain(chain_initializer_interface& starter) { try { if (!_db.find()) { _db.with_write_lock([this, &starter] { - struct auth_inhibitor { - auth_inhibitor(chain_controller& db) : db(db), old_flags(db.node_properties().skip_flags) - { db.node_properties().skip_flags |= skip_authority_check; } - ~auth_inhibitor() - { db.node_properties().skip_flags = old_flags; } - private: - chain_controller& db; - uint32_t old_flags; - } inhibitor(*this); - auto initial_timestamp = starter.get_chain_start_time(); FC_ASSERT(initial_timestamp != time_point_sec(), "Must initialize genesis timestamp." ); FC_ASSERT(initial_timestamp.sec_since_epoch() % config::BlockIntervalSeconds == 0, diff --git a/libraries/chain/include/eos/chain/chain_controller.hpp b/libraries/chain/include/eos/chain/chain_controller.hpp index 17fdfba423e..a14b157454b 100644 --- a/libraries/chain/include/eos/chain/chain_controller.hpp +++ b/libraries/chain/include/eos/chain/chain_controller.hpp @@ -24,7 +24,6 @@ #pragma once #include #include -#include #include #include @@ -149,16 +148,6 @@ namespace eos { namespace chain { return f(); } - template - auto with_producing( Function&& f ) -> decltype((*((Function*)nullptr))()) - { - auto old_producing = _producing; - auto on_exit = fc::make_scoped_exit( [&](){ _producing = old_producing; } ); - _producing = true; - return f(); - } - - template auto without_pending_transactions( Function&& f ) -> decltype((*((Function*)nullptr))()) { @@ -174,13 +163,9 @@ namespace eos { namespace chain { return f(); } - void pop_block(); void clear_pending(); - - - /** * @brief Get the producer scheduled for block production in a slot. * @@ -219,7 +204,6 @@ namespace eos { namespace chain { const global_property_object& get_global_properties()const; const dynamic_global_property_object& get_dynamic_global_properties()const; - const node_property_object& get_node_properties()const; const producer_object& get_producer(const AccountName& ownerName)const; time_point_sec head_block_time()const; @@ -229,8 +213,6 @@ namespace eos { namespace chain { uint32_t block_interval()const { return config::BlockIntervalSeconds; } - node_property_object& node_properties(); - uint32_t last_irreversible_block_num() const; protected: @@ -297,14 +279,11 @@ namespace eos { namespace chain { optional _pending_tx_session; deque _pending_transactions; - bool _producing = false; bool _pushing = false; uint64_t _skip_flags = 0; flat_map _checkpoints; - node_property_object _node_property_object; - typedef pair handler_key; map< AccountName, map > message_validate_handlers; map< AccountName, map > precondition_validate_handlers; diff --git a/libraries/chain/include/eos/chain/node_property_object.hpp b/libraries/chain/include/eos/chain/node_property_object.hpp deleted file mode 100644 index 9c943cee1de..00000000000 --- a/libraries/chain/include/eos/chain/node_property_object.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2017, Respective Authors. - * - * 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. - */ -#pragma once -#include - -namespace eos { namespace chain { - - /** - * @brief Contains per-node database configuration. - * - * Transactions are evaluated differently based on per-node state. - * Settings here may change based on whether the node is syncing or up-to-date. - * Or whether the node is a producer node. Or if we're processing a - * transaction in a producer-signed block vs. a fresh transaction - * from the p2p network. Or configuration-specified tradeoffs of - * performance/hardfork resilience vs. paranoia. - */ - class node_property_object - { - public: - node_property_object(){} - ~node_property_object(){} - - uint32_t skip_flags = 0; - std::map< block_id_type, std::vector< fc::variant_object > > debug_updates; - }; -} } // eos::chain From de27811a649ceb752db00159ae57fe0a01822442 Mon Sep 17 00:00:00 2001 From: Nathan Hourt Date: Thu, 29 Jun 2017 12:07:53 -0500 Subject: [PATCH 3/3] Add test for locking/unlocking EOS --- libraries/chain/include/eos/chain/types.hpp | 2 +- tests/tests/native_contract_tests.cpp | 109 +++++++++++++++----- 2 files changed, 85 insertions(+), 26 deletions(-) diff --git a/libraries/chain/include/eos/chain/types.hpp b/libraries/chain/include/eos/chain/types.hpp index dd8ffdfc27c..53f3f3d2f83 100644 --- a/libraries/chain/include/eos/chain/types.hpp +++ b/libraries/chain/include/eos/chain/types.hpp @@ -67,7 +67,7 @@ #define EOS_SYSTEM_CONTRACT_FUNCTIONS (CreateAccount)(SetCode) #define EOS_CONTRACT_FUNCTIONS (Transfer)(TransferToLocked) #define EOS_STAKED_BALANCE_CONTRACT_FUNCTIONS \ - (CreateProducer)(UpdateProducer)(ApproveProducer)(SetVoteProxy)(AllowVoteProxying) + (StartUnlockEos)(ClaimUnlockedEos)(CreateProducer)(UpdateProducer)(ApproveProducer)(SetVoteProxy)(AllowVoteProxying) namespace eos { namespace chain { using std::map; diff --git a/tests/tests/native_contract_tests.cpp b/tests/tests/native_contract_tests.cpp index 82fe3c7f308..c0fd02d80ed 100644 --- a/tests/tests/native_contract_tests.cpp +++ b/tests/tests/native_contract_tests.cpp @@ -21,7 +21,7 @@ using namespace eos; using namespace chain; -BOOST_AUTO_TEST_SUITE(system_contract_tests) +BOOST_AUTO_TEST_SUITE(native_contract_tests) //Simple test of account creation BOOST_FIXTURE_TEST_CASE(create_account, testing_fixture) @@ -53,7 +53,7 @@ BOOST_FIXTURE_TEST_CASE(create_account, testing_fixture) BOOST_CHECK_EQUAL(joe_active_authority.auth.keys[0].weight, 1); } - chain.produce_blocks(1); /// verify changes survived creating a new block + chain.produce_blocks(1); /// verify changes survived creating a new block { BOOST_CHECK_EQUAL(chain.get_liquid_balance("joe"), Asset(1000)); BOOST_CHECK_EQUAL(chain.get_liquid_balance("init1"), Asset(100000 - 1000)); @@ -66,7 +66,7 @@ BOOST_FIXTURE_TEST_CASE(create_account, testing_fixture) BOOST_CHECK_EQUAL(joe_owner_authority.auth.keys[0].weight, 1); const auto& joe_active_authority = - chain_db.get(boost::make_tuple("joe", "active")); + chain_db.get(boost::make_tuple("joe", "active")); BOOST_CHECK_EQUAL(joe_active_authority.auth.threshold, 1); BOOST_CHECK_EQUAL(joe_active_authority.auth.accounts.size(), 0); BOOST_CHECK_EQUAL(joe_active_authority.auth.keys.size(), 1); @@ -75,6 +75,65 @@ BOOST_FIXTURE_TEST_CASE(create_account, testing_fixture) } } FC_LOG_AND_RETHROW() } +// Verify that staking and unstaking works +BOOST_FIXTURE_TEST_CASE(stake, testing_fixture) +{ try { + // Create account sam with default balance of 100, and stake 55 of it + Make_Blockchain(chain); + Make_Account(chain, sam); + Stake_Asset(chain, sam, Asset(55).amount); + + // Check balances + BOOST_CHECK_EQUAL(chain.get_staked_balance("sam"), Asset(55).amount); + BOOST_CHECK_EQUAL(chain.get_unstaking_balance("sam"), Asset(0).amount); + BOOST_CHECK_EQUAL(chain.get_liquid_balance("sam"), Asset(45).amount); + + chain.produce_blocks(); + + // Start unstaking 20, check balances + BOOST_CHECK_THROW(Begin_Unstake_Asset(chain, sam, Asset(56).amount), chain::message_precondition_exception); + Begin_Unstake_Asset(chain, sam, Asset(20).amount); + BOOST_CHECK_EQUAL(chain.get_staked_balance("sam"), Asset(35).amount); + BOOST_CHECK_EQUAL(chain.get_unstaking_balance("sam"), Asset(20).amount); + BOOST_CHECK_EQUAL(chain.get_liquid_balance("sam"), Asset(45).amount); + + // Make sure we can't liquidate early + BOOST_CHECK_THROW(Finish_Unstake_Asset(chain, sam, Asset(10).amount), chain::message_precondition_exception); + + // Fast forward to when we can liquidate + elog("Hang on, this will take a minute..."); + chain.produce_blocks(config::StakedBalanceCooldownSeconds / config::BlockIntervalSeconds + 1); + + BOOST_CHECK_THROW(Finish_Unstake_Asset(chain, sam, Asset(21).amount), chain::message_precondition_exception); + BOOST_CHECK_EQUAL(chain.get_staked_balance("sam"), Asset(35).amount); + BOOST_CHECK_EQUAL(chain.get_unstaking_balance("sam"), Asset(20).amount); + BOOST_CHECK_EQUAL(chain.get_liquid_balance("sam"), Asset(45).amount); + + // Liquidate 10 of the 20 unstaking and check balances + Finish_Unstake_Asset(chain, sam, Asset(10).amount); + BOOST_CHECK_EQUAL(chain.get_staked_balance("sam"), Asset(35).amount); + BOOST_CHECK_EQUAL(chain.get_unstaking_balance("sam"), Asset(10).amount); + BOOST_CHECK_EQUAL(chain.get_liquid_balance("sam"), Asset(55).amount); + + // Liquidate 2 of the 10 left unstaking and check balances + Finish_Unstake_Asset(chain, sam, Asset(2).amount); + BOOST_CHECK_EQUAL(chain.get_staked_balance("sam"), Asset(35).amount); + BOOST_CHECK_EQUAL(chain.get_unstaking_balance("sam"), Asset(8).amount); + BOOST_CHECK_EQUAL(chain.get_liquid_balance("sam"), Asset(57).amount); + + // Ignore the 8 left in unstaking, and begin unstaking 5, which should restake the 8, and start over unstaking 5 + Begin_Unstake_Asset(chain, sam, Asset(5).amount); + BOOST_CHECK_EQUAL(chain.get_staked_balance("sam"), Asset(38).amount); + BOOST_CHECK_EQUAL(chain.get_unstaking_balance("sam"), Asset(5).amount); + BOOST_CHECK_EQUAL(chain.get_liquid_balance("sam"), Asset(57).amount); + + // Begin unstaking 20, which should only deduct 15 from staked, since 5 was already in unstaking + Begin_Unstake_Asset(chain, sam, Asset(20).amount); + BOOST_CHECK_EQUAL(chain.get_staked_balance("sam"), Asset(23).amount); + BOOST_CHECK_EQUAL(chain.get_unstaking_balance("sam"), Asset(20).amount); + BOOST_CHECK_EQUAL(chain.get_liquid_balance("sam"), Asset(57).amount); +} FC_LOG_AND_RETHROW() } + // Simple test to verify a simple transfer transaction works BOOST_FIXTURE_TEST_CASE(transfer, testing_fixture) { try { @@ -106,11 +165,11 @@ BOOST_FIXTURE_TEST_CASE(transfer, testing_fixture) BOOST_REQUIRE_THROW(chain.push_transaction(trx), message_validate_exception); // "fail to notify receiver, init2" trx.messages[0].notify = {"init2"}; trx.setMessage(0, "Transfer", trans); - chain.push_transaction(trx); + chain.push_transaction(trx); BOOST_CHECK_EQUAL(chain.get_liquid_balance("init1"), Asset(100000 - 100)); BOOST_CHECK_EQUAL(chain.get_liquid_balance("init2"), Asset(100000 + 100)); - chain.produce_blocks(1); + chain.produce_blocks(1); BOOST_REQUIRE_THROW(chain.push_transaction(trx), transaction_exception); // not unique @@ -123,7 +182,7 @@ BOOST_FIXTURE_TEST_CASE(transfer, testing_fixture) BOOST_FIXTURE_TEST_CASE(producer_creation, testing_fixture) { try { Make_Blockchain(chain) - chain.produce_blocks(); + chain.produce_blocks(); BOOST_CHECK_EQUAL(chain.head_block_num(), 1); Make_Account(chain, producer); @@ -136,7 +195,7 @@ BOOST_FIXTURE_TEST_CASE(producer_creation, testing_fixture) BOOST_CHECK_EQUAL(producer.last_aslot, 0); BOOST_CHECK_EQUAL(producer.total_missed, 0); BOOST_CHECK_EQUAL(producer.last_confirmed_block_num, 0); - chain.produce_blocks(); + chain.produce_blocks(); } Make_Key(signing); @@ -149,7 +208,7 @@ BOOST_FIXTURE_TEST_CASE(producer_creation, testing_fixture) BOOST_FIXTURE_TEST_CASE(producer_voting_parameters, testing_fixture) { try { Make_Blockchain(chain) - chain.produce_blocks(21); + chain.produce_blocks(21); vector votes{ {1024 , 512 , 4096 , Asset(5000 ).amount, Asset(4000 ).amount, Asset(100 ).amount, 512 }, @@ -185,9 +244,9 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_parameters, testing_fixture) } BOOST_CHECK_NE(chain.get_global_properties().configuration, medians); - chain.produce_blocks(20); + chain.produce_blocks(20); BOOST_CHECK_NE(chain.get_global_properties().configuration, medians); - chain.produce_blocks(); + chain.produce_blocks(); BOOST_CHECK_EQUAL(chain.get_global_properties().configuration, medians); } FC_LOG_AND_RETHROW() } @@ -195,7 +254,7 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_parameters, testing_fixture) BOOST_FIXTURE_TEST_CASE(producer_voting_parameters_2, testing_fixture) { try { Make_Blockchain(chain) - chain.produce_blocks(21); + chain.produce_blocks(21); vector votes{ {1024 , 512 , 4096 , Asset(5000 ).amount, Asset(4000 ).amount, Asset(100 ).amount, 512 }, @@ -231,10 +290,10 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_parameters_2, testing_fixture) } BOOST_CHECK_NE(chain.get_global_properties().configuration, medians); - chain.produce_blocks(2); - chain.produce_blocks(18, 5); + chain.produce_blocks(2); + chain.produce_blocks(18, 5); BOOST_CHECK_NE(chain.get_global_properties().configuration, medians); - chain.produce_blocks(); + chain.produce_blocks(); BOOST_CHECK_EQUAL(chain.get_global_properties().configuration, medians); } FC_LOG_AND_RETHROW() } @@ -242,7 +301,7 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_parameters_2, testing_fixture) BOOST_FIXTURE_TEST_CASE(producer_voting_1, testing_fixture) { try { Make_Blockchain(chain) - chain.produce_blocks(); + chain.produce_blocks(); Make_Account(chain, joe); Make_Account(chain, bob); @@ -250,7 +309,7 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_1, testing_fixture) { Make_Producer(chain, joe); Approve_Producer(chain, bob, joe, true); // Produce blocks up to, but not including, the last block in the round - chain.produce_blocks(config::BlocksPerRound - chain.head_block_num() - 1); + chain.produce_blocks(config::BlocksPerRound - chain.head_block_num() - 1); { BOOST_CHECK_EQUAL(chain.get_approved_producers("bob").count("joe"), 1); @@ -260,13 +319,13 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_1, testing_fixture) { } // OK, let's go to the next round - chain.produce_blocks(); + chain.produce_blocks(); const auto& gpo = chain.get_global_properties(); BOOST_REQUIRE(boost::find(gpo.active_producers, "joe") != gpo.active_producers.end()); Approve_Producer(chain, bob, joe, false); - chain.produce_blocks(); + chain.produce_blocks(); { BOOST_CHECK_EQUAL(chain.get_approved_producers("bob").count("joe"), 0); @@ -280,13 +339,13 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_1, testing_fixture) { BOOST_FIXTURE_TEST_CASE(producer_voting_2, testing_fixture) { try { Make_Blockchain(chain) - chain.produce_blocks(); + chain.produce_blocks(); Make_Account(chain, joe); Make_Account(chain, bob); Make_Producer(chain, joe); Approve_Producer(chain, bob, joe, true); - chain.produce_blocks(); + chain.produce_blocks(); { BOOST_CHECK_EQUAL(chain.get_approved_producers("bob").count("joe"), 1); @@ -297,7 +356,7 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_2, testing_fixture) { Stake_Asset(chain, bob, Asset(100).amount); // Produce blocks up to, but not including, the last block in the round - chain.produce_blocks(config::BlocksPerRound - chain.head_block_num() - 1); + chain.produce_blocks(config::BlocksPerRound - chain.head_block_num() - 1); { BOOST_CHECK_EQUAL(chain.get_approved_producers("bob").count("joe"), 1); @@ -307,13 +366,13 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_2, testing_fixture) { } // OK, let's go to the next round - chain.produce_blocks(); + chain.produce_blocks(); const auto& gpo = chain.get_global_properties(); BOOST_REQUIRE(boost::find(gpo.active_producers, "joe") != gpo.active_producers.end()); Approve_Producer(chain, bob, joe, false); - chain.produce_blocks(); + chain.produce_blocks(); { BOOST_CHECK_EQUAL(chain.get_approved_producers("bob").count("joe"), 0); @@ -363,13 +422,13 @@ BOOST_FIXTURE_TEST_CASE(producer_proxy_voting, testing_fixture) { } // OK, let's go to the next round - chain.produce_blocks(); + chain.produce_blocks(); const auto& gpo = chain.get_global_properties(); BOOST_REQUIRE(boost::find(gpo.active_producers, "producer") != gpo.active_producers.end()); Approve_Producer(chain, proxy, producer, false); - chain.produce_blocks(); + chain.produce_blocks(); { BOOST_CHECK_EQUAL(chain.get_approved_producers("proxy").count("producer"), 0);