Skip to content

Instantly share code, notes, and snippets.

@redjade
Last active November 23, 2018 14:14
Show Gist options
  • Save redjade/bdbcb10708e165b29d0301c5defa1d83 to your computer and use it in GitHub Desktop.
Save redjade/bdbcb10708e165b29d0301c5defa1d83 to your computer and use it in GitHub Desktop.
on_ramcost.md

Intro

Please remind that this gist is for documenting and understanding what is going on current ram cost approaches and its consequences technically.

Ideal state after eosio resignation below:

  • EOS token stat
    • max_supply : 10000000000
    • supply : 1000000000
  • RAM Bancor state
    • quote.balance.amount = 1000000000.0000 / 1000

Suppose we all agree that ideal state after resignation of system accounts is same with above.

life cycle of _rammarket database

Code below:

      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" );
      }
   }
  • via system_contract::system_contract() in eosio.system.cpp
  • rammarket is a eosio::multi_index named rammarket.
  • when eosio.system contract is setcodeed, _rammarket is initialized,
  • once initialized, rammarket will be never initialized again in case of another setcode of eosio.system.

Let's check what will happen to three important balances of bancor which make ram market of EOSIO.

  • suppy.amount for RAMCORE, a relay token, which represents supply of relay token of bancor.
  • base.balance.amount for RAM which represents real ram availble to buy and is RAM connector balance of bancor.
  • quote.balance.amount for EOS which represents EOS connector balance of bancor.

supply.amount

In current EOSIO, relay token RAMCORE is used only in conversion which is only between EOS and RAM. Therefore supply of RAMCORE never changes as long as this example of conversion is unchanged.

base.balance.amount

base.balance.amount for RAM which represents real ram availble to buy and is RAM connector balance of bancor.

// in eosio.system.hpp
      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;
  • eosio_global_state is eosio::singleton which is a single table on database.
  • With setram action in eosio.system, it can be changed easily.
  • When block producers are going to increase RAM capacity of EOS blockchain, it is conceivable that setram will be pushed through consensus of block producers.

quote.balance.amount

system_token_supply is the trickest part.

// in eosio.token.hpp
         struct currency_stats {
            asset          supply;
            asset          max_supply;
            account_name   issuer;

            uint64_t primary_key()const { return supply.symbol.name(); }
         };

         typedef eosio::multi_index<N(accounts), account> accounts;
         typedef eosio::multi_index<N(stat), currency_stats> stats;
  • stats is a eosio::multi_index named stat
  • max_supply is determined only when creating a token.
  • supply starts at 0 and is increased by token::issue.
   asset token::get_supply( symbol_name sym )const
   {
      stats statstable( _self, sym );
      const auto& st = statstable.get( sym );
      return st.supply;
   }
  • Yes, get_supply returns current accummulated issued amount.
  • And there is NO action or method to decrease the supply of a token at this time.
  • get_supply is used via system_contract::claimrewards for claiming rewards and provides current EOS supply amount to calculate inflation.

an example of rammarket following eos-bios boot-sequence.yml

Please keep in mind that these descriptions below are intended to explain, not to criticize a specific method.

highlight from file /files/boot_sequence.yaml

  • system.setcode of eosio.token contract on account eosio.token
  • token.create with 10000000000.0000 EOS : Should work with 5% inflation, for the next 50 years (end of uint32 block_num anyway)
  • token.issue with 1000011821.0000 EOS : 1B coins, as per distribution model + gift of RAM to new users.
  • system.setcode of eosio.system contract on account eosio for the first time.
  • snapshot.create_accounts with buy_ram_bytes 8192.
  • system.setcode of eosio.bios contract on account eosio.
  • system.setcode of eosio.system contract on account eosio for the second time.
  • system.resign_accounts for resignation of system accounts.

let's see what happens inside token stats and bancor state.

  • token creation
    • when creating a EOS token with 10000000000.0000, max_supply in token stats is set as 10000000000.0000 EOS.
    • when issuing 1000011821.0000 EOS, then supply in token stats is set as 1000011821.0000 EOS.
  • first eosio.system setcode
    • rammarket is created with bancor state below:
      • RAMCORE : supply.amount = 100000000000000ll
      • RAM : base.balance.amount = current available RAM bytes via base.balance.amount = int64_t(_gstate.free_ram());
      • EOS(CORE) : quote.balance.amount = 1000011821.0000 / 1000 via codes below.
auto system_token_supply   = eosio::token(N(eosio.token)).get_supply(eosio::symbol_type(system_token_symbol).name()).amount;

m.quote.balance.amount = system_token_supply / 1000;
  • snapshot.create_accounts
    • no changes in RAMCORE supply.amount
    • RAM base.balance.amount decreases according to conversion of ram buys.
    • EOS(CORE) quote.balance.amount increases according to ram buys.
    • RAM price is determined with starting EOS quote.balance.amount (1000011821.0000 / 1000), not (1000000000.0000 / 1000).
  • second eosio.system setcode
    • Because rammarket already exists, its values is not initialized again.
  • resignation of system accounts
    • This step disables authorization for system accounts.
    • But resignation doesn't change EOS supply value of token stats
    • Also doesn't change EOS balance of bancor state either.

Results

  • effects to token stats
    • EOS token supply is 1000011821.0000.
    • slightly increased supply is used when calculating inflation.
  • effects to bancor
    • quote.balance.amount starts with (1000011821.0000 / 1000), not (1000000000.0000 / 1000)
    • Because of this, price of RAM starts slightly higher and grows slightly steeper.

To get the ideal state

To get the ideal state, it is necessary to change eosio.token and eosio.system accordingly.

  • EOS token supply correction, which is similar what @nsrempel suggested on EOS Validation (EMLG) telegram channel, but not with each account but once with total delta.
    • See comment below for patch of unissue on eosio master (commit id 379cb1a9e2a6557221d946eef4c5c83ddd156799, Tue Jun 5 18:00:26 2018 -0500).
Considering validation tools already check every account balance i think something simpler like

void token::unissue(quantity){
statstable.modify( st, 0, [&]( auto& s ) {
       s.supply -= quantity;
    });
}

would be preferable in the eosio.token contract, then reconfirm balances = total supply

It seems that difficulty of implementation and inclusion into the boot sequence is not that high.

Reference

Credits

I totally trust and rely on two wonderful mathematicians, @sergioyuhjtman from EOS Argentina and @hyunwoongJi from EOSeoul. We discussed and examined Bancor and its implementation in EOSIO thoroughly for weeks with mutual respect and understanding.

I hope future reviewers including mathematicians, developers and economists interested in EOSIO can join this discussion and conclude what is desirable.

@DutchEOS
Copy link

Great analysis. Thank you for that.
Do you have also any idea why the start balance of "m.quote.balance.amount" should be equal to "system_token_supply / 1000"? Is there any logic to this? Why not "system_token_supply / 10000" or "1" or "0" ?
Any views would be much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment