您正在查看: EOS 分类下的文章

关于节点IP配置问题

问题

如果我要加入一个EOS网络并且要参与出块,那么我的config.ini中p2p-peer-address配置了目前出块节点的p2p-listen-endpoint地址中的至少一个,此时我还是无法出块,必须在目前出块节点中至少一个config.ini中添加我的p2p-listen-endpoint才可以。这个和ETH的P2P貌似不太一样,麻烦大佬解释一下这个原理,谢谢!

解答

经过测试,EOS的节点连接都走 P2P为双向,不存在单独的处理方式

主要代码在net_plugin插件中

https://github.com/EOSIO/eos/blob/master/plugins/net_plugin/net_plugin.cpp

代码分析就不细说了,之前有相关发帖

https://www.bcskill.com/index.php/archives/625.html

https://my.oschina.net/u/4069047/blog/3005068

目前涉及 4个参数

  • p2p-server-address
  • p2p-listen-endpoint
  • p2p-peer-address
  • p2p-max-nodes-per-host 限制来自相同IP的P2P连接数

参数讲解参考
https://developers.eos.io/eosio-nodeos/docs/net_plugin

https://techterms.com/definition/p2p

虽然P2P对等网络,最终实现还是socket

客户端connect

https://github.com/EOSIO/eos/blob/5082391c60b0fa5e68157c385cd402bf25aea934/plugins/net_plugin/net_plugin.cpp#L1847

服务端listen

https://github.com/EOSIO/eos/blob/5082391c60b0fa5e68157c385cd402bf25aea934/plugins/net_plugin/net_plugin.cpp#L1901

p2p-peer-address 为connect 使用
p2p-listen-endpoint为 listen使用
p2p-server-address 同上,并且优先覆盖

https://github.com/EOSIO/eos/blob/5082391c60b0fa5e68157c385cd402bf25aea934/plugins/net_plugin/net_plugin.cpp#L2995

只要同时有一个连接存在,则能正常工作。
比如节点A (BP)设置了p2p-listen-endpoint(作为P2P Server),并且开放链接,节点B(同步)只要设置 p2p-peer-address(作为P2P Client) 就可以完成链接。A,B反过来也一样。
但对于P2P既是客户端,也是服务端,所以可以的话,互相加下最稳妥。

小白的主要问题在,他在同一台机器上跑了多个节点,但p2p-max-nodes-per-host用的默认值1,BP节点的p2p-listen-endpoint已经被占用了,然后测试节点再连接,由于IP相同,导致无法通过。

先粗略写下,不清楚再补充把

问题转载自:http://wiki.bcskill.com/?thread-21.htm

EOS转账时,谁来花费这笔记账的费用

对于用户的某个发行的代币记录,只保留一条,所以只有第一次记录时才消耗。
那我们看下到底谁来花费这比费用。
看代码,eosio.token合约-->transfer-->add_balance
https://github.com/EOSIO/eosio.contracts/blob/52fbd4ac7e6c38c558302c48d00469a4bed35f7c/contracts/eosio.token/src/eosio.token.cpp#L114

void token::add_balance( const name& owner, const asset& value, const name& ram_payer )
{
   accounts to_acnts( get_self(), owner.value );
   auto to = to_acnts.find( value.symbol.code().raw() );
   if( to == to_acnts.end() ) {
      to_acnts.emplace( ram_payer, [&]( auto& a ){
        a.balance = value;
      });
   } else {
      to_acnts.modify( to, same_payer, [&]( auto& a ) {
        a.balance += value;
      });
   }
}

其中的ram_payer负责花费这笔费用,那往回查
https://github.com/EOSIO/eosio.contracts/commit/6db6e867a7a6722bdec69de132a7c6fd0c3d4fd0#diff-e78b36e718262da651f3608f8963aa10
之后
此时,eosio.token 版本 1.7.0
https://github.com/EOSIO/eosio.contracts/blob/52fbd4ac7e6c38c558302c48d00469a4bed35f7c/contracts/eosio.token/src/eosio.token.cpp#L97

auto payer = has_auth( to ) ? to : from; // 如果有to的权限就是to的账户来花费,没有的话就是from
sub_balance( from, quantity );
add_balance( to, quantity, payer );

