eos源码解析(二) : bancor算法
近来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_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 );
}
直接,打印输出:
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单位
对应变量:
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
对应变量:
//这是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函数:
对应变量:
supply.amount : 10^14
in.amount : 1482443
quote.balance.amount : 10^10
结果E = 7410567
第4步,调用conver_from_exchange 将RAMCORE换算成RAM
对应变量:
base.banalce.amount : 64G
base.banalce.amount -= 10185760
所以,最后,我们想买10240000byte的ram,结果只买了10185760byte的ram,其余的,扣了手续费。
实际上,卖出ram时,过程和前两步一样,不过在最后收取0.005的手续费。
将上文中,前两步的公式整合,化简,可以发现:
后两步的公式整合,化简可得到:
呵呵!并没有什么0.5/1000之类的幂,也没了什么RAMCORE。
不知道为啥BM一定要两步转换,强行引入RAMCORE,难道是为了确保RAMCORE的总量不变吗?希望知道的童靴答疑。
Leave eos源码解析(二) : bancor算法 to:
Read more #eos posts
Best Posts From camphortree
We have not curated any of camphortree's posts yet. But you can encourage our curation team to review posts by visiting them regularly and by referring other readers. Because we give priority to frequently read content.