美文网首页
eos源码解析(二): bancor算法

eos源码解析(二): bancor算法

作者: 荒原葱郁 | 来源:发表于2018-07-13 08:52 被阅读745次

    近来ram经历了大涨大跌,ram的核心是bancor算法,网上有很多对bancor的介绍,这里就不谈了,eos少数派报告的星主Anima对此有深刻的分析。本文直接从源码解析eos的bancor算法。
    最开始,,,我新建了一条链,对,是一条新链。然后在完整的64G内存上购买了10000k 内存。下面的命令是新建一个账户并购买10000k的内存。
    !!!!直接要结果的,跳过源码,后文自取,不谢不谢!!!!

    cleos system newaccount eosio --transfer accountnum11 EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt --stake-net "100000.0000 SYS" --stake-cpu "100000.0000 SYS" --buy-ram-kbytes 10000
    
    

    在eos的内部,这条命令触发的函数是这样的:

    //payer:eosio receiver:accountnum11 bytes:10000
       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 );
       }
    

    先调用convert,convert涉及到很多函数,这是bancor真正实现,一并列在下面,我加了打印,后面会放打印结果。。。。

    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);
           print("\nconvert_to_exchange \n");
           print( "supply.amount ", supply.amount , "\n" );
           print( "c.balance.amount ", c.balance.amount, "\n" );
           print( "c.weight: ", c.weight, "\n" );
           print( "in.amount: ", in.amount, "\n\n" );
    
          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);
    
           print("\nconvert_from_exchange \n");
           print( "supply.amount ", supply.amount , "\n" );
           print( "c.balance.amount ", c.balance.amount, "\n" );
           print( "c.weight: ", c.weight, "\n" );
           print( "in.amount: ", in.amount, "\n\n" );
    
         // 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("base_amount: ", base.balance.amount , "\n");
          print( "quote: ", quote_symbol, "\n" );
           print( "quote_amount: ", quote.balance.amount, "\n" );
          print( "ex: ", supply.symbol, "\n" );
           print( "ex_amuont: ", supply.amount, "\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
    

    然后调用buyram

      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 on近来ram经历了大涨大跌,ram的核心是bancor算法,网上有很多对bancor的介绍,这里就不谈了,eos少数派报告的星主Anima对此有深刻的分析。本文直接从源码解析eos的bancor算法。
    最开始,,,我新建了一条链,对,是一条新链。然后在完整的64G内存上购买了10000k 内存。下面的命令是新建一个账户并购买10000k的内存。
    !!!!直接要结果的,跳过源码,后文自取,不谢不谢!!!!
    
    

    cleos system newaccount eosio --transfer accountnum11 EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt --stake-net "100000.0000 SYS" --stake-cpu "100000.0000 SYS" --buy-ram-kbytes 10000

    在eos的内部,这条命令触发的函数是这样的:

    //payer:eosio receiver:accountnum11 bytes:10000
       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 );
       }
    

    先调用convert,convert涉及到很多函数,这是bancor真正实现,一并列在下面,我加了打印,后面会放打印结果。。。。

    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);
           print("\nconvert_to_exchange \n");
           print( "supply.amount ", supply.amount , "\n" );
           print( "c.balance.amount ", c.balance.amount, "\n" );
           print( "c.weight: ", c.weight, "\n" );
           print( "in.amount: ", in.amount, "\n\n" );
    
          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);
    
           print("\nconvert_from_exchange \n");
           print( "supply.amount ", supply.amount , "\n" );
           print( "c.balance.amount ", c.balance.amount, "\n" );
           print( "c.weight: ", c.weight, "\n" );
           print( "in.amount: ", in.amount, "\n\n" );
    
         // 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("base_amount: ", base.balance.amount , "\n");
          print( "quote: ", quote_symbol, "\n" );
           print( "quote_amount: ", quote.balance.amount, "\n" );
          print( "ex: ", supply.symbol, "\n" );
           print( "ex_amuont: ", supply.amount, "\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
    

    然后调用buyram

      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_近来ram经历了大涨大跌,ram的核心是bancor算法,网上有很多对bancor的介绍,这里就不谈了,eos少数派报告的星主Anima对此有深刻的分析。本文直接从源码解析eos的bancor算法。
    最开始,,,我新建了一条链,对,是一条新链。然后在完整的64G内存上购买了10000k 内存。下面的命令是新建一个账户并购买10000k的内存。
    !!!!直接要结果的,跳过源码,后文自取,不谢不谢!!!!
    
    
    cleos system newaccount eosio --transfer accountnum11 EOS8mUftJXepGzdQ2TaCduNuSPAfXJHf22uex4u41ab1EVv9EAhWt --stake-net "100000.0000 SYS" --stake-cpu "100000.0000 SYS" --buy-ram-kbytes 10000
    
    

    在eos的内部,这条命令触发的函数是这样的:

    //payer:eosio receiver:accountnum11 bytes:10000
       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 );
       }
    

    先调用convert,convert涉及到很多函数,这是bancor真正实现,一并列在下面,我加了打印,后面会放打印结果。。。。

    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);
           print("\nconvert_to_exchange \n");
           print( "supply.amount ", supply.amount , "\n" );
           print( "c.balance.amount ", c.balance.amount, "\n" );
           print( "c.weight: ", c.weight, "\n" );
           print( "in.amount: ", in.amount, "\n\n" );
    
          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);
    
           print("\nconvert_from_exchange \n");
           print( "supply.amount ", supply.amount , "\n" );
           print( "c.balance.amount ", c.balance.amount, "\n" );
           print( "c.weight: ", c.weight, "\n" );
           print( "in.amount: ", in.amount, "\n\n" );
    
         // 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("base_amount: ", base.balance.amount , "\n");
          print( "quote: ", quote_symbol, "\n" );
           print( "quote_amount: ", quote.balance.amount, "\n" );
          print( "ex: ", supply.symbol, "\n" );
           print( "ex_amuont: ", supply.amount, "\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
    

    然后调用buyram

      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 onfee.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 );
       }
    

    直接,打印输出:

    1786293ms thread-0   apply_context.cpp:28          print_debug          ] 
    [(eosio,buyrambytes)->eosio]: CONSOLE OUTPUT BEGIN =====================
    From: 10240000. RAM TO 0.0000 SYS
    base: 0,RAM
    quote: 4,SYS
    ex: 4,RAMCORE
    
    convert_to_exchange 
    supply.amount 100000000000000
    c.balance.amount 68719476736
    c.weight: 5.000000000000000e-01
    in.amount: 10240000
    E: 7.448915928520706e+06
    
    From: 744.8915 RAMCORE TO 0.0000 SYS
    base: 0,RAM
    quote: 4,SYS
    ex: 4,RAMCORE
    
    convert_from_exchange 
    supply.amount 100000007448915
    c.balance.amount 100 0000 0000
    c.weight: 5.000000000000000e-01
    in.amount: 7448915
    T: 1.489893921873264e+06
    
    From: 148.2443 SYS TO 0. RAM
    base: 0,RAM
    quote: 4,SYS
    ex: 4,RAMCORE
    
    convert_to_exchange 
    supply.amount 100 0000 0000 0000
    c.balance.amount 100 0000 , 0000
    c.weight: 5.000000000000000e-01
    in.amount: 1482443
    E: 7.410567426369141e+06
    
    
    From: 741.0567 RAMCORE TO 0. RAM
    base: 0,RAM
    quote: 4,SYS
    ex: 4,RAMCORE
    
    convert_from_exchange 
    supply.amount 100000007410567
    c.balance.amount 68719476736
    c.weight: 5.000000000000000e-01
    in.amount: 7410567
    T: 1.018576016383362e+07
    
    
    [(eosio,buyrambytes)->eosio]: CONSOLE OUTPUT END   =====================
    
    

    这三个在后文中常见:
    1, base:RAM
    2, quote:SYS
    3, ex:RAMCORE

    我们根据结果倒推算法。
    第1步,调用conver_to_exchange将RAM单位换算成RAMCORE单位

    CodeCogsEqn.gif

    对应变量:

    supply.amoumt  : 10^14
    base.balance.amount  :   68719476736  即64G
    in.amount   :   10240000
    结果 E = 74485915
    supply.amount += 74485915
    base.balance.amount += 10240000
    

    第2步,conver_frome_exchange函数将RAMCORE换算成SYS

    CodeCogsEqn (1).gif

    对应变量:

    //这是ram的初始资金池容量,100万,之所以是10次方,是因为在eos内部,为取整方便,SYS的单位是“分”,不是“元”。 
    quote.balance.amount  :  10^10   
    supply.amount -= 7448915
    quote.balance.amount -= 1489893
    结果T=1489893
    

    上面的分析是从ram-->sys ,意思是要购买10000k的ram,大概需要1489893分钱的SYS即149个EOS。但是像“supply.amount -= ” “quote.balance.amount -=”并不会对supply做真正的修改,只是修改的镜像。在调用buyram时使用“_rammarket.modify”才能修改系统的amount。

    以下的两步是在buyram函数中进行的。
    下面是大家比较关心的手续费问题:之所以要加上199,是要确保1分钱的交易也是需要手续费的,真是雁过拔毛呀!!!除以200 就是乘以0.005。没毛病。。。话说我算术不好。。。1/200 = 0.005 。

              fee.amount = ( fee.amount + 199 ) / 200;
    

    第3步,将SYS转换成RAMCORE,调用conver_to_exchange函数:


    CodeCogsEqn (2).gif

    对应变量:

    supply.amount : 10^14
    in.amount  : 1482443
    quote.balance.amount  : 10^10
    结果E = 7410567
    

    第4步,调用conver_from_exchange 将RAMCORE换算成RAM


    CodeCogsEqn (3).gif

    对应变量:

    base.banalce.amount : 64G
    base.banalce.amount -= 10185760
    

    所以,最后,我们想买10240000byte的ram,结果只买了10185760byte的ram,其余的,扣了手续费。
    实际上,卖出ram时,过程和前两步一样,不过在最后收取0.005的手续费。
    将上文中,前两步的公式整合,化简,可以发现:

    image.png

    后两步的公式整合,化简可得到:

    image.png

    所以,

    当前ram的价格 ≈ 当前的剩余容量 / 池子中的EOS总量

    呵呵!并没有什么0.5/1000之类的幂,也没了什么RAMCORE。
    不知道为啥BM一定要两步转换,强行引入RAMCORE,难道是为了确保RAMCORE的总量不变吗?希望知道的童靴答疑。

    相关文章

      网友评论

          本文标题:eos源码解析(二): bancor算法

          本文链接:https://www.haomeiwen.com/subject/nxodpftx.html