也可以单独的为某些账户开户,cleos 添加参数 --pay-ram-to-open
https://github.com/EOSIO/eos/blob/1418543149b7caf8fc69a23621e3db7f3c6d18ad/programs/cleos/main.cpp#L3075

 transfer->add_flag("--pay-ram-to-open", pay_ram, localized("Pay ram to open recipient's token balance row"));

   add_standard_transaction_options(transfer, "sender@active");
   transfer->set_callback([&] {
      if (tx_force_unique && memo.size() == 0) {
         // use the memo to add a nonce
         memo = generate_nonce_string();
         tx_force_unique = false;
      }

      auto transfer_amount = to_asset(con, amount);
      auto transfer = create_transfer(con, sender, recipient, transfer_amount, memo);
      if (!pay_ram) {
         send_actions( { transfer });
      } else {
         auto open_ = create_open(con, recipient, transfer_amount.get_symbol(), sender); // 使用sender支付内存费用,先开户,再转账
         send_actions( { open_, transfer } );
      }
   });

eosio.token合约种的open实现如下
https://github.com/EOSIO/eosio.contracts/blob/52fbd4ac7e6c38c558302c48d00469a4bed35f7c/contracts/eosio.token/src/eosio.token.cpp#L129

void token::open( const name& owner, const symbol& symbol, const name& ram_payer ) // ram_payer 为上面的 sender
{
   require_auth( ram_payer );

   check( is_account( owner ), "owner account does not exist" );

   auto sym_code_raw = symbol.code().raw();
   stats statstable( get_self(), sym_code_raw );
   const auto& st = statstable.get( sym_code_raw, "symbol does not exist" );
   check( st.supply.symbol == symbol, "symbol precision mismatch" );

   accounts acnts( get_self(), owner.value );
   auto it = acnts.find( sym_code_raw );
   if( it == acnts.end() ) {
      acnts.emplace( ram_payer, [&]( auto& a ){
        a.balance = asset{0, symbol}; // 创建0
      });
   }
}

Hyperion History API 解决方案

Hyperion History API解决方案

History API可以说是EOS主网上几个月来最紧迫的问题。DApps,块浏览器和钱包必须查阅历史信息才能正常工作,而在EOS主网上运行完整的历史记录变得昂贵,复杂且耗时。

V1 History API已被弃用,少数BP坚持不断为整个网络提供完整的公共历史节点,(特别感谢Sw / eden,CryptoLions,EOS Tribe,Greymass,EOS Canada,EOS Asia和Oracle Chain!),而其他许多人也都为解决这个问题付出了巨大努力。

有些人认为这不是一个大问题,认为DApp可以找到一种商业模式来专门支付于他们交易的部分历史节点,而块浏览器和钱包可以使用光历史。然而,EOS社区普遍认为,提供历史链数据可能会妨碍EOS满足可扩展性预期的能力,而这种预测常常会自己实现。

在撰写本文时(2019年3月7日),EOS Blockchain包含大约4600万个块,因此对于新手开始提供服务,节点必须摄取所有这些块以及每秒附加到块链中的另外两个块。目前来看,是一个需要数周的过程。同步后,当前的v1 History Plugin需要超过5 Tb的存储空间才能运行。查询此数据库需要大量处理能力和网络带宽。因此,运行完整的历史记录可能会花费超过15,000美元/月。
一个新视角

几个月前,EOS Rio团队开始就此问题的可能性解决方案进行头脑风暴。我们决定从头开始,而不是专注于感知瓶颈以增加数据摄取,存储和查询功能。第一步是分析可以采取哪些措施来优化数据库大小本身。我们了解到History API v1存储了大量冗余信息。

原始的history_plugin与eosio捆绑在一起,提供了v1 API,存储了嵌套在根操作中的内联动作跟踪。每当用户请求给定帐户的操作历史时,将导致存储和传输过量数据。此外,内联操作通常用作“事件”机制,以通知交易方,并且存储它几乎没有价值。

Hyperion History实现了一种新的数据结构和存储方法:

  • 动作以展平格式存储。
  • 将父字段添加到内联操作以指向父全局序列。
  • 如果内联操作数据与父操作数据相同,则将其视为通知,从而从数据库中删除。
  • 没有存储块或事务数据,可以从动作重建所有信息。
  • 没有存储交易验证信息,因为可以使用Chain API在块信息上验证所有信息,DApp不使用历史记录。
  • 通过这些更改,API格式专注于缩短响应时间,降低带宽开销,并使UI / UX开发人员更易于使用。

数据结构存储如下:

小但强大
更改格式和减少数据冗余可将数据库大小减少约85%,从近5 Tb减少到约650 Gb。为了进一步提高性能,我们设计了一个多线程索引器,它从状态历史插件中提取数据,并且可以在大约72小时内通过适当的硬件优化来摄取完整的EOS区块链,而当前的解决方案可能需要数周时间。

我们还引入了“ABI历史缓存层”组件,以防止在ABI修改上并行处理历史数据时出现反序列化失败。
对于数据库,我们部署了一个Elasticsearch集群,该集群在位于巴西里约热内卢的一级基础架构上并置的两个定制组装裸机服务器上运行。
优化的数据结构倾向于减少CPU和带宽消耗,使基础架构更具可扩展性。运行完. 整历史API的其他BP已经在测试Hyperion并帮助其发展。

我们非常感谢来自EOS Cafe的Syed Jafri为Hyperion HTTP API创建了一个javascript库,并且已经在 bloks.i上集成了Hyperion History(v2 API)。还有Sw / eden团队在cleos上添加v2兼容性。非常感谢eosDAC,CryptoLions和BlockMatrix的贡献。

新history API标准的建议

  • 对于开发人员来说,提供扁平的结果优于今天的history API标准。当前的eosio历史插件不必要地使用冗余信息来扩充数据库(用于最终用户历史记录)。过滤内联操作的可能性允许减少API带宽消耗和编码复杂性。
  • 为适应这些变化,EOS Rio和其他开发历史解决方案的BP们正在倡导history API V2标准,也终将被EOS 社区采用。
  • 请您用一点儿宝贵时间对其进行评估并向我们发送反馈信息。

一个开源项目

EOS Rio已经在 https://eos.hyperion.eosrio.io/v2/docs/index.html上使用Hyperion提供History API。
项目代码和初步设置说明可在 https://github.com/eosrio/Hyperion-History-API获,我们将根据开源许可证发布此用于非商业用途

我们可以帮助任何想要运行Hyperion History的人,我们期待您的反馈信息。
下一步
下一步是为操作流实现WebSocket API。这就是我们现在正在做的事情。
完成后,下一个功能将实现Hyperion Analytics,这是Hyperion History API上的一个高级层,可提供详细的EOS统计信息。
相关链接
文档链接:https://eos.hyperion.eosrio.io/v2/docs/index.html
源代码链接:https://github.com/eosrio/Hyperion-History-API
Javascript库链接:https://github.com/eoscafe/hyperion-api

转载自:https://medium.com/@eosriobrazil/presenting-hyperion-history-api-solution-f8a8fda5865b
参考: https://www.boswps.io/#/poll_detail?proposal=hyperion.api

Error 3090005: irrelevant authority included

问题描述

本想在transfer“回调”函数里处理相关业务前做多签检查,

require_auth2(合约账户, 多签权限);

由于transfer权限问题,并没有将代币发行合约对应的transfer执行权限设置{set action permission}给合约的多签权限,但由于对于每笔交易,我们都需要至少有一名签名者为网络和cpu收费,但此自定义权限并没有相应的权限,所以报此错误。

解决方法

将合约多签权限linkauth给单独账户,并在此处检查此多签账户的active权限。

require_auth2(多签账户, active权限);

参考

https://github.com/EOSIO/eos/issues/4617

获取交易到不可逆的时间

需求

根据交易id(transaction_id)获取交易预计到达不可逆的时间。

方案

当push_transaction 无异常后,会得到 trx_id,以及status状态为executed (状态讲解),以及必须校验此状态,防止(hard_fail 状态攻击)。

"receipt": {
      "status": "executed",
      "cpu_usage_us": 1260,
      "net_usage_words": 18
    },

先通过get_info 获取返回参数中的last_irreversible_block,
再请求接口 POST /v1/history/get_transaction 查询当前的transaction_id(如果此时未能查到,则表示交易被丢弃,交易失败),获取返回参数中 block_num。
用block_num 减去last_irreversible_block获得差值,并用差值乘以0.5s(单出块时间),即可预计(忽略get_info和get_transaction的请求时间差)计算出当前交易预计达到不可逆的时间。

备注

最好在交易发起6s+后获取计算的差值,因为超过6s(BP交替出块)后交易被回滚的几率会很小,当然如果长时间停留在等待页面,期间最好在再做定时时间刷新计算,避免交易已回滚,不必要的等待。