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

EOS 相同data与相同的私钥签出的签名一样(Duplicate transaction error)

由于EOS防护机制,相同的data使用相同的私钥签出的签名是一样的,避免重复交易攻击,
但由于某些需求场景,即使data一致,也想相同签名每次签的不一致,那我们跟一下代码把
根据关键词Duplicate transaction error
https://github.com/EOSIO/eos/blob/eb88d033c0abbc481b8a481485ef4218cdaa033a/libraries/chain/controller.cpp#L2990:18

bool controller::is_known_unexpired_transaction( const transaction_id_type& id) const {
   return db().find<transaction_object, by_trx_id>(id);
}

查看计算trx_id的方法
https://github.com/EOSIO/eos/blob/1418543149b7caf8fc69a23621e3db7f3c6d18ad/programs/cleos/main.cpp#L1306

get_transaction_id->set_callback([&] {
         try {
            fc::variant trx_var = json_from_file_or_string(trx_to_check);
            if( trx_var.is_object() ) {
               fc::variant_object& vo = trx_var.get_object();
               // if actions.data & actions.hex_data provided, use the hex_data since only currently support unexploded data
               if( vo.contains("actions") ) {
                  if( vo["actions"].is_array() ) {
                     fc::mutable_variant_object mvo = vo;
                     fc::variants& action_variants = mvo["actions"].get_array();
                     for( auto& action_v : action_variants ) {
                        if( !action_v.is_object() ) {
                           std::cerr << "Empty 'action' in transaction" << endl;
                           return;
                        }
                        fc::variant_object& action_vo = action_v.get_object();
                        if( action_vo.contains( "data" ) && action_vo.contains( "hex_data" ) ) {
                           fc::mutable_variant_object maction_vo = action_vo;
                           maction_vo["data"] = maction_vo["hex_data"];
                           action_vo = maction_vo;
                           vo = mvo;
                        } else if( action_vo.contains( "data" ) ) {
                           if( !action_vo["data"].is_string() ) {
                              std::cerr << "get transaction_id only supports un-exploded 'data' (hex form)" << std::endl;
                              return;
                           }
                        }
                     }
                  } else {
                     std::cerr << "transaction json 'actions' is not an array" << std::endl;
                     return;
                  }
               } else {
                  std::cerr << "transaction json does not include 'actions'" << std::endl;
                  return;
               }
               auto trx = trx_var.as<transaction>();
               transaction_id_type id = trx.id();
               if( id == transaction().id() ) {
                  std::cerr << "file/string does not represent a transaction" << std::endl;
               } else {
                  std::cout << string( id ) << std::endl;
               }
            } else {
               std::cerr << "file/string does not represent a transaction" << std::endl;
            }

所以相同的data会的到相同的trx_id,以及报Duplicate transaction error

那如何解决呢,查看eos有没有相应的解决方案
查看 cleos工具

  -f,--force-unique           force the transaction to be unique. this will consume extra bandwidth and remove any protections against accidently issuing the same transaction multiple times

支持相应参数,那我们看下实现的细节
https://github.com/EOSIO/eos/blob/1418543149b7caf8fc69a23621e3db7f3c6d18ad/programs/cleos/main.cpp#L325

if (tx_force_unique) {
    trx.context_free_actions.emplace_back( generate_nonce_action() );
}

https://github.com/EOSIO/eos/blob/1418543149b7caf8fc69a23621e3db7f3c6d18ad/programs/cleos/main.cpp#L278:15

chain::action generate_nonce_action() {
   return chain::action( {}, config::null_account_name, "nonce", fc::raw::pack(fc::time_point::now().time_since_epoch().count()));
}

查看eosio.null账户执行 https://bloks.io/account/eosio.null

解决方案

每次发送相同的data时,交易体添加一个针对 eosio.null的nonce的context_free_actions

使用例子

describe('custom transactions', function () {
    const authorization = [{
      actor: 'inita',
      permission: 'active'
    }]

    const eos = Eos({
      keyProvider: wif
    })

    it('context_free_actions', async function() {
      await eos.transaction({
        context_free_actions: [{
            account: "eosio.null",
            name: 'nonce',
            data:  "652511569",
            authorization: []
        }],
        actions: [{
        account: "bcskillsurou",
        name: 'hi',
        authorization: [{
            actor: "bcskillsurou",
            permission: 'active'
        }],
        data: {
            "user": "bcskillsurou"
        }]
      })
    })

    it('nonce', async function() {
      const trx = await eos.transaction({
        actions: [ Object.assign({}, nonce, {authorization}) ],
      })
    })
  })

备注

eosio.null账户为系统默认创建账户
https://github.com/EOSIO/eos/blob/eb88d033c0abbc481b8a481485ef4218cdaa033a/libraries/chain/controller.cpp#L928

create_native_account( config::null_account_name, empty_authority, empty_authority );

对于ecdsa.sign同样数据签名,然后签名一样是因为nonce为固定值。
https://github.com/EOSIO/eosjs-ecc/blob/7ec577cad54e17da6168fdfb11ec2b09d6f0e7f0/src/signature.js#L207

    nonce = 0;
    e = BigInteger.fromBuffer(dataSha256);
    while (true) {
      ecsignature = ecdsa.sign(curve, dataSha256, privateKey.d, nonce++);

context_free_actions

通过对eosio.null账户的nouce动作,可以将无签名的数据打包进入context_free_action字段,结果区块信息如下:

$ cleos --wallet-url http://127.0.0.1:6666 --url http://127.0.0.1:8000 get block 440
{
  "timestamp": "2018-08-14T08:47:09.000",
  "producer": "eosio",
  "confirmed": 0,
  "previous": "000001b760e4a6610d122c5aa5d855aa49e29f3052ac3e40b9e1ef78e0f1fd02",
  "transaction_mroot": "32cb43abd7863f162f4d8f3ab9026623ea99d3f8261d2c8b4d8bf920ab97e3d1",
  "action_mroot": "09afeaf40d6988a14e9e92817d2ccf4023b280075c99f13782a6535ccc58cbb0",
  "schedule_version": 0,
  "new_producers": null,
  "header_extensions": [],
  "producer_signature": "SIG_K1_K2eFDzbxCg3hmQzpzPuLYmiesrciPmTHdeNsQDyFgcHUMFeMC3PntXTqiup5VuNmyb7qmH18FBdMuNKsc7jgCm1TSPFbaj",
  "transactions": [{
      "status": "executed",
      "cpu_usage_us": 290,
      "net_usage_words": 16,
      "trx": {
        "id": "d74843749d1e255f13572b7a3b95af9ddd6df23d1d0ad19d88e1496091d4be2b",
        "signatures": [
          "SIG_K1_KVzwg3QRH6ZmempNsvAxpPQa42hF4tDpV5cqwqo7EY4oSU7NMrEFwG7gdSDCnUHHhmH1EwtVAmV1z9bqtTvvQNSXiSgaWG"
        ],
        "compression": "none",
        "packed_context_free_data": "",
        "context_free_data": [],
        "packed_trx": "8497725bb601973ea96f0000000100408c7a02ea3055000000000085269d000706686168616861010082c95865ea3055000000000000806b010082c95865ea305500000000a8ed3232080000000000d08cf200",
        "transaction": {
          "expiration": "2018-08-14T08:49:08",
          "ref_block_num": 438,
          "ref_block_prefix": 1873362583,
          "max_net_usage_words": 0,
          "max_cpu_usage_ms": 0,
          "delay_sec": 0,
          "context_free_actions": [{
              "account": "eosio.null",
              "name": "nonce",
              "authorization": [],
              "data": "06686168616861"
            }
          ],
          "actions": [{
              "account": "eosiotesta1",
              "name": "hi",
              "authorization": [{
                  "actor": "eosiotesta1",
                  "permission": "active"
                }
              ],
              "data": {
                "user": "yeah"
              },
              "hex_data": "0000000000d08cf2"
            }
          ],
          "transaction_extensions": []
        }
      }
    }
  ],
  "block_extensions": [],
  "id": "000001b8d299602b289a9194bd698476c5d39c5ad88235460908e9d43d04edc8",
  "block_num": 440,
  "ref_block_prefix": 2492570152
}

正常的actions的内容是hi智能合约的调用,而context_free_action中包含了无签名的data数据,是已做数字摘要后的形态。源码中的操作:

//lets also push a context free action, the multi chain test will then also include a context free action
("context_free_actions", fc::variants({
    fc::mutable_variant_object()
       ("account", name(config::null_account_name))
       ("name", "nonce")
       ("data", fc::raw::pack(v))
    })
 )

参考

https://github.com/EOSIO/eos/pull/422
https://github.com/EOSIO/eosjs-ecc/commit/01419633630da42bd76c21503cadb4298cee1ad9
https://github.com/EOSIO/eos/pull/6829
https://github.com/ganioc/eosjs3/blob/203cdae9a70198b53c86e35b18a10560a99c1c12/src/index.test.js
https://github.com/EOSIO/eosjs-ecc/issues/20

owner account does not exist

执行

cleos push action eosio init '[0,"4,SYS"]' -p eosio@active

报错

Error 3050003: eosio_assert_message assertion failure
Error Details:
assertion failure with message: owner account does not exist
pending console output:

原因

https://github.com/EOSIO/eosio.contracts/blob/52fbd4ac7e6c38c558302c48d00469a4bed35f7c/contracts/eosio.system/src/eosio.system.cpp#L365

token::open_action open_act{ token_account, { {get_self(), active_permission} } };
      open_act.send( rex_account, core, get_self() );

https://github.com/EOSIO/eosio.contracts/blob/52fbd4ac7e6c38c558302c48d00469a4bed35f7c/contracts/eosio.token/src/eosio.token.cpp#L133
没有创建 rex_account eosio.rex账户

解决方案

创建 eosio.rex 账户,在执行

Unable to locate package eosio

安装eosio deb包时报错

sudo apt install eosio_1.6.6-1-ubuntu-18.04_amd64.deb
[sudo] password for surou:
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package eosio_1.6.6-1-ubuntu-18.04_amd64.deb
E: Couldn't find any package by glob 'eosio_1.6.6-1-ubuntu-18.04_amd64.deb'
E: Couldn't find any package by regex 'eosio_1.6.6-1-ubuntu-18.04_amd64.deb'

解决方案,使用sudo dpkg -i安装

$ sudo dpkg -i eosio_1.6.6-1-ubuntu-18.04_amd64.deb
Selecting previously unselected package eosio.
(Reading database ... 57664 files and directories currently installed.)
Preparing to unpack eosio_1.6.6-1-ubuntu-18.04_amd64.deb ...
Unpacking eosio (1.6.6-1) ...
Setting up eosio (1.6.6-1) ...

科普 | Cosmos 区块链的工作原理,Part-1:比较 Cosmos 与比特币、以太坊

编者注:本文对 Cosmos 网络中区块链和比特币、以太坊进行了巨细靡遗的比较。作者先从区块链系统的栈层出发,分析了比特币、以太坊在不同栈层上的技术要点,最后回归到 Cosmos 网络中的区块链,概念解释尤为清晰,是不可多得的解释文。

鉴于文章实在太长,我们在文首附上了目录。

目录

  • Cosmos 是什么?
  • 区块链结构简介
    • 比特币栈层结构
    • 以太坊栈层结构
  • 基于比特币与以太坊构建应用程序
  • Cosmos 区块链结构
    • Cosmos 共识层
    • Cosmos 网络层
    • Cosmos 应用层
  • 结论

    密码学货币产业从未停下脚步。

一切都始于2010年比特币的问世。比特币刚问世时,所有人都认为它是数字货币的圣杯。曾经被认为不可能的事情现在变成了现实:第一个点对点(peer-to-peer,P2P)支付网络出现了。

即便在今天,对事物的信任仍然是最难以琢磨并且最珍贵的资产。比特币通过创建第一个“免信任型”系统,绕过了这一问题。但这仅仅是一个开始。

从那之后,比特币就成为了催生更广泛密码学创新的催化剂,这些创新也导致了一系列新型去中心化系统与金融基础设施的出现:以太坊(Ethereum)、闪电网络(Lighting Network)、EOS、Tezos、Maker…… 这个名单还在不断延长。

但是有一个项目与众不同:Cosmos。

在区块链领域,Cosmos 是一个“新生儿”。虽然它的理念已经出现有一段时间了,但其开发团队一直在背后慢慢地开发以确保 Cosmos 设计及实现的正确性。这也使得 Cosmos 最近才公开推出。

因此,有很多人看过 Cosmos 项目之后却不理解它也就不足为奇了。简单浏览 Cosmos 相关资料并不会让他们能够直观地了解 Cosmos,反而会让他们有更多疑问:

  • 什么是 Cosmos?
  • Cosmos 的工作原理是什么?
  • 与比特币、以太坊相比 Cosmos 有什么不同?
  • Cosmos 的特点是什么?

我已经知道 Cosmos 团队快两年了。老实说,当我第一次听说他们在做什么的时候,我和其他人一样对它的概念一无所知。

但当我更深入地了解 Cosmos 之后,我开始非常欣赏它。我这么说不仅是为了引人注目,是真正地发自内心。

我对 Cosmos 非常着迷,所以我们决定将 TruStory 应用构建为一个 Cosmos 区块链应用。(插播:我将在之后的文章中更详细地阐述我们为什么会做出这个决定)

尽管如此,关于 Cosmos 仍然有很多困惑。所以我决定专门为此写一篇文章。我想让读者对 Cosmos 是什么以及它在区块链世界中的定位有一个更深层次的理解。

你准备好开始了么?理清思绪,带上你的思考帽,系好安全带。我们要开车啦!

Cosmos 是什么?

Cosmos 是这样定义自己的:

“一个由多条独立平行区块链组成的去中心化网络,每条平行区块链均采用 BFT 共识算法(例如:Tendermint 共识)。”

哇,好拗口啊!让我们把这个定义拆分成几个容易理解的部分。

独立平行区块链的去中心化网络
我在这里假设读者已经对区块链非常了解了!不过,我还是快速回顾一下:
简单来说,区块链是一个分布在许多计算机上的数据库,每台计算机上的数据库都保持相同的状态。换句话说,每台计算机上的数据库所包含的数据都完全相同。这些计算机共同组成了所谓的“区块链网络”。

比特币和以太坊都是区块链,而 Cosmos 是由许多这样并行运行的区块链组成的区块链网络。

如果你不能完全理解刚才说的,那么在进一步了解 Cosmos 工作原理之前,你最好再多读一些关于区块链的基础知识。(编者注:中译本见文末超链接《区块链是什么鬼》)

“每条区块链都采用 BFT 共识算法”

BFT 是 “Byzantine Fault-Tolerant(拜占庭容错)”的缩写。一条拜占庭容错的区块链能够在网络中部分节点宕机 以及/或者 作恶(即所谓“拜占庭式节点”)的情况下,保证网络依旧具备“安全性”与“活性”等性质。安全性与活性能够确保区块链网络中每个节点维护相同的状态。

插播:如果你想要更深入地了解什么是安全性(Safety)与活性(Liveness),请阅读我的这篇有关分布式共识的文章。(编者注:中译本见文末超链接《分布式共识的工作原理,Part-2》)

因此,一种 “BFT 共识算法” 乃是定义了计算机间通信与协调、使得区块链具有拜占庭容错能力的算法。Cosmos 网络中的所有区块链都采用某种 BFT 共识算法。

比特币和以太坊的共识算法不是典型的 BFT 算法。所以它们不符合 Cosmos 网络中区块链的定义。(值得注意的是,虽然它们不是拜占庭容错的,但仍然可以让比特币和以太坊等区块链加入 Cosmos 网络,仅仅需要一些额外的步骤。如果你觉得费解,不用担心——我们将稍后对此进行更深入的研究。)

插播:如果你还是不清楚什么是 BFT,我在这篇文章中写得蛮清楚了。(编者注:中译本见文末超链接《分布式共识的工作原理,Part-3》)

“Tendermint 共识算法”

Tendermint 是由 Cosmos 开发者提出并构建的一种 BFT 共识算法。Cosmos 网络中的区块链可以使用 Tendermint 共识或任何其他 BFT 共识算法。稍后我们将在本文了解更多关于 Tendermint 的内容。

简单来说,Cosmos 网络是一个由多条并行运行的独立拜占庭容错区块链组成的生态系统。这些区块链是 独立运行的,并且能够与其他区块链进行 互操作。

现在你可能会想,“为什么区块链之间要进行互操作呢?”

好问题!我们很快就会讲到。但首先我们要回顾一下区块链的结构。

区块链结构简介

在深入研究 Cosmos 生态系统中区块链是如何工作和互操作的之前,让我们先回顾一下区块链结构的基础知识。
正如我们前面所讨论的,区块链是一个多机复制数据库,并且在每台计算机上维护相同的数据。这种类型的分布式系统也被称为“复制状态机”。
复制状态机是一种多机复制的确定性状态机,但因为网络中每台计算机都维护着相同的状态,因此在功能上看起来就像一台单机。
听起来很熟悉,对吧?回顾上文区块链的定义,这里的定义仅仅是将“数据库”替换为“状态机”、“数据”替换为“状态”,相信你能明白我的意思。

“确定性” 可以简单地理解为,给定一个确定的输入,状态机将始终产生相同的输出。在区块链系统中,“确定性”意味着如果你从一个给定状态开始执行相同的事务序列,你总是会得到相同的最终状态。

复制状态机从某个状态启动。每笔有效事务都将导致系统状态转变到下一个状态(这与数据库中条目更新相同:如果你更新某个条目,数据库将迁移到包含该更新后数据条目的新状态)。

复制状态机在概念上有三个栈层:

1)应用层
应用层负责定义状态变迁,并在事务发生后更新状态机状态。

2)网络层
网络层负责将在某一个状态机上执行的事务传播到网络中其他所有状态机上。

3)共识层
共识层由算法组成,负责确保在事务执行后每一台状态机都存储相同的状态(即,某一状态机无法伪造不存在的事务)。

3a)抗女巫攻击层
试图在去中心化公网运行的复制状态机还需要第四层(“抗女巫攻击层”),确保任何一台状态机都不能破坏网络。如果没有这一层,状态机可以通过创建许多假身份来篡改状态,从而获得与其投入不成比例的影响或收益(即,发起女巫攻击)。


总之,应用层负责定义状态与管理状态迁移。网络与共识层负责保持每台机器上状态一致(即,确保网络中每个数据库数据一致)。抗女巫攻击层(显然)负责避免女巫攻击。

现在,让我们看看在比特币区块链和以太坊区块链中是如何定义与实现这些栈层的。

比特币栈层结构

1)应用层

