您正在查看: surou 发布的文章

引介 | Ethermint 入门指南

Ethermint 是一个在 Tendermint 上的 Ethereum 优化的产物。将 Ethermint 和 Cosmos Hub 同时发布是为了双方能够促进对方发展:在保证智能合约可编写和执行,同时利用Tendermint引擎提高效率。

Ethermint的主要特性:

  • Web3兼容
  • 高吞吐
  • 横向扩展性强
  • 交易确定性

Cosmos简介

因为以太坊使用的是工作量证明的安全模型,Go Ethereum (Geth)性能很差,也不能提供交易确定性。 使用 Ethermint 不但可以通过 web3 RPC 来部署智能合约,而且操作的速度提升了20倍左右。这得益于区块大小的增长。Geth 的处理速度在 12.5TPS( 每秒交易,transactions per second)。Ethermint 的处理速度在200 TPS,这得益于 Tendermint PoS的确认速度。

Ethermint 相关文档:http://ethermint.readthedocs.io/en/develop/
Ethermint 代码库GitHub:http://github.com/tendermint/ethermint

架构:Hub&Spoke


图解:Cosmos Hub 和 Spoke 架构,每一个空间(Zone)都可以代表一个不同的区块链,也可以是同一条链的多个复制本以实现横向扩展性(Horizontal Scalability)

Tendermint 的核心与应用区块链交互界面(ABCI)

Tendermint 主要由以下两部分组成:区块链共识引擎和上层应用接口。共识引擎称为:Tendermint Core, 它用来保证系统每个节点内交易的顺序是一致的。上层应用接口称为 Application Blockchain Interface (ABCI), 保证交易能被应用处理,不受编程语言的限制。

Tendermint 也可以被用于保证任意应用的状态一致性。它可以作为一个即插即用的共识组件来替代已有的共识引擎。所以,开发人员可以将任意版本的Ethereal代码(Rust,Go,或Haskell),把它当作一个ABCI运行在Tendermint共识引擎之上。这就诞生了一个新的Ethereum 。

另一个例子是 Cosmos network,运用 ABCI&Tendermint Core 实现应用和共识过程的分离。

当我们建立好不同的 zones, 可以使用相应的 Inter-blockchain Communication (IBC)协议进行沟通,实现跨链交互。

主要特性

Web3完全兼容

Ethermint 完全兼容 Ethereum 的 web3 接口和 RPC 调用方法。对于一个已经开发好Ethereum 上的钱包软件的开发者,他很容易将钱包转移到 Ethermint 上运行:只要该改变服务的URL。

开发工具 Truffle 简化了部署智能合约的流程,开发者不需要控制和 Ethereum 节点的交互。若将 Ethereum 节点替换为 Ethermint 节点, 那么你就可以将合约部署在Cosmos上。 这样就实现了合约的交互,花费的时间不超过10秒钟。

吞吐量达到更高的数量级

Tendermint 的 PoS 共识引擎无疑能将大大提高Ethermint 的运行速度,这也是它毫无疑问的优势。建立在容许更高吞吐量的共识引擎之上的效果是使得执行智能合约的成本更低。因为 Tendermint 可以处理速度比 Ethereum 虚拟机(EVM)高20倍,这意味着不会再因为网络缓慢的交易导致费用上涨。 因此,在 Ethermint 中执行一个合约可节省20到50倍的交易费用。

不设限的水平扩展

Ethereum 目前的主要问题之一是可扩展性。 Ethereum 处理速度在12.5 TPS,这就是网络到达瓶颈的地方。 Ethermint 的上限在200 TPS。

但是,假设 Cosmos 上线,网络可达到200 TPS 的上限。 由于模块化的 hub 和 zone 的设计,我们可以轻松地添加第二个 Ethermint zone。也就是说,我们可获得了400 TPS的吞吐量。 再加上两个Ethermint zone,我们得到800 TPS。 这种水平可扩展性的方法是 cosmos 所独有的,它可无限扩展。

实时交易确定性

Bitcoin 和 Ethereum 的 PoW 的共识算法都不会做出“最终”决定,而且在51%的攻击下,理论上过去的块可以被重写。 当没有机会修改过去的块时,块被认为是有“确定性”的。 由于 Nakamoto 协议一致没有提供这样的保证,实际上的共识并不被认为是安全的。

在Ethereum目前的PoW建设中,由于块永远不会“最终化”,您必须等待6个确认,然后才能“确定”您的交易已经进入块状。 通常情况下,Bitcoin 的6次确认大约需要60分钟,而Ethereum 需要2分钟。 在 Ethermint 中,您可以获得 Tendermint 的交易最终确定性,平均在1秒钟内确定块。

Ethermint 的实现

由于Tendermint的共识机制的作用,我们可以即时保证交易的最终性。 在Tendermint中,协商一致的节点首先在对块的内容达成共识之前首先进行多轮投票提案流程。 当这些节点的绝对多数决定一个块时,它们通过状态转换逻辑运行它。 在Ethereum中,共识的过程是相反的,矿工们将交易包含在一个块中,运行状态更新,然后做“计算”来尝试挖掘块。

转载自:https://ethfans.org/posts/a-beginners-guide-to-ethermint

compile failed in 1.6.1 when using [[eosio::on_notify("*::transfer")]]

error: else expression without matching if
000035b: error: OnElseExpr callback failed

but if I use [[eosio::on_notify("eosio.token::transfer")]], compilation is success

解决

升级 eosio.cdt v1.6.2+

参考

https://github.com/EOSIO/eosio.cdt/issues/497
https://github.com/EOSIO/eosio.cdt/blob/796ff8bee9a0fc864f665a0a4d018e0ff18ac383/tests/unit/test_contracts/simple_tests.cpp
https://github.com/EOSIO/eosio.cdt/blob/796ff8bee9a0fc864f665a0a4d018e0ff18ac383/docs/upgrading/1.5-to-1.6.md

关于节点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
      });
   }
}