aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/ast/AST.cpp9
-rw-r--r--libsolidity/ast/AST.h4
-rw-r--r--libsolidity/codegen/Compiler.cpp10
-rw-r--r--libsolidity/codegen/Compiler.h3
-rw-r--r--libsolidity/codegen/CompilerContext.cpp7
-rw-r--r--libsolidity/codegen/CompilerContext.h4
-rw-r--r--libsolidity/interface/CompilerStack.cpp113
-rw-r--r--libsolidity/interface/CompilerStack.h22
-rw-r--r--libsolidity/parsing/Scanner.h4
9 files changed, 132 insertions, 44 deletions
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 9b478393..3cd1dfbe 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -63,6 +63,15 @@ SourceUnitAnnotation& SourceUnit::annotation() const
return static_cast<SourceUnitAnnotation&>(*m_annotation);
}
+string Declaration::sourceUnitName() const
+{
+ solAssert(!!m_scope, "");
+ ASTNode const* scope = m_scope;
+ while (dynamic_cast<Declaration const*>(scope) && dynamic_cast<Declaration const*>(scope)->m_scope)
+ scope = dynamic_cast<Declaration const*>(scope)->m_scope;
+ return dynamic_cast<SourceUnit const&>(*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..44264a07 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -30,11 +30,13 @@ using namespace dev::solidity;
void Compiler::compileContract(
ContractDefinition const& _contract,
- std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
+ std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts,
+ bytes const& _metadata
)
{
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
runtimeCompiler.compileContract(_contract, _contracts);
+ m_runtimeContext.appendAuxiliaryData(_metadata);
// This might modify m_runtimeContext because it can access runtime functions at
// creation time.
@@ -42,12 +44,6 @@ void Compiler::compileContract(
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
m_context.optimise(m_optimize, m_optimizeRuns);
-
- if (_contract.isLibrary())
- {
- solAssert(m_runtimeSub != size_t(-1), "");
- m_context.injectVersionStampIntoSub(m_runtimeSub);
- }
}
void Compiler::compileClone(
diff --git a/libsolidity/codegen/Compiler.h b/libsolidity/codegen/Compiler.h
index c8bf7861..eef078c1 100644
--- a/libsolidity/codegen/Compiler.h
+++ b/libsolidity/codegen/Compiler.h
@@ -42,7 +42,8 @@ public:
void compileContract(
ContractDefinition const& _contract,
- std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
+ std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts,
+ bytes const& _metadata
);
/// 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..2de5a3ec 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -220,13 +220,6 @@ void CompilerContext::appendInlineAssembly(
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "");
}
-void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
-{
- eth::Assembly& sub = m_asm->sub(_subIndex);
- sub.injectStart(Instruction::POP);
- sub.injectStart(fromBigEndian<u256>(binaryVersion()));
-}
-
FunctionDefinition const& CompilerContext::resolveVirtualFunction(
FunctionDefinition const& _function,
vector<ContractDefinition const*>::const_iterator _searchStart
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 3c6d9d73..80671528 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -152,8 +152,8 @@ public:
std::map<std::string, std::string> const& _replacements = std::map<std::string, std::string>{}
);
- /// Prepends "PUSH <compiler version number> POP"
- void injectVersionStampIntoSub(size_t _subIndex);
+ /// Appends arbitrary data to the end of the bytecode.
+ void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); }
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); }
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 9305c5e3..b4fd6d87 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -38,7 +38,11 @@
#include <libsolidity/formal/Why3Translator.h>
#include <libevmasm/Exceptions.h>
-#include <libdevcore/SHA3.h>
+
+#include <libdevcore/SwarmHash.h>
+#include <libdevcore/JSON.h>
+
+#include <json/json.h>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
@@ -79,6 +83,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,32 +223,37 @@ vector<string> CompilerStack::contractNames() const
}
-bool CompilerStack::compile(bool _optimize, unsigned _runs)
+bool CompilerStack::compile(bool _optimize, unsigned _runs, map<string, h160> const& _libraries)
{
if (!m_parseSuccessful)
if (!parse())
return false;
+ m_optimize = _optimize;
+ m_optimizeRuns = _runs;
+ m_libraries = _libraries;
+
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (auto contract = dynamic_cast<ContractDefinition const*>(node.get()))
- compileContract(_optimize, _runs, *contract, compiledContracts);
+ compileContract(*contract, compiledContracts);
+ this->link();
return true;
}
-bool CompilerStack::compile(string const& _sourceCode, bool _optimize)
+bool CompilerStack::compile(string const& _sourceCode, bool _optimize, unsigned _runs)
{
- return parse(_sourceCode) && compile(_optimize);
+ return parse(_sourceCode) && compile(_optimize, _runs);
}
-void CompilerStack::link(const std::map<string, h160>& _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);
}
}
@@ -356,20 +367,28 @@ Json::Value const& CompilerStack::metadata(string const& _contractName, Document
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."));
+
+ solAssert(_contract.contract, "");
std::unique_ptr<Json::Value const>* doc;
- Contract const& currentContract = contract(_contractName);
// checks wheather we already have the documentation
switch (_type)
{
case DocumentationType::NatspecUser:
- doc = &currentContract.userDocumentation;
+ doc = &_contract.userDocumentation;
break;
case DocumentationType::NatspecDev:
- doc = &currentContract.devDocumentation;
+ doc = &_contract.devDocumentation;
break;
case DocumentationType::ABIInterface:
- doc = &currentContract.interface;
+ doc = &_contract.interface;
break;
default:
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type."));
@@ -377,11 +396,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 +599,6 @@ string CompilerStack::absolutePath(string const& _path, string const& _reference
}
void CompilerStack::compileContract(
- bool _optimize,
- unsigned _runs,
ContractDefinition const& _contract,
map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts
)
@@ -581,19 +606,28 @@ 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> compiler = make_shared<Compiler>(_optimize, _runs);
- compiler->compileContract(_contract, _compiledContracts);
+ shared_ptr<Compiler> compiler = make_shared<Compiler>(m_optimize, m_optimizeRuns);
Contract& compiledContract = m_contracts.at(_contract.name());
+ string onChainMetadata = createOnChainMetadata(compiledContract);
+ bytes cborEncodedMetadata =
+ // CBOR-encoding of {"bzzr0": dev::swarmHash(onChainMetadata)}
+ bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} +
+ dev::swarmHash(onChainMetadata).asBytes();
+ solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large");
+ // 16-bit big endian length
+ cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2);
+ compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata);
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 +671,45 @@ 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["sources"] = Json::objectValue;
+ for (auto const& s: m_sources)
+ {
+ solAssert(s.second.scanner, "Scanner not available");
+ meta["sources"][s.first]["keccak256"] =
+ "0x" + toHex(dev::keccak256(s.second.scanner->source()).asBytes());
+ meta["sources"][s.first]["url"] =
+ "bzzr://" + toHex(dev::swarmHash(s.second.scanner->source()).asBytes());
+ }
+ meta["settings"]["optimizer"]["enabled"] = m_optimize;
+ meta["settings"]["optimizer"]["runs"] = m_optimizeRuns;
+ meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
+ _contract.contract->annotation().canonicalName;
+
+ meta["settings"]["remappings"] = Json::arrayValue;
+ set<string> 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"] = metadata(_contract, DocumentationType::ABIInterface);
+ meta["output"]["userdoc"] = metadata(_contract, DocumentationType::NatspecUser);
+ meta["output"]["devdoc"] = metadata(_contract, DocumentationType::NatspecDev);
+
+ return jsonCompactPrint(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..f98a457a 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -113,13 +113,14 @@ 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<std::string, h160> const& _libraries = std::map<std::string, h160>{}
+ );
/// 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<std::string, h160> const& _libraries);
+ bool compile(std::string const& _sourceCode, bool _optimize = false, unsigned _runs = 200);
/// 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.
@@ -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<Json::Value const> interface;
mutable std::unique_ptr<Json::Value const> userDocumentation;
mutable std::unique_ptr<Json::Value const> 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<ContractDefinition const*, eth::Assembly const*>& _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;
+ unsigned m_optimizeRuns = 200;
+ std::map<std::string, h160> m_libraries;
/// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum
/// "context:prefix=target"
std::vector<Remapping> 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.