EIP-1682: 存储租金
Authors | Felix J Lange (@fjl), Martin Holst Swende (@holiman) |
---|---|
Created | 2018-11-10 |
Discussion Link | https://ethereum-magicians.org/t/storage-rent-eip/2357 |
摘要
本 EIP 描述了一种对状态中的数据收费,以及“存档”不再被支付的数据的方案。它还描述了“存档”数据如何恢复。
动机
以太坊区块链以目前的形式是不可持续的,因为它会无限增长。任何区块链都是如此,但以太坊的增长速度超过大多数链。存在许多减缓增长的实现策略。一种常见的策略是“状态修剪”,它会丢弃历史状态,仅保留合约数据的活动副本和一些最近的版本,以处理短程链重组。一些实现还采用压缩技术来尽可能地保持活动状态副本的大小。
即使应用了高级存储优化,今天参与共识的完整节点也需要存储大量数据。未来的存储需求是无限的,因为按照协议规定,存储在合约中的任何数据都必须永久保留。本 EIP 试图通过添加新的共识规则来纠正这一点,这些规则对以太坊状态的大小设置了上限。
添加这些新规则会改变系统的基本保证,并且需要进行硬分叉。以太坊用户已经为创建和修改账户及其存储条目付费。在本 EIP 引入的规则下,用户还必须付费以保持账户可访问。类似的租金方案在 EIP-103 中提出,但即使在当时也被拒绝,因为该提案会扰乱人们的期望。作为以太坊的实施者,我们仍然认为状态租金是以太坊区块链长期可持续发展的正确道路,并且其不良影响可以通过链下工具和谨慎的设计来克服。
规范
随时间存储账户的成本称为 rent
(租金)。应付的 rent
金额取决于账户的大小。用于支付 rent
的 ether
(以太币)会被销毁。每当账户被触及时,就会扣除 rent
。
rent
可以从账户的常规 balance
(余额)或从其“rent balance”(租金余额)中支付。可以通过新的 EVM 操作码为账户提供 rent balance
。收取 rent
时,首先从 rent balance
中扣除。当 rent balance
为零时,则从账户的常规 balance
中扣除。
分离 balance
和 rent balance
的原因是某些合约不接受 ether
发送,或者总是将整个余额发送到其他目的地。对于这些情况,需要单独的 rent balance
。
当账户的 balance
不足以支付租金时,该账户将变为 inactive
(非活跃)。其存储和合约代码将被移除。非活跃账户无法与之交互,即它的行为就像没有合约代码一样。
可以通过重新上传其存储来恢复非活跃账户。要恢复非活跃账户 A
,需要创建一个新的账户 B
,其中包含任意代码,并使用 SSTORE
操作修改其存储,直到它与 A
的存储根匹配。账户 B
可以通过 RESTORETO
操作码恢复 A
。这意味着恢复账户的成本相当于通过连续的 SSTORE
操作重新创建它。
状态的变更
在顶层,向账户 trie 添加了一个新的键 size
。此键跟踪所有账户(包括存储 trie 节点)中的 trie 节点总数。为了跟踪租金,账户条目的结构也发生了变化。
在处理此 EIP 生效的区块之前,客户端会迭代整个状态一次,以计算 trie 节点的数量并将所有账户的表示形式更改为新格式。
账户表示
account = [nonce, balance, storageroot, codehash, rentbalance, rentblock, storagesize]
每个账户都有三个额外的属性:rentbalance
、rentblock
和 storagesize
。
rentbalance
字段跟踪账户可用的 rent balance
金额。在自毁时,任何剩余的 rent balance
都会转移给受益人。对账户的任何修改都会重新计算其当前的 rent balance
。
rentblock
字段跟踪上次重新计算 rent balance
的区块号。创建时,此字段将使用当前区块号进行初始化。每当修改账户时,rentblock
也会使用当前区块号进行更新。
storagesize
字段跟踪与账户相关的存储量。它是一个包含当前设置的存储槽数量的数字。非活跃账户的 storagesize
为零。
收取租金
有一个新的协议常量 MAX_STORAGE_SIZE
,它指定了状态树节点的数量上限:
MAX_STORAGE_SIZE = 2**32 # ~160GB 的状态
每个区块的“存储费用因子”由此常量得出,因此费用会随着接近限制而增加。
def storagefee_factor(block):
ramp = MAX_STORAGE_SIZE / (MAX_STORAGE_SIZE - total_storage_size(block))
return 2**22 * ramp
处理一个区块时,在处理完交易后,会从该区块中交易修改的所有账户中扣除 rent
。每个账户应付的金额基于该账户的存储大小。
def rent(prestate, poststate, addr, currentblock):
fee = 0
for b in range(prestate[addr].rentblock+1, currentblock-1):
fee += storagefee_factor(b) * prestate[addr].storagesize
return fee + storagefee_factor(currentblock) * poststate[addr].storagesize
def charge_rent(prestate, poststate, addr, currentblock):
fee = rent(prestate, poststate, addr, currentblock)
if fee <= poststate[addr].rentbalance:
poststate[addr].rentbalance -= fee
else:
fee -= poststate[addr].rentbalance
poststate[addr].rentbalance = 0
poststate[addr].balance -= min(poststate[addr].balance, fee)
poststate[addr].rentblock = currentblock
新的 EVM 操作码
PAYRENT <amount> <addr>
在任何时候,都可以通过 PAYRENT
操作码来增加账户的 rent balance
。PAYRENT
从执行操作码的账户中扣除给定的 ether
金额,并将其添加到指定为受益人的地址的 rent balance
中。
任何参与者都可以为任何其他参与者支付租金。
Gas 成本:待定
RENTBALANCE <addr>
可以通过 RENTBALANCE
操作码来查询帐户的rent balance
。它将给定地址的rentbalance
字段推送到堆栈。
Gas 成本:类似于 EXTCODEHASH
。
SSIZE <addr>
此操作码将给定帐户的 storagesize
字段推送到堆栈。
Gas 成本:类似于 EXTCODEHASH
。
RESTORETO <addr> <codeaddr>
此操作码恢复给定地址处的非活动帐户。这有点像 SELFDESTRUCT
,但具有更具体的语义。
addr
处的帐户必须为 inactive
(即具有 storagesize
零),并且其 storageroot
必须与执行 RESTORETO
的合约的 storageroot
匹配。codeaddr
指定从中获取代码的合约的地址。codeaddr
帐户的代码必须与 addr
的 codehash
匹配。
如果满足所有这些前提条件,则 RESTORETO
会将执行操作码的帐户的存储转移到 addr
,并将其 storagesize
重置为存储的完整大小。addr
的代码也会被恢复。RESTORETO
还会将任何剩余的余额和租金余额转移到 addr
。执行 RESTORETO
的合约将被删除。
Gas 成本:待定
理由
为什么我们需要单独的租金余额?
帐户需要单独的租金余额,因为某些合约是不可支付的,即它们拒绝常规价值转移。此类合约可能无法维持自身运行,但是这些合约的用户可以通过为其支付租金来保持它们的运行。
拥有额外的余额也使代表用户持有余额的合约更容易。考虑典型的众筹示例,该合约在达到一定余额后会更改行为,并跟踪各个用户的余额。从合约的主要余额中扣除租金会弄乱合约的帐户,如果未达到阈值余额,则使其无法准确地偿还用户。
为什么需要恢复?
以太坊提供的基本保证之一是只有合约本身才能更改合约存储,并且存储将永久存在。如果您在合约中持有 token 余额,您将永远拥有这些 token。通过添加恢复,我们可以一定程度上保持这一保证。
实现影响
拟议的更改试图适应现有的状态转换模型。请注意,没有在帐户无法支付租金时立即停用帐户的机制。用户必须触摸帐户以确保删除其存储,否则我们需要在辅助数据结构中跟踪所有帐户及其租金要求。
向后兼容性
待定
测试用例
待定
实现
待定
版权
在 CC0 下放弃版权和相关权利。
Citation
Please cite this document as:
Felix J Lange (@fjl), Martin Holst Swende (@holiman), "EIP-1682: 存储租金 [DRAFT]," Ethereum Improvement Proposals, no. 1682, November 2018. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1682.