区块链研究区块链研习社区块链大学

《锋哥论道区块链》之五区块链2.0之以太坊--交易

2019-05-01  本文已影响7人  7dfc697cf7a9

1.以太坊交易结构详解
交易基本定义:【从外部拥有账户】发送的加密签名序列化指令。换句话说交易必须是从EOA发起的才能叫交易,CA之间的通信叫消息也有叫内部交易的,现在是有区别,以后这个区别会不会模糊化不知道。交易类型有两种:
消息调用和合约创建(也就是交易产生一个新的以太坊合约)。从EOA到EOA的交易仅是转账。EOA到CA可以激活各种操作。
从交易的结构,可知一个交易包含以下几个部分:
(1)AccountNonce(nonce)
账户随机数。它是以太坊中很小但也很重要的一个细节。以太坊为每个账户和交易都创建了一个Nonce,当从账户发起交易的时候,当前账户的Nonce值就被作为交易的Nonce。这里,如果是普通账户那么Nonce就是它发出的交易数,如果是合约账户就是从它的创建合约数。nonce是一个从零开始的计数器,这意味着第一个交易具有nonce 0.在检索我们的示例地址的交易计数时,我们的交易计数为40,这意味着已经看到了nonce 0到39。下一笔交易的nonce将是40。
为什么要使用这个Nonce呢?其主要目的就是为了防止重复攻击(Replay Attack)。因为交易都是需要签名的,假定没有Nonce,那么只要交易数据和发起人是确定的,签名就一定是相同的,这样攻击者就能在收到一个交易数据后,重新生成一个完全相同的交易并再次提交,比如A给B发了个交易,因为交易是有签名的,B虽然不能改动这个交易数据,但只要反复提交一模一样的交易数据,就能把A账户的所有资金都转到B手里。当使用账户Nonce之后,每次发起一个交易,A账户的Nonce值就会增加,当B重新提交时,因为Nonce对不上了,交易就会被拒绝。这样就可以防止重复攻击。
(2)Price(gasprice)
发送者支付执行交易所需的每个gas的Wei数量。单位Gas的价格,所谓Gas就是交易的消耗,Price就是单位Gas要消耗多少以太币(Ether)。
(3)GasLimit(gas)
发送者愿意为执行交易支付gas数量的最大值。这个数量被设置之后在任何计算完成之前就会被提前扣掉
(4)recipient(to)
交易的接收者
(5)Amount(value)
从交易的发送者到接收者发送的以太币数量
(6)Payload(input)
如果交易类型是消息调用则Palload写为Td,表示输入数据,例如消息的参数,假设有一个注册域名的合约服务,则Td就是该服务需要的参数如IP等。如果交易类型是创建合约,则Payload写为Ti,表示一段代码,这段代码用于创建合约账户,这段初始化代码只会被执行一次就丢弃掉,第二次执行的是创建完的合约代码体。可以看到当接收账户不同时,区别仅仅是Td和Ti的区别。
(7)R/S/V
V、R、S是交易的签名数据(发送方的数字签名)。太坊当中,交易经过数字签名之后,生成的signature是一个长度65的字节数组,它被截成三段,前32字节被放进R,再32字节放进S,最后1个字节放进V。那么为什么要被截成3段呢?以太坊用的是ECDSA算法,R和S就是ECSDA签名输出,V则是Recovery ID。

