From a2d5c97f26c23d15b26d3983903587fab7ff6ab5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 25 Jun 2020 14:20:17 +0200 Subject: [PATCH] EthPM3-compatible metadata --- libsolidity/interface/CompilerStack.cpp | 177 ++++++++++++++++-------- libsolidity/interface/CompilerStack.h | 7 + 2 files changed, 129 insertions(+), 55 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index eef773095..072de5f6c 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -813,7 +813,7 @@ string const& CompilerStack::metadata(Contract const& _contract) const solAssert(_contract.contract, ""); - return _contract.metadata.init([&]{ return createMetadata(_contract); }); + return _contract.metadata.init([&]{ return createEthPMV3Metadata(_contract); }); } Scanner const& CompilerStack::scanner(string const& _sourceName) const @@ -1192,6 +1192,68 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co return it->second; } +Json::Value CompilerStack::settingsForMetadata() const +{ + Json::Value settings = Json::objectValue; + + static_assert(sizeof(m_optimiserSettings.expectedExecutionsPerDeployment) <= sizeof(Json::LargestUInt), "Invalid word size."); + solAssert(static_cast(m_optimiserSettings.expectedExecutionsPerDeployment) < std::numeric_limits::max(), ""); + settings["optimizer"]["runs"] = Json::Value(Json::LargestUInt(m_optimiserSettings.expectedExecutionsPerDeployment)); + + /// Backwards compatibility: If set to one of the default settings, do not provide details. + OptimiserSettings settingsWithoutRuns = m_optimiserSettings; + // reset to default + settingsWithoutRuns.expectedExecutionsPerDeployment = OptimiserSettings::minimal().expectedExecutionsPerDeployment; + if (settingsWithoutRuns == OptimiserSettings::minimal()) + settings["optimizer"]["enabled"] = false; + else if (settingsWithoutRuns == OptimiserSettings::standard()) + settings["optimizer"]["enabled"] = true; + else + { + Json::Value details{Json::objectValue}; + + details["orderLiterals"] = m_optimiserSettings.runOrderLiterals; + details["jumpdestRemover"] = m_optimiserSettings.runJumpdestRemover; + details["peephole"] = m_optimiserSettings.runPeephole; + details["deduplicate"] = m_optimiserSettings.runDeduplicate; + details["cse"] = m_optimiserSettings.runCSE; + details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser; + details["yul"] = m_optimiserSettings.runYulOptimiser; + if (m_optimiserSettings.runYulOptimiser) + { + details["yulDetails"] = Json::objectValue; + details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation; + details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps; + } + + settings["optimizer"]["details"] = std::move(details); + } + + if (m_revertStrings != RevertStrings::Default) + settings["debug"]["revertStrings"] = revertStringsToString(m_revertStrings); + + if (m_metadataLiteralSources) + settings["metadata"]["useLiteralContent"] = true; + + static vector hashes{"ipfs", "bzzr1", "none"}; + settings["metadata"]["bytecodeHash"] = hashes.at(unsigned(m_metadataHash)); + + settings["evmVersion"] = m_evmVersion.name(); + + settings["remappings"] = Json::arrayValue; + set remappings; + for (auto const& r: m_remappings) + remappings.insert(r.context + ":" + r.prefix + "=" + r.target); + for (auto const& r: remappings) + settings["remappings"].append(r); + + settings["libraries"] = Json::objectValue; + for (auto const& library: m_libraries) + settings["libraries"][library.first] = "0x" + toHex(library.second.asBytes()); + + return settings; +} + string CompilerStack::createMetadata(Contract const& _contract) const { Json::Value meta; @@ -1225,63 +1287,11 @@ string CompilerStack::createMetadata(Contract const& _contract) const } } - static_assert(sizeof(m_optimiserSettings.expectedExecutionsPerDeployment) <= sizeof(Json::LargestUInt), "Invalid word size."); - solAssert(static_cast(m_optimiserSettings.expectedExecutionsPerDeployment) < std::numeric_limits::max(), ""); - meta["settings"]["optimizer"]["runs"] = Json::Value(Json::LargestUInt(m_optimiserSettings.expectedExecutionsPerDeployment)); - - /// Backwards compatibility: If set to one of the default settings, do not provide details. - OptimiserSettings settingsWithoutRuns = m_optimiserSettings; - // reset to default - settingsWithoutRuns.expectedExecutionsPerDeployment = OptimiserSettings::minimal().expectedExecutionsPerDeployment; - if (settingsWithoutRuns == OptimiserSettings::minimal()) - meta["settings"]["optimizer"]["enabled"] = false; - else if (settingsWithoutRuns == OptimiserSettings::standard()) - meta["settings"]["optimizer"]["enabled"] = true; - else - { - Json::Value details{Json::objectValue}; - - details["orderLiterals"] = m_optimiserSettings.runOrderLiterals; - details["jumpdestRemover"] = m_optimiserSettings.runJumpdestRemover; - details["peephole"] = m_optimiserSettings.runPeephole; - details["deduplicate"] = m_optimiserSettings.runDeduplicate; - details["cse"] = m_optimiserSettings.runCSE; - details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser; - details["yul"] = m_optimiserSettings.runYulOptimiser; - if (m_optimiserSettings.runYulOptimiser) - { - details["yulDetails"] = Json::objectValue; - details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation; - details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps; - } - - meta["settings"]["optimizer"]["details"] = std::move(details); - } - - if (m_revertStrings != RevertStrings::Default) - meta["settings"]["debug"]["revertStrings"] = revertStringsToString(m_revertStrings); - - 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"] = settingsForMetadata(); + // This is not part of the json-io settings object. meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = _contract.contract->annotation().canonicalName; - meta["settings"]["remappings"] = Json::arrayValue; - set remappings; - for (auto const& r: m_remappings) - remappings.insert(r.context + ":" + r.prefix + "=" + r.target); - for (auto const& r: remappings) - meta["settings"]["remappings"].append(r); - - meta["settings"]["libraries"] = Json::objectValue; - for (auto const& library: m_libraries) - meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes()); - meta["output"]["abi"] = contractABI(_contract); meta["output"]["userdoc"] = natspecUser(_contract); meta["output"]["devdoc"] = natspecDev(_contract); @@ -1289,6 +1299,63 @@ string CompilerStack::createMetadata(Contract const& _contract) const return util::jsonCompactPrint(meta); } +string CompilerStack::createEthPMV3Metadata(Contract const& _contract) const +{ + Json::Value meta; + meta["manifest"] = "ethpm/3"; + + meta["compilers"] = Json::arrayValue; + meta["compilers"][0] = Json::objectValue; + meta["compilers"][0]["name"] = "solidity"; + meta["compilers"][0]["version"] = VersionStringStrict; + meta["compilers"][0]["settings"] = settingsForMetadata(); + meta["compilers"][0]["contractTypes"] = Json::arrayValue; + meta["compilers"][0]["contractTypes"][0] = _contract.contract->name(); + + +// TODO +// meta["language"] = m_importedSources ? "SolidityAST" : "Solidity"; + + /// All the source files (including self), which should be included in the metadata. + set referencedSources; + referencedSources.insert(_contract.contract->sourceUnit().annotation().path); + for (auto const sourceUnit: _contract.contract->sourceUnit().referencedSourceUnits(true)) + referencedSources.insert(sourceUnit->annotation().path); + + meta["sources"] = Json::objectValue; + for (auto const& s: m_sources) + { + if (!referencedSources.count(s.first)) + continue; + + solAssert(s.second.scanner, "Scanner not available"); + Json::Value& sourceObject = meta["sources"][s.first]; + sourceObject = Json::objectValue; + sourceObject["type"] = "solidity"; + sourceObject["checksum"]["algorithm"] = "keccak256"; + sourceObject["checksum"]["hash"] = "0x" + toHex(s.second.keccak256().asBytes()); + if (optional licenseString = s.second.ast->licenseString()) + sourceObject["license"] = *licenseString; + if (m_metadataLiteralSources) + sourceObject["content"] = s.second.scanner->source(); + else + { + sourceObject["urls"] = Json::arrayValue; + sourceObject["urls"].append("bzz-raw://" + toHex(s.second.swarmHash().asBytes())); + sourceObject["urls"].append(s.second.ipfsUrl()); + } + } + + meta["contractTypes"] = Json::objectValue; + Json::Value& contractType = meta["contractTypes"][_contract.contract->name()]; + contractType["sourceId"] = _contract.contract->sourceUnitName(); + contractType["abi"] = contractABI(_contract); + contractType["userdoc"] = natspecUser(_contract); + contractType["devdoc"] = natspecDev(_contract); + + return util::jsonCompactPrint(meta); +} + class MetadataCBOREncoder { public: diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 509623cc1..28bfd9aeb 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -397,9 +397,16 @@ private: /// does not exist. ContractDefinition const& contractDefinition(std::string const& _contractName) const; + /// @returns the JSON compiler settings object for the metadata. + Json::Value settingsForMetadata() const; + /// @returns the metadata JSON as a compact string for the given contract. std::string createMetadata(Contract const& _contract) const; + /// @returns the metadata JSON as a compact string in a format compatible with EthPM + /// version 3 for the given contract. + std::string createEthPMV3Metadata(Contract const& _contract) const; + /// @returns the metadata CBOR for the given serialised metadata JSON. bytes createCBORMetadata(std::string const& _metadata, bool _experimentalMode);