提交 fe962e60 编写于 作者: N Nathan Hourt

Ref #19: Implement ApproveProducer

An account can now approve a producer, and that producer's votes are
increased appropriately. The account can remove approval, and the votes
are decreased appropriately. Additionally, the producer is added/removed
to the account's approved producers list as desired.

TODO: A producer with more votes than the rest is still never rotated in
上级 f28757d2
......@@ -66,7 +66,7 @@
#define EOS_SYSTEM_CONTRACT_FUNCTIONS (CreateAccount)(DefineStruct)(SetMessageHandler)
#define EOS_CONTRACT_FUNCTIONS (Transfer)(TransferToLocked)
#define EOS_STAKED_BALANCE_CONTRACT_FUNCTIONS (CreateProducer)(UpdateProducer)
#define EOS_STAKED_BALANCE_CONTRACT_FUNCTIONS (CreateProducer)(UpdateProducer)(ApproveProducer)
namespace eos { namespace chain {
using std::map;
......@@ -105,6 +105,8 @@ namespace eos { namespace chain {
using shared_string = boost::interprocess::basic_string<char, std::char_traits<char>, allocator<char>>;
template<typename T>
using shared_vector = boost::interprocess::vector<T, allocator<T>>;
template<typename T>
using shared_set = boost::interprocess::set<T, std::less<T>, allocator<T>>;
using private_key_type = fc::ecc::private_key;
using chain_id_type = fc::sha256;
......
......@@ -7,7 +7,7 @@
namespace eos {
struct CreateAccount_Notify_Staked {
static void validate_preconditions(chain::precondition_validate_context& context);
static void validate_preconditions(chain::precondition_validate_context&) {}
static void apply(chain::apply_context& context);
};
......
......@@ -13,7 +13,7 @@ namespace eos {
* @brief The StakedBalanceObject class tracks the staked balance (voting balance) for accounts
*/
class StakedBalanceObject : public chainbase::object<chain::staked_balance_object_type, StakedBalanceObject> {
OBJECT_CTOR(StakedBalanceObject)
OBJECT_CTOR(StakedBalanceObject, (approvedProducers))
id_type id;
types::AccountName ownerName;
......@@ -21,6 +21,8 @@ class StakedBalanceObject : public chainbase::object<chain::staked_balance_objec
types::ShareType stakedBalance = 0;
types::ShareType unstakingBalance = 0;
types::Time lastUnstakingTime = types::Time::maximum();
chain::shared_set<types::AccountName> approvedProducers;
};
struct byOwnerName;
......
......@@ -10,10 +10,6 @@
namespace eos {
using namespace chain;
void CreateAccount_Notify_Staked::validate_preconditions(precondition_validate_context& context) {
}
void CreateAccount_Notify_Staked::apply(apply_context& context) {
auto create = context.msg.as<types::CreateAccount>();
context.mutable_db.create<StakedBalanceObject>([&create](StakedBalanceObject& sbo) {
......@@ -142,12 +138,55 @@ void UpdateProducer::apply(apply_context& context) {
}
void ApproveProducer::validate(message_validate_context& context) {
auto approve = context.msg.as<types::ApproveProducer>();
EOS_ASSERT(approve.approve == 0 || approve.approve == 1, message_validate_exception,
"Unknown approval value: ${val}; must be either 0 or 1", ("val", approve.approve));
EOS_ASSERT(approve.producer.size() != 0, message_validate_exception,
"Approved producer's name cannot be empty");
}
void ApproveProducer::validate_preconditions(precondition_validate_context& context) {
const auto& db = context.db;
auto approve = context.msg.as<types::ApproveProducer>();
auto producer = db.find<ProducerVotesObject, byOwnerName>(approve.producer);
auto voter = db.find<StakedBalanceObject, byOwnerName>(context.msg.sender);
EOS_ASSERT(producer != nullptr, message_precondition_exception,
"Could not approve producer '${name}'; no such producer found", ("name", approve.producer));
EOS_ASSERT(voter != nullptr, message_precondition_exception,
"Could not find balance for '${name}'", ("name", context.msg.sender));
if (approve.approve)
EOS_ASSERT(voter->approvedProducers.count(approve.producer) == 0, message_precondition_exception,
"Cannot add approval to producer '${name}'; producer is already approved",
("name", approve.producer));
else
EOS_ASSERT(voter->approvedProducers.count(approve.producer) == 1, message_precondition_exception,
"Cannot remove approval from producer '${name}'; producer is not approved",
("name", approve.producer));
}
void ApproveProducer::apply(apply_context& context) {
auto& db = context.mutable_db;
auto approve = context.msg.as<types::ApproveProducer>();
auto producer = db.find<ProducerVotesObject, byOwnerName>(approve.producer);
auto voter = db.find<StakedBalanceObject, byOwnerName>(context.msg.sender);
auto raceTime = ProducerScheduleObject::get(db).currentRaceTime;
// Add/remove votes from producer
db.modify(*producer,
[approve = approve.approve, amount = voter->stakedBalance, &raceTime](ProducerVotesObject& pvo) {
if (approve)
pvo.updateVotes(amount, raceTime);
else
pvo.updateVotes(-amount, raceTime);
});
// Add/remove producer from voter's approved producer list
db.modify(*voter, [&approve](StakedBalanceObject& sbo) {
if (approve.approve)
sbo.approvedProducers.insert(approve.producer);
else
sbo.approvedProducers.erase(approve.producer);
});
}
} // namespace eos
......@@ -221,9 +221,15 @@ protected:
*
* Example:
* @code{.cpp}
* // Create testing_databases db1 and db2, with db2 having ID "id2"
* // Create testing_database db1
* Make_Database(db1)
* Make_Database(db2, id2)
*
* // The above creates the following objects:
* chainbase::database db1_db;
* block_log db1_log;
* fork_database db1_fdb;
* native_contract::native_contract_chain_initializer db1_initializer;
* testing_database db1;
* @endcode
*/
#define Make_Database(...) BOOST_PP_OVERLOAD(MKDB, __VA_ARGS__)(__VA_ARGS__)
......@@ -368,6 +374,19 @@ protected:
*/
#define Make_Producer(...) BOOST_PP_OVERLOAD(MKPDCR, __VA_ARGS__)(__VA_ARGS__)
/**
* @brief Shorthand way to set approval of a block producer
*
* Use Approve_Producer to change an account's approval of a block producer:
* @code{.cpp}
* // Set joe's approval for pete's block producer to Approve
* Approve_Producer(db, joe, pete, true);
* // Set joe's approval for pete's block producer to Disapprove
* Approve_Producer(db, joe, pete, false);
* @endcode
*/
#define Approve_Producer(...) BOOST_PP_OVERLOAD(APPDCR, __VA_ARGS__)(__VA_ARGS__)
/**
* @brief Shorthand way to update a block producer
*
......
......@@ -64,7 +64,7 @@
{ \
eos::chain::SignedTransaction trx; \
trx.emplaceMessage(#owner, config::StakedBalanceContractName, vector<AccountName>{}, "CreateProducer", \
types::CreateProducer{#owner, key, cfg}); \
types::CreateProducer{#owner, key, cfg}); \
trx.expiration = db.head_block_time() + 100; \
trx.set_reference_block(db.head_block_id()); \
db.push_transaction(trx); \
......@@ -74,6 +74,16 @@
Make_Key(owner ## _producer); \
MKPDCR4(db, owner, owner ## _producer_public_key, BlockchainConfiguration{});
#define APPDCR4(db, voter, producer, approved) \
{ \
eos::chain::SignedTransaction trx; \
trx.emplaceMessage(#voter, config::StakedBalanceContractName, vector<AccountName>{}, "ApproveProducer", \
types::ApproveProducer{#producer, approved? 1 : 0}); \
trx.expiration = db.head_block_time() + 100; \
trx.set_reference_block(db.head_block_id()); \
db.push_transaction(trx); \
}
#define UPPDCR4(db, owner, key, cfg) \
{ \
eos::chain::SignedTransaction trx; \
......
......@@ -24,11 +24,15 @@
#include <eos/chain/chain_controller.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/native_contract/objects.hpp>
#include <chainbase/chainbase.hpp>
#include <fc/crypto/digest.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/range/algorithm/find.hpp>
#include "../common/database_fixture.hpp"
......@@ -180,4 +184,42 @@ BOOST_FIXTURE_TEST_CASE(producer_voting_parameters_2, testing_fixture)
BOOST_CHECK_EQUAL(db.get_global_properties().configuration, medians);
} FC_LOG_AND_RETHROW() }
BOOST_FIXTURE_TEST_CASE(producer_voting, testing_fixture, *boost::unit_test::expected_failures(1)) {
try {
Make_Database(db)
db.produce_blocks();
Make_Account(db, joe);
Make_Account(db, bob);
Make_Producer(db, joe);
Approve_Producer(db, bob, joe, true);
db.produce_blocks();
{
const auto& bobBalance = db_db.get<StakedBalanceObject, byOwnerName>("bob");
BOOST_CHECK_EQUAL(bobBalance.approvedProducers.count("joe"), 1);
const auto& joeVotes = db_db.get<ProducerVotesObject, byOwnerName>("joe");
BOOST_CHECK_EQUAL(joeVotes.getVotes(), bobBalance.stakedBalance);
}
db.produce_blocks(config::BlocksPerRound);
const auto& gpo = db.get_global_properties();
#warning FIXME: Expected test failure: Should start working when updating producer schedule based on votes works
BOOST_CHECK(boost::find(gpo.active_producers, "joe") != gpo.active_producers.end());
Approve_Producer(db, bob, joe, false);
db.produce_blocks();
{
const auto& bobBalance = db_db.get<StakedBalanceObject, byOwnerName>("bob");
BOOST_CHECK_EQUAL(bobBalance.approvedProducers.count("joe"), 0);
const auto& joeVotes = db_db.get<ProducerVotesObject, byOwnerName>("joe");
BOOST_CHECK_EQUAL(joeVotes.getVotes(), 0);
}
} FC_LOG_AND_RETHROW()
}
} // namespace eos
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册