Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Ref #15, #19: Progress Un/Locking EOS
Browse files Browse the repository at this point in the history
Initial work on locking EOS, beginning unlock, and claiming unlocked
EOS.

TODO: Actually split the system contract, and define names for the
different parts so we can check that the required notifications are made.
Also, figure out how to write notify handlers for the native contracts,
to handle these critical balance updates in the staking/unstaking of
EOS. See the #warning TODO comments in the commit for context.
  • Loading branch information
nathanielhourt committed Jun 3, 2017
1 parent 8c8e555 commit ae5cf81
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 12 deletions.
7 changes: 2 additions & 5 deletions libraries/chain/include/eos/chain/account_object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ namespace eos { namespace chain {
id_type id;
AccountName name;
Asset balance;
UInt64 votes = 0;
UInt64 converting_votes = 0;
Time last_vote_conversion;
};

struct by_name;
Expand Down Expand Up @@ -118,5 +115,5 @@ namespace eos { namespace chain {
CHAINBASE_SET_INDEX_TYPE(eos::chain::account_object, eos::chain::account_index)
CHAINBASE_SET_INDEX_TYPE(eos::chain::permission_object, eos::chain::permission_index)

FC_REFLECT(eos::chain::account_object, (id)(name)(balance)(votes)(converting_votes)(last_vote_conversion) )
FC_REFLECT(eos::chain::permission_object, (id)(owner)(parent)(name) )
FC_REFLECT(eos::chain::account_object, (id)(name)(balance))
FC_REFLECT(eos::chain::permission_object, (id)(owner)(parent)(name))
2 changes: 2 additions & 0 deletions libraries/chain/include/eos/chain/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ const static int ProducerCount = 21;
const static int IrreversibleThresholdPercent = 70 * Percent1;

const static UInt128 ProducerRaceLapLength = std::numeric_limits<UInt128>::max();

const static auto StakedBalanceCooldownSeconds = fc::days(3).to_seconds();
} } // namespace eos::config

template<typename Number>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#include <fc/array.hpp>

#include <eos/chain/types.hpp>
#include <eos/chain/chain_controller.hpp>
#include <eos/chain/BlockchainConfiguration.hpp>

#include <chainbase/chainbase.hpp>
Expand Down
2 changes: 2 additions & 0 deletions libraries/chain/include/eos/chain/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ namespace eos { namespace chain {
transaction_object_type,
producer_object_type,
chain_property_object_type,
staked_balance_object_type, ///< Defined by native_system_contract_plugin
producer_votes_object_type, ///< Defined by native_system_contract_plugin
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
};
Expand Down Expand Up @@ -196,6 +197,7 @@ FC_REFLECT_ENUM( eos::chain::object_type,
(transaction_object_type)
(producer_object_type)
(chain_property_object_type)
(staked_balance_object_type)
(producer_votes_object_type)
(OBJECT_TYPE_COUNT)
)
Expand Down
13 changes: 13 additions & 0 deletions libraries/types/types.eos
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ struct Transfer
amount Asset
memo String

struct TransferToLocked
from AccountName
to AccountName
amount ShareType

struct StartUnlockEos
account AccountName
amount ShareType

struct ClaimUnlockedEos
account AccountName
amount ShareType

struct CreateAccount
creator AccountName
name AccountName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@

namespace eos {

struct TransferToLocked {
static void validate(chain::message_validate_context& context);
static void validate_preconditions(chain::precondition_validate_context& context);
static void apply(chain::apply_context& context);
};

struct StartUnlockEos {
static void validate(chain::message_validate_context& context);
static void validate_preconditions(chain::precondition_validate_context& context);
static void apply(chain::apply_context& context);
};

struct ClaimUnlockedEos {
static void validate(chain::message_validate_context& context);
static void validate_preconditions(chain::precondition_validate_context& context);
static void apply(chain::apply_context& context);
};

struct CreateProducer {
static void validate(chain::message_validate_context& context);
static void validate_preconditions(chain::precondition_validate_context& context);
Expand Down
92 changes: 89 additions & 3 deletions plugins/native_system_contract_plugin/staked_balance_contract.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,102 @@
#include <eos/native_system_contract_plugin/staked_balance_contract.hpp>

#include <eos/chain/global_property_object.hpp>
#include <eos/chain/producer_object.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/chain/exceptions.hpp>

#include "staked_balance_objects.hpp"

namespace eos {
using namespace chain;

void TransferToLocked::validate(message_validate_context& context) {
auto lock = context.msg.as<types::TransferToLocked>();
EOS_ASSERT(lock.amount > 0, message_validate_exception, "Locked amount must be positive");
EOS_ASSERT(lock.to == lock.from || context.msg.has_notify(lock.to),
message_validate_exception, "Recipient account must be notified");
#warning TODO: check that staked balance contract is notified (what's that contract's name?...)
}

void TransferToLocked::validate_preconditions(precondition_validate_context& context) {
auto lock = context.msg.as<types::TransferToLocked>();
ShareType balance;
try {
const auto& sender = context.db.get<account_object, by_name>(lock.from);
context.db.get<account_object, by_name>(lock.to);
balance = sender.balance.amount;
} EOS_RECODE_EXC(fc::exception, message_precondition_exception)
EOS_ASSERT(balance >= lock.amount, message_precondition_exception,
"Account ${a} lacks sufficient funds to lock ${amt} EOS", ("a", lock.from)("amt", lock.amount));
#warning TODO: check that account still has minimum balance?... Eww, that's gonna get complicated
}

void TransferToLocked::apply(apply_context& context) {
auto lock = context.msg.as<types::TransferToLocked>();
const auto& locker = context.db.get<account_object, by_name>(lock.from);
context.mutable_db.modify(locker, [&lock](account_object& a) {
a.balance.amount -= lock.amount;
});
#warning TODO: Credit amount to staked balance (but this contract doesn't do that...)
}

void StartUnlockEos::validate(message_validate_context& context) {
auto unlock = context.msg.as<types::StartUnlockEos>();
EOS_ASSERT(unlock.amount >= 0, message_validate_exception, "Unlock amount cannot be negative");
}

void StartUnlockEos::validate_preconditions(precondition_validate_context& context) {
auto unlock = context.msg.as<types::StartUnlockEos>();
ShareType balance;
try {
balance = context.db.get<StakedBalanceObject, byOwnerName>(unlock.account).stakedBalance;
} EOS_RECODE_EXC(fc::exception, message_precondition_exception)
EOS_ASSERT(balance >= unlock.amount, message_precondition_exception,
"Insufficient locked funds to unlock ${a}", ("a", unlock.amount));
}

void StartUnlockEos::apply(apply_context& context) {
auto unlock = context.msg.as<types::StartUnlockEos>();
context.mutable_db.modify(context.db.get<StakedBalanceObject, byOwnerName>(unlock.account),
[&unlock, &db = context.db](StakedBalanceObject& sbo) {
// If there was any asset left unclaimed from a previous unstaking, move it back to staked first
sbo.stakedBalance += sbo.unstakingBalance;
// OK, now unstakingBalance is logically zero, so we can just overwrite it with its new value
sbo.unstakingBalance = unlock.amount;
// Deduct the now unstaking balance from the staked balance, and record the time
sbo.stakedBalance -= unlock.amount;
sbo.lastUnstakingTime = db.get(dynamic_global_property_object::id_type()).time;
});
}

void ClaimUnlockedEos::validate(message_validate_context& context) {
auto claim = context.msg.as<types::ClaimUnlockedEos>();
EOS_ASSERT(claim.amount > 0, message_validate_exception, "Claim amount must be positive");
}

void ClaimUnlockedEos::validate_preconditions(precondition_validate_context& context) {
auto claim = context.msg.as<types::ClaimUnlockedEos>();
auto balance = context.db.find<StakedBalanceObject, byOwnerName>(claim.account);
EOS_ASSERT(balance != nullptr, message_precondition_exception,
"Could not find staked balance for ${name}", ("name", claim.account));
auto balanceReleaseTime = balance->lastUnstakingTime + config::StakedBalanceCooldownSeconds;
auto now = context.db.get(dynamic_global_property_object::id_type()).time;
EOS_ASSERT(now >= balanceReleaseTime, message_precondition_exception,
"Cannot claim balance until ${releaseDate}", ("releaseDate", balanceReleaseTime));
EOS_ASSERT(balance->unstakingBalance >= claim.amount, message_precondition_exception,
"Cannot claim ${claimAmount} as only ${available} is available for claim",
("claimAmount", claim.amount)("available", balance->unstakingBalance));
}

void ClaimUnlockedEos::apply(apply_context& context) {
auto claim = context.msg.as<types::ClaimUnlockedEos>();
context.mutable_db.modify(context.db.get<StakedBalanceObject, byOwnerName>(claim.account),
[&claim](StakedBalanceObject& sbo) {
sbo.unstakingBalance -= claim.amount;
});
#warning TODO: Credit amount to account balance (but this contract doesn't do that...)
}

void CreateProducer::validate(message_validate_context& context) {
auto create = context.msg.as<types::CreateProducer>();
EOS_ASSERT(create.name.size() > 0, message_validate_exception, "Producer owner name cannot be empty");
Expand Down Expand Up @@ -57,15 +146,12 @@ void UpdateProducer::apply(apply_context& context) {
}

void ApproveProducer::validate(message_validate_context& context) {

}

void ApproveProducer::validate_preconditions(precondition_validate_context& context) {

}

void ApproveProducer::apply(apply_context& context) {

}

} // namespace eos
36 changes: 33 additions & 3 deletions plugins/native_system_contract_plugin/staked_balance_objects.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,26 @@

#include <eos/types/types.hpp>

#include <chainbase/chainbase.hpp>

#include <boost/multi_index/mem_fun.hpp>

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)

id_type id;
types::AccountName ownerName;

types::ShareType stakedBalance = 0;
types::ShareType unstakingBalance = 0;
types::Time lastUnstakingTime = types::Time::maximum();
};

/**
* @brief The ProducerVotesObject class tracks all votes for and by the block producers
*
Expand Down Expand Up @@ -76,15 +92,26 @@ class ProducerVotesObject : public chainbase::object<chain::producer_votes_objec
};

using boost::multi_index::const_mem_fun;

/// Index producers by their owner account's name
struct byOwnerName;

using StakedBalanceMultiIndex = chainbase::shared_multi_index_container<
StakedBalanceObject,
indexed_by<
ordered_unique<tag<by_id>,
member<StakedBalanceObject, StakedBalanceObject::id_type, &StakedBalanceObject::id>
>,
ordered_unique<tag<byOwnerName>,
member<StakedBalanceObject, types::AccountName, &StakedBalanceObject::ownerName>
>
>
>;

/// Index producers by projected race finishing time, from soonest to latest
struct byProjectedRaceFinishTime;
/// Index producers by votes, from greatest to least
struct byVotes;

using ProducerMultiIndex = chainbase::shared_multi_index_container<
using ProducerVotesMultiIndex = chainbase::shared_multi_index_container<
ProducerVotesObject,
indexed_by<
ordered_unique<tag<by_id>,
Expand All @@ -104,3 +131,6 @@ using ProducerMultiIndex = chainbase::shared_multi_index_container<
>;

} // namespace eos

CHAINBASE_SET_INDEX_TYPE(eos::StakedBalanceObject, eos::StakedBalanceMultiIndex)
CHAINBASE_SET_INDEX_TYPE(eos::ProducerVotesObject, eos::ProducerVotesMultiIndex)

0 comments on commit ae5cf81

Please sign in to comment.