区块链技术博客
www.b2bchain.cn

区块链技术对以太坊ambitrum技术架构的深入理解

第20447篇区块链技术文章区块链技术对以太坊ambitrum技术架构的深入理解

简而言之,arbitrum在第二层实现了AVM虚拟机,然后在虚拟机上模拟EVM执行环境

推荐阅读:链新闻选择|阅读以太坊扩展热玩家arbitrum>

原题:L2-深入了解arbitrum
作者:李星

arbitrum是一个第二层汇总方案。与乐观主义类似,国家的最终性是由“挑战”机制来保证的。优化的挑战是在layer1中模拟事务的执行,并判断执行后事务的状态是否正确。该方法需要模拟EVM在第1层的执行环境,比较复杂。氨溴酸的挑战相对较轻。在层1中执行操作(AVM),以确定操作是否正确执行。正如arbitrum的介绍文件中提到的,整个挑战需要大约500字节的数据和大约9W的气体。为了应对这一挑战,arbitrum在AVM虚拟机中实现了AVM虚拟机和EVM执行。AVM虚拟机的优点是底层结构便于状态证明

arbitrum开发文档详细介绍了arbitrum的体系结构和设计。如果您对AVM和L1/L2交互细节感兴趣,可以耐心查看“内部arbitrum”一章

整体框架

深入理解以太坊二层方案 Arbitrum 技术架构

arbitrum系统主要由三部分组成(图中的右侧部分,自下而上):ethbridge、AVM执行环境和Arbos。Ethbridge主要实现收件箱/发件箱管理和汇总协议。Ethbridge在第1层实现,Arbos在AVM虚拟机上执行EVM。总之,arbitrum在第二层实现AVM虚拟机,然后在虚拟机上模拟EVM执行环境。之所以用AVM来模拟EVM,是因为AVM的状态表达得比较好,便于第1层挑战ethbridge和AVM执行环境对应的源代码,这个模块图太笼统了,然后细分为:

深入理解以太坊二层方案 Arbitrum 技术架构

  • ethbridge主要实现收件箱、发件箱和汇总协议三部分功能在收件箱中存储“事务信息”,这些信息将“同步”到Arbos并执行。发件箱中从L2到L1“存储”的事务主要是取款事务。Rollup协议主要是关于L2状态的保存和挑战。特别是,所有的arbitrum事务首先提交给L1,然后由Arbos执行。除了一些外部接口外,Arbos主要实现EVM模拟器。整个模拟器是在AVM上实现的。整个EVM仿真器是用mini语言实现的,arbitrum在AVM上实现了mini语言编译器。简而言之,arbitrum定义了新的硬件(机器)和指令集,实现了一种高级语言Mini。arbitrum通过mini语言实现了EVM模拟器,它可以执行相应的事务,因为所有事务都是在AVM中执行的,所以事务的执行状态可以用AVM状态来表示。AVM的代码是arbitrum/packages/ARB AVM CPP

  • AVM的状态由PC、stack、register和其他状态组成。AVM的状态是这些状态的散列值串联的结果,AVM在C++中实现,AVM表示的逻辑在machinehash函数(machinestate)中实现。机械技术类的。AVM的特点是除了执行外,还可以更方便地表示(证明)执行状态。深入了解AVM的基本数据结构,AVM的基本数据类型有:

uint256ut-整数型

codepoint-当前代码指令表示

元组-元组,由8个值组成。元组中的元素仍然可以是元组缓冲区数组,最大长度为2^64

AVM State

hashpreimage-一个固定的哈希类型,hashvalue=hash(value,prevhashvalue)

除了数据表示之外,每个数据类型都可以很容易地将其哈希值计算为一个状态。详细查看codepoint和tuple基本数据类型

codepoint类型将多个操作“捆绑”在一起。每个码点不仅记录当前操作,还包含前一个码点的哈希信息。这样,所有的操作都可以串联起来,当前的码点不仅可以表示当前的操作,而且可以明确操作的依赖关系。码点的类型定义在:packages/ARB AVM CPP/AVMuvalues/include/AVMuvalues/codepoint.hpp中。

