Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scratch space #167

Closed
vbuterin opened this issue Oct 29, 2016 · 4 comments
Closed

Scratch space #167

vbuterin opened this issue Oct 29, 2016 · 4 comments
Labels

Comments

@vbuterin
Copy link
Contributor

vbuterin commented Oct 29, 2016

Specification

If block.number >= FORK_BLKNUM, add two precompile contracts at addresses 0x0e and 0x0f. Both contracts shard 1 gas per byte of calldata plus 20 base gas cost. The contracts work as follows:

  • 0x0e: All bytes of the calldata except for the first 32 are saved in a new key/value map using sha3(sender) xor sha3(target) as a key where target is the address in the first 32 bytes and sender is the sender of the call.
  • 0x0f: Immediately return the calldata saved in the key/value map using sha3(sender) xor sha3(target) as a key where target is the address in the first 32 bytes of calldata as a key and sender is the sender of the call.

The key/value map is reset at the end of processing every transaction.

Rationale

This allows for certain kinds of applications to be built more cheaply, safely and efficiently. For example, consider the use case of sending ERC20 tokens along with a call. The current workflow is:

  1. Sender authorizes token to withdraw funds
  2. Sender calls contract
  3. Contract calls token to withdraw funds, and checks the response to make sure that the withdrawal succeeded. If it did then it continues executing.

However, this is ugly, requiring ERC20 tokens to have a complex withdrawal feature, and is gas-inefficient, requiring four SSTOREs: (i) creating the withdrawal authorization, (ii) decreasing sender's ERC20 token balance, (iii) increasing the contract's ERC20 token balance, (iv) removing the withdrawal authorization. Also, it requires a call to the token contract as the first operation of the contract's code, before other state changes; if the token is untrusted this may lead to re-entrancy vulnerabilities.

Using scratch space, we can change the mechanism to the following:

  1. Sender calls token to send funds to the contract. Token saves in the recipient's scratch space that this was the last transaction that took place.
  2. Sender calls contract
  3. Contract checks scratch space and makes sure that the last transaction of the token sent the correct number of coins, from the sender, to their account. If the check passed, the contract empties the scratch space and continues executing.

This now only requires two SSTOREs, to do the actual balance change, and so has a gas cost that is proportional to the actual cost of what took place. Re-entrancy is no longer an issue.

Another use case is mutex locks - a contract may put a mutex flag into some scratch space, using a random address as a target, thereby avoiding the 10000 gas cost of having a mutex feature. A third is as a general call-deferral technique to avoid re-entrancy issues, where instead of making a call immediately, the contract saves a record in scratch space and then allows the sender to later call the contract and "consume" the record. A final important use case is for keeping track of intermediate variables for purposes of gas payment in the context of EIP #86.

@Arachnid
Copy link
Contributor

Wouldn't this make more sense as an EVM feature?

Alternately, could the gas model be adjusted so that nearly all the cost of an SSTORE is refunded at the end of the call if it resulted in no committable changes to state?

@tawaren
Copy link

tawaren commented Mar 1, 2017

I would like to present another use-case, where this can be of great use.
It could be used to pass & return values for a delegate call, because the sender would stay the same even in the delegate, the delegate would have access to the scratch space of the calling contract. At the moment this either has to be done by storing it in the storage, which is expensive or over the calldata/return, which has the limitation of being linear (and fixed size/upper bounded in case of return), where the scratch space would be key value based and not size limited. From a solidity view (or generally a HLL perspective), this would allow to create temporary dynamic sized arrays or mappings and pass them to library calls and especially return them from library calls (witch would otherwise be impossible because the return size must be known in advance). Big applications, that consist of one central contract storing all the data and a lot of libraries providing the functionality could profit from this as well. For such application it would allow to have per transaction state that can be accessed from each library without the need of passing it along with each call.

@github-actions
Copy link

github-actions bot commented Jan 5, 2022

There has been no activity on this issue for two months. It will be closed in a week if no further activity occurs. If you would like to move this EIP forward, please respond to any outstanding feedback or add a comment indicating that you have addressed all required feedback and are ready for a review.

@github-actions github-actions bot added the stale label Jan 5, 2022
@github-actions
Copy link

This issue was closed due to inactivity. If you are still pursuing it, feel free to reopen it and respond to any feedback or request a review in a comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants