Cosmos Transaction 生命周期

数与科技 · · 875 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

引言

区块链是由人类杰出科技成果组合而成的全新架构,对于生产关系的变革有巨大的潜力。数与科技团队致力于在实践中探索更加开放的加密经济基础设施,我们将逐步拆解主流区块链的底层技术细节,并且尝试将碎片化的区块链知识整理成树状知识图谱分享给社区,也欢迎有兴趣的朋友一起加入。

Cosmos是一套成熟且高可用区块链开发框架,拥有模块化设计、基于Golang开发、支持跨链协议等特点,是目前比较主流的区块链开发框架。通过Cosmos SDK,开发者可以快速构建出属于自己的区块链,高效地进行一系列个性化开发。

本文为Cosmos文档翻译系列第五篇,点击查看往期内容剖析 Cosmos SDK 应用程序,Part 1:全节点与核心文件》

创建

Transaction创建

命令行界面是主要的应用程序界面之一,Tx可以由用户输入以下命令来创建,其中[command]是Tx的类型,[args]是相关参数,[flags]是相关配置例如gas price:

此命令将自动创建Tx,使用帐户的私钥对其进行签名,并将其广播到其他节点。

创建Tx有一些必需的和可选的参数,其中--from指定该Tx的发起账户,例如一个发送代币的Tx,则将从 from指定的账户提取资产。

Gas 和 Fees

此外,用户可以使用这几个参数来表明他们愿意支付多少fee:

--gas 指的是gas的数量,gas代表Tx消耗的计算资源,需要消耗多少gas取决于具体的Tx,在Tx执行之前无法被精确计算出来,但可以通过在--gas后带上参数auto来进行估算。

--gas-adjustment(可选)可用于适当的增加 gas ,以避免其被低估。例如,用户可以将gas-adjustment设为1.5,那么被指定的gas将是被估算gas的1.5倍。

--gas-prices 指定用户愿意为每单位gas支付多少fee,可以是一种或多种代币。例如,--gas-prices=0.025uatom, 0.025upho就表明用户愿意为每单位的gas支付0.025uatom 和 0.025upho。

--fees 指定用户总共愿意支付的fee。

所支付fee的最终价值等于gas的数量乘以gas的价格。换句话说,fees = ceil(gas * gasPrices)。由于可以使用gas价格来计算fee,也可以使用fee来计算gas价格,因此用户仅指定两者之一即可。

随后,验证者通过将给定的或计算出的gas-prices与他们本地的min-gas-prices进行比较,来决定是否在其区块中写入该Tx。如果gas-prices不够高,该Tx将被拒绝,因此鼓励用户支付更多fee。

CLI示例

应用程序的用户可以在其CLI中输入以下命令,用来生成一个将1000uatom从senderAddress发送到 recipientAddress的Tx,该命令指定了用户愿意支付的gas(其中gas数量为自动估算的1.5倍,每单位gas价格为0.025uatom)。

appcli tx send <recipientAddress> 1000uatom --from <senderAddress> --gas auto --gas-adjustment 1.5 --gas-prices 0.025uatom

其他的Transaction创建方法

命令行是与应用程序进行交互的一种简便方法,但是Tx也可以使用REST interface或应用程序开发人员定义的某些其他入口点来创建命令行。从用户的角度来看,交互方式取决于他们正在使用的是页面还是钱包(例如,Tx使用Lunie.io 创建并使用Ledger Nano S对其进行签名)。

添加到交易池

每个全节点(Tendermint节点)接收到Tx后都会发送一个名为CheckTx的ABCI message,用来检查Tx的有效性,CheckTx会返回abci.ResponseCheckTx。 如果Tx通过检查,则将其保留在节点的 交易池(每个节点唯一的内存事务池)中等待出块,Tx如果被发现无效,诚实的节点将丢弃该Tx。在达成共识之前,节点会不断检查传入的Tx并将其广播出去。

创建

全节点在CheckTx期间对Tx先执行无状态检查,然后进行有状态检查,目的是尽早识别并拒绝无效Tx,以免浪费计算资源。

