您正在查看: EOS-新手教程 分类下的文章

EOS Bancor 算法测试

为了实际项目中,解决相对有限资源,异步动态价格交换的问题,测试下Bancor 算法

从EOS系统合约中,抽离出测试代码如下

#include <iostream>
#include <cmath>
#include <list>

typedef double real_type;
int64_t supply_amount = 10000000000000ll;                         // RAMCORE

struct connector {
    int64_t balance;
    double weight = .5;
};

int64_t convert_from_exchange( connector& reserve, const int64_t& tokens_amount )
{
    const double R0 = reserve.balance;
    const double S0 = supply_amount;
    const double dS = -tokens_amount; // dS < 0, tokens are subtracted from supply
    const double Fi = double(1) / reserve.weight;

    double dR = R0 * ( std::pow(1. + dS / S0, Fi) - 1. ); // dR < 0 since dS < 0
    if ( dR > 0 ) dR = 0; // rounding errors
    reserve.balance -= int64_t(-dR);
    supply_amount   -= tokens_amount;
    return int64_t(-dR);
}

int64_t convert_to_exchange( connector& reserve, const int64_t& tokens_amount )
{
    const double S0 = supply_amount;
    const double R0 = reserve.balance;
    const double dR = tokens_amount; // dS < 0, tokens are subtracted from supply
    const double F =  reserve.weight;

    double dS = S0 * ( std::pow(1. + dR / R0, F) - 1. );
    if ( dS < 0 ) dS = 0; // rounding errors
    reserve.balance += tokens_amount;
    supply_amount   += int64_t(dS);
    return int64_t(dS);
}

// RAM / EOS
int main() {
    struct connector base;           // RAM
    base.balance = 90091267903;
    struct connector quote;
    quote.balance = 56842445020ll;   // EOS

    int64_t ram_byte = 0;
    int64_t tokens_amount = 10000000000;
    int64_t total_tokens_amount = 0;
    int64_t total_ram_byte = 0;
    int total_num = 10;                 // 测试循环次数
    std::list <int64_t > list_ram_byte;
    // Buy EOS->RAM
    int64_t tmp = 0;
    for (int i = 0; i < total_num; ++i) {
        tmp = convert_to_exchange( quote, tokens_amount );
        ram_byte = convert_from_exchange( base, tmp );
        total_ram_byte += ram_byte;
        list_ram_byte.push_back(ram_byte);
        total_tokens_amount += tokens_amount;
        std::cout << "Buy EOS->RAM " << ram_byte << std::endl;
    }

    for (int i = 0; i < total_num; ++i) {
        tmp = convert_to_exchange( base, list_ram_byte.back() );
        list_ram_byte.pop_back();
        tokens_amount = convert_from_exchange( quote, tmp );
        std::cout << "One Sell RAM->EOS " << tokens_amount << std::endl;
    }
    // Sell RAM->EOS
    //tmp = convert_to_exchange( base, total_ram_byte );
    //tokens_amount = convert_from_exchange( quote, tmp );
    //std::cout << "One Sell RAM->EOS " << tokens_amount << std::endl;

    return 0;
}

测试结果如下

Buy EOS->RAM 13478152673
Buy EOS->RAM 9970155844
Buy EOS->RAM 7674007724
Buy EOS->RAM 6089163862
Buy EOS->RAM 4949324006
Buy EOS->RAM 4102144882
Buy EOS->RAM 3455335389
Buy EOS->RAM 2950326086
Buy EOS->RAM 2548490488
Buy EOS->RAM 2223515894


Sell RAM->EOS 9999999996
Sell RAM->EOS 10000000000
Sell RAM->EOS 9999999999
Sell RAM->EOS 10000000000
Sell RAM->EOS 9999999998
Sell RAM->EOS 10000000000
Sell RAM->EOS 9999999999
Sell RAM->EOS 10000000000
Sell RAM->EOS 10000000000
Sell RAM->EOS 10000000001