在了解基本类型的基础上,datastack可以由一系列元组实现:

 using value =   std::variant<Tuple, uint256_t, CodePointStub, HashPreImage, Buffer>;   enum ValueTypes { NUM, CODEPT, HASH_PRE_IMAGE, TUPLE, BUFFER = 12, CODE_POINT_STUB = 13 };    
  • 总结一下,PC、stack、,AVM中的寄存器等可以用散列结果表示。AVM的整个状态由这些散列值的级联数据的散列来表示,当提交给L1的状态不同时,两个挑战者(assessor和Challenger)首先对状态进行划分以找到“发散点”。定义发散点后,质询双方可以提供执行环境,L1可以执行相关操作来判断之前提交的状态是否正确。L1的质询处理逻辑在ARB桥eth/contracts/challenge/challenge.sol中实现。整个质询机制由一个超时机制来保证。为了突出核心流程,简化流程,如下图所示:

  • 深入理解以太坊二层方案 Arbitrum 技术架构

  • 挑战者通过InitializeCallenge函数发起挑战。其次,挑战者和评估者通过对分执行来确定不可分割的“分歧点”。在确定分岔点之后,Challenger使用onestepproveexecution函数来确定assert之前提交的状态是否正确initializechallenge标识Challenger和Challenger,并标识需要被质询的状态(存储在challengestate中)。质询状态是由一个或多个二分块状态组成的Merkle树。树的根是:

  • 深入理解以太坊二层方案 Arbitrum 技术架构

  • 整个执行过程可以分为几个小过程,每个对分由起始气体和结束气体表示,状态用来记录交互顺序。Turn=转弯。Challenger表示在Challenger初始化后,Challenger首先启动分叉点分割,bisetexecution在选择前分割片段,如果可能的话,再次分割片段:

bisetexecution的函数定义如下:

CodePoint

bisetexecutionuChainHashes是再次分割点的状态。如果需要再次分割,则需要满足分割点数的要求:

     struct CodePoint {            Operation op;            uint256_t nextHash;             CodePoint(Operation op_, uint256_t nextHash_)                : op(op_), nextHash(nextHash_) {}             bool isError() const {                return nextHash == 0 && op == Operation{static_cast(0)};            }        }; 

Tuple

简而言之,每个分割必须被分割成400个拷贝

     struct RawTuple {            HashPreImage cachedPreImage;            std::vector data;            bool deferredHashing = true;             RawTuple() : cachedPreImage({}, 0), deferredHashing(true) {}        }; 

Oldendhash用于验证状态。这个切分的切分部分是前面的切分部分之一。需要检查分段的有效性:

初始状态正确。这个分割不能超过最后一个分割范围,通过Merkle树的路径检查,最后一个状态与前一个分割的结束状态不同,确定开始状态和结束状态是最后一个分段

更新与细分

深入理解以太坊二层方案 Arbitrum 技术架构

Rollup Challenge

onestepproveexecution

相对应的challengestate

当无法分割时,Challenger提供初始状态(证明),L1进行相应的计算。计算结果应与提供的结果一致uuOldEndHash不一致。不一致性意味着挑战者成功地证明了前面的计算是错误的

正确的结束状态是由executestep计算的。Executestep在packages/ARB bridge eth/contracts/arch/onestepproofcommon.sol中实现。核心是executeop函数,它读取当前上下文的op,执行并更新状态。感兴趣的伙伴可以自己检查

function initializeChallenge(                IOneStepProof[] calldata_executors,                address_resultReceiver,                bytes32_executionHash,                uint256_maxMessageCount,                address_asserter,                address_challenger,                uint256_asserterTimeLeft,                uint256_challengerTimeLeft,                IBridge_bridge    ) external override {                ...               asserter =_asserter;                challenger =_challenger;                ...                turn = Turn.Challenger;                challengeState =_executionHash;                ...       } 

确定初始状态和结束状态是最后一个质询状态的某些部分。初始状态通过证明计算,前提是确认Oldendhash与计算结束状态不同。差异表明上一次提交的结束状态是错误的

仲裁是第二层汇总方案。质询机制用于确定汇总状态的最终性。为了引入光质询机制,arbitrum定义了一个可以方便地证明执行状态的虚拟机AVM,并设计了Mini语言和编译器。在与EVM兼容的AVM上模拟了EVM的执行环境。当被质询时,执行过程被分成400个部分,并且L1执行少量指令以确定状态是否正确

整个执行过程可以分割成多个小过程,每个小过程 (bisection) 由起始和结束的 gas 和状态来表示。

turn 用来记录交互顺序。turn = Turn.Challenger 表明在初始化挑战后,首先由 Challenger 发起分歧点分割。

bisectExecution

bisectExecution 挑选之前分割片段,并如可能将片段进行再次分割:

bisectExecution 的函数定义如下:

