字数 2446,阅读大约需 13 分钟
关注我,带你转型Web3开发,分享我从2018年入行区块链以来的开发经验(DeFi/GameFi/NFT/DAO/OTC/钱包/Trading Bot/MEME Launch)
在区块链应用(DApp)开发中,区块回滚是个常见的麻烦。区块链上的数据以区块形式存储,但由于网络分叉或共识机制特性,如以太坊的叔块或比特币的孤块,回滚时有发生。
这种情况会影响DApp的数据一致性,今天我们一起聊聊如何有效应对区块回滚的问题?
简单来说,区块回滚指的是链上已确认的区块被舍弃,另一条分叉链成为主链的过程。这通常发生在:
回滚会导致已被确认的交易重新变为未确认状态,严重点的话甚至可能被完全丢弃。
处理区块回滚,我的思路就三点:
后端实时监控链上的最新区块号和父区块哈希(ParentBlockHash),与本地记录对比。如果发现不一致,立即启动回滚预警机制。对于不那么在意实时性的DApp,可以延后跟踪1到6个区块,等待网络自行解决短期分叉就好。
发现回滚后,后端就得迅速调整受影响交易的状态,比如从"已确认"改为"待确认"或"已回滚"。数据库里的交易状态设计要清晰,明确定义各种中间态,如pending(待确认)、confirmed(已确认)、reverted(已回滚),前端同样也要有中间态的展示。
通过API或WebSocket将状态变化推送给前端,确保界面及时反映最新情况。对于那些特别讲究实时性的DApp,可以直接从链上读取数据;其他场景则可以依赖后端API提供状态更新。
一般情况下,交易不会因区块回滚而丢失。节点会根据交易状态自动处理:
资金不会因交易丢失而蒸发,因为资金状态是由链上数据决定的。只要交易最终被重新打包并确认,资金流向就不会出问题。不过在回滚期间,用户界面上看到的余额可能会忽高忽低,这时候就需要贴心地提示用户了。
拿我开发的一个单币质押项目举例,假设用户质押了100个ETH,交易被打包到区块10000,但这个区块随后发生了回滚,前后端应该怎样协同处理?
项目用的是Golang后端、React前端、Solidity合约、PostgreSQL数据库和RabbitMQ消息中间件。
后端需要运行一个监听服务,定期获取最新区块信息,检测可能的回滚情况,简化版代码示例如下:
package main
import (
"context"
"fmt"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/common"
"time"
)
type BlockInfostruct {
Numberuint64
Hash common.Hash
ParentHash common.Hash
}
funcmonitorChain(client *ethclient.Client) {
var lastBlocks = make(map[uint64]common.Hash) // 记录过去的区块高度和哈希
for {
// 获取最新区块
block, err := client.BlockByNumber(context.Background(),nil)
if err !=nil {
fmt.Println("获取区块失败:", err)
time.Sleep(5 * time.Second)
continue
}
currentHeight := block.NumberU64()
currentHash := block.Hash()
parentHash := block.ParentHash()
// 检查是否发生回滚
if storedHash, exists := lastBlocks[currentHeight]; exists && storedHash != currentHash {
fmt.Printf("检测到区块冲突: 高度 %d 哈希不匹配\n", currentHeight)
handleRollback(currentHeight)
}
// 检查父区块哈希是否匹配
if prevHash, exists := lastBlocks[currentHeight-1]; exists && prevHash != parentHash {
fmt.Printf("检测到区块回滚: 高度 %d 的父区块哈希不匹配\n", currentHeight)
handleRollback(currentHeight-1)
}
// 更新区块记录
lastBlocks[currentHeight] = currentHash
// 清理过老的记录,只保留最近100个区块
if currentHeight > 100 {
delete(lastBlocks, currentHeight-100)
}
time.Sleep(5 * time.Second)
}
}
funchandleRollback(rollbackHeightuint64) {
// 查找受影响的交易
txs := db.Query("SELECT tx_hash FROM transactions WHERE block_number >= ?", rollbackHeight)
for _, tx :=range txs {
// 更新交易状态
db.Exec("UPDATE transactions SET status = 'reverted' WHERE tx_hash = ?", tx.TxHash)
}
}
数据库设计上,transactions表至少应包含以下字段:
tx_hash:交易哈希block_number:交易所在区块高度status:交易状态(pending/confirmed/reverted)confirmations:确认数timestamp:时间戳前端要同时监听区块变化和后端状态更新通知,简化版代码示例如下:
import { useEffect, useState }from 'react';
import { ethers }from 'ethers';
// 假设WebSocket连接已建立
const ws =newWebSocket('ws://your-backend-api.com/ws');
functionTransactionMonitor({ txHash }) {
const [txStatus, setTxStatus] =useState('pending');
const [confirmations, setConfirmations] =useState(0);
const [message, setMessage] =useState('');
const provider =new ethers.providers.Web3Provider(window.ethereum);
useEffect(() => {
// 监听区块变化
constblockListener = (blockNumber) => {
console.log('新区块:', blockNumber);
// 查询交易收据
if (txHash) {
provider.getTransactionReceipt(txHash).then(receipt => {
if (receipt) {
const newConfirmations = blockNumber - receipt.blockNumber;
setConfirmations(newConfirmations);
// 确认数达到阈值,更新状态
if (newConfirmations >= 6 && txStatus === 'pending') {
setTxStatus('confirmed');
}
}
});
}
};
provider.on('block', blockListener);
// WebSocket监听后端通知
ws.onmessage = (event) => {
const data =JSON.parse(event.data);
// 仅处理当前交易的状态更新
if (data.tx_hash === txHash) {
setTxStatus(data.status);
setMessage(data.message || '');
// 如果交易被回滚,显示通知
if (data.status === 'reverted') {
showNotification('交易状态变化', '您的交易已被回滚,正在等待重新确认');
}
}
};
return () => {
provider.removeListener('block', blockListener);
};
}, [txHash, txStatus]);
const statusDisplay = {
'pending': '处理中',
'confirmed': '已确认',
'reverted': '已回滚'
};
return (
<div className="transaction-status">
<h3>交易状态: {statusDisplay[txStatus]}</h3>
<p>确认数: {confirmations}</p>
{message && <p className="message">{message}</p>}
{txStatus === 'reverted' && (
<div className="alert">
此交易所在区块已回滚,等待网络重新打包确认。资金安全不受影响。
</div>
)}
</div>
);
}
functionshowNotification(title, body) {
// 显示浏览器通知或UI提示
if (Notification.permission === 'granted') {
newNotification(title, { body });
}else {
// 回退到UI提示
console.log(`${title}: ${body}`);
}
}
1. DEX(去中心化交易所)
2. NFT交易平台
3. DeFi借贷平台
created → 提交到链上:pending → 首次打包:included → 足够确认:confirmedincluded/confirmed → reverted → 重新打包:included → ...revert_count字段记录回滚次数block_hash字段存储交易所在区块哈希,便于验证区块回滚在DApp开发里确实挺烦人的,但只要我们处理得当,也没啥大不了。核心就三招:及时发现区块回滚、及时更新本地数据状态、前后端同步好。
以后区块链技术再发展,估计会有更省心的方案,咱们开发者也能少操点心。
你觉得怎么样?欢迎在评论区分享你的看法或疑问,也可以加我微信聊聊!
1111
我的产品体系:
《Web3开发转型陪跑计划》
《数字货币投资工具使用教学》
《AI编程教学》
本人技术栈:
Solidity/Golang/Rust/NodeJS/Python/AI编程
项目合作,欢迎你来链接我!
都看到这里了,不关注一下嘛👇
基于全文检索与主题相似度
说在前面 作为一个在区块链搬砖多年的开发者,我发现共识机制是面试中最常被问到的技术问题之一。 今天,我想梳理一下6种主流共识机制,为我也为你在面对相关问题时能够准确、专业地回答。 共识机制解决的核心问题 在分布式网络中,节点之间如何在没有中心权威的情况下对某个值或状态达成一致?这就是共识机制要解决的
一个不愿提及的教训 2021年的一天,我丢失了价值约¥2W的数字货币。这源于一个愚蠢的操作,至今记忆犹新。回顾起来,这是在HD Wallet理解上交的一笔“学费”。 事情是这样的:我用一个测试钱包接收了一笔USDT,这个钱包的助记词曾在我的博客文章(如何使用MetaMask)中公开过。我知道这个助记
关注我,带你转型Web3开发,分享我从2018年入行区块链以来的开发经验(DeFi/GameFi/NFT/DAO/OTC/钱包/Trading Bot/MEME Launch),免费领取我私下整理的“智能合约学习”资料一份,帮你少走弯路! 说在前面 在Solidity开发中,搞懂数据存储位置就像掌握