Derick
1456 words
7 minutes
读懂区块 —— 以太坊的时间机器

“每一个区块,都是一次全网共识的快照。

它不仅记录了交易,更记录了时间的流逝。”


一、开场:区块,不只是数据#

在上一讲中,我们成功连上了以太坊节点,并读取了最新区块号

那时,我们看到的只是一个数字——

例如:

最新的区块号是: 5689012

但如果说,这个数字代表了以太坊心脏跳动的节奏,你是否会想更进一步?

——这些区块,到底“长什么样”?

——它们存着什么?

——交易又是如何被打包进去的?

在这一讲,我们就来一起“拆开”一个区块,看看里面的世界。


二、区块的本质:世界状态的“快照”#

如果你来自传统后端背景,可以这样理解:

  • 在数据库中,一次事务提交 会改变数据状态;
  • 在区块链中,一次区块确认 会改变全网状态。

每一个区块(Block)都像一张“快照”,

记录了自上一个区块以来,发生的所有状态变化。

概念类比说明
区块号 (Block Number)数据版本号记录世界状态的顺序
区块哈希 (Block Hash)快照 ID唯一标识这个区块内容
父区块哈希 (Parent Hash)上一个版本指针像 Git 的 parent commit
时间戳 (Timestamp)提交时间区块打包的时间
交易列表 (Transactions)操作日志区块内包含的所有操作

以太坊区块链,就是由这些“快照”首尾相连形成的时间长河。

你可以随时读取过去任何一个区块的状态,就像翻阅时间机器中的档案。


三、Go-Ethereum 实战:读取一个完整区块#

接下来,我们动手验证这一切。

目标:

使用 Go 语言获取最新区块的详细信息,

并打印其中的核心字段。

1. 新建文件 block_inspect.go#

package main

import (
	"context"
	"fmt"
	"log"
	"math/big"

	"github.com/ethereum/go-ethereum/ethclient"
)

func main() {
	// 1. 连接到节点
	client, err := ethclient.Dial("https://sepolia.infura.io/v3/<YOUR_PROJECT_ID>")
	if err != nil {
		log.Fatalf("连接节点失败: %v", err)
	}

	// 2. 获取最新区块头,拿到区块号
	header, err := client.HeaderByNumber(context.Background(), nil)
	if err != nil {
		log.Fatalf("无法获取区块头: %v", err)
	}
	blockNumber := header.Number

	fmt.Printf("🔹 当前最新区块号: %v\n", blockNumber)

	// 3. 获取完整区块对象
	block, err := client.BlockByNumber(context.Background(), blockNumber)
	if err != nil {
		log.Fatalf("无法获取区块信息: %v", err)
	}

	// 4. 打印关键字段
	fmt.Println("———————— 区块信息 ————————")
	fmt.Printf("区块号: %v\n", block.Number())
	fmt.Printf("区块哈希: %v\n", block.Hash().Hex())
	fmt.Printf("父区块哈希: %v\n", block.ParentHash().Hex())
	fmt.Printf("时间戳: %v\n", block.Time())
	fmt.Printf("交易数量: %d\n", len(block.Transactions()))

	// 5. 读取区块内的第一笔交易(若存在)
	if len(block.Transactions()) > 0 {
		tx := block.Transactions()[0]
		fmt.Println("———————— 第一笔交易 ————————")
		fmt.Printf("交易哈希: %v\n", tx.Hash().Hex())
		fmt.Printf("发起地址: %v\n", tx.From()) // 注意:tx.From() 需要额外签名信息,此处略
		fmt.Printf("接收地址: %v\n", tx.To())
		fmt.Printf("Gas 消耗上限: %v\n", tx.Gas())
	}
}

运行它:

go run block_inspect.go

你会看到类似输出:

🔹 当前最新区块号: 5689012
———————— 区块信息 ————————
区块号: 5689012
区块哈希: 0x7f3a1d...
父区块哈希: 0x9b7f20...
时间戳: 1731098890
交易数量: 142
———————— 第一笔交易 ————————
交易哈希: 0xa1b9f...
接收地址: 0x32F0...D5a
Gas 消耗上限: 21000

恭喜!你已经成功“读懂”了一个区块的骨架。


四、交易:区块的灵魂#

区块是容器,交易是灵魂。

在以太坊中,所有状态变化(账户余额、合约执行、事件日志)都来源于交易。

没有交易,就没有区块。

没有区块,就没有状态。

一笔交易包含什么?#

字段含义
nonce发送者账户的交易计数器,防止重放
to接收方地址(若为空,则是部署合约)
value转账的 ETH 数量
gas允许消耗的最大 Gas
gasPrice / maxFeePerGas每单位 Gas 的费用
data合约调用参数或部署代码
v, r, s签名字段,验证交易的合法性

当节点验证一笔交易时,它实际上在EVM 沙箱中执行了这段代码

然后将执行结果(包括状态变化、事件日志)记录入区块。


五、区块链中的时间与不可逆性#

每个区块都有一个时间戳 timestamp

它并不是毫秒级精度,而是区块被“打包”进链时的粗略时间。

区块之间间隔大约为 12 秒(PoS 共识下),

这意味着以太坊的“时间”是由共识推动的,而非系统时钟。

区块链世界的时间有一个重要特征:

过去的状态永远不会被修改,只能被追加。

这就是所谓的不可篡改性(Immutability)

而这,也正是它区别于一切传统数据库的根本。


六、思考与扩展#

  1. 能否通过代码获取指定区块号的区块?

    提示:client.BlockByNumber(ctx, big.NewInt(1234567))

  2. 能否将区块数据以 JSON 格式输出?

    试着用 json.MarshalIndent(block, "", ” ”)

  3. 区块中有 100 多笔交易,你能否循环遍历并打印它们的哈希?

    下一讲,我们就要这么做。


七、过渡:从区块到交易日志#

我们已经看到了区块和交易的结构。

但在智能合约的世界中,真正有价值的数据往往隐藏在**事件日志(Logs)**中。

它们是应用层的“信号”,

代表某个合约函数被执行、某笔业务逻辑已触发。


八、总结#

我们学到了什么关键点
区块是以太坊的时间片段每个区块都是全网状态的快照
交易是状态变化的源头每笔交易都会改变链上状态
区块哈希形成了链式结构确保数据不可篡改
Go 语言可轻松读取区块内容BlockByNumber 是关键方法
读懂区块 —— 以太坊的时间机器
https://blog.ithuo.net/posts/blockchain-tutorial-evm-3/
Author
Derick
Published at
2024-12-06