比特币的主要应用是 P2P 交易。比特币使用 Script(一种堆栈式非图灵完备的语言)来定义与执行交易。当发送方通过交易发送比特币时,发送方将使用脚本来编码指定谁才能掌控这笔资金。Script 包含一组操作码或者说命令,发送方可以使用这些操作码来指定要花费一笔比特币所需满足的条件。

2)网络层

当发送方向接收方发送比特币时,该转账交易必须被广播到网络中,才能使矿工将其打包进区块中。比特币使用一种“Gossip 协议”来确保每个节点都会将其接收的所有新区块或交易发送至邻居节点(peer)。Gossip 协议是确保消息在全部节点间传播的 P2P 协议。比特币网络中所有节点都会将其新接收的有效交易立即发送给其邻居节点,从而使得待打包交易能够在几秒钟内通过点对点网络传播到大多数节点。

3)共识层

在交易被转播到网络中后,还需要将其添加到区块链中才能完成转账(即让网络中的计算机都来执行这个事务)。验证交易并将其打包到区块中的过程称为“中本聪共识(Nakamoto Consensus)”。中本聪共识的运行原理可以在其他论坛或文章找到。如果你想深入了解,这篇文章是一个很好的入门文章。

3a)抗女巫攻击层

中本聪共识依赖于“工作量证明(Proof-of-Work)”来防止女巫攻击。基本上,产生一个新区块所需的算力使得比特币共识协议自身能够抵抗女巫攻击。由于矿工需要大量的算力来产生下一个区块,使得他们无法在不增加大量算力(与资金)投入的情况下“伪造”多个身份。

