BCSkill (Block chain skill )
区块链中文技术社区

只讨论区块链底层技术
遵守一切相关法律政策!

Mandel 将于 2022 年收购 EOSIO

自两年多前发布 EOSIO 2.0 以来,EOS 网络没有进行过重大升级。从那时起,block.one 产生了 EOSIO 2.1 和 EOSIO 2.2 的候选版本;然而,由于各种原因,Clarionos 团队和更广泛的 EOS 社区不希望所有代码都与最新版本捆绑在一起。

展望未来,Clarionos 团队会将 EOSIO 代码库分叉到一个新的代码库中,我们称之为 Mandel(Mandelbrot 的缩写)。在所有 EOSIO 驱动的区块链之间达成更广泛的共识之前,Mandel 名称是一个占位符。

Mandel 的第一个版本将是 3.0,并将从 EOSIO 2.0 派生,同时从 EOSIO 2.1 和 EOSIO 2.2 中挑选一些最有价值的增强功能。Mandel 3.0 还将引入两个新的硬分叉:合同支付、增强的可配置区块链参数。此外,它将从 EOSIO 2.1 中挑选可配置的 WASM 限制硬分叉。

虽然 EOS 区块生产者大部分仍使用 EOSIO 2.0,但一些 EOS 基础设施节点和其他下游软件已升级到 EOSIO 2.1。在迁移到 Mandel 3.0 之前要求这些节点“降级”到 EOSIO 2.0 可能会造成不必要的短期负担;因此,Clairionos 还将发布源自 EOSIO 2.1 的 Mandel 2.3 版本,该版本对 Mandel 3.0 启用的新硬分叉支持精选支持。EOSIO 2.1 节点应该能够无缝升级到 Mandel 2.3,同时与网络保持同步。
Clarionos 的目标是将尽可能多的 EOSIO 2.1 增强功能迁移到 Mandel 3.0,而不会延迟关键硬分叉的交付。

即将推出的硬分叉功能

1. 可配置的 WASM 限制

这种硬分叉允许区块生产者增加可以部署的智能合约的规模,这将允许部署更大、更强大的合约。出于安全目的,EOSIO 必须限制各种 wasm 参数,例如内存、函数数量等。一旦合约达到这些限制之一,开发人员就被迫将其代码划分为多个合约。早在 EOSVM 为 EOS 带来巨大的性能提升之前,就已经建立了最初的限制。我们现在认为增加这些限制是安全的。我们没有一次性增加,而是使它们可配置。这使网络有能力在未来扩展或在攻击者以某种方式利用额外容量时对其进行调整。

2. 合同支付

开发人员面临的最具挑战性的事情之一是使他们的应用程序易于使用。要求用户从网络租用 CPU、NET 和 RAM 资源以便与应用程序交互是一个主要的可用性障碍。在理想的世界中,智能合约将支付合约用户所需的所有资源。
当今存在的 EOS 要求每笔交易都必须由至少一个密钥签名,并且每个权限级别的阈值至少为 1。这限制了合约获取用户所需资源的潜力。
我们开发了一种不需要硬分叉的合同支付方法,但“黑客”涉及发布任何人都可以签名的“私人”密钥。当我们可以简单地允许相同的交易发生而没有任何密钥对其进行签名时,这会给网络带来不必要的负担。
在没有任何密钥的情况下可能发生的操作的一个示例是合约需要执行一些维护任务。合约愿意支付自己的维护费用,它不关心谁授权了交易。如果没有要执行的维护,那么合约将简单地拒绝交易并避免任何资源使用。维护任务的一个示例可能是当前由已弃用的延迟事务执行的许多任务。
通过合约支付,可以实现与比特币交易结构相同的交易。这消除了那些只想将 EOS 用作货币的人的帐户创建成本。它还将使实施 隐私令牌成为可能,而不会让您的隐私受到资源系统的损害。虽然这些事情将通过合同支付成为可能,但它们超出了当前路线图的范围。

有关合同付款的更多信息,请点击此处

3. 增强的可配置区块链参数

这个硬分叉功能使添加/删除/配置未来的目标功能变得更加容易。不必为每个新功能或可配置参数添加新的本机内在函数,合约可以调用一个内在函数。这允许合约根据特征的存在或配置参数的值进行有条件的操作。此功能主要在 EOSIO 2.1 中实现,但经过审查,Clarionos 团队得出结论,需要进行一些小调整以确保更一致的操作。

系统合约升级

Clarionos 团队对 EOS 系统联系人有一个拉取请求,该请求将通过发布“私钥”来启用合同支付功能。这将允许应用程序在等待硬分叉生效的同时开发增强的用户体验。硬分叉后也需要利用该特性。

时间线

以下时间表是理想的,可能会随着发展的变化而变化。

  • 2022 年 1 月 31 日— Mandel 3.0 的候选版本
  • 2022 年 2 月— Mandel 3.0 测试网络启动和社区验证
  • 2022 年 3 月 1 日— Mandel 3.0 最终版本
  • 2022 年 3 月 2 日——网络部署 Contract Pays 系统合约
  • 2022 年 4 月 1 日——Mandel 2.3 发布
  • 2022 年 4 月 9 日——下一次伊甸园选举
  • 2022 年 5 月 19 日——硬分叉激活。(2022年的黄金分割率)

这将标志着 EOS 独立于 block.one 的象征性完成,因为这将是 EOS 网络第一次运行不是由 block.one 开发或发布的软件版本。
资金
EOS 网络基金会已与 Clarionos 达成协议(待区块生产者批准),在 Mandel 3.0 候选版本交付后(2022 年 1 月 31 日)向 Clarionos 支付 200,000 EOS。然后,Clarionos 将支持社区修复在测试阶段发现的任何错误。

