区块链研究实验室-以太坊应用程序架构设计
六月殇浮华落月f
发表于 2022-12-23 08:29:57
189
0
0
区块链技术为设计应用程序的体系结构和布局带来了一系列新的挑战:传统的客户端 - 服务器应用程序现在混合了第三个新组件 - 区块链。
' u% ?- w7 w" M: Y
在本文中,我将介绍以太网应用程序的一些最传统的方案,这些方案来自这三个组件之间的不同交互。我将讨论无服务器应用程序,浏览器插件,私有节点,脱机签名以及在设计解决方案布局时发挥作用的其他问题。
3 V) f; y) _, j+ V7 M
无服务器应用程序中的客户端区块链& h+ f, z9 r8 Y1 H5 d
以太坊应用程序的规范风格是无服务器应用程序,其中应用程序的整个流程完全在客户端和区块链之间发生。
这里的第一步是将客户端代码实际分发给您的用户。最简单的方法是设置一个包含支持Web3的Web应用程序的静态页面。此类页面可以托管在任何地方:AWS S3,Google Cloud,Github页面,其他云提供商或您自己的服务器。此外,如果您可以依靠支持bzz或ipfs协议的客户端,您甚至可以通过Swarm或IPFS对其进行分散,以实现完全分散。' R% D0 _/ e8 t: M5 [5 P: Y6 C
; W8 k; I6 q J% V
查询区块链
下一步是让应用程序能够从区块链中读取信息,正如您所知,区块链需要连接到活动的以太坊节点。这将由您的web3提供程序设置,该提供程序处理与节点的实际web3连接。
9 U4 e* i# ?7 t+ e: v
现在,您的一些用户可能已经建立了与节点的连接,例如,通过官方的Mist客户端,或者通过像非常流行的Metamask这样的浏览器插件,它就像区块链的轻量级客户端。他们的常见问题解答甚至提供了一个代码片段,介绍如何检测客户端是否可用,并将其用作web3的提供者。, [8 y6 ^* S! P* `# I, `& @
; p8 w4 d# R9 p' J! t
// Adapted from https://github.com/MetaMask/faq/ ... d#partly_sunny-web3—ethereum-browser-environment-check
window.addEventListener(‘load’, function() {. ]( R/ T: V2 D! o T4 f: ~3 h3 I/ c
// Checking if Web3 has been injected by the browser (Mist/MetaMask); r5 q( {9 C6 P ^7 R5 E4 \2 M
if (typeof web3 !== ‘undefined’) {
// Use Mist/MetaMask’s provider, O/ j+ T$ ~# [7 X' W( X
3 ^) v0 N" Y) ?; p& q7 o4 v2 V. }. j
window.web3 = new Web3(web3.currentProvider);, t1 g8 ^( L. ?# V* j
} else {: M j8 ]+ v; L8 L. m I6 L8 |
6 s. r0 U0 t; ]8 h6 Z. A# I
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
: e \9 r9 j: f. U7 t% l- P; y) X
window.web3 = new Web3(new Web3.providers.HttpProvider(“http://localhost:8545”));
6 {; P! ]% e' [" S5 u: j" i
}
// Now you can start your app & access web3 freely:
" t0 s* B9 v- L$ j( f( ~
startApp()
})
那些没有Mist或Metamask的用户该怎么做?如果您只需要它们查询区块链而不实际发送任何事务,则可以建立与公共以太坊节点的连接。这应该是向公众开放的geth或奇偶校验节点,但不会暴露用于管理帐户的个人API,也不会有任何未锁定的帐户。这样的节点只能作为查询常量合同函数的网关。如果您不想自己托管,Infura的优秀人员可以免费提供公共节点。5 m' }6 }0 k$ f( g8 R2 f
4 J/ i' c& x z8 |
这样,具有正在运行的以太坊基础架构的用户可以使用他们与自己的可信节点的连接,而不太精通技术的用户可以使用公共节点。这意味着后者用户为了易于使用而选择信任第三方受控节点提供的信息。# a" g7 c% v* G9 Y8 j8 Z+ `
发送交易
查询区块链很简单,但如果您希望用户提交交易并在智能合约上实际执行状态更改操作,该怎么办?
9 O% D; ]1 B2 I5 x' ]8 m
对于那些安装了Mist或Metamask的用户,问题立即得到解决,因为它们都提供了一种管理用户帐户并请求用户根据应用程序的请求批准事务的方法。 Mist将为用户的本地节点及其帐户提供签名交易的网关,而Metamask将在客户端签署交易并将其转发到Metamask公共节点。1 O* S' L* ]' k0 k
至于其他用户,如果您不要求他们安装Metamask或使用Mist来使用您的应用程序,您需要指导他们手动发送交易以与您的应用程序进行交互,无论他们使用哪个钱包。5 F5 G# O+ n/ b$ z3 A4 y1 {: N
9 Q0 _9 G6 M7 ?: L
大多数应用程序通过要求用户向地址发送一定量的ETH来实现此功能,可选地包括QR或复制到剪贴板按钮。8 W f2 Y% _) d5 j* s0 s
您的客户端应用程序可以监视合同事件,以便在执行带外事务时更新用户界面。 由于观看事件只是查询区块链的一种方式,因此可以通过公共节点轻松完成。
现在,为了执行合同功能,您可能需要请求用户发送ETH以及其他数据以执行特定功能。 您可以使用契约函数的请求方法轻松获取运行方法所需的数据,并将其与目标地址一起呈现给用户。
SimpleToken.at(tokenAddress).transfer.request(“0xbcfb5e3482a3edee72e101be9387626d2a7e994a”, 1e18).params[0].data
// returns data for invoking transfer with args => ‘0xa9059cbb00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000de0b6b3a7640000’& {, W# o: p! b. }% Y9 H
就个人而言,我不是这种模式的忠实粉丝,因为它需要用户进行相当复杂的交互。并非所有用户都知道其他数据如何在以太坊中运行,更不用说如何在事务中发送它。1 ]2 _2 B) w! ?3 G6 \6 n
# c% n$ J+ M2 L" |% Q3 `+ C2 l5 @
如果您确实需要实施它,请确保合同的回退功能拒绝所有付款或具有合理的默认行为,因此如果用户在手动发送交易时忘记包含其他数据,则他不会丢失已发送的资金。
请注意,在许多情况下,设计巧妙的回退函数实际上可能始终执行发送事务的用户的预期行为。这样,您的智能合约只会对发送资金的用户做出反应,并根据当前状态运行不同的功能,而无需用户设置任何其他数据。, R1 T/ d. j; K W K
另一个技巧是添加代理合同,其中每个合同在收到Ether时在主合同中执行特定功能。举个简单的例子,假设您正在实施一个二元投票应用程序:您的主要合同是投票 - 是和投票 - b不是 功能。除了要求用户在其交易中包含yes-or-no标志,您可以部署另外两个合同,除了在接收时代表邮件发件人在主合同中调用vote-yes或vote-no之外没有逻辑任何交易。毋庸置疑,这种方法仅适用于相当简单的应用程序,但通常可以帮助降低浏览器中没有以太坊感知软件的用户的复杂性。
然后,用户需要使用某个以太网来支持该帐户,可以是来自不同的帐户,也可以是直接来自交换服务;考虑在这里放入Shapeshift集成以便于使用。一旦帐户具有支付交易费用的所需余额,客户端代码可以通过简单的点击代表用户执行任何所需的操作。
; {6 |# X2 \+ ~/ x7 v5 S) x! A
在幕后,您的应用程序将使用生成的帐户的私钥对客户端的任何事务进行签名,然后将它们中继到公共节点以供执行。6 @6 q9 A" \3 d5 X" }
- V) W( W) n; b0 m L! S" m/ B
这种模式涉及繁重的编码,因为您需要添加生成和加密以及导入以太坊帐户的功能(您可能需要查看以太坊钱包库以实现这些任务)。此外,所有事务都需要手动制作和签名,然后作为原始事务发送到节点。它还需要用户的更多参与,该用户负责设置新帐户,并负责保护帐户文件。5 S& v- d( S. m. }$ b
9 j* i5 l" u3 n @' f5 _* `! z
然而,一旦设置完毕,您的用户就可以直接从您的应用程序执行以太网交易,而不需要任何软件。. Y% v+ d+ I3 a$ c/ ^7 ]7 o- t
总而言之,您使用哪种方法将在很大程度上取决于您要与之接触的用户类型,以及您希望它们与您的应用程序的交互类型,无论是连续使用,还是不频繁或一次性使用访问。2 p" ]9 i" I: H4 j8 F+ `
服务器与区块链链接
( j9 i U8 D& Z7 v: G- ~
现在,让我们将混合服务器添加到服务器中,并将客户端留在此部分。 因此,以下内容不仅适用于应用服务器,还适用于独立应用,脚本或批处理。, ]4 k4 y) e7 S# l+ {6 I& e8 F# [
设置本地节点
第一个解决方案是vanilla:设置本地以太坊节点并使用应用程序中的JSON RPC接口来执行所有区块链操作。- V/ R; u5 y5 _- t5 s D. y
. l2 Z7 _3 j1 n! y
如果你沿着这条路走下去,请记住你正在盲目地信任你要连接的公共节点:虽然它不能修改你发送给它的交易,它可以选择不将它们转发到网络,或者提供虚假的回复 对你的疑问。 这可以通过同时连接到多个节点,并向所有节点发送相同的事务或查询来减轻; 但这会使您的代码库变得更加复杂。
a) u5 O# P2 T$ a, E. Q- R
功能整合
在通过不同的方式设置客户端区块链和服务器区块链查询和事务之后,是时候讨论如何一起编排所有内容。
' m1 M+ J" g' `
协调客户端和服务器
- f) h; u$ S+ z2 O: ]
让客户端和服务器同时与区块链交互意味着您可能需要协调它们。例如,您可能希望让服务器对客户端在链上执行的操作做出反应,或者在客户端中显示某个合同上的状态更改。) U& X$ b! O1 h" K6 s
从客户端和服务器观察合同变更的规范方法是监听合同事件。仔细设计您的事件,以确保所有相关操作都有关联的事件,并使用索引参数,以便观察者只能过滤与它们相关的事件。通常,客户端只会直接侦听影响它们的事件,而服务器可能会监控与应用程序相关的所有合同。* m% f6 h2 w# a0 n6 I
6 T7 s8 p3 V4 M6 i
如果直接从您的应用程序代码发出特定事务,您也可以直接监视它们,以检查它们是否成功。
客户端还可以向服务器发布他们发布的事务的id,作为在链上执行的操作的证明,而不是让服务器监听事件。但是,请记住,恶意行为者可能正在监视链上的事务,并且可能会向服务器推送来自不同客户端的事务ID,假装它属于他。确保仅将客户端到服务器消息用作通知,而不是事实来源。' v2 k; r! E# o: w8 W4 P8 d6 O
" N/ x( H. d' U* N# J, p Z
无论是否依赖监控交易或事件,请确保在合理数量的确认后才对其进行反应。即使您已经挖掘了一个事务,它仍然可能会受到链重组,并最终在不同的上下文中运行,可能会变得无效。在对一个链上事件采取行动之前,等待大约十几个块(更多在testnets中),但是考虑让你的最终用户知道交易成功但未经证实,以免让他们陷入困境。
- ~7 Q S" f$ K" R; l
现在,您需要回答的一个关键问题是为什么您需要一台服务器。在传统的客户端 - 服务器应用程序中,服务器充当永久存储器,强制执行业务逻辑,并协调客户端;现在可以在链上执行的所有任务。
0 }6 [ T' G% n8 ^' k) M5 W
尽管如此,支持您的应用的服务器仍有许多用途。首先,链上代码不能直接与脱链服务一起使用。这意味着如果您想要与第三方服务集成,注入外部数据(如USD / ETH费率),或执行基本操作(如发送电子邮件),则需要服务器来处理此问题。. n) T) M; H' D/ Q6 M4 |" c
服务器还可以充当智能合约的缓存或索引引擎。虽然最终的事实来源是区块链,但您的客户可能依赖服务器获取搜索功能,并在链上验证返回的数据。
8 S5 V2 \1 S4 C$ T! V) d c7 @: `& e
由于使用合同存储产生的gas成本,目前在以太坊上大数据存储成本过高。因此,您的应用程序也可能依赖于服务器来存储大量数据,而只有一个哈希链在一起进行验证。复杂计算也会发生同样的情况,这可能会超过以太坊区块gas限制,因此需要在单独的基础设施中运行。
# V/ s3 L1 y& w2 {4 H" p
值得一提的是,越来越多的项目出现在EVM周围,提供与这些服务的无缝集成。一些示例是用于存储的Filecoin或Storj,用于计算的Truebit或用于oracles的Oraclize。最终,服务器可能会变得越来越薄,直到它们消失在无数的侧链服务和集成中。
成为第一个吐槽的人