Jiawei

Posted on Feb 26, 2022Read on Mirror.xyz

Web 3.0 应用的架构

原文链接

作者:Preethi Kasireddy (Twitter:iam_preethi

翻译:Jiawei(Twitter:Elegy4TheArctic

Web 3.0 应用(或称“ DApps”)的架构与 Web 2.0 应用完全不同。

以 Medium 为例,它是一个简单的博客网站,用户可以在上面发布自己的内容,并与他人的内容进行互动。

作为一个 Web 2.0 应用,Medium 听起来可能很简单,但是其架构中包含不少东西:

首先,必须有一个存储基本数据的地方,比如用户、帖子、标签、评论、点赞等等。这需要一个能够不断更新的数据库。

其次,后端代码(使用 Node.js、 Java 或 Python 等语言编写)必须定义 Medium 的业务逻辑。例如,当一个新用户注册,发布一个新的博客,或者评论别人的博客时,会发生什么?

再者,前端代码(通常用 JavaScript、 HTML 和 CSS 编写)必须定义 Medium 的 UI 逻辑。例如,网站看起来是什么样的,当用户与页面上的每个元素进行交互时会发生什么?

把所有这些拼在一起:当你在 Medium 上写博客文章时,你与它的前端交互,前端与后端交互,后端与数据库交互。所有这些代码都托管在中心化的服务器上,并通过互联网浏览器发送给用户。这是对当今大多数 Web 2.0 应用工作方式的一个高阶概述。

但这一切都正在改变。

区块链为 Web 3.0 应用开启了一个令人兴奋的新方向。在这篇文章中,我们将关注以太坊给我们带来了什么。

是什么让 Web 3.0 与众不同

与像 Medium 这样的 Web 2.0 应用不同,Web 3.0 消除了「中间人」的存在。没有存储应用状态的中心化数据库,也没有存放后端逻辑的中心化 Web 服务器。

相反,你可以利用区块链去中心化的状态机上搭建应用,该状态机由互联网上的匿名节点所维护。

所谓「状态机」,我指的是维护给定程序的状态,并且可以更新未来状态的机器。区块链作为一种状态机,由一些起始状态实例化,并且有非常严格的规则(例如共识)来定义状态如何转换。

更棒的是,没有单一实体可以控制这个去中心化的状态机 —— 它由网络中的每个人共同维护。

那么后端服务器呢?在 Web 3.0 中,你可以编写智能合约来定义应用的逻辑,并将它们部署到去中心化的状态机上,而非 Medium 那样。这意味着每个想要构建区块链应用的人都会在这个共享状态机上部署他们的代码。

那么前端呢?它几乎保持不变,除了一些例外,我们将在后面讨论。

下图是它的架构:

再走近些

现在,让我们更深入地探讨一下这一切是如何实现的。

区块链

以太坊经常被吹捧为「世界计算机」。因为它是一个全局可访问的、确定性的状态机,由 P2P 网络节点所维护,且状态更改需要遵循网络中的共识原则。

所以,换句话说,它被设计成一个任何人都可以访问和写入的状态机。因此,这台状态机不由任何单一实体所拥有,而是由网络中的所有人共同拥有。

还有一件事你需要要知道:数据只能被写入以太坊 —— 而不能更新现有的数据。

智能合约

智能合约是一个运行在以太坊上的程序,它定义了发生在区块链上的状态变化背后的逻辑。智能合同是用高级语言编写的,比如 Solidity 或 Vyper。

因为智能合约代码存储在以太坊上,任何人都可以查看网络上所有智能合约的应用逻辑。

以太坊虚拟机 EVM

接下来是以太坊虚拟机,它执行智能合约中所定义的逻辑,并处理状态机上的状态更改。

EVM 无法直接「读懂」 Solidity 和 Vyper 等用于编写智能合约的高级语言。而需要将高级语言编译成字节码,这样 EVM 就可以执行这些字节码了。

前端

最后,正如我们前面提到的,前端定义了 UI 逻辑,但前端还会与智能合约中定义的应用逻辑进行交互。

前端和智能合约之间的交互比上图中显示的稍微复杂一些。接下来让我们仔细看看。

前端代码如何与以太坊上的智能合约进行通信

我们希望前端与智能合约进行交互,以便它们能够调用函数。请记住,以太坊是一个去中心化的网络。网络中的每个节点都保存着状态机上所有状态的副本,包括与每个智能合约相关的代码和数据。

当我们想要与区块链上的数据和代码进行交互时,我们需要与其中一个节点进行交互。这是因为任何节点都可以广播交易请求。然后,矿工将执行这笔交易并将结果的状态更改广播到整个网络。

广播一笔新的交易有两种方式:

  1. 自己搭建一个运行以太坊的节点
  2. 使用第三方服务提供的节点,比如 Infura、 Alchemy 和 Quicknode

如果使用第三方服务,就可以省掉自己运行全节点所带来的麻烦。毕竟,在自己的服务器上设置一个新的以太坊节点可能需要几天时间。(需要同步的数据量很大 —— 甚至可能占用比一台普通笔记本电脑还多的带宽和存储空间)

此外,随着 DApp 的扩展,存储整条链的成本也随之上升,你需要添加更多的节点来扩展你的基础设施。这就是为什么当基础设施变得更加复杂的时候,你需要全职的 DevOps 工程师。它们将帮助你维护基础设施,以确保可靠的正常运行时间和快速响应时间。

综上,避免这些麻烦是许多 DApps 选择使用像 Infura 或 Alchemy 这样的服务来管理他们的节点基础设施的原因。当然,由于这会造成一个中心化的阻塞点,因此需要权衡利弊。但我们以后再讨论它吧。

接下来,让我们谈谈「提供商」(Provider)。当你需要与区块链交互时(无论你是自己设置还是使用第三方服务的现有节点) ,你连接的节点通常被称为 Provider。

每个 以太坊客户端(即 Provier)都实行 JSON-RPC 规范。这确保当前端应用想要与区块链交互时,有一套统一的方法可以遵照。如果你需要了解 JSON-RPC 的入门知识:它是一个无状态、轻量级的远程过程调用(RPC)协议,定义了一些数据结构和处理规则。它与传输无关,因此这些概念可以在同一个过程中使用,通过 sockets、HTTP 或是许多不同的消息传递环境。它使用 JSON(RFC 4627)作为数据格式。

一旦通过 Provider 连接到区块链,你就可以读取存储在区块链上的状态。但是如果你想写入状态,在提交交易到区块链之前,还需要使用你的私钥为交易签名。

例如,假设我们有一个 DApp,让用户在区块链上阅读或发布博客文章。前端可能有一个按钮,允许任何人查询特定用户写的博客文章。(回想一下,从区块链读数据并不需要用户对交易进行签名)

然而,当用户想要在区块链上发布一个新帖子时,我们的 DApp 会要求用户使用他们的私钥对交易进行签名,只有这样 DApp 才会将交易转发给区块链。否则,节点将不会接受这笔交易。

交易签名正是 Metamask 的用武之地。

Metamask 是一个工具,使应用轻松地处理密钥管理和交易签名。它非常简单:Metamask 在浏览器中存储用户的私钥,当前端需要用户对交易进行签名时,它会调用 Metamask。

Metamask 还提供与区块链的连接(作为一个 Provider) ,它需要签名交易,因此连接了 Infura 提供的节点。Metamask 既是 Provider,也是签名者。🤯

区块链中的存储

当然,如果你在构建一个所有智能合约和数据都存储在链上的应用,这种架构是可行的。但是任何一个在以太坊上开发过应用的人都知道,在区块链上存储虽然取用方便,但却非常昂贵。

请记住,用户每次需要添加新的数据到以太坊上时都需要支付费用。这是因为向去中心化的状态机添加一个状态会增加维护该状态机的节点的成本。

如果你的 DApp 要求用户为每笔需要新增状态的交易支付一笔额外费用,显然这并不是好的用户体验。解决这个问题的一种方法是,使用去中心化的链下存储方案,如 IPFS 或 Swarm。

IPFS 是一个支持存取数据的分布式文件系统。因此,IPFS 系统不是将数据存储在中心化数据库中,而是将数据分发并存储在一个 P2P 网络中。这样,在需要的时候你可以很容易地找到它。

IPFS 还有一个叫做「Filecoin」的激励层,它通过激励机制使世界各地的节点来存储和检索这些数据。你可以使用一个 Provider,比如 Infura (提供了 IPFS 节点)或 Pinata(提供了易用的服务,你可以将你的文件「Pin」到 IPFS 上,然后获取 IPFS 的哈希值,并将其存储在区块链上)

与之类似,Swarm 是一个去中心化的存储网络,但也有一个显著的区别。Filecoin 是一个单独的系统,而 Swarm 的激励系统是内置的,通过在以太坊上的智能合约来实现数据的存储和检索。

这样,在使用 IPFS 或 Swarm 后,我们应用的架构看起来会是这样:

一些敏锐的读者可能已经注意到,前端代码是没有存储在区块链上。我们可以在 AWS 上托管这些代码,就像通常在 Web 2.0 中做的那样。但这会给 DApp 带来中心化的阻碍。如果 AWS 瘫痪了怎么办?如果它审查你的应用怎么办?

这就是为什么,如果你想建立一个真正的 DApp,你可能会选择将前端也托管在去中心化存储解决方案上,比如 IPFS 或 Swarm。

所以现在你的应用架构看起来更像这样:

区块链上的查询

到目前为止,我们已经讨论了如何对交易进行签名,然后发送交易来对区块链进行写入。但是从区块链上的智能合约中读取数据又该怎么做呢?有两种主要的方法可以实现这一点:

  1. 智能合约事件(Events)

    你可以使用 Web3.js 库查询和监听智能合约事件。在每次触发事件时指定调用一个回调函数。例如,如果你有这么一个智能合约,它在每个区块都会从 A 发送持续的付款流给 B,那么每次向 B 付款时,你就可以发送一个事件。前端代码可以监听由智能合约所触发的事件,并据此进行响应。

  2. The Graph

    上述方法是有效的,但也有一些局限性。例如,如果你部署完一个智能合约,随后才意识到需要发出的事件没有被包含进去,该怎么办?不幸的是,你只能使用该事件和数据重新部署一个智能合约。此外,使用回调函数来处理各种 UI 逻辑也非常复杂。

这就是 The Graph 用武之地。

The Graph 是一个链下的索引方案,简化了以太坊上的数据查询。借助 The Graph,你可以定义哪些智能合约需要索引、哪些事件和函数调用需要监听,可以规定如何将传入的事件转化为前端逻辑(或是使用 API 的程序)可处理的实体。它使用 GraphQL 作为查询语言,这种语言深受前端工程师的喜爱:因为与传统的 REST API 相比,它能表示更多的信息。

通过索引区块链数据,The Graph 实现了链上数据查询的低延迟。

现在,你的 DApp 架构看起来像这样:

我们已经快讨论完了,只剩一个主要的话题:扩容。

为你的 DApp 扩容

正如你可能已经听说的那样,以太坊并不具备可扩展性 —— 至少现在并不。

以太坊平均 gas 价格

平均交易费用

平均区块大小

很明显的一个问题是,由于以太坊的高额 gas 费和饱和的区块,在上面部署 DApp 会导致非常糟糕的用户体验。值得庆幸的是,一些解决方案正在开发中。

一个流行的扩容方案是 Polygon,一个 L2 扩容方案。与在主链上执行交易不同,Polygon 拥有处理和执行交易的「侧链」。侧链是一条与主链相连接的次级区块链。每隔一段时间,侧链就会将其近期区块聚合在一起,并提交回主链。

其他 L2 解决方案还有 Optimistic Rollups 和 zkRollups。它们的想法是类似的:我们使用一个「Rollup」智能合约来对链下交易进行批量处理,然后定期将这些交易提交给主链。

L2 方案在链下执行交易(交易执行在链上需要花许多时间) ,只将交易数据存储在链上。这实现了区块链的扩容,因为我们不需要在链上执行每一笔交易。这也使得交易更快更便宜 —— 并且在必要的时候仍可以与以太坊进行交互。

把所有这些都整合在一起!

如果这一切让你感到头晕目眩,那么你并不孤单。整合所有这些工具无疑是复杂的,并且可能会导致痛苦的开发体验。但不要担心,我们开始看到一些新的开发框架,它们真正改善了开发者的体验。

例如,Hardhat 是一个开发者框架,使得以太坊开发者更容易构建、部署和测试他们的智能合约。安全帽提供了「Hardhat Network」,开发人员可以使用该网络将他们的智能合约部署到本地网络上,而无需处理实时环境。更棒的是,它提供了一个很赞的插件生态系统,使开发更便利。Hardhat 还提供类似于 JavaScript 的 console.log ()功能,用于调试。

当然,这仅仅是个开始,我希望将来能够看到更好的开发工具。

结论

大多数人花费了几个月的时间来弄清楚区块链是如何工作的,所以如果你是一个新的 DApp 开发人员,我希望这篇文章能为您节省一些时间。是时候开始 Build 了!

如果你对构建 Web 3.0 应用感兴趣,那么请注册我们的下一批 DApp 开发训练营,在这里你将学习到如何在以太坊上构建和部署您你的第一个 DApp。

像往常一样,如果你有任何问题或发现这篇文章的任何错误,欢迎评论!:)

Recommended Reading