BCSkill (Block chain skill ) 技术社区
社区QQ群:791420381
推荐论坛:https://eosfans.io
Telegram: https://t.me/bcskill

符合标准的EOS助记词生成私钥

最近要对接HD钱包,测试得到EOS的
eosjs-ecc 不符合标准的BIP44

m/44'/194'/0'/0/0

修正方法为

const hdkey = require('hdkey')
const wif = require('wif')
const ecc = require('eosjs-ecc')
const bip39 = require('bip39')
const mnemonic = 'real flame win provide layer trigger soda erode upset rate beef wrist fame design merit'
const seed = bip39.mnemonicToSeedSync(mnemonic)
const master = hdkey.fromMasterSeed(Buffer(seed, 'hex'))
const node = master.derive("m/44'/194'/0'/0/0")
console.log("publicKey: "+ecc.PublicKey(node._publicKey).toString())
console.log("privateKey: "+wif.encode(128, node._privateKey, false))

根据测试例子,修改eosjs-ecc支持扩展方法

package.json 增加新依赖

npm i bip39
npm i hdkey
npm i wif

修改api_common.js 增加bip44参数

默认false兼容之前版本

seedPrivate: (seed, bip44 = false) => PrivateKey.fromSeed(seed, bip44).toString(),

修改 key_private.js

增加新依赖

const hdkey = require('hdkey')
const WIFReturn = require('wif')
const bip39 = require('bip39')

修改fromSeed支持新实现

PrivateKey.fromSeed = function(seed, bip44) { // generate_private_key
    if (!(typeof seed === 'string')) {
        throw new Error('seed must be of type string');
    }
    if(bip44) {
      const seedString = bip39.mnemonicToSeedSync(seed)
      const master = hdkey.fromMasterSeed(Buffer(seedString, 'hex'))
      const node = master.derive("m/44'/194'/0'/0/0")
      return WIFReturn.encode(128, node._privateKey, false)
    }
    return PrivateKey.fromBuffer(hash.sha256(seed));
}

测试

测试助记词

real flame win provide layer trigger soda erode upset rate beef wrist fame design merit

原版eosjs-ecc

<script src="./dist-web/eosjs-ecc.js"></script>
    <script>
   (async () => {
    let wif = eosjs_ecc.seedPrivate('real flame win provide layer trigger soda erode upset rate beef wrist fame design merit');
    console.log(wif + '\n');
    let pubKey = eosjs_ecc.privateToPublic(wif);
    console.log(pubKey + '\n');
   })();
  </script>

生成结果为

5JX94izH6NMZkGqq9VvrmSTWux28HKRyns5mBwujzB9p48XSgNQ
EOS6j4E5ksFkDBAP32XXYseTaUkGqBKqPzYjcwUVeBV4JY8UbS1N5

使用修改后得标准BIP44

<script src="./dist-web/eosjs-ecc.js"></script>
    <script>
   (async () => {
    let wif = eosjs_ecc.seedPrivate('real flame win provide layer trigger soda erode upset rate beef wrist fame design merit',true);
    console.log(wif + '\n');
    let pubKey = eosjs_ecc.privateToPublic(wif);
    console.log(pubKey + '\n');
   })();
  </script>

结果为

5KX4T16FtxG9LvRJukA31TP9BKq3jYve3xQ3Px3ui8mzuJ7nUYE
EOS61oRAVkx1rqPM8mEsBZxPAFAa9Nm6kLa7mQs6mRKTsRTFQaad7

参考

https://github.com/satoshilabs/slips/blob/master/slip-0044.md
https://iancoleman.io/bip39/

备注

eosjs编译时需要先

npm run build

不能直接

npm run build_browser

如果测试自定义得bip44中得 coin type,可以在https://iancoleman.io/bip39/ 中得Derivation Path 选项中,选择BIP32,并去掉最后一层,例如,自定义coin type为9527

m/44'/9527'/0'/0

理解以太坊的Layer 2扩容解决方案:状态通道(State Channels)、Plasma 和 Truebit

对于以太坊来说,2018年是专注底层架构之年。今年很多早期参与者会测试网络极限,并且重新关注以太坊的扩容技术。

以太坊仍然处于起步阶段。直到现在,它仍然没有那么安全或者可扩展。对于任何密切关注以太坊技术的人来说,这都是很好理解的。但在过去一年,ICO推动的炒作已经过分夸大了目前的网络能力。以太坊和web3的美好愿景—— 一个安全,易于使用的去中心化网络,由一系列通用的经济协议来约束,并且被数十亿人来使用——这个承诺希望在即,但是只有在关键的底层架构完成的前提下才能实现

那些正在致力于构建以太坊的底层架构以及扩展其性能的项目通常被称为扩容解决方案。这些方案有很多种形式,并且常常彼此兼容或互补。

在这篇长文中,我想深入探讨某一类扩容解决方案:“链下(off-chain)”或者说“layer 2”解决方案

  • 首先,我们来总体讨论下以太坊(以及所有公链)的扩容挑战。

  • 其次,我们会介绍解决扩容挑战的不同方法,并且会区分“layer 1”和“layer 2”解决方案。

  • 第三,我们会深入研究layer 2解决方案并且解释它是如何工作的——具体来说,我们会讨论状态通道PlasmaTruebit

