比特币核心数据结构

1,769 阅读5分钟

我们学习计算机时曾经有这么一个定义:程序=数据结构+算法,对于一个区块链,我认为从技术方面看与程序的定义类似,核心一个是共识算法,一个是核心数据结构,这两点直接决定了这条区块链工作运行原理。比特币的共识算法,在这一篇《哈希函数与比特币共识算法PoW》中已经讲述了其原理,这一篇主要讲述比特币核心数据结构这一部分。主要包括如下内容:

  • Blockchain(区块链)
  • Block(区块)
  • Block Header(区块头部)
  • Merkle Tree(默克尔树)
  • Transaction(交易)

Blockchain

区块链很难用一个什么定义来描述,如果一定要描述的话,借用以太坊黄皮书中的解释:区块链就是一个具有共享状态的密码性安全交易的单机(cryptographically secure transactional singleton machine with shared-state)。这里按数据结构的层面来描述区块链,就是用哈希指针将一些列区块串联起来形成的一条数据链。在比特币区块链中,是有区块这个概念的,现在部分区块链中已经没有区块这个概念了,感兴趣的可以关注一下DAG。

Block

上面讲到区块链是由一个个区块构成的,哪区块是怎么定义的?由区块头部和交易两部分构成:

  • 区块头部: a data structure containing the block's metadata
  • 交易列表: an array (vector in rust) of transactions
    image

C++版本实现定义:

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

    //...省略部分代码...
}

Rust版本实现定义,可以看到基本都是一样的:

#[derive(Debug, PartialEq, Clone, Serializable, Deserializable)]
pub struct Block {
	pub block_header: BlockHeader,          // 区块头部
	pub transactions: Vec<Transaction>,     // 交易列表
}

BlockHeader

区块头部定义如下:

c++版本实现定义:

/** Nodes collect new transactions into a block, hash them into a hash tree,
 * and scan through nonce values to make the block's hash satisfy proof-of-work
 * requirements.  When they solve the proof-of-work, they broadcast the block
 * to everyone and the block is added to the block chain.  The first transaction
 * in the block is a special one that creates a new coin owned by the creator
 * of the block.
 */
class CBlockHeader
{
public:
    // header
    int32_t nVersion;       // 版本号,指定验证规则(indicates which set of block validation rules to follow) 
    uint256 hashPrevBlock;  // 前一区块哈希(实际计算时取得是前一区块头哈希)(a reference to the parent/previous block in the blockchain)
    uint256 hashMerkleRoot; // 默克尔根哈希(a hash (root hash) of the merkle tree data structure containing a block's transactions)
    uint32_t nTime;         // 时戳(seconds from Unix Epoch)
    uint32_t nBits;         // 区块难度(aka the difficulty target for this block)
    uint32_t nNonce;        // 工作量证明nonce(value used in proof-of-work)

    // ...部分代码省略...
}

Rust实现版本定义,可以看到基本都是一样的:

#[derive(PartialEq, Clone, Serializable, Deserializable)]
pub struct BlockHeader {
	pub version: u32,                   // 版本号,指定验证规则
	pub previous_header_hash: H256,     // 前一区块哈希
	pub merkle_root_hash: H256,         // 默克尔根哈希
	pub time: u32,                      // 时戳
	pub bits: Compact,                  // 区块难度
	pub nonce: u32,                     // 工作量证明nonce
}

Merkle Tree

上面区块头部的hashMerkleRoot字段就是默克尔树根哈希值,默克尔树可以说是区块中最重要的一个数据结构,不仅仅提供了整个区块所有交易的完整性验证,。

image

Rust版本Merkle tree代码实现如下:

/// Calculates the root of the merkle tree
/// https://en.bitcoin.it/wiki/Protocol_documentation#Merkle_Trees
pub fn merkle_root<T: AsRef<H256> + Sync>(hashes: &[T]) -> H256{
	// 递归结束条件
	if hashes.len() == 1 {
		return hashes[0].as_ref().clone();
	}
	// 哈希成对,为下一步计算做准备
	let mut row = Vec::with_capacity(hashes.len() / 2);
	let mut i = 0;
	while i + 1 < hashes.len() {
		row.push((&hashes[i], &hashes[i + 1]));
		i += 2
	}

	// duplicate the last element if len is not even
	if hashes.len() % 2 == 1 {
		let last = &hashes[hashes.len() - 1];
		row.push((last, last));     //如果元素是奇数个数,将最后一个元素复制一份,自己与自己配对
	}
	
	// 一层一层的递归计算,一直到只有最后一个根哈希值
	let res: Vec<_>;
	// Only compute in parallel if there is enough work to benefit it
	if row.len() > 250 {
		res = row.par_iter().map(|x| merkle_node_hash(&x.0, &x.1)).collect();
	} else {
		res = row.iter().map(|x| merkle_node_hash(&x.0, &x.1)).collect();
	}
	merkle_root(&res)       
	// 这里不用担心递归的深度,其一,比特币区块大小有限,限制了交易数量;其二,是因为Merkle tree是类似二叉树的结构,递归计算次数为log(n),即使n非常大,递归次数也很小。
}

/// Calculate merkle tree node hash
pub fn merkle_node_hash<T>(left: T, right: T) -> H256 where T: AsRef<H256> {
	dhash256(&*concat(left, right))     //双SHA-256哈希运算
}

#[inline]
fn concat<T>(a: T, b: T) -> H512 where T: AsRef<H256> { //将2个哈希值并在一起
	let mut result = H512::default();
	result[0..32].copy_from_slice(&**a.as_ref());
	result[32..64].copy_from_slice(&**b.as_ref());
	result
}

Transaction

Rust实现版本定义如下:

pub struct Transaction {
	pub version: i32,                       //协议版本,明确这笔交易参照的规则协议
	pub inputs: Vec<TransactionInput>,      //输入列表
	pub outputs: Vec<TransactionOutput>,    //输出列表
	pub lock_time: u32,                     //锁定时间
}

可以看到,交易中最重要的是输入列表和输出列表,说明这次比特币转账时比特币的来源和转账去向。

Rust版本实现输入列表定义:

pub struct TransactionInput {
	pub previous_output: OutPoint,      //上一笔交易输出
	pub script_sig: Bytes,              //解锁脚本
	pub sequence: u32,                  //序列号
	pub script_witness: Vec<Bytes>,     //见证脚本
}

/** An outpoint - a combination of a transaction hash and an index n into its vout */
pub struct OutPoint {
	pub hash: H256,     //交易ID
	pub index: u32,     //输出索引,表明是交易中的第几个输出
}

上面这个OutPoint定义如何没有理解的话,可参考下图进行理解:

Rust版本实现输出列表定义:

pub struct TransactionOutput {
	pub value: u64,             //输出金额
	pub script_pubkey: Bytes,   //锁定脚本,对于一个比特币交易来说,交易本身是不用关心输出的地址,交易只需要关心锁定脚本,当使用的时候能使用正确的解锁脚本即可动用比特币。
}

更多请参考我的另一篇博文比特币交易

参考文档
Bitcoin Developer Reference
parity-bitcoin

如果文章中哪里有疑问或者错误,或者想学习交流探讨区块链技术,可通过下面公众号与我交流学习!