未来方向

该路线图是 EOS 独立的最短路径,也是振兴 EOS 多年计划的第一步。您将在下一次路线图更新中看到的一些项目包括:3 秒确定性、加速 EVM 支持的内在函数以及加速隐私应用程序的内在函数。
EOS 即将以多年未见的速度加快发展速度。

原文地址:https://medium.com/edenoneos/eos-mandel-to-takeover-eosio-in-2022-2e25bf5451f0

EVM+:帮助EOS实现EVM兼容

引言:EVM+工作组正在采取行动,在EOS上部署以太坊虚拟机

概述

EOS正在凝聚社区力量共同开发生态所需的关键项目,从而进一步推动EOS生态的复苏。我们拥有才华横溢的开发人员,能够做出重大贡献的社区领袖,以及责任心和行动力都非常强的EOS网络基金会。
EOS正在开辟自己的前进道路,并开启一个新的协作时代。
目前,由EOS网络基金会资助成立的六大核心工作组正在高速运行,每个工作组都在其领域发挥重要作用,其中API+、Core+、Wallet+和Audit+四个工作组蓝皮书已接近完成,每份蓝皮书都是重要的研究成果,将详细阐述发展目标,各自领域如何取得成功的可行策略等。
今天,我们将带领大家认识EVM+核心工作组,在正式介绍之前,我们先来了解一下六大核心工作组的概况。

六大核心工作组介绍

目前,EOS网络基金会赞助成立了六个核心工作组,他们分别是:

  • API+:主要负责提供数据接口,帮助EOS生态以外的应用程序更好的集成EOS网络。
  • Core+:主要负责维护EOS系统使EOS更适合运行各种类别的应用程序。
  • Wallet+:主要负责配合软件将EOS集成到外部应用程序中。
  • Audit+:主要负责为EOS应用提供安全分析工具和合约审计的整体框架。
  • EVM+:主要负责实现与EVM的兼容以及探索EOS可行性的经济激励措施。
  • Recover+:主要负责建立危机处理框架,通过制定紧急联络、反应机制、DAO保险和白帽奖励等措施,帮助EOS项目在遭遇黑客攻击后以更合理有效的方式应对危机及追回丢失资产。

核心工作组将会与EOS生态中最有经验的人和组织合作,在落实EOS路线图方面发挥重要作用。这将是一个评估EOS生态现状、识别潜在问题并为未来发展制定路线图的绝佳机会。

每个工作组都收到了来自EOS网络基金会提供的20–25万美元的资助,另外EOS网络基金会还支出10万美元成立了一个监督小组,专门监督工作组的整体进展。

但出于社区呼声和生态需求的考虑,EVM+工作组与其他工作组的工作流程略有不同。在EOS Argentina的领导下,EVM+工作组将采取更敏捷的方式进行开发。

EVM+工作组不撰写蓝皮书,而是直接进行开发和产品交付,从而更快的在EOS智能合约层面实现EVM兼容。另外, EVM+工作组的初始预算资金为20万美元。

EVM+工作组介绍

EOS目前面临的最重要的问题之一是区块链的互操作性。
区块链领域最容易产生的问题是向内聚焦发展,而忽视了向外兼容取长补短,这样的问题在EOS表现的很明显。开发人员经常陷入一种思维逻辑:EOS拥有最好的技术,所以EOS是最好的地方,即使其他公链生态的人都还没有意识到EOS的潜力。

这样的想法会使EOS与更广阔的区块链世界隔绝开来,很容易坐井观天错过许多机会。许多开发者离开EOS,转投以太坊,原因就是虽然以太坊存在缺陷,但是却拥有更好的兼容性。

这也是我们成立EVM+工作组并加速这方面工作的重要原因,在EOS上部署以太坊虚拟机是一件非常重要的工作,我们需要推动EOS融入更广阔的区块链生态,不断的引进来、走出去,兼容并蓄快速发展。

从技术及业务角度来看,实现EVM兼容能够在释放EOS潜力方面发挥至关重要的作用。我们需要更多的以太坊开发者和用户加入EOS生态,而帮助EOS实现EVM兼容是吸引他们加入的最佳方式。

站在成功的肩膀上,吸引开发人才

