Skip to content
This repository has been archived by the owner on Oct 4, 2019. It is now read-only.

ECIP-1040: Generalized Account Versioning Scheme for Hard Fork #86

Closed
wants to merge 2 commits into from

Conversation

sorpaas
Copy link
Contributor

@sorpaas sorpaas commented Dec 30, 2017

(Rendered)

Abstract

This defines a method of hard forking while maintaining the exact functionality of existing account by allowing multiple versions of the virtual machines to execute in the same block. This is also useful to define future account state structures when we introduce the on-chain WebAssembly virtual machine.

Motivation

When EIP150 was activated on Ethereum Classic, it was critised that it changes the behaviour of many existing smart contracts on the blockchain, and made several ones unusable. The same thing will also happen in the future if we decided to add some of the Byzantium hard forks to Ethereum Classic.

Take the REVERT opcode as an example. Currently there're already many contracts on the Ethereum Classic blockchain with the REVERT opcode. This opcode is now considered invalid. When EVM executes to REVERT, it will treat it as an invalid opcode, consume all the gases, and return an error. However, when we hard fork to define the actual REVERT, existing users would suddenly find that the behavior of their existing contract would have changed -- instead of consuming all gases, it will now only consume less than the given gas limit and return. Several on-chain contract might rely on the old behavior for some of their functionality. Besides, if a dapp developer does not follow the hard fork, he or she will suddenly find that the dapp might suddenly break because the existing method to detect whether a contract execution succeeds or not does not work any more.

One of the important mission of blockchain is to "become your own bank". In Bitcoin, that means once a transaction is executed, the fund will always be controlled by the receiver unless he or she sends it out again. In Ethereum, an additional important property is that, once a smart contract is deployed, it should always execute in the original expected manner. As a result, it is important to have a way to maintain compatibility of existing contracts and at the same time easily add new protocol features.

Note that this specification might not apply to all hard forks. We have emergency hard forks in the past due to network attacks. Whether they should maintain existing account compatibility should be evaluated in individual basis. If the attack can only be executed once against some particular contracts, then the scheme defined here might still be applicable. Otherwise, having a plain emergency hard fork might still be a good idea.

### Abstract

This defines a method of hard forking while maintaining the exact functionality of existing account by allowing multiple versions of the virtual machines to execute in the same block. This is also useful to define future account state structures when we introduce the on-chain WebAssembly virtual machine.

### Motivation

When EIP150 was activated on Ethereum Classic, it was critised that it changes the behaviour of many existing smart contracts on the blockchain, and made several ones unusable. The same thing will also happen in the future if we decided to add some of the Byzantium hard forks to Ethereum Classic.

Take the REVERT opcode as an example. Currently there're already many contracts on the Ethereum Classic blockchain with the REVERT opcode. This opcode is now considered invalid. When EVM executes to REVERT, it will treat it as an invalid opcode, consume all the gases, and return an error. However, when we hard fork to define the actual REVERT, existing users would suddenly find that the behavior of their existing contract would have changed -- instead of consuming all gases, it will now only consume less than the given gas limit and return. Several on-chain contract might rely on the old behavior for some of their functionality. Besides, if a dapp developer does not follow the hard fork, he or she will suddenly find that the dapp might suddenly break because the existing method to detect whether a contract execution succeeds or not does not work any more.

One of the important mission of blockchain is to "become your own bank". In Bitcoin, that means once a transaction is executed, the fund will always be controlled by the receiver unless he or she sends it out again. In Ethereum, an additional important property is that, once a smart contract is deployed, it should always execute in the original expected manner. As a result, it is important to have a way to **maintain compatibility of existing contracts** and at the same time **easily add new protocol features**.

Note that this specification might not apply to all hard forks. We have emergency hard forks in the past due to network attacks. Whether they should maintain existing account compatibility should be evaluated in individual basis. If the attack can only be executed once against some particular contracts, then the scheme defined here might still be applicable. Otherwise, having a plain emergency hard fork might still be a good idea.
@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

I'd like to note that it is important to maintain compatibility with Ethereum smart-contracts (if you don't want to break compatibility and develop your own compiler and IDE like remix).

REVERT opcode does not rollback events, thus this opcode is necessary for debugging. Personally, I don't see any opportunity to create a DApp that will rely on "burn the rest of the gas of failed transaction" functionality. Please, provide any examples if it is possible.

https://ethereum.stackexchange.com/questions/29049/debugging-with-revert-byzantium-eip-206

I think that it is necessary to implement REVERT opcode because (1) necessity of debugging functionality and (2) maintaining compatibility with Ethereum smart-contracts.

ethereum/EIPs#206

@sorpaas
Copy link
Contributor Author

sorpaas commented Dec 30, 2017

@Dexaran Can you elaborate? Because this ECIP is about how we can better implement REVERT opcode and other Byzantium features without breaking existing smart contracts.

"burn the rest of the gas of failed transaction" is being relied on right now by many old dapps to detect whether a transaction succeeds or not. But please note that this and REVERT opcode are not what this ECIP is mainly about.

@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

"burn the rest of the gas of failed transaction" is being relied on right now by many old dapps to detect whether a transaction succeeds or not.

I do not think so. As a DApp developer, I can say that it is not a good practice to evaluate whether the transaction fails or not via watching the gas consumption. Btw, there is not that much DApps on ETC.
Can you give at least one example? I do not think that the issue of maintaining the exact functionality of contracts is relevant.

this ECIP is about how we can better implement REVERT opcode and other Byzantium features without breaking existing smart contracts.

Byzantium protocol upgrade have not introduced any breaking changes. I'm in favor of implementing it exactly as Ethereum Foundation did. Rationale: maintain compatibility of Solidity language, compiler and VM.

@sorpaas
Copy link
Contributor Author

sorpaas commented Dec 30, 2017

Yeah let's focus on the discussion in the second part. I only used REVERT as an example here and IMHO it's not the main point.

Byzantium protocol upgrade have not introduced any breaking changes.

Many people would argue otherwise, and some would even argue that EIP150, which is merely gas table changes, introduces breaking changes because it made several old contracts too costly to ever be used.

The thing we want to discuss here, is that is there a way to satisfy both people who think like you and people who think like above? And I argue it is possible. What we do here is essentially not changing old contracts but disallow creating them. So you want REVERT opcode? Just deploy a new contract. Don't like REVERT code and want to keep it the old way? Just continue to use the old contract.

Another thing to note here is that we do not break compatibility with Ethereum in this ECIP. End-user APIs would be exactly the same because the additional fields in receipts that has not previously existed can be filled in with null or a default value. We can have the exact API as Ethereum Foundation if we want.

@sorpaas
Copy link
Contributor Author

sorpaas commented Dec 30, 2017

Also note, that EVM for new contracts work exactly the same as Ethereum (if we decided to add all Byzantium upgrades). As a result, compatibility of Solidity language, compiler and VM is maintained. We have an additional advantage that we kept the old contracts continue to work and satisfy an additional set of people.

@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

The thing we want to discuss here, is that is there a way to satisfy both people who think like you and people who think like above? And I argue it is possible. What we do here is essentially not changing old contracts but disallow creating them. So you want REVERT opcode? Just deploy a new contract. Don't like REVERT code and want to keep it the old way? Just continue to use the old contract.

What about those who relied on REVERT behavior already? For example DexNS contracts are relying on REVERT : https://github.com/EthereumCommonwealth/DexNS/blob/master/DexNS_Frontend.sol#L79-L89

I don't want DexNS users to lose the gas and I was aware of REVERT when designing DexNS. I suppose that all the DApp developers were aware of it and used throw insted if they don't want to rely on revert().

I don't see any reason to overcomplicate the Geth code with changes that are not really necessary. And I don't see any value in keeping old smart-contracts operating exactly the same as it was before the HF.

@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

DexNS contract is immutable and it is relying on REVERT opcode that was described here: ethereum/EIPs#206

The main goal of Ethereum Classic is to provide a truly decentralized, immutable and censorship-resistant DApp development platform. The main goal is to keep an original history of Ethereum's transactions. Not to define new protocols.

DexNS contract is designed so that it is relying on REVERT opcode (that was promised to be implemented in future at the moment of DexNS development) and I want it to rely on it.

@sorpaas
Copy link
Contributor Author

sorpaas commented Dec 30, 2017

What happens in the blockchain is that right now in Ethereum Classic REVERT is just an invalid opcode and it has no other meanings. Consider we reject REVERT hard fork on ETC, your contract would still continue to work forever. If otherwise, and you want to take advantage of the reduced gas cost, you just deploy a new contract.

I know we all developers only want our own code to work. However, it is also important to consider that if we don't keep everyone else who consider the immutability philosophy more important, many people may reject the hard fork and REVERT on ETC might never happen or there will be a chain split. I would argue that keeping everyone together is more important. Plus here we have a way to also keep everyone happy.

Regarding complexity, existing implementations like geth and parity already use parameterized EVM. So the complexity added is essentially just changing a memory pointer in an if statement.

@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

If otherwise, and you want to take advantage of the reduced gas cost, you just deploy a new contract.

I don't want to deploy a new contract. I want an already-existing contract to work exacly as programmed and it is programmed to rely on this opcode: ethereum/EIPs#206

Again, we have not promised to define new protocols. We have promised to keep the original history of transactions. I don't want to change history of transactions or violate immutability property. I want to rely on changes that were promised and write immutable contracts so that they will work according to the defined protocol without necessity to redeploy them or apply any other changes to make immutable contract to work exactly as if they are immutable.

Regarding complexity, existing implementations like geth and parity already use parameterized EVM. So the complexity added is essentially just changing a memory pointer in an if statement.

I will argue that every unnecessary change from the original Ethereum's geth implementation will overcomplicate further compatibility maintenance.

@sorpaas
Copy link
Contributor Author

sorpaas commented Dec 30, 2017

I don't lean on either side of the immutability philosophy, but there are indeed people who think immutability differently compared with you.

With DexNS if you somehow rely on the new REVERT behavior, you might want to take the initiative for starting the REVERT hard fork as currently it has not yet been proposed here. And a hard fork on ETC might take months/years to happen. Old invalid opcode REVERT behavior would probably already work for you for most of the cases. So things should be fine.

@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

When EIP150 was activated on Ethereum Classic, it was critised that it changes the behaviour of many existing smart contracts on the blockchain, and made several ones unusable.

Can you give me any links or references so that I can read what exactly was critised and who have ciritised it?

I have not heard critics about it and I'd like to read it first.

So things should be fine.

You are just proposing to violate exactly what we promised to do: "Keep the original history of Ethereum transactions". No one has ever proposed to keep protocol immutability.

Ethereum Classic is a decentralized platform that runs smart contracts: applications that run exactly as programmed...

And DexNS is programmed to rely on opcode that exists on Ethereum at the moment of DexNS development, isn't it?

@sorpaas
Copy link
Contributor Author

sorpaas commented Dec 30, 2017

@Dexaran I think @elaineo may be able to throw some lights on this: https://twitter.com/eiaine/status/936068772888158209

But again, whether EIP150 or REVERT is good or bad is not the main point of the ECIP. I'm happy to remove reference to EIP150 and REVERT from the documents and the argument still stands.

@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

I think that this ECIP should not be applied in the event that any changes have already been specified on Ethereum, but their presence / absence has not been determined in the ETC.

I think that when any EIP comes to existence then we should create a ECIP and come to a consensus of whether it will be implemented on ETC or not so that developers should know whether they can rely on proposed Ethereum (Classic) changes or not at the moment of DApp development.

@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

As for EIPs that were proposed on ETH but there was no discussion, arguments FOR or AGAINST implementing this I think that we should implement them without changes for compatibility reasons by default because we are Ethereum.

Nobody claims that Ethereum is not an original protocol. We claim that the history of Ethereum transactions is not original.

@sorpaas
Copy link
Contributor Author

sorpaas commented Dec 30, 2017

@Dexaran I think this is out of scope of the discussion of ECIP-1040... and you might be arguing to the wrong person.

If the argument is related to whether we should implement all future Ethereum hard forks, then it might be better to argue on Slack or Discord because honestly I'm not the person you should convince...

@Dexaran
Copy link
Member

Dexaran commented Dec 30, 2017

OK. First of all, I will ask you to remove the reference to REVERT opcode because the opcode is not related to this ECIP.

One of the important mission of blockchain is to "become your own bank". In Bitcoin, that means once a transaction is executed, the fund will always be controlled by the receiver unless he or she sends it out again. In Ethereum, an additional important property is that, once a smart contract is deployed, it should always execute in the original expected manner. As a result, it is important to have a way to maintain compatibility of existing contracts and at the same time easily add new protocol features.

I'd like to ask you to define what exactly "compatibility" means in the ECIP context.

As for smart-contracts, I would say that we have to decide what to do with smart-contract that were programmed to change in future. May be it is necessary to introduce a special option for new contracts to specify whether a contract that you are deploying is intended to autoupgrade to the newest version or stay with current version forever (and not to rely on any changes).

WebAssembly

I tend to agree that this approach is a good option for WASM implementation.

@elaineo
Copy link
Member

elaineo commented Dec 31, 2017

EIP-150 changed gas costs, which made some existing contracts invalid, if the contract was calling another contract with an expected amount of gas. The gas costs were changed because they left the network susceptible to low-cost DoS attacks. Maintaining account compatibility would make no sense for EIP150, because it was fixing a security vulnerability.

EIP150 Discussion here.

@sorpaas sorpaas changed the title ECIP-1040: Maintain Existing Account Compatibility While Hard Forking ECIP-1040: Generalized Account Versioning Scheme for Hard Fork Dec 31, 2017
@Dexaran
Copy link
Member

Dexaran commented Dec 31, 2017

Currently, all smart contracts rely on further protocol updates. I will say that if you want the contracts to "work exactly as programmed", then you need to introduce an optional opportunity make contract to execute with old version of EVM and not rely on further protocol upgrades.

As for already-existing contracts, I will say that they are programmed to rely on protocol upgrades.

@Dexaran
Copy link
Member

Dexaran commented Dec 31, 2017

After the first hard fork using this scheme, the account state stored in the world state trie is changed to become a five-item RLP encoding: nonce, balance, storageRoot, codeHash and version. The version field defines that when a contract call transaction or CALL opcode is executed against this contract, which version of the virtual machine should be used. Four-item RLP encoding account state are considered to have the version 0.

I will suggest to add applyVersion variable. If the applyVersion is set to true then the proposed versioning will work as described. If the applyVersion is set to false then this smart-contract should always be executed with current (latest) version of EVM.

This will allow to write "long-term" contracts that will rely on further updates of the protocol exactly as it is possible now.

@sorpaas
Copy link
Contributor Author

sorpaas commented Dec 31, 2017

@Dexaran I'm just curious about this claim:

Currently, all smart contracts rely on further protocol updates.

ECIPs (and even many EIPs) don't have a fixed schedule for hard fork. It is usually unclear of whether a particular hard fork would happen. What's more, details of the hard fork usually also get changed. How do developers rely on something that is unknown?

@Dexaran
Copy link
Member

Dexaran commented Dec 31, 2017

@sorpaas In case of REVERT opcode it was clear enough how this is intended to work and the revert(); syntax was already supported by Remix. In this case, each contract that used the revert(); syntax was relying on REVERT opcode.

@Dexaran
Copy link
Member

Dexaran commented Dec 31, 2017

@sorpaas

Currently, all smart contracts rely on further protocol updates.

If you will change the protocol then smart-contracts will change their behavior currently. Every smart-contract developer is aware of this. These are the rules of this network now and it is known.

@zmitton
Copy link

zmitton commented Mar 4, 2019

Could swapping between VM implementations cause a DOS vector for txs that make multiple contract calls back and forth? This looks like another security vulnerability if so, and for very little reason.

@sorpaas
Copy link
Contributor Author

sorpaas commented Mar 4, 2019

@zmitton No. Any call wouldn't be any more expensive in terms of complexity. So no additional DoS vector is present.

However, I would recommend using ethereum/EIPs#1707 instead of this as that one is much simpler to implement.

@sorpaas sorpaas closed this Jun 27, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants