Jenner's Blog

不变秃,也要变强!

0%

比特币与以太坊

交易

记账

比特币

未花费的交易输出。翻译成大白话就是我花的钱都是别人给我的钱,而且花的时候必须花完。比如我有一个2 BTC(比特币) 的UTXO,我想给你1 BTC,我就必须把这个 2BTC的UTXO 花掉,然后生成两个UTXO,一个 1BTC 的UTXO给你,一个UTXO(小于1 BTC,差值为交易费)给我自己。想看余额怎么办,就查一下这个地址里有多少UTXO,把里面的BTC加起来就是余额。

以太坊

以太坊有两种账户:
外部账户(externally owned accounts),由密钥控制。
合约账户(contract accounts),由智能合约的代码控制。
以太坊的账户包括四个字段:一个随机数、账户的余额、合约代码(如果有的话)、存储(通常为空)。
只有合约账户才有代码,其中存储的是 codeHash(这个账户的以太坊虚拟机代码的哈希值)。这个字段在生成后是不可修改的,这意味着智能合约代码是不可修改的。
外部账户可以触发交易,而合约账户不能主动发起交易,只能在被触发后按预先编写的智能合约代码执行。
合约账户可以设置多重签名(multisign),比如一个简单示例是:现有一个合约账户,它要求一个转账由发起转账的人(Alice)和另一个人(Charles)签名均可。因此,当 Alice 通过这个合约向 Bob 转账 20 个 ETH 时,合约会通知 Charles 签名,在他签名后,Bob 才可以收到这 20 个 ETH。

交易结构

比特币

交易结构:

交易结构

交易输入结构:

交易输入

交易输出结构:

交易输出

以太坊

和比特币一样,以太坊的区块中存储的也是交易(transaction),它包括的字段主要有:接受者(接受的账户)、发送者的签名、发送者发给接受者的以太币金额等。
与比特币的区块中存储的是交易不同,以太坊的区块中以梅克尔帕特里夏树(Merkle Patricia Tree,MPT)形式存储的是相应的以太坊账户的状态。
以太坊的交易是所谓的状态转换函数,一个交易触发它的执行,它将相应的账户从一个状态转变成新状态,然后新状态被存储在区块链的数据区块中。
外部账户可以创建交易,用自己的私钥进行签名之发送消息给另一个外部账户或合约账户。两个外部账户之间传送的消息即为转账操作。从外部账户到合约账户的消息会激活合约账户的代码,执行各种操作,也就是我们常说的调用智能合约。可以通过向0地址发起交易来创建合约账户。交易包含以下主要字段:

  • Type:交易的类型,ContractCreation(创建合约)还是MessageCall(调用合约或转账)
  • Nonce: 发送地址的交易计数
  • Value: 向目标账户发送的金额
  • ReceiveAddress:接受方地址
  • GasPrice:为交易付出的Gas价格
  • Gas:为交易付出的Gas
  • Data:交易的附加数据
  • VRS:交易签名结构体

收据(Receipt)
账户创建交易并向其它节点广播后,会被其它节点执行并放入准备打包的区块。在这个过程中会生成一个收据。收据的主要字段有:

  • blockHash: 交易所在块的哈希值
  • blockNumber: 交易在块的序号
  • transactionHash: 交易的哈希值
  • transactionIndex: 交易在块中的序号
  • cumulativeGasUsed: 执行完此交易时候,块内消耗的总的gas值
  • gasUsed:本交易所消耗的gas
  • contractAddress: 当此交易为创建合约时,表示所创建合约的地址,否则为空
  • logs: 此交易的日志

需要注意的是,在不同的节点之间并不会发送状态和收据,后两者在可以通过交易计算得到。

交易验证

比特币

比特币交易是首先要提供一个用于解锁 UTXO(用私钥去匹配锁定脚本)的脚本(常称为解锁脚本:Signature script),这也叫交易输入,交易的输出则是指向一个脚本(称为锁定脚本:PubKey script),这个脚本表达了:谁的签名(签名是常见形式,并不一定必须是签名)能匹配这个输出地址,钱就支付给谁。

每一个比特币节点会通过同时执行这解锁和锁定脚本(不是当前的锁定脚本,是指上一个交易的锁定脚本)来验证一笔交易,脚本组合结果为真,则为有效交易。

签名流程

1.查找该笔交易对应的UTXO
2.获得该UTXO对应的锁定脚本
3.复制该交易对象,并在复制副本中将该Input的解锁脚本字段的值设置为之前 交易输出部分的锁定脚本的字段
4.清除其他Input的解锁脚本字段
5.对这个改造后的交易对象计算Hash
6.使用私钥对Hash进行签名
7.将签名结果sig和签名人的地址PubK放入input的解锁脚本中
8.构成输入部分

验证流程

解锁脚本先进行入栈,然后锁定脚本入栈计算。
如最为常见类型的比特币交易脚本(支付到公钥哈希:P2PKH(Pay-to-Public-Key-Hash))组合是这样:

脚本

验证解锁脚本中的PubK(验证PubKHash)与sig(公钥验证)与锁定脚本的计算结果是否一致。

以太坊

验证交易的有效性

  • 数据量必须<32KB
  • 交易金额必须非负(>=0)
  • 交易的gas limit必须低于block的gas limit
  • 签名数据必须有效,能够解析出发送者地址
  • 交易的gas price必须高于pool设定的最低gas price(除非是本地交易)
  • 交易的nonce值必须高于当前链上该账户的nonce值(低于则说明这笔交易已经被打包过了)
  • 当前账户余额必须大于“交易金额 + gasprice * gaslimit”
  • 交易的gas limit必须大于对应数据量所需的最低gas水平

验证签名

用消息和签名推导出对方的公钥。再通过公钥,签名,消息的哈希值计算出一个叫 r 的值,这个 r 是签名的一部分,校验签名就是拿计算出来的 r 和签名中携带的 r 经行对比,如果一致就校验通过。

交易验证后,提交到txPool中,还需要广播出去,一方面通知EVM执行该交易,另一方面要把交易信息广播给其他结点。

区块

区块结构

比特币

区块信息
区块结构
区块
区块头
区块头

以太坊

  • ParentHash:父块的哈希值
  • Number:块编号
  • Timestamp:块产生的时间戳
  • GasUsed:交易消耗的Gas
  • GasLimit:Gas限制
  • Difficulty:POW的难度值
  • Beneficiary:块打包手续费的受益人,也称矿工
  • Nonce:一个随机数,使得块头哈希满足POW需求
  • StateRoot:状态树的根哈希值
  • TransactionsRoot:交易树的根哈希值
  • ReceiptsRoot:收据树的根哈希值

每个矿工在把交易打包成块的时候,会组织三颗树:

  • 交易树,树叶里是交易
  • 收据树,树叶里是交易生成的收据
  • 状态树,树叶里是交易影响到(节省空间)的账户状态

三棵树求取根哈希,可以得到区块头中的StateRoot,TransactionsRoot,ReceiptsRoot三个字段。这样就建立了交易和区块头字段的映射。当其他用户收到块,根据块里的交易可以计算出收据和状态,计算三个根哈希后和区块头的三个字段进行验证,判断这是否为合法的块。

参考

比特币脚本及交易分析

点击下方打赏按钮,获得支付宝二维码