写给初学者
区块链可以粗浅理解成:很多人各自保存一份结构相同、按顺序增长的「账本页」(区块),通过密码学和共识规则,让大家对「哪一页才是合法的下一页」达成一致。你不需要一次弄懂所有词,本页把概念拆成和本 Demo 一一对应的几块。
本仓库是一个教学用迷你链 + Web 控制台:链逻辑跑在 Node 服务端(Next.js 的 Route Handler),你在浏览器里点点按钮,就是在调用 HTTP 接口改链的状态。它不是以太坊主网,也没有真金白银。
若你关心挖矿在算什么(类比特币的 80 字节头、Hash256、小端目标、Merkle),可直接跳读 《PoW 算法说明(类比特币·完整速查)》;与根目录 README.md 第 12 节内容一致,便于离线复习与面试口述。
路由与各页面功能
下面这张表对应顶部导航和实际文件路径,方便你对照代码。
| 路由 | 主要做什么 | 代码入口(参考) |
|---|---|---|
/ | 总览推荐操作顺序、常见问题(余额为 0、转账后没变等)。 | app/page.tsx |
/wallet | 生成密钥对(演示用)、把公钥/私钥/矿工地址等存到浏览器 localStorage;用 POST /api/balance 查余额(避免超长公钥走 URL)。 | app/wallet/page.tsx |
/mine | 指定矿工地址(公钥)。单次:1 单线程挖一块、2 多 Worker 约 100% CPU 挖一块。持续挖矿:通过下拉选算力模式(默认约 100% 多 Worker,可选 90% 或单线程)、可选最长秒数,循环取任务 → 本机 PoW → POST /api/mine 提交,可停止(当前块完成后结束)。服务端始终只校验并链接。 | app/mine/page.tsx、app/mine/mineClient.ts、app/mine/minePow.worker.ts、app/api/mine/work/route.ts、app/api/mine/route.ts |
/transfer | 构造一笔已签名转账,提交后只进入交易池;要上链需再到「挖矿」完成 PoW 并提交区块。 | app/transfer/page.tsx、app/api/transactions/route.ts |
/explorer | 轮询链状态:高度、难度、最新哈希、交易池列表、区块列表(可展开 JSON)。支持自动滚到最新(可关)。 | app/explorer/page.tsx、app/api/state/route.ts |
/learn | 就是当前这篇「学习与原理」长文。 | app/learn/page.tsx |
跨页面共用的「当前钱包状态」在 src/context/WalletContext.tsx,由 src/components/AppChrome.tsx 包住整站,所以换页不会丢主钱包公钥(除非清除本地存储)。
技术栈说明
框架与语言
- Next.js 16(App Router):全栈框架。本项目的页面在
app/**/page.tsx,HTTP API在app/api/**/route.ts(Route Handlers)。一次pnpm dev同时起前端页面和后端接口。 - React 19:构建 UI。带交互、要用浏览器 API(如
localStorage)的组件会标"use client"。 - TypeScript:静态类型,链核心逻辑在
src/lib/*.ts,减少低级错误。 - Tailwind CSS v4:样式通过
@import "tailwindcss"与globals.css里的自定义类(如.panel、.learn_doc)组合。
与链相关的库
- elliptic:椭圆曲线密码,本项目用 secp256k1(和比特币同曲线家族)做密钥生成、签名与验签。见
src/lib/wallet.ts。 - Node crypto:
sha256用于交易 id(Transaction.calculateId)。 - @noble/hashes + @scure/base:区块 PoW 的 Hash256(双 SHA-256)与 hex 编解码在
src/lib/powBytes.ts(浏览器与 Node 共用算法;服务端src/lib/pow.ts/sha256d.ts委托同一套字节逻辑)。Merkle 与确定性序列化仍见src/lib/merkle.ts、src/lib/hash.ts。 - ws:可选的 P2P WebSocket 服务,在
src/instrumentation.ts里于 Node 进程启动时挂上。见src/lib/p2pServer.ts。
工程与包管理
- pnpm:推荐包管理器;
package.json里有packageManager字段;根目录.npmrc允许 Next 依赖(如sharp)执行安装脚本。 - Jest + ts-jest:单元测试链逻辑,见
__tests__/blockchain.test.ts。 - ESLint:
eslint.config.mjs(flat config)+eslint-config-next。
区块链在解决什么问题(直觉版)
想象多人要共用一本账,又不能完全信任某一台中心服务器:
- 每一笔变动要有凭证(密码学签名),别人能验证是不是本人授权。
- 账页的先后顺序要明确,且难以被单方面篡改(后面区块会「挂」在前面区块的哈希上,一改全链对不上)。
- 谁来决定「下一页写什么」要有规则——本 Demo 用极简的 工作量证明(PoW):谁先算出符合难度的哈希,谁就有权提议下一个区块(并拿奖励)。
真实公链还有更复杂的共识、经济模型、状态存储等;本仓库只保留最小可运行骨架,方便你把「区块—交易—挖矿—同步」跑通。
本项目里的「链」长什么样
在代码里,链就是一个区块数组,从创世块(高度/索引 0)开始,一块接一块。类 Blockchain(src/lib/blockchain.ts)维护:
chain:已确认的区块列表。transactionPool:已广播/提交、但尚未写入任何区块的交易。difficulty:当前 PoW 难度(参与目标值T = (2^256-1) >> difficulty,越大越难挖),会按出块间隔在上下限内调整。详见 《PoW 算法说明》。
界面上的高度在 API 里用的是 chain.length(包含创世块)。所以只存在创世块时高度为 1;再挖一块高度为 2,以此类推。
区块结构、哈希与工作量证明
每个区块大致包含:索引、上一块哈希、时间戳、本块包含的数据(交易列表 + 奖励交易)、本块哈希、难度、nonce。
块哈希(展示值):由80 字节区块头经 Hash256(双 SHA-256)得到 32 字节,再整体反序后转十六进制,即你在浏览器里看到的「块哈希」。它与「头里存的父哈希字节序」不是同一视角,下面会说明。
PoW:对区块头做 Hash256 得到 work,将 work 视为小端无符号 256 位整数,必须严格小于目标值 T = (2^256-1) >> difficulty;通过递增 32 位 nonce(溢出绕回)试错。data 先算 Merkle 根再写入头。核心实现见 src/lib/powBytes.ts(浏览器挖矿与链上校验同源),服务端封装见 src/lib/pow.ts、src/lib/merkle.ts、src/lib/sha256d.ts。
更系统的公式、字段表与源码索引见专节 《PoW 算法说明(类比特币·完整速查)》。
链校验:新块必须满足「索引 = 上一块 +1」「父哈希对上」「区块头可复算且 Hash256 满足目标」等条件,否则拒绝。见 isValidNewBlock / isValidChain。
交易、钱包与数字签名
钱包在本 Demo 里就是一对密钥:私钥保密,公钥公开当作「地址」。转账时用私钥对交易内容派生出的 id做签名,链上节点用公钥验证——伪造签名在计算上不可行(在正确实现前提下)。
出块奖励交易没有 fromAddress(视为币基/系统铸币),不需要签名;普通转账必须有 fromAddress + 签名。见 src/lib/transaction.ts。
余额是怎么算出来的
本项目没有 UTXO 模型,而是扫整条链:对每个区块里的每笔交易,若 toAddress 等于你的公钥就加金额,若 fromAddress 等于你的公钥就减金额。见 Blockchain.getBalance。
因此:
- 挖矿奖励打给矿工公钥 → 该公钥余额增加(默认每次奖励
MINE_REWARD,常为 50)。 - 多挖几次、奖励都进同一地址 → 余额累加(例如两次 ×50 = 100)。
- 交易还在交易池里 → 不会计入余额,必须被打进区块。
- 查余额的公钥必须和「链上记录里出现的字符串」完全一致(矿工地址填错就会查错人)。
挖矿、出块奖励与难度
每一轮开始前,服务端在 GET /api/mine/work 中把当前交易池快照与一笔奖励交易(发给矿工地址、时间戳与任务绑定)组成 data,连同父哈希、难度、固定时间戳等一并下发。你在浏览器里对这份快照做 PoW 找 nonce,再 POST /api/mine 提交完整 Block JSON;服务端 Blockchain.submitMinedBlock 校验通过后 addBlock 并清空交易池。持续挖矿只是在前端重复「取任务 → PoW → 提交」。若在你挖矿期间池子或链尖变了,该轮提交会失败并提示重新取任务(持续模式会因此结束循环)。
难度调整:每隔若干块,根据出块间隔调高或调低 difficulty(有上下限,见 POW_MIN_DIFFICULTY / POW_MAX_DIFFICULTY),目标值随 difficulty 变化。真实比特币用 compact nBits 编码目标,这里用位移推导目标,便于对照理解。
更细的逐步说明(点击按钮后发生了什么、怎样算挖成功)见下一节 《挖矿详解》。
挖矿详解:含义、点击流程与成功条件
1. 「挖矿」在这里是什么意思?
日常语言里可以把它想成:用计算机做大量试错计算,直到算出一个满足规定形状的区块「指纹」(哈希),从而有权把一页新账本(新区块)接到现有链上。这种用算力换「记账权」的思路,属于工作量证明(Proof of Work, PoW)的一种极简实现。
在本仓库里,「挖矿」不是去挖比特币主网;它是在你自己的浏览器里对服务端下发的任务做 PoW,再由服务端 Blockchain.submitMinedBlock 校验通过后把块追加到 chain 并清空交易池。(创世块仍在 Node 里用 mineBlockPow 预挖一次,逻辑与浏览器一致,见 createGenesisBlock。)
2. 「挖矿」页上有哪些操作?
实现见 app/mine/page.tsx(调度)、app/mine/mineClient.ts(单线程与多 Worker 入口):
- 单次挖矿:挖矿一次(单线程) 走
mineBlockPowU8;100% 性能挖一块 走满逻辑核数的minePow.worker.ts分片。各只完成一轮上链。 - 持续挖矿:页面「算力模式(下拉)」中选择模式,默认选中约 100% 逻辑 CPU(多 Worker),可改为 90% 或单线程;可选「最长运行(秒)」(不填则仅「停止持续挖矿」结束)。循环调用与单次相同的「取任务 → PoW → 提交」;某轮失败会中断循环。停止请求在当前这一块的 PoW 或提交结束后生效(无法在同步哈希循环中途强行打断)。
3. 每一轮上链,从上到下发生了什么?
无论单次还是持续里的每一圈,技术顺序如下(便于对照源码):
- 浏览器请求
GET /api/mine/work?minerAddress=...(见app/mine/page.tsx、app/api/mine/work/route.ts),拿到 JSON 任务:version、previousBlockHashHex、timestampMs、difficulty、data(池内交易 + 奖励交易)、nextIndex。 - 本机算 PoW:
app/mine/mineClient.ts调用mineBlockPowU8(单线程)或app/mine/minePow.worker.ts内mineBlockPowShardU8(多 Web Worker 分片),在用户 CPU 上循环改nonce,直到满足下一节的哈希条件。 - 组装待提交区块:用任务里的字段填入
Block形状——index = nextIndex,previousHash = previousBlockHashHex,timestamp = timestampMs,data原样沿用任务快照,difficulty不变,hash/nonce为刚算出的结果。 - 浏览器向
POST /api/mine发 JSON:{ "minerAddress": "…", "block": { … } }(见app/api/mine/route.ts)。 - 服务端
Block.fromJSON后执行submitMinedBlock:核对链尖与交易池是否与任务快照一致、奖励是否指向该minerAddress、verifyBlockPow通过,再addBlock。 - 清空交易池:校验通过后
transactionPool = []。 - 持久化与广播:
touchPersistence()、broadcastLatest()(同前)。 - 响应浏览器:HTTP
201返回新块 JSON;若池子或链尖已变,返回400与「请重新获取挖矿任务」类提示。
4. 怎样算「挖矿成功」?算的是什么?
「成功」在这里指:找到 32 位 nonce,使得Hash256(80 字节区块头)在小端无符号 256 位整数意义下严格小于目标值 T。实现见 mineBlockPowU8 / hashMeetsTargetU8(src/lib/powBytes.ts),服务端校验用 verifyBlockPow(src/lib/pow.ts,内部同源)。
区块头字段(与比特币布局一致,难度以 uint32 写入头内以便与块上 difficulty 绑定):
version(4 字节 LE,当前为 1)prevBlock(32 字节:父块「展示用块哈希」的十六进制解码后按字节反序,与比特币头内存储方式一致;创世父哈希为全 0)merkleRoot(32 字节:对data做 Merkle 树;普通交易叶子为Hash256(txid 字节),其它条目用稳定序列化后再 Hash256,奇数个叶子复制最后一个)time(4 字节 LE,Unix 秒,由毫秒时间戳换算)difficulty(4 字节 LE,与链上难度字段相同)nonce(4 字节 LE,从 0 递增,溢出后绕回 0)
目标值:T = (2^256 - 1) >> difficulty(targetFromDifficulty)。difficulty 越大,T 越小,期望尝试次数指数级上升(随机模型下约每 +1 减半命中概率)。
展示的块哈希:对 Hash256 的 32 字节输出再整体反序后转十六进制(比特币区块浏览器上的块 ID 惯例),与头内参与运算的字节顺序区分开——面试时能说清这一点很重要。
循环逻辑是不断调用 computeBlockWorkHashU8(或经 computeBlockWorkHash 的 Buffer 封装)换 nonce,直到命中目标;在有限时间内几乎必然成功。若提交时链尖或交易池已与任务快照不一致,POST /api/mine 会返回错误而非 PoW「运气」问题。
5. 和你在界面上看到的现象怎么对应?
- 单次挖矿的两个按钮各触发一轮上述流程,成功后高度 +1;矿工地址多一笔奖励(查余额请用同一公钥)。
- 持续挖矿会在日志里出现
[第 N 块]前缀,每成功一轮高度 +1;达到设定秒数或点停止后结束。 - 交易池里有待打包转账时,同一次挖矿会把它们和奖励一起写进
data;因此转账后要再挖一次才会在余额上看到转账效果。 - 难度会在每挖若干块后根据出块快慢微调(
getAdjustedDifficulty),你在「链上浏览」里看到的难度数字可能随时间变化。
6. 与真实比特币等公链的差异(避免误解)
本仓库已采用Hash256 + 小端整数与目标比较 + Merkle 根 + 展示块哈希反序等核心习惯,与比特币 PoW 同一套叙事;差异主要在于:目标值由本链的 difficulty 位移推导,而未实现 compact nBits;区块头未包含 height / bits 的完整主网编码;也无主网共识与分叉选择。掌握本节后读比特币开发者文档时,可直接对照「nBits、工作量证明校验、merkleblock」等章节。
需要一页纸背下来的字段表、公式与文件对照,请继续阅读 下一节《PoW 算法说明》(与根目录 README.md 第 12 节内容同源,便于离线复习)。
PoW 算法说明(类比特币·完整速查)
本节把当前仓库里的挖矿判定写成可对照源码的规格说明,方便写进简历/面试口述;与 README.md「工作量证明(PoW)算法说明」互为补充。
1. 一句话
对固定 80 字节区块头计算 work = Hash256(header) = SHA256(SHA256(header));记 H = uint256LE(work),T = (2^256-1) >> difficulty,当且仅当 H < T 时 PoW 有效。界面上的块哈希为 reverse(work) 的十六进制。
2. 区块头 80 字节布局(全小端)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 4 | version | int32 LE,当前恒为 1 |
| 4 | 32 | prevBlock | 父块展示哈希(64 hex)解码后按字节反序写入头内;创世为 32 字节全 0x00(即 previousHash 存 64 个 0) |
| 36 | 32 | merkleRoot | 本块 data 的 Merkle 根(见下节) |
| 68 | 4 | time | Unix 秒,uint32 LE |
| 72 | 4 | difficulty | uint32 LE,与区块上的 difficulty 字段一致 |
| 76 | 4 | nonce | uint32 LE,挖矿时从 0 递增,溢出绕回 0 |
3. Merkle 树规则
- 交易(
instanceof Transaction或 JSON 还原后带id的交易形对象):叶子 =Hash256(hex_decode(tx.id))。 - 其它对象(如创世
{ "type": "genesis" }):叶子 =Hash256(utf8(stableStringify(obj)))。 - 自底向上两两拼接
Hash256(left ‖ right);若本层奇数个节点,复制最后一个再配对(比特币规则)。 - 根即为头内
merkleRoot的 32 字节。
4. 难度与调整
目标 T = targetFromDifficulty(difficulty) = (2^256-1) >> difficulty(BigInt 位移)。difficulty 越大,T 越小,期望尝试次数近似按指数增长。
每隔 DIFFICULTY_ADJUST_INTERVAL 块按出块时间与 BLOCK_GENERATION_INTERVAL 比较,难度 ±1,并夹在 POW_MIN_DIFFICULTY 与 POW_MAX_DIFFICULTY 之间。创世难度由 POW_GENESIS_DIFFICULTY 决定,且 isValidGenesisBlock 会校验该值。
5. 校验顺序(接块时)
index === prev.index + 1,previousHash === prev.hash(展示哈希字符串一致)。- 用当前块的
data、timestamp、difficulty、nonce重算work,确认reverse(work).toString("hex") === block.hash。 - 确认
uint256LE(work) < T(difficulty)。
对应代码:Blockchain.isValidNewBlock → verifyBlockPow。
6. 源码文件索引
src/lib/powBytes.ts— 与链校验一致的 Uint8Array 路径:doubleSha256U8、computeBlockWorkHashU8、mineBlockPowU8、mineBlockPowShardU8、verifyBlockPowU8、targetFromDifficulty等(依赖@noble/hashes/sha2.js、@scure/base)src/lib/pow.ts— Node/服务端 Buffer 封装:computeBlockWorkHash、mineBlockPow(创世预挖)、verifyBlockPow等src/lib/sha256d.ts—doubleSha256(委托powBytes)src/lib/merkle.ts—computeMerkleRoot(委托powBytes)src/lib/hash.ts—stableStringifysrc/lib/mineTypes.ts—MineWorkResponse(与GET /api/mine/work对齐)src/lib/blockchain.ts—submitMinedBlock、getAdjustedDifficulty、isValidGenesisBlock、createGenesisBlockapp/mine/mineClient.ts、app/mine/minePow.worker.ts— 浏览器侧挖矿调度与 Web Workersrc/lib/block.ts—fromJSON将交易还原为Transaction,保证持久化后 Merkle 一致src/lib/config.ts—POW_*与出块参数
7. 升级与旧数据
若你曾使用旧版「单 SHA256 + 十六进制前缀 0」的链数据,升级到当前 PoW 后无法通过校验。请删除 data/chain-state.json(或改用新的 CHAIN_DATA_PATH)后重启节点。
8. 与比特币主网的差异(面试收尾句)
主网用 compact nBits 编码目标、区块头字段演进更复杂;本演示用位移目标 + 固定头布局,重在对齐 Hash256、LE 比较、Merkle、展示反序四条主线,读 BIP / 比特币开发者文档时可逐项对照「我们还简化了什么」。
交易池与「为什么要再挖一次矿」
交易池(Mempool)是「已验证通过、等待被打包」的交易集合。提交转账 API 成功,只表示进入了池子,账本(链)尚未更新。
只有挖矿成功、生成新区块,这些交易才作为区块数据的一部分成为不可更改的历史(在本 Demo 里没有分叉选择,更简单)。所以流程是:提交交易 → 再挖矿 → 余额变化可在链上体现。
P2P 网络在做什么
单进程 Demo 里,链存在服务端内存(并可写 JSON 文件)。若开第二个节点(如 pnpm dev:node2),两个进程各自有一份状态,通过 WebSocket 交换「最新块」「全链」「交易池」消息,尝试对齐更长的合法链。见 src/lib/p2pServer.ts。
这与比特币的 P2P 协议复杂度差很远,但能帮助理解「节点之间要同步什么数据」。
数据存在哪里
- 链状态:默认写入项目下
data/chain-state.json(可通过CHAIN_DATA_PATH、CHAIN_PERSIST配置)。重启 Node 可恢复。 - 浏览器里的钱包草稿:
localStorage,仅本机、本域名,与链上账户体系无关。
后端 API 一览
与页面按钮对应的主要接口(详见仓库根目录 README.md 的表格与 curl 示例):
GET /api/state:给浏览器聚合状态;加?includeBlocks=false时不返回区块数组(链上浏览页轮询用,体积小)。POST /api/wallets:生成密钥对。POST /api/balance:推荐;body 为 JSON,例如{ "address": "你的公钥hex" }。POST /api/transactions:提交签名交易。GET /api/mine/work?minerAddress=...:下发当前挖矿任务(交易池快照 + 奖励交易 + 难度与时间戳)。持续挖矿时每一轮都会重新请求。POST /api/mine:body 为{ "minerAddress", "block" },提交客户端已算好 PoW 的完整区块 JSON,服务端校验后上链。无单独的「持续挖矿」接口,多轮即多次调用上述两接口。
代码地图(该读哪些文件)
按「从外到内」的顺序阅读,通常最省力:
- 页面与交互:
app/**/page.tsx(挖矿相关另有app/mine/mineClient.ts、minePow.worker.ts)、src/components/SiteNav.tsx、src/context/WalletContext.tsx(跨页钱包状态)。 - HTTP 入口:
app/api/**/route.ts。每个文件导出GET/POST等函数,即 REST 端点。 - 单例链与持久化:
src/lib/chainService.ts(内存里的唯一Blockchain实例)、src/lib/persistence.ts(读写 JSON)。 - 链核心:
src/lib/blockchain.ts、src/lib/block.ts、src/lib/transaction.ts、src/lib/powBytes.ts、src/lib/pow.ts、src/lib/merkle.ts、src/lib/sha256d.ts、src/lib/hash.ts(stableStringify)、src/lib/config.ts、src/lib/mineTypes.ts。 - 密码学钱包工具:
src/lib/wallet.ts(elliptic secp256k1)。 - P2P:
src/lib/p2pServer.ts、进程入口src/instrumentation.ts。 - 自动化测试:配置见 Jest;若有针对链逻辑的测试,可直接 new
Blockchain断言,无需经过 HTTP。
从点击按钮到链状态变化
以「挖矿」为例,一条典型路径是:
- 浏览器
/mine页先GET /api/mine/work?minerAddress=...,由app/api/mine/work/route.ts返回任务 JSON。 - 页面在本机运行
mineBlockPowU8(单线程)或 Web Worker 分片(多核),得到hash与nonce;与「挖矿」页上选的单次/持续模式一致。 - 再向
POST /api/mine提交{ minerAddress, block };Next.js 交给app/api/mine/route.ts。 - 处理器
getBlockchain()取全进程唯一链实例,执行submitMinedBlock:校验通过后更新chain、清空transactionPool,并touchPersistence()。 broadcastLatest()通过 WebSocket 同步其他节点(若启用 P2P)。- 接口返回
201与新块 JSON;/explorer轮询/api/state等即可看到高度与池子变化。
「转账」路径仍是:提交交易只进池子,直到某次「挖矿」成功把池内交易(与任务快照一致)写入区块。
环境变量与可调参数
在启动命令前设置环境变量即可(Windows PowerShell 示例:$env:MINE_REWARD=100; pnpm dev)。
| 变量 | 作用(初学者理解) |
|---|---|
MINE_REWARD | 每挖出一个块,奖励交易给矿工的数额(默认常是 50)。 |
DIFFICULTY_ADJUST_INTERVAL | 每隔多少个块检查一次出块速度,并尝试调整难度。 |
BLOCK_GENERATION_INTERVAL | 期望的出块间隔(秒),用于难度调整的参考,不是强制休眠。 |
P2P_PORT / PEERS / ENABLE_P2P | 控制是否起 WebSocket、监听端口、主动连接哪些节点。 |
CHAIN_PERSIST / CHAIN_DATA_PATH | 是否把链写入磁盘、文件路径(多节点时请用不同路径避免互抢)。 |
POW_GENESIS_DIFFICULTY | 创世块 PoW 难度(参与 T = (2^256-1) >> difficulty)。改动后须清空旧链数据,见 PoW 算法说明 §7。 |
POW_MIN_DIFFICULTY / POW_MAX_DIFFICULTY | 自动难度调整时的上下限,防止过易或过难。 |
本演示与真实公链的差距
便于初学者建立正确预期,避免把 Demo 当成生产系统:
- 无真实经济安全模型、无大量节点博弈;P2P 与共识极简。
- 无私钥由用户硬件/钱包托管的最佳实践;验签流程为教学简化。
- 无 UTXO、无 SPV 轻客户端的默克尔证明路径、无智能合约执行环境(本链仅在 PoW 头内使用 Merkle 根打包
data)。 - 难度目标为教学用位移模型(非主网
nBits);奖励与调整参数与主网不可类比。
延伸学习建议
- 阅读 Bitcoin白皮书 中与「链结构、工作量证明、时间戳」相关的章节(概念层面)。
- 了解 以太坊账户模型与UTXO 模型的对比(本 Demo 更接近简化的账户余额扫描)。
- 学习 Next.js App Router 官方文档中的 Route Handlers、Server/Client Component 边界。
- 在浏览器开发者工具里对
src/lib/powBytes.ts(或打包后的对应模块)下断点,跟一轮mineBlockPowU8;或在 Node 侧跟createGenesisBlock里的mineBlockPow。对照 《PoW 算法说明》 与根目录README.md第 12 节;改MINE_REWARD、POW_*观察/explorer与余额变化。