符合预期,剩余资源越少,价格越高的需求

参考

https://zhuanlan.zhihu.com/p/40162089

EOS V2.0.x 部署eosio.bios合约,需先激活 WTMSIG_BLOCK_SIGNATURES 共识

问题

按v1.8.x部署的方式,启动链部署eosio.bios时报env.set_proposed_producers_ex unresolveable

$ cleos set contract eosio contracts/eosio.bios
Reading WASM from /mnt/d/github/contracts/eosio.bios/eosio.bios.wasm...
Publishing contract...
Error 3070000: WASM Exception
Error Details:
env.set_proposed_producers_ex unresolveable
pending console output:

测试环境

OS:Ubuntu 18.04 server
EOS: v2.0.1
合约:v1.9.1

刚启动时可以看到目前支持了以下新共识

info  2020-02-06T01:42:48.853 nodeos    chain_plugin.cpp:421          operator()           ] Support for builtin protocol feature 'PREACTIVATE_FEATURE' (with digest of '0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd') is enabled without activation restrictions
info  2020-02-06T01:42:48.854 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'ONLY_LINK_TO_EXISTING_PERMISSION' (with digest of '1a99a59d87e06e09ec5b028a9cbb7749b4a5ad8819004365d02dc4379a8b7241') is enabled with preactivation required
info  2020-02-06T01:42:48.856 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'FORWARD_SETCODE' (with digest of '2652f5f96006294109b3dd0bbde63693f55324af452b799ee137a81a905eed25') is enabled with preactivation required
info  2020-02-06T01:42:48.857 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'WTMSIG_BLOCK_SIGNATURES' (with digest of '299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707') is enabled with preactivation required
info  2020-02-06T01:42:48.857 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'REPLACE_DEFERRED' (with digest of 'ef43112c6543b88db2283a2e077278c315ae2c84719a8b25f25cc88565fbea99') is enabled with preactivation required
info  2020-02-06T01:42:48.858 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'NO_DUPLICATE_DEFERRED_ID' (with digest of '4a90c00d55454dc5b059055ca213579c6ea856967712a56017487886a4d4cc0f') is enabled with preactivation required
info  2020-02-06T01:42:48.860 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'RAM_RESTRICTIONS' (with digest of '4e7bf348da00a945489b2a681749eb56f5de00b900014e137ddae39f48f69d67') is enabled with preactivation required
info  2020-02-06T01:42:48.861 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'WEBAUTHN_KEY' (with digest of '4fca8bd82bbd181e714e283f83e1b45d95ca5af40fb89ad3977b653c448f78c2') is enabled with preactivation required
info  2020-02-06T01:42:48.862 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'DISALLOW_EMPTY_PRODUCER_SCHEDULE' (with digest of '68dcaa34c0517d19666e6b33add67351d8c5f69e999ca1e37931bc410a297428') is enabled with preactivation required
info  2020-02-06T01:42:48.863 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'ONLY_BILL_FIRST_AUTHORIZER' (with digest of '8ba52fe7a3956c5cd3a656a3174b931d3bb2abb45578befc59f283ecd816a405') is enabled with preactivation required
info  2020-02-06T01:42:48.864 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'RESTRICT_ACTION_TO_SELF' (with digest of 'ad9e3d8f650687709fd68f4b90b41f7d825a365b02c23a636cef88ac2ac00c43') is enabled with preactivation required
info  2020-02-06T01:42:48.864 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'FIX_LINKAUTH_RESTRICTION' (with digest of 'e0fb64b1085cc5538970158d05a009c24e276fb94e1a0bf6a528b48fbc4ff526') is enabled with preactivation required
info  2020-02-06T01:42:48.865 nodeos    chain_plugin.cpp:408          operator()           ] Support for builtin protocol feature 'GET_SENDER' (with digest of 'f0af56d2c5a48d60a4a5b5c903edfb7db3a736a94ed589d0b797df33ff9d3e1d') is enabled with preactivation required

