EOSIO / eosio.contracts

Smart contracts that provide some of the basic functions of the EOSIO blockchain
https://eosio.github.io/eosio.contracts/latest
MIT License
326 stars 574 forks source link

REX Implementation #117

Closed zorba80 closed 3 years ago

zorba80 commented 6 years ago

REX Implementation

This issue describes the EOSIO resource exchange (REX) implementation details. For the mathematical details of the Bancor algorithm used in REX, please see this article. In the following, we use SYS to represent the core token of an EOSIO based blockchain.

User REX funds

For REX related actions, a user needs to create a REX fund and deposit SYS tokens into the fund. deposit action, when called for the first time, creates a rex_fund record for the user and sets the balance to the passed SYS amount. Subsequent deposit calls add the passed amount to rex_fund's balance field. An inline token transfer from user's SYS balance is executed. All REX expenses and proceeds are taken out of or added to rex_fund, with one exception being the action unstaketorex which allows buying REX with staked tokens. withdraw action allows a user to take SYS tokens out of rex_fund. An inline token transfer to user's SYS balance is executed.

REX pool balances

REX pool represents the global state of the REX system. It comprises multiple balances:

Buying REX with liquid tokens

As the name suggests, the action buyrex allows a user to buy REX tokens in exchange for a provided number of SYS tokens (let's call it payment). It is the action that allows a users lend their SYS tokens. The number of issued REX tokens is calculated such that the ratio total_lendable/total_rex is the same before and after the action is executed. That is, this action leaves the value of a REX token unchanged. A voting requirement is imposed in buyrex: a user must have voted for a proxy or at least 21 producers. payment is added to user's vote stake, and votes of corresponding producers are updated. SYS tokens used for the purchase are taken out of user's rex_fund.

Buying REX with staked tokens

A user can buy REX using staked tokens, without the need for unstaking to liquid tokens first, via unstaketorex action. The action takes as input the staked tokens owner account name (owner), the account name to whom the tokens have been previously staked (receiver), the amount to be unstaked from Network bandwidth (from_net), and the amount to be unstaked from CPU bandwidth (from_cpu). The amount of REX tokens rewarded in return for from_net + from_cpu is calculated the same way as in buyrex. Same voting requirement also applies. owner vote weight is updated to its current value, and from_net and from_cpu are subtracted from the resource limits of receiver.

REX maturities

A delay is imposed on selling REX tokens after they're purchased. Tokens bought cannot be sold until 4 days after end of the day using UTC. Upon multiple purchases, tokens are accumulated into separate maturity buckets represented by rex_maturities field of owner's rex_balance. These buckets hold the amounts that can be sold within less than a day, 2 days, ..., 5 days. The amount of matured REX tokens that can be sold immediately is stored in matured_rex field of rex_balance. Note that this delay is imposed in order to give the renting market time to react. The action consolidate allows the owner to consolidate all maturity buckets (not including REX tokens in the savings bucket described below) and matured REX into one bucket with maturity date of 4 days after the end of the day.

REX savings bucket

In addition to maturity buckets described above, a REX owner can utilize a special bucket which we call the "savings bucket". REX held in this bucket does not mature and cannot be sold directly. Users can move already purchased REX from other buckets to their savings bucket at will using the action mvtosavings. This action moves REX out from the user's buckets as necessary starting with the bucket with furthest maturity date. In order to sell REX in the savings bucket, the user must first explicitly move tokens out of it using the action mvfrsavings which can be executed at any time. The mvfrsavings action moves REX from the savings bucket to a bucket with a maturity date that is 4 days after the end of the day.

Selling REX

sellrex allows user to sell a given number of REX tokens in exchange for SYS. This is equivalent to unlending SYS tokens. If enough SYS tokens are available in total_unlent, the order is processed; otherwise the order is queued until the condition is satisfied. Sell order price is determined at processing time and not order creation time, assuming the two are different. The owner's vote stake is updated to current value of REX tokens held after the order is filled. Using action cnclrexorder, an owner can cancel a queued order any time before it's filled. The proceeds of selling REX are added to owner's rex_fund.

Processing REX sell orders

When a sellrex order can't be filled, it is added to a queue. That is, a rex_order record is created. Its fields are owner which is the unique primary key, rex_requested, order_time, proceeds, stake_change, and is_open. Initially we set rex_requested to REX tokens to be sold, is_open = true, proceeds = 0.0000 SYS, stake_change = 0.0000 SYS, and order_time = current_time_point(). When a rex_order is filled (function fill_rex_order), which can happen because some action provides enough unlent SYS tokens, we set is_open = false, set proceeds to the current exchange price of rex_requested, and calculate the change in vote stake and save it to stake_change. We also update owner's REX balance, rex_balance -= rex_requested, and set vote_stake to current value of rex_balance. rex_pool balances are updated accordingly. The order is then moved to the end of the queue. This is achieved by using a secondary key that is a function of order_time and is_open. The rest of rex_order processing has to be executed in an action pushed by owner. That involves transfering proceeds to owner's rex_fund, updating owner's vote weight by adding stake_change (can be positive or negative) to vote stake (field staked in voter_info), and then deleting the order. That is done by calling function update_rex_account which also updates the votes of corresponding producers. An owner can have only one open rex_order. If the owner pushes a second sellrex action that can not be filled immediately, the requested REX tokens are added to the rex_requested field of the already existing order.

REX loans

A user can rent CPU and Network resources on behalf of a receiver account using rentcpu and rentnet actions in exchange for a specified SYS payment. These actions create a rex_loan record in cpuloan and netloan tables, respectively. The number of SYS tokens to be added to receiver CPU or Network resources (staked tokens denoted by total_staked field of rex_loan) is calculated from payment using the current market price determined by Bancor algorithm. The two connectors of the algorithm are total_unlent and total_rent. Calculated total_staked is transferred from total_unlent to total_lent, and payment is added to total_rent. After the loan is created, payment is added to REX pool total_lendable and total_unlent thus increasing value of REX token and increasing SYS tokens available for renting. Loan duration is 30 days. At the end of the duration, staked tokens (total_staked) are subtracted from receiver resource limits. total_staked is moved back from total_lent to total_unlent, and total_rent is updated accordingly using Bancor equation.

Loan automatic renewal

In rentnet and rentcpu, a user can provide an additional amount of SYS tokens that are added to the loan's balance field. At expiration, a loan is renewed if it has enough funds, i.e. balance >= payment, otherwise the loan is closed and the user is refunded any tokens remaining in the loan's balance. If a loan is renewed, total_staked is recalculated using current market price and receiver resource limits are updated to reflect that. REX pool balances are updated as well. A loan owner can fund a loan, identified by loan_num, using fundcpuloan or fundnetloan. The owner can also withdraw from a loan's balance using defcpuloan or defnetloan.

REX maintenance

In most REX actions, the function runrex is called. It processes a fixed number (set to 2) of unfilled REX sell orders, expired Network loans, and expired CPU loans. Processing REX orders and loans is described above. Any account can call runrex directly by pushing the rexexec action which takes as input the maximum number (max) of orders, Network loans, and CPU loans to be processed. REX sell orders are given higher priority than loans. Which means that no new loans are created and no existing loans are renewed if rex_order queue is not empty. The action updaterex updates an owner's vote stake to the current SYS value of their REX balance. It also updates the vote weights of producers that the owner has voted for. The action closerex deletes an owner's records from REX tables and frees the used RAM. If the owner has a non-zero REX balance, the action fails; otherwise, the owner's rex_balance entry is deleted. If the owner has no outstanding loans and a zero rex_fund balance, rex_fund entry is deleted.

Voting requirements

In order to buy REX, an account must have voted for at least 21 producers or delegated their vote to a proxy. As long as an account holds any REX tokens, the condition must be satisfied.

wuyahuang commented 6 years ago

why rex need fund to deposit token, can we just call inline token transfer from user EOS balance when buy rex ?

zorba80 commented 6 years ago

Fund is used not only in buyrex but also in all actions that modify user's balance. It is also convenient for refunds and delayed sellrex orders which might be executed in actions pushed by another user.

bebo-dot-dev commented 6 years ago

Is anyone at Block One or anyone else at all who has worked on REX willing to go on the record either here in this issue or on the REX pull request and say in unambiguous terms whether this issue is in scope or out of scope to be addressed in REX:

It is possible for someone intent on voting for 1 BP to gain access to REX by creating a new account, making that new account a proxy, voting for 1 BP with that proxy account and then delegate their vote to that proxy account.

zorba80 commented 6 years ago

Yes, it is possible. However, we are leaning towards leaving the design as it currently is.

bebo-dot-dev commented 6 years ago

Thank you for clearing up the current stance on gaming access to REX via a proxy @zorba80

For my own understanding I'm trying to succinctly summarize from the REX pool balances and Processing REX sell orders stories above how rewards are calculated when the sellrex / close_rex_order actions are called.

REX pool balances advises that the REX current exchange price is calculated as total_lendable / total_rex. It also says that the proceeds of ramfee and namebids are added to total_lendable and so are the CPU and NET loan payments (from the REX rentcpu and rentnet actions I assume).

It would appear that the actual rewards / proceeds calculation is performed here: https://github.com/EOSIO/eosio.contracts/blob/rex-2/eosio.system/src/rex.cpp#L499-L503

To recap, I would say that the rewards calculated at close_rex_order call time are a function of a point in time calculation based on the REX pool balance where runtime accumulated ram trading fees and premium account name bids are a part of the REX pool total_lendable balance.

Would you agree that this is a fair and accurate summary of how rewards will be calculated for a user selling REX tokens with the REX feature as it stands today ?

zorba80 commented 6 years ago

@jjssoftware, yes this is an accurate summary.

bebo-dot-dev commented 6 years ago

many thanks @zorba80, very much appreciated

bt9 commented 6 years ago

Yes, it is possible. However, we are leaning towards leaving the design as it currently is.

@zorba80

Why leave this "design" in? We have a cabal that controls many sock puppets and you make it easier for them if you leave this in.

gleehokie commented 6 years ago

Why leave this "design" in? We have a cabal that controls many sock puppets and you make it easier for them if you leave this in.

@bt9 please suggest an alternative, I have yet to hear a suggestion that would reasonably work. Note https://github.com/EOSIO/eosio.contracts/issues/91#issuecomment-429488402.

bebo-dot-dev commented 6 years ago

@gleehokie your link to https://github.com/EOSIO/eosio.contracts/issues/91#issuecomment-429488402 identifies the one and only change that can work to fix the REX proxy loophole; proxy accounts having the ability to vote for low BP counts is a general EOSIO platform weakness and it just so happens that REX further highlights this weakness because it undermines one of the original measures of success for REX in no uncertain terms: the goal of improving voting engagement.

Ignoring this issue with the hope that the bar to create a proxy account to game access to REX is a bar too high for most to follow and that the system as it stands is "good enough" is naive and an irresponsible stance in my opinion.

I honestly don't want to turn this issue into a governance discussion but there's no escaping the fact that EOSIO is a blockchain with governance. Platform base layer governance exists to support the business rules built into EOSIO code and in symbiotic fashion the business rules built into the EOSIO code support platform base layer governance.

It's at times like these when a gap is identified that undermines the ability for that symbiotic governance <-> platform relationship to successfully work, the responsible and reasonable thing to do is to close that gap to strengthen the platform overall.

If we were to talk about the nuts and bolts of what's really needed to close this gap, voter_info needs a is_proxy_active indicator to describe whether an account is an active proxy which would be managed by how many BPs a proxy votes for and other touch points on the proxy in the system_contract would assert and maintain this value.

This change would reasonably work if it was undertaken and it is also a responsible change when considering the overall health and well being of the platform.

The questions that remain unanswered are whether block one is wiling to concede that proxy accounts having the ability to vote for low BP counts is a general EOSIO platform weakness and whether block one is willing to address this gap with a code fix or not.

tkaraivanov commented 6 years ago

Why leave this "design" in? We have a cabal that controls many sock puppets and you make it easier for them if you leave this in.

@bt9 please suggest an alternative, I have yet to hear a suggestion that would reasonably work. Note #91 (comment).

I think the requirement to vote for a minimum of 21 producers in order to use REX should be removed. There should be a requirement to vote, but not for how many. Trying to control voter behavior is not good practice. Let me explain myself.

Currently, the requirement allegedly accomplishes 2 things: (1) (indisputable) creates an incentive for token holders to vote, so it is expected to increase overall voter participation. (2) (disputable) results in a better spread of votes between producer candidates.

(1) is accomplished without the requirement to vote for at least 21. All we need is just a requirement to vote.

The problem I see with (2) is that it is too easy to circumvent even without the proxy problem. Users can vote for irrelevant candidates (ones that have too few votes). It's very easy to create new candidates. Furthermore, this requirement creates an incentive for strategic voting.

All of this is not good IMO because when a system rule can be circumvented too easily, what happens is that honest users follow the rule and dishonest users circumvent it. So it can be said that the system gives an edge to dishonest users, which also means that it will attract dishonest users. Ideally, I think we want to build an environment that attracts more honest users.

This is just my opinion on the matter, and I understand it is debatable. However, if in the end we decide that voting for a minimum of 21 is a good system rule, why not implement it as a general requirement to voting, not just REX? To me, this seems like a patch that doesn't address the problem at its core.

bt9 commented 6 years ago

This is just my opinion on the matter, and I understand it is debatable. However, if in the end we decide that voting for a minimum of 21 is a good system rule, why not implement it as a general requirement to voting, not just REX? To me, this seems like a patch that doesn't address the problem at its core.

What is the problem at its core then?

gleehokie commented 5 years ago

The questions that remain unanswered are whether block one is wiling to concede that proxy accounts having the ability to vote for low BP counts is a general EOSIO platform weakness and whether block one is willing to address this gap with a code fix or not.

@jjssoftware should users not be allowed to vote as they see fit? Please expand on why you think proxy accounts (in particular) voting for low BP counts is a problem.

bebo-dot-dev commented 5 years ago

@jjssoftware should users not be allowed to vote as they see fit? Please expand on why you think proxy accounts (in particular) voting for low BP counts is a problem.

I find your question perplexing @gleehokie:

https://github.com/EOSIO/eosio.contracts/blob/038e15b162603698677003a17e8d577356d51742/eosio.system/src/rex.cpp#L57

Perhaps you need to have a word with @bytemaster Daniel Larimer to ask him why the original REX proposal stated:

In order to buy REX an account must set and maintain a voting proxy or approve at least 21 block producers.

https://medium.com/@bytemaster/proposal-for-eos-resource-renting-rent-distribution-9afe8fb3883a

If voting for at least 21 block producers in the context of REX makes no sense to you, have a think about crusading for that requirement to be completely removed. One thing is certain: in the context of REX, allowing a user to vote for i.e. 1 BP via a proxy and also be rewarded for doing so goes directly against whatever original goal this requirement has.

I can offer an opinion why I believe that voting for a low number of BPs poses potential problems; it is healthy account voting behaviour to vote for a wide spread of block producers rather than voting for a small number of block producers because voting for many BPs promotes diversity in BP specialty skills and geographical decentralization.

Putting REX to one side, allowing proxies to vote for i.e. 1 BP is a general platform voter engagement weakening problem because in the worst case scenario, it promotes BP centralization by concentrating / funneling the voting power of many accounts to 1 BP. REX simply highlights this weakness further by not only continuing to allow it, but REX will also reward accounts for doing it.

bt9 commented 5 years ago

Seems like it would break down the security model of DPoS if its anything less than 21.

wanderingbort commented 5 years ago

Update on REX Development

Release Blocking Concerns

Initial Market State and Bootstrapping

Prior to the proper functioning of a Bancor market, initial values for the pools at either side of a connector must be initialized. Generally, this can be accomplished in a few ways but after review the most promising are:

While the bidding process is potentially more accurate, it is also requires a far more complex launch ceremony. As such, initial development will not support this option. This leaves initial “virtual” balances as the preferred method of bootstrapping a new instantiation of the REX.

In order to provide better documentation to future REX operators, some analysis will occur such that general guidelines for determining initial “virtual” balances is available.

Resilience To Market Resets

In concert with bootstrapping concerns, there are still outstanding features which will attempt to prevent a complete reset of the market except in the rare event of all owners deciding to sell their REX shares. This will likely take the form of a “minimum”ratio of balances in the Bancor pools under which various measures, like delaying withdrawals, will exist to maintain a stable market maker and prevent the market from returning abruptly to its initial state.

Buying REX with staked tokens

The current implementation in GitHub requires liquid tokens in an internal REX balance prior to processing a buyrex action. This presents a strong incentive for active voters to unstake tokens, wait for the refund to process, and then buy into the REX. During this period, the voting influence of these tokens is removed from the elections and this runs counter to the one of the goals of the REX to increase voter participation.

As a result, prior to release, a path for participation in the REX will be added which maintains voter influence during the transition. The exact form of this interaction is in development and will be announced as soon as it satisfies the requirements stated above as well as the requirements of staked token management necessary to prevent resource exploits.

REX Maturity and Testing

In order to prevent possible price manipulation, the concept of maturity is being added to REX purchases. This does not affect the rewards for buying REX but will act as a vesting period over which newly purchased REX cannot be sold. Once outside this time period the given REX tokens can be sold at will. The REX maturity period is now implemented and currently set to 4 days. This value was chosen such that it is longer than the normal resource unstaking period and provides the market adequate time to react. Feedback on the time period is welcome.

Updates on Existing Features

“Toggle” for Additional Fees

In earlier discussions, the deposit of fees generated from other system level functions was contentious and it was stated that there would be a toggle for those fees prior to release. After analysis, it was determined that the best practice was not to provide a soft-switch which would impose RAM and CPU overhead during chain operations. Instead, operators who wish to deploy the REX without those inputs will have clear instructions on how to remove the code, prior to compilation of the contract, so that the deployed contracts are as lean as possible. The code currently in GitHub has been modified to support this removal, if desired, as cleanly as possible.

Voting Rules

One of the goals of the REX is to increase voter participation on a public EOSIO-based blockchain. To achieve this goal, an invariant was established such that participants in the REX needed to EITHER vote for at least 21 block producer candidates OR delegate their voting influence to a registered proxy.

Discussions in the interim have questioned whether this invariant was sufficient to affect a number of different nuanced states in the general voting game-theory. After analysis, the development team has concluded that the current rules are sufficient to at least affect voter apathy. By requiring participants to overcome the initial barriers of entry, the expectation is that the REX will increase voter participation.

Qualitative expectations, such as increasing informed-voter participation OR decreasing active voters with malicious intent, is beyond the scope of a software process such as the REX as the ability to judge these qualities in a voter’s participation is outside of the scope and mandate of the reference system contracts for public EOSIO-based blockchains.

The development team respectfully requests that further discussion on this very contentious topic focus on a more measurable goal of decreasing voter apathy.

bitcoiners commented 5 years ago

Any progress on this? When release?

wuyahuang commented 5 years ago

If I buy REX using staked SYS tokens, then sell and withdraw all of it. So I will get SYS tokens immediately or need to wait for 72 hours?

zorba80 commented 5 years ago

@wuyahuang When you buy REX, whether using staked or liquid SYS tokens, you need to wait for 4 days (counting from the end of the day UTC) before you can sell it.

zorba80 commented 5 years ago

@bitcoiners This has been implemented. It's now in the testing phase and deployed on testnets.

dawnFromDark commented 5 years ago

how does users can inquire that how many CPU/NET do they rent from REX? there is no api or something else,right? only way inquire the table cpuloan use: jeos get table eosio eosio cpuloan

but on testnet or main-net in the fututre,there will be many accounts info,how can you filter your appointed account info? cleos -u https://jungle.eosio.cr:443 get table eosio eosio cpuloan --key-type 'name' --index '2' -L strarteosfee -U redtestpeng3 it can not filter any info, may you guys help me ,thank you.

heifner commented 5 years ago

./cleos --url https://jungle2.cryptolions.io get table eosio eosio cpuloan --index 3 --key-type name -L strarteosfee -U strarteosfee

newro commented 5 years ago

Can I reduce or remove rex maturities for testing sellrex command?

zorba80 commented 5 years ago

You can set num_of_maturity_buckets = 0 in https://github.com/EOSIO/eosio.contracts/blob/e3a33325826217c7ca1f33a0f8c16b725328e627/contracts/eosio.system/src/rex.cpp#L795