51 large

EIPS

rubyu2 · 于 发布 · 最后由 rubyu2回复 · 761 次阅读

EIP:Ethereum improvement proposal.

EIP-3: Addition of CALLDEPTH opcode

建议增加CALLDEPTH操作码
之前我们提到过利用调用栈深度来进行攻击的方式(send操作也可以看作调用外部合约),当然不止在发送ether时可以利用这种方式,通过call或者其他调用外部合约的操作码也可以利用这种攻击方式,所以有人提议智能合约中应该增加 CALLDEPTH操作码来返回调用栈深度。当前,建议所有调用外部合约的操作,都应该判断调用结果。

EIP: 5 Title: Gas Usage for RETURN and CALL*

当前在EVM中执行调用另外一个合约的function时,返回的值是固定长度的。当然可以返回更大的值,但是需要提前分配内存空间,而返回值可能不会全部使用分配的内存,这样就造成内存浪费,但是同时必须支付这笔gas,所以这样的设计显然是昂贵不行的。
这个EIP旨在返回strings类型或者变长的array。并且保证消耗的gas只是用于支付返回值使用的内存。

具体做法是:在使用CALL类的方法时,指定input_start, input_size, output_start, output_size,在执行操作码前,先支付input_start + input_size内存应付的gas,在调用后,根据实际的返回值占用内存n,得到min(output_size, n),根据这个值来支付剩余的gas。

这样做,首先因为调用外部合约是无法知道返回值的长度的,分配任意长度是非常危险的,所以要预先制定返回长度,同时为无用的内存支付gas也是不必要的。

这个提议使得调用合约可以非常方便的返回string和数组。但是因为要之前的call类方法的参数和gas消耗机制,可能会需要一些兼容性的工作。

在讨论中https://github.com/ethereum/EIPs/issues/8 ,gavofyork 指出

Disadvantage of original proposal:

CALL breaks the rule that no opcode can run OOG once the execution of the op-code has started.
To be clear, until now the formal spec for the EVM dispatch sequence is:

  1. Decode instruction.
  2. Check gas requirements (bail with OOG exception if not met).
  3. Deduct gas.
  4. Execute instruction.
  5. Refund unused gas.

This would fundamentally alter to:

  1. Decode instruction.
  2. Check gas requirements (bail with OOG exception if not met).
  3. Deduct gas.
  4. Execute instruction.
  5. Check additional gas requirements (bail with OOG exception if not met).
  6. Deduct/refund additional/unused gas. This would add a base layer of additional complexity and reduce our ability to reason about edge circumstances particularly regarding possible attack scenarios due to gas usage.

这种做法其实修改了,原有的EVM dispatch的顺序,在调用完call之后,还需要检查增加的gas消耗(根据返回值内存使用情况计算)。

While indeed, this adds another step for call-like opcodes, you are hiding a lot of complexity that is already present, especially with regards to call-like opcodes:

  1. decode instruction
  2. check stack requirements (throw OOG if not met)
  3. calculate and check gas requirements (throw OOG if out of gas)
  4. enlarge memory if necessary
  5. check that the call depth is not too large (push 0 to stack otherwise)
  6. check that we have enough ether to send along with the call (push 0 to stack otherwise)
  7. if checks succeeded: perform the call (passing on memory pointers for to a new stack frame)
  8. copy output into memory (that step might be done as the last one of the inner stack frame)
  9. refund gas

And perhaps some other steps I have missed here. With this Proposal, this turns into:

  1. decode instruction
  2. check stack requirements (throw OOG if not met)
  3. calculate and check gas requirements (throw OOG if out of gas)
  4. enlarge memory if necessary
  5. check that the call depth is not too large (push 0 to stack otherwise)
  6. check that we have enough ether to send along with the call (push 0 to stack otherwise)
  7. if checks succeeded: perform the call (passing on memory pointers for to a new stack frame)
  8. calculate and check gas requirements for writing output into memory (throw OOG if out of gas)
  9. enlarge memory if necessary
  10. copy output into memory
  11. refund gas

Or, to make that even simpler: Every opcode and step that accesses memory grows memory and this growing of memory has to be paid with gas. Before this proposal, the call semantics were:

  1. access memory both at input and output area to grow it artificially
  2. perform the call, passing pointers into input and output area
  3. have the data magically appear in the output area unknowing how much has been written

With this proposal:

  1. access memory at input
  2. perform the call, passing input data via a pointer
  3. copy output into output area

EIP7: DELEGATECALL

DelegateCall和CallCode非常像,不同的在于它将当前作用域的sender和value传到被调用的子contract,这样被调用的合约和当前合约就是相同的上下文环境(调用栈不同,同时会有gas消耗)

  • 87 large
    marshluca

    看來早就有人提出來了,只是一直是 draft version.

  • 51 large
    rubyu2

    @marshluca EIP-3很早就有人提过了,这个问题其实相对还可以接受,可能就需要每次判断外部调用的结果。
    EIP-5的问题比较严重,现在调用外部合约只能返回是否正确的bool值,返回string和不定长数组还是非常有用的。