查询当前已激活共识

curl -X POST http://127.0.0.1:8888/v1/chain/get_activated_protocol_features -d '{}' | jq

查看代码 (github)
https://github.com/EOSIO/eos/blob/e73a43be09478ed3fe5d48071156e1b7caa0e3e1/libraries/chain/controller.cpp#L3330

template<>
void controller_impl::on_activation<builtin_protocol_feature_t::wtmsig_block_signatures>() {
   db.modify( db.get<protocol_state_object>(), [&]( auto& ps ) {
      add_intrinsic_to_whitelist( ps.whitelisted_intrinsics, "set_proposed_producers_ex" );
   } );
}

报错信息提示WTMSIG_BLOCK_SIGNATURES
查看合约更新日志
https://github.com/EOSIO/eosio.contracts/releases/tag/v1.9.1

The eosio.system and eosio.bios contracts contained in this release can only be deployed on an EOSIO blockchain after the activation of the WTMSIG_BLOCK_SIGNATURES consensus protocol upgrade.

只有开启WTMSIG_BLOCK_SIGNATURES共识才能使用此合约。
根据查看 https://github.com/EOSIO/eos/pull/7404

开启WTMSIG_BLOCK_SIGNATURES共识

目前开启WTMSIG_BLOCK_SIGNATURES共识,需要系统账户先部署eosio.boot,并且此合约在单独的合约分支(吐槽下EOSIO代码版本管理...)
先获取下add-boot-contract分支合约

git clone --branch add-boot-contract https://github.com/EOSIO/eosio.contracts.git
./[path]/eosio.contracts/build.sh

和之前部署一样先激活PREACTIVATE_FEATURE

curl -X POST http://127.0.0.1:8888/v1/producer/schedule_protocol_feature_activations -d '{"protocol_features_to_activate": ["0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd"]}' | jq

然后部署下eosio.boot合约

cleos set contract eosio [path]/eosio.contracts/build/contracts/eosio.boot -p eosio@active

激活WTMSIG_BLOCK_SIGNATURES协议

cleos -u http://127.0.0.1:8888 push transaction '{"delay_sec":0,"max_cpu_usage_ms":0,"actions":[{"account":"eosio","name":"activate","data":{"feature_digest":"299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707"},"authorization":[{"actor":"eosio","permission":"active"}]}]}'

最后查询下PREACTIVATE_FEATUREWTMSIG_BLOCK_SIGNATURES是否已成功激活

curl -X POST http://127.0.0.1:8888/v1/chain/get_activated_protocol_features -d '{}' | jq
{
  "activated_protocol_features": [
    {
      "feature_digest": "0ec7e080177b2c02b278d5088611686b49d739925a92d9bfcacd7fc6b74053bd",
      "activation_ordinal": 0,
      "activation_block_num": 1176,
      "description_digest": "64fe7df32e9b86be2b296b3f81dfd527f84e82b98e363bc97e40bc7a83733310",
      "dependencies": [],
      "protocol_feature_type": "builtin",
      "specification": [
        {
          "name": "builtin_feature_codename",
          "value": "PREACTIVATE_FEATURE"
        }
      ]
    },
    {
      "feature_digest": "299dcb6af692324b899b39f16d5a530a33062804e41f09dc97e9f156b4476707",
      "activation_ordinal": 1,
      "activation_block_num": 12770,
      "description_digest": "ab76031cad7a457f4fd5f5fca97a3f03b8a635278e0416f77dcc91eb99a48e10",
      "dependencies": [],
      "protocol_feature_type": "builtin",
      "specification": [
        {
          "name": "builtin_feature_codename",
          "value": "WTMSIG_BLOCK_SIGNATURES"
        }
      ]
    }
  ]
}

然后继续之前的步骤,继续部署eosio.bios合约,以及后面的步骤

cleos set contract eosio contracts/eosio.bios

参考
https://developers.eos.io/manuals/eos/latest/nodeos/upgrade-guides/1.8-upgrade-guide
https://eosio.stackexchange.com/questions/5241/how-to-activate-the-wtmsig-block-signatures-protocol-feature
https://eosio.stackexchange.com/questions/5235/error-while-deploying-eosio-system-contract-to-eosio-account-while-setting-up-a

EOS合约内判断账户是否部署了合约

根据社区小伙伴需求,需要合约内查看某账户是否部署了合约,
我们根据setabi action,看下系统合约代码
https://github.com/EOSIO/eosio.contracts/blob/636406b45a4e1d4c3d7b308f6064dfe61b962814/contracts/eosio.system/src/eosio.system.cpp#L361

  void native::setabi( const name& acnt, const std::vector<char>& abi ) {
      eosio::multi_index< "abihash"_n, abi_hash >  table(get_self(), get_self().value);
      auto itr = table.find( acnt.value );
      if( itr == table.end() ) {
         table.emplace( acnt, [&]( auto& row ) {
            row.owner = acnt;
            row.hash = eosio::sha256(const_cast<char*>(abi.data()), abi.size());
         });
      } else {
         table.modify( itr, same_payer, [&]( auto& row ) {
            row.hash = eosio::sha256(const_cast<char*>(abi.data()), abi.size());
         });
      }
   }

当账户部署合约时,会将合约的hash更新到abihash表,所以我们根据这个表,来判断是否部署了合约就好了

cleos -u https://api.eoslaomao.com get table eosio eosio abihash -L eosio.token -l 1

返回数据

{
  "rows": [{
      "owner": "eosio.token",
      "hash": "85fd4e647e88e595223e69d09a3368a14a45d29320ed1515f54fdfac6ca999df"
    }
  ],
  "more": true
}

当合约账户删除部署合约时,

cleos -u https://api.eoslaomao.com set abi -c

这个hash值为

0000000000000000000000000000000000000000000000000000000000000000
结论

判断下对应账户在该abihash表有记录,且hash值不为0000000000000000000000000000000000000000000000000000000000000000,即为已部署合约。

备注

cleos get code 拿到的是 wasm 和 abi 原数据,然后本地计算的hash(查看源代码),这个一般主要是判断 合约 有没有更新变化用。如果只是判断是否部署合约,还是本文更直接

推送交易上链“假”失败的处理方案

解决问题

对于通过Rpc推送交易上链,因超时等因素导致无返回交易id或者失败,实际交易已上链。

解决方案

在推送交易时附加一个action,action可以填一个与此次推送任务对应的唯一值mark,并把当前链的块高度记下

void addmark( const name& owner, const string& mark )
{
   require_auth( owner );
}

然后mongo会把这个action数据以及对应的交易Id 洗出来