(7)init(只有在合约创建交易中存在)
用来初始化新合约账户的EVM代码片段。init值会执行一次,然后就会被丢弃。当init第一次执行的时候,它返回一个账户代码体,也就是永久与合约账户关联的一段代码。
(9)data(可选域,只有在消息通信中存在)
消息通话中的输入数据(也就是参数)。例如,如果智能合约就是一个域名注册服务,那么调用合约可能就会期待输入域例如域名和IP地址
2.交易与账户
在“账户”这个章节中我们了解到:交易—消息通信和合约创建交易两者都总是被外部拥有账户触发并提交到区块链的。换种思维思考就是,交易是外部世界和以太坊内部状态的桥梁。
但是这也并不代表一个合约与另一个合约无法通信。在以太坊状态全局范围内的合约可以与在相同范围内的合约进行通信。他们是通过“消息”或者“内部交易”进行通信的。我们可以认为消息或内部交易类似于交易,不过与交易有着最大的不同点—它们不是由外部拥有账户产生的。相反,他们是被合约产生的。它们是虚拟对象,与交易不同,没有被序列化而且只存在与以太坊执行环境。
当一个合约发送一个内部交易给另一个合约,存在于接收者合约账户相关联的代码就会被执行。
一个重要需要注意的事情是内部交易或者消息不包含gasLimit。因为gas limit是由原始交易的外部创建者决定的(也就是外部拥有账户)。外部拥有账户设置的gas limit必须要高到足够将交易完成,包括由于此交易而长生的任何”子执行”,例如合约到合约的消息。如果,在一个交易或者信息链中,其中一个消息执行使gas已不足,那么这个消息的执行会被还原,包括任何被此执行触发的子消息。不过,父执行没必要被还原。
3.交易执行
3.1交易执行的整体概述
我们已经到了以太坊协议最复杂的部分:交易的执行。假设你发送了一笔交易给以太坊网络处理,将以太坊状态转换成包含你的交易这个过程到底发生了什么?
首先,为了可以被执行所有的交易必须都要符合最基础的一系列要求,包括:
1)交易必须是正确格式化的RLP。”RLP”代表Recursive Length Prefix,它是一种数据格式,用来编码二进制数据嵌套数组。以太坊就是使用RLP格式序列化对象。
2)有效的交易签名。
3)有效的交易序号。回忆一下账户中的nonce就是从此账户发送出去交易的计数。如果有效,那么交易序号一定等于发送账户中的nonce。
4)交易的gas limit 一定要等于或者大于交易使用的intrinsic gas,intrinsic gas包括:
》执行交易预订费用为21,000gas
》 随交易发送的数据的gas费用(每字节数据或代码为0的费用为4gas,每个非零字节的数据或代码费用为68gas)
》如果交易是合约创建交易,还需要额外的32,000gas
5)发送账户余额必须有足够的Ether来支付”前期”gas费用。前期gas费用的计算比较简单:首先,交易的gas limit乘以交易的gas价格得到最大的gas费用。然后,这个最大gas费用被加到从发送方传送给接收方的总值。
如果交易符合上面所说的所有要求,那么我们进行下面步骤:
第一步,我们从发送者的余额中扣除执行的前期费用,并为当前交易将发送者账户中的nonce增加1(注假设当前交易之前账户的nonce为0,此时为1,交易中的nonce为0)。此时,我们可以计算剩余的gas,将交易的总gas减去使用的intrinsic gas。
第二步,开始执行交易。在交易执行的整个过程中,以太坊保持跟踪“子状态”。子状态是记录在交易中生成的信息的一种方式,当交易完成时会立即需要这些信息。具体来说,它包含:
(1)自毁集:在交易完成之后会被丢弃的账户集(如果存在的话)
(2)日志系列:虚拟机的代码执行的归档和可检索的检查点
(3)退款余额:交易完成之后需要退还给发送账户的总额。回忆一下我们之前提到的以太坊中的存储需要付费,发送者要是清理了内存就会有退款。以太坊使用退款计数进行跟踪退款余额。退款计数从0开始并且每当合约删除了一些存储中的东西都会进行增加。
第三步,交易所需的各种计算开始被处理。
当交易所需的步骤全部处理完成,并假设没有无效状态,通过确定退还给发送者的未使用的gas量,最终的状态也被确定。除了未使用的gas,发送者还会得到上面所说的“退款余额”中退还的一些津贴。一旦发送者得到退款之后:
(1)gas的Ether就会矿工
(2)交易使用的gas会被添加到区块的gas计数中(计数一直记录当前区块中所有交易使用的gas总量,这对于验证区块时是非常有用的)
(3)所有在自毁集中的账户(如果存在的话)都会被删除
最后,我们就有了一个新的状态以及交易创建的一系列日志。
现在我们已经介绍了交易执行的基本知识,让我们再看看合约创建交易和消息通信的一些区别。
3.2合约创建
回忆一下在以太坊中,有两种账户类型:合约账户和外部拥有账户。当我们说一个交易是“合约创建”,是指交易的目的是创建一个新的合约账户。
为了创建一个新的合约账户,我们使用一个特殊的公式来声明新账户的地址。然后我们使用下面的方法来初始化一个账户:
(1)设置nonce为0
(2)如果发送者通过交易发送了一定量的Ether作为value,那么设置账户的余额为value
(3)将存储设置为0
(4)设置合约的codeHash为一个空字符串的Hash值
一旦我们完成了账户的初始化,使用交易发送过来的init code(查看”交易和信息”章节来复习一下init code),实际上就创造了一个账户。init code的执行过程是各种各样的。取决于合约的构造器,可能是更新账户的存储,也可能是创建另一个合约账户,或者发起另一个消息通信等等。
当初始化合约的代码被执行之后,会使用gas。交易不允许使用的gas超过剩余gas。如果它使用的gas超过剩余gas,那么就会发生gas不足异(OOG)常并退出。如果一个交易由于gas不足异常而退出,那么状态会立刻恢复到交易前的一个点。发送者也不会获得在gas用完之前所花费的gas。
不过,如果发送者随着交易发送了Ether,即使合约创建失败Ether也会被退回来。
如果初始化代码成功的执行完成,最后的合约创建的花费会被支付。这些是存储成本,与创建的合约代码大小成正比(再一次,没有免费的午餐)。如果没有足够的剩余gas来支付最后的花费,那么交易就会再次宣布gas不足异常并中断退出。
如果所有的都正常进行没有任何异常出现,那么任何剩余的未使用gas都会被退回给原始的交易发送者,现在改变的状态才被允许永久保存。
3.3消息通信(Message calls)
消息通信的执行与合约创建比较类似,只不过有一点点区别。
由于没有新账户被创建,所以消息通信的执行不包含任何的init code。不过,它可以包含输入数据,如果交易发送者提供了此数据的话。一旦执行,消息通信同样会有一个额外的组件来包含输出数据,如果后续执行需要此数据的话就组件就会被使用。
就像合约创建一样,如果消息通信执行退出是因为gas不足或交易无效(例如栈溢出,无效跳转目的地或无效指令),那么已使用的gas是不会被退回给原始触发者的。相反,所有剩余的未使用gas也会被消耗掉,并且状态会被立刻重置为余额转移之前的那个点。
没有任何方法停止或恢复交易的执行而不让系统消耗你提供的所有gas,直到最新的以太坊更新。例如,假设你编写了一个合约,当调用者没有授权来执行这些交易的时候抛出一个错误。在以太坊的前一个版本中,剩余的gas也会被消耗掉,并且没有任何gas退回给发送者。但是拜占庭更新包括了一个新的“恢复”代码,允许合约停止执行并且恢复状态改变而不消耗剩余的gas,此代码还拥有返回交易失败原因的能力。如果一个交易是由于恢复而退出,那么未使用的gas就会被返回给发送者。

上一篇下一篇

猜你喜欢

热点阅读