EVM+工作组的领导团队是EOS创世区块生产者EOS Argentina,该团队也是Evodex和EOS Metamask钱包背后的开发人员。
为EVM+工作组提供协助的还有EOS生态的著名开发者Syed Jafri,他在2020年凭借EOSIO.EVM赢得了Block.one举办的EVM挑战赛, EOSIO.EVM使Solidity开发人员能够在EOSIO上快速启动并以高达100倍的速度和低于1000倍的成本运行应用程序。可以说,Syed Jafri在帮助EOS实现EVM兼容方面十分有经验。
EVM是EOS急需的创新。EOS公链在过去几年中面临的主要问题之一是资金匮乏。让我们和以太坊生态做一个对比:
以太坊速度更慢、效率更低而且Gas成本非常高,但以太坊和其他基于EVM的公链之间拥有更高的网络效应和代币流动性。另一方面,一部分以太坊上的开发人员能够通过创造基于Solidity编写的模块化、可重复利用、安全开源的智能合约获得高额回报,另一部分开发者可通过OpenZeppelin等组织的收录的代码库获得丰富的开发工具和教程。一个不可否认的事实是:EVM生态系统中的开发人员工具、代码库、可供dApp开发人员使用的开源代码一直在快速增长。
反观EOS生态,开发人员只能使用不太成熟的工具从头开始构建许多代码库,一个不恰当的比喻是:以太坊生态的开发者制造汽车就像是搭积木,把现成的车轮、方向盘、发动机等拼接在一起就好,而EOS生态的开发者想要制造汽车,需要从制造车轮开始。
EOS网络基金会赞助的其他工作组正在专注于改善EOS开发人员的体验,提供更多可用的本地工具,但都需要一些时间才能充分实现。
因此,我们和社区都认为,我们可以利用EVM已经存在的人才和资源,并通过EVM兼容将这些引入EOS,以此挖掘出EOS生态的潜力。
EOS发展已经进入极其重要的一年,EOS仍然面临两个巨大挑战:
一方面,EOS必须向潜在的开发者展示其巨大的潜力和价值;
另一方面,EOS必须消除开发者进入生态的障碍,尽可能降低门槛,吸引更多开发者加入。
即使项目方或开发人员相信EOS更好,但如果进入EOS生态的过程漫长、繁琐,项目方和开发人员也不会加入。因此帮助其他公链的开发人员以无障碍、低门槛的方式,将项目部署、迁移或克隆到EOS生态,是更行之有效的方式。
EVM+工作组:在EVM和EOSIO之间架起桥梁
EVM+的总体目标是从开发人员和用户的角度,使EOS成为一个兼容的EVM平台。为此,以下是工作组正在解决的关键问题:
智能合约级别的兼容与本地节点实施:EOS Argentina正在努力开发基于智能合约的解决方案,使 EVM 能够在 EOS VM WASM 运行时正常运行。 这将允许EOS网络在不进行协议升级的情况下升级EVM运行(无需分叉)。
ETH 预编译合约:这不仅有利于EOS上的EVM,同时也有利于智能合约开发人员。 EOS与其他链一样,交易执行时间有限(协议为150毫秒,BP为30毫秒)。 尽管EOS VM功能强大且速度很快,但仍有一些类型的操作在Web汇编中执行时,缺乏它们在本机代码中可以进行的优化。 为了加快速度,EVM+工作组正在寻求将ETH预编译实现为EOS内在函数(本地代码)。
完全兼容RPC:该领域的计划仍在开发中,但EVM+团队的目标是至少支持大多数RPC,以允许在ETH上使用通用工具/开发环境。这将是该计划最重要的工作之一。
确定性Gas消耗/计费:EOS链使用三种资源:CPU(主观度量/可变价格)、NET(客观度量/可变价格)和RAM(客观度量/可变价格)计费。这方面工作的挑战是为用户提供确定性的Gas消耗和有竞争力的Gas价格,同时防止Web3 API被滥用。
支持Metamask:这意味着EOS生态将支持Metamask钱包,以便EOS本地合约和使用Solidity编写的EVM合约进行交互。
EVM+工作组将按照上述方向快速推进工作,为EOS实现EVM兼容创建最佳解决方案,并计划在2022年夏天之前产生切实可行的解决方案。
目前的六个工作组正在构建一个框架,使开发人员和用户更容易参与EOS,促进EOS网络的增长,使EOS生态系统更具吸引力。 EOS的未来从未如此光明。 我们邀请您加入我们,共同创造区块链世界最伟大的公链回归故事。
请继续关注EOS网络基金会赞助的其他工作组的深入研究。
关于EOS网络基金会
EOS网络基金会是一个非营利性的组织,旨在倾听社区声音、传达社区意愿并扶持社区优质项目发展,成为EOS社区的信息共享桥梁,并为EOS生态提供资金、技术、运营、未来规划、生态构建等关键基础设施支持,进一步发挥EOS作为世界上速度最快的治理型区块链的全部潜力。
EOS网络基金会Discord
EOS网络基金会中文Twitter
EOS网络基金会中文Medium
EOS网络基金会中文电报订阅号
EOS网络基金会中文电报群

https://medium.com/eos-network-foundation/evm-bringing-the-ethereum-virtual-machine-to-eos-f26b88466d99

EOS 希望与 Dan Larimer 一起履行 2017 年的承诺

关键要点

EOS 网络基金会通过招募网络架构师 Dan Larimer,在加强其独立于 Block.one 方面迈出了重要一步。
Larimer 和他的团队将分叉 EOSIO 代码库。
多年来,该项目的创始公司 Block.one 一直未能兑现承诺,社区普遍对此表示不满,但这些进展都是如此。

EOS 网络基金会已聘请 EOSIO 的创始开发者 Dan Larimer 帮助其接管 EOS 生态系统的方向。该合作伙伴关系是在 EOS 社区投票停止授予其创始支持者 Block.one 之后的。

EOS Network Foundation 的目标是更美好的未来

EOS 网络基金会的目标是复兴。

Dan Larimer 是 EOS 的架构师,之前是该项目的创始支持者 Block.one 的一部分,现在将帮助该基金会重振该项目的生态系统。为了进一步建立与 Block.one 的独立性,Larimer 和他的团队将分叉 EOSIO 代码库。

分叉将发生在两个主要升级中:Mandel 2.3 和 Mandel 3.0。EOS Network Foundation 获得了 200,000 EOS 来帮助 Larimer 和他的团队执行分叉。

Larimer 在新闻稿中表示,Mandel 代码分叉是“通往 EOS 独立的最短路径”。他还将分叉描述为“振兴 EOS 多年计划的第一步”。

今天宣布的事态发展是在 EOS 社区多年的困境之后发生的。EOS 在 2017 年声名鹊起,当时 Block.one 通过 ICO 筹集了 41 亿美元来资助该项目。它是加密社区中被称为“以太坊杀手”的几个区块链之一。然而,它未能兑现承诺。在筹集资金之后,Block.one 提出了一些在几年后仍未推出的举措。

Block.one 的失误导致 EOS 社区内长期存在分歧。该公司被指控阻碍了该项目的进展,EOS 网络基金会的首席执行官不断声称由于 Block.one 未能执行而遭受了损失。在两个阵营之间多年的紧张关系之后,EOS 区块生产者在 12 月投票停止了 6700 万个 EOS 代币的归属,这些代币计划在未来六到七年内为 Block.one 解锁。该拨款目前价值约 1.8 亿美元。

EOS 网络基金会希望加入 Larimer 将使该项目更接近于实现其最初的愿景。上周,它从 EOS 社区获得了 2100 万美元的资金,以实现其目标。能否卷土重来,还有待观察。

英文原文:https://cryptobriefing.com/eos-wants-fulfill-2017-promises-with-dan-larimer/

以太坊源码解析:state

本篇文章分析的源码地址为:https://github.com/ethereum/go-ethereum
分支:master
commit id: 257bfff316e4efb8952fbeb67c91f86af579cb0a

引言

对于任何一个区块链项目来说,账户信息的保存都是关键。在以太坊中,保存账户信息的代码是由 state 模块实现的。

与比特币区块链不同的是,以太坊没有使用 UTXO(Unspent Transaction Output) 模型,而是使用的账户余额模型。这篇文章,我们就来看看 state 模块是如何实现账户余额模型的。

什么是 state

要介绍 state,就不得不提区块链的账户模型了。在各个区块链项目中,都会有一个账户地址,类似于我们的银行账户;每个账户都会对应着一些信息,比如有多少币等,类似于我们在银行某个账户下的余额。而保存这些账户对应信息的方式,就是账户模型。

目前在区块链的世界里有两种账户模型:UTXO(Unspent Transaction Output) 模型和账户余额模型。UTXO 的中文翻译为「未花费的交易输出」。这种方式不记录账户余额,只记录每笔交易(一次转账就是一笔交易),账户的余额是通过计算账户的所有历史交易得出的(想像一下如果你知道你老婆/老公的银行账户的每笔交易,那么你就可以算出她/他现在卡还有多少钱了)。(这篇文章里我们不详细讨论 UTXO 模型,感兴趣的读者可以自己搜索相关文章,网上的讲解还是挺多的)。

账户余额模型与我们常用到的银行账户相似,都是保存了我们账户的余额。当有人给我们转账时,就将余额的数字加上转账的值;当我们转账给别人时,就将余额的数字减去转账的值。

这么看来,账户余额模型是比较容易理解的。以太坊使用的就是账户余额模型,而实现这一模型的,正是 state 模块。之所以模块名叫 state,我猜也是因为它就像一个状态机:它记录了每个账户的状态,每当有交易发生时,就更改一下相应账户的状态。

state 模块中主要的对象是 StateDB 对象,正是它记录了每个账户的信息,其中包含 balance(以太币的数量)、nonce(交易标号,见「重放攻击」小节),等信息。这从它的方法中就能看出来,比如:

func (self *StateDB) GetBalance(addr common.Address) *big.Int
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int)
func (self *StateDB) SubBalance(addr common.Address, amount *big.Int)
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int)
func (self *StateDB) GetNonce(addr common.Address) uint64
func (self *StateDB) SetNonce(addr common.Address, nonce uint64)
func (self *StateDB) GetCode(addr common.Address) []byte
func (self *StateDB) SetCode(addr common.Address, code []byte)
......

所以,总得来说,以太坊的 state 实现了账户余额模型,保存了以太坊账户的所有信息。每当这些信息发生改变,state 对象就会发生改变,就像一个状态机一样。

实现架构

state 的实现其实比较简单,但由于加了一些缓存功能,乍看上去会觉得比较乱。我画了一张图来示意 state 的主要实现:

从图上可以看出,state 模块对外的主要对象是 StateDB,这个对象提供了各种管理账户信息的方法(可以很容易地在 state/statedb.go 中查看到,这里就不一一列举了)。在对象的内部主要有四个字段,下面我们分别简单的解释一下这四个字段。

stateObjects 是一个 map,用来缓存所有从数据库(也就是 trie 字段)中读取出来的账户信息,无论这些信息是否被修改过都会缓存在这里。因此在需要将所有数据提交到数据库或 trie 字段代表的 trie 对象时,只需将 stateObjects 中保存的信息提交即可(当然需要借助 stateObjectsDirty 字段踢除没被修改过的信息)。

stateObjectsDirty 很显然是用来记录哪些账户信息被修改过了。需要注意的是,这个字段并不时刻与 stateObjects 对应,并且也不会在账户信息被修改时立即修改这个字段。事实上,这个字段是与 journal 字段相关的:在进行某些操作时(StateDB.Finalise 和 StateDB.Commit)时,才会将 journal 字段中记录的被修改的账户整理到 stateObjectsDirty 中。

journal 字段记录了 StateDB 对象的所有操作,以便用来进行回滚操作。需要注意的是,当 stateObjects 中的所有信息被写入到 trie 字段代表的 trie 树中后,journal 字段会被清空,无法再进行回滚了。