以太坊栈层结构

1)应用层

与比特币不同,以太坊的设计初衷是构建一个能够运行去中心化应用的平台。以太坊包含一种高级语言(即,Solidity),使得开发者能够通过编写智能合约定义去中心化应用的具体功能。EVM(以太坊虚拟机,Ethereum Virtual Machine)是以太坊应用层的核心。EVM 使用 EVM 编译器将智能合约代码编译成字节码,用户可以通过交易的形式,将该字节码上传到区块链之后,EVM 就可以执行这些字节码,从而改变去中心化应用的状态(即,更新以太坊节点存储的该智能合约相关状态)。由于以太坊网络中所有节点均运行 EVM,这也保证所有节点的状态一致。

2)网络层

与比特币相似,以太坊也使用 Gossip 协议,使得节点能够与其邻居节点通信。

3)共识层

为了达成共识,以太坊使用了与中本聪共识相似的“Ethash”,但 Ethash 与中本聪共识有一些关键区别。如果你需要了解以太坊共识算法的工作原理,请阅读我之前的一篇文章。(编者注:中译本见文末超链接《以太坊的工作原理》)

3a)抗女巫攻击层

与比特币一样,Ethash 依赖于工作量证明(目前为止,译者注:未来以太坊 2.0 将切换到 PoS 共识机制)来抵御女巫攻击。

