我想到了解某事的最佳方式是尝试从头开始建立它,看看会发生什么。
对于今年的JS @ PayPal会议,我认为这将是一个有趣的术语,即在30分钟的谈话中举行的码代码比特币。我从未尝试过一个现场编码的谈话,所以我想我会扔在深渊!
这是我在会议之前录制的全长视频,深入解释我所做的一切:
比特币是蒙娜丽莎,激光眼睛。我建造的是......右边的东西。
“块”就像包含各种数据的对象,它在比特币网络上传播,因此每个人都有相同的状态和事实源,包括相同的交易。
每个块都被加密绑定到上一个块,形成了一系列具有新数据和新事务的块。
每个块包含事务,这意味着硬币可以在网络上的不同演员之间传输。鲍勃送Jane 5硬币,Jane发送哈利3个硬币,等等。我们可以通过查看所有历史块来跟踪余额。
块需要被生成“开采”,这需要一定的计算能力和运气。这确保了网络:要破解或攻击比特币,您需要比网络上的其他人更多的计算能力。
任何成功挖掘街区的人都赚取了固定数量的硬币。这鼓励矿工继续挖掘。
交易也会出现可选费用。这些费用也由街区的矿工收集,以及块奖励。所以挖掘新块有一种双重激励。
挖掘块随着时间的推移逐渐逐渐变得更加难以更加困难或更容易,以确保每个新块以大约恒定的速率开采。保持块速率常量很重要:块时间太高,网络将无法使用。块时间太低,网络可以轻松地用冲突的块垃圾邮件。
网络中的每个节点和矿工都同意所有这些规则,以及块的格式。这使得创建虚假块非常困难,或者发送花费多次硬币的交易 - 即使您确实有大量的计算能力。
首先,我提出了块链的公共接口的某种类型。
需要发件人,接收者,金额和用户定义费用的事务。费用将确定交易在网络上的优先事项。
block_time是我们在生成的每个块之间的目标。比特币瞄准每个街区之间的10分钟;我们将要去1秒钟,所以我们可以在本地迅速运行这个并尝试一下。
Initial_Reward是每个街区的硬币的奖励。这种奖励涉及谁开采街区。这种奖励将随着时间的推移减半,并将鼓励更多矿工试图排雷,流程交易,并将网络保持安全和免疫从双重花费或其他攻击。
right_halving_schedule是此奖励中的一半之前的块数。比特币通常每四年减半。我们的目标是每20个块,相当于每20秒。
block_size_limit是可以包含在任何块中的最大交易数。我们将将其设置为10,但比特币的实际限制要大得多,并且基于块的MB大小。
genesis_block定义了初始硬编码块。所有其他块都将具有此成因块作为其祖先。我们现在将颁发此街区到Satoshi,我们将定义默认难度和初始块奖励。
当我们在代码中使用它们时,每个人的含义都应该变得更加清晰。
接下来,让我们排除块链。这将是我们的“数据库”或所有块和事务的记录系统。
您可能认为像比特币一样的块链是一个链接列表,其中每个块是列表中的节点。
这棵树可能有许多竞争的分支。我们如何知道哪个分支信任?简单的!我们选择最长的分支,因为这是采取最多计算努力的分支。分支越长,我们可以拥有的更有信心,没有其他分支可以超越它。
因此,我们在内存中使用树数据结构来表示我们的块链,因为这允许我们在任何给定时间轻松计算最长分支。
我们通过Genesis_Block作为这棵树的根,因为这将永远是第一个块。硬编码这使得事情更容易,并没有提供特定的缺点。
接下来,让我们写一些代码来挖掘一个新的块。这将是当天最具挑战性的运动。
现有的区块链,此处以root代表。我们需要在此处调用getLongestbranchnode()来查找当前的“真实”块链,我们希望在顶部挖掘另一个块。
矿工的Publickey。这需要成为块的一部分,因此网络上的其他人都知道谁将获得挖掘块的奖励。
交易列表。如果我们没有任何交易以发布到网络,则挖掘块并不有多点。
大部分都很容易。我们将矿工公钥,ID,父ID,时间,索引和事务插入到新块中。
我们将重新验证此处的交易以确保他们正确签名。每个其他节点都会在接受新块之前做同样的事情,我们希望最大限度地提高我们的街区的机会,而不会让任何人拒绝拒绝它的好借口。
我们希望每20个街区减半奖励。因此,我们计算当前块索引的当前奖励将是什么,假设奖励在1024开始,并且每20个块减半。这里有一些快速的数学来实现这一点,通过检查当前块索引是否已被20分开,如果是,则为奖励减半。否则我们保持同样的奖励。
实际上,比特币每年2016块或大约每2周重新计算一次难度一次。为简单起见,我们将重新计算每个新块的难度,以便我们可以实时演示。
这是一种计算困难的极其天真的方式,但它适用于这个例子。
如果该块花了太少的时间来生成,我们将增加1的困难。
这意味着我们不断重新校准创建下一个块的困难,基于我们需要多长时间创建上一个块。这使得块以大致常数的速率创建,无论有多少矿工都试图挖掘新块。
您拥有的矿工越多,“哈希汇率”越高,这是一个衡量世界各地使用多少计算权来挖掘新硬币的计算力。
这意味着哈希速率/难度应始终是恒定数,这使每个新块的平均时间保持相对恒定。
一旦我们创建了一个新块,我们仍然没有完成。如果难度是正确的,则基于上一个块,网络将仅接受该块。
我们在这里以一种非常天真的方式检查这个困难。我们哈希拦截,将哈希转换为一个数字,如果数字因难度均匀地划分,而且没有剩余的,我们允许它通过。
这意味着随着难度越来越高,块将传递难度检查的较小且较小的机会,我们必须越来越多地猜测生成一个新块。这需要更多的计算能力,更多的时间。
相反也是如此。如果难度编号越来越低,则新块将通过难度检查有更高的机会,并且生成新块将花费更少的猜测。这取得了更少的计算能力,更少的时间。
这种难度检查的简单版本有效,因为它更有可能是由2的低数字来分隔,比2比17更高的数字。因此,更容易生成其哈希被删除的块,并且更难生成一个块,其哈希恰好可被17可分离。
当新块传播到网络时,我们需要能够将其添加到我们的树数据结构中。
首先,我们要验证块是否与其哈希匹配,并且块内的所有事务都已正确签名。
然后,我们希望验证块通过所有正确的规则。目前,我们只是检查难度匹配。实际上,我们想检查块的每个字段并完全验证。我们不能相信网络上的任何其他节点,因此我们希望验证每件事。
如果块无效,此时它将被节点拒绝。由于整个网络遵循完全相同的分散规则,因此每个节点都将接受完全相同的有效块,并拒绝完全相同的无效块,而无需核心权限告诉他们哪些块有效。
因此,攻击者不仅需要花费大量的电力来用于运行计算来创建新块 - 它们也会非常困难地弯曲网络上的每个其他节点设置的规则,以便完整于A的完整性堵塞。
最后,我们只需要找到新块指定的父块,并将新块作为子节点添加到该父节点。这将现有树与新块扩展,因此我们可以在以后使用它来计算每个人的当前余额。
我们需要做的最后一件事是采取以下一切:
基于每个块中的这些值计算链上每个钱包的最终余额。
我们通过在任何特定时间找到最长的链,并在所有余额,费用和阻止奖励中找到最长的链条。我们需要执行此操作的所有数据都包含在每个块中。
您将注意事务是零和游戏。从一个帐户中减去每个量并添加到另一个帐户。每笔费用也一样。因此,交易只能使用已经存在于网络上的硬币,它们无法创建或销毁硬币。 (当然,您可以将硬币发送到死地址或在划船事故中丢失私钥。这不会破坏硬币,它只使它们永久地无法进入)。
然而,奖励不是一个零和游戏 - 我们正在向网络添加新硬币,而不会带走它们。这实际上是所有新比特币的来源。这也是比特币最终到达固定帽的情况。最终,块奖励一半,一半,减半到零。此时,没有任何新的比特币可以从系统中添加或减去,我们最终有2100万的固定帽。
此减半计划提供比特币,其S2F值,其价格每四年将其价格上升像发条一样。只是在开玩笑。
我们现在完成了数据结构/数据库的一端。我们有一个块链,我们能够创建新的块,将它们添加到树上,并根据树中最长的块链计算每个钱包的最终余额。
比特币不仅仅在单个计算机或服务器上运行。我们需要一种连接点对点网络的方法,与他人共享这些块,并将新事务发布到网络。所以我们需要一个节点!
同样,我们需要为我们的节点定义某些类型和存根,因此我们有一些东西可以对代码进行编码。
首先,我们将实例化我们需要运行该节点的内容:
我们需要一个加密密钥对。我们将使用公钥来识别节点/钱包/矿工,以及私钥以签署任何新事务。每个矿工,节点,钱包或任何其他actor都使用此公钥来识别网络上。私钥帮助他们证明了他们的身份。
我们需要连接到另一个节点的对等网络。为方便起见,我们在此处使用了假网络,因此我们可以广播和倾听活动。实际上,比特币将通过互联网连接到互联网上的真实点对点网络,而无需任何集中式服务器。
当然,我们需要块链本身,我们已经在上面实施了。我们将在此处实例化该块链,因此节点可以使用它来存储新块和挖掘新块并获得当前的钱包余额。
我们需要一个想象力。这将要存储传入的事务,直到可以添加到块 - 因为它需要一些时间来生成一个块,我们需要某处以保持交易的平均时间。
我们需要能够侦听从网络上的任何其他节点发送的事务:
此时,我们只是将交易推入Mempool。我们可以在这一点上验证它 - 但我们已经在将交易中纳入一个块之前这样做,所以我们现在会跳过它。
Mempool是新交易的暂存区域。他们不是最终的或'真实',而且没有人应该依靠这些Mempool交易看他们是否已付款。为了成为最终的,交易需要包括在新开采的块中。
为此,我们使用我们的私钥创建签名消息。这包含了发送者地址,接收者地址,我们要发送的金额,以及我们要附加到事务的费用(在网络上提供优先级)。
只有对公钥(公共地址)的相应私钥的人只能签署这样的事务。如果没有您的私钥,没有人可以代表您发送交易。当然,私钥永远不会与网络上的其他人分享。
一旦我们创建并签署此交易,仍有所待的所有待办事项就是将其播放到网络,因此可以由侦听的任何其他节点拾取,并添加到其Mempool中。
可以由网络上的任何其他节点创建块,这是大多数块将来自现实世界的位置。因此,我们需要能够倾听其他人的新块,并将它们添加到我们的区块链中。
我们已经定义了验证块在AddBlock中的块的完整性逻辑,因此我们只是在此处调用该方法。如果该块被拒绝,没有大不了的事,我们将继续像往常一样继续。
我们也将打破当前的Mempool。实际上,我们可能只希望从新块中的Mempool中过滤输出交易,但我们将在此处进行快捷方式以简化。
为避免杀死我的浏览器,我在异步循环中运行它每500ms的运行。在循环中,我们从Mempool中拔出有限数量的事务(基于我们之前定义的块大小限制),我们试图挖掘一个包含这些事务的新块。
实际上,我们通常会搜索具有最高费用的交易,因此我们可以获得挖掘块的最大奖励。在这里,为简单起见,我们只是按照它们被添加到Mempool的顺序服用它们。
如果我们在挖掘一个新块中成功,我们将该块发布到网络以被其他节点消耗。
如果其他人在我们这样做之前挖掘了一个新的块,我们会重新开始,并尝试在该块之上挖掘一个新的块 - 由于CreateBlock将始终尝试在任何给定时间的最长块链中挖掘。
运行此项目时,我的合作伙伴Jill创建了一个很棒的UI,可以帮助您可视化发生的事情,因为节点挖掘新块并来回发送事务:
我希望这很有用,帮助您了解用于电力比特币的一些原则! 请记住,这是真正的比特币节点和挖掘软件的巨大简化。 它仅供学习目的,而且更多。