提交 5f22b40a 编写于 作者: W Wang Zhi

Revert "remove noused contract"

This reverts commit cefc7c98.
上级 cefc7c98
......@@ -33,6 +33,8 @@ add_subdirectory(integration_test)
add_subdirectory(hddpool)
add_subdirectory(hdddeposit)
add_subdirectory(eosio.system_dev)
file(GLOB SKELETONS RELATIVE ${CMAKE_SOURCE_DIR}/contracts "skeleton/*")
......
file(GLOB ABI_FILES "*.abi")
configure_file("${ABI_FILES}" "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY)
add_wast_executable(TARGET eosio.system_dev
INCLUDE_FOLDERS ${STANDARD_INCLUDE_FOLDERS}
LIBRARIES libc++ libc eosiolib eosio.token
DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR}
)
eosio.system
----------
This contract enables users to stake tokens, and then configure and vote on producers and worker proposals.
Users can also proxy their voting influence to other users.
The state of this contract is read to determine the 21 active block producers.
Actions:
The naming convention is codeaccount::actionname followed by a list of paramters.
Indicates that a particular account wishes to become a producer
## eosio.system::cfgproducer account config
- **account** the producer account to update
- updates the configuration settings for a particular producer, these
Storage changes are billed to 'account'
## eosio.system::okproducer account producer vote
- **account** the account which is doing the voting
- **producer** the producer which is being voted for (or unvoted for)
- **vote** true if the producer should be voted for, false if not
Each account has a maximum number of votes it can maintain. Storage changes will be billed to 'account'
## eosio.system::setproxy account proxy
- **account** the account which is updating it's proxy
- **proxy** the account which will have the power to vote account's stake
All current votes are removed and a new proxy link is created. The votes for every producer the proxy
has voted for are updated immediately.
Storage changes will be billed to 'account'
## eosio.system::unstake account quantity
- **account** - the account which is requesting their balance be unstaked
- **quantity** - the quantity which will be unstaked, unstaked tokens lose voting influence immediately
- in order to unstake tokens, an account must request them. The user will receive them over
time via weekly withdraws. The length of time will be configured by the median time as set by
the active producers.
- If this is called while in the process of unstaking, the currently pending unstake is canceled as if
quantity were 0, then it is applied as if a new unstake request was made.
- all producers this 'from' has voted for will have their votes updated immediately.
- bandwidth and storage for the deferred transaction will be billed to 'from'
## eosio.system::withdraw account
- this action can only be triggered by eosio.system via a deferred transaction generated by unstake
- this will generate an inline eosio.token::transfer call from=eosio.system to=account and amount equal to the next withdraw increment
- this will generate another deferred withdraw to continue the process if there are still withdraws to be made
## eosio.system::transfer from to amount memo
- decrements balance of from if amount <= balance
- increments balance of to by amount
- memo is ignored
## eosio.system::stakevote account amount
- the primary currency of eosio is controlled by the token contract which enables users to transfer
balances from one user to another. The receiver is notified on incoming balances and the vote contract
will stake the tokens on receipt.
- all producers this 'from' has voted for will have their votes updated immediately.
## eosio.system::onblock account blocktime blocknum
- this special action is triggered when a block is applied by the given producer and cannot be generated from
any other source. It is used to pay producers and calculate missed blocks of other producers.
- producer pay is deposited into the producer's stake balance and can be withdrawn over time.
- if blocknum is the start of a new round this may update the active producer config from the producer votes.
## eosio.system::freeze producer accounts true|false
- requires permission of the @producers account
- if an account is frozen, all authorizations of that account are rejected and the code for that account will not be run
## eosio.system::paystandby producer
- every block some amount of tokens is paid
- at most once per day a producer may claim a percentage of the standby pay equal to their totalvotes / allvotes
/**
* @file
* @copyright defined in eos/LICENSE
*/
#include "eosio.system.hpp"
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/datastream.hpp>
#include <eosiolib/serialize.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/privileged.h>
#include <eosiolib/transaction.hpp>
#include <eosio.token/eosio.token.hpp>
#include <cmath>
#include <map>
namespace eosiosystem {
using eosio::asset;
using eosio::indexed_by;
using eosio::const_mem_fun;
using eosio::bytes;
using eosio::print;
using eosio::permission_level;
using std::map;
using std::pair;
static constexpr time refund_delay = 3*24*3600;
static constexpr time refund_expiration_time = 3600;
//##YTA-Change start:
/*
struct user_resources {
account_name owner;
asset net_weight;
asset cpu_weight;
int64_t ram_bytes = 0;
uint64_t primary_key()const { return owner; }
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( user_resources, (owner)(net_weight)(cpu_weight)(ram_bytes) )
};*/
//##YTA-Change end:
/**
* Every user 'from' has a scope/table that uses every receipient 'to' as the primary key.
*/
struct delegated_bandwidth {
account_name from;
account_name to;
asset net_weight;
asset cpu_weight;
uint64_t primary_key()const { return to; }
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( delegated_bandwidth, (from)(to)(net_weight)(cpu_weight) )
};
struct refund_request {
account_name owner;
time request_time;
eosio::asset net_amount;
eosio::asset cpu_amount;
uint64_t primary_key()const { return owner; }
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( refund_request, (owner)(request_time)(net_amount)(cpu_amount) )
};
/**
* These tables are designed to be constructed in the scope of the relevant user, this
* facilitates simpler API for per-user queries
*/
//##YTA-Change start:
//typedef eosio::multi_index< N(userres), user_resources> user_resources_table;
//##YTA-Change end:
typedef eosio::multi_index< N(delband), delegated_bandwidth> del_bandwidth_table;
typedef eosio::multi_index< N(refunds), refund_request> refunds_table;
/**
* This action will buy an exact amount of ram and bill the payer the current market price.
*/
void system_contract::buyrambytes( account_name payer, account_name receiver, uint32_t bytes ) {
auto itr = _rammarket.find(S(4,RAMCORE));
auto tmp = *itr;
auto eosout = tmp.convert( asset(bytes,S(0,RAM)), CORE_SYMBOL );
buyram( payer, receiver, eosout );
}
/**
* When buying ram the payer irreversiblly transfers quant to system contract and only
* the receiver may reclaim the tokens via the sellram action. The receiver pays for the
* storage of all database records associated with this action.
*
* RAM is a scarce resource whose supply is defined by global properties max_ram_size. RAM is
* priced using the bancor algorithm such that price-per-byte with a constant reserve ratio of 100:1.
*/
void system_contract::buyram( account_name payer, account_name receiver, asset quant )
{
require_auth( payer );
eosio_assert( quant.amount > 0, "must purchase a positive amount" );
auto fee = quant;
fee.amount = ( fee.amount + 199 ) / 200; /// .5% fee (round up)
// fee.amount cannot be 0 since that is only possible if quant.amount is 0 which is not allowed by the assert above.
// If quant.amount == 1, then fee.amount == 1,
// otherwise if quant.amount > 1, then 0 < fee.amount < quant.amount.
auto quant_after_fee = quant;
quant_after_fee.amount -= fee.amount;
// quant_after_fee.amount should be > 0 if quant.amount > 1.
// If quant.amount == 1, then quant_after_fee.amount == 0 and the next inline transfer will fail causing the buyram action to fail.
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {payer,N(active)},
{ payer, N(eosio.ram), quant_after_fee, std::string("buy ram") } );
if( fee.amount > 0 ) {
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {payer,N(active)},
{ payer, N(eosio.ramfee), fee, std::string("ram fee") } );
}
int64_t bytes_out;
const auto& market = _rammarket.get(S(4,RAMCORE), "ram market does not exist");
_rammarket.modify( market, 0, [&]( auto& es ) {
bytes_out = es.convert( quant_after_fee, S(0,RAM) ).amount;
});
eosio_assert( bytes_out > 0, "must reserve a positive amount" );
_gstate.total_ram_bytes_reserved += uint64_t(bytes_out);
_gstate.total_ram_stake += quant_after_fee.amount;
user_resources_table userres( _self, receiver );
auto res_itr = userres.find( receiver );
if( res_itr == userres.end() ) {
res_itr = userres.emplace( receiver, [&]( auto& res ) {
res.owner = receiver;
res.ram_bytes = bytes_out;
});
} else {
userres.modify( res_itr, receiver, [&]( auto& res ) {
res.ram_bytes += bytes_out;
});
}
set_resource_limits( res_itr->owner, res_itr->ram_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount );
}
/**
* The system contract now buys and sells RAM allocations at prevailing market prices.
* This may result in traders buying RAM today in anticipation of potential shortages
* tomorrow. Overall this will result in the market balancing the supply and demand
* for RAM over time.
*/
void system_contract::sellram( account_name account, int64_t bytes ) {
require_auth( account );
eosio_assert( bytes > 0, "cannot sell negative byte" );
user_resources_table userres( _self, account );
auto res_itr = userres.find( account );
eosio_assert( res_itr != userres.end(), "no resource row" );
eosio_assert( res_itr->ram_bytes >= bytes, "insufficient quota" );
asset tokens_out;
auto itr = _rammarket.find(S(4,RAMCORE));
_rammarket.modify( itr, 0, [&]( auto& es ) {
/// the cast to int64_t of bytes is safe because we certify bytes is <= quota which is limited by prior purchases
tokens_out = es.convert( asset(bytes,S(0,RAM)), CORE_SYMBOL);
});
eosio_assert( tokens_out.amount > 1, "token amount received from selling ram is too low" );
_gstate.total_ram_bytes_reserved -= static_cast<decltype(_gstate.total_ram_bytes_reserved)>(bytes); // bytes > 0 is asserted above
_gstate.total_ram_stake -= tokens_out.amount;
//// this shouldn't happen, but just in case it does we should prevent it
eosio_assert( _gstate.total_ram_stake >= 0, "error, attempt to unstake more tokens than previously staked" );
userres.modify( res_itr, account, [&]( auto& res ) {
res.ram_bytes -= bytes;
});
set_resource_limits( res_itr->owner, res_itr->ram_bytes, res_itr->net_weight.amount, res_itr->cpu_weight.amount );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio.ram),N(active)},
{ N(eosio.ram), account, asset(tokens_out), std::string("sell ram") } );
auto fee = ( tokens_out.amount + 199 ) / 200; /// .5% fee (round up)
// since tokens_out.amount was asserted to be at least 2 earlier, fee.amount < tokens_out.amount
if( fee > 0 ) {
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {account,N(active)},
{ account, N(eosio.ramfee), asset(fee), std::string("sell ram fee") } );
}
}
void validate_b1_vesting( int64_t stake ) {
const int64_t base_time = 1527811200; /// 2018-06-01
const int64_t max_claimable = 100'000'000'0000ll;
const int64_t claimable = int64_t(max_claimable * double(now()-base_time) / (10*seconds_per_year) );
eosio_assert( max_claimable - claimable <= stake, "b1 can only claim their tokens over 10 years" );
}
void system_contract::changebw( account_name from, account_name receiver,
const asset stake_net_delta, const asset stake_cpu_delta, bool transfer )
{
require_auth( from );
eosio_assert( stake_net_delta != asset(0) || stake_cpu_delta != asset(0), "should stake non-zero amount" );
eosio_assert( std::abs( (stake_net_delta + stake_cpu_delta).amount )
>= std::max( std::abs( stake_net_delta.amount ), std::abs( stake_cpu_delta.amount ) ),
"net and cpu deltas cannot be opposite signs" );
account_name source_stake_from = from;
if ( transfer ) {
from = receiver;
}
// update stake delegated from "from" to "receiver"
{
del_bandwidth_table del_tbl( _self, from);
auto itr = del_tbl.find( receiver );
if( itr == del_tbl.end() ) {
itr = del_tbl.emplace( from, [&]( auto& dbo ){
dbo.from = from;
dbo.to = receiver;
dbo.net_weight = stake_net_delta;
dbo.cpu_weight = stake_cpu_delta;
});
}
else {
del_tbl.modify( itr, 0, [&]( auto& dbo ){
dbo.net_weight += stake_net_delta;
dbo.cpu_weight += stake_cpu_delta;
});
}
eosio_assert( asset(0) <= itr->net_weight, "insufficient staked net bandwidth" );
eosio_assert( asset(0) <= itr->cpu_weight, "insufficient staked cpu bandwidth" );
if ( itr->net_weight == asset(0) && itr->cpu_weight == asset(0) ) {
del_tbl.erase( itr );
}
} // itr can be invalid, should go out of scope
// update totals of "receiver"
{
user_resources_table totals_tbl( _self, receiver );
auto tot_itr = totals_tbl.find( receiver );
if( tot_itr == totals_tbl.end() ) {
tot_itr = totals_tbl.emplace( from, [&]( auto& tot ) {
tot.owner = receiver;
tot.net_weight = stake_net_delta;
tot.cpu_weight = stake_cpu_delta;
});
} else {
totals_tbl.modify( tot_itr, from == receiver ? from : 0, [&]( auto& tot ) {
tot.net_weight += stake_net_delta;
tot.cpu_weight += stake_cpu_delta;
});
}
eosio_assert( asset(0) <= tot_itr->net_weight, "insufficient staked total net bandwidth" );
eosio_assert( asset(0) <= tot_itr->cpu_weight, "insufficient staked total cpu bandwidth" );
set_resource_limits( receiver, tot_itr->ram_bytes, tot_itr->net_weight.amount, tot_itr->cpu_weight.amount );
if ( tot_itr->net_weight == asset(0) && tot_itr->cpu_weight == asset(0) && tot_itr->ram_bytes == 0 ) {
totals_tbl.erase( tot_itr );
}
} // tot_itr can be invalid, should go out of scope
// create refund or update from existing refund
if ( N(eosio.stake) != source_stake_from ) { //for eosio both transfer and refund make no sense
refunds_table refunds_tbl( _self, from );
auto req = refunds_tbl.find( from );
//create/update/delete refund
auto net_balance = stake_net_delta;
auto cpu_balance = stake_cpu_delta;
bool need_deferred_trx = false;
// net and cpu are same sign by assertions in delegatebw and undelegatebw
// redundant assertion also at start of changebw to protect against misuse of changebw
bool is_undelegating = (net_balance.amount + cpu_balance.amount ) < 0;
bool is_delegating_to_self = (!transfer && from == receiver);
if( is_delegating_to_self || is_undelegating ) {
if ( req != refunds_tbl.end() ) { //need to update refund
refunds_tbl.modify( req, 0, [&]( refund_request& r ) {
if ( net_balance < asset(0) || cpu_balance < asset(0) ) {
r.request_time = now();
}
r.net_amount -= net_balance;
if ( r.net_amount < asset(0) ) {
net_balance = -r.net_amount;
r.net_amount = asset(0);
} else {
net_balance = asset(0);
}
r.cpu_amount -= cpu_balance;
if ( r.cpu_amount < asset(0) ){
cpu_balance = -r.cpu_amount;
r.cpu_amount = asset(0);
} else {
cpu_balance = asset(0);
}
});
eosio_assert( asset(0) <= req->net_amount, "negative net refund amount" ); //should never happen
eosio_assert( asset(0) <= req->cpu_amount, "negative cpu refund amount" ); //should never happen
if ( req->net_amount == asset(0) && req->cpu_amount == asset(0) ) {
refunds_tbl.erase( req );
need_deferred_trx = false;
} else {
need_deferred_trx = true;
}
} else if ( net_balance < asset(0) || cpu_balance < asset(0) ) { //need to create refund
refunds_tbl.emplace( from, [&]( refund_request& r ) {
r.owner = from;
if ( net_balance < asset(0) ) {
r.net_amount = -net_balance;
net_balance = asset(0);
} // else r.net_amount = 0 by default constructor
if ( cpu_balance < asset(0) ) {
r.cpu_amount = -cpu_balance;
cpu_balance = asset(0);
} // else r.cpu_amount = 0 by default constructor
r.request_time = now();
});
need_deferred_trx = true;
} // else stake increase requested with no existing row in refunds_tbl -> nothing to do with refunds_tbl
} /// end if is_delegating_to_self || is_undelegating
if ( need_deferred_trx ) {
eosio::transaction out;
out.actions.emplace_back( permission_level{ from, N(active) }, _self, N(refund), from );
out.delay_sec = refund_delay;
cancel_deferred( from ); // TODO: Remove this line when replacing deferred trxs is fixed
out.send( from, from, true );
} else {
cancel_deferred( from );
}
auto transfer_amount = net_balance + cpu_balance;
if ( asset(0) < transfer_amount ) {
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {source_stake_from, N(active)},
{ source_stake_from, N(eosio.stake), asset(transfer_amount), std::string("stake bandwidth") } );
}
}
// update voting power
{
asset total_update = stake_net_delta + stake_cpu_delta;
auto from_voter = _voters.find(from);
if( from_voter == _voters.end() ) {
from_voter = _voters.emplace( from, [&]( auto& v ) {
v.owner = from;
v.staked = total_update.amount;
});
} else {
_voters.modify( from_voter, 0, [&]( auto& v ) {
v.staked += total_update.amount;
});
}
eosio_assert( 0 <= from_voter->staked, "stake for voting cannot be negative");
if( from == N(b1) ) {
validate_b1_vesting( from_voter->staked );
}
if( from_voter->producers.size() || from_voter->proxy ) {
update_votes( from, from_voter->proxy, from_voter->producers, false );
}
}
}
void system_contract::delegatebw( account_name from, account_name receiver,
asset stake_net_quantity,
asset stake_cpu_quantity, bool transfer )
{
eosio_assert( stake_cpu_quantity >= asset(0), "must stake a positive amount" );
eosio_assert( stake_net_quantity >= asset(0), "must stake a positive amount" );
eosio_assert( stake_net_quantity + stake_cpu_quantity > asset(0), "must stake a positive amount" );
eosio_assert( !transfer || from != receiver, "cannot use transfer flag if delegating to self" );
changebw( from, receiver, stake_net_quantity, stake_cpu_quantity, transfer);
} // delegatebw
void system_contract::undelegatebw( account_name from, account_name receiver,
asset unstake_net_quantity, asset unstake_cpu_quantity )
{
eosio_assert( asset() <= unstake_cpu_quantity, "must unstake a positive amount" );
eosio_assert( asset() <= unstake_net_quantity, "must unstake a positive amount" );
eosio_assert( asset() < unstake_cpu_quantity + unstake_net_quantity, "must unstake a positive amount" );
eosio_assert( _gstate.total_activated_stake >= min_activated_stake,
"cannot undelegate bandwidth until the chain is activated (at least 15% of all tokens participate in voting)" );
changebw( from, receiver, -unstake_net_quantity, -unstake_cpu_quantity, false);
} // undelegatebw
void system_contract::refund( const account_name owner ) {
require_auth( owner );
refunds_table refunds_tbl( _self, owner );
auto req = refunds_tbl.find( owner );
eosio_assert( req != refunds_tbl.end(), "refund request not found" );
eosio_assert( req->request_time + refund_delay <= now(), "refund is not available yet" );
// Until now() becomes NOW, the fact that now() is the timestamp of the previous block could in theory
// allow people to get their tokens earlier than the 3 day delay if the unstake happened immediately after many
// consecutive missed blocks.
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio.stake),N(active)},
{ N(eosio.stake), req->owner, req->net_amount + req->cpu_amount, std::string("unstake") } );
refunds_tbl.erase( req );
}
} //namespace eosiosystem
{
"version": "eosio::abi/1.0",
"types": [{
"new_type_name": "account_name",
"type": "name"
},{
"new_type_name": "permission_name",
"type": "name"
},{
"new_type_name": "action_name",
"type": "name"
},{
"new_type_name": "transaction_id_type",
"type": "checksum256"
},{
"new_type_name": "weight_type",
"type": "uint16"
}],
"____comment": "eosio.bios structs: set_account_limits, setpriv, set_global_limits, producer_key, set_producers, require_auth are provided so abi available for deserialization in future.",
"structs": [{
"name": "permission_level",
"base": "",
"fields": [
{"name":"actor", "type":"account_name"},
{"name":"permission", "type":"permission_name"}
]
},{
"name": "key_weight",
"base": "",
"fields": [
{"name":"key", "type":"public_key"},
{"name":"weight", "type":"weight_type"}
]
},{
"name": "prod_meta",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"total_votes", "type":"float64"}
]
},{
"name": "bidname",
"base": "",
"fields": [
{"name":"bidder", "type":"account_name"},
{"name":"newname", "type":"account_name"},
{"name":"bid", "type":"asset"}
]
},{
"name": "permission_level_weight",
"base": "",
"fields": [
{"name":"permission", "type":"permission_level"},
{"name":"weight", "type":"weight_type"}
]
},{
"name": "wait_weight",
"base": "",
"fields": [
{"name":"wait_sec", "type":"uint32"},
{"name":"weight", "type":"weight_type"}
]
},{
"name": "authority",
"base": "",
"fields": [
{"name":"threshold", "type":"uint32"},
{"name":"keys", "type":"key_weight[]"},
{"name":"accounts", "type":"permission_level_weight[]"},
{"name":"waits", "type":"wait_weight[]"}
]
},{
"name": "newaccount",
"base": "",
"fields": [
{"name":"creator", "type":"account_name"},
{"name":"name", "type":"account_name"},
{"name":"owner", "type":"authority"},
{"name":"active", "type":"authority"}
]
},{
"name": "setcode",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"vmtype", "type":"uint8"},
{"name":"vmversion", "type":"uint8"},
{"name":"code", "type":"bytes"}
]
},{
"name": "setabi",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"abi", "type":"bytes"}
]
},{
"name": "updateauth",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"permission", "type":"permission_name"},
{"name":"parent", "type":"permission_name"},
{"name":"auth", "type":"authority"}
]
},{
"name": "deleteauth",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"permission", "type":"permission_name"}
]
},{
"name": "linkauth",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"code", "type":"account_name"},
{"name":"type", "type":"action_name"},
{"name":"requirement", "type":"permission_name"}
]
},{
"name": "unlinkauth",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"code", "type":"account_name"},
{"name":"type", "type":"action_name"}
]
},{
"name": "canceldelay",
"base": "",
"fields": [
{"name":"canceling_auth", "type":"permission_level"},
{"name":"trx_id", "type":"transaction_id_type"}
]
},{
"name": "onerror",
"base": "",
"fields": [
{"name":"sender_id", "type":"uint128"},
{"name":"sent_trx", "type":"bytes"}
]
},{
"name": "buyrambytes",
"base": "",
"fields": [
{"name":"payer", "type":"account_name"},
{"name":"receiver", "type":"account_name"},
{"name":"bytes", "type":"uint32"}
]
},{
"name": "sellram",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"bytes", "type":"uint64"}
]
},{
"name": "buyram",
"base": "",
"fields": [
{"name":"payer", "type":"account_name"},
{"name":"receiver", "type":"account_name"},
{"name":"quant", "type":"asset"}
]
},{
"name": "delegatebw",
"base": "",
"fields": [
{"name":"from", "type":"account_name"},
{"name":"receiver", "type":"account_name"},
{"name":"stake_net_quantity", "type":"asset"},
{"name":"stake_cpu_quantity", "type":"asset"},
{"name":"transfer", "type":"bool"}
]
},{
"name": "undelegatebw",
"base": "",
"fields": [
{"name":"from", "type":"account_name"},
{"name":"receiver", "type":"account_name"},
{"name":"unstake_net_quantity", "type":"asset"},
{"name":"unstake_cpu_quantity", "type":"asset"}
]
},{
"name": "refund",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"}
]
},{
"name": "delegated_bandwidth",
"base": "",
"fields": [
{"name":"from", "type":"account_name"},
{"name":"to", "type":"account_name"},
{"name":"net_weight", "type":"asset"},
{"name":"cpu_weight", "type":"asset"}
]
},{
"name": "user_resources",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"net_weight", "type":"asset"},
{"name":"cpu_weight", "type":"asset"},
{"name":"ram_bytes", "type":"uint64"}
]
},{
"name": "total_resources",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"net_weight", "type":"asset"},
{"name":"cpu_weight", "type":"asset"},
{"name":"ram_bytes", "type":"uint64"}
]
},{
"name": "refund_request",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"request_time", "type":"time_point_sec"},
{"name":"net_amount", "type":"asset"},
{"name":"cpu_amount", "type":"asset"}
]
},{
"name": "blockchain_parameters",
"base": "",
"fields": [
{"name":"max_block_net_usage", "type":"uint64"},
{"name":"target_block_net_usage_pct", "type":"uint32"},
{"name":"max_transaction_net_usage", "type":"uint32"},
{"name":"base_per_transaction_net_usage", "type":"uint32"},
{"name":"net_usage_leeway", "type":"uint32"},
{"name":"context_free_discount_net_usage_num", "type":"uint32"},
{"name":"context_free_discount_net_usage_den", "type":"uint32"},
{"name":"max_block_cpu_usage", "type":"uint32"},
{"name":"target_block_cpu_usage_pct", "type":"uint32"},
{"name":"max_transaction_cpu_usage", "type":"uint32"},
{"name":"min_transaction_cpu_usage", "type":"uint32"},
{"name":"max_transaction_lifetime", "type":"uint32"},
{"name":"deferred_trx_expiration_window", "type":"uint32"},
{"name":"max_transaction_delay", "type":"uint32"},
{"name":"max_inline_action_size", "type":"uint32"},
{"name":"max_inline_action_depth", "type":"uint16"},
{"name":"max_authority_depth", "type":"uint16"}
]
},{
"name": "eosio_global_state",
"base": "blockchain_parameters",
"fields": [
{"name":"max_ram_size", "type":"uint64"},
{"name":"total_ram_bytes_reserved", "type":"uint64"},
{"name":"total_ram_stake", "type":"int64"},
{"name":"last_producer_schedule_update", "type":"block_timestamp_type"},
{"name":"last_pervote_bucket_fill", "type":"uint64"},
{"name":"pervote_bucket", "type":"int64"},
{"name":"perblock_bucket", "type":"int64"},
{"name":"total_unpaid_blocks", "type":"uint32"},
{"name":"total_activated_stake", "type":"int64"},
{"name":"thresh_activated_stake_time", "type":"uint64"},
{"name":"last_producer_schedule_size", "type":"uint16"},
{"name":"total_producer_vote_weight", "type":"float64"},
{"name":"last_name_close", "type":"block_timestamp_type"}
]
},{
"name": "eosio_global_count",
"base": "",
"fields": [
{"name":"total_accounts", "type":"uint64"}
]
},{
"name": "producer_info",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"total_votes", "type":"float64"},
{"name":"producer_key", "type":"public_key"},
{"name":"is_active", "type":"bool"},
{"name":"url", "type":"string"},
{"name":"unpaid_blocks", "type":"uint32"},
{"name":"last_claim_time", "type":"uint64"},
{"name":"location", "type":"uint16"}
]
},{
"name": "producer_info_ext",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"seq_num", "type":"uint16"}
]
},{
"name": "producers_seq",
"base": "",
"fields": [
{"name":"seq_num", "type":"uint16"},
{"name":"prods_l1", "type":"prod_meta"}
{"name":"prods_l2", "type":"prod_meta[]"}
{"name":"prods_l3", "type":"prod_meta[]"}
]
},{
"name": "regproducer",
"base": "",
"fields": [
{"name":"producer", "type":"account_name"},
{"name":"producer_key", "type":"public_key"},
{"name":"url", "type":"string"},
{"name":"location", "type":"uint16"}
]
},{
"name": "unregprod",
"base": "",
"fields": [
{"name":"producer", "type":"account_name"}
]
},{
"name": "setram",
"base": "",
"fields": [
{"name":"max_ram_size", "type":"uint64"}
]
},{
"name": "regproxy",
"base": "",
"fields": [
{"name":"proxy", "type":"account_name"},
{"name":"isproxy", "type":"bool"}
]
},{
"name": "voteproducer",
"base": "",
"fields": [
{"name":"voter", "type":"account_name"},
{"name":"proxy", "type":"account_name"},
{"name":"producers", "type":"account_name[]"}
]
},{
"name": "voter_info",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"},
{"name":"proxy", "type":"account_name"},
{"name":"producers", "type":"account_name[]"},
{"name":"staked", "type":"int64"},
{"name":"last_vote_weight", "type":"float64"},
{"name":"proxied_vote_weight", "type":"float64"},
{"name":"is_proxy", "type":"bool"}
]
},{
"name": "claimrewards",
"base": "",
"fields": [
{"name":"owner", "type":"account_name"}
]
},{
"name": "setpriv",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"is_priv", "type":"int8"}
]
},{
"name": "rmvproducer",
"base": "",
"fields": [
{"name":"producer", "type":"account_name"}
]
},{
"name": "set_account_limits",
"base": "",
"fields": [
{"name":"account", "type":"account_name"},
{"name":"ram_bytes", "type":"int64"},
{"name":"net_weight", "type":"int64"},
{"name":"cpu_weight", "type":"int64"}
]
},{
"name": "set_global_limits",
"base": "",
"fields": [
{"name":"cpu_usec_per_period", "type":"int64"}
]
},{
"name": "producer_key",
"base": "",
"fields": [
{"name":"producer_name", "type":"account_name"},
{"name":"block_signing_key", "type":"public_key"}
]
},{
"name": "set_producers",
"base": "",
"fields": [
{"name":"schedule", "type":"producer_key[]"}
]
},{
"name": "require_auth",
"base": "",
"fields": [
{"name":"from", "type":"account_name"}
]
},{
"name": "setparams",
"base": "",
"fields": [
{"name":"params", "type":"blockchain_parameters"}
]
},{
"name": "connector",
"base": "",
"fields": [
{"name":"balance", "type":"asset"},
{"name":"weight", "type":"float64"}
]
},{
"name": "exchange_state",
"base": "",
"fields": [
{"name":"supply", "type":"asset"},
{"name":"base", "type":"connector"},
{"name":"quote", "type":"connector"}
]
}, {
"name": "namebid_info",
"base": "",
"fields": [
{"name":"newname", "type":"account_name"},
{"name":"high_bidder", "type":"account_name"},
{"name":"high_bid", "type":"int64"},
{"name":"last_bid_time", "type":"uint64"}
]
}
],
"actions": [{
"name": "newaccount",
"type": "newaccount",
"ricardian_contract": ""
},{
"name": "setcode",
"type": "setcode",
"ricardian_contract": ""
},{
"name": "setabi",
"type": "setabi",
"ricardian_contract": ""
},{
"name": "updateauth",
"type": "updateauth",
"ricardian_contract": ""
},{
"name": "deleteauth",
"type": "deleteauth",
"ricardian_contract": ""
},{
"name": "linkauth",
"type": "linkauth",
"ricardian_contract": ""
},{
"name": "unlinkauth",
"type": "unlinkauth",
"ricardian_contract": ""
},{
"name": "canceldelay",
"type": "canceldelay",
"ricardian_contract": ""
},{
"name": "onerror",
"type": "onerror",
"ricardian_contract": ""
},{
"name": "buyrambytes",
"type": "buyrambytes",
"ricardian_contract": ""
},{
"name": "buyram",
"type": "buyram",
"ricardian_contract": ""
},{
"name": "sellram",
"type": "sellram",
"ricardian_contract": ""
},{
"name": "delegatebw",
"type": "delegatebw",
"ricardian_contract": ""
},{
"name": "undelegatebw",
"type": "undelegatebw",
"ricardian_contract": ""
},{
"name": "refund",
"type": "refund",
"ricardian_contract": ""
},{
"name": "regproducer",
"type": "regproducer",
"ricardian_contract": ""
},{
"name": "setram",
"type": "setram",
"ricardian_contract": ""
},{
"name": "bidname",
"type": "bidname",
"ricardian_contract": ""
},{
"name": "unregprod",
"type": "unregprod",
"ricardian_contract": ""
},{
"name": "regproxy",
"type": "regproxy",
"ricardian_contract": ""
},{
"name": "voteproducer",
"type": "voteproducer",
"ricardian_contract": ""
},{
"name": "claimrewards",
"type": "claimrewards",
"ricardian_contract": ""
},{
"name": "setpriv",
"type": "setpriv",
"ricardian_contract": ""
},{
"name": "rmvproducer",
"type": "rmvproducer",
"ricardian_contract": ""
},{
"name": "setalimits",
"type": "set_account_limits",
"ricardian_contract": ""
},{
"name": "setglimits",
"type": "set_global_limits",
"ricardian_contract": ""
},{
"name": "setprods",
"type": "set_producers",
"ricardian_contract": ""
},{
"name": "reqauth",
"type": "require_auth",
"ricardian_contract": ""
},{
"name": "setparams",
"type": "setparams",
"ricardian_contract": ""
}],
"tables": [{
"name": "producers2",
"type": "producer_info_ext",
"index_type": "i64",
"key_names" : ["owner"],
"key_types" : ["uint64"]
},{
"name": "prods_seq",
"type": "producers_seq",
"index_type": "i64",
"key_names" : ["seq_num"],
"key_types" : ["uint64"]
},{
"name": "producers",
"type": "producer_info",
"index_type": "i64",
"key_names" : ["owner"],
"key_types" : ["uint64"]
},{
"name": "global",
"type": "eosio_global_state",
"index_type": "i64",
"key_names" : [],
"key_types" : []
},{
"name": "gcount",
"type": "eosio_global_count",
"index_type": "i64",
"key_names" : [],
"key_types" : []
},{
"name": "voters",
"type": "voter_info",
"index_type": "i64",
"key_names" : ["owner"],
"key_types" : ["account_name"]
},{
"name": "userres",
"type": "user_resources",
"index_type": "i64",
"key_names" : ["owner"],
"key_types" : ["uint64"]
},{
"name": "delband",
"type": "delegated_bandwidth",
"index_type": "i64",
"key_names" : ["to"],
"key_types" : ["uint64"]
},{
"name": "rammarket",
"type": "exchange_state",
"index_type": "i64",
"key_names" : ["supply"],
"key_types" : ["uint64"]
},{
"name": "refunds",
"type": "refund_request",
"index_type": "i64",
"key_names" : ["owner"],
"key_types" : ["uint64"]
},{
"name": "namebids",
"type": "namebid_info",
"index_type": "i64",
"key_names" : ["newname"],
"key_types" : ["account_name"]
}
],
"ricardian_clauses": [],
"abi_extensions": []
}
/**
* @file
* @copyright defined in eos/LICENSE
*/
#pragma once
#include <eosio.system/native.hpp>
#include <eosiolib/asset.hpp>
#include <eosiolib/time.hpp>
#include <eosiolib/privileged.hpp>
#include <eosiolib/singleton.hpp>
#include <eosio.system/exchange_state.hpp>
#include <string>
namespace eosiosystem {
using eosio::asset;
using eosio::indexed_by;
using eosio::const_mem_fun;
using eosio::block_timestamp;
struct name_bid {
account_name newname;
account_name high_bidder;
int64_t high_bid = 0; ///< negative high_bid == closed auction waiting to be claimed
uint64_t last_bid_time = 0;
auto primary_key()const { return newname; }
uint64_t by_high_bid()const { return static_cast<uint64_t>(-high_bid); }
};
typedef eosio::multi_index< N(namebids), name_bid,
indexed_by<N(highbid), const_mem_fun<name_bid, uint64_t, &name_bid::by_high_bid> >
> name_bid_table;
struct eosio_global_state : eosio::blockchain_parameters {
uint64_t free_ram()const { return max_ram_size - total_ram_bytes_reserved; }
uint64_t max_ram_size = 64ll*1024 * 1024 * 1024;
uint64_t total_ram_bytes_reserved = 0;
int64_t total_ram_stake = 0;
block_timestamp last_producer_schedule_update;
uint64_t last_pervote_bucket_fill = 0;
int64_t pervote_bucket = 0;
int64_t perblock_bucket = 0;
uint32_t total_unpaid_blocks = 0; /// all blocks which have been produced but not paid
int64_t total_activated_stake = 0;
uint64_t thresh_activated_stake_time = 0;
uint16_t last_producer_schedule_size = 0;
double total_producer_vote_weight = 0; /// the sum of all producer votes
block_timestamp last_name_close;
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE_DERIVED( eosio_global_state, eosio::blockchain_parameters,
(max_ram_size)(total_ram_bytes_reserved)(total_ram_stake)
(last_producer_schedule_update)(last_pervote_bucket_fill)
(pervote_bucket)(perblock_bucket)(total_unpaid_blocks)(total_activated_stake)(thresh_activated_stake_time)
(last_producer_schedule_size)(total_producer_vote_weight)(last_name_close) )
};
//##YTA-Change start:
struct eosio_global_count {
uint64_t total_accounts = 1;
EOSLIB_SERIALIZE( eosio_global_count, (total_accounts) )
};
//##YTA-Change end:
struct producer_info {
account_name owner;
double total_votes = 0;
eosio::public_key producer_key; /// a packed public key object
bool is_active = true;
std::string url;
uint32_t unpaid_blocks = 0;
uint64_t last_claim_time = 0;
uint16_t location = 0;
uint64_t primary_key()const { return owner; }
double by_votes()const { return is_active ? -total_votes : total_votes; }
bool active()const { return is_active; }
void deactivate() { producer_key = public_key(); is_active = false; }
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( producer_info, (owner)(total_votes)(producer_key)(is_active)(url)
(unpaid_blocks)(last_claim_time)(location) )
};
//##YTA-Change start:
struct producer_info_ext {
account_name owner;
uint16_t seq_num = 1; // from 1 to 21
uint64_t primary_key()const { return owner; }
EOSLIB_SERIALIZE( producer_info_ext, (owner)(seq_num) )
};
struct prod_meta {
account_name owner;
double total_votes = 0;
EOSLIB_SERIALIZE( prod_meta, (owner)(total_votes) )
};
struct producers_seq {
uint16_t seq_num = 1; // from 1 to 21
prod_meta prods_l1; // only one
std::vector<prod_meta> prods_l2; //max 5
std::vector<prod_meta> prods_l3;
uint64_t primary_key()const { return seq_num; }
EOSLIB_SERIALIZE( producers_seq, (seq_num) )
};
//##YTA-Change end:
struct voter_info {
account_name owner = 0; /// the voter
account_name proxy = 0; /// the proxy set by the voter, if any
std::vector<account_name> producers; /// the producers approved by this voter if no proxy set
int64_t staked = 0;
/**
* Every time a vote is cast we must first "undo" the last vote weight, before casting the
* new vote weight. Vote weight is calculated as:
*
* stated.amount * 2 ^ ( weeks_since_launch/weeks_per_year)
*/
double last_vote_weight = 0; /// the vote weight cast the last time the vote was updated
/**
* Total vote weight delegated to this voter.
*/
double proxied_vote_weight= 0; /// the total vote weight delegated to this voter as a proxy
bool is_proxy = 0; /// whether the voter is a proxy for others
uint32_t reserved1 = 0;
time reserved2 = 0;
eosio::asset reserved3;
uint64_t primary_key()const { return owner; }
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( voter_info, (owner)(proxy)(producers)(staked)(last_vote_weight)(proxied_vote_weight)(is_proxy)(reserved1)(reserved2)(reserved3) )
};
//##YTA-Change start:
struct user_resources {
account_name owner;
asset net_weight;
asset cpu_weight;
int64_t ram_bytes = 0;
uint64_t primary_key()const { return owner; }
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( user_resources, (owner)(net_weight)(cpu_weight)(ram_bytes) )
};
//##YTA-Change end:
//##YTA-Change start:
typedef eosio::multi_index< N(userres), user_resources> user_resources_table;
//##YTA-Change end:
typedef eosio::multi_index< N(voters), voter_info> voters_table;
typedef eosio::multi_index< N(producers), producer_info,
indexed_by<N(prototalvote), const_mem_fun<producer_info, double, &producer_info::by_votes> >
> producers_table;
//##YTA-Change start:
typedef eosio::multi_index< N(producers2), producer_info_ext> producers_ext_table;
typedef eosio::multi_index< N(prods_seq), producers_seq> producers_seq_table;
//##YTA-Change end:
typedef eosio::singleton<N(global), eosio_global_state> global_state_singleton;
//##YTA-Change start:
typedef eosio::singleton<N(gcount), eosio_global_count> global_count_singleton;
//##YTA-Change end:
// static constexpr uint32_t max_inflation_rate = 5; // 5% annual inflation
static constexpr uint32_t seconds_per_day = 24 * 3600;
static constexpr uint64_t system_token_symbol = CORE_SYMBOL;
class system_contract : public native {
private:
voters_table _voters;
producers_table _producers;
//##YTA-Change start:
producers_ext_table _producers2;
//##YTA-Change end:
global_state_singleton _global;
eosio_global_state _gstate;
rammarket _rammarket;
public:
system_contract( account_name s );
~system_contract();
// Actions:
void onblock( block_timestamp timestamp, account_name producer );
// const block_header& header ); /// only parse first 3 fields of block header
// functions defined in delegate_bandwidth.cpp
/**
* Stakes SYS from the balance of 'from' for the benfit of 'receiver'.
* If transfer == true, then 'receiver' can unstake to their account
* Else 'from' can unstake at any time.
*/
void delegatebw( account_name from, account_name receiver,
asset stake_net_quantity, asset stake_cpu_quantity, bool transfer );
/**
* Decreases the total tokens delegated by from to receiver and/or
* frees the memory associated with the delegation if there is nothing
* left to delegate.
*
* This will cause an immediate reduction in net/cpu bandwidth of the
* receiver.
*
* A transaction is scheduled to send the tokens back to 'from' after
* the staking period has passed. If existing transaction is scheduled, it
* will be canceled and a new transaction issued that has the combined
* undelegated amount.
*
* The 'from' account loses voting power as a result of this call and
* all producer tallies are updated.
*/
void undelegatebw( account_name from, account_name receiver,
asset unstake_net_quantity, asset unstake_cpu_quantity );
/**
* Increases receiver's ram quota based upon current price and quantity of
* tokens provided. An inline transfer from receiver to system contract of
* tokens will be executed.
*/
void buyram( account_name buyer, account_name receiver, asset tokens );
void buyrambytes( account_name buyer, account_name receiver, uint32_t bytes );
/**
* Reduces quota my bytes and then performs an inline transfer of tokens
* to receiver based upon the average purchase price of the original quota.
*/
void sellram( account_name receiver, int64_t bytes );
/**
* This action is called after the delegation-period to claim all pending
* unstaked tokens belonging to owner
*/
void refund( account_name owner );
// functions defined in voting.cpp
void regproducer( const account_name producer, const public_key& producer_key, const std::string& url, uint16_t location );
void unregprod( const account_name producer );
void setram( uint64_t max_ram_size );
void voteproducer( const account_name voter, const account_name proxy, const std::vector<account_name>& producers );
void regproxy( const account_name proxy, bool isproxy );
void setparams( const eosio::blockchain_parameters& params );
// functions defined in producer_pay.cpp
void claimrewards( const account_name& owner );
void setpriv( account_name account, uint8_t ispriv );
void rmvproducer( account_name producer );
void bidname( account_name bidder, account_name newname, asset bid );
private:
void update_elected_producers( block_timestamp timestamp );
//##YTA-Change start:
/// whether a procuder has qulification to produce block in next loop
bool is_qualification_producers( account_name name );
void update_producers_seq_totalvotes( uint64_t seq_num, account_name owner, double total_votes);
std::pair<eosio::producer_key,uint16_t> getProducerForSeq(uint64_t seq_num );
std::pair<eosio::producer_key,uint16_t> genProducerData( account_name owner );
//##YTA-Change end:
// Implementation details:
//defind in delegate_bandwidth.cpp
void changebw( account_name from, account_name receiver,
asset stake_net_quantity, asset stake_cpu_quantity, bool transfer );
//defined in voting.hpp
static eosio_global_state get_default_parameters();
void update_votes( const account_name voter, const account_name proxy, const std::vector<account_name>& producers, bool voting );
// defined in voting.cpp
void propagate_weight_change( const voter_info& voter );
};
} /// eosiosystem
#include "eosio.system.hpp"
#include <eosiolib/dispatcher.hpp>
#include "producer_pay.cpp"
#include "delegate_bandwidth.cpp"
#include "voting.cpp"
#include "exchange_state.cpp"
namespace eosiosystem {
system_contract::system_contract( account_name s )
:native(s),
_voters(_self,_self),
_producers(_self,_self),
_producers2(_self,_self),
_global(_self,_self),
_rammarket(_self,_self)
{
//print( "construct system\n" );
_gstate = _global.exists() ? _global.get() : get_default_parameters();
auto itr = _rammarket.find(S(4,RAMCORE));
if( itr == _rammarket.end() ) {
auto system_token_supply = eosio::token(N(eosio.token)).get_supply(eosio::symbol_type(system_token_symbol).name()).amount;
if( system_token_supply > 0 ) {
itr = _rammarket.emplace( _self, [&]( auto& m ) {
m.supply.amount = 100000000000000ll;
m.supply.symbol = S(4,RAMCORE);
m.base.balance.amount = int64_t(_gstate.free_ram());
m.base.balance.symbol = S(0,RAM);
m.quote.balance.amount = system_token_supply / 1000;
m.quote.balance.symbol = CORE_SYMBOL;
});
}
} else {
//print( "ram market already created" );
}
}
eosio_global_state system_contract::get_default_parameters() {
eosio_global_state dp;
get_blockchain_parameters(dp);
return dp;
}
system_contract::~system_contract() {
//print( "destruct system\n" );
_global.set( _gstate, _self );
//eosio_exit(0);
}
void system_contract::setram( uint64_t max_ram_size ) {
require_auth( _self );
eosio_assert( _gstate.max_ram_size < max_ram_size, "ram may only be increased" ); /// decreasing ram might result market maker issues
eosio_assert( max_ram_size < 1024ll*1024*1024*1024*1024, "ram size is unrealistic" );
eosio_assert( max_ram_size > _gstate.total_ram_bytes_reserved, "attempt to set max below reserved" );
auto delta = int64_t(max_ram_size) - int64_t(_gstate.max_ram_size);
auto itr = _rammarket.find(S(4,RAMCORE));
/**
* Increase or decrease the amount of ram for sale based upon the change in max
* ram size.
*/
_rammarket.modify( itr, 0, [&]( auto& m ) {
m.base.balance.amount += delta;
});
_gstate.max_ram_size = max_ram_size;
_global.set( _gstate, _self );
}
void system_contract::setparams( const eosio::blockchain_parameters& params ) {
require_auth( N(eosio) );
(eosio::blockchain_parameters&)(_gstate) = params;
eosio_assert( 3 <= _gstate.max_authority_depth, "max_authority_depth should be at least 3" );
set_blockchain_parameters( params );
}
void system_contract::setpriv( account_name account, uint8_t ispriv ) {
require_auth( _self );
set_privileged( account, ispriv );
}
void system_contract::rmvproducer( account_name producer ) {
require_auth( _self );
auto prod = _producers.find( producer );
eosio_assert( prod != _producers.end(), "producer not found" );
_producers.modify( prod, 0, [&](auto& p) {
p.deactivate();
});
}
void system_contract::bidname( account_name bidder, account_name newname, asset bid ) {
require_auth( bidder );
eosio_assert( eosio::name_suffix(newname) == newname, "you can only bid on top-level suffix" );
eosio_assert( newname != 0, "the empty name is not a valid account name to bid on" );
eosio_assert( (newname & 0xFull) == 0, "13 character names are not valid account names to bid on" );
eosio_assert( (newname & 0x1F0ull) == 0, "accounts with 12 character names and no dots can be created without bidding required" );
eosio_assert( !is_account( newname ), "account already exists" );
eosio_assert( bid.symbol == asset().symbol, "asset must be system token" );
eosio_assert( bid.amount > 0, "insufficient bid" );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {bidder,N(active)},
{ bidder, N(eosio.names), bid, std::string("bid name ")+(name{newname}).to_string() } );
name_bid_table bids(_self,_self);
print( name{bidder}, " bid ", bid, " on ", name{newname}, "\n" );
auto current = bids.find( newname );
if( current == bids.end() ) {
bids.emplace( bidder, [&]( auto& b ) {
b.newname = newname;
b.high_bidder = bidder;
b.high_bid = bid.amount;
b.last_bid_time = current_time();
});
} else {
eosio_assert( current->high_bid > 0, "this auction has already closed" );
eosio_assert( bid.amount - current->high_bid > (current->high_bid / 10), "must increase bid by 10%" );
eosio_assert( current->high_bidder != bidder, "account is already highest bidder" );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio.names),N(active)},
{ N(eosio.names), current->high_bidder, asset(current->high_bid),
std::string("refund bid on name ")+(name{newname}).to_string() } );
bids.modify( current, bidder, [&]( auto& b ) {
b.high_bidder = bidder;
b.high_bid = bid.amount;
b.last_bid_time = current_time();
});
}
}
/**
* Called after a new account is created. This code enforces resource-limits rules
* for new accounts as well as new account naming conventions.
*
* Account names containing '.' symbols must have a suffix equal to the name of the creator.
* This allows users who buy a premium name (shorter than 12 characters with no dots) to be the only ones
* who can create accounts with the creator's name as a suffix.
*
*/
void native::newaccount( account_name creator,
account_name newact
/* no need to parse authorities
const authority& owner,
const authority& active*/ ) {
if( creator != _self ) {
auto tmp = newact >> 4;
bool has_dot = false;
for( uint32_t i = 0; i < 12; ++i ) {
has_dot |= !(tmp & 0x1f);
tmp >>= 5;
}
if( has_dot ) { // or is less than 12 characters
auto suffix = eosio::name_suffix(newact);
if( suffix == newact ) {
name_bid_table bids(_self,_self);
auto current = bids.find( newact );
eosio_assert( current != bids.end(), "no active bid for name" );
eosio_assert( current->high_bidder == creator, "only highest bidder can claim" );
eosio_assert( current->high_bid < 0, "auction for name is not closed yet" );
bids.erase( current );
} else {
eosio_assert( creator == suffix, "only suffix may create this account" );
}
}
}
user_resources_table userres( _self, newact);
userres.emplace( newact, [&]( auto& res ) {
res.owner = newact;
});
set_resource_limits( newact, 0, 0, 0 );
//##YTA-Change start:
global_count_singleton global(_self, _self);
eosio_global_count gstate;
if(global.exists()) {
gstate = global.get();
}
gstate.total_accounts += 1;
global.set( gstate, _self );
//##YTA-Change end:
}
} /// eosio.system
EOSIO_ABI( eosiosystem::system_contract,
// native.hpp (newaccount definition is actually in eosio.system.cpp)
(newaccount)(updateauth)(deleteauth)(linkauth)(unlinkauth)(canceldelay)(onerror)
// eosio.system.cpp
(setram)(setparams)(setpriv)(rmvproducer)(bidname)
// delegate_bandwidth.cpp
(buyrambytes)(buyram)(sellram)(delegatebw)(undelegatebw)(refund)
// voting.cpp
(regproducer)(unregprod)(voteproducer)(regproxy)
// producer_pay.cpp
(onblock)(claimrewards)
)
#include <eosio.system/exchange_state.hpp>
namespace eosiosystem {
asset exchange_state::convert_to_exchange( connector& c, asset in ) {
real_type R(supply.amount);
real_type C(c.balance.amount+in.amount);
real_type F(c.weight/1000.0);
real_type T(in.amount);
real_type ONE(1.0);
real_type E = -R * (ONE - std::pow( ONE + T / C, F) );
//print( "E: ", E, "\n");
int64_t issued = int64_t(E);
supply.amount += issued;
c.balance.amount += in.amount;
return asset( issued, supply.symbol );
}
asset exchange_state::convert_from_exchange( connector& c, asset in ) {
eosio_assert( in.symbol== supply.symbol, "unexpected asset symbol input" );
real_type R(supply.amount - in.amount);
real_type C(c.balance.amount);
real_type F(1000.0/c.weight);
real_type E(in.amount);
real_type ONE(1.0);
// potentially more accurate:
// The functions std::expm1 and std::log1p are useful for financial calculations, for example,
// when calculating small daily interest rates: (1+x)n
// -1 can be expressed as std::expm1(n * std::log1p(x)).
// real_type T = C * std::expm1( F * std::log1p(E/R) );
real_type T = C * (std::pow( ONE + E/R, F) - ONE);
//print( "T: ", T, "\n");
int64_t out = int64_t(T);
supply.amount -= in.amount;
c.balance.amount -= out;
return asset( out, c.balance.symbol );
}
asset exchange_state::convert( asset from, symbol_type to ) {
auto sell_symbol = from.symbol;
auto ex_symbol = supply.symbol;
auto base_symbol = base.balance.symbol;
auto quote_symbol = quote.balance.symbol;
//print( "From: ", from, " TO ", asset( 0,to), "\n" );
//print( "base: ", base_symbol, "\n" );
//print( "quote: ", quote_symbol, "\n" );
//print( "ex: ", supply.symbol, "\n" );
if( sell_symbol != ex_symbol ) {
if( sell_symbol == base_symbol ) {
from = convert_to_exchange( base, from );
} else if( sell_symbol == quote_symbol ) {
from = convert_to_exchange( quote, from );
} else {
eosio_assert( false, "invalid sell" );
}
} else {
if( to == base_symbol ) {
from = convert_from_exchange( base, from );
} else if( to == quote_symbol ) {
from = convert_from_exchange( quote, from );
} else {
eosio_assert( false, "invalid conversion" );
}
}
if( to != from.symbol )
return convert( from, to );
return from;
}
} /// namespace eosiosystem
#pragma once
#include <eosiolib/asset.hpp>
namespace eosiosystem {
using eosio::asset;
using eosio::symbol_type;
typedef double real_type;
/**
* Uses Bancor math to create a 50/50 relay between two asset types. The state of the
* bancor exchange is entirely contained within this struct. There are no external
* side effects associated with using this API.
*/
struct exchange_state {
asset supply;
struct connector {
asset balance;
double weight = .5;
EOSLIB_SERIALIZE( connector, (balance)(weight) )
};
connector base;
connector quote;
uint64_t primary_key()const { return supply.symbol; }
asset convert_to_exchange( connector& c, asset in );
asset convert_from_exchange( connector& c, asset in );
asset convert( asset from, symbol_type to );
EOSLIB_SERIALIZE( exchange_state, (supply)(base)(quote) )
};
typedef eosio::multi_index<N(rammarket), exchange_state> rammarket;
} /// namespace eosiosystem
/**
* @file
* @copyright defined in eos/LICENSE
*/
#pragma once
#include <eosiolib/action.hpp>
#include <eosiolib/public_key.hpp>
#include <eosiolib/types.hpp>
#include <eosiolib/print.hpp>
#include <eosiolib/privileged.h>
#include <eosiolib/optional.hpp>
#include <eosiolib/producer_schedule.hpp>
#include <eosiolib/contract.hpp>
namespace eosiosystem {
using eosio::permission_level;
using eosio::public_key;
typedef std::vector<char> bytes;
struct permission_level_weight {
permission_level permission;
weight_type weight;
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( permission_level_weight, (permission)(weight) )
};
struct key_weight {
public_key key;
weight_type weight;
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( key_weight, (key)(weight) )
};
struct authority {
uint32_t threshold;
uint32_t delay_sec;
std::vector<key_weight> keys;
std::vector<permission_level_weight> accounts;
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE( authority, (threshold)(delay_sec)(keys)(accounts) )
};
struct block_header {
uint32_t timestamp;
account_name producer;
uint16_t confirmed = 0;
block_id_type previous;
checksum256 transaction_mroot;
checksum256 action_mroot;
uint32_t schedule_version = 0;
eosio::optional<eosio::producer_schedule> new_producers;
// explicit serialization macro is not necessary, used here only to improve compilation time
EOSLIB_SERIALIZE(block_header, (timestamp)(producer)(confirmed)(previous)(transaction_mroot)(action_mroot)
(schedule_version)(new_producers))
};
/*
* Method parameters commented out to prevent generation of code that parses input data.
*/
class native : public eosio::contract {
public:
using eosio::contract::contract;
/**
* Called after a new account is created. This code enforces resource-limits rules
* for new accounts as well as new account naming conventions.
*
* 1. accounts cannot contain '.' symbols which forces all acccounts to be 12
* characters long without '.' until a future account auction process is implemented
* which prevents name squatting.
*
* 2. new accounts must stake a minimal number of tokens (as set in system parameters)
* therefore, this method will execute an inline buyram from receiver for newacnt in
* an amount equal to the current new account creation fee.
*/
void newaccount( account_name creator,
account_name newact
/* no need to parse authorites
const authority& owner,
const authority& active*/ );
void updateauth( /*account_name account,
permission_name permission,
permission_name parent,
const authority& data*/ ) {}
void deleteauth( /*account_name account, permission_name permission*/ ) {}
void linkauth( /*account_name account,
account_name code,
action_name type,
permission_name requirement*/ ) {}
void unlinkauth( /*account_name account,
account_name code,
action_name type*/ ) {}
void canceldelay( /*permission_level canceling_auth, transaction_id_type trx_id*/ ) {}
void onerror( /*const bytes&*/ ) {}
};
}
#include "eosio.system.hpp"
#include <eosiolib/print.hpp>
#include <eosio.token/eosio.token.hpp>
namespace eosiosystem {
const int64_t min_pervote_daily_pay = 100'0000;
//##YTA-Change start:
//Change total vote rate from 15% to 1% for test network
//const int64_t min_activated_stake = 150'000'000'0000;
const int64_t min_activated_stake = 10'000'000'0000;
//##YTA-Change end:
const uint32_t blocks_per_year = 52*7*24*2*3600; // half seconds per year
const uint32_t seconds_per_year = 52*7*24*3600;
const uint32_t blocks_per_day = 2 * 24 * 3600;
const uint32_t blocks_per_hour = 2 * 3600;
const uint64_t useconds_per_day = 24 * 3600 * uint64_t(1000000);
const uint64_t useconds_per_year = seconds_per_year*1000000ll;
const int64_t block_initial_timestamp = 1551369600ll; // epoch year 2019.03.01 unix timestamp 1551369600s
//yta seo total= yta_seo_year[i] * YTA_SEO_BASE
const uint32_t YTA_SEO_BASE = 10'0000;
const double YTA_PRECISION =10000.0000;
const uint32_t yta_seo_year[62] = {
1000, 900, 800, 700,
600, 600, 500, 500,
400, 400, 300, 300,
200, 200, 200,
100, 100, 100,
90, 90, 90,
80, 80, 80,
70, 70, 70, 70,
60, 60, 60, 60,
50, 50, 50, 50, 50,
40, 40, 40, 40, 40,
30, 30, 30, 30, 30,
20, 20, 20, 20, 20,
10, 10, 10, 10, 10,
9, 9, 9, 9, 9
};
void system_contract::onblock( block_timestamp timestamp, account_name producer ) {
using namespace eosio;
require_auth(N(eosio));
/** until activated stake crosses this threshold no new rewards are paid */
if( _gstate.total_activated_stake < min_activated_stake )
return;
if( _gstate.last_pervote_bucket_fill == 0 ) /// start the presses
_gstate.last_pervote_bucket_fill = current_time();
/**
* At startup the initial producer may not be one that is registered / elected
* and therefore there may be no producer object for them.
*/
auto prod = _producers.find(producer);
if ( prod != _producers.end() ) {
_gstate.total_unpaid_blocks++;
_producers.modify( prod, 0, [&](auto& p ) {
p.unpaid_blocks++;
});
}
//##YTA-Change start:
/// only update block producers once every minute, block_timestamp is in half seconds
//if( timestamp.slot - _gstate.last_producer_schedule_update.slot > 120 ) {
/// update block producers once every two minute due to election strategy is more complex than before
if( timestamp.slot - _gstate.last_producer_schedule_update.slot > 240 ) {
//##YTA-Change end:
update_elected_producers( timestamp );
if( (timestamp.slot - _gstate.last_name_close.slot) > blocks_per_day ) {
name_bid_table bids(_self,_self);
auto idx = bids.get_index<N(highbid)>();
auto highest = idx.begin();
if( highest != idx.end() &&
highest->high_bid > 0 &&
highest->last_bid_time < (current_time() - useconds_per_day) &&
_gstate.thresh_activated_stake_time > 0 &&
(current_time() - _gstate.thresh_activated_stake_time) > 14 * useconds_per_day ) {
_gstate.last_name_close = timestamp;
idx.modify( highest, 0, [&]( auto& b ){
b.high_bid = -b.high_bid;
});
}
}
}
}
using namespace eosio;
void system_contract::claimrewards( const account_name& owner ) {
require_auth(owner);
const auto& prod = _producers.get( owner );
eosio_assert( prod.active(), "producer does not have an active key" );
eosio_assert( _gstate.total_activated_stake >= min_activated_stake,
"cannot claim rewards until the chain is activated (at least 15% of all tokens participate in voting)" );
auto ct = current_time();
//eosio_assert( ct - prod.last_claim_time > useconds_per_day, "already claimed rewards within past day" );
const asset token_supply = token( N(eosio.token)).get_supply(symbol_type(system_token_symbol).name() );
const auto usecs_since_last_fill = ct - _gstate.last_pervote_bucket_fill;
print("usecs_since_last_fill: ", usecs_since_last_fill, "\n");
print("_gstate.last_pervote_bucket_fill: ", _gstate.last_pervote_bucket_fill, "\n");
print("now(): ", now(), "\n");
int idx_year = (int)((now()- block_initial_timestamp) / seconds_per_year);
auto seo_token = yta_seo_year[idx_year] * YTA_SEO_BASE;
print("idx_year: ", idx_year, "\n");
print("yta_seo_year[idx_year]: ", yta_seo_year[idx_year], "\n");
print( "token_supply: ", token_supply, "\n");
print("seo_token: ", seo_token, "\n");
if( usecs_since_last_fill > 0 && _gstate.last_pervote_bucket_fill > 0 ) {
auto new_tokens = static_cast<int64_t>(seo_token * YTA_PRECISION * double(usecs_since_last_fill)/double(useconds_per_year));
print("new_token: ", new_tokens, "\n");
auto to_producers = new_tokens;
auto to_per_block_pay = to_producers / 4;
auto to_per_vote_pay = to_producers - to_per_block_pay;
INLINE_ACTION_SENDER(eosio::token, issue)( N(eosio.token), {{N(eosio),N(active)}},
{N(eosio), asset(new_tokens), std::string("issue tokens for producer pay and savings")} );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio),N(active)},
{ N(eosio), N(eosio.bpay), asset(to_per_block_pay), "fund per-block bucket" } );
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio),N(active)},
{ N(eosio), N(eosio.vpay), asset(to_per_vote_pay), "fund per-vote bucket" } );
_gstate.pervote_bucket += to_per_vote_pay;
_gstate.perblock_bucket += to_per_block_pay;
_gstate.last_pervote_bucket_fill = ct;
}
int64_t producer_per_block_pay = 0;
if( _gstate.total_unpaid_blocks > 0 ) {
producer_per_block_pay = (_gstate.perblock_bucket * prod.unpaid_blocks) / _gstate.total_unpaid_blocks;
}
int64_t producer_per_vote_pay = 0;
if( _gstate.total_producer_vote_weight > 0 ) {
producer_per_vote_pay = int64_t((_gstate.pervote_bucket * prod.total_votes ) / _gstate.total_producer_vote_weight);
}
if( producer_per_vote_pay < min_pervote_daily_pay ) {
producer_per_vote_pay = 0;
}
_gstate.pervote_bucket -= producer_per_vote_pay;
_gstate.perblock_bucket -= producer_per_block_pay;
_gstate.total_unpaid_blocks -= prod.unpaid_blocks;
_producers.modify( prod, 0, [&](auto& p) {
p.last_claim_time = ct;
p.unpaid_blocks = 0;
});
if( producer_per_block_pay > 0 ) {
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio.bpay),N(active)},
{ N(eosio.bpay), owner, asset(producer_per_block_pay), std::string("producer block pay") } );
}
if( producer_per_vote_pay > 0 ) {
INLINE_ACTION_SENDER(eosio::token, transfer)( N(eosio.token), {N(eosio.vpay),N(active)},
{ N(eosio.vpay), owner, asset(producer_per_vote_pay), std::string("producer vote pay") } );
}
}
} //namespace eosiosystem
/**
* @file
* @copyright defined in eos/LICENSE
*/
#include "eosio.system.hpp"
#include <eosiolib/eosio.hpp>
#include <eosiolib/crypto.h>
#include <eosiolib/print.hpp>
#include <eosiolib/datastream.hpp>
#include <eosiolib/serialize.hpp>
#include <eosiolib/multi_index.hpp>
#include <eosiolib/privileged.hpp>
#include <eosiolib/singleton.hpp>
#include <eosiolib/transaction.hpp>
#include <eosio.token/eosio.token.hpp>
#include <algorithm>
#include <cmath>
namespace eosiosystem {
using eosio::indexed_by;
using eosio::const_mem_fun;
using eosio::bytes;
using eosio::print;
using eosio::singleton;
using eosio::transaction;
using std::swap;
/**
* This method will create a producer_config and producer_info object for 'producer'
*
* @pre producer is not already registered
* @pre producer to register is an account
* @pre authority of producer to register
*
*/
void system_contract::regproducer( const account_name producer, const eosio::public_key& producer_key, const std::string& url, uint16_t location ) {
eosio_assert( url.size() < 512, "url too long" );
eosio_assert( producer_key != eosio::public_key(), "public key should not be the default value" );
require_auth( producer );
auto prod = _producers.find( producer );
if ( prod != _producers.end() ) {
_producers.modify( prod, producer, [&]( producer_info& info ){
info.producer_key = producer_key;
info.is_active = true;
info.url = url;
info.location = location;
});
} else {
_producers.emplace( producer, [&]( producer_info& info ){
info.owner = producer;
info.total_votes = 0;
info.producer_key = producer_key;
info.is_active = true;
info.url = url;
info.location = location;
});
}
//##YTA-Change start:
auto prod_ext = _producers2.find( producer );
if ( prod_ext == _producers2.end() ) {
_producers2.emplace( producer, [&]( producer_info_ext& info ){
info.owner = producer;
info.seq_num = 1; //This should have some policy to generate the seq_num
});
}
//##YTA-Change end:
}
void system_contract::unregprod( const account_name producer ) {
require_auth( producer );
const auto& prod = _producers.get( producer, "producer not found" );
_producers.modify( prod, 0, [&]( producer_info& info ){
info.deactivate();
});
}
//##YTA-Change start:
/*
void system_contract::update_elected_producers( block_timestamp block_time ) {
_gstate.last_producer_schedule_update = block_time;
auto idx = _producers.get_index<N(prototalvote)>();
std::vector< std::pair<eosio::producer_key,uint16_t> > top_producers;
top_producers.reserve(21);
for ( auto it = idx.cbegin(); it != idx.cend() && top_producers.size() < 21 && 0 < it->total_votes && it->active(); ++it ) {
top_producers.emplace_back( std::pair<eosio::producer_key,uint16_t>({{it->owner, it->producer_key}, it->location}) );
}
if ( top_producers.size() < _gstate.last_producer_schedule_size ) {
return;
}
/// sort by producer name
std::sort( top_producers.begin(), top_producers.end() );
std::vector<eosio::producer_key> producers;
producers.reserve(top_producers.size());
for( const auto& item : top_producers )
producers.push_back(item.first);
bytes packed_schedule = pack(producers);
if( set_proposed_producers( packed_schedule.data(), packed_schedule.size() ) >= 0 ) {
_gstate.last_producer_schedule_size = static_cast<decltype(_gstate.last_producer_schedule_size)>( top_producers.size() );
}
}
*/
void system_contract::update_elected_producers( block_timestamp block_time ) {
_gstate.last_producer_schedule_update = block_time;
std::vector< std::pair<eosio::producer_key,uint16_t> > top_producers;
top_producers.reserve(21);
for( uint16_t seq_num = 1; seq_num <= 21 ; seq_num++ ) {
std::pair<eosio::producer_key,uint16_t> ppinfo = getProducerForSeq( seq_num );
if( ppinfo.first.producer_name != 0 ) {
top_producers.emplace_back( ppinfo );
}
}
if ( top_producers.size() < _gstate.last_producer_schedule_size ) {
return;
}
/// sort by producer name
std::sort( top_producers.begin(), top_producers.end() );
std::vector<eosio::producer_key> producers;
producers.reserve(top_producers.size());
for( const auto& item : top_producers )
producers.push_back(item.first);
bytes packed_schedule = pack(producers);
if( set_proposed_producers( packed_schedule.data(), packed_schedule.size() ) >= 0 ) {
_gstate.last_producer_schedule_size = static_cast<decltype(_gstate.last_producer_schedule_size)>( top_producers.size() );
}
}
std::pair<eosio::producer_key,uint16_t> system_contract::getProducerForSeq(uint64_t seq_num ) {
producers_seq_table prodseq(_self, seq_num);
auto ps_itr = prodseq.find (seq_num);
if( ps_itr == prodseq.end() )
return std::pair<eosio::producer_key,uint16_t>({{0, eosio::public_key{}}, 0});
if(is_qualification_producers(ps_itr->prods_l1.owner)) {
return genProducerData(ps_itr->prods_l1.owner);
}
prodseq.modify( ps_itr, _self, [&]( producers_seq& info ){
info.prods_l3.push_back(info.prods_l1);
sort(info.prods_l2.begin(), info.prods_l2.end() ,[](const prod_meta &a, const prod_meta &b) { return a.total_votes < b.total_votes; });
if( info.prods_l2.begin() != info.prods_l2.end() ) {
info.prods_l1 = *(info.prods_l2.begin());
info.prods_l2.erase(info.prods_l2.begin());
}
sort(info.prods_l3.begin(), info.prods_l3.end() ,[](const prod_meta &a, const prod_meta &b) { return a.total_votes < b.total_votes; });
if( info.prods_l3.begin() != info.prods_l3.end() ) {
info.prods_l1 = *(info.prods_l3.begin());
info.prods_l3.erase(info.prods_l3.begin());
}
});
return genProducerData(ps_itr->prods_l1.owner);
}
std::pair<eosio::producer_key,uint16_t> system_contract::genProducerData(account_name owner) {
auto prod = _producers.find(owner);
if ( prod == _producers.end() ) {
return std::pair<eosio::producer_key,uint16_t>({{0, eosio::public_key{}}, 0});
}
return std::pair<eosio::producer_key,uint16_t>({{prod->owner , prod->producer_key}, prod->location});
}
bool system_contract::is_qualification_producers( account_name name ) {
auto prod = _producers.find(name);
if ( prod == _producers.end() ) {
return false;
}
if( !prod->active())
return false;
user_resources_table userres( _self, name );
auto res_itr = userres.find( name );
if( res_itr == userres.end() ) {
return false;
}
asset totalbw = res_itr->cpu_weight + res_itr->net_weight;
asset threshold{ 5000000000, CORE_SYMBOL };
if( totalbw < threshold)
return false;
return true;
}
void system_contract::update_producers_seq_totalvotes( uint64_t seq_num, account_name owner, double total_votes) {
if(seq_num == 0 || seq_num > 21)
return;
producers_seq_table prodseq(_self, seq_num);
auto ps_itr = prodseq.find (seq_num);
prodseq.modify( ps_itr, _self, [&]( producers_seq& info ){
if( info.prods_l1.owner == owner ) {
info.prods_l1.total_votes = total_votes;
return;
}
for(auto it = info.prods_l2.begin(); it != info.prods_l2.end(); it++) {
if(it->owner == owner) {
it->total_votes = total_votes;
return;
}
}
for(auto it = info.prods_l3.begin(); it != info.prods_l3.end(); it++) {
if(it->owner == owner) {
it->total_votes = total_votes;
return;
}
}
});
}
//##YTA-Change end:
double stake2vote( int64_t staked ) {
/// TODO subtract 2080 brings the large numbers closer to this decade
double weight = int64_t( (now() - (block_timestamp::block_timestamp_epoch / 1000)) / (seconds_per_day * 7) ) / double( 52 );
return double(staked) * std::pow( 2, weight );
}
/**
* @pre producers must be sorted from lowest to highest and must be registered and active
* @pre if proxy is set then no producers can be voted for
* @pre if proxy is set then proxy account must exist and be registered as a proxy
* @pre every listed producer or proxy must have been previously registered
* @pre voter must authorize this action
* @pre voter must have previously staked some EOS for voting
* @pre voter->staked must be up to date
*
* @post every producer previously voted for will have vote reduced by previous vote weight
* @post every producer newly voted for will have vote increased by new vote amount
* @post prior proxy will proxied_vote_weight decremented by previous vote weight
* @post new proxy will proxied_vote_weight incremented by new vote weight
*
* If voting for a proxy, the producer votes will not change until the proxy updates their own vote.
*/
void system_contract::voteproducer( const account_name voter_name, const account_name proxy, const std::vector<account_name>& producers ) {
require_auth( voter_name );
update_votes( voter_name, proxy, producers, true );
}
void system_contract::update_votes( const account_name voter_name, const account_name proxy, const std::vector<account_name>& producers, bool voting ) {
//validate input
if ( proxy ) {
eosio_assert( producers.size() == 0, "cannot vote for producers and proxy at same time" );
eosio_assert( voter_name != proxy, "cannot proxy to self" );
require_recipient( proxy );
} else {
//##YTA-Change start:
//eosio_assert( producers.size() <= 30, "attempt to vote for too many producers" );
// One voter can only vote for one producer
eosio_assert( producers.size() <= 1, "attempt to vote for too many producers" );
//##YTA-Change end:
for( size_t i = 1; i < producers.size(); ++i ) {
eosio_assert( producers[i-1] < producers[i], "producer votes must be unique and sorted" );
}
}
auto voter = _voters.find(voter_name);
eosio_assert( voter != _voters.end(), "user must stake before they can vote" ); /// staking creates voter object
eosio_assert( !proxy || !voter->is_proxy, "account registered as a proxy is not allowed to use a proxy" );
/**
* The first time someone votes we calculate and set last_vote_weight, since they cannot unstake until
* after total_activated_stake hits threshold, we can use last_vote_weight to determine that this is
* their first vote and should consider their stake activated.
*/
if( voter->last_vote_weight <= 0.0 ) {
_gstate.total_activated_stake += voter->staked;
if( _gstate.total_activated_stake >= min_activated_stake && _gstate.thresh_activated_stake_time == 0 ) {
_gstate.thresh_activated_stake_time = current_time();
}
}
auto new_vote_weight = stake2vote( voter->staked );
if( voter->is_proxy ) {
new_vote_weight += voter->proxied_vote_weight;
}
boost::container::flat_map<account_name, pair<double, bool /*new*/> > producer_deltas;
if ( voter->last_vote_weight > 0 ) {
if( voter->proxy ) {
auto old_proxy = _voters.find( voter->proxy );
eosio_assert( old_proxy != _voters.end(), "old proxy not found" ); //data corruption
_voters.modify( old_proxy, 0, [&]( auto& vp ) {
vp.proxied_vote_weight -= voter->last_vote_weight;
});
propagate_weight_change( *old_proxy );
} else {
for( const auto& p : voter->producers ) {
auto& d = producer_deltas[p];
d.first -= voter->last_vote_weight;
d.second = false;
}
}
}
if( proxy ) {
auto new_proxy = _voters.find( proxy );
eosio_assert( new_proxy != _voters.end(), "invalid proxy specified" ); //if ( !voting ) { data corruption } else { wrong vote }
eosio_assert( !voting || new_proxy->is_proxy, "proxy not found" );
if ( new_vote_weight >= 0 ) {
_voters.modify( new_proxy, 0, [&]( auto& vp ) {
vp.proxied_vote_weight += new_vote_weight;
});
propagate_weight_change( *new_proxy );
}
} else {
if( new_vote_weight >= 0 ) {
for( const auto& p : producers ) {
auto& d = producer_deltas[p];
d.first += new_vote_weight;
d.second = true;
}
}
}
for( const auto& pd : producer_deltas ) {
double total_votes = 0;
auto pitr = _producers.find( pd.first );
if( pitr != _producers.end() ) {
eosio_assert( !voting || pitr->active() || !pd.second.second /* not from new set */, "producer is not currently registered" );
_producers.modify( pitr, 0, [&]( auto& p ) {
p.total_votes += pd.second.first;
if ( p.total_votes < 0 ) { // floating point arithmetics can give small negative numbers
p.total_votes = 0;
}
_gstate.total_producer_vote_weight += pd.second.first;
//eosio_assert( p.total_votes >= 0, "something bad happened" );
total_votes = p.total_votes;
});
} else {
eosio_assert( !pd.second.second /* not from new set */, "producer is not registered" ); //data corruption
}
//##YTA-Change start:
auto pitr2 = _producers2.find( pd.first );
if( pitr2 != _producers2.end() ) {
//pitr2->seq_num
update_producers_seq_totalvotes(pitr2->seq_num, pd.first, total_votes);
} else {
eosio_assert( !pd.second.second /* not from new set */, "producer is not registered" ); //data corruption
}
//##YTA-Change end:
}
_voters.modify( voter, 0, [&]( auto& av ) {
av.last_vote_weight = new_vote_weight;
av.producers = producers;
av.proxy = proxy;
});
}
/**
* An account marked as a proxy can vote with the weight of other accounts which
* have selected it as a proxy. Other accounts must refresh their voteproducer to
* update the proxy's weight.
*
* @param isproxy - true if proxy wishes to vote on behalf of others, false otherwise
* @pre proxy must have something staked (existing row in voters table)
* @pre new state must be different than current state
*/
void system_contract::regproxy( const account_name proxy, bool isproxy ) {
require_auth( proxy );
auto pitr = _voters.find(proxy);
if ( pitr != _voters.end() ) {
eosio_assert( isproxy != pitr->is_proxy, "action has no effect" );
eosio_assert( !isproxy || !pitr->proxy, "account that uses a proxy is not allowed to become a proxy" );
_voters.modify( pitr, 0, [&]( auto& p ) {
p.is_proxy = isproxy;
});
propagate_weight_change( *pitr );
} else {
_voters.emplace( proxy, [&]( auto& p ) {
p.owner = proxy;
p.is_proxy = isproxy;
});
}
}
void system_contract::propagate_weight_change( const voter_info& voter ) {
eosio_assert( voter.proxy == 0 || !voter.is_proxy, "account registered as a proxy is not allowed to use a proxy" );
double new_weight = stake2vote( voter.staked );
if ( voter.is_proxy ) {
new_weight += voter.proxied_vote_weight;
}
/// don't propagate small changes (1 ~= epsilon)
if ( fabs( new_weight - voter.last_vote_weight ) > 1 ) {
if ( voter.proxy ) {
auto& proxy = _voters.get( voter.proxy, "proxy not found" ); //data corruption
_voters.modify( proxy, 0, [&]( auto& p ) {
p.proxied_vote_weight += new_weight - voter.last_vote_weight;
}
);
propagate_weight_change( proxy );
} else {
auto delta = new_weight - voter.last_vote_weight;
for ( auto acnt : voter.producers ) {
auto& pitr = _producers.get( acnt, "producer not found" ); //data corruption
_producers.modify( pitr, 0, [&]( auto& p ) {
p.total_votes += delta;
_gstate.total_producer_vote_weight += delta;
});
}
}
}
_voters.modify( voter, 0, [&]( auto& v ) {
v.last_vote_weight = new_weight;
}
);
}
} /// namespace eosiosystem
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册