Skip to content

中本聪谈 SPV 纯客户端模式的实现——致迈克·赫恩(2010 年 12 月 29 日) ​

首次公开证据

打开首次公开证据
查看 1 个交叉存档

我一直在开发简化支付验证的 Java 实现,目标是构建一个能在 Android 手机上运行的客户端。因此,我一直在深入思考存储需求和比特币的可扩展性,这引出了一些论文没有回答的问题(也许以后可以出一个新版论文,因为我认为其中有些内容现在已经过时了)。

论文中设想的简化支付验证,是让你像发送到 IP 地址时那样直接接收交易——不过没人这么用——或者由一个节点按公钥索引所有交易,而你可以像从邮件服务器下载邮件那样下载它们。

相反,我认为纯客户端节点应该接收完整区块,以便从中扫描属于自己的交易。它们不需要存储或索引这些区块。首次下载时,它们只需要下载区块头,因为在程序第一次运行之前不可能收到任何付款(0.3.18 中增加了下载区块头的命令)。从那以后,它们下载完整区块(但只存储区块头)。

纯客户端模式的代码大部分已经实现。github 上有一个包含它的功能分支,我也在这条消息中附上了补丁。

以下是更多相关说明:

“这是目前为止我的客户端模式实现。纯客户端模式只记录区块头,不使用 tx 索引。它不能生成新币,但仍然可以发送和接收交易。它还没有完全达到供最终用户使用的完成状态,不过这无关紧要,因为如果没有启用 fClient,它就完全不会产生任何影响。目前,它主要是一份文档,展示了纯客户端重新实现者所需的功能切分边界。

在 fClient=true 的情况下,我只测试了仅下载区块头的首次下载。

补充一点背景。CBlockIndex 包含区块头的全部信息,因此,为了只使用区块头运行,我像往常一样维护 CBlockIndex 结构。nFile/nBlockPos 为空,因为完整区块没有记录到磁盘上。

无需删除中间的 blk*.dat 就能在开启/关闭客户端模式之间平稳切换的代码尚未实现。大体上只需让非客户端模式的 LoadBlockIndex 忽略区块位置为空的区块索引条目。这样它就会以完整区块的形式重新下载这些区块。切换回客户端模式没有问题,即使完整区块在那里,它也不介意。

如果首次区块下载变得太漫长,我们会希望把客户端模式作为一种选项,让新用户能够快速开始使用。如果能平稳关闭客户端模式,他们以后可以关闭它,让程序下载完整区块,以便开始生成新币。不过他们更应该直接使用 getwork 矿工加入矿池。

纯客户端的重新实现完全不需要实现 EvalScript,或者最多只需实现标准交易模板所用的五种操作。”

具体来说,比特币中有各种神奇数字,而代码和论文都没有解释它们从何而来。例如,在发行 2100 万枚币后通胀便会停止。这一数字必然是以某种方式得出的,但我看不出是怎么得出的。

有根据的猜测,而且算下来是整数。我想要一个这样的数值:如果它非常流行,不会太低;如果它并不流行,也不会太高。

另一个数字是 10 分钟的区块目标时间。我理解选择这个时间是为了让交易能够在网络中传播。然而,像 BGP 这样现有的大型 P2P 网络可以在不到 1 分钟内把新数据传播到全世界。

如果传播需要 1 分钟,那么猜 10 分钟是合理的。这样节点只会损失 10% 的工作量(1 分钟/10 分钟)。如果因延迟而浪费的 CPU 时间所占比例更大,可能会存在一些我还没想到的弱点。攻击者不会受到延迟影响,因为他在连接自己的区块,所以会占据优势。区块链会因延迟而更频繁地暂时分叉。

我感兴趣的最后一个数字是 500kb 的区块大小限制。根据维基百科,仅 Visa 在 2009 年就处理了 620 亿笔交易。换算下来,平均每秒有 2000 笔交易,因此峰值速率可能约为其两倍,即 4000 笔交易/秒。按 10 分钟的区块目标时间计算,在峰值时,一个区块可能需要容纳 240 万笔交易,这根本塞不进 500kb。这个 500kb 是会随着时间推移从官方客户端中逐步移除的临时限制,还是某种更根本性的限制?

等实际使用量接近这一限制,并确认一切运行正常后,可以分阶段提高限制。

最终,当我们有了纯客户端实现时,区块链的大小就不会那么重要了。在那之前,由于所有用户在开始时仍然必须下载整条区块链,如果能把它维持在合理大小会更好。

在交易量非常高的情况下,网络节点会趋于集中,会出现更多矿池挖矿和 GPU 矿场,而用户会运行纯客户端。通过优化和并行化方面的开发,它可以继续扩展。

无论软件当前的容量是多少,它都会以摩尔定律的速度自动增长,即每年约 60%。

英文原文 ​

展开英文原文

I have been working on a Java implementation of the simplified payment verification, with an eye to building a client that runs on Android phones. So I've been thinking a lot about storage requirements and the scalability of BitCoin, which led to some questions that the paper did not answer (maybe there could be a new version of the paper at some point, as I think aspects of it are now out of date).

The simplified payment verification in the paper imagined you would receive transactions directly, as with sending to IP address which nobody uses, or a node would index all transactions by public key and you could download them like downloading mail from a mail server.

Instead, I think client-only nodes should receive full blocks so they can scan them for their own transactions. They don't need to store them or index them. For the initial download, they only need to download headers, since there couldn't be any payments before the first time the program was run (a header download command was added in 0.3.18). From then on, they download full blocks (but only store the headers).

Code for client-only mode is mostly implemented. There's a feature branch on github with it, also I'm attaching the patch to this message.

Here's some more about it:

"Here's my client-mode implementation so far. Client-only mode only records block headers and doesn't use the tx index. It can't generate, but it can still send and receive transactions. It's not fully finished for use by end-users, but it doesn't matter because it's a complete no-op if fClient is not enabled. At this point it's mainly documentation showing the cut-lines for client-only re-implementers.

With fClient=true, I've only tested the header-only initial download.

A little background. CBlockIndex contains all the information of the block header, so to operate with headers only, I just maintain the CBlockIndex structure as usual. The nFile/nBlockPos are null, since the full block is not recorded on disk.

The code to gracefully switch between client-mode on/off without deleting blk*.dat in between is not implemented yet. It would mostly be a matter of having non-client LoadBlockIndex ignore block index entries with null block pos. That would make it re-download those as full blocks. Switching back to client-mode is no problem, it doesn't mind if the full blocks are there.

If the initial block download becomes too long, we'll want client mode as an option so new users can get running quickly. With graceful switch-off of client mode, they can later turn off client mode and have it download the full blocks if they want to start generating. They should rather just use a getwork miner to join a pool instead.

Client-only re-implementations would not need to implement EvalScript at all, or at most just implement the five ops used by the standard transaction templates."

Specifically, BitCoin has a variety of magic numbers and neither the code nor the paper explain where they came from. For example, the fact that inflation ceases when 21 million coins have been issued. This number must have been arrived at somehow, but I can't see how.

Educated guess, and the maths work out to round numbers. I wanted something that would be not too low if it was very popular and not too high if it wasn't.

Another is the 10 minute block target. I understand this was chosen to allow transactions to propagate through the network. However existing large P2P networks like BGP can propagate new data worldwide in <1 minute.

If propagation is 1 minute, then 10 minutes was a good guess. Then nodes are only losing 10% of their work (1 minute/10 minutes). If the CPU time wasted by latency was a more significant share, there may be weaknesses I haven't thought of. An attacker would not be affected by latency, since he's chaining his own blocks, so he would have an advantage. The chain would temporarily fork more often due to latency.

The final number I'm interested in is the 500kb limit on block sizes. According to Wikipedia, Visa alone processed 62 billion transactions in 2009. Dividing through we get an average of 2000 transactions per second, so peak rate is probably around double that at 4000 transactions/sec. With a ten minute block target, at peak a block might need to contain 2.4 million transactions, which just won't fit into 500kb. Is this 500kb a temporary limitation that will be slowly removed over time from the official client or something more fundamental?

A higher limit can be phased in once we have actual use closer to the limit and make sure it's working OK.

Eventually when we have client-only implementations, the block chain size won't matter much. Until then, while all users still have to download the entire block chain to start, it's nice if we can keep it down to a reasonable size.

With very high transaction volume, network nodes would consolidate and there would be more pooled mining and GPU farms, and users would run client-only. With dev work on optimising and parallelising, it can keep scaling up.

Whatever the current capacity of the software is, it automatically grows at the rate of Moore's Law, about 60% per year.

为历史保存,为中文读者整理。每篇档案均附来源与英文原文。