本文的重点是让读者能够对于layer 2解决方案工作原理有一个完整和详细的概念性理解。但是我们不会深入研究代码或特定的实施方案。反之,我们会专注于理解用来构建这些系统的经济机制,还有一些在所有layer 2扩容技术中得以共享的共识。

1. 公链的扩容挑战

首先,重要的是理解“扩容”并不是一个单一的、特定的问题。它指的是一系列以太坊必须要解决的挑战,从而以太坊才能被全球数十亿人使用。

最常讨论的扩容挑战是交易吞吐量问题。目前,以太坊每秒可以处理大约15笔交易,而相比而言Visa的处理能力大约为45,000/tps。在过去一年,一些应用例如以太猫,或者偶发的ICO,都足够火爆,能让以太坊网络“降速”并且会提高交易费用(gas)。

像以太坊这种公链的关键限制在于,每一笔交易都需要被网络中的每一个节点处理。以太坊区块链上的每个操作,一笔交易,以太猫的诞生,部署新的 ERC20 合约,每一个发生在以太坊区块链上的操作都必须由网络中的每个节点并行处理。这是设计理念决定的,它是保证公链权威性的一部分。节点们不用依赖其他节点来告诉它们,区块链的当前状态是什么,它们自己就能搞清楚。

这就从根本上给以太坊网络的交易吞吐量造成了限制:它不能高于我们对单个节点所要求的交易吞吐量。

我们可以让每个节点去承担更多工作。如果我们将区块的大小加倍(也就是区块的gas值限制),也就意味着每个节点处理每个区块的工作量大致是之前的两倍。但是这就带来了影响去中心化的代价问题:节点需要做更多工作意味着低性能的计算机(例如消费级设备)可能会退出以太坊网络,如果算力集中在高性能节点运营者手中,挖矿就变得非常中心化。

所以,我们需要找到让区块链能够在不提高单个节点工作量的情况下,负担更多有用工作的方法。

从概念上来说,有两种方法可以解决这个问题

Ⅰ. 如果每个节点不必并行处理每一项操作会怎么样?

第一种方法和我们预设的前提不同,如果我们构建一个区块链,其中每个节点不需要都去处理每个操作会怎么样?如果网络被分成两部分,它们还可以半独立的进行操作,又会怎么样?

A部分可以处理一批交易,同时B部分可以处理另一批交易。这样就可以有效地使区块链的交易吞吐量翻倍,因为我们的限制现在能够被两个部分同时处理。如果我们将区块链分为很多不同的部分,那么我们就可以把区块链的吞吐量提高很多倍。

这就是“分片”技术背后的见解,“分片”是由Vitalik的以太坊研究团队和其他社群提出的扩容解决方案。区块链被分成很多部分,它们被称为“分片”,每个分片都可以独立地处理交易。分片通常被称为是“layer 1”扩容解决方案,因为这是在以太坊底层协议上实现的。如果您想了解有关分片的更多内容,我建议您阅读extensive FAQ这篇博文

II. 如果在以太坊现有处理交易能力之外去进行更多有效操作会怎么样?

第二种选择是从反方向进行考虑的:并不是提高以太坊区块链本身的能力,如果我们可以利用已有的能力做更多的事情,会怎么样呢?底层以太坊区块链的吞吐量是相同的,但是实际上我们可以做更多对于人们和应用程序有用的操作,例如交易,游戏中的状态更新,或是简单的计算。

这就是类似状态通道PlasmaTruebit等“链下”技术背后的见解。当然,这些技术都在解决不同的问题,但它们都是通过“链下”操作,而不是在以太坊区块链上进行操作,同时仍然保证了足够的安全性和不可更改性。

这些也被称为“layer 2”解决方案,因为它们是构建在太坊主链“之上”的。他们不需要更改底层协议,他们只是以基于以太坊的智能合约的形式存在,与链下的软件进行交互。

2.layer 2扩容解决方案是加密经济解决方案

在深入了解具体的layer 2扩容解决方案前,了解使得他们可行的底层概念是非常重要的。

支撑公有链最基础的动力源泉就是加密经济共识。通过仔细协调激励机制以及通过软件和加密算法对激励进行保障,我们就可以创建对于系统内部状态达成共识的可靠计算机网络。这就是中本聪白皮书中的关键见解,并且这种见解现在也被应用在许多不同公链的设计中,包括比特币和以太坊。

加密经济共识给予我们一个确定性的核心——除非有极端事件例如51%×××发生,否则我们都知道链上操作——例如支付或者智能合约,会像写定的那样执行。

在layer 2解决方案背后的见解,就是我们可以把核心内核的确定性作为锚定——我们附加其他经济机制的一个固定点。 第二层的经济机制可以向外扩展公链的效用,让我们能够进行链下交互,如果有需要的话,仍然可以可靠地转回到核心内核上。

这些构建在以太坊底层“之上”区块链层,并不总是与链上操作具有相同的保障。但是它们仍然有足够的不可更改性和安全性,所以仍然是非常有用的。尤其是当不可更改性的要求略微下降时,我们能以更快的速度或更低的开销来进行操作。

加密经济并不是随着中本聪的白皮书而开始和结束,它只是一种我们正在学着应用的技术。加密经济不仅体现在对于核心协议的设计,还体现在对于第二层系统的设计,其扩展了底层区块链的功能性。

I. 状态通道(State channels)

