当我们开发智能合约时,我们有几种不同类型的可用存储:
- 易失性栈访问:堆栈(Stack)
- 易失性存储器访问:内存(Memory)
- 非易失性:存储(Storage)
此外,我们还提供了背景信息:
- 与合约相关的代码
- 访问交易的数据字段
如何使用不同的存储类型
堆栈
除了 STOP,JUMPDEST 和 INVALID 操作以外,所有以太坊虚拟机操作(EVM 操作码)都使用堆栈来读取或写入。但是,还有能够在不执行任何计算的情况下进行读取或写入数据的操作:
堆栈深度范围从 0 到最大值 1024
- POP:获取栈顶的值(级别 0)
- PUSH1...PUSH32 (PUSHX):在栈顶插入 X 个字节
- DUP1…DUP16 (DUPX):复制栈顶 X 处的值
- SWAP1…SWAP16 (SWAPX):交换 X 处和栈顶的值
内存
访问此内存(读取或写入)的操作有:
- CALLDATACOPY:读取交易的数据字段并将其加载到内存中
- CODECOPY:读取与当前合约相关联的代码并将其加载到内存中
- EXTCODECOPY:读取与外部合约相关联的代码并将其加载到内存中
- MLOAD:从内存读取一个值
- MSTORE:在内存中存储一个词或 32 字节的值
- MSTORE8:在内存中存储一个 8 字节的值
存储
与堆栈和内存不同,存储在此内存中的数据在交易访问的合约地址上是永久的。维护此内存的操作有:
- SLOAD
- SSTORE
要注意分别代表存储(Storage)和内存(Memory)的“S”和“M”。
成本
上述所有的操作都有以 Gas 为单位的操作成本。当用户发起一笔交易时,他/她都会以 ETH 计价给出单位 Gas 的价格。矿工已经配置了最低的 Gas 价格,较高的最低 Gas 价格会给矿工带来更多的利润,但是会需要放弃更多的交易。这就形成了一个用单位 Gas 来交易 ETH 的市场。代码消耗的 Gas 越少,执行代码所需的资源就会越有效率。下表根据数据的大小,展示了不同操作码的 Gas 开销:
其中最昂贵的是非易失性存储。当数据大小为几 KB 时,使用内存操作与使用堆栈操作的 Gas 成本是差不多的,但当数据大小增加时,成本呈指数级增加。这种关系如下图所示:
-3*+ROUND(POWER(,2)/512,0)-
如果我们基于https://ethgasstation.info 网站设置 Gas 的 ETH 价格:
-2018/01/14 12:17 AM-
我们就可以用 ETH 价格来估算出每种存储类型的成本:
- STD = 5gwei, FAST = 50gwei -
上表的值是以 ETH 为单位的价格。矿工将打包价格更高的交易,因此当用户为单位 Gas 支付更多的 ETH 时,他/她的交易就会被更快地打包进区块中。
最后,我们可以设置一个美元对 ETH 的汇率,例如,1356.30 美元:
-1356.3$ = 1 ETH, 2018/01/14 12:17 AM-
结论
将数据永久存储在以太坊中是极其昂贵的。使用以太坊存储数据没有任何意义。以太坊应该只存储正常运行所需要的数据,并将数据存储委托给其他解决方案:如 Swarm、Filecoin、IPFS 等等。一个不错的主意是将默克尔树的根哈希值作为数据篡改证明存储在外部服务器中。
此外,ETH 价格不可预知,可能的快速增长会导致操作的开销(以法定货币为单位)增长到更高的级别。如果发生这种情况,矿工必须调整单位 Gas 的最低 ETH 价格,以重新调整开销。
通过分析智能合约的 Gas 开销来控制操作的成本是非常重要的。
相关资料
- https://ethereum.github.io/yellowpaper/paper.pdf
- https://www.coindesk.com/ethereum-price/
- https://remix.ethereum.org
链接: https://medium.com/coinmonks/storing-on-ethereum-analyzing-the-costs-922d41d6b316