Learn about Bitcoin and the genius behind the blockchain concept as we delve into Ethereum
本文原版英文出处点击这里,可能需要,本文使用谷歌翻译,有错误或不准确的地方欢迎大家批评。
比特币在2009年让世界大吃一惊,并普及了分散式安全货币交易的想法。 然而,它背后的概念可以扩展到远远超过数字货币。 以太坊试图做到这一点,将分散交易的权力与图灵完整的合同制结合起来。 请继续阅读,了解它的工作原理!
这是3系列的第一部分。
2009年,一位笔名叫Satoshi Nakamoto的人发布了一篇标志性的比特币白皮书。 比特币准备好解决一个非常具体的问题:如果在没有认证中心为每一笔交易做仲裁,出现双重支出问题怎么解决呢?
公平起见,在比特币发布之前,这个问题一直存在于研究人员的脑海里。 但是,在以前的解决方案具有研究质量的地方,比特币成功地为大众带来了一个可用于生产的设计。
最早提及直接应用于比特币的一些概念是从20世纪90年代开始的。 2005年,计算机科学家Nick Szabo介绍了Bitcoin的前身Bitgold的概念,分享了它的许多概念。 Bitgold和比特币之间的相似性足以让一些人推测他可能是Satoshi Nakamoto。
双重支出问题是交易处理的一个特例。 根据定义,交易必须发生或不发生。 此外,一些(但不是全部)交易必须提供在其他交易之前或之后发生的担保(换句话说,它们必须是原子的)。 原子性导致了订购的概念:交易在其他交易之前或之后发生或不发生。 缺乏原子性恰恰是双重支出问题的问题:“支出”或从支出方A向接收方B发送资金必须发生在特定时间点以及任何其他交易之前和之后。 如果情况并非如此,那么可以在分开但同时进行的交易中花费不止一次的资金。
谈到日常货币运作时,交易通常由银行进行仲裁。 当用户登录他或她的个人银行系统并执行电汇时,银行确保过去和未来的操作都是一致的。 虽然这个过程对于外部人来说似乎很简单,但它实际上是一个涉及清算程序和结算要求的相关流程。 事实上,其中一些程序考虑了双重支出情况的可能性以及在这些情况下应该采取的措施。 这些相当复杂的过程不应该让人吃惊,这导致了计算机科学研究人员的目标在很大程度上似乎不可能超越延迟。
因此,任何融资交易系统必须解决的主要问题是“如何在没有中央当局的情况下订购交易”。 此外,过去的交易顺序是否有效也不容怀疑。 为了货币系统的成功,任何一方都不可以修改以前的交易。 换句话说,过去交易的“审批流程”也必须到位。 这正是比特币区块链系统旨在解决的问题。
如果您有兴趣阅读必须达成共识的系统及其面临的问题,那么拜占庭将军问题的论文是一个好的开始。
虽然在这一点上区块链的概念是模糊的,但在详细讨论它之前,让我们回顾一下区块链试图解决的问题。
公钥加密是处理事务验证其中一个问题的重要工具。公钥密码学依赖于非常具体的一组问题的非对称数学复杂性。公钥密码学中的不对称体现在两个密钥的存在:公钥和私钥。这些键为了特定目的而串联使用。尤其是:
创建一组可验证的事务的关键是操作签署数据。让我们看看如何通过使用公钥密码体系来验证一个非常简单的事务。
假设有一个账户持有人A拥有50个加密货币。作为之前交易的一部分,这些加密货币被发送给他。账户持有人A现在想要将这些加密货币发送给账户持有人B.B和任何其他想要仔细检查该交易的人必须能够确认实际上是谁将加密货币发送给乙。此外,他们必须能够看到B兑现了他们,而没有其他人。显然,他们也应该能够找到相对于其他交易发生的确切时间点。但是,在这一点上,我们不能这样做。幸运的是,我们可以做所有其他事情。
举个简单的例子,假设交易中的数据只是前一个交易(一个给予A50加密货币的交易)的标识符,当前所有者的公钥和前一个所有者的签名(确认他或她首先将这些代币寄给A):
{
"previous-transaction-id": "FEDCBA987654321...",
"owner-pubkey": "123456789ABCDEF...",
"prev-owner-signature": "AABBCCDDEEFF112233..."
}
当前交易的加密货币数量是多余的:它与其上一笔交易的金额相同。
证明A是这些加密货币的所有者已经存在:他或她的公钥已嵌入到交易中。 现在无论采取什么行动都必须以某种方式进行验证。 一种方法是将信息添加到交易中,然后生成新的签名。 由于A想要向B汇款,所增加的信息可能只是B的公钥。 在创建这个新事务后,它可以使用A的私钥进行签名。 这证明了A,并且只有A参与了创建这项交易。 换句话说,在基于JavaScript的伪代码中:
function aToB(privateKeyA, previousTransaction, publicKeyB) {
const transaction = {
"previous-transaction-id": hash(previousTransaction),
"owner-pubkey": publicKeyB
};
transaction["prev-owner-signature"] = sign(privateKeyA, transaction);
return transaction;
}
值得注意的是,我们已将事务ID定义为其二进制表示的哈希值。换句话说,一个事务ID就是它的哈希(在这一点上,使用未指定的哈希算法)。由于我们稍后将解释的几个原因,这很方便。目前,这只是一种可能的做事方式。
让我们分开代码并逐步写下代码:
因此,新交易中的签名在新交易和旧交易之间创建了可验证的链接。新交易明确指向旧交易,新交易的签名只能由旧交易的私钥持有者生成(旧交易通过owner-pubkey字段明确告诉我们这是谁)。所以旧事务持有可以使用它的人的公钥,新事务持有接收它的人的公共密钥以及使用私人密钥创建的签名。
如果这一点看起来很难理解,那么可以这样想:它全部来源于这个简单的表达式:使用公钥可以验证用私钥签名的数据。没有什么更多。消费者只需签署一段说明“我是交易ID XXX所有者”的数据,我在此将其中的每一枚硬币都发送给B“。 B和其他任何人都可以检查它是否是A,只有A,谁写的。为此,他们只需访问A的公钥,该公钥在交易中可用。从数学上保证除了A的私钥以外不能使用与A的公共密钥串联的密钥。所以通过简单地访问A的公钥,任何人都可以看到A是谁给B发钱。这使B成为这笔钱的合法所有者。当然,这是一个简化。有两件事我们没有考虑过:谁说这50个硬币在哪里A的财产(或者换句话说,A确实拥有一些随机交易的所有权,他或她是合法的所有者? B的硬币(在其他交易之前还是之后?)。
如果您有兴趣了解更多关于公钥密码学背后的数学知识,可以参阅JWT手册第7章的代码示例简单介绍。
在讨论订购问题之前,我们先来解决硬币生成的问题。在我们的例子中,我们假定A是50个硬币的合法所有者,因为给A他或她的硬币的交易只是像任何其他交易一样建模:它在所有者字段中拥有A的公钥,并且它指向先前交易。那么,谁把这些硬币给了A?更重要的是,谁把硬币交给了另一个人?我们只需要遵循交易链接。每笔交易指向链中的前一笔交易,那么这50个硬币从哪里来?在某些时候,链条必须结束。
为了理解这是如何工作的,最好考虑一个实际案例,让我们看看比特币如何处理它。比特币中的硬币有两种不同的创建方式。首先是独特的起源区块。起始块是一个特殊的硬编码事务,指向其他以前的事务。这是系统中的第一笔交易,具有特定数量的比特币,并指向属于比特币创建者Satoshi Nakamoto的公钥。这笔交易中的一些硬币被发送到某些地址,但他们从来没有真正使用过这么多。比特币中的大部分硬币来自另一个地方:它们是一种诱因。正如我们将在下一节中看到的关于排序事务一样,用于执行此操作的方案需要网络中的节点以计算形式贡献工作。为了激励更多的节点贡献计算,一定数量的硬币在成功完成任务时被赋予贡献节点。这种激励主要导致生成新硬币的特殊交易。这些交易也是交易链接以及创世区块的终点。比特币中的每一枚硬币都可以追溯到这些激励措施中的一种或起源块。许多加密货币系统采用这种硬币发生模式,每种模式都有自己的细微差别和对硬币创造的要求。在比特币中,按照设计,随着更多的硬币被创建,更少的硬币被奖励为奖励。最终,硬币的创造将停止。
比特币给现有加密货币计划带来的最大贡献是使交易成为原子的分散方式。 比特币之前,研究人员提出了不同的方案来实现这一点 其中一个计划是一个简单的投票系统。 为了更好地理解比特币方法的魔力,最好探索这些尝试。
在投票系统中,每个事务都由执行它的节点广播。 因此,继续A向A发送50个硬币的例子,A准备一个新的交易指向给他或她那50个硬币的交易,然后将B的公钥放入其中并使用他或她自己的私钥 (A’s)签名。 然后该事务被发送到网络中由A所知的每个节点。 假设除了A和B之外,还有三个其他节点:C,D,E。
现在让我们假设A实际上是一个恶意节点。 尽管看起来A想要发送B 50个加密货币,但同时A播放这个交易,它也播放一个不同的一个:A将这50个相同的加密货币发送给C.
const aToB = {
"previous-transaction-id": "FEDCBA987654321...",
"owner-pubkey": "123456789ABCDEF...", // B
"prev-owner-signature": "..."
};
const aToC = {
"previous-transaction-id": "FEDCBA987654321...",
"owner-pubkey": "00112233445566...", // C
"prev-owner-signature": "..."
};
请注意previous-transaction-id指向同一个事务。 A同时将此事务发送到网络中的不同节点。 谁得到50个加密货币? 更糟糕的是,如果这50枚加密币被用来换取东西,A可能会从B和C获得货物,尽管其中一枚加密货币不会获得加密币。
由于这是一个分布式网络,每个节点在决策中应该有一定的权重。 让我们考虑一下前面提到的投票系统。 现在每个节点都应该对是否选择哪个事务进行投票。
Node Vote
A A to B
B A to B
C A to C
D A to C
E A to B
每个节点都投了一票,A到B被选为应该首先进行的交易。显然,这使得A到C交易无效,指向与A到B相同的硬币。看起来这个解决方案有效,但只是表面上看起来如此。让我们看看为什么。
首先,我们考虑一下情况A与其他节点勾结。 E投了一个随机投票还是以某种方式激励A选择了另一个交易?没有真正的方法来确定这一点。
其次,我们的模型不考虑交易传播的速度。在一个足够大的节点网络中,一些节点可能会在其他节点之前看到一些事务。这会导致投票不平衡。无法确定未来的交易是否会使已到达的交易无效。更重要的是,无法确定刚刚到达的交易是在等待投票的其他交易之前或之后进行的。除非交易被所有节点看到,否则投票可能是不公平的。更糟糕的是,一些节点可能会主动延迟事务的传播。
最后,恶意节点可能会注入无效事务以导致目标拒绝服务。这可以用来支持某些交易而不是其他交易。
投票不能解决这些问题,因为它们是系统设计的固有特征。不管用什么来支持一种交易,都不能选择。只要一个节点或一组节点能以某种方式支持某些事务而不是其他事务,系统就无法工作。正是这个元素使得加密货币的设计变得如此艰辛。需要一个天才的中风来克服这样一个深刻的设计问题。
在分布式系统中恶意节点投票的问题最为人所知的是拜占庭将军问题。虽然有数学证明,只要存在一定比例的非恶意节点,就可以克服这个问题,但这并不能解决加密货币的问题:节点添加便宜。因此,不同的解决方案是必要的。
物理对救援
无论使用哪种系统来确保某些交易优于其他交易,节点都不应该能够100%确定地选择哪些交易。只有一种方法可以确定是这种情况:如果节点能够做到这一点在物理上是不可能的。节点添加起来很便宜,因此无论恶意用户控制多少节点,他或她都很难将其用于他或她的优势。
答案是CPU的能力。如果订购交易需要一定量的工作和可验证的工作,那么最初的工作很难执行,但验证起来很便宜。从某种意义上说,密码学的工作原理是相同的:某些相关操作在计算上不可行,而其他操作便宜。加密数据在暴力加密密钥旁边很便宜。从私钥中获得公共密钥很便宜,而以相反方式实现则是不可行的。散列数据很便宜,但通过修改输入数据找到具有特定要求的散列却不是。这是比特币和其他加密货币所依赖的主要操作,以确保平均而言,任何节点都无法超越其他节点。让我们看看这是如何工作的。
首先,我们来定义一个块是什么。块只是一组交易。在块内部,这些交易按照特定顺序设置并满足任何交易的基本要求。特别是,无效交易(例如从没有资金的账户获得资金)不能成为一个区块的一部分。除了交易之外,一个区块还有一些被称为工作证明的东西。工作证明是允许任何节点验证创建该块的人执行了大量计算工作的数据。换句话说,没有节点可以创建一个有效的块,而不需要执行不确定的但相当多的工作。我们将在稍后看到这是如何工作的,但现在知道创建任何块需要一定的计算能力,并且任何其他节点都可以检查该块创建的人是否已经花费了该功耗。
现在让我们回到我们先前的恶意节点A的例子,它试图同时创建两个单独的交易,一个向B发送资金,另一个向C发送资金,然后将两个交易广播到网络中,每个创建块的节点(可能包括A)都会选择一些事务并按照他们喜欢的方式进行排序。这些节点会注意到两个不兼容的事务是同一个块的一部分,并且会丢弃一个。他们可以自由选择丢弃哪一个。在按照他们选择的顺序放置这些事务之后,每个节点开始解决找到符合协议设置条件的块的散列的难题。一个简单的条件可能是“用三个前导零找到该块的散列”。
这创建了一个有趣的场景,因为即使A是恶意节点并控制另一个节点(例如E),网络上的任何其他节点仍然有机会找到不同的有效块。 换句话说,这种方案使得恶意节点很难控制网络。
但是,必须考虑大量恶意节点串通并共享CPU功率的情况。实际上,控制大多数节点的实体(根据CPU功率而非数量)可以通过比其他节点更快地创建块来执行双重支出攻击。足够大的网络依靠积聚CPU能力的困难。在投票系统中,攻击者只需要向网络添加节点(这很容易,因为免费访问网络是设计目标),但在基于CPU电源的方案中,攻击者面临物理限制:越来越多地访问强大的硬件。
最后,我们可以尝试对区块链是什么以及它是如何工作的完整定义。区块链是一个可验证的交易数据库,它载有所有发生的所有交易的有序列表。事务存储在块中。创建块是一项特意计算密集型任务。创建有效块的困难迫使任何人花费一定的工作量。这确保了足够大网络中的恶意用户不会轻易超越诚实用户。网络中的每个块都指向前一个块,从而有效地创建一个链。区块链中区块的时间越长(离最后一个区块越远),其可能从中移除的概率就越小。换句话说,块越老,它就越安全。
我们在前面段落中留下的一个重要细节是当两个不同节点同时发现不同但仍然有效的块时发生的情况。从某种意义上说,这看起来像交易所面临的同样的问题:选择哪一个。与交易相比,每个区块所需的工作量证明系统让我们找到一个方便的解决方案:由于每个区块都需要一定的工作量,因此唯一有效的区块链是其中最多区块的区块链。想一想:如果工作证明系统工作,因为每个块需要一定量的工作(和时间),最长的一组有效块是最难破的。如果一个恶意节点或一组节点试图创建一组不同的有效块,通过总是选择最长的块链,他们总是必须重做更多的块(因为每个节点指向前一块,改变一块块之后的所有块都强制改变)。这也是恶意节点组需要控制超过50%的网络计算能力来实际进行攻击的原因。少于这一点,网络的其他部分将更快地创建更长的区块链。
如果较长版本的区块链是由其他节点计算的,则有效的区块有效,但可以找到区块链较短的区块。废弃区块中的交易再次发送到等待纳入未来区块的交易池中。这会导致新交易保持未经确认的状态,直到他们找到尽可能长的区块链。节点定期从其他节点接收更新版本的区块链。
如果有足够多的节点同时从网络的另一部分断开连接,则网络完全可能分叉。如果发生这种情况,每个分支将继续与另一个分离创建块。如果网络将来再次合并,节点将比较不同版本的区块链并选择更长的区块链。具有更大计算能力的分支将永远赢。如果分叉要持续足够长的时间,合并发生时大量的交易将被撤销。正是由于这个原因,叉子是有问题的。
更换协议或运行节点的软件也可能导致分叉。这些更改可能导致节点使其他节点认为有效的块无效。效果与网络相关的分支相同。
虽然我们还没有深入研究比特币或以太坊如何处理交易的细节,但它们内置了一定的可编程性。比特币允许在每笔交易中指定特定条件。如果满足这些条件,交易可以花费。另一方面,以太坊则更进一步:系统内置了图灵完整的编程语言。我们将在本系列的下一篇文章中关注以太坊,但现在我们来看看创新的方式,区块链的概念可以被利用,而不仅仅是发送金钱。为此,我们将在比特币之上开发一个简单的永久消息系统。它将如何工作?
我们已经看到了可以验证的区块链商店交易。每个交易都由可执行该交易的人签署,然后再广播到网络。然后在执行工作证明后将其存储在块内。这意味着交易中嵌入的任何信息都会永久存储在区块链中。块的时间戳用作消息日期的证明,并且工作证明过程用作其不可变性质的证明。
比特币使用脚本系统来描述用户为了花钱而必须执行的步骤。最常见的脚本就是“简单地通过签署此消息证明您是某个私钥的所有者”。这就是所谓的“付给pubkey散列”脚本。以反编译的形式,它看起来像:
<sig> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
其中和由消费者提供,其余部分由原始发送者指定。这只是一系列混合的数据和操作。此脚本的解释器是基于堆栈的虚拟机。执行的细节超出了本文的范围,但您可以在比特币维基上找到一个很好的总结。重要的是,交易可以在脚本中嵌入数据。
实际上,在事务中嵌入数据的操作码是有效的:OP_RETURN操作码。无论数据如何,OP_RETURN操作码都存储在事务中。当然,允许的数据量有限制:40字节。这是非常少的,但仍然有一些有趣的应用程序可以用这么小的存储空间来执行。其中之一是我们的永久消息系统。另一个有趣的用例是“存在证明”概念。通过将资产的散列存储在区块链中,它可以在添加到区块的位置证明其存在。事实上,已经有这样一个项目。没有什么能够阻止您将我们的永久消息系统用于类似的用途。还有一些其他用途允许系统准备只有在满足条件后才能使用的交易,或者当提供者提供具有某种数字资产的证据时,以及某个最小数量的用户同意使用该交易的时间。与传统的货币系统相比,可编程性开辟了许多可能性,并为加密货币带来了另一大优势。
我们的系统将作为HTTP服务工作。数据将作为POST请求的主体以JSON格式传递。该服务将有三个端点加上一个用于调试。
THE /NEW ENDPOINT
它使用传入的用户名和密码创建一个新用户。示例主体:
{
"id": "username:password", // password is not hashed for simplicity,
// TLS is required!
"testnet": true // True to use Bitcoin's test network
}
The response is of the form:
{
"address": "..." // A Bitcoin address for the user just created
}
THE /ADDRESS ENDPOINT
Returns the address for an existing user. Sample body:
{
"id": "username:password", // password is not hashed for simplicity,
// TLS is required!
}
The response is identical to the /new endpoint.
THE /MESSAGE ENDPOINT
Broadcasts a transaction to the Bitcoin network with the message stored in it. A fee is usually required for the network to accept the transaction (though some nodes may accept transactions with no fees). Messages can be at most 33 bytes long. Sample body:
{
"id": "username:password",
"fee": 667,
"message": "test"
}
The response is either a transaction id or an error message. Sample of a successful response:
{
"status": "Message sent!",
"transactionId": "3818b4f03fbbf091d5b52edd0a58ee1f1834967693f5029e5112d36f5fdbf2f3"
}
Using the transaction id one can see the message stored in it. One can use any publicly available blockchain explorer to do this.
THE /DEBUGNEW ENDPOINT
Similar to the /new endpoint but allows one to create an user with an existing Bitcoin private key (and address). Sample body:
{
"id": "username:password", // password is not hashed for simplicity,
// TLS is required!
"testnet": true, // True to use Bitcoin's test network
"privateKeyWIF": "..." // A private key in WIF format.
// Note testnet keys are different from livenet keys,
// so the private key must agree with the
// value of the "testnet" key in this object
}
The response is identical to the /new endpoint.
The most interesting endpoint is the one that builds and broadcasts the transaction (/message). We use the bitcore-lib and bitcore-explorers libraries to do this:
getUnspentUtxos(from).then(utxos => {
let inputTotal = 0;
utxos.some(utxo => {
inputTotal += parseInt(utxo.satoshis);
return inputTotal >= req.body.fee;
});
if(inputTotal < req.body.fee) {
res.status(402).send('Not enough balance in account for fee');
return;
}
const dummyPrivateKey = new bitcore.PrivateKey();
const dummyAddress = dummyPrivateKey.toAddress();
const transaction =
bitcore.Transaction()
.from(utxos)
.to(dummyAddress, 0)
.fee(req.body.fee)
.change(from)
.addData(`${messagePrefix}${req.body.message}`)
.sign(req.account.privateKeyWIF);
broadcast(transaction.uncheckedSerialize()).then(body => {
if(req.webtaskContext.secrets.debug) {
res.json({
status: 'Message sent!',
transactionId: body,
transaction: transaction.toString(),
dummyPrivateKeyWIF: dummyPrivateKey.toWIF()
});
} else {
res.json({
status: 'Message sent!',
transactionId: body
});
}
}, error => {
res.status(500).send(error.toString());
});
}, error => {
res.status(500).send(error.toString());
});
具体代码非常简单:
获取地址的未使用的交易(即可用的硬币,余额)。
使用未使用的事务作为输入来构建新的事务。
将交易指向新的空白地址。将0个硬币分配给该地址(不要不必要地发送金钱)。
设置费用。
设置未用完的钱将被送回的地址(更改地址)。
添加我们的消息。
广播交易。
比特币需要使用以前交易的资金来构建交易。也就是说,当发送硬币时,它不是指定的起始地址,而是指向包含在指向不同目标地址的新事务中的那个地址的事务。从这些交易中扣除发送到目的地的货币。在我们的案例中,我们使用这些交易来支付费用。其他一切都会发回我们的地址。
由于WebTasks的强大功能,部署和使用这些代码是小菜一碟。首先克隆存储库:
git clone git@github.com:auth0-blog/ethereum-series-bitcoin-perpetual-message-example.git
现在确保您安装了Webtask命令行工具:
npm install -g wt-cli
如果您还没有这样做,请初始化您的WebTask凭证(这是一次性过程):
wt init
Now deploy the project:
cd ethereum-series-bitcoin-perpetual-message-example
wt create --name bitcoin-perpetual-message --meta 'wt-node-dependencies={"bcryptjs":"2.4.3","bitcore-lib":"0.13.19","bitcore-explorers-bitcore-lib-0.13.19":"1.0.1-3"}' app.js
Your project is now ready to test! Use CURL to try it out:
curl -X POST https://wt-sebastian_peyrott-auth0_com-0.run.webtask.io/bitcoin-perpetual-message/new -d '{ "id":"test:test", "testnet":true }' -H "Content-Type: application/json"
{"address":"mopYghMw5i7rYiq5pfdrqFt4GvBus8G3no"} # This is your Bitcoin address
您现在必须为您的新比特币地址添加一些资金。 如果你在比特币的测试网上,你可以简单地使用一个水龙头。
水龙头是比特币网站,免费提供硬币到地址。 这些测试网很容易获得。 对于“livenet”,您需要使用比特币交易所购买比特币。
现在发送消息!
curl -X POST https://wt-sebastian_peyrott-auth0_com-0.run.webtask.io/bitcoin-perpetual-message/message -d '{ "id":"test:test", "fee":667, "message":"test" }' -H "Content-Type: application/json"
{"status":"Message sent!","transactionId":"3818b4f03fbbf091d5b52edd0a58ee1f1834967693f5029e
现在,您可以使用区块链浏览器和事务ID来查看事务。如果您在链接的页面底部前往WTMSG:test前缀前看到我们的消息。这将永久存储在区块链中。
自己试试! https://wt-sebastian_peyrott-auth0_com-0.run.webtask.io/bitcoin-perpetual-message/上的web任务现场直播。不过,您需要创建自己的帐户并为其提供资金。
您也可以获取此示例的完整代码并运行它!
区块链支持分布式的验证事务。同时他们为双重支出问题提供了创造性的解决方案。这促使了cryptcoin的兴起,其中比特币是最受欢迎的例子。比特币每天都有数百万美元的交易,而且这种趋势并没有任何放缓的迹象。比特币提供了一组有限的操作来定制交易。尽管如此,许多创造性的应用程序通过区块链和计算的结合而出现。以太坊是其中最好的例子:将分散交易与图灵完整的执行环境结合在一起。在本系列的下一篇文章中,我们将仔细研究以太坊与比特币的不同之处以及分散式应用程序的概念如何通过它实现。