无状态检查不需要知道节点的状态,即轻客户端或脱机节点都可以检查,因此计算开销较小。无状态检查包括确保地址不为空、强制使用非负数、以及定义中指定的其他逻辑。

状态检查根据提交的状态验证Tx和Message。例如,检查相关值是否存在并能够进行交易,账户是否有足够的资产,发送方是否被授权或拥有正确的交易所有权。在任何时刻,由于不同的原因,全节点通常具有应用程序内部状态的多种版本。例如,节点将在验证Tx的过程中执行状态更改,但仍需要最后的提交状态才能响应请求,节点不能使用未提交的状态更改来响应请求。

为了验证Tx,全节点调用的CheckTx包括无状态检查和有状态检查,进一步的验证将在DeliverTx阶段的后期进行。其中CheckTx从对Tx进行解码开始。

解码

当Tx从应用程序底层的共识引擎(如Tendermint)被接收时,其仍处于[]byte编码形式,需要将其解码才能进行操作。随后,runTx 函数会被调用,并以runTxModeCheck模式运行,这意味着该函数将运行所有检查,但是会在执行Message和写入状态更改之前退出。

ValidateBasic

Message是由module的开发者实现的Msg接口中的一个方法。它应包括基本的无状态完整性检查。例如,如果Message是要将代币从一个账户发送到另一个账户,则ValidateBasic会检查账户是否存在,并确认账户中代币金额为正,但不需要了解状态,例如帐户余额。

AnteHandler

AnteHandler是可选的,但每个应用程序都需要定义。AnteHandler使用副本为特定的Tx执行有限的检查,副本可以使对Tx进行状态检查时无需修改最后的提交状态,如果执行失败,还可以还原为原始状态。

例如, auth模块的AnteHandler检查并增加序列号,检查签名和帐号,并从Tx的第一个签名者中扣除费用,这个过程中所有状态更改都使用checkState

Gas

Context相当于GasMeter,会计算出在Tx的执行过程中多少gas已被使用。用户提供的Tx所需的gas数量称为GasWanted。Tx在实际执行过程中消耗的gas被称为GasConsumed,如果GasConsumed超过GasWanted,将停止执行,并且对状态副本的修改不会被提交。否则,CheckTx设置GasUsed等于GasConsumed并返回结果。在计算完gas和fee后,验证器节点检查用户指定的值gas-prices是否小于其本地定义的值min-gas-prices。

丢弃或添加到交易池

如果在CheckTx期间有任何失败,Tx将被丢弃,并且Tx的生命周期结束。如果CheckTx成功,则Tx将被广播到其他节点,并会被添加到交易池,以便成为待出区块中的候选Tx。

交易池保存所有全节点可见的Tx,全节点会将其最近的Tx保留在交易池缓存中,作为防止重放攻击的第一道防线。理想情况下,mempool.cache_size的大小足以容纳整个交易池中的所有Tx。如果交易池缓存太小而无法跟踪所有Tx,CheckTx会识别出并拒绝重放的Tx。

现有的预防措施包括fee和序列号 计数器,用来区分重放Tx和相同的Tx。如果攻击者尝试向某个节点发送多个相同的Tx,则保留交易池缓存的完整节点将拒绝相同的Tx,而不是在所有Tx上运行CheckTx 。如果Tx有不同的序列号,攻击者会因为需要支付费用而取消攻击。

验证器节点与全节点一样,保留一个交易池以防止重放攻击,但它也用作出块过程中未经验证的交易池。请注意,即使Tx在此阶段通过了所有检查,仍然可能会被发现无效,因为CheckTx没有完全验证Tx(CheckTx实际上并未执行message)。

写入区块

共识是验证者节点就接受哪些Tx达成协议的过程,它是反复进行的。每个回合都始于出块节点创建一个包含最近Tx的区块,并由验证者节点(具有投票权的特殊全节点)负责达成共识,同意接受该区块或出一个空块。验证者节点执行共识算法,例如Tendermint BFT,调用ABCI请求确认Tx,从而达成共识。

达成共识的第一步是区块提案,共识算法从验证者节点中选择一个出块节点来创建和提议一个区块,用来写入Tx,Tx必须在该提议者的交易池中。

状态变更

