- 说明
作者为道议程开发团队核心成员谭粤飞
一 基础知识
本标准中的钻石就是指智能合约,这种合约使用代理合约(下面会详述)引入新增加的功能
钻石的每个面都代表一个代理合约(delegate contract)
每个钻石可拥有多个面(代理合约)
合约需要调用哪个代理合约就可以调用哪个代理合约
任何关于合约的变化用diamond event表示
二 钻石的种类
1.可升级钻石(Upgradeable Diamond):有diamondCut函数,用于替换,新增和删除函数。
2.已完工钻石(Finished Diamond):它是一种可升级钻石,但是却在可升级钻石的基础上去掉了diamondCut函数,因此不再可更新。
3.单切钻石(Single Cut Diamond):它在构造函数中使用diamondCut函数添加除diamondCut函数以外所有的其它函数。这种钻石也不可更新。
三 关键术语
1.放大镜(loupe):它也是钻石的一个面(代理合约),这个代理合约提供各种功能用来查看其它的代理合约及钻石的功能。
2.不可更新函数(immutable function):它是定义在钻石或代理合约中,不可更新或删除的函数。
四 工作原理
1.钻石通过delegatecall调用代理合约中的函数。
2.钻石通过调用diamondCut函数增加、替换或删除代理合约以及函数。
3.当钻石发生任何变化时,产生一个DiamondCut事件。
五 使用钻石标准时的注意事项
1.包含fallback函数和构造函数,可以包含或不包含immutable function。
2.diamondCut函数可以在钻石中定义,定义为immutable function,也可以在代理合约中定义。
3.diamondCut函数将函数选择器(function selector)和代理合约挂钩并发送DiamondCut事件。
4.当钻石的某个函数被调用时,如果该函数是直接定义在钻石内的immutable function则马上执行;否则执行fallback函数,由fallback函数找到定义这个函数的代理合约,再由delegatecall执行这个函数;如果fallback函数找不到任何一个代理合约定义了这个函数,则执行过程回滚。
5.调用diamondCut函数时,用参数 bytes[] memory _diamondCut 来定义要添加、更新或删除的函数。这个过程中如果函数选择器(function selector)对应的代理合约地址为address(0),则从钻石中删除该函数选择器;否则将该函数选择器添加到钻石中,或用该函数选择器更新钻石中原有的函数选择器。如果函数选择器已经关联了指定的代理合约 或 该函数选择器不能被删除,则执行过程回滚。
6.每当有函数添加、更新或删除时,要发送DiamondCut事件,并记录之。
7.钻石必须遵循并实现ERC-165。如果钻石有diamondCut函数,则它的接口ID为 ”Diamond.diamondCut.selector“;DiamondLoupe的接口ID为 “DiamondLoupe.facets.selector ^ DiamondLoupe.facetFunctionSelectors.selector ^ DiamondLoupe.facetAddresses.selector ^ DiamondLoupe.facetAddress.selector”。
8.使用本标准时,亦可添加、更新或删除其它函数,但只要有任何改动,都要发送DiamondCut事件。
9.用户和钻石交互时,是和钻石的合约地址交互,钻石的合约地址是不变的,但钻石的代理合约地址可变(使用diamondCut函数改变)。
六 设计思路
1 关于函数选择器的使用
在本标准中,带有合约ABI的函数选择器能提供足够多关于一个函数的信息,方便用户的调用。虽说函数签名也包含函数的相关信息,但它不含返回值的信息,也无法提供函数是否只读的信息。此外,标准使用函数选择器也是因为它们在使用中更省燃料。
2 关于燃料费的考虑
当系统使用代理函数调用时,确实会产生额外的开销,但可以通过下面几种方式减小这种开销:
1)代理合约要尽量小,以减小燃料开销。函数越多自然消耗的燃料就越多。
2)钻石的大小没有上限,因此可以在其中加入优化燃料开销的函数。比如当设计者在钻石中实现ERC721标准时,当要进行批量转账时可以用ERC1412标准的批量转账函数以减小燃料开销。
3)因为代理合约可以尽量小,所以设计者在设计时可以用Solidity优化器产生尽量多的字节码但同时让代理合约使用尽量少的燃料。
3 关于合约存储的考虑
本标准并未定义数据的存储方式,但对此我们有如下建议:
关于钻石的存储:自Solidity 0.6.4开始,设计者可以在合约中的任何位置定义指向结构体的指针。这使得钻石和代理合约能分别创建各自的数据存储,避免互相干扰。更多细节可参考:New Storage Layout For Proxy Contracts and Diamonds.
继承的数据存储:由于钻石和它所有的代理合约都使用相同的存储空间,因此在钻石和代理合约的源代码中,相同的存储变量必须以相同的次序进行定义。这里提供一个具体设计的参考:
1)所有的存储变量都应该定义为internal。
2)创建一个存储合约,它包含钻石需要用到的存储变量。
3)让钻石继承该存储合约。
4)让代理合约继承该存储合约。
5)如果想增加一个新的代理合约,该合约用于增加新的存储变量,那就干脆创建一个新的存储合约并让其继承原有的存储合约,同时在这个新的存储合约中添加这些存储变量,使用这个新的存储合约和新的代理合约。
6)但凡想增加新的存储变量就重复第5步。
非结构化的存储:在某些情形下,设计者会使用汇编语言在某些特定的存储位置存储和读取数据。这个方法的优点是对先前使用过的存储位置,即便代理合约没有用过也不需要定义它们。
外部数据的存储可以用通用的API根据其数据类型进行存储,细节请参看ERC930。
不可更新的钻石(Immutable Diamond):
设计者可以设计一个不可更新的钻石----通过调用diamondCut函数删除diamondCut函数即可。删除此函数后,钻石将不再可进行任何改变。
函数的版本:
当用户调用某个函数时,可以通过函数所在的代理合约的地址判断该函数的版本。DiamondLoop接口中有个专门的函数facetAddress就是用于此目的。这个函数以函数选择器为参数,返回函数所在的代理合约的地址。
代理合约之间共享函数:
当我们需要调用另外一个代理合约中的函数时,可以采取下列方式:
1)将函数代码拷贝进本代理合约中。
2)将多个代理合约都有的函数放到另一个合约中,并让这些代理合约继承该合约。
3)用delegatecall调用其它代理合约中的函数,如下所示:
bytes memory myFunction = abi.encodeWithSignature("myFunction(uint256)", 4);
(bool success, uint result) = address(this).delegatecall(myFunction);
require(success, "myFunction failed.");
4 关于安全的考虑
所有权和认证
注意:关于钻石所有权和认证的设计与实现并不是本标准要讨论的内容,这里的示例仅供参考。
对钻石所有权和认证的设计可以有多种方式。其中对认证的设计可以简单可以复杂。钻石标准本身没有限制。比如对所有权和认证的处理可以就简单地用一个账户地址处理,该账户地址可以添加、更新或删除任何函数,也可以用DAO来处理。
钻石存储的安全
如果一个用户添加或更新了函数,不管他喜欢还是不喜欢,他实际上就改变了存储。这一方面很强大,另一方面也很危险。不过这也可以用来减小或规避风险,比如可以限制“谁”才能添加、更新、删除函数,限制“什么时候”才能添加、更新、删除函数。
对于限制“谁”,我们可以这么做:
1)只允许某个受信的用户或组织更新钻石
2)只允许某个DAO更新钻石
3)只允许多签地址更新钻石
4)只允许该钻石的所有者更新
5)把钻石定义为single cut diamond,不让任何人更新
对于限制“什么时候”,我们可以这么做:
1)只在系统的开发和测试阶段允许更新。当系统上线主网时,定义为single cut diamond
2)当功能定型后,去掉更新的功能
3)在diamondCut中编写进时间限制,限制其更新的时间。
参考链接
https://eips.ethereum.org/EIPS/eip-2535
- 说明
当有太多函数和代码时,以太坊合约可能会达到24KB的最大合约大小限制,EIP-2535(Diamond Standard)帮助解决这个问题!
该标准的制定者Nick Mudge在7月10日的博客文章中透露,尽管Vitalik Buterin坚持使用“代理合约”(指可以通过调用其他合约的功能来保持较小的合约)可能是解决这些限制的潜在方法,但他创建的标准化代理合约“钻石标准”可能是锦上添花。
根据Mudge的说法,EIP-2535钻石标准可以标准化程序员如何从任意数量的合约中创建小合约调用功能。实施钻石标准的合约称为钻石,以区别于常规合约和代理合约。
除了具有许多不同的方面和功能之外,钻石标准还具有灵活而透明的创建可升级钻石的方法。
所有如果你还在为ERC-1155合约的升级问题而烦恼,那么赶快了解下EIP-2535,因为就连ERC-1155标准也在它的升级部分,采用了EIP-2353……并且它还说:
为了减轻在更改合同地址时发出事件的需要,请考虑使用EIP-2535中所述的代理模式。 这还将具有为用户提供稳定的合同地址的额外好处。
还有,ERC-1155的作者Ronan Sandford已经在为EIP-2535开发工具:https://twitter.com/wighawag/status/1280992800545349644
EIP-2535简介
一个钻石是一组可以访问相同的存储变量并共享相同的以太坊地址的合约。
一个合约体系结构使得合约的升级变得更加灵活,突破合约大小限制,并且保持透明性。
既然能够减小合约,那么就能够优化Dapp(部分功能)的gas手续费!具有方法,请阅读Diamond Standard Reference Implementation。
摘要
一个钻石是一个代理合约,它支持同时使用多个逻辑或代理合约(delegate contrac)。在该标准中,逻辑或代理合约一词是多方面的。 我们知道真正的钻石可以有很多面。使用哪一面取决于调用哪个函数。每个方面都提供一个或多个函数。
一个钻石可以提供:
- 一种自动添加、替换和删除多个函数的方法(在同一transaction即事务中)。
- 一个显示添加、替换和删除了哪些函数的事件。
- 一种查看钻石以了解其函数的方法。
- 解决24KB最大合约大小限制。钻石可以是任意大小。
- 根据需要以及在需要时启用零,部分或全部钻石不变性。
- 专为工具和用户界面软件而设计。
EIP2535 钻石标准解读
其它进阶阅读
- Understanding Diamonds on Ethereum
- Ethereum's Maximum Contract Size Limit is Solved with the Diamond Standard
- Solidity Storage Layout For Proxy Contracts and Diamonds
- New Storage Layout For Proxy Contracts and Diamonds
- Diamond Setter
- Upgradeable smart contracts using the Diamond Standard
- buidler-deploy supports diamonds
- 制定了避免以太坊合约规模限制的新标准
如有您有任何问题,欢迎在这里交流。
或者直接联系作者:
- https://twitter.com/mudgen
- 该 Email 地址已受到反垃圾邮件插件保护。要显示它需要在浏览器中启用 JavaScript。
本提案已被以太坊官方收录(https://eips.ethereum.org/EIPS/eip-2569),因为时间关系,后续不再更新内容。如想看最新内容,请访问:
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2569.md
eip | 提案标题 | 作者 | 讨论 | status | type | category | created |
---|---|---|---|---|---|---|---|
2569 |
Saving and Displaying Image Onchain for Universal Tokens |
Hua Zhang (@dgczhh), Yuefei Tan (@whtyfhas), Derek Zhou (@zhous) |
就在这 |
Draft |
Standards Track |
ERC |
2020-03-28 |
Simple Summary简述
A set of interfaces to save an SVG image in Ethereum, and to retrieve the image file from Ethereum for universal tokens.
阅读全文: EIP-2569: 一切通证的图片的链上存储和显示
这意味着提案进入全行业视野!
欢迎大家参与提案的定稿(Final)讨论——用以太坊钱包(imToken、Trust、MetaMask)直接登录本站即可参与。
也欢迎大家参与我们为改进 ETH 2.0 验证者质押新策略而发起的新提案:
naturaldao.io/project/eip2794.html
正在修订中,敬请期待!
获得 1888 ISM、8.6 REP、自签名勋章一枚
获得 7288 ISM、33.2 REP、自签名勋章一枚、纪念币一枚(公开售价0.66ETH)、协调员竞选权
这是我们设计的颁发给验证者的荣誉勋章样稿
本提案智能合约代码(涉及Solidity、Vyper、Pathon和JavaScript四种开发语言):Github代码
感谢HackDAO参赛小组成员:周朝晖、臻励、张华、淳光、黎兆、范伟、谢谢谢谢T、CyberForker-MemeMaster、musk、QUEENA
Simple Summary
本提案是一个新的协议,它以新的质押机制,以及一个激动人心的荣誉制度,替换 ETH 2 验证者账户始终需要质押32ETH这一具有超高风险的规则。为 ETH 2 Staking 带来极其可靠的安全保障。
Motivation (Risk Analysis 风险分析)
根据ETH 2最新发布的规范,phase 0至少需要16,384个节点才会启动。这会将524288个ETH冻结至phase 1,而以太坊的一枝独秀,还可能吸引更多的验证者节点(validator count),以太坊官方的计划文档《Ethereum Roadmap》的Eth 2.0 Economics部分,最终验证者节点数预期的起点甚至高达1百万!这些毫无疑问都会助推ETH的价格上涨。
根据我们的经验,如果ETH价格上涨十倍,或者它发生2017年那样的暴涨,验证者就会有兑现(为法币)的强烈冲动,而届时因为32个ETH的成本太高,将很少有新的验证者节点产生。原有验证者账号暴减,而没有新验证者节点的快速补充,两种情况同时发生,那么ETH 2就可能毁于节点数暴减而带来的系统灾难。
所以我们必须有一个防止这两种情况同时发生的解决方案。
阅读全文: ETH 2.0 Staking改进提案(EIP-2794讨论稿)
项目总结,33分钟起:https://www.bilibili.com/video/BV1QT4y1j7ZJ?p=10