在上一篇文章中,我们成功实现了一个可以跑起来的迷你区块链,掌握了区块、链结构和简单的挖矿逻辑。但是,一个真正的区块链不仅仅是区块堆起来就行,还需要处理交易(tx),保证数据不可篡改,并让节点能够安全达成共识。
本篇文章,我们将深入交易设计、区块验证与链安全,手把手用 Go 实现更完整的区块链核心功能。
一、交易(Transaction)设计
在真实的区块链中,交易是区块的核心内容,每一个区块都是交易的集合。
1️⃣ 交易结构
我们可以用 Go 定义一个简单的交易结构:
type Transaction struct {
ID []byte
From string
To string
Amount float64
}
- ID:交易哈希,用于唯一标识
- From/To:转账双方
- Amount:交易金额
💡 提示:在真实区块链中,交易还需要签名验证、防重放攻击等,这里先简化为演示逻辑。
2️⃣ 生成交易哈希
func HashTransaction(tx Transaction) []byte {
data := tx.From + "|" + tx.To + "|" + strconv.Itoa(tx.Amount)
h := sha256.Sum256([]byte(data))
return h[:]
}
每笔交易通过哈希保证唯一性和完整性,一旦交易被篡改,哈希就会变化。
3️⃣ 交易签名(示例)
func (tx *Transaction) Sign(privKey *ecdsa.PrivateKey) error {
tx.ID = tx.Hash()
r, s, err := ecdsa.Sign(rand.Reader, privKey, tx.ID)
if err != nil {
return err
}
tx.Signature = append(r.Bytes(), s.Bytes()...)
return nil
}
Signature 是发送方私钥生成的签名
- 节点验证时可以用公钥确认交易合法性
二、交易池(Mempool)
在区块生成前,交易需要存放在交易池中等待打包:
type TxPool struct {
Transactions []*Transaction
}
func (pool *TxPool) AddTransaction(tx *Transaction) {
pool.Transactions = append(pool.Transactions, tx)
}
- 挖矿节点从交易池中挑选交易打包到新区块
- 可以根据交易手续费、优先级进行排序
🔹 交易池让区块链系统可以异步处理交易,提高效率。
三、区块的交易列表与验证
1️⃣ 区块结构
在上篇文章中,我们的区块只有数据字段,这里增加交易列表:
// Block 区块结构体,代表区块链中的一个区块
type Block struct {
Index int `json:"index"` // 区块索引(高度)
Timestamp int64 `json:"timestamp"` // 区块生成时间戳
Transactions []string `json:"transactions"` // 包含的交易ID列表
PrevHash string `json:"prev_hash"` // 前一个区块的哈希值
Nonce int64 `json:"nonce"` // 工作量证明的随机数
Hash string `json:"hash"` // 当前区块的哈希值
}
2️⃣ 新增区块时验证交易
新增区块时,需要保证每笔交易有效,例如发送方有足够余额、交易未被篡改。
// VerifyTransaction 验证交易签名的有效性
func VerifyTransaction(tx Transaction) bool {
pubBytes, err := hex.DecodeString(tx.From)
if err != nil {
return false
}
x, y := elliptic.Unmarshal(elliptic.P256(), pubBytes)
if x == nil {
return false
}
pub := ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}
sigBytes, err := hex.DecodeString(tx.Signature)
if err != nil {
return false
}
h := HashTransaction(tx)
return ecdsa.VerifyASN1(&pub, h, sigBytes)
}
✅ 提示:这是最基础的验证逻辑,真实链还需要更复杂的数字签名和状态验证。
四、区块链共识机制简述
在去中心化环境中,多个节点可能同时出块,必须有规则确定哪条链才是有效链,这是网络与系统中分布式一致性的经典问题。
- 工作量证明(PoW):上篇已实现基础挖矿逻辑。
- 最长链原则:节点总是认同最长的有效链。
// AddBlock 向区块链中添加新区块
func AddBlock(b Block) bool {
chainMutex.Lock()
defer chainMutex.Unlock()
last := blockchain[len(blockchain)-1]
// 验证区块的前哈希是否正确
if b.PrevHash != last.Hash {
return false
}
// 验证区块哈希值是否正确
if CalculateHash(b) != b.Hash {
return false
}
// 验证工作量证明是否有效
if !strings.HasPrefix(b.Hash, strings.Repeat("0", difficulty)) {
return false
}
blockchain = append(blockchain, b)
return true
}
当节点接收到新区块时,只要验证交易合法并满足 PoW 条件,就可以加入链中。
五、挖矿与区块奖励
矿工在验证并打包交易后,生成新区块,并获得奖励(挖矿收益):
// CoinbaseTx 创建一个Coinbase交易(挖矿奖励)
// coinbaseData: 区块中包含的额外数据(如矿工信息)
// to: 接收奖励的地址
// amount: 奖励金额
func CoinbaseTx(coinbaseData, to string, amount int) UTXOTx {
// Coinbase交易没有输入,使用特殊标记
inputs := []TxInput{
{
Txid: "0", // 特殊标记表示coinbase交易
Vout: -1, // 特殊值
Signature: coinbaseData,
PubKey: "coinbase", // 标记为coinbase
},
}
// 创建输出给矿工的奖励
outputs := []TxOutput{
{
Address: to,
Amount: amount,
},
}
return UTXOTx{Inputs: inputs, Outputs: outputs}
}
- 挖矿奖励通过特殊交易(coinbase)发放
- 提高节点参与动力
六、简单余额查询示例
结合交易结构,我们可以快速实现账户余额查询:
func (bc *Blockchain) GetBalance(address string) float64 {
var balance float64
for _, block := range bc.Blocks {
for _, tx := range block.Transactions {
if tx.From == address {
balance -= tx.Amount
}
if tx.To == address {
balance += tx.Amount
}
}
}
return balance
}
🔹 这个方法展示了交易在链上的价值流向,也为钱包功能奠定基础。
七、实践总结
通过本篇,我们完成了:
- 交易结构、哈希与签名的基础设计
- 交易池(Mempool)的简单管理
- 区块验证与 PoW 共识机制的集成
- 挖矿奖励与账户余额的初步计算
这样,我们的迷你区块链不仅能存储数据,还能处理简单的交易逻辑,朝着一个功能更完整的区块链系统又迈进了一步。