I propose an alternative design (which Nick said he also considered at some point), which in my opinion can bring bigger benefits than EIP-1087, at lower cost (by this I mean new opcodes with very simple semantics and gas accounting rules, and keeping the existing gas accounting rules for SSTORE intact).
as mentioned in that other thread EIP-1087:
one year ago there was a discussion between me, @chriseth and @pirapira about very similar approach. It would be great to hear their opinion.
As far as I remember, there were following aspects to discuss:
Which contracts from the call stack should be allowed to access (read/write) which transient variables?
Should be a new variable instance created on new recursive call (stack) or an existing instance should be reused?
one year ago we have decided to drop the transient storage EIP in favour to the idea of EIP-1087.
But if we review that decision once more, we should possibly think more about signalling we would like to implement.
Possibly we have more use cases than ReentranceLock here.
Thank you very much for this, I shall ask if they remember
Good question - I will need to update the EIP. The idea is that transient variables are private to the contract, in the same way the storage is
If a contract gets re-entered within the same transaction, it accesses the same transient storage. Otherwise it would like existing memory, but with different addressing.
Another use case (though a bit contrived at the moment) is passing back error messages from the deeper execution frames. Again, passing them via outputs is not reliable, since intermediate frames can modify them.
What happens if something writes to transient storage and then reverts? Would the transient storage changes be rolled back or retained for the remainder of the transaction (assuming not all gas was burned during the revert)?
Good question! The transient storage would be retained for the remainder of the transaction. Because otherwise the two use cases I am thinking about (reentrant lock and error message passing) are harder to program. Also, semantics of not interacting with reverts and invalid instructions makes implementation simpler. I will include this clarification into the EIP
As mentioned in the other thread, I think this option is less attractive because it introduces more complexity to the EVM, in the form of new opcodes, and another form of memory with new semantics. I think changing gas accounting around SSTORE/SLOAD is much simpler and more versatile.
Further, a change to SSTORE/SLOAD gas accounting will reduce gas costs for contracts already using storage for transient or repeated updates. Examples include contracts using storage to implement locks, and ERC20 tokens using âapproveâ between contracts. If transient storage is implemented instead, new standards would need to be developed to permit the use of transient storage for these purposes.
When implementing contract-proxies using DELEGATECALL, all direct arguments are relayed from the caller to the callee via the CALLDATA, leaving no room for meta-data between the proxy and the proxee. Also, the proxy must be careful about storage access to avoid collision with targetstorage-slots. Since transient storage would be shared, it would be possible to use transient storage to pass information between the proxy and the target.
Itâs not clear to me why you canât just pass the metadata along with the call data. Further, this approach would not work if you may end up with recursive calls; itâs the equivalent of using global variables.
Transient storage does not interact with reverts or invalid transactions, that means if a frame reverts, its effects on the transient storage remain until the end of the transaction.
I think this is a really bad idea. Every other state change is reverted when a call reverts or throws; introducing a new semantic for this one type of storage would be counterintuitive, and creates a special case that is likely to lead to a great number of nonobvious bugs and security issues.
I am now trying to see if the Transient Storage is attractive enough without comparison to the SSTORE reduction proposal, which I think stands well on its own.
Thanks for this feedback. You see, I am not very sure how this should work. But for concreteness, I choose non-revertability of the transient storage. It turns out that this non-revertability gives smart contracts a unique resource that wasnât available before (namely reliable communication from the re-enterancy frames that reverted/threw). But it could have the drawbacks you are talking about. I will try to make this more concrete with some POC.
If contract A (frame 1) calls into contract B, then B calls into A again (frame 2). Frame 2 of contract A reverts, and returns some data. Frame of contract B can choose to discard, or modify the return data instead of passing it verbatim to Frame 1 of contract A. Non-revertiable transient storage would allow Frame 2 of contract A pass any info to Frame 1 of contract A regardless of what contract B is doing.
Whatâs an actual use-case for this, though? Where would it be useful?
In general nonlocal returns are a big source of confusion and nonobvious control flow in traditional programming. Iâm not keen on adding them to the EVM.
I do not know yet. I only discovered this two days ago, after having answered @MicahZoltu question above. I decided then not to roll back my choice, but explore it a bit more. Thanks for this discussion, BTW
BTW, I can recognize here another dropped proposal I have made in ethereum/solidity
to support an trailing data in message call. Possibly it worth reviewing it once more.
You will need to point me to the existing implementation of ReentranceLock, I could not find it, sorry.
The reentrancy lock has to be unlocked after the call regardless of whether one uses storage or transient storage. Because after the call is complete, the lock is still locked. So no change in usage here, apart from the gas cost.
Transient Storage cannot be used directly to implement signalling between frames of different contracts. All interactions between distinct contracts can only happen via CALL and STATICCALL.
You will need to point me to the existing implementation of ReentranceLock, I could not find it, sorry.
Oh⌠I mean nothing special. Just a standard implementation with some further message call in the locked scope.
See Contract Mutex, modifier noReentrancy
I see what you mean. To read from other contractsâs storage, one would need to modify the TLOAD opcode to have 2 arguments, one for address of account you are reading, and the other - for the address of the âcellâ you are reading. It might not be a bad idea, actually.
One use case for contracts reading other contracts transient storage could be calling libraries via CALL (STATICCALL) instead of DELEGATECALL or CALLCODE, and passing structures (like trees and linked lists) without having to serialise them into input data. Calling via CALL and STATICCALL is arguably safer, because you donât give the callee access to your storage.
I thought about usual public accessors, not about extending TLOAD opcode.
Replacing accessor functions with ânativeâ read access per opcode deserves seperate EIP and cautios evalation. The idea to have a ânativeâ per-reference read-only access to data structures without serialization looks to me as a major change targeting EVM-2.0.
I need to compare it to library pattern.