function bisectExecution(                bytes32[] calldata_merkleNodes,                                                                            uint256_merkleRoute,                                                                                       uint256_challengedSegmentStart,                                                                            uint256_challengedSegmentLength,                                                                           bytes32_oldEndHash,                uint256_gasUsedBefore,                bytes32_assertionRest,                                                                                     bytes32[] calldata_chainHashes                                                                        ) external onlyOnTurn { 

_chainHashes 是再次分割点的状态。如果需要再次分割,需要满足分割点的个数规定:

uint256 private constant EXECUTION_BISECTION_DEGREE = 400;          require(             _chainHashes.length ==                                                                                    bisectionDegree(_challengedSegmentLength, EXECUTION_BISECTION_DEGREE) + 1,                        "CUT_COUNT"  ); 

简单的说,每次分割,必须分割成 400 份。

_oldEndHash 是用来验证状态这次分割的分割片段是上一次分割中的某个。需要检查分割的有效性:

 require(_chainHashes[_chainHashes.length - 1] !=_oldEndHash, "SAME_END");                             require(             _chainHashes[0] == ChallengeLib.assertionHash(_gasUsedBefore,_assertionRest),                        "segment pre-fields"                                                                              );            require(_chainHashes[0] != UNREACHABLE_ASSERTION, "UNREACHABLE_START");                                require(             _gasUsedBefore <_challengedSegmentStart.add(_challengedSegmentLength),                               "invalid segment length"                                                                          );   

起始状态正确。这次分割不能超出上次分割范围,并且最后一个状态和上一个分割的结束状态不一样。

 bytes32 bisectionHash =                                                                                   ChallengeLib.bisectionChunkHash(                                                                         _challengedSegmentStart,                                                                             _challengedSegmentLength,                 _chainHashes[0],                 _oldEndHash              );          verifySegmentProof(bisectionHash,_merkleNodes,_merkleRoute); 

通过 merkle 树的路径检查确定起始状态和结束状态是上一次某个分割。

updateBisectionRoot(_chainHashes,_challengedSegmentStart,_challengedSegmentLength); 

更新细分分割对应的 challengeState。

深入理解以太坊二层方案 Arbitrum 技术架构

oneStepProveExecution

当不能分割后,挑战者提供初始状态(证明),并由 L1 进行相应的计算。计算的结果应该和提供的 _oldEndHash 不一致。不一致说明挑战者成功证明了之前的计算结果不对。

(uint64 gasUsed, uint256 totalMessagesRead, bytes32[4] memory proofFields) = executors[prover].executeStep(          bridge,          _initialMessagesRead,           [_initialSendAcc,_initialLogAcc],          _executionProof,          _bufferProof ); 

通过 executeStep 计算出正确的结束状态。executeStep 实现在 packages/arb-bridge-eth/contracts/arch/OneStepProofCommon.sol 中。核心是 executeOp 函数,针对当前的 context 读取 op,执行并更新状态。感兴趣的小伙伴可以自行查看。

rootHash = ChallengeLib.bisectionChunkHash(              _challengedSegmentStart,              _challengedSegmentLength,               oneStepProofExecutionBefore(                          _initialMessagesRead,                          _initialSendAcc,                          _initialLogAcc,                          _initialState,                            proofFields                        ),                  _oldEndHash                    );                }    verifySegmentProof(rootHash,_merkleNodes,_merkleRoute); 

确定初始状态和结束状态是上一次挑战状态中的某个分割。初始状态由提供的证明(proof)计算获得。

require(     _oldEndHash !=                  oneStepProofExecutionAfter(                              _initialSendAcc,                              _initialLogAcc,                              _initialState,                                gasUsed,                                totalMessagesRead,                                proofFields                   ),             "WRONG_END"           ); 

确认 _oldEndHash 和计算获得结束状态不一样。不一样才说明之前提交的结束状态是错误的。

_currentWin(); 

计算完成后,确定胜利方。

总结

Arbitrum 是 Layer2 Rollup 的一种方案。采用挑战机制确定 Rollup 状态的终局性。为了引入轻便挑战机制,Arbitrum 定义了 AVM,一种可以方便证明执行状态的虚拟机,并设计了 mini 语言和编译器。在 AVM 上模拟了 EVM 的执行环境,兼容 EVM。挑战时将执行过程进行 400 分分割,由 L1 执行少量指令确定状态是否正确。

区块链技术对以太坊ambitrum技术架构的深入理解 由www.b2bchain.cn 提供
文章整理自网络,只为个人学习与分享使用
链接地址https://www.b2bchain.cn/20447.html

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 区块链技术对以太坊ambitrum技术架构的深入理解
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们