【区块链开发指南】区块链基础之区块和交易

在区块链网络当中,所有的数据都以区块的形式记录在各个节点上。而每个区块又以单独的文件保存在节点本地磁盘上,在比特币(Linux系统)中所有的区块信息都保存在~/.bitcoin/blocks/目录下面,并以blk***.dat文件名标示,如下图所示:

【区块链开发指南】区块链基础之区块和交易_第1张图片

区块结构

根据https://en.bitcoin.it/wiki/Block所描述的,区块的结构如下:

Field 描述 大小
Magic no ”魔法数“,常数0xD9B4BEF9 4 bytes
Blocksize 区块大小 4 bytes
Blockheader 区块头 80 bytes
Transaction counter 交易数量,正整数 1 - 9 bytes
transactions 交易列表 -many transactions

首先是一个“魔法数”,根据描述这是个常数占4个字节,然后是4个字节的区块大小,然后是区块头80字节,然后是1-9字节的交易数量,最后是所有的交易。但是在实际的比特币代码当中却并不是这么定义的,

class CBlock : public CBlockHeader
{
public:
    // network and disk
    std::vector vtx;

    // memory only
    mutable bool fChecked;

    // ...

    CBlockHeader GetBlockHeader() const
    {
        CBlockHeader block;
        block.nVersion       = nVersion;
        block.hashPrevBlock  = hashPrevBlock;
        block.hashMerkleRoot = hashMerkleRoot;
        block.nTime          = nTime;
        block.nBits          = nBits;
        block.nNonce         = nNonce;
        return block;
    }

    std::string ToString() const;
};

可以发现,实际上只定义了区块头和所有的交易,或许是不同版本的差异,但是看github上过去版本的bitcoin代码中也都没有定义上述表格中的变量,所以个人认为实际的结构应该是代码中定义的。

再来看看区块头部的结构,https://en.bitcoin.it/wiki/Block_hashing_algorithm

Field 目的 更新时间 大小 (Bytes)
Version 区块版本号 升级软件并指定新版本号时 4
hashPrevBlock 前一个区块的256-bit 哈希值 产生新的区块 32
hashMerkleRoot Merkle树根的256-bit 哈希值 收到了新的交易 32
Time 从 1970-01-01 00:00 UTC到现在为止的时间间隔,单位为秒 每几秒 4
Bits 当前POW的目标哈希值的压缩形式 难度调整时 4
Nonce 32-bit 随机数 尝试新的hash时 4

每个区块头都包含前一个区块的哈希值,所以所有的区块就像链表一样连成了一条链,链的头部就是创世块(Genesis Block)。所有的交易都以Merkle tree的形式进行索引,并在block header中保存Merkle tree的树根,如果当前交易数量是奇数的话,那么最后一个交易将会被计算两次哈希值。当前POW的难度以压缩的形式保存在4个字节的Bits中,最后通过mining找到的随机数记录在最后的Nonce中。

交易结构

关于比特币交易,更好的参考是比特币官方文档https://en.bitcoin.it/wiki/Transaction,解释的很详细。

交易是在区块链网络中传输的最基本的数据结构,所有有效的交易最终都会被打包进区块中并保存在区块链上,比特币中交易的数据结构如下,

Field 描述 大小
Version no 版本号,当前为1 4 bytes
In-counter 输入交易数量,正整数 1 - 9 bytes
list of inputs 输入列表,每个区块中第一个交易被称为“Coinbase” -many inputs
Out-counter 输出交易数量,正整数 1 - 9 bytes
list of outputs 输出列表,每个区块中第一个输出交易是给矿工的奖励 -many outputs
lock_time 锁定时间,如果非0并且小于0xFFFFFFFF,那么就是指块序号;如果交易已经终结,那么就是指时间戳 4 bytes

一个简单的交易输入如下:

Input:
Previous tx: f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6
Index: 0
scriptSig: 304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d10
90db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501

首先是前一笔交易的哈希值(Previous tx),然后是花费的是第几个输出(Index),最后是解锁脚本(scriptSig),其中解锁脚本=签名+公钥,只有提供正确的解锁脚本,才能花费对应的交易。

一个简单的输出如下:

Output:
Value: 5000000000
scriptPubKey: OP_DUP OP_HASH160 404371705fa9bd789a2fcd52d2c580b65d35549d
OP_EQUALVERIFY OP_CHECKSIG

首先是输出金额(Value),然后是锁定脚本,锁定脚本包括脚本系统中的一系列操作符。

交易类型

比特币目前提供了两种不同的交易类型,如下所示。通过这两者类型的交易可以组合出更加复杂的交易,称之为合约。

(1)Pay-to-PubkeyHash

scriptPubKey: OP_DUP OP_HASH160  OP_EQUALVERIFY OP_CHECKSIG
scriptSig:  

这个也是最常见的交易,目标地址就是比特币地址,花费时需要提供签名和公钥。

(2)Pay-to-Script-Hash(P2SH)

scriptPubKey: OP_HASH160  OP_EQUAL 
scriptSig: ..signatures... 

在P2SH中,目标地址由脚本哈希取代,解锁脚本中才包含签名和脚本内容。下面有两种类型的交易的对比,首先是普通的多签名脚本,解锁脚本中只需要5个公钥中任意两个私钥的签名即可。

Locking Script 2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG
Unlocking Script Sig1 Sig2

然后是P2SH脚本,

Redeem Script 2 PubKey1 PubKey2 PubKey3 PubKey4 PubKey5 5 OP_CHECKMULTISIG
Locking Script OP_HASH160 <20-byte hash of redeem script> OP_EQUAL
Unlocking Script Sig1 Sig2 redeem script

可以看出锁定脚本中只需要包括Redeem Script的哈希即可,而不用包含长长的一串公钥信息,具体的脚本内容由解锁脚本提供,相当于是将交易的大部分内容交给了交易花费者去处理,从而减少了网络传输的数据。根据《Master Bitcoin》所述,与直接使⽤复杂脚本以锁定输出的⽅式相⽐,P2SH具有以下特点:

  • 在交易输出中,复杂脚本由简短电⼦指纹取代,使得交易代码变短。
  • 脚本能被编译为地址,⽀付指令的发出者和⽀付者的⽐特币钱包不需要复杂⼯序就可以执⾏P2SH。
  • P2SH将构建脚本的重担转移⾄接收⽅,⽽⾮发送⽅。
  • P2SH将⻓脚本数据存储的负担从输出⽅(存储于UTXO集,影响内存)转移⾄输⼊⽅(仅存储于区块链)。
  • P2SH将⻓脚本数据存储的重担从当前(⽀付时)转移⾄未来(花费时)。
  • P2SH将⻓脚本的交易费成本从发送⽅转移⾄接收⽅,接收⽅在使⽤该笔资⾦时必须含有赎回脚本。

你可能感兴趣的:(区块链开发指南)