-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
Inconsistent bytecode created for identical contract input using standard-json compiler input #9573
Comments
Thanks for reporting this! Looking at the assembly output, two things are interesting:
|
To fix this properly, we have to reset the yul string repository between contracts, which would invalidate the inline assembly part of the AST which is stored in parsed form and thus uses yul strings. |
Actually the difference is only in the internal routines, so resetting the yul string repository might be all that is needed. |
It turns out that the problem is that AST IDs are encoded into function names which leads to the optimizing handling them in a different order resulting in a different way functions are inlined. |
Current plan of attack:
|
This bug has really big implications for Buidler's new compilation pipeline. We are now forced to disable some optimizations we've been working on. Can someone point to the first version of solc with this problem? That would be incredibly valuable. |
The problem should be present whenever you have some Yul code and the yul optimizer is activated. It got activated together with the regular optimizer starting from 0.6.0. You have Yul code whenever you use ABIEncoderV2. I think there are also some other situations here yul code is generated, but it might not have that problem. Actually I think this only happens if you have multiple files in one compilation unit that contain structs or contracts with the same name. So if you don't just flatten everything ( ;) ), you should be rather safe. The reason is that the names of the Yul functions always have the data type name and its AST ID. If the data type name is already unique, a change in the AST ID should not matter much. |
I think that's not always true. I believe our new compilation pipeline can easily trigger this bug. Here's an example of what we were planning to do, and why it would fail:
|
@alcuadrado maybe I didn't understand, but are you saying that a user deploys a contract from a source that is not stored permanently and then cannot verify the contract? If this is so extremely common, then deployment tools should make sure they only work from a clean git repo that is synced to origin or even better, store the sources and the metadata somewhere. Or did I misunderstand you - I don't see how this is related to importing or flattening, for example. |
@chriseth the sources (meaning, the Solidity files) are stored permanently. What is not stored permanently is the JSON input used by solc. In the first scenario, this JSON had the contracts The point is that, because of this bug, this optimization (ignoring A in a re-compilation because it doesn't affect B and C) cannot be done. Does that make sense? |
@fvictorio as I said, my assumption is that this bug does not manifest itself as long as the files do not contain structs or contracts of the same name, but I will try to verify this assumption. In general, the optimizer settings, file names, remappings and so on are crucial for verification and thus have to be stored. It would be great if deployment tools could help store the metadata file. If the metadata file is not requested as one of the output artefacts, then it is very much more difficult to source-verify a contract. |
Oh, yes, I forgot to add that detail: in that example, files So the thing we want to do is to be able to make the optimization of only compiling I agree that this, in theory, this bug could be worked around by the deployment tool, but that doesn't change our situation with respect to the compilation pipeline. |
@fvictorio ok, you are right, I confused the flattening in the initial example with the main problem of two files sharing identifiers of the same name. I assume that if you don't flatten then this will be a very rare case, but I guess we'll just fix this issue and then we are all happy - as long as everyone uses the latest version of Solidity :) |
Disabling the FullInliner results in the same bytecode, so it might be that we only have to fix that component. @ekpyron I think using the blockHasher does not really help here because it will consider two functions to be different if they call two different but equivalent functions. |
It turns out the actual fix is much easier: The function inliner processes functions according to their YulString sorting order. If they are processed based on their name (or position in the source, which is equivalent) instead, the bug is gone, and even the metadata is fully equal. |
@chriseth can we assume that this bug will never happen if the optimizer is disabled? |
See issue ethereum/solidity#9573 When the solc optimizer is enabled, there might be inconsistencies in the generated bytecodes. To avoid this we need to make sure that the files involved are always the same for a given group. We don't need to do this if the solc optimizer is disabled.
@fvictorio this specific bug needs the yul optimizer to be enabled. |
It turns out that if you update the example code to Solidity 0.7.0, the bug is not present. I compared the yul code generated and it seems that there are some functions that are duplicated it 0.5.17: One for "memory" and one for "memory pointer", while the distinction between "pointer" or "not pointer" is only relevant for storage and thus it was removed for memory at some point. I implemented and verified a fix, but we cannot test it due to lack of example input. |
If someone encounters an example that fails for 0.7.0, please report it! |
Hi. This bug caused me a lot of trouble recently when I tried to verify some contracts that a colleague had compiled using solc-js |
The cause of the bug was just that optimizer steps were applied in a different order to the individual functions, so no reason for concern. |
Leaving a note here that this affects Argent wallet contracts that are on solc 0.6.12 and using Compiled as part of larger contracts set Compiled alone Affected contracts are: |
Building contracts individually produces more reproducible bytecode. Due to a bug in the 0.6.10 compiler (ethereum/solidity/issues/9573)
Hi @fvictorio @alcuadrado is this issue solved? For instance if I compile Is there any hardhat configuration I'm missing? |
I don't think you can reasonably expect hardhat and truffle to produce the same bytecode since they both implement their own build pipeline. I.e. they define the standard input JSON that is passed to the compiler, which is not necessarily the same. |
@vittominacori from what I can tell, the difference is just in the metadata (the last bytes) generated by Hardhat and Truffle. I don't quite understand why they are different though. (Btw, I obtained this by actually compiling your project, because the gist files you shared are super different in length, that can't be right.) This issue is about a difference in the executable part of the bytecode, so I don't think you are running into a regression. |
Yes, sorry. Maybe I had old node modules installed. Here the env
|
Description
During deployment of Augur, one of the contracts would not verify on Etherscan properly: the deployment process created one bytecode for "ZeroXTrade", while giving the Standard Json to Etherscan and having them compile created bytecode with VERY slightly different bytecode (of different lengths!).
Due to past issues with contract verification, Augur flattens all contracts prior to deployment. The Standard Json that is provided to solidity lists many contracts to be compiled, but all of them are completely flattened and self-contained (with significant code-duplication between them).
The issue is that the bytecode created for a self-contained contract changes depending on what OTHER self-contained contracts are compiled at the same time. In the example below, I have linked two different inputs that are identical except for an additional self-contained contract being compiled at the same time which demonstrates this issue.
Environment
Steps to Reproduce
standard-json examples that show the issue:
https://gist.github.com/epheph/20428a1ff7163bce1d47be7d7b623344
The only difference between these two files is the "Augur.sol" section (which jq is discarding the output of).
If you run each of these through solc 0.5.17:
Look at the last characters,
c2
vsd1
. There are many other slight differences, but this is the first.ZeroXTrade.sol makes NO reference to Augur.sol, so the fact that Augur.sol is compiled in the same solidity run should not change the ZeroXTrade's bytecode.
/cc @nuevoalex
The text was updated successfully, but these errors were encountered: