第一章:区块链数据基础——区块结构、交易哈希、链上数据模型与存储原理

各位同学好,我是你们的老朋友。今天咱们来聊聊区块链数据最底层的东西。说实话,很多人一上来就冲进链上数据分析,结果连区块里装了什么、交易哈希怎么算的都没搞明白。我当年刚入行时也踩过这个坑——有一次分析一笔异常转账,折腾了半天才发现是自己把交易哈希的字节序搞反了。嗯,从那以后我就养成了一个习惯:先把地基打牢。

1.1 区块结构:链上数据的“集装箱”

区块链说白了就是一个不断增长的链表。每个区块就像一个大集装箱,里面装着这段时间内发生的所有交易。我个人习惯把区块结构拆成三部分来看:

  • 区块头(Block Header):元数据,约80字节
  • 交易列表(Transaction List):核心数据,大小可变
  • 区块元数据:比如区块大小、交易数量等统计信息

区块头里藏着很多关键信息。我画了一张图,帮你快速理解它的内部结构:

区块结构示意图 区块头(Block Header) 版本号(4字节) 前区块哈希(32字节) Merkle根(32字节) 时间戳(4字节) 难度目标(4字节) Nonce(4字节) 交易列表(Transaction List) 交易1 交易2 交易3 ...交易N

核心要点:区块头里的前区块哈希把整个链条串了起来。你改任何一个历史区块,后面所有区块的哈希都得变——这就是区块链不可篡改的底层逻辑。

1.2 交易哈希:链上数据的“身份证”

每笔交易都有一个唯一的哈希值,就像人的身份证号。交易哈希是怎么算出来的?说白了,就是把交易的所有字段拼在一起,然后做两次SHA256哈希。

举个例子,一笔简单的转账交易:

// 交易原始数据(简化版)
{
  "from": "0xAbc...123",
  "to": "0xDef...456",
  "value": "1000000000000000000",  // 1 ETH
  "nonce": 5,
  "gasPrice": "20000000000",
  "gasLimit": 21000,
  "data": "0x"
}

// 计算交易哈希的过程
// 1. RLP编码所有字段
// 2. 对编码结果做SHA256
// 3. 再对结果做一次SHA256
txHash = SHA256(SHA256(RLP_encode(tx_data)))

避坑指南:我曾经在分析一笔跨链交易时,发现两边记录的哈希对不上。查了半天才发现——以太坊的交易哈希是64位十六进制字符串,而比特币是大小端字节序相反的。不同链的哈希格式不一样,千万别想当然。

1.3 链上数据模型:账户模型 vs UTXO模型

目前主流的链上数据模型有两种。你想想看,这就像两种不同的记账方式:

特性 账户模型(以太坊) UTXO模型(比特币)
核心概念 账户余额 + 状态 未花费交易输出
交易结构 from → to → value 输入列表 → 输出列表
并发处理 需要nonce防重放 天然支持并行
隐私性 较低(地址可追踪) 较高(可混币)
典型应用 智能合约、DeFi 支付、价值存储

我个人更习惯用账户模型做分析,因为状态变化更直观。但UTXO模型在追踪资金流向时其实更清晰——每一笔钱都能追溯到源头。我记得有一次做反洗钱分析,UTXO链上的资金流图简直像一张蜘蛛网,但每一步都清清楚楚。

1.4 存储原理:数据到底存在哪?

很多人以为区块链数据存在“云上”或者“某个服务器里”。其实不是。每个全节点都在本地存了一份完整的数据副本。存储结构一般分三层:

  • 区块文件(blk*.dat):原始区块数据,按顺序存储
  • 状态数据库(LevelDB/RocksDB):当前账户状态、交易索引等
  • 索引数据库:加速查询,比如根据地址查交易记录

以以太坊为例,它的状态数据用Merkle Patricia Trie(MPT)树来组织。为什么要用树结构?因为要快速验证某个账户的余额是否存在,而不需要遍历整个数据库。嗯,这里有个细节:MPT树的根哈希就存在区块头里,所以只要根哈希对得上,整个状态就是可信的。

注意:链上数据不是无限存储的。比特币全节点大约500GB,以太坊已经超过1TB。我建议做数据分析时,不要直接跑全节点,用归档节点或者数据服务商(比如Infura、QuickNode)会更省事。否则光同步数据就得花好几天。

1.5 实战:如何从区块中提取交易数据

光说不练假把式。咱们用Python演示一下怎么从原始区块数据中提取交易信息:

import requests
import json

# 以以太坊为例,通过JSON-RPC获取区块数据
def get_block_transactions(block_number):
    url = "https://mainnet.infura.io/v3/YOUR_PROJECT_ID"
    
    payload = {
        "jsonrpc": "2.0",
        "method": "eth_getBlockByNumber",
        "params": [hex(block_number), True],  # True表示包含完整交易数据
        "id": 1
    }
    
    response = requests.post(url, json=payload)
    block_data = response.json()["result"]
    
    # 提取交易列表
    transactions = block_data["transactions"]
    
    print(f"区块 {block_number} 包含 {len(transactions)} 笔交易")
    
    # 打印前3笔交易的哈希和金额
    for tx in transactions[:3]:
        print(f"交易哈希: {tx['hash']}")
        print(f"发送方: {tx['from']}")
        print(f"接收方: {tx['to']}")
        print(f"金额: {int(tx['value'], 16) / 1e18} ETH")
        print("---")
    
    return transactions

# 调用示例
txs = get_block_transactions(19000000)

小技巧:我在做链上监控时,不会每次都拉全量数据。一般只关注交易哈希、from/to地址、value这几个字段。数据量大的时候,用流式处理(比如Kafka + Spark Streaming)比直接请求RPC接口高效得多。

1.6 本章小结

好了,咱们把区块链数据的基础过了一遍。区块结构就像集装箱,交易哈希是身份证,数据模型决定了你怎么查账,存储原理告诉你数据在哪。这些概念虽然基础,但后面做异常交易识别时,每一步都离不开它们。

记住一句话:链上数据是透明的,但只有懂它的人才能看到真相。


公众号:蓝海资料掘金营,微信deep3321