使用“扫一扫”即可将网页分享至朋友圈。
一文澄清以太坊开发者常见误解:Gas 交易与智能合约
最近,我偶然读到了一篇题为《程序员关于时区的误解》的文章,让我爆笑不已。这篇文章让我想到了程序员在其它方面的误解,如人名和时间,于是我开始寻找有没有关于以太坊的。奈何寻觅无果,我只得尽自己的绵薄之力。
关于Gas的误解
调用estimateGas会返回交易所需消耗的gas量
调用 estimateGas 确实会返回一个 gas 耗费量,但这是该笔交易在 当前状态 下被打包会花费的 gas 量。而区块链的当前状态可能与你需要该笔交易上链时的状态大相径庭。因此,当你的交易被有效打包进区块时,可能会采用不同的代码路径,需要消耗的 gas 量也有可能完全不同。
如果执行的代码相同,我的交易所需消耗的 gas 量也相同。
不对。即使你使用相同的参数来执行相同的指令,gas 成本也有可能不同。例如,相比已经有非零值的存储位置,如果你要写入新的存储位置,SSTORE(写入存储操作)的成本会高得多(参见 EIP2200)。这就意味着,如果你向一个新地址发送两笔 ERC20 代币转账,第一笔交易的成本会比第二笔高得多,即使二者执行的代码完全相同。
如果状态完全相同,我的交易所需消耗的 gas 量也相同
通常情况下是的,除非你很倒霉地碰上了硬分叉,导致一些操作重新定价。虽然这听起来很复杂,但说白了就是,你无法针对 dApp 中交易的 gas 上限进行安全的硬编码,除非你决心在每次发生硬分叉后都发布 dApp 更新。
如果代码相同,状态也相同,且没有发生硬分叉,我就可以相信 estimateGas的返回值了吗?
这下你可以相信 estimateGas 的返回值就是你的交易所需消耗的 gas 量了,但是你不知道这笔交易是否会如你所愿的那般进行。所谓的 gas 估测,就是节点将使用不同的 gas 值来尝试你的交易,并返回确保你的交易不会失败的最低 gas 值。但是,节点只会看你的交易,不会看交易的内部调用。这就意味着,如果你调用的合约代码有一个 try/catch 块,导致内部调用发生后无法撤销,你获得的 gas 估测值对调用合约来说是够用的,但是对被调用合约来说就不够了。
在多签名钱包中,这种情况经常发生:即使是在交易失败的情况下,大多数多签钱包会将操作标记为已执行,也就是说它们无法撤销最外层的交易(所带来的影响)。因此,一个原生的 gas 估测返回的值可能对多签代码来说是足够的,对你实际想运行的操作来说不一定足够。这就是为什么 Gnosis Safe 有一个专门的 gas 估测方法。
请注意,这也就是为什么因为 gas 不够而导致操作失败的情况很难察觉。内部调用可能会因为被分配到的 gas 太少而将 gas 耗尽,而交易本身可能还有很多 gas 可用。这就意味着,查看交易的 gas 使用量和 gas 上限并非检测 gas 错误的可靠方式。
管他呢,我每次多发送点 gas 就好了
多数情况下,这个方法是管用的。但是请记住,合约是可以查看它在一笔交易中收到的 gas 的。因此,可以轻而易举地将合约编写成,一旦收到过多 gas,交易就会失败。不过我怀疑的是,除了证明这一点外,这么做没有任何意义。
关于交易的误解
只要节点接受了交易,交易就会被挖出
想得美哦。以太坊的网络拥堵会导致 gas 价格波动很大,因此你的交易可能会被逐出 mempool(等待被挖出的交易集合)。如果 gas 价格飙升,你就需要重新发送交易。
我可以略微提高 gas 价格然后重新提交交易
只要你将 gas 价格提高到与你交互的节点所需的最小量(参见 txpool.pricebump ),那就没什么问题,否则还是会被拒绝。
矿工总选择 gas 价格最高的交易
并不一定。矿工可以随心所欲进行选择。他们可能会为了自己的利益而塞入自己的交易,甚至可以开一个协议外通道,为符合自己要求的用户打包交易。
但是,即使他们依据收益来决定打包优先级,如何以最优方式填满区块也是一个背包问题(knapsack problem)。由于交易无法被分割成几部分,所以,在 gas 上限为 10M 的区块中打包两个 5M gas 交易,而不是一个 6M gas 的交易,可能更为有利可图,即使 5M gas 交易的 gas 价格低于 6M gas 交易。
如果我以更高的 gas 价格发送相同的交易,矿工会选择后一个交易来替换前一个交易吗
替换交易必须在旧交易上链之前发送到矿工那里。也就是说,如果你发送了替换交易,你依然需要监控你之前发送的同一个 nonce 下的所有交易的哈希值。
关于 Nonce 的误解
我可以通过 getTransactionCount得到我的下一笔交易的 nonce
这取决于你所使用的区块参数。如果你根据最新区块来查询你的交易记数,就会忽略你的未打包交易,并进一步导致你不小心覆盖你的某笔未打包交易。
我可以通过 getTransactionCount('pending')得到我的下一笔交易的 nonce
虽然这在大多数情况下可行,但是你不能保证你的所有未打包交易都在你所查询的节点的 mempool 中。如果你有很多未打包交易,你所通信的节点可能已经丢弃了其中一些交易,但是这些交易仍有可能存在于其它地方!
关于 Log 的误解
我可以通过持续调用 getLogs 来有效监控事件
尽管这是一个非常管用的方法(没错,说的就是轮询!),但是遇上链重组就会出问题。如果你要轮询最新区块上的新 log,你不会收到关于区块重组的通知,也不知道你所看到的事件是否需要重新调整。
我可以通过安装过滤程序来有效监控事件
直到两周前,这还不是一种常见选择,因为 Infura 不支持基于 http 的过滤程序,MetaMask 默认使用基于 http 的过滤程序,也就是说你的 dApp 有 99% 的用户都使用这种过滤程序(注:我可能有些夸大)。除了新事件之外,过滤程序还会通知你因区块重组而删除的事件。但是,这就要求你正在与之交互的基础设施和节点保持在线。如果它们碰巧丢失了过滤程序的状态,你就有可能错过重组事件。
我可以通过 websocket 订阅来有效监控事件
太好了!这样下来,除了要相信你的节点会保持在线之外,你还要相信你本人会保持在线,你和节点之间的连接是可靠的。我想知道这周你在参加 Zoom 会议时掉线了几次?
现在,我必须承认,我已经对这个话题有点着迷了,以至于我在 Devcon 5 上就此进行了一场闪电演讲。如果你想了解更多内容,EIP234 很好地阐述了这些挑战的基本原理,ethereumjs-blockstream 则解决了这一问题。
关于合约的误解
智能合约是不可更改的
兄弟,如果你还有这种想法,你真的 out 了。我在一篇长达 30 页的文章中阐述过这一点,真的非常长。
不包含任何 DELEGATECALL的智能合约就是不可更改的
实际上,合约可以定期调用( CALL)到一个可变地址中,并将结果作为计算的一部分,或者作为更改状态的指令,从而更改正在运行的代码。
那不包含任何 DELEGATECALL或 CALL的智能合约,总是不可更改的了吧?
还有 STATICCALL。别忘了 STATICCALL!
不包含任何 CALL 的智能合约是不可更改的
你还得排除一种情况:这个智能合约是通过 CREATE2 部署的,会在其初始码(initcode)中动态载入运行时,并且可以自毁。在这种情况下,“所有者” 可以销毁合约,并使用不同的代码在同一个地址上重新创建这个合约。
不包含任何 CALL 且不通过 CREATE2 部署的智能合约是不可更改的
还得排除一种情况:这个合约是通过由 CREATE2 部署的合约部署的。因此,你需要追溯整个部署链条,找到最初创建合约的以太坊外部账户,确保没有任何猫腻,而且不存在自毁操作。这篇文章深入探究了这一问题。
关于 ERC20 代币的误解
我就不展开了,这个话题更适合写成一篇完整的文章。在与代币交互时,使用 OpenZeppelin 的 SafeERC20(你可以在这篇文章中阅读更多相关内容)就好。请记住,在转账时,接收者所收到的代币并不一定等于发送者被扣除的代币。我们来看下一部分吧。
关于以太币的误解
以太币的总供应量只会增加
我们都知道,有很多以太币是无法使用的,有的是因为外部账户的私钥丢失,有的是因为意外发送到全零地址,还有的是因为被卡在合约中无法处理(对不起,我没忍住)。总而言之,这部分以太币依然存在,但是无法访问。
不过,有一种方法可以销毁以太币。如果你指令一个合约自毁 selfdestruct 并指定其自身作为资金的接收方,这个合约内的所有以太币都将被销毁。这就意味着,只要愿意销毁比区块奖励更多的以太币,就可以让以太币通缩。
我可以写一个能拒绝任何以太币转入的合约
你或许知道,如果你没有声明任何 payable方法,Solidity 会拒绝所有发送到你的合约的以太币转账,防止资金被卡在合约内。但是,我们也可以在不触发任何代码的情况下,将资金发送到合约内:要么将该合约指定为自毁操作奖励的接收方,要么将其指定为区块奖励的接收方。正如 @gorgos 在评论中指出的那样,可以预先计算出合约部署地址,并在合约部署前将以太币发送到该地址。
也就是说,如果你追踪所有发送到你的合约的以太币转账,你的总余额可能大于你处理的所有转账的总和。
“广东财经网”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与
我们联系删除或处理,客服邮箱1098101642@qq.com,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同
其观点或证实其内容的真实性。
- 声音提醒
- 60秒后自动更新
【民政部回应何时恢复婚姻登记】民政部社会事务司二级巡视员杨宗涛表示,婚姻登记场所是人群聚集场所,部分地区暂停婚姻登记工作是对人民群众安全负责。未停止登记的地方推广用电话、网络、qq群预约登记,控制登记人数,分批分段登记,减少人员聚集和在登记机关停留时间。已停止婚姻登记地方将根据当地疫情控制情况逐渐恢复。
15:54欧洲央行副行长金多斯:欧洲央行还没有达到逆转利率。宽松政策的副作用更明显了。
15:54财经网站Forexlive分析师Justin Low评瑞士1月CPI月率:尽管通胀年率保持稳定,但核心通胀率有所下降,这一点令人更加担忧。这只是进一步巩固了瑞士央行维持现有货币政策不变,并可能在未来寻求更多宽松措施的观点。
15:54【武汉两大批发市场商户开业率超过80%】商务部市场建设司司长朱小良10日称,目前武汉生活必需品供应基本正常,除冷鲜肉、鲜叶菜等少数品种存在结构性短缺外,大部分重要生活物资供应充足,未发生明显抢购或脱销断档情况。当地白沙洲和四季美两大批发市场正常营业,商户开业率超过80%,日交易量上升至3000吨以上。(国是直通车)
15:53【日本2019年经常项目顺差增长4.4%】日本财务省10日发布的国际收支初步统计报告显示,主要受外国人入境游消费扩大影响,2019年日本经常项目顺差增长4.4%。报告显示,2019年日本经常项目顺差为20.06万亿日元(1美元约合109.8日元)。其中,商品贸易方面,由于液化天然气价格下跌等因素,进口下降5.6%至75.56万亿日元;由于汽车部件及钢铁等产品出口减少,出口下降6.3%至76.12万亿日元。货物贸易顺差减少53.8%,为5536亿日元。(新华社)
15:53欧元兑美元EUR/USD短线波动不大,现报1.0952。
15:53瑞士1月CPI年率:0.2%,前值:0.2%,预期:0.1%;瑞士1月CPI月率:-0.2%,前值:0%,预期:-0.2%。
15:52英镑兑美元GBP/USD短线走低13点,现报1.2890。
15:52【宁德时代“供电”国产特斯拉 有望拉低售价20%】全国乘联会秘书长崔东树分析认为,“随着特斯拉国产化率提升和产能爬坡,未来特斯拉国产车型的价格下探空间还是很大的。”他预计今年下半年,国产Model 3车型的售价就有望降至25万元,降幅接近20%。(新京报)
15:51【内蒙古:对不裁员或少裁员企业返还上年度50%失业保险费】据内蒙古新型冠状病毒肺炎疫情防控工作指挥部消息,疫情期间,内蒙古对不裁员或少裁员的企业返还上年度实际缴纳失业保险费的50%,对面临暂时性经营困难的中小企业,返还标准提高到上年度6个月企业及其职工缴纳社会保险费的50%。
15:51【商务部:2019年服务进出口总额54152.9亿元 同比增长2.8%】商务部新闻发言人表示,2019年,在服务贸易创新发展试点等政策的激励下,我国服务贸易总体保持平稳向上态势,逆差明显下降,结构显著优化,高质量发展成效初步显现。全年服务进出口总额54152.9亿元(人民币,下同),同比增长2.8%。其中,出口总额19564.0亿元,同比增长8.9%;进口总额34588.9亿元,同比减少0.4%。(第一财经)
15:51【振华股份:疫情导致下游客户开工推迟 产品库存上升】振华化学公告,公司目前生产经营稳定,所有产能均正常开工,原材料采购能基本满足生产需要,由于疫情导致下游客户开工推迟,公司出货量减少,产品库存有所上升。公司将根据客户需求、疫情及市场变化,适度调整生产经营策略,尽可能保持生产经营的稳定。
15:50环旭电子2月10日晚间公告,公司2020年1月合并营业收入为23.27亿元,较去年同期的合并营业收入减少27.83%,较2019年12月合并营业收入环比减少37.23%。
15:49精测电子:与京东方集团签订了多份销售合同,合同累计金额达到6.96亿元。
17:12华夏银行:银保监会同意本公司在全国银行间债券市场发行不超过100亿元人民币的金融债券,募集资金全部用于绿色信贷。
17:03