以太坊交易的生命周期
V刘晨曦
发表于 2022-12-2 23:32:43
127
0
0
9 `2 d! {3 t3 u/ ?
以太坊交易的端到端遍历,即从你的浏览器/控制台出发进入以太坊网络,然后再回到你的浏览器/控制; j R9 {+ j& l
了解当你使用Metamask或Myetherwallet等插件,而非运行自己的节点之时,交易是如何进行的$ p2 x" |2 P; B4 Q- [
如果你比较偏执多疑,不信任任何插件,想自己执行交易,该怎么做?
# i# h) i% g7 \2 r6 z+ o* ?
本文读者需要对以太坊及其组成部分,如账户、gas和合约等具备基础性了解。关于这些概念的详细解释可以参见9 ?9 Z* q& z: ~! \
+ j6 E* I1 @2 c h+ J
这篇文章
(编者注:中译本见文末《以太坊中的账户、交易、Gas和区块GasLimit》超链接)。如果你是一个不熟悉以太坊的开发者,
这篇文章' V* k# g. M% U: f _* G% F
或许对你很有帮助。你也可以从0 _, B& p/ ~+ f$ F% x3 }/ d
# W: u* X' a C) N- X' q7 i( s
这篇文章
中学习如何构建简单的分布式应用。如果你已经有过执行交易的亲身经历,本文对你的意义会更大。例如,可以是将一些以太币发送给另一个人或合约的交易。再比如,还可以是在与分布式应用进行交互的情况下,如果你在
这个网站
* x0 M9 r( H7 N
上买了一些代币,这就是一笔交易。如果你为一位候选人投票,这也是一笔交易。一.以太坊交易的端到端综述让我们以下列合约调用为例,并遍历该函数调用/交易是如何执行及永久存储在区块链上的整体流程。
点击此处1 n! S# V0 K7 S
可了解整个合约。从较高层次来说,这是一个投票合约,其中你可以预置一些候选人在选举中进行角逐,任何人都能为他们投票。这些投票将会记录在区块链上。Voting.deployed().then(function(instance){9 \" F% o* @ _
* F: [+ E# t% ~/ u, S7 C* R5 D
instance.voteForCandidate('Nick',{gas:140000,from:web3.eth.accounts[0]}).then(function(r){' j D* l; _* C1 ?2 X2 T" r% v# M
7 J$ z0 F0 y6 i$ B
console.log("Votedsuccessfully!")1 {2 S$ z4 h$ D( H. u
})& s7 r6 [* Y( O' c. B7 m, z
})9 P4 Q8 t6 w9 f0 Z, s7 L, a
假设你在自己的计算机上本地运行了一个以太坊客户端(Geth或Parity),你的计算机连接到了某个以太坊网络(测试网络或是主网),你有权访问该合约地址和ABI,从而执行该交易。如果你构建过分布式应用,应该对上述代码并不陌生。这是一个名为“Voting(投票)”的合约,已经部署在了区块链上。我们以该合约为例,执行一个叫作voteForCandidate的函数,输入候选人的姓名、该交易的gas上限和执行该交易的账户。从名称中可以看出,该函数能够用来为候选人投票,选票记录在区块链上。在下文,我们将尝试解构该调用,了解你在执行该javascript函数时会发生的一切。1.构建原始交易对象如下图所示,voteForCandidate函数调用首先被转化成了原始交易(rawTxn)。Web3js库被用来构建原始交易对象。txnCount=web3.eth.getTransactionCount(web3.eth.accounts[0])2 ~2 }! P4 B- j* C- G X, h* |9 a
varrawTxn={1 N+ ?8 ], w9 u& o
+ B, U1 e& ^7 W5 I
nonce:web3.toHex(txnCount),: j! j. m2 U' j$ u- Z: y
5 B$ p$ d3 B% I* L& S6 V
gasPrice:web3.toHex(100000000000),
gasLimit:web3.toHex(140000),1 F1 p4 x$ T1 q" } X# l
% d# H3 ^( e5 J" y. i
to:'0x633296baebc20f33ac2e1c1b105d7cd1f6a0718b',
- T! z: `# w+ \
value:web3.toHex(0),
: t9 |; X# n# h) z( r, C
data:'0xc7ed014952616d6100000000000000000000000000000000000000000000000000000000'
3 D% l: t1 ^0 f1 o% a
};
$ J; o' F. _8 M
让我们试着了解下该原始交易对象中的所有字段,以及它们是如何设置的。nonce(随机数):每个以太坊账户都有一个叫做nonce的字段,来记录该账户已执行的交易总数。Nonce的值随着每个新交易的执行不断增加,这能让网络了解执行交易需要遵循的顺序。Nonce也被用来进行重放保护。
gasPrice(gas价格):即你愿为该交易支付的每单位gas的价格。如果你正在主网上执行交易,
: B/ X4 l0 K; ]+ z7 J) e) F2 ?
ETHGasStation: |1 w% Q5 z- G' T
1 \9 Z0 N1 U7 l2 o1 w
上正好有一个网站,你可以参照其建议为你的交易设定gas价格,以便交易在一定时间内成功执行。Gas价格目前以GWei为单位,其范围是0.1->100+Gwei。下文会进一步介绍gas价格及其影响。gasLimit(gas上限):即你愿为该交易支付的最高gas总额。该上限能确保在出现交易执行问题(比如陷入无限循环)之时,你的账户不会耗尽所有资金。一旦交易执行完毕,剩余所有gas会返还至你的账户。to:即该函数调用被送往的地址。0x633296baebc20f33ac2e1c1b105d7cd1f6a0718b是我们的案例中投票合约的地址。Value(值):即你打算发送的以太币总量。当我们执行voteForCandidate函数时,我们根本没有发送以太币,因此value为零。如果你要执行一个交易,向另一个人或合约发送以太币,你会需要设置value值。data(数据):让我们来看看data字段是如何计算出来的。你先从voteForCandidate(bytes32candidate)(32字节的候选人)的
ABI0 t7 @3 u) k3 v. x
! p4 Q z3 m7 K- R: O
中获取函数签名,并得出它的哈希值。>web3.sha3('voteForCandidate(bytes32candidate)')& E$ N3 z8 s' A. {* r) ], V
! y3 ]' s6 `5 o3 o
'0xc7ed014922ff9493a686391b70ca0e8bb7e80f91c98a5cd3d285778ab2e245b3'1 f3 y$ t, _2 `7 M6 w
取该哈希值的前4个字节,即:0xcc9ab267。然后将参数‘Nick’转化为32字节,得到52616d6100000000000000000000000000000000000000000000000000000000将二者结合,得到数据有效载荷。2.签署交易如果你记得的话,你使用了web3.eth.accounts[0]来执行该交易。以太坊网络需要知道你确实是该账户的所有者,从而确保其他人不能以你的名义执行该交易。要向网络证明这点的方法是使用该账户的相应私钥签署交易。签署过后的交易如下图所示:constprivateKey=Buffer.from('e331b6d69882b4ab4ea581s88e0b6s4039a3de5967d88dfdcffdd2270c0fd109','hex')9 H. n7 X. \3 b3 i, H
8 p+ N4 u# C* I- t' @
consttxn=newEthereumTx(rawTxn)2 u+ b: G0 `$ B7 G
& E% \5 X# y/ f+ M' X; p% h
txn.sign(privateKey)
9 s/ p/ G2 ~% A) Q& `
constserializedTxn=txn.serialize()
- e# [: `( \5 E9 q- N" K8 d
3.交易经由本地验证签署过后的交易会提交至你的本地以太坊节点。然后你的本地节点会验证已签名的交易,确保它真的是由这个账户地址签署过的。4.交易被广播至网络已签署交易经由你的geth/parity节点广播至其对等节点,这些对等节点再将该交易广播给它们的对等节点,以此类推。一旦交易被广播至网络,你的本地节点也会输出该交易的id,你可以用它来追踪你的交易的状态。该交易id就是已签署交易对象的哈希值。transactionId=sha3(serializedTxn); O& B$ p8 i% n/ W/ v/ |2 `) V* H' _
# j Y& Q D; i7 ^* l
如果你是在公共的以太坊网络上执行该交易的话,追踪你的交易状态的最佳方式是通过3 I6 r ?6 F' R- a# K( U( U0 x0 A
0 s+ w; U2 H q+ k2 `; V1 _- Q
etherscan.io
1 u1 n. q- j1 p. h: E+ S
。如上图所示,你是否注意到了有几个节点被标记为Etherscan节点。Etherscan的团队运行了几个节点,并将一个很棒的前端网络应用连接到了Etherscan上。如果你的交易被他们的节点选中,你可以在他们的网站上查看自己的待定交易。还要记住的一点是,并非所有节点都会接受你的交易。其中一些节点可能被设置成了只接受gas价格超过某一最低值的交易。如果你设置的gas价格低于该下限,则节点会忽略你的交易。5.矿工节点接受交易正如图中所示,以太坊网络同时拥有矿工节点和非矿工节点。可能你也知道,矿工的职责是将你的交易包含到区块上。矿工是交易池的维护者,你的交易先是被添加进交易池,再由矿工进行开始评估。从上图中你会注意到矿工将所有交易存储在根据gas价格分类的池中。gas价格越高,该交易就越有可能被添加进下一个区块。这是矿工节点的常见设定(为得到更高的报酬进行优化)。不过,矿工可以将自己的节点设置成根据自己的喜好对交易进行分类(例如,他们为了帮助网络只挖掘gas价格低的交易)。从上图中,你看出我们的voteForCandidate交易是如何沉入矿池底端的吗?一旦所有gas价格高的交易都被挖出来包含进区块之后,矿工将挖掘我们的交易。还有要注意的一点是,矿池可以容纳的交易数是有限的。比如,一场众筹正在火热进行中或是一个非常流行的分布式应用(如加密猫)横空出世。人们提交了gas价格高的交易,希望矿工能最先选中他们的交易。如果gas价格高的交易填满了矿池,gas价格低的交易就会被放弃。我们的候选人Nick在一段时间内就别想收到任何选票了。在这种情况下,我们甚至得重新广播我们的交易。另一个能让你的交易在矿池里上升的技巧是重新提交你的交易,提高gas价格并维持nonce值不变。这样一来,当矿工接收到新交易时,gas价格更高的新交易会覆盖之前的交易。如果改变了nonce值,重新提交的交易就会被认为是不同的交易(最后会为Nick举行两次投票)。2 Y8 G6 b+ P2 U
' Z7 Z) W& I- y: V8 n+ @
这里) x& W4 u" _5 B/ K: I" {
推荐一篇很好的文章(编者注:中译本见文末《科普|释放阻塞的以太坊交易》超链接),作者, L( i$ R9 N# g" v
JimMcDonald7 W: f8 m% N$ G" w9 c' `/ J
# |1 a! S7 I% N! ^; _. W$ K
对此做出了深入的解释。6.矿工节点找到一个有效区块并将它广播至网络矿工最后选中了我们的交易,与其他交易一起包含进区块。矿工只能选择一定量的交易添加进区块,因为以太坊已经设置了单个区块gas上限,换言之,交易的所有gas上限总数不能超过区块gas上限。你可以在
# H8 n4 J- }7 u+ j
ethstats.net* E3 H6 n/ T( A$ t
上查看当前gas上限。一旦矿工选择将交易包含进区块,这些交易将被验证并包含进一个待处理区块,工作量证明开始。某个矿工节点(通过解决工作量证明难题)最终会找到一个有效的区块,并将这一区块添加到区块链上。就像经由你的本地节点广播的原始交易会被其他节点接收那样,矿工节点也会将这一有效区块广播给其他节点。7.本地节点接收/同步新区块最终,你的本地节点将接收这个新区块,并同步区块链在本地的副本。一旦接收到这个新区块,本地节点就会执行区块里的所有交易。如果你使用truffle执行你的交易,truffle会不断测验区块链以求确认。一旦它发现交易被确认,就会执行then()区块中的代码,并打印(我们每个例子的)控制台日志函数。二.使用Metamask而非本地节点如果你安装了
MetaMask/ t$ J7 [ K4 i+ ]+ t: W8 @1 _
4 g7 J% E% b: n0 H, Q
浏览器插件,你就能在你的浏览器中管理你的账户。密钥只会存储在你的浏览器上,因此你是唯一一个有权访问你的账户和私钥的人。当你在浏览器上执行交易之时,插件会将你的函数调用转化成原始交易,并用你的私钥签署交易。Metamask运行自己的节点,并且使用这些节点来广播你的交易(Metamask使用的是* x @. G; w2 w" P. \# k, D2 F d
Infura
运营的节点)。如此一来,你就没必要运行自己的以太坊节点了。三.线下签名如果你不喜欢用插件,或者担心你的本地geth节点有可能受损(遭篡改),该怎么办呢?要解决这一问题有一个安全之策。你是否注意到了,前两个步骤根本不需要联网。如果你想确保自己的交易绝对不会受到篡改,你可以使用一台没有联网的计算机将这一函数调用转换成原始交易,并使用你的私钥签署该交易。之后,你可以复制已签署交易串,并使用联网的计算机将其广播至网络。你可以使用
Etherscan
和
4 K8 k7 C9 c3 N* \ D$ z! W
Infura3 G* v2 r2 A# z# @; p, `
等服务将你已签署的交易广播至网络。另一个安全之策是使用
3 z$ i& x9 e2 N8 U
Ledger
或
7 q4 f4 c" M# M$ N. c9 \! [
Trezor
等硬件钱包。这类钱包存储了你的私钥,而签署交易的密钥已经编程进了硬件本身。它们需要联网的原因只是为了发布你的已签署交易。
成为第一个吐槽的人