共识的下一步是执行Tx以完全验证它们,所有的全节点收到出块节点广播的区块并调用ABCI函数BeginBlock,DeliverTx,和EndBlock。全节点在本地运行的每个过程将产生一个明确的结果,因为message的状态转换是确定性的,并且Tx在提案中有明确的顺序。

DeliverTx

baseapp中定义的ABCI函数DeliverTx会执行大部分状态转换,DeliverTx会针对共识中确定的顺序,对块中的每个Tx按顺序运行。DeliverTx几乎和CheckTx相同,但是会以deliver模式调用runTx函数而不是check模式。全节点不使用checkState,而是使用deliverState。

解码:由于DeliverTx是通过ABCI调用的,因此Tx会以[]byte的形式被接收。节点首先会对Tx进行解码,然后在runTxModeDeliver中调用runTx,runTx除了会执行CheckTx中的检查外,还会执行Tx和并写入状态的变化。

检查: 全节点会再次调用validateBasicMsgs和AnteHandler。之所以进行第二次检查,是因为在Tx进交易池的过程中,可能没有相同的Tx,但恶意出块节点的区块可能包括无效Tx。但是这次检查特殊的地方在于, AnteHandler不会将gas-prices与节点的min-gas-prices比较,因为每个节点的min-gas-prices可能都不同,这样比较的话可能会产生不确定的结果。

路由和Handler: CheckTx退出后,DeliverTx会继续运行 runMsgs 来执行Tx中的每个Msg。由于Tx可能具有来自不同模块的message,因此baseapp需要知道哪个模块可以找到适当的Handler。因此,路由通过模块管理器来检索路由名称并找到对应的Handler。

Handler:handler是用来执行Tx中的每个message,并且使状态转换到从而保持deliverTxState。handler在Msg的模块中定义,并写入模块中的适当存储区。

Gas:在Tx被传递的过程中,GasMeter是用来记录有多少gas被使用,如果执行完成,GasUsed会被赋值并返回abci.ResponseDeliverTx。如果由于BlockGasMeter 或者 GasMeter 耗尽或其他原因导致执行中断,程序则会报出相应的错误。

如果由于Tx无效或GasMeter用尽而导致任何状态更改失败,Tx的处理将被终止,并且所有状态更改都将还原。区块提案中无效的Tx会导致验证者节点拒绝该区块并投票给空块。

提交

最后一步是让节点提交区块和状态更改,在重跑了区块中所有的Tx之后,验证者节点会验证区块的签名以最终确认它。不是验证者节点的全节点不参与共识(即无法投票),而是接受投票信息以了解是否应提交状态更改。

当收到足够的验证者票数(2/3+的加权票数)时,完整的节点将提交一个新的区块,以添加到区块链网络中并最终确定应用程序层中的状态转换。此过程会生成一个新的状态根,用作状态转换的默克尔证明。应用程序使用从Baseapp继承的ABCI方法Commit,Commit通过将deliverState写入应用程序的内部状态来同步所有的状态转换。提交状态更改后,checkState从最近提交的状态重新开始,并将deliverState重置为空以保持一致并反映更改。

请注意,并非所有区块都具有相同数量的Tx,并且共识可能会导致一个空块。在公共区块链网络中,验证者可能是拜占庭恶意的,这可能会阻止将Tx提交到区块链中。可能的恶意行为包括出块节点将某个Tx排除在区块链之外,或者投票反对某个出块节点。

至此,Tx的生命周期结束,节点已验证其有效性,并提交了这些更改。Tx本身,以[]byte的形式被存储在区块上进入了区块链网络。

原文链接:

https://docs.cosmos.network/master/

作者:cosmos.network

你可能还喜欢:

Cosmos SDK 介绍(下)

剖析 Cosmos SDK,Part 1:全节点与核心文件

剖析 Cosmos SDK,Part 2:模块与接口

关于我们:

杭州数与科技于2017年在中国杭州成立,由来自EOSIO、COSMOS等开源社区的核心开发者组成。我们致力于在实践中探索主流区块链底层技术,建设开放的信任经济基础设施、消除信任建立成本。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:数与科技

查看原文:Cosmos Transaction 生命周期

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

875 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传