第一章:区块链数据基础——区块结构、交易哈希、链上数据模型与存储原理
各位同学好,我是你们的老朋友。今天咱们来聊聊区块链数据最底层的东西。说实话,很多人一上来就冲进链上数据分析,结果连区块里装了什么、交易哈希怎么算的都没搞明白。我当年刚入行时也踩过这个坑——有一次分析一笔异常转账,折腾了半天才发现是自己把交易哈希的字节序搞反了。嗯,从那以后我就养成了一个习惯:先把地基打牢。
1.1 区块结构:链上数据的“集装箱”
区块链说白了就是一个不断增长的链表。每个区块就像一个大集装箱,里面装着这段时间内发生的所有交易。我个人习惯把区块结构拆成三部分来看:
- 区块头(Block Header):元数据,约80字节
- 交易列表(Transaction List):核心数据,大小可变
- 区块元数据:比如区块大小、交易数量等统计信息
区块头里藏着很多关键信息。我画了一张图,帮你快速理解它的内部结构:
核心要点:区块头里的前区块哈希把整个链条串了起来。你改任何一个历史区块,后面所有区块的哈希都得变——这就是区块链不可篡改的底层逻辑。
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