db 字段代表的是一个从数据库中访问 trie 对象的对象,比如 OpenTrie。db 是一个接口类型,但在实际的调用代码中,它只有一个实例就是 cachingDB 这个对象。这个对象是对 trie.Database 的一个包装,通过 pastTries 字段缓存了部分曾经访问过的 trie 对象。这样当下次再次访问这个 trie 时,就不需要从数据库中读取了。这个缓存的功能需要 cachedTrie 对象的配合,在将 trie 提交到数据库的同时通知 cachingDB 进行缓存。

trie 字段也是一个接口类型的字段,它代表了保存账户信息的 trie 对象。在实际调用代码中,它也只有一个实例,就是 cachedTrie 这个对象。这个对象是对 trie.SecureTrie 的一个封装,主要修改是改写了 trie.SecureTrie 的 Commit 方法:在新的 Commit 方法中,除了调用 trie.SecureTrie 的 Commit 方法外,还会与 cachingDB 配合,调用 cachingDB.pushTrie 将当前的 trie.SecureTtrie 缓存到 cachingDB 中。

可以看出代表账户余额模型的对象就是 StateDB 对象,它就像一个 KV 数据库,以账户地址作为 Key、以账户信息作为 Value 进行数据的存储和查询。其底层使用 trie 对象来实现这种类似 KV 结构的数据的存储。而在数据库层面,StateDB 又增加了一些缓存机制,使得运行时效率更高(但也使得代码更复杂一点)。

但不得不说,由于底层使用 trie 对象保存所有数据,StateDB 与 KV 数据库不同的是,在任意时刻或提交数据到数据库(Commit)后,可以得到 trie 对象的哈希值。在生成区块时,这个哈希值是保存在区块头的 Root 字段中的。如此一来,就能随时从数据库中读取任意区块的 state 信息了。

功能解析

了解了 state 模块的整体设计结构以后,其实代码是很容易读懂的。因此我不打算面面俱到的介绍 state 模块的所有功能。在这一节里,我选了几个比较重要的功能,进行简单的介绍。

保存账户信息

前面我们说过,StateDB 就像一个 KV 数据库,底层使用 trie 保存数据。那么到底 K 是什么、 V 是什么呢?

其实要了解 KV 分别是什么,从下面的代码就可以看出来:

func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
    ......
    enc, err := self.trie.TryGet(addr[:])
    if len(enc) == 0 {
        self.setError(err)
        return nil
    }
    var data Account
    if err := rlp.DecodeBytes(enc, &data); err != nil {
        return nil
    }
    ......
}

很明显,所谓的 Key 就是账户地址,Value 就是一个 Account 对象。它的定义如下:

type Account struct {
    Nonce    uint64
    Balance  *big.Int
    Root     common.Hash // merkle root of the storage trie
    CodeHash []byte
}

因此每个账户中,都包含了四个信息:代表账户操作编号的 Nonce,代表账户余额的 Balance,代表数据存储 trie 哈希的 Root,和代表合约代码哈希的 CodeHash。我们会在介绍以太坊合约时再介绍 Root 和 CodeHash 字段代表的含义;关于 Nonce 的意义,参看本篇文章中的「重放攻击」小节。

可以看到,所谓的余额模型真的非常简单,就是用 Balance 字段记录当前余额就可以了。在矿工生成一个新的区块时,会处理所有的交易和合约。如果涉及到A账户给B账户转账的操作,就从A的账户中减去交易数值,然后给B账户加上同一数值。当所有交易处理完成后,这些交易引起的 StateDB 的变化使其内容的 trie 对象生成了新的 root 哈希;矿工将这个啥希记录到区块的 header 中。这样当别人收到这个区块时,可以重复这一过程,如果得到的 StateDB 的哈希与区块头中记录的哈希一致,则交易验证成功,说明这个区块的交易是正确的。

重放攻击

我们知道,以太坊中的转账是通过一个个交易完成的。现在设想这样一个场景:A 给 B 发起了一笔转账交易,这次交易被所有矿工确认为合法的,因此转账成功;这时候 B 机灵一动,突然想到这笔交易既然之前被认定为合法的,那再次把这笔交易发给矿工,应该还是合法的吧?毕竟交易本身的数据没有变过。所以 B 把之前 A 发起的这笔交易找出来,又重新发到了网络上。这就是重放攻击。如果没有预防措施,那么 B 就可以用这个交易不断地把 A 的钱转给自己。

在以太坊中,防止重放攻击的任务正是由前面提到的 Account 结构中的 Nonce 字段完成的。具体来说,以太坊中每笔交易中都需要记录一个发起账户的 nonce 值:

type Transaction struct {
    data txdata
    ......
}

type txdata struct {
    AccountNonce uint64          `json:"nonce"    gencodec:"required"`
    ......
}

一笔交易就是由 Transaction 结构代表的,而 txdata 结构中的 AccountNonce 就是 Account.Nonce 的值。在构造一笔交易时,发起者需要将 txdata.AccountNonce 字段设置为发起账户的 Account.Nonce 值加 1。

在矿工出块进行验证时,会对 Transaction 中的 AccountNonce 值进行验证,如果这个值确实比发起者账户的 Account.Nonce 值大 1,则为有效的;否则这个交易目前是无效的(如果 txdata.AccountNonce 与 Account.Nonce 的差 > 1,说明这笔交易可能以后会生效,就暂时保留;如果这个差 < 1,则直接丢弃这个交易)。

除了验证 Transaction 与 Account 的 nonce 值,还需要在 Transaction 结构整体验证成功、转账完成后,将发起账户的 Account.Nonce 值加 1。这样才能在使用这笔交易发起重放攻击后,让这种攻击失效。