bool
mongo_db_plugin_impl::add_action_trace( mongocxx::bulk_write& bulk_action_traces, const chain::action_trace& atrace,
                                        const chain::transaction_trace_ptr& t,
                                        bool executed, const std::chrono::milliseconds& now,
                                        bool& write_ttrace )
{
   using namespace bsoncxx::types;
   using bsoncxx::builder::basic::kvp;

   const auto block_time = std::chrono::duration_cast<std::chrono::milliseconds>(
               std::chrono::microseconds{atrace.block_time.to_time_point().time_since_epoch().count()} );

   if( executed && atrace.receipt->receiver == chain::config::system_account_name ) {
      update_account( atrace, now, block_time );
   }
   .....
   else if(atrace.act.name == name("addmark")) {
      add_trx_marks( atrace, now, block_time );
   }
void mongo_db_plugin_impl::add_trx_marks( const chain::action_trace& atrace, const std::chrono::milliseconds& now, const std::chrono::milliseconds& block_time ) {
   using namespace bsoncxx::types;
   using bsoncxx::builder::basic::kvp;
   using bsoncxx::builder::basic::make_document;

   auto addma = fc::raw::unpack<mongo_db_plugin::addmark>(atrace.act.data);

   auto doc = make_document( 
                           kvp( "mark", addma.mark),
                           kvp( "trx_id", atrace.trx_id.str()),
                           kvp( "owner", addma.owner.to_string()),
                           kvp( "contract",atrace.receipt.receiver.to_string()),
                           kvp( "block_time", b_date{block_time}),
                           kvp( "block_timestamp", block_time.count()),
                           kvp( "createdAt", b_date{now} ));

   try {
      if( !_trx_marks.insert_one(doc.view())) {
         EOS_ASSERT( false, chain::mongo_db_insert_fail, "Failed to insert _trx_marks ${name} : ${mark}", ("name", addma.owner.to_string())("mark", addma.mark) );
      }
   } catch (...) {
      handle_mongo_exception( "_trx_marks", __LINE__ );
   }
}

测试查询记录

也就是如果推送没返回交易id,可以尝试查洗出的这个表,如果有的话,使用对应的交易id做数据补全。

{
    "_id" : ObjectId("5df34b721e722d468e02260f"),
    "mark" : "944c1698dc62c1817cc02c68207c47bf85dfc7e830839d2ab7267899bb04ada9",
    "trx_id" : "0e65b2b921019604b7928fdea11fe4e02e10550fe50fe767cbb84043b245e5ee",
    "owner" : "bcskillsurou",
    "contract" : "eosio.token",
    "block_time" : ISODate("2019-12-13T08:27:30.000Z"),
    "block_timestamp" : NumberLong(1576225650000),
    "createdAt" : ISODate("2019-12-13T08:27:30.028Z")
}

如果超过一段时间,且判断mongo中块高度增长正常,且超过前面记下的块高度一段距离(以防意外的话,就是超过当前最新区块与不可逆块的差)【防止mongo同步问题】,也没记录的话,就可以判定失败了。

mongo 同步节点出错,导致数据脏后得处理

讨论前提

方案讨论的前提是在,本地有一份低于mongo数据库块高度得链数据备份
如果没有得话,此文章无效,只能清数据库,整体重放了。所以体现了,定期备份得重要性!

方案实施

查询mongo 数据,确认最后写入得块高度
blocks表,查看最后插入记录得 block_num 块号。
然后查询下其余各表

  • block_states
  • action_traces
  • transaction_traces

将该块号下得所有记录按照交易id,块号都删掉。
因为不能保证最后一个块得数据是写入完整得。
其他的表,插入时有更新判断,不会造成重复,所以无需删除,如果有自行新加得表,自行处理。

启动同步节点

使用之前得低于mongo块高度得链数据备份,启动同步节点
添加--mongodb-block-start参数,指定从哪个块开始,才解析块数据写入mongo.
假设此时mongo删去最后一块后,块高度为1000,那执行开始位置为 1000 + 1

nodeos --mongodb-block-start 1001

开始启动后,节点开始同步,等到1001块时,开始mongo插件开始解析块数据,并写入mongo

附加

由于mongo插件缺少判断,导致提前插入数据,修改如下
https://github.com/EOSIO/eos/blob/3c553db73864ea19458512b4669cf9942dc59f57/plugins/mongo_db_plugin/mongo_db_plugin.cpp#L705

void mongo_db_plugin_impl::process_applied_transaction( const chain::transaction_trace_ptr& t ) {
   try {
      // always call since we need to capture setabi on accounts even if not storing transaction traces
      if( start_block_reached ) { // 新加判断
           _process_applied_transaction( t );
       }
   } catch (fc::exception& e) {
      elog("FC Exception while processing applied transaction trace: ${e}", ("e", e.to_detail_string()));
   } catch (std::exception& e) {
      elog("STD Exception while processing applied transaction trace: ${e}", ("e", e.what()));
   } catch (...) {
      elog("Unknown exception while processing applied transaction trace");
   }
}