mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Metadata stamp.
This commit is contained in:
@@ -63,6 +63,14 @@ SourceUnitAnnotation& SourceUnit::annotation() const
|
||||
return static_cast<SourceUnitAnnotation&>(*m_annotation);
|
||||
}
|
||||
|
||||
string Declaration::sourceUnitName() const
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ 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,
|
||||
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(
|
||||
|
||||
@@ -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,
|
||||
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.
|
||||
|
||||
@@ -227,6 +227,13 @@ void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
|
||||
sub.injectStart(fromBigEndian<u256>(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<ContractDefinition const*>::const_iterator _searchStart
|
||||
|
||||
@@ -155,6 +155,9 @@ public:
|
||||
/// Prepends "PUSH <compiler version number> POP"
|
||||
void injectVersionStampIntoSub(size_t _subIndex);
|
||||
|
||||
/// Prepends "PUSH <metadata hash> 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.
|
||||
|
||||
@@ -38,7 +38,10 @@
|
||||
#include <libsolidity/formal/Why3Translator.h>
|
||||
|
||||
#include <libevmasm/Exceptions.h>
|
||||
#include <libdevcore/SHA3.h>
|
||||
|
||||
#include <libdevcore/SwarmHash.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
@@ -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<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;
|
||||
}
|
||||
|
||||
@@ -236,13 +246,13 @@ bool CompilerStack::compile(string const& _sourceCode, bool _optimize)
|
||||
return parse(_sourceCode) && compile(_optimize);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Json::Value const>* 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<ContractDefinition const*, eth::Assembly const*>& _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> 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);
|
||||
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<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);
|
||||
|
||||
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;
|
||||
|
||||
@@ -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<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);
|
||||
|
||||
/// 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<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;
|
||||
size_t 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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user