可以看到,Account.Nonce 主要功能就是用来避免重放攻击。但这需要代表交易的 Transaction 结构和矿工的配合,即 Transaction 中有 AccountNonce 字段记录着此次转账完成后账户的 Account.Nonce 值应该是多少;而矿工需要验证这个值,且在转账完成后修改账户的 Account.Nonce 值。

快照与回滚

在 StateDB 的实现中,还有快照与回滚的功能,这两个功能主要是由下面两个方法提供的:

func (self *StateDB) Snapshot() int {
    id := self.nextRevisionId
    self.nextRevisionId++
    self.validRevisions = append(self.validRevisions, revision{id, self.journal.length()})
    return id
}

func (self *StateDB) RevertToSnapshot(revid int) {
    // 根据快照 id,从 validRevisions 中查找快照信息
    idx := sort.Search(len(self.validRevisions), func(i int) bool {
        return self.validRevisions[i].id >= revid
    })
    if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
        panic(fmt.Errorf("revision id %v cannot be reverted", revid))
    }
    snapshot := self.validRevisions[idx].journalIndex

    // 恢复快照
    self.journal.revert(self, snapshot)
    self.validRevisions = self.validRevisions[:idx]
}

StateDB.Snapshot 方法创建一个快照,返回一个 int 值作为快照的 ID。StateDB.RevertToSnapshot 用这个 ID 将 StateDB 的状态恢复到某一个快照状态。

这两个方法的实现都很简单,从中可以看出,StateDB.nextRevisionId 字段用来生成快照的有效 ID,而 StateDB.validRevisions 记录所有有效快照的信息。关键实现其实在 StateDB.journal 字段中,这个字段的类型是 journal 结构。我们详细看一下这个结构的实现。

journal 结构在 state/journal.go 中,它的定义如下:

type journal struct {
    entries []journalEntry         // Current changes tracked by the journal
    dirties map[common.Address]int // Dirty accounts and the number of changes
}

其中 entries 字段的类型是 journalEntry 类型的数组,journalEntry 是一个接口类型,主要方法就是用来恢复数据的 revert 方法,它代表了对某一操作进行回滚的操作,因此实现了 journalEntry 接口的对象有很多个,我把它们罗列在这里:

type createObjectChange struct
type resetObjectChange struct
type suicideChange struct
type balanceChange struct
type nonceChange struct
type storageChange struct
type codeChange struct
type refundChange struct
type addLogChange struct
type addPreimageChange struct
type touchChange struct

可以看到这些代表具体回滚操作的对象,对应了所有对 StateDB 的操作。每当有对 StateDB 的操作时,就会构造一个对应的回滚操作并调用 journal.append 方法将其加入到 journal.entries 中。比如对于增加余额的操作:

func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
    stateObject := self.GetOrNewStateObject(addr)
    if stateObject != nil {
        stateObject.AddBalance(amount)
    }
}

func (c *stateObject) AddBalance(amount *big.Int) {
    ......
    c.SetBalance(new(big.Int).Add(c.Balance(), amount))
}

func (self *stateObject) SetBalance(amount *big.Int) {
    // 构造 SetBalance 的回滚操作 balanceChange 并加其记录到 `journal.entries` 中
    self.db.journal.append(balanceChange{
        account: &self.address,
        prev:    new(big.Int).Set(self.data.Balance),
    })
    self.setBalance(amount)
}

journal.append 的实现很简单直接:

func (j *journal) append(entry journalEntry) {
    j.entries = append(j.entries, entry)
    if addr := entry.dirtied(); addr != nil {
        j.dirties[*addr]++
    }
}

这样,journal.entries 中积累了所有操作的回滚操作。当调用 StateDB.RevertToSnapshot 进行回滚操作时,就会调用 journal.revert 方法:

func (j *journal) revert(statedb *StateDB, snapshot int) {
    for i := len(j.entries) - 1; i >= snapshot; i-- {
        // Undo the changes made by the operation
        j.entries[i].revert(statedb)

        // Drop any dirty tracking induced by the change
        if addr := j.entries[i].dirtied(); addr != nil {
            if j.dirties[*addr]--; j.dirties[*addr] == 0 {
                delete(j.dirties, *addr)
            }
        }
    }
    j.entries = j.entries[:snapshot]
}

在 journal.revert 中,会从 journal.entries 中最后一项开始、向前至参数中指定的项,调用它们的 revert 方法。我们以 balanceChange 为例看看这些回滚对象是如何操作的。刚才提到过在修改 balance 的 stateObject.SetBalance 中会构造一个 balanceChange 对象:

func (self *stateObject) SetBalance(amount *big.Int) {
    // 构造 SetBalance 的回滚操作 balanceChange 并加其记录到 `journal.entries` 中
    self.db.journal.append(balanceChange{
        account: &self.address,
        prev:    new(big.Int).Set(self.data.Balance),
    })
    self.setBalance(amount)
}

其中 balanceChange.prev 字段保存了修改之前的 balance 值。那么在 balanceChange.revert 中就将这个值重新恢复到账户信息中就行了:

func (ch balanceChange) revert(s *StateDB) {
    s.getStateObject(*ch.account).setBalance(ch.prev)
}

注意这里调用的是 stateObject.setBalance 而不是 stateObject.SetBalance,后者会再次将修改加入到 journal 中,这并不是我们想要的操作。

现在我们可以总结一下 state 模块是如何实现快照和回滚功能的:

  1. 将所有可能的修改作一个统计。
  2. 实现所有可能操作对应的回滚操作对象。
  3. 在每次进行操作前,将对应的回滚对象加入到回滚操作的数组中,例如 journal.entries。
  4. 要在当前状态下创建一个快照,就记录下当前 journal.entries 的长度(因为 journal.entries 中一个数组)。
  5. 要恢复某个快照(即实现回滚操作),就从 journal.entries 中最后一项开始,向前至指定的快照索引,逐一调用这些对象的 revert 操作。

其实还是挺简单的,我们日常开发中要实现类似的功能,也可以参考这个实现方式。

要注意一点的是快照与回滚只能针对还未提交到数据库中的账户信息,即存在于 stateObject 中的信息。如果已经被提交到数据库中,就无法回滚了。(其实要想实现也是可以的,只是以太坊的代码没有这么实现而已)

总结

在这篇文章里,我们介绍了以太坊的 state 模块。 state 模块实现了以太坊的账户余额模型,它以一种类似 KV 数据库的形式存储账户信息,其中以账户地址作为 Key,以账户信息(Account 结构)作为 Value。它的底层使用 trie 对象对数据进行存储,这样不但可以快速获取某一账户的信息,还可以得到 trie 的 root 哈希保存到区块头中,这样矿工生成区块后,别的节点就可以重现交易对账户的修改,并将最终的保存账户信息的 trie 的 root 哈希与区块头中保存的哈希进行比较,方便对区块进行验证。

以上就是对 state 模块的所有分析。水平有限,如果有错误还请留言或邮件指出,非常感谢。

转载自:https://www.jianshu.com/p/bdc9b669576d

EOS的BFT-DPOS共识机制的进化过程及背后逻辑

不知道是阅读量到了质变,还是后面引用的这篇文章太好,我终于对BFT-DPOS有了更深刻的理解

POS:
出块不再由算力说了算,由节点持有的stake说了算,解决了POW算力资源被大量无用消耗,但由于无条件信任代表,节点作恶非常容易 , 比如nothing at stake攻击

DPOS:
动态产生一定数量的代表比如21个节点代表,只由这21个节点生成区块,降低了作恶的概率,同时用事后惩罚限制节点代表作恶行为,但始终不够安全(怎么惩罚,使用抵押物?抵押物需要多少?作恶导致的失败交易的损失都是可以量化的吗?如果不能量化抵押物就没有意义),同时确认速度确实也慢(需要14个block)

BFT+DPOS:
一个区块生产后通过BFT协议立马确认,解决了DPOS确认慢问题,需要超过1/3节点才能作恶,相比POW 51%作恶条件,这个1/3值相对来说还是偏低了点,但是这是一个折中,也只能这样了

BFT + DPOS + 小块:
该协议是为了加快出块速度,该改进的核心思想是让同一个节点产生6个小块。这个能提升性能的核心原因是6个小块的产生没有等待确认的环节。在BFT+DPOS算法中,一个节点要创建新块,必须等上一个块被确认(或者超时),因为它不确定上一个节点是否作恶是否会被确认,所以它只能等。但是如果上一个块时也是该节点产生的,它自然不需要等上一块确认,因为它知道上一个块是真实的,不是作恶的块,最后肯定会被确认,所以安心生产下一块。

可见DPOS+BFT+小块机制中,区块生成和确认以pipeline的方式进行,是同时进行的,加快了出块速度。

何为作恶:

各种共识算法,节点基本都会进行数据检验功能,因而数据伪造作恶基本不可能。那真正的作恶是什么?

主动作恶:节点主动生产两个区块,给一部分节点发送新区块A,另外一部分节点发送区块A1,这样系统就产生分叉了。

被动作恶: 块传播因为网络延时或者中断,会导致一部分节点缺失该块的信息,进而会产生分叉,然后后续区块生产者发现分叉后,如果遵循只选择一个分支生产区块就会让分叉收敛(能收敛最后就能达成共识),但是如果继续保持并加重这个分叉,最后就会导致整个链变成一颗森林,最后节点就永远没法达成共识, 这是被动作恶。比如POS的noting at stake攻击,当系统出现分叉时,生产者基于自己的利益会在两个分叉区块上各自生成一个区块(这样不管哪个分叉获胜,自己都能获得收益),这其实间接助长和加重了分叉

因而要阻止这些作恶行为,有三种方法:

1)机制保证,让生产者没有时间来生成2个区块(POW)

2)利益保证,生成两个区块最后肯定没用(BFT算法, 大家最后只会认可一个快),生产者获取不了什么好处,自然没必要生产两个区块

3)事后惩罚,比如DPOS,这种方法其实没啥用,因为事后发现作恶节点后,系统没法回溯区块,只能惩罚作恶节点,但是这个作恶损失是不确定的(前面提到过),起不到惩罚作用,且抵押物模式是一种限制,不利于生态发展

引用原文

区块链中最重要的便是共识算法,比特币使用的是POW(Proof of Work,工作量证明),以太币使用的POS(Proof of Stake,股权证明)而EOS使用的是BFT-DPOS。

什么是BFT-DPOS呢?即拜占庭容错式的委任权益证明。

要想明白BFT-DPOS的运行机制,首先就要先明白什么是DPOS。

由于POW在比特币的共识算法中极大地消耗了算法的资源。而且会有算法集中的问题,所以在2014年的时候Dan Larimer提出了一个相较于POW来说更加高效,轻便的共识机制即DPOS。该共识机制一边能让网络成本小型化,另一方面有回复语每个持股人一定的投票权。

这些超级节点呢能够:供相关计算资源和网络资源,保证节点的正常运行;当轮到某超级节点拥有出块权时,超级节点收集该时段内的所有交易,并对交易验证后打包成区块广播至其他超级节点,其他节点验证后把区块添加到自己的数据库中。这种共识机制采用随机的见证人出块顺序,出块速度为 3 秒,交易不可逆需要45秒。为什么需要 45 秒呢?因为 DPoS 下,见证人生产一个新区块,才表示他对之前的整条区块链进行了确认,表明这个见证人认可目前的整条链。而一个交易要达到不可逆状态,需要 三分之二以上的见证人确认,在 EOS 里就是 14 个见证人。DPoS共识算法也有极强的抗分叉能力,因为区块添加到一条区块链分叉的速率与拥有该共识的超级节点比例是相关的。当一个超级节点设法在两条分叉上同时生产区块时,EOS的持有者会在下一轮投票中将该超级节点删掉,并且EOS社区会给予相关恶意节点一定的惩罚。因此,在一般情况下,使用DPoS的EOS都是很难经历分叉的。

其次,我们还要明白BFT所代表的的意义。

拜占庭容错技术(Byzantine Fault Tolerance,BFT)是一类分布式计算领域的容错技术。拜占庭假设是对现实世界的模型化,由于硬件错误、网络拥塞或中断以及遭到恶意攻击等原因,计算机和网络可能出现不可预料的行为。拜占庭容错技术被设计用来处理这些异常行为,并满足所要解决的问题的规范要求。

拜占庭容错技术来源于拜占庭将军问题。拜占庭将军问题是Leslie Lamport在20世纪80年代提出的一个假象问题。拜占庭是东罗马帝国的首都,由于时拜占庭罗马帝国国土辽阔,每支军队的驻地分隔很远,将军们只能靠信使传递消息发生战争时,将军们必须制订统一的行动计划。然而,这些将军中有叛徒,叛徒希望通过影响统一行动计划的制定与传播,破坏忠诚的将军们一致的行动计划。因此,将军们必须有一个预定的方法协议,使所有忠诚的将军能够达成一致,而且少数几个叛徒不能使忠诚的将军做出错误的计划。也就是说,拜占庭将军问题的实质就是要寻找一个方法,使得将军们能在一个有叛徒的非信任环境中建立对战斗计划的共识。在分布式系统中,特别是在区块链网络环境中,也和拜占庭将军的环境类似,有运行正常的服务器(类似忠诚的拜占庭将军),有故障的服务器还有破坏者的服务器(类似叛变的拜占庭将军)。共识算法的核心是在正常的节点间形成对网络状态的共识。

简单形容就是:通过在一群数量有限的节点中,使用轮换或者其他算法来筛选出某个节点作为主节点。并且赋予该节点出块的权利。在主节点是将该时段的交易打包成区块后用自己的私钥对该区块签名,并将其广播到所有节点。当主节点收到至少三分之二的不同节点的签名区块后,则该区块完成了所有节点的验证成为不可逆区块串联到区块链中。

BFT与DPOS二者相结合就诞生了BFT—DPOS共识算法。

、为了挖掘 EOS 系统的性能,Daniel Larimer 在以上基础上又进行了修改。首先,他将出块速度由 3 秒 缩短至 0.5 秒,理论上这样可以极大提升系统性能,但带来了网络延迟问题:0.5 秒的确认时间会导致下一个出块者还没有收到上一个出块者的区块,就该生产下一个区块了,那么下一个出块者会忽略上一个区块,导致区块链分叉(相同区块高度有两个区块)。比如:中国见证人后面可能就是美国见证人,中美网络延迟有时高达 300ms,很有可能到时美国见证人没有收到中国见证人的区块时,就该出块了,那么中国见证人的区块就会被略过。

为解决这个问题,Daniel Larimer 将原先的随机出块顺序改为由见证人商议后确定的出块顺序,这样网络连接延迟较低的见证人之间就可以相邻出块。比如:日本的见证人后面是中国的见证人,再后面是俄罗斯的见证人,再后面是英国的见证人,再后面是美国的见证人。这样可以大大降低见证人之间的网络延迟。使得 0.5 秒的出块速度有了理论上的可能。

为了保证万无一失,不让任何一个见证人因为网络延迟的意外而被跳过,Daniel Larimer 让每个见证人连续生产 6 个区块,也就是每个见证人还是负责 3 秒的区块生产,但是由最初的只生产 1 个变成生产 6 个。最恶劣的情况下,6 个区块中,最后一个或两个有可能因为网络延迟或其他意外被下一个见证人略过,但 6 个区块中的前几个会有足够的时间传递给下一个见证人。

再来讨论 BFT-DPoS 的交易确认时间问题:每个区块生产后立即进行全网广播,区块生产者一边等待 0.5 秒生产下一个区块,同时会接收其他见证人对于上一个区块的确认结果。新区块的生产和旧区块确认的接收同时进行。大部分的情况下,交易会在 1 秒之内确认(不可逆)。这其中包括了 0.5 秒的区块生产,和要求其他见证人确认的时间

使用上述BFT-DPoS协议就可以使得EOS的出块间隔从原来的3秒降低到500毫秒,这也使得跨链通信的时延大大缩短,单位时间内可确认的交易数量大大提升。笔者相信如果这样的机制在EOSIO1.0的正式版本中成功实现,那无疑是区块链技术向支持百万级别用户的目标迈出的巨大一步。

引用原文来自:金色财经
转载自:https://www.cxyzjd.com/article/ITleaks/80359033