Skip to content

Commit

Permalink
v3.0.4 pull request (#112)
Browse files Browse the repository at this point in the history
* rex18

* rex18

* rex18

* rex18

* rex18

* rex18

* rex18

* rex18

* rex18

* pass REX test for eosio.contracts 1.8.3

Co-authored-by: vlbos <[email protected]>
  • Loading branch information
2 people authored and qianxiaofeng committed Dec 26, 2019
1 parent 550d2b1 commit d86bad1
Show file tree
Hide file tree
Showing 6 changed files with 515 additions and 55 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


## BOSCore Version : v3.0.4
### EOSIO Contracts Version: v1.6.0 (part 1.8.1)
### EOSIO Contracts Version: v1.6.0 (part 1.8.1-1.8.3)

The design of the EOSIO blockchain calls for a number of smart contracts that are run at a privileged permission level in order to support functions such as block producer registration and voting, token staking for CPU and network bandwidth, RAM purchasing, multi-sig, etc. These smart contracts are referred to as the system, token, msig and wrap (formerly known as sudo) contracts.

Expand Down
69 changes: 69 additions & 0 deletions contracts/eosio.system/include/eosio.system/eosio.system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,28 @@ namespace eosiosystem {
return ( flags & ~static_cast<F>(field) );
}

static constexpr uint32_t seconds_per_hour = 3600;
static constexpr int64_t useconds_per_hour = int64_t(seconds_per_hour) * 1000'000ll;




/**
* eosio.system contract defines the structures and actions needed for blockchain's core functionality.
* - Users can stake tokens for CPU and Network bandwidth, and then vote for producers or
* delegate their vote to a proxy.
* - Producers register in order to be voted for, and can claim per-block and per-vote rewards.
* - Users can buy and sell RAM at a market-determined price.
* - Users can bid on premium names.
* - A resource exchange system (REX) allows token holders to lend their tokens,
* and users to rent CPU and Network resources in return for a market-determined fee.
*/

// A name bid, which consists of:
// - a `newname` name that the bid is for
// - a `high_bidder` account name that is the one with the highest bid so far
// - the `high_bid` which is amount of highest bid
// - and `last_bid_time` which is the time of the highest bid
struct [[eosio::table, eosio::contract("eosio.system")]] name_bid {
name newname;
name high_bidder;
Expand Down Expand Up @@ -252,6 +274,49 @@ namespace eosiosystem {

typedef eosio::multi_index< "rexpool"_n, rex_pool > rex_pool_table;

// `rex_return_pool` structure underlying the rex return pool table. A rex return pool table entry is defined by:
// - `version` defaulted to zero,
// - `last_dist_time` the last time proceeds from renting, ram fees, and name bids were added to the rex pool,
// - `pending_bucket_time` timestamp of the pending 12-hour return bucket,
// - `oldest_bucket_time` cached timestamp of the oldest 12-hour return bucket,
// - `pending_bucket_proceeds` proceeds in the pending 12-hour return bucket,
// - `current_rate_of_increase` the current rate per dist_interval at which proceeds are added to the rex pool,
// - `proceeds` the maximum amount of proceeds that can be added to the rex pool at any given time
struct [[eosio::table,eosio::contract("eosio.system")]] rex_return_pool {
uint8_t version = 0;
time_point_sec last_dist_time;
time_point_sec pending_bucket_time = time_point_sec::maximum();
time_point_sec oldest_bucket_time = time_point_sec::min();
int64_t pending_bucket_proceeds = 0;
int64_t current_rate_of_increase = 0;
int64_t proceeds = 0;

static constexpr uint32_t total_intervals = 30 * 144; // 30 days
static constexpr uint32_t dist_interval = 10 * 60; // 10 minutes
static constexpr uint8_t hours_per_bucket = 12;
static_assert( total_intervals * dist_interval == 30 * seconds_per_day );

uint64_t primary_key()const { return 0; }
};

typedef eosio::multi_index< "rexretpool"_n, rex_return_pool > rex_return_pool_table;

// `rex_return_buckets` structure underlying the rex return buckets table. A rex return buckets table is defined by:
// - `version` defaulted to zero,
// - `return_buckets` buckets of proceeds accumulated in 12-hour intervals
struct [[eosio::table,eosio::contract("eosio.system")]] rex_return_buckets {
uint8_t version = 0;
std::map<time_point_sec, int64_t> return_buckets;

uint64_t primary_key()const { return 0; }
};

typedef eosio::multi_index< "retbuckets"_n, rex_return_buckets > rex_return_buckets_table;

// `rex_fund` structure underlying the rex fund table. A rex fund table entry is defined by:
// - `version` defaulted to zero,
// - `owner` the owner of the rex fund,
// - `balance` the balance of the fund.
struct [[eosio::table,eosio::contract("eosio.system")]] rex_fund {
uint8_t version = 0;
name owner;
Expand Down Expand Up @@ -338,6 +403,8 @@ namespace eosiosystem {
eosio_global_state3 _gstate3;
rammarket _rammarket;
rex_pool_table _rexpool;
rex_return_pool_table _rexretpool;
rex_return_buckets_table _rexretbuckets;
rex_fund_table _rexfunds;
rex_balance_table _rexbalance;
rex_order_table _rexorders;
Expand Down Expand Up @@ -693,6 +760,7 @@ namespace eosiosystem {

// defined in rex.cpp
void runrex( uint16_t max );
void update_rex_pool();
void update_resource_limits( const name& from, const name& receiver, int64_t delta_net, int64_t delta_cpu );
void check_voting_requirement( const name& owner,
const char* error_msg = "must vote for at least 21 producers or for a proxy before buying REX" )const;
Expand All @@ -714,6 +782,7 @@ namespace eosiosystem {
static time_point_sec get_rex_maturity();
asset add_to_rex_balance( const name& owner, const asset& payment, const asset& rex_received );
asset add_to_rex_pool( const asset& payment );
void add_to_rex_return_pool( const asset& fee );
void process_rex_maturities( const rex_balance_table::const_iterator& bitr );
void consolidate_rex_balance( const rex_balance_table::const_iterator& bitr,
const asset& rex_in_sell_order );
Expand Down
2 changes: 2 additions & 0 deletions contracts/eosio.system/src/eosio.system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace eosiosystem {
_guarantee(_self, _self.value),
_rammarket(_self, _self.value),
_rexpool(_self, _self.value),
_rexretpool(get_self(), get_self().value),
_rexretbuckets(get_self(), get_self().value),
_rexfunds(_self, _self.value),
_rexbalance(_self, _self.value),
_rexorders(_self, _self.value),
Expand Down
151 changes: 143 additions & 8 deletions contracts/eosio.system/src/rex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,15 +606,13 @@ namespace eosiosystem {
*/
void system_contract::add_loan_to_rex_pool( const asset& payment, int64_t rented_tokens, bool new_loan )
{
add_to_rex_return_pool( payment );
_rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& rt ) {
// add payment to total_rent
rt.total_rent.amount += payment.amount;
// move rented_tokens from total_unlent to total_lent
rt.total_unlent.amount -= rented_tokens;
rt.total_lent.amount += rented_tokens;
// add payment to total_unlent
rt.total_unlent.amount += payment.amount;
rt.total_lendable.amount = rt.total_unlent.amount + rt.total_lent.amount;
// increment loan_num if a new loan is being created
if ( new_loan ) {
rt.loan_num++;
Expand Down Expand Up @@ -667,6 +665,8 @@ namespace eosiosystem {
{
check( rex_system_initialized(), "rex system not initialized yet" );

update_rex_pool();

const auto& pool = _rexpool.begin();

auto process_expired_loan = [&]( auto& idx, const auto& itr ) -> std::pair<bool, int64_t> {
Expand Down Expand Up @@ -770,6 +770,108 @@ namespace eosiosystem {

}

/**
* @brief Adds returns from the REX return pool to the REX pool
*/
void system_contract::update_rex_pool()
{
auto get_elapsed_intervals = [&]( const time_point_sec& t1, const time_point_sec& t0 ) -> uint32_t {
return ( t1.sec_since_epoch() - t0.sec_since_epoch() ) / rex_return_pool::dist_interval;
};

const time_point_sec ct = current_time_point();
const uint32_t cts = ct.sec_since_epoch();
const time_point_sec effective_time{cts - cts % rex_return_pool::dist_interval};

const auto ret_pool_elem = _rexretpool.begin();
const auto ret_buckets_elem = _rexretbuckets.begin();

if ( ret_pool_elem == _rexretpool.end() || effective_time <= ret_pool_elem->last_dist_time ) {
return;
}

const int64_t current_rate = ret_pool_elem->current_rate_of_increase;
const uint32_t elapsed_intervals = get_elapsed_intervals( effective_time, ret_pool_elem->last_dist_time );
int64_t change_estimate = current_rate * elapsed_intervals;

{
const bool new_return_bucket = ret_pool_elem->pending_bucket_time <= effective_time;
int64_t new_bucket_rate = 0;
time_point_sec new_bucket_time = time_point_sec::min();
_rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) {
if ( new_return_bucket ) {
int64_t remainder = rp.pending_bucket_proceeds % rex_return_pool::total_intervals;
new_bucket_rate = ( rp.pending_bucket_proceeds - remainder ) / rex_return_pool::total_intervals;
new_bucket_time = rp.pending_bucket_time;
rp.current_rate_of_increase += new_bucket_rate;
change_estimate += remainder + new_bucket_rate * get_elapsed_intervals( effective_time, rp.pending_bucket_time );
rp.pending_bucket_proceeds = 0;
rp.pending_bucket_time = time_point_sec::maximum();
if ( new_bucket_time < rp.oldest_bucket_time ) {
rp.oldest_bucket_time = new_bucket_time;
}
}
rp.proceeds -= change_estimate;
rp.last_dist_time = effective_time;
});

if ( new_return_bucket ) {
_rexretbuckets.modify( ret_buckets_elem, same_payer, [&]( auto& rb ) {
rb.return_buckets[new_bucket_time] = new_bucket_rate;
});
}
}

const time_point_sec time_threshold = effective_time - seconds(rex_return_pool::total_intervals * rex_return_pool::dist_interval);
if ( ret_pool_elem->oldest_bucket_time <= time_threshold ) {
int64_t expired_rate = 0;
int64_t surplus = 0;
_rexretbuckets.modify( ret_buckets_elem, same_payer, [&]( auto& rb ) {
auto& return_buckets = rb.return_buckets;
auto iter = return_buckets.begin();
while ( iter != return_buckets.end() && iter->first <= time_threshold ) {
auto next = iter;
++next;
const uint32_t overtime = get_elapsed_intervals( effective_time,
iter->first + seconds(rex_return_pool::total_intervals * rex_return_pool::dist_interval) );
surplus += iter->second * overtime;
expired_rate += iter->second;
return_buckets.erase(iter);
iter = next;
}
});

_rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) {
if ( !ret_buckets_elem->return_buckets.empty() ) {
rp.oldest_bucket_time = ret_buckets_elem->return_buckets.begin()->first;
} else {
rp.oldest_bucket_time = time_point_sec::min();
}
if ( expired_rate > 0) {
rp.current_rate_of_increase -= expired_rate;
}
if ( surplus > 0 ) {
change_estimate -= surplus;
rp.proceeds += surplus;
}
});
}

if ( change_estimate > 0 && ret_pool_elem->proceeds < 0 ) {
_rexretpool.modify( ret_pool_elem, same_payer, [&]( auto& rp ) {
change_estimate += rp.proceeds;
rp.proceeds = 0;
});
}

if ( change_estimate > 0 ) {
_rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& pool ) {
pool.total_unlent.amount += change_estimate;
pool.total_lendable = pool.total_unlent + pool.total_lent;
});
}
}

template <typename T>
int64_t system_contract::rent_rex( T& table, const name& from, const name& receiver, const asset& payment, const asset& fund )
{
Expand Down Expand Up @@ -830,7 +932,7 @@ namespace eosiosystem {
asset stake_change( 0, core_symbol() );
bool success = false;

const int64_t unlent_lower_bound = ( uint128_t(2) * rexitr->total_lent.amount ) / 10;
const int64_t unlent_lower_bound = rexitr->total_lent.amount / 10;
const int64_t available_unlent = rexitr->total_unlent.amount - unlent_lower_bound; // available_unlent <= 0 is possible
if ( proceeds.amount <= available_unlent ) {
const int64_t init_vote_stake_amount = bitr->vote_stake.amount;
Expand Down Expand Up @@ -970,10 +1072,7 @@ namespace eosiosystem {
{
#if CHANNEL_RAM_AND_NAMEBID_FEES_TO_REX
if ( rex_available() ) {
_rexpool.modify( _rexpool.begin(), same_payer, [&]( auto& rp ) {
rp.total_unlent.amount += amount.amount;
rp.total_lendable.amount += amount.amount;
});
add_to_rex_return_pool( amount );
// inline transfer to rex_account
token::transfer_action transfer_act{ token_account, { from, active_permission } };
transfer_act.send( from, rex_account, amount,
Expand Down Expand Up @@ -1113,6 +1212,42 @@ namespace eosiosystem {
return rex_received;
}

/**
* @brief Adds an amount of core tokens to the REX return pool
*
* @param fee - amount to be added
*/
void system_contract::add_to_rex_return_pool( const asset& fee )
{
update_rex_pool();
if ( fee.amount <= 0 ) {
return;
}

const time_point_sec ct = current_time_point();
const uint32_t cts = ct.sec_since_epoch();
const uint32_t bucket_interval = rex_return_pool::hours_per_bucket * seconds_per_hour;
const time_point_sec effective_time{cts - cts % bucket_interval + bucket_interval};
const auto return_pool_elem = _rexretpool.begin();
if ( return_pool_elem == _rexretpool.end() ) {
_rexretpool.emplace( get_self(), [&]( auto& rp ) {
rp.last_dist_time = effective_time;
rp.pending_bucket_proceeds = fee.amount;
rp.pending_bucket_time = effective_time;
rp.proceeds = fee.amount;
});
_rexretbuckets.emplace( get_self(), [&]( auto& rb ) { } );
} else {
_rexretpool.modify( return_pool_elem, same_payer, [&]( auto& rp ) {
rp.pending_bucket_proceeds += fee.amount;
rp.proceeds += fee.amount;
if ( rp.pending_bucket_time == time_point_sec::maximum() ) {
rp.pending_bucket_time = effective_time;
}
});
}
}

/**
* @brief Updates owner REX balance upon buying REX tokens
*
Expand Down
42 changes: 42 additions & 0 deletions tests/eosio.system_tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,48 @@ class eosio_system_tester : public TESTER {
return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "rex_pool", data, abi_serializer_max_time );
}

fc::variant get_rex_return_pool() const {
vector<char> data;
const auto& db = control->db();
namespace chain = eosio::chain;
const auto* t_id = db.find<eosio::chain::table_id_object, chain::by_code_scope_table>( boost::make_tuple( config::system_account_name, config::system_account_name, N(rexretpool) ) );
if ( !t_id ) {
return fc::variant();
}

const auto& idx = db.get_index<chain::key_value_index, chain::by_scope_primary>();

auto itr = idx.lower_bound( boost::make_tuple( t_id->id, 0 ) );
if ( itr == idx.end() || itr->t_id != t_id->id || 0 != itr->primary_key ) {
return fc::variant();
}

data.resize( itr->value.size() );
memcpy( data.data(), itr->value.data(), data.size() );
return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "rex_return_pool", data, abi_serializer_max_time );
}

fc::variant get_rex_return_buckets() const {
vector<char> data;
const auto& db = control->db();
namespace chain = eosio::chain;
const auto* t_id = db.find<eosio::chain::table_id_object, chain::by_code_scope_table>( boost::make_tuple( config::system_account_name, config::system_account_name, N(retbuckets) ) );
if ( !t_id ) {
return fc::variant();
}

const auto& idx = db.get_index<chain::key_value_index, chain::by_scope_primary>();

auto itr = idx.lower_bound( boost::make_tuple( t_id->id, 0 ) );
if ( itr == idx.end() || itr->t_id != t_id->id || 0 != itr->primary_key ) {
return fc::variant();
}

data.resize( itr->value.size() );
memcpy( data.data(), itr->value.data(), data.size() );
return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "rex_return_buckets", data, abi_serializer_max_time );
}

void setup_rex_accounts( const std::vector<account_name>& accounts,
const asset& init_balance,
const asset& net = core_sym::from_string("80.0000"),
Expand Down
Loading

0 comments on commit d86bad1

Please sign in to comment.