From 5789eaa78d0e00f6289101e02f7de5e9decdc7e5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 14 Nov 2016 11:46:43 +0100 Subject: Metadata stamp. --- libsolidity/ast/AST.cpp | 8 +++ libsolidity/ast/AST.h | 4 ++ libsolidity/codegen/Compiler.cpp | 10 ++-- libsolidity/codegen/Compiler.h | 3 +- libsolidity/codegen/CompilerContext.cpp | 7 +++ libsolidity/codegen/CompilerContext.h | 3 + libsolidity/interface/CompilerStack.cpp | 98 +++++++++++++++++++++++++++------ libsolidity/interface/CompilerStack.h | 20 +++++-- libsolidity/parsing/Scanner.h | 4 ++ 9 files changed, 126 insertions(+), 31 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 9b478393..f8eb7108 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -63,6 +63,14 @@ SourceUnitAnnotation& SourceUnit::annotation() const return static_cast(*m_annotation); } +string Declaration::sourceUnitName() const +{ + ASTNode const* scope = m_scope; + while (dynamic_cast(scope) && dynamic_cast(scope)->m_scope) + scope = dynamic_cast(scope)->m_scope; + return dynamic_cast(*scope).annotation().path; +} + ImportAnnotation& ImportDirective::annotation() const { if (!m_annotation) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 89b96f09..ab4be1ea 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -158,6 +158,10 @@ public: ASTNode const* scope() const { return m_scope; } void setScope(ASTNode const* _scope) { m_scope = _scope; } + /// @returns the source name this declaration is present in. + /// Can be combined with annotation().canonicalName to form a globally unique name. + std::string sourceUnitName() const; + virtual bool isLValue() const { return false; } virtual bool isPartOfExternalInterface() const { return false; } diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index 22d25698..168e9462 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -30,7 +30,8 @@ using namespace dev::solidity; void Compiler::compileContract( ContractDefinition const& _contract, - std::map const& _contracts + std::map const& _contracts, + h256 const& _metadataHash ) { ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize); @@ -43,11 +44,8 @@ void Compiler::compileContract( m_context.optimise(m_optimize, m_optimizeRuns); - if (_contract.isLibrary()) - { - solAssert(m_runtimeSub != size_t(-1), ""); - m_context.injectVersionStampIntoSub(m_runtimeSub); - } + solAssert(m_runtimeSub != size_t(-1), ""); + m_context.injectMetadataHashIntoSub(m_runtimeSub, _metadataHash); } void Compiler::compileClone( diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h index c8bf7861..58ef1963 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -42,7 +42,8 @@ public: void compileContract( ContractDefinition const& _contract, - std::map const& _contracts + std::map const& _contracts, + h256 const& _metadataHash ); /// Compiles a contract that uses DELEGATECALL to call into a pre-deployed version of the given /// contract at runtime, but contains the full creation-time code. diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index b99b0548..9b921e9d 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -227,6 +227,13 @@ void CompilerContext::injectVersionStampIntoSub(size_t _subIndex) sub.injectStart(fromBigEndian(binaryVersion())); } +void CompilerContext::injectMetadataHashIntoSub(size_t _subIndex, h256 const& _metadataHash) +{ + eth::Assembly& sub = m_asm->sub(_subIndex); + sub.injectStart(Instruction::POP); + sub.injectStart(u256(_metadataHash)); +} + FunctionDefinition const& CompilerContext::resolveVirtualFunction( FunctionDefinition const& _function, vector::const_iterator _searchStart diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 3c6d9d73..c8a35f93 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -155,6 +155,9 @@ public: /// Prepends "PUSH POP" void injectVersionStampIntoSub(size_t _subIndex); + /// Prepends "PUSH POP" + void injectMetadataHashIntoSub(size_t _subIndex, h256 const& _metadataHash); + void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); } /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9305c5e3..ed44b109 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -38,7 +38,10 @@ #include #include -#include + +#include + +#include #include #include @@ -79,6 +82,8 @@ void CompilerStack::reset(bool _keepSources) { m_sources.clear(); } + m_optimize = false; + m_optimizeRuns = 200; m_globalContext.reset(); m_sourceOrder.clear(); m_contracts.clear(); @@ -217,17 +222,22 @@ vector CompilerStack::contractNames() const } -bool CompilerStack::compile(bool _optimize, unsigned _runs) +bool CompilerStack::compile(bool _optimize, unsigned _runs, map const& _libraries) { if (!m_parseSuccessful) if (!parse()) return false; + m_optimize = _optimize; + m_optimizeRuns = _runs; + m_libraries = _libraries; + map compiledContracts; for (Source const* source: m_sourceOrder) for (ASTPointer const& node: source->ast->nodes()) if (auto contract = dynamic_cast(node.get())) - compileContract(_optimize, _runs, *contract, compiledContracts); + compileContract(*contract, compiledContracts); + this->link(); return true; } @@ -236,13 +246,13 @@ bool CompilerStack::compile(string const& _sourceCode, bool _optimize) return parse(_sourceCode) && compile(_optimize); } -void CompilerStack::link(const std::map& _libraries) +void CompilerStack::link() { for (auto& contract: m_contracts) { - contract.second.object.link(_libraries); - contract.second.runtimeObject.link(_libraries); - contract.second.cloneObject.link(_libraries); + contract.second.object.link(m_libraries); + contract.second.runtimeObject.link(m_libraries); + contract.second.cloneObject.link(m_libraries); } } @@ -352,24 +362,31 @@ Json::Value const& CompilerStack::interface(string const& _contractName) const } Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const +{ + if (!m_parseSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + + return metadata(contract(_contractName), _type); +} + +Json::Value const& CompilerStack::metadata(Contract const& _contract, DocumentationType _type) const { if (!m_parseSuccessful) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); std::unique_ptr* doc; - Contract const& currentContract = contract(_contractName); // checks wheather we already have the documentation switch (_type) { case DocumentationType::NatspecUser: - doc = ¤tContract.userDocumentation; + doc = &_contract.userDocumentation; break; case DocumentationType::NatspecDev: - doc = ¤tContract.devDocumentation; + doc = &_contract.devDocumentation; break; case DocumentationType::ABIInterface: - doc = ¤tContract.interface; + doc = &_contract.interface; break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); @@ -377,11 +394,19 @@ Json::Value const& CompilerStack::metadata(string const& _contractName, Document // caches the result if (!*doc) - doc->reset(new Json::Value(InterfaceHandler::documentation(*currentContract.contract, _type))); + doc->reset(new Json::Value(InterfaceHandler::documentation(*_contract.contract, _type))); return *(*doc); } +string const& CompilerStack::onChainMetadata(string const& _contractName) const +{ + if (!m_parseSuccessful) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful.")); + + return contract(_contractName).onChainMetadata; +} + Scanner const& CompilerStack::scanner(string const& _sourceName) const { return *source(_sourceName).scanner; @@ -572,8 +597,6 @@ string CompilerStack::absolutePath(string const& _path, string const& _reference } void CompilerStack::compileContract( - bool _optimize, - unsigned _runs, ContractDefinition const& _contract, map& _compiledContracts ) @@ -581,19 +604,21 @@ void CompilerStack::compileContract( if (_compiledContracts.count(&_contract) || !_contract.annotation().isFullyImplemented) return; for (auto const* dependency: _contract.annotation().contractDependencies) - compileContract(_optimize, _runs, *dependency, _compiledContracts); + compileContract(*dependency, _compiledContracts); - shared_ptr compiler = make_shared(_optimize, _runs); - compiler->compileContract(_contract, _compiledContracts); + shared_ptr compiler = make_shared(m_optimize, m_optimizeRuns); Contract& compiledContract = m_contracts.at(_contract.name()); + string onChainMetadata = createOnChainMetadata(compiledContract); + compiler->compileContract(_contract, _compiledContracts, dev::swarmHash(onChainMetadata)); compiledContract.compiler = compiler; compiledContract.object = compiler->assembledObject(); compiledContract.runtimeObject = compiler->runtimeObject(); + compiledContract.onChainMetadata = onChainMetadata; _compiledContracts[compiledContract.contract] = &compiler->assembly(); try { - Compiler cloneCompiler(_optimize, _runs); + Compiler cloneCompiler(m_optimize, m_optimizeRuns); cloneCompiler.compileClone(_contract, _compiledContracts); compiledContract.cloneObject = cloneCompiler.assembledObject(); } @@ -637,6 +662,43 @@ CompilerStack::Source const& CompilerStack::source(string const& _sourceName) co return it->second; } +string CompilerStack::createOnChainMetadata(Contract const& _contract) const +{ + Json::Value meta; + meta["version"] = 1; + meta["language"] = "Solidity"; + meta["compiler"]["version"] = VersionString; + meta["compilationTarget"] = + _contract.contract->sourceUnitName() + + ":" + + _contract.contract->annotation().canonicalName; + + for (auto const& s: m_sources) + { + solAssert(s.second.scanner, "Scanner not available"); + meta["sources"][s.first]["swarm"] = + "0x" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes()); + } + meta["settings"]["optimizer"]["enabled"] = m_optimize; + meta["settings"]["optimizer"]["runs"] = m_optimizeRuns; + + 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); + + for (auto const& library: m_libraries) + meta["settings"]["libraries"][library.first] = "0x" + toHex(library.second.asBytes()); + + meta["output"]["abi"] = metadata(_contract, DocumentationType::ABIInterface); + meta["output"]["natspec"] = metadata(_contract, DocumentationType::NatspecUser); + + Json::FastWriter writer; + writer.omitEndingLineFeed(); + return writer.write(meta); +} + string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) const { string ret; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index ea706122..74a7dfc5 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -113,14 +113,15 @@ public: /// Compiles the source units that were previously added and parsed. /// @returns false on error. - bool compile(bool _optimize = false, unsigned _runs = 200); + bool compile( + bool _optimize = false, + unsigned _runs = 200, + std::map const& _libraries = std::map{} + ); /// Parses and compiles the given source code. /// @returns false on error. bool compile(std::string const& _sourceCode, bool _optimize = false); - /// Inserts the given addresses into the linker objects of all compiled contracts. - void link(std::map const& _libraries); - /// Tries to translate all source files into a language suitable for formal analysis. /// @param _errors list to store errors - defaults to the internal error list. /// @returns false on error. @@ -170,6 +171,7 @@ public: /// @param type The type of the documentation to get. /// Can be one of 4 types defined at @c DocumentationType Json::Value const& metadata(std::string const& _contractName, DocumentationType _type) const; + std::string const& onChainMetadata(std::string const& _contractName) const; /// @returns the previously used scanner, useful for counting lines during error reporting. Scanner const& scanner(std::string const& _sourceName = "") const; @@ -213,6 +215,7 @@ private: eth::LinkerObject object; eth::LinkerObject runtimeObject; eth::LinkerObject cloneObject; + std::string onChainMetadata; ///< The metadata json that will be hashed into the chain. mutable std::unique_ptr interface; mutable std::unique_ptr userDocumentation; mutable std::unique_ptr devDocumentation; @@ -233,16 +236,18 @@ private: std::string absolutePath(std::string const& _path, std::string const& _reference) const; /// Compile a single contract and put the result in @a _compiledContracts. void compileContract( - bool _optimize, - unsigned _runs, ContractDefinition const& _contract, std::map& _compiledContracts ); + void link(); + Contract const& contract(std::string const& _contractName = "") const; Source const& source(std::string const& _sourceName = "") const; + std::string createOnChainMetadata(Contract const& _contract) const; std::string computeSourceMapping(eth::AssemblyItems const& _items) const; + Json::Value const& metadata(Contract const&, DocumentationType _type) const; struct Remapping { @@ -252,6 +257,9 @@ private: }; ReadFileCallback m_readFile; + bool m_optimize = false; + size_t m_optimizeRuns = 200; + std::map m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum /// "context:prefix=target" std::vector m_remappings; diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index 65de8bd4..d6b48c6f 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -80,6 +80,8 @@ public: void reset() { m_position = 0; } + std::string const& source() const { return m_source; } + ///@{ ///@name Error printing helper functions /// Functions that help pretty-printing parse errors @@ -102,6 +104,8 @@ public: explicit Scanner(CharStream const& _source = CharStream(), std::string const& _sourceName = "") { reset(_source, _sourceName); } + std::string source() const { return m_source.source(); } + /// Resets the scanner as if newly constructed with _source and _sourceName as input. void reset(CharStream const& _source, std::string const& _sourceName); /// Resets scanner to the start of input. -- cgit v1.2.3