ethereum/EIPs-170 Contract code size limit

eip title author type category status created
170
Contract code size limit
Vitalik Buterin
Standards Track
Core
Final
2016-11-04

 

Hard fork

Spurious Dragon

Parameters

  • FORK_BLKNUM: 2,675,000
  • CHAIN_ID: 1 (main net)

Specification

If block.number >= FORK_BLKNUM, then if contract creation initialization returns data with length of more than 0x6000(2**14 + 2**13) bytes, contract creation fails with an out of gas error.

如果合约的数据大小大于0x6000,那么该合约的创建将会失败

Rationale

Currently, there remains one slight quadratic vulnerability in Ethereum: when a contract is called, even though the call takes a constant amount of gas, the call can trigger O(n) cost in terms of reading the code from disk, preprocessing the code for VM execution, and also adding O(n) data to the Merkle proof for the block's proof-of-validity. At current gas levels, this is acceptable even if suboptimal. At the higher gas levels that could be triggered in the future, possibly very soon due to dynamic gas limit rules, this would become a greater concern—not nearly as serious as recent denial of service attacks, but still inconvenient especially for future light clients verifying proofs of validity or invalidity. The solution is to put a hard cap on the size of an object that can be saved to the blockchain, and do so non-disruptively by setting the cap at a value slightly higher than what is feasible with current gas limits.

目前ethereum仍有一个脆弱点:当一个合约被调用时,即使它使用的是固定的gas值,这个调用仍然会触发O(n)量级的花销在从硬盘中读取代码、对VM执行的代码进行预处理和将O(n)数据添加到Merkle证明中以验证块的有效性上。在目前的gas水平下,这是可以接受的,即使不是最理想的。但是在未来可能被触发的较高的gas水平的情况下,可能很快由于动态气体限制规则而其将成为一个更大的担忧——可能不像最近的拒绝服务攻击那么严重,但仍然不方便,特别是对未来的轻客户端验证有效性或无效的证明。

解决方案是对一个对象的大小设置一个硬盘的上限(在这里既为合约的数据大小不大于0x6000),该上限可以保存到区块链,并通过将上限设置在略高于当前气体限制的可行值的情况下进行非干扰性设置。

 

References

  1. EIP-170 issue and discussion: https://github.com/ethereum/EIPs/issues/170
  2. pyethereum implementation: https://github.com/ethereum/pyethereum/blob/5217294871283d8dc4fb3ca9d8a78c7d416490e8/ethereum/messages.py#L397

该代码实现:

  • EIP 170: Contract code size limit - changes the maximum code size that a contract on the blockchain can have. This update prevents an attack scenario where large pieces of account code can be accessed repeatedly at a fixed gas cost. The maximum size has been set to 24576 bytes, which is larger than any currently deployed contract.改变区块链中能够有的最大代码大小.盖更新将防止在设定好的gas开销中,大片账户代码被重复接收的攻击方案,最大的大小设置为24576字节,这比目前所有被设置的合约都大
def create_contract(ext, msg):
    log_msg.debug('CONTRACT CREATION')

    code = msg.data.extract_all()

    if ext.tx_origin != msg.sender:
        ext.increment_nonce(msg.sender)

    if ext.post_metropolis_hardfork() and msg.sender == null_address:
        msg.to = utils.mk_contract_address(msg.sender, 0)
        # msg.to = sha3(msg.sender + code)[12:]
    else:
        nonce = utils.encode_int(ext.get_nonce(msg.sender) - 1)
        msg.to = utils.mk_contract_address(msg.sender, nonce)

    b = ext.get_balance(msg.to)
    if b > 0:
        ext.set_balance(msg.to, b)
        ext.set_nonce(msg.to, 0)
        ext.set_code(msg.to, b'')
        # ext.reset_storage(msg.to)

    msg.is_create = True
    # assert not ext.get_code(msg.to)
    msg.data = vm.CallData([], 0, 0)
    snapshot = ext.snapshot()
    if len(ext.get_code(msg.to)):
        log_msg.debug('CREATING CONTRACT ON TOP OF EXISTING CONTRACT')
    #     return 0, 0, b''

    ext.set_nonce(msg.to, 1 if ext.post_spurious_dragon_hardfork() else 0)//
    res, gas, dat = _apply_msg(ext, msg, code)

    log_msg.debug('CONTRACT CREATION FINISHED', res=res, gas=gas, dat=dat if len(dat) < 2500 else ("data<%d>" % len(dat)))

    if res:
        if not len(dat):
            # ext.set_code(msg.to, b'')
            return 1, gas, msg.to
        gcost = len(dat) * opcodes.GCONTRACTBYTE
        if gas >= gcost and (len(dat) <= 24576 or not ext.post_anti_dos_hardfork())://合约创建钱判断gas的使用,合约的大小等
            gas -= gcost
        else://否则创建失败
            dat = []
            log_msg.debug('CONTRACT CREATION FAILED', have=gas, want=gcost, block_number=ext.block_number)
            if ext.post_homestead_hardfork():
                ext.revert(snapshot)
                return 0, 0, b''
        ext.set_code(msg.to, bytearray_to_bytestr(dat))
        log_msg.debug('SETTING CODE', addr=encode_hex(msg.to), lendat=len(dat))
        return 1, gas, msg.to
    else:
        ext.revert(snapshot)
        return 0, gas, dat