diff options
Diffstat (limited to 'libsolidity/interface/StandardCompiler.cpp')
-rw-r--r-- | libsolidity/interface/StandardCompiler.cpp | 86 |
1 files changed, 81 insertions, 5 deletions
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index db89e16c..2b280dd5 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -25,6 +25,7 @@ #include <libsolidity/ast/ASTJsonConverter.h> #include <libevmasm/Instruction.h> #include <libdevcore/JSON.h> +#include <libdevcore/SHA3.h> using namespace std; using namespace dev; @@ -91,6 +92,19 @@ Json::Value formatErrorWithException( return formatError(_warning, _type, _component, message, formattedMessage, location); } +/// Returns true iff @a _hash (hex with 0x prefix) is the Keccak256 hash of the binary data in @a _content. +bool hashMatchesContent(string const& _hash, string const& _content) +{ + try + { + return dev::h256(_hash) == dev::keccak256(_content); + } + catch (dev::BadHexCharacter) + { + return false; + } +} + StringMap createSourceList(Json::Value const& _input) { StringMap sources; @@ -162,13 +176,72 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (!sources) return formatFatalError("JSONError", "No input sources specified."); + Json::Value errors = Json::arrayValue; + for (auto const& sourceName: sources.getMemberNames()) + { + string hash; + if (sources[sourceName]["keccak256"].isString()) + hash = sources[sourceName]["keccak256"].asString(); + if (sources[sourceName]["content"].isString()) - m_compilerStack.addSource(sourceName, sources[sourceName]["content"].asString()); + { + string content = sources[sourceName]["content"].asString(); + if (!hash.empty() && !hashMatchesContent(hash, content)) + errors.append(formatError( + false, + "IOError", + "general", + "Mismatch between content and supplied hash for \"" + sourceName + "\"" + )); + else + m_compilerStack.addSource(sourceName, content); + } else if (sources[sourceName]["urls"].isArray()) - return formatFatalError("UnimplementedFeatureError", "Input URLs not supported yet."); + { + if (!m_readFile) + return formatFatalError("JSONError", "No import callback supplied, but URL is requested."); + + bool found = false; + vector<string> failures; + + for (auto const& url: sources[sourceName]["urls"]) + { + ReadFile::Result result = m_readFile(url.asString()); + if (result.success) + { + if (!hash.empty() && !hashMatchesContent(hash, result.contentsOrErrorMessage)) + errors.append(formatError( + false, + "IOError", + "general", + "Mismatch between content and supplied hash for \"" + sourceName + "\" at \"" + url.asString() + "\"" + )); + else + { + m_compilerStack.addSource(sourceName, result.contentsOrErrorMessage); + found = true; + break; + } + } + else + failures.push_back("Cannot import url (\"" + url.asString() + "\"): " + result.contentsOrErrorMessage); + } + + for (auto const& failure: failures) + { + /// If the import succeeded, let mark all the others as warnings, otherwise all of them are errors. + errors.append(formatError( + found ? true : false, + "IOError", + "general", + failure + )); + } + } else return formatFatalError("JSONError", "Invalid input source specified."); + } Json::Value const& settings = _input.get("settings", Json::Value()); @@ -196,7 +269,6 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); }; - Json::Value errors = Json::arrayValue; bool success = false; try @@ -306,7 +378,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) } Json::Value contractsOutput = Json::objectValue; - for (string const& contractName: m_compilerStack.contractNames()) + for (string const& contractName: success ? m_compilerStack.contractNames() : vector<string>()) { size_t colon = contractName.find(':'); solAssert(colon != string::npos, ""); @@ -382,9 +454,13 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) { return compileInternal(_input); } + catch (Exception const& _exception) + { + return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compileInternal: " + boost::diagnostic_information(_exception)); + } catch (...) { - return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compilerInternal"); + return formatFatalError("InternalCompilerError", "Internal exception in StandardCompiler::compileInternal"); } } |