状态通道(State Channel)是一种用于执行交易和其他状态更新的“链下(off-chain)”技术。但是,一个状态通道“内”发生的事务仍保持了很高的安全性和不可更改性。如果出现任何问题,我们仍然可以选择回溯到链上交易中确定的“稳定内核”。

大部分读者会对支付通道(payment channel)比较熟悉,这一概念已存在多年了,并且最近通过闪电网络(lightning network)部署在了比特币上。状态通道是支付通道泛化出来的形式,它们不仅可用于支付,还可用于在区块链上进行任意的“状态更新”,就像在智能合约内部进行更改一样。 2015 年,Jeff Coleman 第一次详细介绍了状态通道。

解释状态通道的运作方式的最佳方法就是来看一个样例。请记住这是一个概念性的解释,也就是说我们不会涉及具体实现的技术细节。

现在设想Alice和Bob想玩一场井字游戏,胜者可以获得一个eth。为了实现这一目标,最简单的方法就是在以太坊上创建一个智能合约,它可以实现井字游戏规则并跟踪每个玩家的动作。每次玩家想要移动时,他们都会向合约发送一个交易。当一名玩家获胜时,根据规则描述,合约会付给胜者一个eth。

这样做是可行的,但是效率低下并且速度很慢。Alice和Bob正在让整个以太坊网络处理他们的游戏过程,这对于他们的需求来说有点过于夸张。玩家每想要移动一步都需要支付gas费用,并且还要在进行下一步移动之前等待区块打包。

但是,我们可以设计一个新的系统,它能使Alice和Bob在玩井字游戏时产生尽可能少的链上操作。 Alice和Bob能够以链下的方式更新游戏状态,同时又很确定计算结果在需要时仍能将其恢复到以太坊主链上验证。我们把这个系统称之为“状态通道”。

首先,我们在以太坊主链上创建一个能够理解井字游戏规则的智能合约“Judge”,同时它也能够确认Alice和Bob是我们游戏中的两位玩家。该合约持有一个eth的奖励。

然后,Alice和Bob开始玩游戏。Alice创建并签署一个交易,它描述了她游戏的第一步,然后将其发送给Bob,Bob也对交易签了名,再将签名后的版本发回并为自己保留一份副本。然后Bob也创建并签署一个描述他游戏中第一步的交易,并发送给Alice,她也会对交易签名,再将其发回,并保留一份副本。每一次,他们都会这样互相更新游戏的当前状态。每一笔交易都包含一个“随机数”,这样就使我们可以直接知道游戏中移动的顺序。

到目前为止,还没有发生任何链上的操作。Alice和Bob只是通过互联网向彼此发送交易,还没有任何东西传达到区块链上。但是,所有交易都可以发送给 Judge 合约,换句话说,它们是有效的以太坊交易。你可以把这看作两人彼此来回填写了一系列经过区块链认证的支票。实际上,并没有钱从银行中存入或取出,但是每人都有一堆可以随时存入的支票。

当Alice和Bob结束游戏时(可能是因为Alice赢了),他们可以通过向 Judge 合约提交最终状态(比如,交易列表)来关闭该通道,这样就只需要付一次交易费用。Judge合约会确定双方都对这个“最终状态”签名,并等待一段时间来确保没人会对结果提出合理质疑,然后向Alice支付一个eth的奖励。

为什么我们需要设置让 Judge 合约等待一下的”质疑时间“?

想象Bob并没有给 Judge 合约发送真实的最终状态,而是发送一份之前他赢了Alice的状态。但是 Judge 仅仅是一份合约,它本身无法知晓这是否是最新的状态。

而质疑时间给了Alice一个机会向 Judge 合约证明Bob对游戏最终状态撒了谎。如果有更近期的状态,她就会有一份已签名交易的副本,并且可以将其提供给 Judge合约。Judge 合约可以通过检查随机数来判断Alice的版本是否是最新的,然后驳回Bob窃取胜利的企图。

特性和限制

状态通道在许多应用中都很有用,它们对于在链上执行操作进行了严密的改进。但重要的是要注意,在决定应用程序是否适合被通道化时,需要做出一些特定的权衡:

  • 状态通道依赖于有效性
    如果Alice在质疑时间内掉线了(可能是Bob渴望赢得奖品,破坏了她家的网络连接),她可能无法在质疑时间结束前做出回应。但是,Alice可以付费给其他人,让其保存一份她的状态副本,并作为她的权益代表,以保持系统的有效性。
  • 当参与者将在长时间内交换大量状态更新时,状态通道非常有用
    这是因为部署 Judge 合约时创建一个通道会产生初始成本。但是一旦部署完成,该通道内每一个状态更新的成本都会非常低。
  • 状态通道最适于有一组确定参与者的应用程序
    这是因为 Judge 合约必须始终知晓所有参与到给定通道的实体(比如,地址)。当然我们可以增加或删除用户,但是每次都需要更改合约。
  • 状态通道有很强的隐私性
    因为一切都参与者之间的通道“内”发生,而不是公开广播并记录在链上。只有开头和结尾的交易必须公开。
  • 状态通道具有即时终结性
    这意味着只要双方都签署了一个状态更新,这个状态就可以被认为是最终状态。双方都有明确保证,如果有必要,他们可以将状态“强制执行”到链上。

我们 L4 团队正致力于创建 Counterfactual框架:一个能在以太坊上推广普及使用状态通道的框架。我们的总体目标是使开发者可以在他们的应用程序中以模块化的方式使用状态通道,而不需要成为状态通道的专家。你可以在这里了解更多该项目的信息。我们将在 2018 年的第一季度发布技术细节文件。

