From 83b3bd0227f3d98ce3e98d94440ca05cb5dc11ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Sep 2021 15:50:37 +0200 Subject: [PATCH 1/2] Add a version of isValidMetadata() that accepts already parsed metadata --- test/Metadata.cpp | 34 +++++++++++++++++++--------------- test/Metadata.h | 9 ++++++--- test/libsolidity/Metadata.cpp | 16 ++++++++-------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/test/Metadata.cpp b/test/Metadata.cpp index 61654ae00..687b9d73e 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include using namespace std; @@ -170,30 +169,35 @@ std::optional> parseCBORMetadata(bytes const& _metadata) } } -bool isValidMetadata(string const& _metadata) +bool isValidMetadata(string const& _serialisedMetadata) { Json::Value metadata; - if (!util::jsonParseStrict(_metadata, metadata)) + if (!util::jsonParseStrict(_serialisedMetadata, metadata)) return false; + return isValidMetadata(metadata); +} + +bool isValidMetadata(Json::Value const& _metadata) +{ if ( - !metadata.isObject() || - !metadata.isMember("version") || - !metadata.isMember("language") || - !metadata.isMember("compiler") || - !metadata.isMember("settings") || - !metadata.isMember("sources") || - !metadata.isMember("output") || - !metadata["settings"].isMember("evmVersion") || - !metadata["settings"].isMember("metadata") || - !metadata["settings"]["metadata"].isMember("bytecodeHash") + !_metadata.isObject() || + !_metadata.isMember("version") || + !_metadata.isMember("language") || + !_metadata.isMember("compiler") || + !_metadata.isMember("settings") || + !_metadata.isMember("sources") || + !_metadata.isMember("output") || + !_metadata["settings"].isMember("evmVersion") || + !_metadata["settings"].isMember("metadata") || + !_metadata["settings"]["metadata"].isMember("bytecodeHash") ) return false; - if (!metadata["version"].isNumeric() || metadata["version"] != 1) + if (!_metadata["version"].isNumeric() || _metadata["version"] != 1) return false; - if (!metadata["language"].isString() || metadata["language"].asString() != "Solidity") + if (!_metadata["language"].isString() || _metadata["language"].asString() != "Solidity") return false; /// @TODO add more strict checks diff --git a/test/Metadata.h b/test/Metadata.h index c91ceb559..86b43e245 100644 --- a/test/Metadata.h +++ b/test/Metadata.h @@ -21,6 +21,7 @@ */ #include +#include #include #include @@ -48,8 +49,10 @@ std::string bytecodeSansMetadata(std::string const& _bytecode); /// - everything else is invalid std::optional> parseCBORMetadata(bytes const& _metadata); -/// Expects a serialised metadata JSON and returns true if the -/// content is valid metadata. -bool isValidMetadata(std::string const& _metadata); +/// Expects a serialised metadata JSON and returns true if the content is valid metadata. +bool isValidMetadata(std::string const& _serialisedMetadata); + +/// Expects a deserialised metadata JSON and returns true if the content is valid metadata. +bool isValidMetadata(Json::Value const& _metadata); } // end namespaces diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index ae2dd85db..706bf15d5 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -58,9 +58,9 @@ optional compileAndCheckLicenseMetadata(string const& _contractName, cha BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata(_contractName); - BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); BOOST_REQUIRE(metadata["sources"].isMember("A.sol")); @@ -250,9 +250,9 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("A"); - BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); BOOST_CHECK(metadata["sources"].isMember("A")); @@ -291,9 +291,9 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("C"); - BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 3); BOOST_CHECK(metadata["sources"].isMember("A")); @@ -320,8 +320,8 @@ BOOST_AUTO_TEST_CASE(metadata_useLiteralContent) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); string metadata_str = compilerStack.metadata("test"); Json::Value metadata; - util::jsonParseStrict(metadata_str, metadata); - BOOST_CHECK(solidity::test::isValidMetadata(metadata_str)); + BOOST_REQUIRE(util::jsonParseStrict(metadata_str, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK(metadata.isMember("settings")); BOOST_CHECK(metadata["settings"].isMember("metadata")); BOOST_CHECK(metadata["settings"]["metadata"].isMember("bytecodeHash")); @@ -354,8 +354,8 @@ BOOST_AUTO_TEST_CASE(metadata_viair) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); string metadata_str = compilerStack.metadata("test"); Json::Value metadata; - util::jsonParseStrict(metadata_str, metadata); - BOOST_CHECK(solidity::test::isValidMetadata(metadata_str)); + BOOST_REQUIRE(util::jsonParseStrict(metadata_str, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK(metadata.isMember("settings")); if (_viair) { @@ -381,9 +381,9 @@ BOOST_AUTO_TEST_CASE(metadata_revert_strings) BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); std::string const& serialisedMetadata = compilerStack.metadata("A"); - BOOST_CHECK(solidity::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; BOOST_REQUIRE(util::jsonParseStrict(serialisedMetadata, metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK_EQUAL(metadata["settings"]["debug"]["revertStrings"], "strip"); } From 405a9e9971dadba12c4c8eb33e06895c10a9528c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 10 Sep 2021 15:54:32 +0200 Subject: [PATCH 2/2] Generate separate metadata for the old and the new codegen --- libsolidity/interface/CompilerStack.cpp | 31 ++++++++++--------------- libsolidity/interface/CompilerStack.h | 25 +++++++++++++------- test/libsolidity/Metadata.cpp | 28 ++++++++++++++++++---- 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 68411b071..16df9b7b1 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1000,20 +1000,12 @@ Json::Value CompilerStack::methodIdentifiers(string const& _contractName) const return methodIdentifiers; } -string const& CompilerStack::metadata(string const& _contractName) const +bytes CompilerStack::cborMetadata(string const& _contractName, bool _forIR) const { if (m_stackState < AnalysisPerformed) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); - return metadata(contract(_contractName)); -} - -bytes CompilerStack::cborMetadata(string const& _contractName) const -{ - if (m_stackState < AnalysisPerformed) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful.")); - - return createCBORMetadata(contract(_contractName)); + return createCBORMetadata(contract(_contractName), _forIR); } string const& CompilerStack::metadata(Contract const& _contract) const @@ -1023,7 +1015,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const solAssert(_contract.contract, ""); - return _contract.metadata.init([&]{ return createMetadata(_contract); }); + return _contract.metadata.init([&]{ return createMetadata(_contract, m_viaIR); }); } CharStream const& CompilerStack::charStream(string const& _sourceName) const @@ -1283,7 +1275,8 @@ void CompilerStack::compileContract( shared_ptr compiler = make_shared(m_evmVersion, m_revertStrings, m_optimiserSettings); compiledContract.compiler = compiler; - bytes cborEncodedMetadata = createCBORMetadata(compiledContract); + solAssert(!m_viaIR, ""); + bytes cborEncodedMetadata = createCBORMetadata(compiledContract, /* _forIR */ false); try { @@ -1332,7 +1325,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract) IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings, sourceIndices(), this); tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run( _contract, - createCBORMetadata(compiledContract), + createCBORMetadata(compiledContract, /* _forIR */ true), otherYulSources ); } @@ -1434,7 +1427,7 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co return it->second; } -string CompilerStack::createMetadata(Contract const& _contract) const +string CompilerStack::createMetadata(Contract const& _contract, bool _forIR) const { Json::Value meta; meta["version"] = 1; @@ -1510,8 +1503,8 @@ string CompilerStack::createMetadata(Contract const& _contract) const static vector hashes{"ipfs", "bzzr1", "none"}; meta["settings"]["metadata"]["bytecodeHash"] = hashes.at(unsigned(m_metadataHash)); - if (m_viaIR) - meta["settings"]["viaIR"] = m_viaIR; + if (_forIR) + meta["settings"]["viaIR"] = _forIR; meta["settings"]["evmVersion"] = m_evmVersion.name(); meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = *_contract.contract->annotation().canonicalName; @@ -1617,7 +1610,7 @@ private: bytes m_data; }; -bytes CompilerStack::createCBORMetadata(Contract const& _contract) const +bytes CompilerStack::createCBORMetadata(Contract const& _contract, bool _forIR) const { if (m_metadataFormat == MetadataFormat::NoMetadata) return bytes{}; @@ -1626,7 +1619,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract) const _contract.contract->sourceUnit().annotation().experimentalFeatures ); - string meta = metadata(_contract); + string meta = (_forIR == m_viaIR ? metadata(_contract) : createMetadata(_contract, _forIR)); MetadataCBOREncoder encoder; @@ -1637,7 +1630,7 @@ bytes CompilerStack::createCBORMetadata(Contract const& _contract) const else solAssert(m_metadataHash == MetadataHash::None, "Invalid metadata hash"); - if (experimentalMode || m_viaIR) + if (experimentalMode || _forIR) encoder.pushBool("experimental", true); if (m_metadataFormat == MetadataFormat::WithReleaseVersionTag) encoder.pushBytes("solc", VersionCompactBytes); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 9c713da24..bbef4dc64 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -326,11 +326,16 @@ public: /// @returns a JSON representing a map of method identifiers (hashes) to function names. Json::Value methodIdentifiers(std::string const& _contractName) const; - /// @returns the Contract Metadata - std::string const& metadata(std::string const& _contractName) const; + /// @returns the Contract Metadata matching the pipeline selected using the viaIR setting. + std::string const& metadata(std::string const& _contractName) const { return metadata(contract(_contractName)); } - /// @returns the cbor-encoded metadata. - bytes cborMetadata(std::string const& _contractName) const; + /// @returns the CBOR-encoded metadata matching the pipeline selected using the viaIR setting. + bytes cborMetadata(std::string const& _contractName) const { return cborMetadata(_contractName, m_viaIR); } + + /// @returns the CBOR-encoded metadata. + /// @param _forIR If true, the metadata for the IR codegen is used. Otherwise it's the metadata + /// for the EVM codegen + bytes cborMetadata(std::string const& _contractName, bool _forIR) const; /// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions Json::Value gasEstimates(std::string const& _contractName) const; @@ -339,6 +344,7 @@ public: /// This is mostly a workaround to avoid bytecode and gas differences between compiler builds /// caused by differences in metadata. Should only be used for testing. void setMetadataFormat(MetadataFormat _metadataFormat) { m_metadataFormat = _metadataFormat; } + private: /// The state per source unit. Filled gradually during parsing. struct Source @@ -437,11 +443,14 @@ private: /// Can only be called after state is SourcesSet. Source const& source(std::string const& _sourceName) const; + /// @param _forIR If true, include a flag that indicates that the bytecode comes from the + /// experimental IR codegen. /// @returns the metadata JSON as a compact string for the given contract. - std::string createMetadata(Contract const& _contract) const; + std::string createMetadata(Contract const& _contract, bool _forIR) const; /// @returns the metadata CBOR for the given serialised metadata JSON. - bytes createCBORMetadata(Contract const& _contract) const; + /// @param _forIR If true, use the metadata for the IR codegen. Otherwise the one for EVM codegen. + bytes createCBORMetadata(Contract const& _contract, bool _forIR) const; /// @returns the contract ABI as a JSON object. /// This will generate the JSON object and store it in the Contract object if it is not present yet. @@ -459,9 +468,9 @@ private: /// This will generate the JSON object and store it in the Contract object if it is not present yet. Json::Value const& natspecDev(Contract const&) const; - /// @returns the Contract Metadata + /// @returns the Contract Metadata matching the pipeline selected using the viaIR setting. /// This will generate the metadata and store it in the Contract object if it is not present yet. - std::string const& metadata(Contract const&) const; + std::string const& metadata(Contract const& _contract) const; /// @returns the offset of the entry point of the given function into the list of assembly items /// or zero if it is not found or does not exist. diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 706bf15d5..8d0b65e4c 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -344,24 +344,42 @@ BOOST_AUTO_TEST_CASE(metadata_viair) } )"; - auto check = [](char const* _src, bool _viair) + auto check = [](char const* _src, bool _viaIR) { CompilerStack compilerStack; compilerStack.setSources({{"", std::string(_src)}}); compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); - compilerStack.setViaIR(_viair); + compilerStack.setViaIR(_viaIR); BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); - string metadata_str = compilerStack.metadata("test"); + Json::Value metadata; - BOOST_REQUIRE(util::jsonParseStrict(metadata_str, metadata)); + BOOST_REQUIRE(util::jsonParseStrict(compilerStack.metadata("test"), metadata)); BOOST_CHECK(solidity::test::isValidMetadata(metadata)); BOOST_CHECK(metadata.isMember("settings")); - if (_viair) + if (_viaIR) { BOOST_CHECK(metadata["settings"].isMember("viaIR")); BOOST_CHECK(metadata["settings"]["viaIR"].asBool()); } + else + BOOST_CHECK(!metadata["settings"].isMember("viaIR")); + + BOOST_CHECK(compilerStack.cborMetadata("test") == compilerStack.cborMetadata("test", _viaIR)); + BOOST_CHECK(compilerStack.cborMetadata("test") != compilerStack.cborMetadata("test", !_viaIR)); + + map const parsedCBORMetadata = requireParsedCBORMetadata( + compilerStack.runtimeObject("test").bytecode, + CompilerStack::MetadataFormat::WithReleaseVersionTag + ); + + if (_viaIR) + { + BOOST_CHECK(parsedCBORMetadata.count("experimental") == 1); + BOOST_CHECK(parsedCBORMetadata.at("experimental") == "true"); + } + else + BOOST_CHECK(parsedCBORMetadata.count("experimental") == 0); }; check(sourceCode, true);