'EXTSLOAD' opcode proposal


#1

I wanted to gather feedback around proposing a new EXTSLOAD opcode, which would allow a contract to read a storage position from another account.

While I understand that this could be regarded as a Bad Idea ™ since it promotes breaking encapsulation, I think it naturally follows EXTCODEHASH. Once you have validated that an account holds a particular kind of contract using EXTCODEHASH, you can safely use EXTSLOAD to check a storage position from that account, knowing that it will have the semantics you expect.

The gas cost for this operation could be much cheaper than actually performing a call to execute a getter from a contract, since it does not require executing (nor loading!) any code on the queried contract, but just retrieving a single storage location.

As for high-level usage of this opcode, this means that getters for a contract (assuming it will rely on a single implementation) could be implemented as methods from Solidity libraries that use this opcode behind the scenes to check the code hash and subsequently retrieve the value from storage.

contract Box {
  uint256 value;
  constructor (uint256 _value) public { value = _value; }
}

library BoxReader {
  function getValue(Box target) internal {
    // optionally use extcodehash on Box to validate it matches the code
    // use extsload on Box to retrieve the value
  }
}

contract Reader {
  using BoxReader for Box; 

  Box box;
  constructor() { box = new Box(); }

  function readBox() public {
    uint256 value = box.getValue();
    // ...
  }
}

Besides gas cost savings, the actual use case that drove me to this opcode was using EXTCODEHASH for validating implementation contracts behind DELEGATECALL proxy contracts. On a system that relies on this pattern for upgradeability, using EXTCODEHASH for checking that a contract has the expected code does not cut it, since it may be a proxy sitting in front of the actual implementation contract. This means that the validation would actually be a 3-step process:

  • EXTCODEHASH the target account to check it is indeed a proxy,
  • EXTSLOAD to retrieve the implementation contract’s address,
  • and then EXTCODEHASH the retrieved implementation address and compare it against the desired one

Given I’m not that familiar with the internals of EVM implementations, I wanted to gather some feedback around this before formally proposing it as an EIP. Thanks in advance for any comments!


#2

This is a bad idea. If a storage is marked as “private” this of course means that you can read it off-chain but it is private for a reason: external contracts are not allowed to read any storage. If someone wants to implement reading storage (either for all accounts or for some) they can just implement a function which other accounts can call. This would yield about 700 gas for the call plus 200 gas for the SLOAD and some extra overhead for the function selector / memory expansion.


#3

Hey Jochem! I understand that reading storage can be implemented as a getter if the contract wants to expose that value. The point of the proposal is coming up with a cheap way (in terms of gas) to go around getters when dealing within a set of closed and well-known contracts, since it removes the need to set up a new context for the CALL, loading the code, running the function selector, etc.


#4

It’s worth noting that the gas cost for EXTSLOAD would have to be quite high - much higher than SLOAD because it requires the target account and it’s storage to be loaded. That’s at least 50% of the cost of performing a call (the remaining being loading the other account’s code and a small amount of time creating a new EVM frame). So it would become more expensive to use EXTSLOAD by about the second or third value loaded that way.


#5

related:

may be