diff --git a/Changelog.md b/Changelog.md index 1151b3d7c..229d720d5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,10 +4,12 @@ Breaking changes: * ABI: remove the deprecated ``constant`` and ``payable`` fields. * ABI: the ``type`` field is now required and no longer specified to default to ``function``. * Commandline interface: remove the text-based ast printer (``--ast``). + * Command line interface: Switch to the new error reporter by default. ``--old-reporter`` falls back to the deprecated old error reporter. + * Command line interface: Add option to disable or choose hash method between IPFS and Swarm for the bytecode metadata. * General: Disallow explicit conversions from external function types to ``address`` and add a member called ``address`` to them as replacement. * General: New reserved keywords: ``virtual``. + * Standard JSON Interface: Add option to disable or choose hash method between IPFS and Swarm for the bytecode metadata. * Type checker: Resulting type of exponentiation is equal to the type of the base. Also allow signed types for the base. - * Command line interface: Switch to the new error reporter by default. ``--old-reporter`` falls back to the deprecated old error reporter. Language Features: diff --git a/docs/metadata.rst b/docs/metadata.rst index 0250a06d1..9da950231 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -10,16 +10,18 @@ this file to query the compiler version, the sources used, the ABI and NatSpec documentation to more safely interact with the contract and verify its source code. -The compiler appends a Swarm hash of the metadata file to the end of the -bytecode (for details, see below) of each contract, so that you can retrieve -the file in an authenticated way without having to resort to a centralized -data provider. +The compiler appends by default the IPFS hash of the metadata file to the end +of the bytecode (for details, see below) of each contract, so that you can +retrieve the file in an authenticated way without having to resort to a +centralized data provider. The other available options are the Swarm hash and +not appending the metadata hash to the bytecode. These can be configured via +the :ref:`Standard JSON Interface`. -You have to publish the metadata file to Swarm (or another service) so that -others can access it. You create the file by using the ``solc --metadata`` +You have to publish the metadata file to IPFS, Swarm, or another service so +that others can access it. You create the file by using the ``solc --metadata`` command that generates a file called ``ContractName_meta.json``. It contains -Swarm references to the source code, so you have to upload all source files and -the metadata file. +IPFS and Swarm references to the source code, so you have to upload all source +files and the metadata file. The metadata file has the following format. The example below is presented in a human-readable way. Properly formatted metadata should use quotes correctly, @@ -86,7 +88,9 @@ explanatory purposes. }, metadata: { // Reflects the setting used in the input json, defaults to false - useLiteralContent: true + useLiteralContent: true, + // Reflects the setting used in the input json, defaults to "ipfs" + bytecodeHash: "ipfs" } // Required for Solidity: File and name of the contract or library this // metadata is created for. @@ -111,8 +115,8 @@ explanatory purposes. } .. warning:: - Since the bytecode of the resulting contract contains the metadata hash, any - change to the metadata results in a change of the bytecode. This includes + Since the bytecode of the resulting contract contains the metadata hash by default, any + change to the metadata might result in a change of the bytecode. This includes changes to a filename or path, and since the metadata includes a hash of all the sources used, a single whitespace change results in different metadata, and different bytecode. @@ -124,7 +128,7 @@ Encoding of the Metadata Hash in the Bytecode ============================================= Because we might support other ways to retrieve the metadata file in the future, -the mapping ``{"bzzr1": , "solc": }`` is stored +the mapping ``{"ipfs": , "solc": }`` is stored `CBOR `_-encoded. Since the mapping might contain more keys (see below) and the beginning of that encoding is not easy to find, its length is added in a two-byte big-endian @@ -132,12 +136,12 @@ encoding. The current version of the Solidity compiler usually adds the followin to the end of the deployed bytecode:: 0xa2 - 0x65 'b' 'z' 'z' 'r' '1' 0x58 0x20 <32 bytes swarm hash> + 0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash> 0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding> 0x00 0x32 So in order to retrieve the data, the end of the deployed bytecode can be checked -to match that pattern and use the Swarm hash to retrieve the file. +to match that pattern and use the IPFS hash to retrieve the file. Whereas release builds of solc use a 3 byte encoding of the version as shown above (one byte each for major, minor and patch version number), prerelease builds @@ -145,25 +149,25 @@ will instead use a complete version string including commit hash and build date. .. note:: The CBOR mapping can also contain other keys, so it is better to fully - decode the data instead of relying on it starting with ``0xa265``. + decode the data instead of relying on it starting with ``0xa264``. For example, if any experimental features that affect code generation are used, the mapping will also contain ``"experimental": true``. .. note:: - The compiler currently uses the "swarm version 1" hash of the metadata, - but this might change in the future, so do not rely on this sequence - to start with ``0xa2 0x65 'b' 'z' 'z' 'r' '1'``. We might also - add additional data to this CBOR structure, so the - best option is to use a proper CBOR parser. + The compiler currently uses the IPFS hash of the metadata by default, but + it may also use the bzzr1 hash or some other hash in the future, so do + not rely on this sequence to start with ``0xa2 0x64 'i' 'p' 'f' 's'``. We + might also add additional data to this CBOR structure, so the best option + is to use a proper CBOR parser. Usage for Automatic Interface Generation and NatSpec ==================================================== The metadata is used in the following way: A component that wants to interact -with a contract (e.g. Mist or any wallet) retrieves the code of the contract, from that -the Swarm hash of a file which is then retrieved. -That file is JSON-decoded into a structure like above. +with a contract (e.g. Mist or any wallet) retrieves the code of the contract, +from that the IPFS/Swarm hash of a file which is then retrieved. That file +is JSON-decoded into a structure like above. The component can then use the ABI to automatically generate a rudimentary user interface for the contract. @@ -177,7 +181,7 @@ For additional information, read :doc:`Ethereum Natural Language Specification ( Usage for Source Code Verification ================================== -In order to verify the compilation, sources can be retrieved from Swarm +In order to verify the compilation, sources can be retrieved from IPFS/Swarm via the link in the metadata file. The compiler of the correct version (which is checked to be part of the "official" compilers) is invoked on that input with the specified settings. The resulting diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index b8c518bc5..8e27fd133 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -236,7 +236,12 @@ Input Description // Metadata settings (optional) "metadata": { // Use only literal content and not URLs (false by default) - "useLiteralContent": true + "useLiteralContent": true, + // Use the given hash method for the metadata hash that is appended to the bytecode. + // The metadata hash can be removed from the bytecode via option "none". + // The other options are "ipfs" and "bzzr1". + // If the option is omitted, "ipfs" is used by default. + "bytecodeHash": "ipfs" }, // Addresses of the libraries. If not all libraries are given here, // it can result in unlinked objects whose output data is different. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index f3c8ffa15..539cca48e 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -159,6 +159,13 @@ void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources) m_metadataLiteralSources = _metadataLiteralSources; } +void CompilerStack::setMetadataHash(MetadataHash _metadataHash) +{ + if (m_stackState >= ParsingPerformed) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set metadata hash before parsing.")); + m_metadataHash = _metadataHash; +} + void CompilerStack::addSMTLib2Response(h256 const& _hash, string const& _response) { if (m_stackState >= ParsingPerformed) @@ -182,6 +189,7 @@ void CompilerStack::reset(bool _keepSettings) m_generateEWasm = false; m_optimiserSettings = OptimiserSettings::minimal(); m_metadataLiteralSources = false; + m_metadataHash = MetadataHash::IPFS; } m_globalContext.reset(); m_scopes.clear(); @@ -1155,6 +1163,10 @@ string CompilerStack::createMetadata(Contract const& _contract) const if (m_metadataLiteralSources) meta["settings"]["metadata"]["useLiteralContent"] = true; + + static vector hashes{"ipfs", "bzzr1", "none"}; + meta["settings"]["metadata"]["bytecodeHash"] = hashes.at(unsigned(m_metadataHash)); + meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = _contract.contract->annotation().canonicalName; @@ -1263,7 +1275,17 @@ private: bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimentalMode) { MetadataCBOREncoder encoder; - encoder.pushBytes("bzzr1", dev::bzzr1Hash(_metadata).asBytes()); + + if (m_metadataHash == MetadataHash::IPFS) + { + solAssert(_metadata.length() < 1024 * 256, "Metadata too large."); + encoder.pushBytes("ipfs", dev::ipfsHash(_metadata)); + } + else if (m_metadataHash == MetadataHash::Bzzr1) + encoder.pushBytes("bzzr1", dev::bzzr1Hash(_metadata).asBytes()); + else + solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); + if (_experimentalMode) encoder.pushBool("experimental", true); if (m_release) diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 72a81b437..11fb960a3 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -92,6 +92,12 @@ public: CompilationSuccessful }; + enum class MetadataHash { + IPFS, + Bzzr1, + None + }; + struct Remapping { std::string context; @@ -171,6 +177,11 @@ public: /// Must be set before parsing. void useMetadataLiteralSources(bool _metadataLiteralSources); + /// Sets whether and which hash should be used + /// to store the metadata in the bytecode. + /// @param _metadataHash can be IPFS, Bzzr1, None + void setMetadataHash(MetadataHash _metadataHash); + /// Sets the sources. Must be set before parsing. void setSources(StringMap _sources); @@ -418,6 +429,7 @@ private: langutil::ErrorList m_errorList; langutil::ErrorReporter m_errorReporter; bool m_metadataLiteralSources = false; + MetadataHash m_metadataHash = MetadataHash::IPFS; bool m_parserErrorRecovery = false; State m_stackState = Empty; /// Whether or not there has been an error during processing. diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index ca27deac2..c954b8a3d 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -378,9 +378,16 @@ boost::optional checkOptimizerDetail(Json::Value const& _details, s boost::optional checkMetadataKeys(Json::Value const& _input) { - if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool()) - return formatFatalError("JSONError", "\"settings.metadata.useLiteralContent\" must be Boolean"); - static set keys{"useLiteralContent"}; + if (_input.isObject()) + { + if (_input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool()) + return formatFatalError("JSONError", "\"settings.metadata.useLiteralContent\" must be Boolean"); + + static set hashes{"ipfs", "bzzr1", "none"}; + if (_input.isMember("bytecodeHash") && !hashes.count(_input["bytecodeHash"].asString())) + return formatFatalError("JSONError", "\"settings.metadata.bytecodeHash\" must be \"ipfs\", \"bzzr1\" or \"none\""); + } + static set keys{"useLiteralContent", "bytecodeHash"}; return checkKeys(_input, keys, "settings.metadata"); } @@ -710,6 +717,16 @@ boost::variant StandardCompile return *result; ret.metadataLiteralSources = metadataSettings.get("useLiteralContent", Json::Value(false)).asBool(); + if (metadataSettings.isMember("bytecodeHash")) + { + auto metadataHash = metadataSettings["bytecodeHash"].asString(); + ret.metadataHash = + metadataHash == "ipfs" ? + CompilerStack::MetadataHash::IPFS : + metadataHash == "bzzr1" ? + CompilerStack::MetadataHash::Bzzr1 : + CompilerStack::MetadataHash::None; + } Json::Value outputSelection = settings.get("outputSelection", Json::Value()); @@ -735,6 +752,7 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting compilerStack.setOptimiserSettings(std::move(_inputsAndSettings.optimiserSettings)); compilerStack.setLibraries(_inputsAndSettings.libraries); compilerStack.useMetadataLiteralSources(_inputsAndSettings.metadataLiteralSources); + compilerStack.setMetadataHash(_inputsAndSettings.metadataHash); compilerStack.setRequestedContractNames(requestedContractNames(_inputsAndSettings.outputSelection)); compilerStack.enableIRGeneration(isIRRequested(_inputsAndSettings.outputSelection)); diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 9d0320abb..e5562cd2f 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -68,6 +68,7 @@ private: OptimiserSettings optimiserSettings = OptimiserSettings::minimal(); std::map libraries; bool metadataLiteralSources = false; + CompilerStack::MetadataHash metadataHash = CompilerStack::MetadataHash::IPFS; Json::Value outputSelection; }; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index a061fc3d9..19883d5bd 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -122,15 +122,18 @@ static string const g_strInputFile = "input-file"; static string const g_strInterface = "interface"; static string const g_strYul = "yul"; static string const g_strIR = "ir"; +static string const g_strIPFS = "ipfs"; static string const g_strEWasm = "ewasm"; static string const g_strLicense = "license"; static string const g_strLibraries = "libraries"; static string const g_strLink = "link"; static string const g_strMachine = "machine"; static string const g_strMetadata = "metadata"; +static string const g_strMetadataHash = "metadata-hash"; static string const g_strMetadataLiteral = "metadata-literal"; static string const g_strNatspecDev = "devdoc"; static string const g_strNatspecUser = "userdoc"; +static string const g_strNone = "none"; static string const g_strOpcodes = "opcodes"; static string const g_strOptimize = "optimize"; static string const g_strOptimizeRuns = "optimize-runs"; @@ -144,6 +147,7 @@ static string const g_strSrcMap = "srcmap"; static string const g_strSrcMapRuntime = "srcmap-runtime"; static string const g_strStandardJSON = "standard-json"; static string const g_strStrictAssembly = "strict-assembly"; +static string const g_strSwarm = "swarm"; static string const g_strPrettyJson = "pretty-json"; static string const g_strVersion = "version"; static string const g_strIgnoreMissingFiles = "ignore-missing"; @@ -174,6 +178,7 @@ static string const g_argLibraries = g_strLibraries; static string const g_argLink = g_strLink; static string const g_argMachine = g_strMachine; static string const g_argMetadata = g_strMetadata; +static string const g_argMetadataHash = g_strMetadataHash; static string const g_argMetadataLiteral = g_strMetadataLiteral; static string const g_argNatspecDev = g_strNatspecDev; static string const g_argNatspecUser = g_strNatspecUser; @@ -218,6 +223,14 @@ static set const g_machineArgs g_streWasm }; +/// Possible arguments to for --metadata-hash +static set const g_metadataHashArgs +{ + g_strIPFS, + g_strSwarm, + g_strNone +}; + static void version() { sout() << @@ -696,7 +709,12 @@ Allowed options)", "Switch to linker mode, ignoring all options apart from --libraries " "and modify binaries in place." ) - (g_argMetadataLiteral.c_str(), "Store referenced sources are literal data in the metadata output.") + ( + g_argMetadataHash.c_str(), + po::value()->value_name(boost::join(g_metadataHashArgs, ",")), + "Choose hash method for the bytecode metadata or disable it." + ) + (g_argMetadataLiteral.c_str(), "Store referenced sources as literal data in the metadata output.") ( g_argAllowPaths.c_str(), po::value()->value_name("path(s)"), @@ -923,6 +941,22 @@ bool CommandLineInterface::processInput() return link(); } + if (m_args.count(g_argMetadataHash)) + { + string hashStr = m_args[g_argMetadataHash].as(); + if (hashStr == g_strIPFS) + m_metadataHash = CompilerStack::MetadataHash::IPFS; + else if (hashStr == g_strSwarm) + m_metadataHash = CompilerStack::MetadataHash::Bzzr1; + else if (hashStr == g_strNone) + m_metadataHash = CompilerStack::MetadataHash::None; + else + { + serr() << "Invalid option for --metadata-hash: " << hashStr << endl; + return false; + } + } + m_compiler.reset(new CompilerStack(fileReader)); unique_ptr formatter; @@ -935,6 +969,8 @@ bool CommandLineInterface::processInput() { if (m_args.count(g_argMetadataLiteral) > 0) m_compiler->useMetadataLiteralSources(true); + if (m_args.count(g_argMetadataHash)) + m_compiler->setMetadataHash(m_metadataHash); if (m_args.count(g_argInputFile)) m_compiler->setRemappings(m_remappings); m_compiler->setSources(m_sourceCodes); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index cdcc5063b..87ac8efd8 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -111,6 +111,8 @@ private: std::unique_ptr m_compiler; /// EVM version to use langutil::EVMVersion m_evmVersion; + /// Chosen hash method for the bytecode metadata. + CompilerStack::MetadataHash m_metadataHash = CompilerStack::MetadataHash::IPFS; /// Whether or not to colorize diagnostics output. bool m_coloredOutput = true; }; diff --git a/test/Metadata.cpp b/test/Metadata.cpp index bdbf427e9..d8a593b79 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -41,9 +41,9 @@ bytes onlyMetadata(bytes const& _bytecode) size_t metadataSize = (_bytecode[size - 2] << 8) + _bytecode[size - 1]; if (size < (metadataSize + 2)) return bytes{}; - // Sanity check: assume the first byte is a fixed-size CBOR array with either 2 or 3 entries + // Sanity check: assume the first byte is a fixed-size CBOR array with 1, 2 or 3 entries unsigned char firstByte = _bytecode[size - metadataSize - 2]; - if (firstByte != 0xa2 && firstByte != 0xa3) + if (firstByte != 0xa1 && firstByte != 0xa2 && firstByte != 0xa3) return bytes{}; return bytes(_bytecode.end() - metadataSize - 2, _bytecode.end() - 2); } @@ -185,7 +185,9 @@ bool isValidMetadata(string const& _metadata) !metadata.isMember("settings") || !metadata.isMember("sources") || !metadata.isMember("output") || - !metadata["settings"].isMember("evmVersion") + !metadata["settings"].isMember("evmVersion") || + !metadata["settings"].isMember("metadata") || + !metadata["settings"]["metadata"].isMember("bytecodeHash") ) return false; diff --git a/test/libsolidity/GasCosts.cpp b/test/libsolidity/GasCosts.cpp index 8aad4fab7..977553286 100644 --- a/test/libsolidity/GasCosts.cpp +++ b/test/libsolidity/GasCosts.cpp @@ -19,7 +19,7 @@ */ #include -#include +#include #include #include @@ -40,18 +40,18 @@ namespace test #define CHECK_DEPLOY_GAS(_gasNoOpt, _gasOpt) \ do \ { \ - u256 bzzr1Cost = GasMeter::dataGas(dev::bzzr1Hash(m_compiler.metadata(m_compiler.lastContractName())).asBytes(), true); \ + u256 ipfsCost = GasMeter::dataGas(dev::ipfsHash(m_compiler.metadata(m_compiler.lastContractName())), true); \ u256 gasOpt{_gasOpt}; \ u256 gasNoOpt{_gasNoOpt}; \ u256 gas = m_optimiserSettings == OptimiserSettings::minimal() ? gasNoOpt : gasOpt; \ BOOST_CHECK_MESSAGE( \ - m_gasUsed >= bzzr1Cost, \ + m_gasUsed >= ipfsCost, \ "Gas used: " + \ m_gasUsed.str() + \ - " is less than the data cost for the bzzr1 hash: " + \ - u256(bzzr1Cost).str() \ + " is less than the data cost for the IPFS hash: " + \ + u256(ipfsCost).str() \ ); \ - u256 gasUsed = m_gasUsed - bzzr1Cost; \ + u256 gasUsed = m_gasUsed - ipfsCost; \ BOOST_CHECK_MESSAGE( \ gas == gasUsed, \ "Gas used: " + \ @@ -96,17 +96,18 @@ BOOST_AUTO_TEST_CASE(string_storage) compileAndRun(sourceCode); if (Options::get().evmVersion() <= EVMVersion::byzantium()) - CHECK_DEPLOY_GAS(134071, 130763); + CHECK_DEPLOY_GAS(134209, 130895); // This is only correct on >=Constantinople. else if (Options::get().useABIEncoderV2) { if (Options::get().optimizeYul) - CHECK_DEPLOY_GAS(151455, 127653); + CHECK_DEPLOY_GAS(127785, 127785); else - CHECK_DEPLOY_GAS(151455, 135371); + CHECK_DEPLOY_GAS(151587, 135371); } else - CHECK_DEPLOY_GAS(126861, 119591); + CHECK_DEPLOY_GAS(126993, 119723); + if (Options::get().evmVersion() >= EVMVersion::byzantium()) { callContractFunction("f()"); diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 89093e035..a4463ce0f 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include using namespace std; @@ -60,28 +61,54 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) } )"; for (auto release: std::set{true, VersionIsRelease}) - { - CompilerStack compilerStack; - compilerStack.overwriteReleaseFlag(release); - compilerStack.setSources({{"", std::string(sourceCode)}}); - compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); - compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); - BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); - bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; - std::string const& metadata = compilerStack.metadata("test"); - BOOST_CHECK(dev::test::isValidMetadata(metadata)); - bytes hash = dev::bzzr1Hash(metadata).asBytes(); - BOOST_REQUIRE(hash.size() == 32); - auto const cborMetadata = requireParsedCBORMetadata(bytecode); - BOOST_CHECK(cborMetadata.size() == 2); - BOOST_CHECK(cborMetadata.count("solc") == 1); - if (release) - BOOST_CHECK(cborMetadata.at("solc") == toHex(VersionCompactBytes)); - else - BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict); - BOOST_CHECK(cborMetadata.count("bzzr1") == 1); - BOOST_CHECK(cborMetadata.at("bzzr1") == toHex(hash)); - } + for (auto metadataHash: set{ + CompilerStack::MetadataHash::IPFS, + CompilerStack::MetadataHash::Bzzr1, + CompilerStack::MetadataHash::None + }) + { + CompilerStack compilerStack; + compilerStack.overwriteReleaseFlag(release); + compilerStack.setSources({{"", std::string(sourceCode)}}); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); + compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); + compilerStack.setMetadataHash(metadataHash); + BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); + bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; + std::string const& metadata = compilerStack.metadata("test"); + BOOST_CHECK(dev::test::isValidMetadata(metadata)); + + auto const cborMetadata = requireParsedCBORMetadata(bytecode); + if (metadataHash == CompilerStack::MetadataHash::None) + BOOST_CHECK(cborMetadata.size() == 1); + else + { + bytes hash; + string hashMethod; + if (metadataHash == CompilerStack::MetadataHash::IPFS) + { + hash = dev::ipfsHash(metadata); + BOOST_REQUIRE(hash.size() == 34); + hashMethod = "ipfs"; + } + else + { + hash = dev::bzzr1Hash(metadata).asBytes(); + BOOST_REQUIRE(hash.size() == 32); + hashMethod = "bzzr1"; + } + + BOOST_CHECK(cborMetadata.size() == 2); + BOOST_CHECK(cborMetadata.count(hashMethod) == 1); + BOOST_CHECK(cborMetadata.at(hashMethod) == toHex(hash)); + } + + BOOST_CHECK(cborMetadata.count("solc") == 1); + if (release) + BOOST_CHECK(cborMetadata.at("solc") == toHex(VersionCompactBytes)); + else + BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict); + } } BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) @@ -94,31 +121,57 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) function g(function(uint) external returns (uint) x) public {} } )"; - for(auto release: std::set{true, VersionIsRelease}) - { - CompilerStack compilerStack; - compilerStack.overwriteReleaseFlag(release); - compilerStack.setSources({{"", std::string(sourceCode)}}); - compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); - compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); - BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); - bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; - std::string const& metadata = compilerStack.metadata("test"); - BOOST_CHECK(dev::test::isValidMetadata(metadata)); - bytes hash = dev::bzzr1Hash(metadata).asBytes(); - BOOST_REQUIRE(hash.size() == 32); - auto const cborMetadata = requireParsedCBORMetadata(bytecode); - BOOST_CHECK(cborMetadata.size() == 3); - BOOST_CHECK(cborMetadata.count("solc") == 1); - if (release) - BOOST_CHECK(cborMetadata.at("solc") == toHex(VersionCompactBytes)); - else - BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict); - BOOST_CHECK(cborMetadata.count("bzzr1") == 1); - BOOST_CHECK(cborMetadata.at("bzzr1") == toHex(hash)); - BOOST_CHECK(cborMetadata.count("experimental") == 1); - BOOST_CHECK(cborMetadata.at("experimental") == "true"); - } + for (auto release: set{true, VersionIsRelease}) + for (auto metadataHash: set{ + CompilerStack::MetadataHash::IPFS, + CompilerStack::MetadataHash::Bzzr1, + CompilerStack::MetadataHash::None + }) + { + CompilerStack compilerStack; + compilerStack.overwriteReleaseFlag(release); + compilerStack.setSources({{"", std::string(sourceCode)}}); + compilerStack.setEVMVersion(dev::test::Options::get().evmVersion()); + compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); + compilerStack.setMetadataHash(metadataHash); + BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); + bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; + std::string const& metadata = compilerStack.metadata("test"); + BOOST_CHECK(dev::test::isValidMetadata(metadata)); + + auto const cborMetadata = requireParsedCBORMetadata(bytecode); + if (metadataHash == CompilerStack::MetadataHash::None) + BOOST_CHECK(cborMetadata.size() == 2); + else + { + bytes hash; + string hashMethod; + if (metadataHash == CompilerStack::MetadataHash::IPFS) + { + hash = dev::ipfsHash(metadata); + BOOST_REQUIRE(hash.size() == 34); + hashMethod = "ipfs"; + } + else + { + hash = dev::bzzr1Hash(metadata).asBytes(); + BOOST_REQUIRE(hash.size() == 32); + hashMethod = "bzzr1"; + } + + BOOST_CHECK(cborMetadata.size() == 3); + BOOST_CHECK(cborMetadata.count(hashMethod) == 1); + BOOST_CHECK(cborMetadata.at(hashMethod) == toHex(hash)); + } + + BOOST_CHECK(cborMetadata.count("solc") == 1); + if (release) + BOOST_CHECK(cborMetadata.at("solc") == toHex(VersionCompactBytes)); + else + BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict); + BOOST_CHECK(cborMetadata.count("experimental") == 1); + BOOST_CHECK(cborMetadata.at("experimental") == "true"); + } } BOOST_AUTO_TEST_CASE(metadata_relevant_sources) @@ -218,16 +271,13 @@ BOOST_AUTO_TEST_CASE(metadata_useLiteralContent) jsonParse(metadata_str, metadata); BOOST_CHECK(dev::test::isValidMetadata(metadata_str)); BOOST_CHECK(metadata.isMember("settings")); + BOOST_CHECK(metadata["settings"].isMember("metadata")); + BOOST_CHECK(metadata["settings"]["metadata"].isMember("bytecodeHash")); if (_literal) { - BOOST_CHECK(metadata["settings"].isMember("metadata")); BOOST_CHECK(metadata["settings"]["metadata"].isMember("useLiteralContent")); BOOST_CHECK(metadata["settings"]["metadata"]["useLiteralContent"].asBool()); } - else - { - BOOST_CHECK(!metadata["settings"].isMember("metadata")); - } }; check(sourceCode, true); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 336455fd2..aef8ee3c1 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -364,7 +364,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), string("6080604052348015600f57600080fd5b5060") + - (VersionIsRelease ? "3e" : toHex(bytes{uint8_t(60 + VersionStringStrict.size())})) + + (VersionIsRelease ? "3f" : toHex(bytes{uint8_t(61 + VersionStringStrict.size())})) + "80601d6000396000f3fe6080604052600080fdfe" ); BOOST_CHECK(contract["evm"]["assembly"].isString()); @@ -377,7 +377,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation) "tag_1:\n /* \"fileA\":0:14 contract A { } */\n pop\n dataSize(sub_0)\n dup1\n " "dataOffset(sub_0)\n 0x00\n codecopy\n 0x00\n return\nstop\n\nsub_0: assembly {\n " "/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n 0x00\n " - "dup1\n revert\n\n auxdata: 0xa265627a7a72315820" + "dup1\n revert\n\n auxdata: 0xa26469706673582212" ) == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); BOOST_CHECK_EQUAL(contract["evm"]["gasEstimates"].size(), 1); diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index 2ee698008..80a7c6327 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -14,9 +14,9 @@ contract C { } // ---- // creation: -// codeDepositCost: 1122600 +// codeDepositCost: 1122800 // executionCost: 1167 -// totalCost: 1123767 +// totalCost: 1123967 // external: // a(): 530 // b(uint256): infinite diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index f22576380..d167e7d3c 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -17,9 +17,9 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 624200 +// codeDepositCost: 624400 // executionCost: 657 -// totalCost: 624857 +// totalCost: 625057 // external: // a(): 429 // b(uint256): 884 diff --git a/test/libsolidity/gasTests/data_storage.sol b/test/libsolidity/gasTests/data_storage.sol index 5efa87d57..29cade28c 100644 --- a/test/libsolidity/gasTests/data_storage.sol +++ b/test/libsolidity/gasTests/data_storage.sol @@ -13,8 +13,8 @@ contract C { } // ---- // creation: -// codeDepositCost: 256800 +// codeDepositCost: 257000 // executionCost: 300 -// totalCost: 257100 +// totalCost: 257300 // external: // f(): 252 diff --git a/test/libsolidity/gasTests/dispatch_large.sol b/test/libsolidity/gasTests/dispatch_large.sol index 7fc28339c..7cb873710 100644 --- a/test/libsolidity/gasTests/dispatch_large.sol +++ b/test/libsolidity/gasTests/dispatch_large.sol @@ -24,9 +24,9 @@ contract Large { } // ---- // creation: -// codeDepositCost: 636800 +// codeDepositCost: 637000 // executionCost: 670 -// totalCost: 637470 +// totalCost: 637670 // external: // a(): 451 // b(uint256): 846 diff --git a/test/libsolidity/gasTests/dispatch_large_optimised.sol b/test/libsolidity/gasTests/dispatch_large_optimised.sol index cee223b4c..4f6f238ad 100644 --- a/test/libsolidity/gasTests/dispatch_large_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_large_optimised.sol @@ -27,9 +27,9 @@ contract Large { // optimize-runs: 2 // ---- // creation: -// codeDepositCost: 260400 +// codeDepositCost: 260600 // executionCost: 300 -// totalCost: 260700 +// totalCost: 260900 // external: // a(): 398 // b(uint256): 1105 diff --git a/test/libsolidity/gasTests/dispatch_medium.sol b/test/libsolidity/gasTests/dispatch_medium.sol index 0c1d80126..9a3b4835c 100644 --- a/test/libsolidity/gasTests/dispatch_medium.sol +++ b/test/libsolidity/gasTests/dispatch_medium.sol @@ -11,9 +11,9 @@ contract Medium { } // ---- // creation: -// codeDepositCost: 253000 +// codeDepositCost: 253200 // executionCost: 294 -// totalCost: 253294 +// totalCost: 253494 // external: // a(): 428 // b(uint256): 846 diff --git a/test/libsolidity/gasTests/dispatch_medium_optimised.sol b/test/libsolidity/gasTests/dispatch_medium_optimised.sol index b2c76140a..44b4a72e0 100644 --- a/test/libsolidity/gasTests/dispatch_medium_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_medium_optimised.sol @@ -14,9 +14,9 @@ contract Medium { // optimize-runs: 2 // ---- // creation: -// codeDepositCost: 140800 -// executionCost: 183 -// totalCost: 140983 +// codeDepositCost: 141000 +// executionCost: 190 +// totalCost: 141190 // external: // a(): 398 // b(uint256): 863 diff --git a/test/libsolidity/gasTests/dispatch_small.sol b/test/libsolidity/gasTests/dispatch_small.sol index cb163d1ba..87634ac73 100644 --- a/test/libsolidity/gasTests/dispatch_small.sol +++ b/test/libsolidity/gasTests/dispatch_small.sol @@ -6,9 +6,9 @@ contract Small { } // ---- // creation: -// codeDepositCost: 83600 +// codeDepositCost: 83800 // executionCost: 135 -// totalCost: 83735 +// totalCost: 83935 // external: // fallback: 118 // a(): 383 diff --git a/test/libsolidity/gasTests/dispatch_small_optimised.sol b/test/libsolidity/gasTests/dispatch_small_optimised.sol index 7153a784c..487f2c9d9 100644 --- a/test/libsolidity/gasTests/dispatch_small_optimised.sol +++ b/test/libsolidity/gasTests/dispatch_small_optimised.sol @@ -9,9 +9,9 @@ contract Small { // optimize-runs: 2 // ---- // creation: -// codeDepositCost: 60400 +// codeDepositCost: 60600 // executionCost: 111 -// totalCost: 60511 +// totalCost: 60711 // external: // fallback: 118 // a(): 376