基于比特币与以太坊构建应用程序

我希望以上内容让你对区块链结构有了一定了解。当我们讨论 “比特币” 或 “以太坊”时,这些名字指的是相关的所有栈层。因为比特币和以太坊是由这些栈层组成的整体。

你无法将以太坊智能合约与其底层 Ethhash 共识层分开,也因此单独讨论这两个主题都没有意义。比特币也是如此,不使用中本聪共识与工作量证明你就无法进行比特币交易。

另一方面,Cosmos 采用了一种稍微不同的模式:它将应用层与共识层和网络层分开。

因为 Cosmos 的目标是建立一个区块链网络,所以这样设计是有意义的。在这个区块链网络中,每条区块链是独立的,并且有它自己的需要和要求(即,它自己的应用)。在这种情况下,想要提出一种一刀切的、适合所有区块链的应用层,是行不通的。让我们用几个例子来研究一下原因。

比特币局限性的一个例子
假设我们准备构建一个货币应用程序。在这种情况下,像 Bitcoin Scrypt 这种简单的基于堆栈的脚本语言是最佳选择。比特币脚本语言不仅可以很好地实现从一个地址到另一个地址的价值转移,并且非常简单、不是图灵完备的。

因此,它不太容易受到各种类型安全漏洞的影响,而这些安全漏洞可能会严重影响图灵完备的编程语言。这正是我们在处理货币与价值存储时想要的。但是这种简单也有其局限性。

使用 Scrypt 做任何更复杂的事情(例如:去中心化预测市场)都非常困难。比特币脚本语言不仅受其可执行代码复杂性限制,对于开发者来说也十分不友好。更糟糕的是,比特币区块链交易的处理速度很慢(大约每秒 7 笔交易)。因此,直接在比特币区块链上构建需要高交易吞吐量的应用是不现实的。

以太坊局限性的一个例子

与比特币相反,以太坊的 EVM 与智能合约语言(Solidity)是为了支持更灵活的应用程序而设计的。Solidity 是一种图灵完备的编程语言,因此理论上它可以执行任意算法复杂度的代码。

在实际应用中,由于 Solidity 易出错并易受到安全攻击,所以使用 Solidity 开发任意复杂度的程序是相当困难的。这种特性与处理价值转移的应用程序背道而驰,在后者这个场景种,安全性是最重要的。

此外,智能合约也非常难以升级,从而使得迭代开发非常困难。合约一旦部署上链,你所能做的就只有祈祷它能够平稳运行!与比特币一样,以太坊交易处理速率也非常低(大约每秒能够处理 15 笔交易),因此在以太坊区块链上构建需要高交易吞吐量的应用也是不现实的。

Cosmos 的提出就是为了满足这种实际业务需要,尽管它为此做了一些较大的牺牲。接下来我们将深入研究具体的细节。但在那之前,我们还必须理解 Cosmos 中区块链的三个栈层是什么样的。

Cosmos 区块链结构

首先,我们将从共识层开始了解 Cosmos,以便更好地理解在 Cosmos 上开发应用程序与使用比特币或以太坊有何不同。

Cosmos 共识层

Cosmos 网络中区块链使用 Tendermint 共识算法。Tendermint 是一个 2014 年诞生的开源项目,“旨在解决比特币工作量证明(Proof-of-Work, PoW)共识算法的速度、可扩展性与环境问题”。

Tendermint 共识算法是一个 “无视应用层(application-agnostic)的共识引擎”。从本质上讲,这意味着任何区块链都可以使用 Tendermint 共识算法,它是拜占庭容错的,并且使用 PoS 算法来抵御女巫攻击。

又是一大堆术语!我们好好说道说道。

Tendermint 共识是如何运作的?

回顾一下,共识算法的存在是为了保证事务执行后,状态机中保存的状态一致;而 Tendermint 共识算法定义了一种“能让所有节点对下个区块达成共识”的规则。

让我们看看相关因素及规则是如何运作的吧!

验证者

负责达成状态一致的节点称为“验证者”。任何愿意协助整个网络达成共识的参与节点都能成为验证者;作为回报,验证者会获得交易手续费和区块奖励。Tendermint 整合这些验证者的投票结果,确定下一个区块的正确状态。