另一个值得注意的针对以太坊的状态通道项目是 Raiden,该项目目前正致力于构建一个支付通道网络,使用了和闪电网络类似的范式。这意味着你不必与想要交易的特定人员开通状态通道。你可以与一个连接到更大型通道网络的实体架设一个单独的通道,这样你就能够向连接到同一网络的任何人支付而无需额外费用。

除了 Counterfactual 和 Raiden,在以太坊上还有几个用于特定于应用程序的状态通道。例如,Funfair 就为他们的去中心化×××构建了他们称之为“Fate channels”的状态通道,SpankChain 为成人产业演员构建了一套单向支付通道(他们还为其 ICO 使用了状态通道),还有 Horizon Games 也在他们的第一款基于以太坊的游戏中使用了状态通道。

II. Plasma

2017 年 8 月 11 日,Vitalik Buterin 和 Joseph Poon 发表了一篇题为《Plasma:自主智能合约》( Plasma: Autonomous Smart Contracts)的论文。这份论文介绍了一种新技术,它能使以太坊每秒可以处理的交易远比现在更多。

就像状态通道一样,Plasma 是一种管理链下交易的技术,同时依靠底层的以太坊区块链来实现其安全性。但是 Plasma 采用了一种新思路,它是通过创建依附于以太坊“主”区块链的“子”区块链。这些子链又可以相应地产生它们自己的子链,子链的子链又可以产生子链,如此递进下去。

其结果就是,我们可以在子链级别上执行许多复杂的操作,在与以太坊主链保持最低限度交互的情况下,运行拥有数千名用户的整个应用程序。Plasma 子链可以更快地操作,承担的交易费用更低,因为子链上的操作无需在整个以太坊区块链上进行重复。

plasma.io/plasma.pdf

为了弄清楚 Plasma 的运行原理,我们来看一个如何使用它的样例。

让我们试想你正在创建一个基于以太坊的集换式卡牌游戏。这些卡牌是一些基于 ERC 721 不可替代的token(比如 Cryptokitties),但是拥有一些特定的特征和属性,可以让玩家相互对战——就像炉石传说或者万智牌一样。这种复杂操作在链上执行的成本很高,所以你决定在你的应用程序中使用 Plasma 作为替代方案。

首先,我们在以太坊主链上创建一系列的智能合约,它们可作为 Plasma 子链的“根节点”。Plasma 根节点包含了子链的基本“状态交易规则”(诸如“交易无法消费已消费过的资产”),也记录了子链状态的哈希值,并建立一种允许用户在以太坊主链和子链间转移资产的“桥接”服务。

然后,创建我们的子链。子链可以拥有自己的共识算法,在这个例子中,我们假设它使用了 PoA(Proof of Authority),POA是一种依赖于可信区块生产者(比如,验证者)的简单共识机制。在“工作量证明”系统中,区块生产者与 POW 系统中的矿工类似,它们是接收交易,形成区块并收取交易费用的节点。为了让样例简单点,假设你(也就是创建游戏的公司)是创建区块的唯一实体,即你的公司运行着几个节点,这些节点就是子链的区块生产者。

一旦子链创建并激活,区块生产者会定期的向根节点合约进行提交。这意味着他们实际上在说“我提交的 X 是子链中当前最新的区块”。这些提交被当做子链中事务的证明,链上记录在 Plasma 根节点里。

现在子链已经准备好了,我们可以创建集换式卡牌游戏的基本组件。这些卡片是基于 ERC721,在以太坊主链上初始化,然后由 Plasma 根节点转移到子链上。这里引入了一个关键点:Plasma 可以扩展我们与基于区块链的数字资产的交互,但是这些资产应当首先创建在以太坊主链上。然后,我们将实际的游戏应用程序以智能合约的方式部署到子链上,这样子链就包含了游戏所有的逻辑和规则。

当用户想要玩我们的游戏时,他们只需要和子链交互。他们可以持有资产(ERC721 卡牌),可以为了以太币购买并交换它们,可以与其他用户对战,还有其他任何游戏中允许的行为,而这些行为都不需要与主链直接交互。因为只有很少的节点(比如,区块生产者)才需要处理交易,所以交易费会很低,操作也能更快。
但是这种模式安全吗?
通过把更多操作从主链迁移到子链上,很明显我们可以执行更多的操作了。但是这样安全吗?发生在子链上的交易是否具备权威性?毕竟,我们方才描述的系统只有一个中心实体控制着子链的区块生产。这样不是中心化吗?这样公司不是随时都能窃取你的资产或者拿走你收集的卡牌吗?

简而言之,即使是在子链中单个实体完全控制所有区块生产的情况下,Plasma 也为你提供了一个基本保证,即你始终都可以将你的资金和资产退回到主链上。如果一个区块生产者开始恶意行事,最坏的情况也仅仅是强迫你离开这个子链。

让我们了解一下区块生产者能恶意行为的几种方式,同时了解 Plasma 会如何处理这些情况。

首先,想象一个区块生产者试图通过说谎欺骗你,他们可以通过创建一个伪造的新区块,声称你的资金被他们控制了。由于他们是唯一的区块生产者,所以他们可以自由引入一个并不遵循我们区块链规则的新区块。就像其他区块一样,他们也得将这个区块存在的证据提交给 Plasma 根节点合约。