通过质押对抗女巫攻击

每个验证者的票都有自己的投票权重,投票权重通常是在创世块产生时确定,或是在开始运行后根据应用层开发者所设计的某些逻辑来决定。一般来说,由验证者锁在系统中的代币量(作为质押品)决定投票权重的大小,这种质押物也被称为“保证金”。

Consensus 共识

按照规则,验证者要按轮次(round)对每一个区块达成共识。每一轮都包含三个基本步骤:提议阶段(Propose)、预投票阶段(Prevote)、预提交阶段(Precommit),以及两个后续步骤:提交阶段(Commit)、新高度阶段(NewHeight)。从抽象角度来看,验证者按照以下协议规则共同决定下一高度要使用什么区块:

  1. 首先是提议阶段,由指定的验证者提出一个区块——每一轮中的提议者都是从有序的列表中按照投票权重的比例,确定性地选择出来的。
  2. 接着进入预投票阶段——每一位验证者广播他们各自的预投票。
  3. 当该轮次中某一区块收到超过 2/3 的预投票,我们就称其为 “polka”。一旦出现 “polka”,就进入下一个阶段。
  4. 进入预提交阶段,由每一个验证者广播他们的预提交的投票。
    • 如果某一特定区块收到超过 2/3 的预投票,就进入提交阶段,这个阶段会将区块加入区块链,并增加区块高度。每当有新的区块加入区块链,所在区块链的区块高度就 +1。
    • 如果失败,则要么返回预投票阶段,要么回到预提交阶段。

要注意的是,在任何高度上,都有可能需要一轮以上的投票才能提交一个区块。因为可能出现以下情况:

  • 被指定的“提议者”在应该提出区块时掉线
  • 提议者所提出的区块违反一些预先定义的规则
  • Tendermint 依靠超时机制确保区块链出块不会遇到延宕。如果在超时前,提议区块没有收到超过 2/3 的预投票,则由新的提议者再次进行提出区块流程。

协议细节详见此处

总的来说,Tendermint 选择了与比特币的中本聪共识、以太坊 Ethash 不同的路线, 让我们做一些重点对比:

确定性与概率性

与中本聪共识和 Ethash 这类概率性共识不同, Tendermint 是确定共识——这意味着 Tendermint 每个区块都是最终确定的,而不像比特币的区块只是处于“很可能”被确定的状态。

我们回顾一下中本聪共识,区块总是处于“未确定”状态——只有确定某个区块在“最长链”上,才能有把握认为该块正在被最终确定,这也是为什么比特币交易需要等“6个区块确认”。


而在 Tendermint 中,验证者成功投票及提交后,区块就立即被确认了。

固定验证者 vs. 可变验证者

中本聪共识及 Ethash 允许矿工随时选择加入或退出,并不需要其他矿工提前知晓。相反地,Tendermint 共识要求维护一个事先知晓且固定的验证者集合,验证者身份是靠他们的公钥来辨认的。

领导 vs. 无领导

中本聪共识及 Ethash 没有指定领导者来提议下一个区块(i.e. 任何矿工都有可能挖到下一个区块)。另一方面,Tendermint 选择领导者,或称为提议者,负责提出下一个区块。

明确的 vs. 模糊的超时机制

中本聪共识和 Ethash 没有使用超时机制来确保矿工一定能出块,而 Tendermint 有明确的超时机制保证区块链的出块过程不会遭遇延宕。

100 个验证者 vs. 1000 个验证者

Invalid cast from type 'string_type' to Object

查询交易返回

clfsc -u https://beta-api-chain.xxx.com get transaction_id 784886dbbc6cbbbfd2a223e1fc91a7b87231718aa94efdf39ec1e085331d9490
Error 3010006: Invalid transaction
Ensure that your transaction JSON follows the right transaction format!
You can refer to contracts/fsciolib/transaction.hpp for reference
Error Details:
Fail to parse transaction JSON '784886dbbc6cbbbfd2a223e1fc91a7b87231718aa94efdf39ec1e085331d9490'
Invalid cast from type 'string_type' to Object

查看数据

{
"timestamp": "2019-07-31T09:47:28.000",
"producer": "peacock15chy",
"confirmed": 0,
"previous": "006174d7444f5590416508b2cc03991549a9163ef8cf96dab4a5ce00f0f5ebf2",
"transaction_mroot": "d5cf5dc8b315844c92b625c3ff25ffb62766cdc6cb86ff69c64f4d0c927bd3cb",
"action_mroot": "3fbfdf22745803b79bd90022c9308775a1b6296beee81987efce8ef877dac543",
"schedule_version": 1,
"new_producers": null,
"header_extensions": [],
"producer_signature": "SIG_K1_KfnTYbdhByrtGbHrdNStbhYo2MQakmoweyBeQcRMmpXcgdPKB66yGnGAfT1g1rPfK3SVXFXAULU2CexRCoEqxdg6wSYUii",
"transactions": [
{
"status": "executed",
"cpu_usage_us": 419,
"net_usage_words": 0,
"trx": "784886dbbc6cbbbfd2a223e1fc91a7b87231718aa94efdf39ec1e085331d9490"
}
],
"block_extensions": [],
"id": "006174d85cf3afe91baaca598413f4395e412723ed9478eb6e797085dc6ab391",
"block_num": 6386904,
"ref_block_prefix": 1506454043
}

发现trx非object导致,查看本地代码

get_transaction_id_subcommand(CLI::App* actionRoot) {
      auto get_transaction_id = actionRoot->add_subcommand("transaction_id", localized("Get transaction id given transaction object"));
      get_transaction_id->add_option("transaction", trx_to_check, localized("The JSON string or filename defining the transaction which transaction id we want to retrieve"))->required();

      get_transaction_id->set_callback([&] {
         try {
            auto trx_var = json_from_file_or_string(trx_to_check);
            auto trx = trx_var.as<transaction>();
            std::cout << string(trx.id()) << std::endl;
         } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_check))
      });
   }
};

并未做判断,怀疑版本太旧,查看github 最新代码
https://github.com/EOSIO/eos/blob/eb88d033c0abbc481b8a481485ef4218cdaa033a/programs/cleos/main.cpp#L1316

 get_transaction_id->set_callback([&] {
         try {
            fc::variant trx_var = json_from_file_or_string(trx_to_check);
            if( trx_var.is_object() ) {  // 增加了判断
               fc::variant_object& vo = trx_var.get_object();
               // if actions.data & actions.hex_data provided, use the hex_data since only currently support unexploded data
               if( vo.contains("actions") ) {
                  if( vo["actions"].is_array() ) {
                     fc::mutable_variant_object mvo = vo;
                     fc::variants& action_variants = mvo["actions"].get_array();
                     for( auto& action_v : action_variants ) {
                        if( !action_v.is_object() ) {
                           std::cerr << "Empty 'action' in transaction" << endl;
                           return;
                        }
                        fc::variant_object& action_vo = action_v.get_object();
                        if( action_vo.contains( "data" ) && action_vo.contains( "hex_data" ) ) {
                           fc::mutable_variant_object maction_vo = action_vo;
                           maction_vo["data"] = maction_vo["hex_data"];
                           action_vo = maction_vo;
                           vo = mvo;
                        } else if( action_vo.contains( "data" ) ) {
                           if( !action_vo["data"].is_string() ) {
                              std::cerr << "get transaction_id only supports un-exploded 'data' (hex form)" << std::endl;
                              return;
                           }
                        }
                     }
                  } else {
                     std::cerr << "transaction json 'actions' is not an array" << std::endl;
                     return;
                  }
               } else {
                  std::cerr << "transaction json does not include 'actions'" << std::endl;
                  return;
               }
               auto trx = trx_var.as<transaction>();
               transaction_id_type id = trx.id();
               if( id == transaction().id() ) {
                  std::cerr << "file/string does not represent a transaction" << std::endl;
               } else {
                  std::cout << string( id ) << std::endl;
               }
            } else {
               std::cerr << "file/string does not represent a transaction" << std::endl;
            }
         } EOS_RETHROW_EXCEPTIONS(transaction_type_exception, "Fail to parse transaction JSON '${data}'", ("data",trx_to_check))
      });

所以解决方案就是更新对应的代码,或者直接升级版本。