如上所述,用户有能将他们的资产随时退回到主链上的最终保障。在这个情景下,用户(或者更确切来说是代表他们权益的应用程序)会侦测到这种盗窃的企图,并在区块生产者尝试和使用他们“偷到”的资产之前把自己的资产撤回到主链上。

Plasma 还创建了一种机制,防止欺诈时不能退回到主链上。Plasma 包含了一种机制,任何人(包括你)都可以向根节点合约发布欺诈证明(fraud proof),试图证明区块生产者作弊。这个欺诈证明会包含之前区块的信息,并且允许我们证明,根据子链中的交易状态规则,这个错误的区块并不能正确接上之前的状态。如果欺诈被证实,那么子链就会“回滚”到前一个区块。更妙的是,我们还构建了一种体系:任何签出错误区块的区块生产者会被处罚,他们会因此丢失链上的保证金。

plasma.io/plasma.pdf

但是提交欺诈证明需要访问底层数据,即需要用之前的实际区块历史来证明欺诈。如果区块生产者为了防止Alice向根节点合约提交欺诈证明,而并没有共享之前区块的信息的话,那怎么办?

在这种情况下,Alice的解决方案就是收回资产并脱离子链。本质上说,爱丽丝向根节点合约提交了一份“欺诈证明”。在一段延迟期内,任何人都可以质疑该证明(比如,展示后面的合法区块证明实际上她消费了这些资产)。之后,Alice的资产将会被移回到以太坊主链上。

plasma.io/plasma.pdf

最后,区块生产者可以审查子链中的用户。如果区块生成者愿意,他们可以直接不在其区块中包含特定交易,从而有效阻止用户在子链上执行的任何操作。如上所述,解决方案仅仅是把我们所有的资产退回到以太坊主链上就可以了。

但是,资产退回本身也会带来风险。其中一个问题就是如果所有使用这一子链的用户同时都要退回资产会怎么样。在这种大量提现的情况下,以太坊主链可能没有足够的容量来处理每个人在质疑期内的交易,也就意味着用户可能会丢失资金。尽管有许多可能的技术能够防止这种情况发生,例如,通过延长质疑时间的方式来响应资产退回的需求。

值得注意的是,并不是所有的块生产者都必定被一个实体所控制——这只是我们案例中的极端个例。我们可以创建子链,把区块生产者分布在许多不同实体间,即像公有链一样真正地去中心化。在这种情况下,区块生产者如上述方式进行干预的风险更小,而用户不得不将资产转移回以太坊主链的风险也更小。

现在我们已经介绍了状态通道和 Plasma,以下几点对比值得注意。

它们之间一个不同之处在于,当状态通道中所有参与方都一致同意退回资产时,状态通道可以立即执行退回。如果Alice和Bob同意关闭通道并撤回他们的资金。只要他们都认同最终状态,他们就可以立即从状态通道取得他们的资产。这在 Plasma 上是不可能的,如上所述,用户在取出资产的过程中必须经历一个质疑时间。

与 Plasma 相比,状态通道每笔交易的平均交易费更便宜,而且速度更快。这意味着我们应该可以在 Plasma 子链上构建状态通道。例如,一个应用程序中两个用户在进行一系列的小型交易。在子链级别构建状态通道应该会比直接在子链上执行每个交易更加便宜和迅速。

最后,需要注意的是这些只是部分描述,我们还遗漏了许多细节。Plasma 本身还处于非常早期的阶段。如果你有兴趣了解 更多关于 Plasma 的现状,可以查看 Vitalik 最近的一个关于 “最小化可行性Plasma(Minimal Viable plasma)” 的提议(即精简的 plasma 实施方案)。一个台湾的团队正在进行这项工作,可以在这个代码库中查看。OmiseGo 正在研究他们的去中心化交易的实施,他们在这里发布了进度最近更新信息。

III. Truebit

Truebit 是一种帮助以太坊在链下进行繁重或复杂计算的技术。这使得它与状态通道和Plasma不同,它们对于提高以太坊区块链的总交易吞吐量更有用。正如我们在开篇部分所讨论的那样,扩容是一个多方面的挑战,需要的不仅仅是更高的交易吞吐量。Truebit不会让我们完成更多的交易,但它能让基于以太坊的应用程序去处理更复杂的事务,并且仍能被主链验证。

这就让我们能够对以太坊应用程序做一些有用的操作,这些操作的计算成本太高,无法在链上执行。例如,验证来自其他区块链的简单支付验证(SPV)证明,这个证明可以让以太坊智能合约“检查”交易是否在另一个链上已经发生(如比特币或狗狗币)。

让我们看一个例子。试想你有一些代价很高的计算(比如 SPV 证明)需要作为以太坊应用程序的一部分来执行。你不能单纯的把它当做以太坊主链上智能合约的一部分,因为SPV证明的计算成本是非常昂贵的。请记住,因为每个节点都要并行处理这种操作,所以在以太坊上直接进行任何计算都是非常昂贵的,。以太坊的区块有最大费用(gas值)限制,从而为该区块中所有交易能执行的计算总量设置了上限。但是SPV证明的计算成本实在太大,即使它是区块中唯一的交易,它需要的gas值也是单个区块gas值上限的许多倍。

相反,链下你可以只需要向人支付很少的费用来完成计算。去完成计算并由你付费的这个人被称作解算者(solver)。

首先,解算者支付智能合约中的保证金。然后,你给解算者一份他们需要计算内容的描述,它们运行计算,并返回结果。如果结果是正确的(大多在一秒钟内),他们的保证金将被退回。如果事实证明,解算者并没有正确地执行计算——比如他们欺诈或犯了错误——他们就会失去他们的保证金。

但是我们如何判断结果是正确的还是错误的?Truebit使用一种叫做“验证游戏”(verification game)的经济机制。从本质上讲,我们为其他被称为挑战者的参与方创造了激励,来检查解算者的工作。如果挑战者能够通过验证游戏证明解算者提交了错误结果,那么他们会获得奖励,而解算者则会失去保证金。

因为验证游戏是在链上执行的,所以它不能直接地计算结果(这会破坏整个系统的设计初衷——如果我们可以在链上进行计算,我们就不需要Truebit)。相反,我们强迫解算者和挑战者确定他们意见不一致的具体操作。实际上,我们将双方逼入一个死角——找到导致他们对结果意见不一的具体代码行。

一旦确定了具体的操作,实际上它就足够小到可以由以太坊主链执行了。然后,我们通过以太坊的智能合约执行该操作,该合约一劳永逸地解决了哪一方说了真话,哪些又是谎言或错误。

如果您想了解更多有关Truebit的信息,你可以阅读这篇论文,或者阅读Simon de la Rouviere的博客文章

结论

layer 2解决方案都有一个共同见解:一旦我们有了公链提供的有确定性的核心,我们就可以把它作为加密经济系统的锚定,然后扩展区块链应用的可用性。

现在我们已经研究了一些示例,我们可以更具体地了解layer 2解决方案如何应用这种见解。layer 2解决方案所使用的经济机制往往是互动游戏:它们通过为各参与方创造激励来相互竞争或彼此“检查”工作。区块链应用程序可以假设某个给定的声明可能是真的,因为我们已经为另一方创造了强烈的激励来提供证明其错误的信息。

在状态通道方案中,就是通过给各方机会互相“反驳”,来确定通道的最终状态。在Plasma方案中,就是如何管理欺诈证据和提现的方法。在Truebit方案中,我们通过激励挑战者来证明解算者是错误的,以确保解算者给出真实结果。

这些系统将有助于解决将以太坊扩展到庞大的全球用户群所涉及的一些挑战。有些系统,像状态通道和Plasma,将会增加平台的交易吞吐量。而其他的系统,像Truebit,可以将更困难的计算作为智能合约的一部分,从而开创新的使用案例。

这三个示例仅代表了加密经济扩容解决方案设计可能性的一小部分。我们甚至没有涉及像CosmosPolkadot这样的“跨链协议”所做的工作(尽管这些协议到底是“layer 2”解决方案还是其他东西,这已经完全是另一篇文章的主题)。我们还应该期望发明新的、意想不到的layer 2系统,这些系统可以改进现有模型,或者在速度,不可更改性和开销成本之间提供新的权衡。

比任何特定的layer 2解决方案更重要的是,底层技术的进一步发展以及使其首先成为可能的机制:加密经济的设计。

这些layer 2扩容方案有力证明了像以太坊这样的可编程区块链的长期价值。建立基于layer 2解决方案的经济机制,只有在可编程区块链上才有可能:因为你需要用脚本语言编写执行交互式游戏的程序。由于像比特币这样的区块链只提供了有限的脚本功能,这对于它们来说很困难(或者有些情况下,比如 Plasma,这是完全不可能实现的)。

以太坊允许我们创建layer 2解决方案,以便在速度,不可更改性和成本开销之间找到新的权衡点。这可以让底层区块链更能够适用于更多种类的应用程序,因为面对不同威胁模型的不同类型应用程序会对不同的权衡模式有天生的偏向。对于需要保障乃至国家性范围内的高价值交易时,我们可以使用主链。对于更偏重交易速度的数字资产交易,我们可以使用 Plasma。layer 2方案让我们能够在不影响底层区块链的前提下取得权衡,并保持去中心化和不可更改性。

而且,很难事先预测给定的扩展方案需要哪些脚本功能。当设计以太坊的时候,Plasma和 Truebit还尚未发明。但是因为以太坊是完全可编程的,实际上它能够实现我们能发明的任何经济机制。

区块链技术的价值是建立在加密经济共识的确定性内核,而诸如以太坊这样的可编程区块链才是能够充分利用这种价值的唯一途径。

感谢Vitalik Buterin,Jon Choi,Matt Condon,Chris Dixon,Hudson Jameson,Denis Nazarov和Jesse Walden对本文早期草稿的评论。
转载自:https://www.cnblogs.com/chongdongshequ/p/9700759.html

EOS 根据链Mongo数据判断交易状态

根据几个测试交易对比

  1. 正常发起普通交易(余额充足,非延迟)
  2. 正常发起延迟交易(余额充足,延迟)
  3. 正常发起延迟交易,交易到期前,将转出账户的余额转空(余额不足,延迟)

对比链Mongo表transactionstransaction_traces

对于transactions体现了交易所在块是否不可逆irreversible,而transaction_traces

 "receipt" : {
        "status" : "hard_fail",
        ...
    },

status状态来判断交易状态。
对于交易对比的1和2对于数据来说只是delay_sec数量问题,并且交易到期前,数据库中无法查到延迟交易。
1或2与3进行比较,transactions无关键差别,transaction_tracesstatus状态存在差别。

清洗数据

为了方便数据的使用,最好还是需要单独新洗出一个交易状态表,此表中关键包含,

  1. 交易id
  2. 所在块号
  3. 交易状态
  4. 是否不可逆

测试数据如下

库名:trx_status

{
    "_id" : ObjectId("5fbb8ef5349bfe65ef744506"),
    "trx_id" : "c1811e16a77288e3f374faf2eb2b0308d2f480b3414a07357d6e45cf29ffe56e",
    "block_num" : 11414152,
    "status" : 0,
    "irreversible" : true,
    "block_time" : ISODate("2020-11-23T10:29:10.000Z"),
    "block_timestamp" : NumberLong(1606127350000),
    "createdAt" : ISODate("2020-11-23T10:29:09.952Z"),
    "updatedAt" : ISODate("2020-11-23T10:31:05.985Z"),
    "block_id" : "00ae2a8818a6bd8cd33e9873419ac7258813ea9578dde95c8e0771397351b952"
}

交易等待不可逆

status==0 && irreversible == fasle

交易丢失(丢块)

status==0 && irreversible == fasle && ((current_time - block_time) >((head_block_num - last_irreversible_block_num) * 0.5 + 容差值))
// current_time - block_time 为当前时间与交易创建时间的秒数
// 容差值是为了避免误判,通常为 1-2分钟即可

交易失败

status!=0

因为交易到执行时间前,数据库中没有插入数据,所以没有delayed事件

当交易不可逆

status==0 && irreversible == true

备注

status的状态

status_enum {
    // 这个表示执行成功(所以不需要执行出错逻辑)
    executed  = 0,
    // 客观的来说,执行失败了(或者没有执行),某一个出错逻辑执行了
    soft_fail = 1,
    // 执行失败了,并且执行的出错逻辑也失败了,所以并没有状态改变
    hard_fail = 2,
    // 交易被延迟了,计划到未来的某个时间执行
    delayed   = 3,
    // 交易过期了,并且存储空间返还给用户
    expired   = 4  ///< transaction expired and storage space refuned to user
};

修改配置

config.ini配置中,去掉onblock交易,较少垃圾数据

mongodb-filter-out = eosio:onblock:

参考

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

使用Go开发一个简单反向代理服务

最近,团队的小伙伴反映,我们这边一个短连接服务在一台普通的服务器上吞吐量受到限制,所以把服务迁移到高性能机器上,虽然硬件是数倍的提升但压测发现吞吐量并没有预期的效果。

结合后台服务本身的特点初步原因分析:

  1. 从下往上看:服务属于计算IO密集型,性能瓶颈多在于计算请求,但高配机压测过程中,受到单实例模块之间通讯采用串行调用的特点,虽然单点请求计算性能有很大提速,但总体并行上不去,CPU利用率低

  2. 从上往下看: 吞吐量受服务器的接受能力影响很大,由于短连接接入层目前只有一个实例,无论部署在中配或是高配,除非是多实例模式或者类似nginx这种多worker工作模型,一般情况下,单实例accept的效果有限,高并发时容易成为瓶颈

  3. 从服务进程的角度看,单个web api的请求accept队列(backlog)是有限制的,如果多实例部署也许能补短。

分析到这里,很多人都想到可以通过扩容+分布式通讯的方式来弥补短板。是的,方法是摆在面前,但是你想到一个方法不难,难的是你要如何去验证你的想法。毕竟对于一个成熟的产品技术框架,不是随便都能重构的,一定要数据说话。

不过如何调优不是本文的目的,本文的目的是如何使用Go来快速实现一个反向代理服务来验证前面的背景想法。

设计

一个反向代理层,无论是四层还是七层,我觉得实现上主要需要具备以下工作:

  • 负载均衡算法
  • 请求可传递
  • endpoints可权重配置
  • endpoints故障处理
    关于使用Go写负载均衡算法,之前在 《关于Round-Robin》这文章提及过,这里不延伸。

以http为例,go如何快速实现反向代理?

查看go的文档,发现源码net/http/httputil提供了一个叫 ReverseProxy:https://godoc.org/net/http/httputil#ReverseProxy 的玩意,这个就是golang自带反向代理功能,而且使用很简单

ReverseProxy提供了ServerHTTP方法,这意味着我们可以跟普通http handler一样简单地使用它来处理请求

ReverseProxy 暴露了NewSingleHostReverseProxy的方法

// NewSingleHostReverseProxy returns a new ReverseProxy that rewrites
// URLs to the scheme, host, and base path provided in target. If the
// target's path is "/base" and the incoming request was for "/dir",
// the target request will be for /base/dir.
func NewSingleHostReverseProxy(target *url.URL) *ReverseProxy {
        targetQuery := target.RawQuery
        director := func(req *http.Request) {
                req.URL.Scheme = target.Scheme
                req.URL.Host = target.Host
                req.URL.Path = singleJoiningSlash(target.Path, req.URL.Path)
                if targetQuery == "" || req.URL.RawQuery == "" {
                        req.URL.RawQuery = targetQuery + req.URL.RawQuery
                } else {
                        req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
                }
        }
        return &ReverseProxy{Director: director}
}

这样,我们可以通过一行代码就基本上实现了主体的反向代理功能了,如下:

httputil.NewSingleHostReverseProxy(address)

实现

结合Round-Robin,我们尝试实现我们的反向代理层
带权重的负载均衡实现 round-robin.go

package roundrobin

// RR: 基于 权重round robin算法的接口
type RR interface {
    Next() interface{}
    Add(node interface{}, weight int)
    RemoveAll()
    Reset()
}

const (
    RR_NGINX = 0 //Nginx算法
    RR_LVS   = 1 //LVS算法
)

//算法实现工厂类
func NewWeightedRR(rtype int) RR {
    if rtype == RR_NGINX {
        return &WNGINX{}
    } else if rtype == RR_LVS {
        return &WLVS{}
    }
    return nil
}

//节点结构
type WeightNginx struct {
    Node            interface{}
    Weight          int
    CurrentWeight   int
    EffectiveWeight int
}

func (ww *WeightNginx) fail() {
    ww.EffectiveWeight -= ww.Weight
    if ww.EffectiveWeight < 0 {
        ww.EffectiveWeight = 0
    }
}

//nginx算法实现类
type WNGINX struct {
    nodes []*WeightNginx
    n     int
}

//增加权重节点
func (w *WNGINX) Add(node interface{}, weight int) {
    weighted := &WeightNginx{
        Node:            node,
        Weight:          weight,
        EffectiveWeight: weight}
    w.nodes = append(w.nodes, weighted)
    w.n++
}

func (w *WNGINX) RemoveAll() {
    w.nodes = w.nodes[:0]
    w.n = 0
}

//下次轮询事件
func (w *WNGINX) Next() interface{} {
    if w.n == 0 {
        return nil
    }
    if w.n == 1 {
        return w.nodes[0].Node
    }

    return nextWeightedNode(w.nodes).Node
}

func nextWeightedNode(nodes []*WeightNginx) (best *WeightNginx) {
    total := 0

    for i := 0; i < len(nodes); i++ {
        w := nodes[i]

        if w == nil {
            continue
        }

        w.CurrentWeight += w.EffectiveWeight
        total += w.EffectiveWeight
        if w.EffectiveWeight < w.Weight {
            w.EffectiveWeight++
        }

        if best == nil || w.CurrentWeight > best.CurrentWeight {
            best = w
        }
    }

    if best == nil {
        return nil
    }
    best.CurrentWeight -= total
    return best
}

func (w *WNGINX) Reset() {
    for _, s := range w.nodes {
        s.EffectiveWeight = s.Weight
        s.CurrentWeight = 0
    }
}

//节点结构
type WeightLvs struct {
    Node   interface{}
    Weight int
}

//lvs算法实现类
type WLVS struct {
    nodes []*WeightLvs
    n     int
    gcd   int //通用的权重因子
    maxW  int //最大权重
    i     int //被选择的次数
    cw    int //当前的权重值
}

//下次轮询事件
func (w *WLVS) Next() interface{} {
    if w.n == 0 {
        return nil
    }

    if w.n == 1 {
        return w.nodes[0].Node
    }

    for {
        w.i = (w.i + 1) % w.n
        if w.i == 0 {
            w.cw = w.cw - w.gcd
            if w.cw <= 0 {
                w.cw = w.maxW
                if w.cw == 0 {
                    return nil
                }
            }
        }
        if w.nodes[w.i].Weight >= w.cw {
            return w.nodes[w.i].Node
        }
    }
}

//增加权重节点
func (w *WLVS) Add(node interface{}, weight int) {
    weighted := &WeightLvs{Node: node, Weight: weight}
    if weight > 0 {
        if w.gcd == 0 {
            w.gcd = weight
            w.maxW = weight
            w.i = -1
            w.cw = 0
        } else {
            w.gcd = gcd(w.gcd, weight)
            if w.maxW < weight {
                w.maxW = weight
            }
        }
    }
    w.nodes = append(w.nodes, weighted)
    w.n++
}

func gcd(x, y int) int {
    var t int
    for {
        t = (x % y)
        if t > 0 {
            x = y
            y = t
        } else {
            return y
        }
    }
}
func (w *WLVS) RemoveAll() {
    w.nodes = w.nodes[:0]
    w.n = 0
    w.gcd = 0
    w.maxW = 0
    w.i = -1
    w.cw = 0
}
func (w *WLVS) Reset() {
    w.i = -1
    w.cw = 0
}

主体部分 main.go

var RR = rr.NewWeightedRR(rr.RR_NGINX)

type handle struct {
    addrs []string
}

func (this *handle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    addr := RR.Next().(string)
    remote, err := url.Parse("http://" + addr)
    if err != nil {
        panic(err)
    }
    proxy := httputil.NewSingleHostReverseProxy(remote)
    proxy.ServeHTTP(w, r)
}

func startServer() {
    //被代理的服务器host和port
    h := &handle{}
    h.addrs = []string{"172.17.0.2:28080", "172.17.0.3:28080"}

    w := 1
    for _, e := range h.addrs {
        RR.Add(e, w)
        w++
    }
    err := http.ListenAndServe(":28080", h)
    if err != nil {
        log.Fatalln("ListenAndServe: ", err)
    }
}

func main() {
    startServer()
}

在ReverseProxy中的ServeHTTP方法实现了这个具体的过程,主要是对源http包头进行重新封装,而后发送到后端服务器。

这样,我们一个简单快速的反向代理层就实现了,日常可以基于它自定义负载我们的服务。
转载自:https://lihaoquan.me/2018/4/24/go-reverse-proxy.html