mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
3a01a87afe
@ -1,6 +1,7 @@
|
||||
### 0.4.7 (unreleased)
|
||||
|
||||
Features:
|
||||
* Code generator: Inject the Swarm hash of a metadata file into the bytecode.
|
||||
* Optimizer: Some dead code elimination.
|
||||
|
||||
Bugfixes:
|
||||
|
@ -232,6 +232,142 @@ Either add ``--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"`` to
|
||||
|
||||
If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__LibraryName____``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case.
|
||||
|
||||
*****************
|
||||
Contract Metadata
|
||||
*****************
|
||||
|
||||
The Solidity compiler automatically generates a JSON file, the
|
||||
contract metadata, that contains information about the current contract.
|
||||
It can be used to query the compiler version, the sources used, the ABI
|
||||
and NatSpec documentation in order to more safely interact with the contract
|
||||
and to verify its source code.
|
||||
|
||||
The compiler appends a Swarm hash of the metadata file to the end of the
|
||||
bytecode (for details, see below) of each contract, so that you can retrieve
|
||||
the file in an authenticated way without having to resort to a centralized
|
||||
data provider.
|
||||
|
||||
Of course, you have to publish the metadata file to Swarm (or some other service)
|
||||
so that others can access it. The file can be output by using ``solc --metadata``
|
||||
and the file will be called ``ContractName_meta.json``.
|
||||
It will contain Swarm references to the source code, so you have to upload
|
||||
all source files and the metadata file.
|
||||
|
||||
The metadata file has the following format. The example below is presented in a
|
||||
human-readable way. Properly formatted metadata should use quotes correctly,
|
||||
reduce whitespace to a minimum and sort the keys of all objects to arrive at a
|
||||
unique formatting.
|
||||
Comments are of course also not permitted and used here only for explanatory purposes.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
{
|
||||
// Required: The version of the metadata format
|
||||
version: "1",
|
||||
// Required: Source code language, basically selects a "sub-version"
|
||||
// of the specification
|
||||
language: "Solidity",
|
||||
// Required: Details about the compiler, contents are specific
|
||||
// to the language.
|
||||
compiler: {
|
||||
// Required for Solidity: Version of the compiler
|
||||
version: "0.4.6+commit.2dabbdf0.Emscripten.clang",
|
||||
// Optional: Hash of the compiler binary which produced this output
|
||||
keccak256: "0x123..."
|
||||
},
|
||||
// Required: Compilation source files/source units, keys are file names
|
||||
sources:
|
||||
{
|
||||
"myFile.sol": {
|
||||
// Required: keccak256 hash of the source file
|
||||
"keccak256": "0x123...",
|
||||
// Required (unless "content" is used, see below): URL to the
|
||||
// source file, protocol is more or less arbitrary, but a Swarm
|
||||
// URL is recommended
|
||||
"url": "bzzr://56ab..."
|
||||
},
|
||||
"mortal": {
|
||||
// Required: keccak256 hash of the source file
|
||||
"keccak256": "0x234...",
|
||||
// Required (unless "url" is used): literal contents of the source file
|
||||
"content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }"
|
||||
}
|
||||
},
|
||||
// Required: Compiler settings
|
||||
settings:
|
||||
{
|
||||
// Required for Solidity: Sorted list of remappings
|
||||
remappings: [ ":g/dir" ],
|
||||
// Optional: Optimizer settings (enabled defaults to false)
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 500
|
||||
},
|
||||
// Required for Solidity: File and name of the contract or library this
|
||||
// metadata is created for.
|
||||
compilationTarget: {
|
||||
"myFile.sol": "MyContract"
|
||||
},
|
||||
// Required for Solidity: Addresses for libraries used
|
||||
libraries: {
|
||||
"MyLib": "0x123123..."
|
||||
}
|
||||
},
|
||||
// Required: Generated information about the contract.
|
||||
output:
|
||||
{
|
||||
// Required: ABI definition of the contract
|
||||
abi: [ ... ],
|
||||
// Required: NatSpec user documentation of the contract
|
||||
userdoc: [ ... ],
|
||||
// Required: NatSpec developer documentation of the contract
|
||||
devdoc: [ ... ],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Encoding of the Metadata Hash in the Bytecode
|
||||
=============================================
|
||||
|
||||
Because we might support other ways to retrieve the metadata file in the future,
|
||||
the mapping ``{"bzzr0": <Swarm hash>}`` is stored
|
||||
[CBOR](https://tools.ietf.org/html/rfc7049)-encoded. Since the beginning of that
|
||||
encoding is not easy to find, its length is added in a two-byte big-endian
|
||||
encoding. The current version of the Solidity compiler thus adds the following
|
||||
to the end of the deployed bytecode::
|
||||
|
||||
0xa1 0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash> 0x00 0x29
|
||||
|
||||
So in order to retrieve the data, the end of the deployed bytecode can be checked
|
||||
to match that pattern and use the Swarm hash to retrieve the file.
|
||||
|
||||
Usage for Automatic Interface Generation and NatSpec
|
||||
====================================================
|
||||
|
||||
The metadata is used in the following way: A component that wants to interact
|
||||
with a contract (e.g. Mist) retrieves the code of the contract, from that
|
||||
the Swarm hash of a file which is then retrieved.
|
||||
That file is JSON-decoded into a structure like above.
|
||||
|
||||
The component can then use the ABI to automatically generate a rudimentary
|
||||
user interface for the contract.
|
||||
|
||||
Furthermore, Mist can use the userdoc to display a confirmation message to the user
|
||||
whenever they interact with the contract.
|
||||
|
||||
Usage for Source Code Verification
|
||||
==================================
|
||||
|
||||
In order to verify the compilation, sources can be retrieved from Swarm
|
||||
via the link in the metadata file.
|
||||
The compiler of the correct version (which is checked to be part of the "official" compilers)
|
||||
is invoked on that input with the specified settings. The resulting
|
||||
bytecode is compared to the data of the creation transaction or CREATE opcode data.
|
||||
This automatically verifies the metadata since its hash is part of the bytecode.
|
||||
Excess data corresponds to the constructor input data, which should be decoded
|
||||
according to the interface and presented to the user.
|
||||
|
||||
|
||||
***************
|
||||
Tips and Tricks
|
||||
***************
|
||||
|
@ -28,13 +28,13 @@ namespace dev
|
||||
{
|
||||
|
||||
/// Serialise the JSON object (@a _input) with identation
|
||||
std::string jsonPrettyPrint(Json::Value const& _input)
|
||||
inline std::string jsonPrettyPrint(Json::Value const& _input)
|
||||
{
|
||||
return Json::StyledWriter().write(_input);
|
||||
}
|
||||
|
||||
/// Serialise theJ SON object (@a _input) without identation
|
||||
std::string jsonCompactPrint(Json::Value const& _input)
|
||||
inline std::string jsonCompactPrint(Json::Value const& _input)
|
||||
{
|
||||
Json::FastWriter writer;
|
||||
writer.omitEndingLineFeed();
|
||||
|
@ -38,13 +38,14 @@ h256 swarmHashSimple(bytesConstRef _data, size_t _size)
|
||||
return keccak256(toLittleEndian(_size) + _data.toBytes());
|
||||
}
|
||||
|
||||
h256 swarmHashIntermediate(bytes const& _input, size_t _offset, size_t _length)
|
||||
h256 swarmHashIntermediate(string const& _input, size_t _offset, size_t _length)
|
||||
{
|
||||
bytesConstRef ref;
|
||||
bytes innerNodes;
|
||||
if (_length <= 0x1000)
|
||||
return swarmHashSimple(bytesConstRef(_input.data() + _offset, _length), _length);
|
||||
ref = bytesConstRef(_input).cropped(_offset, _length);
|
||||
else
|
||||
{
|
||||
bytes innerNodes;
|
||||
size_t maxRepresentedSize = 0x1000;
|
||||
while (maxRepresentedSize * (0x1000 / 32) < _length)
|
||||
maxRepresentedSize *= (0x1000 / 32);
|
||||
@ -53,11 +54,12 @@ h256 swarmHashIntermediate(bytes const& _input, size_t _offset, size_t _length)
|
||||
size_t size = std::min(maxRepresentedSize, _length - i);
|
||||
innerNodes += swarmHashIntermediate(_input, _offset + i, size).asBytes();
|
||||
}
|
||||
return swarmHashSimple(bytesConstRef(&innerNodes), _length);
|
||||
ref = bytesConstRef(&innerNodes);
|
||||
}
|
||||
return swarmHashSimple(ref, _length);
|
||||
}
|
||||
|
||||
h256 dev::swarmHash(bytes const& _input)
|
||||
h256 dev::swarmHash(string const& _input)
|
||||
{
|
||||
return swarmHashIntermediate(_input, 0, _input.size());
|
||||
}
|
||||
|
@ -20,12 +20,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <libdevcore/FixedHash.h>
|
||||
#include <libdevcore/Common.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
/// Compute the "swarm hash" of @a _data
|
||||
h256 swarmHash(bytes const& _data);
|
||||
h256 swarmHash(std::string const& _data);
|
||||
|
||||
}
|
||||
|
@ -432,7 +432,7 @@ LinkerObject const& Assembly::assemble() const
|
||||
unsigned bytesPerTag = dev::bytesRequired(bytesRequiredForCode);
|
||||
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
|
||||
|
||||
unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1;
|
||||
unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + m_auxiliaryData.size();
|
||||
for (auto const& sub: m_subs)
|
||||
bytesRequiredIncludingData += sub->assemble().bytecode.size();
|
||||
|
||||
@ -525,8 +525,10 @@ LinkerObject const& Assembly::assemble() const
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataRef.empty() && !subRef.empty())
|
||||
if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty())
|
||||
// Append a STOP just to be sure.
|
||||
ret.bytecode.push_back(0);
|
||||
|
||||
for (size_t i = 0; i < m_subs.size(); ++i)
|
||||
{
|
||||
auto references = subRef.equal_range(i);
|
||||
@ -568,6 +570,9 @@ LinkerObject const& Assembly::assemble() const
|
||||
}
|
||||
ret.bytecode += dataItem.second;
|
||||
}
|
||||
|
||||
ret.bytecode += m_auxiliaryData;
|
||||
|
||||
for (unsigned pos: sizeRef)
|
||||
{
|
||||
bytesRef r(ret.bytecode.data() + pos, bytesPerDataRef);
|
||||
|
@ -71,6 +71,9 @@ public:
|
||||
AssemblyItem appendJumpI(AssemblyItem const& _tag) { auto ret = append(_tag.pushTag()); append(solidity::Instruction::JUMPI); return ret; }
|
||||
AssemblyItem errorTag() { return AssemblyItem(PushTag, 0); }
|
||||
|
||||
/// Appends @a _data literally to the very end of the bytecode.
|
||||
void appendAuxiliaryDataToEnd(bytes const& _data) { m_auxiliaryData += _data; }
|
||||
|
||||
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
|
||||
AssemblyItems const& items() const { return m_items; }
|
||||
AssemblyItem const& back() const { return m_items.back(); }
|
||||
@ -125,10 +128,12 @@ private:
|
||||
Json::Value createJsonValue(std::string _name, int _begin, int _end, std::string _value = std::string(), std::string _jumpType = std::string()) const;
|
||||
|
||||
protected:
|
||||
// 0 is reserved for exception
|
||||
/// 0 is reserved for exception
|
||||
unsigned m_usedTags = 1;
|
||||
AssemblyItems m_items;
|
||||
std::map<h256, bytes> m_data;
|
||||
/// Data that is appended to the very end of the contract.
|
||||
bytes m_auxiliaryData;
|
||||
std::vector<std::shared_ptr<Assembly>> m_subs;
|
||||
std::map<h256, std::string> m_strings;
|
||||
std::map<h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
||||
|
@ -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)
|
||||
|
@ -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,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(
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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 = ¤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 +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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -78,6 +78,7 @@ static string const g_argCloneBinaryStr = "clone-bin";
|
||||
static string const g_argOpcodesStr = "opcodes";
|
||||
static string const g_argNatspecDevStr = "devdoc";
|
||||
static string const g_argNatspecUserStr = "userdoc";
|
||||
static string const g_argMetadata = "metadata";
|
||||
static string const g_argAddStandard = "add-std";
|
||||
static string const g_stdinFileName = "<stdin>";
|
||||
|
||||
@ -91,6 +92,7 @@ static set<string> const g_combinedJsonArgs{
|
||||
"opcodes",
|
||||
"abi",
|
||||
"interface",
|
||||
"metadata",
|
||||
"asm",
|
||||
"ast",
|
||||
"userdoc",
|
||||
@ -117,6 +119,7 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args)
|
||||
for (string const& arg: {
|
||||
g_argAbiStr,
|
||||
g_argSignatureHashes,
|
||||
g_argMetadata,
|
||||
g_argNatspecUserStr,
|
||||
g_argAstJson,
|
||||
g_argNatspecDevStr,
|
||||
@ -202,6 +205,18 @@ void CommandLineInterface::handleSignatureHashes(string const& _contract)
|
||||
cout << "Function signatures: " << endl << out;
|
||||
}
|
||||
|
||||
void CommandLineInterface::handleOnChainMetadata(string const& _contract)
|
||||
{
|
||||
if (!m_args.count(g_argMetadata))
|
||||
return;
|
||||
|
||||
string data = m_compiler->onChainMetadata(_contract);
|
||||
if (m_args.count("output-dir"))
|
||||
createFile(_contract + "_meta.json", data);
|
||||
else
|
||||
cout << "Metadata: " << endl << data << endl;
|
||||
}
|
||||
|
||||
void CommandLineInterface::handleMeta(DocumentationType _type, string const& _contract)
|
||||
{
|
||||
std::string argName;
|
||||
@ -467,6 +482,7 @@ Allowed options)",
|
||||
(g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.")
|
||||
(g_argNatspecUserStr.c_str(), "Natspec user documentation of all contracts.")
|
||||
(g_argNatspecDevStr.c_str(), "Natspec developer documentation of all contracts.")
|
||||
(g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.")
|
||||
("formal", "Translated source suitable for formal analysis.");
|
||||
desc.add(outputComponents);
|
||||
|
||||
@ -581,9 +597,7 @@ bool CommandLineInterface::processInput()
|
||||
// TODO: Perhaps we should not compile unless requested
|
||||
bool optimize = m_args.count("optimize") > 0;
|
||||
unsigned runs = m_args["optimize-runs"].as<unsigned>();
|
||||
bool successful = m_compiler->compile(optimize, runs);
|
||||
if (successful)
|
||||
m_compiler->link(m_libraries);
|
||||
bool successful = m_compiler->compile(optimize, runs, m_libraries);
|
||||
|
||||
if (successful && m_args.count("formal"))
|
||||
if (!m_compiler->prepareFormalAnalysis())
|
||||
@ -659,6 +673,8 @@ void CommandLineInterface::handleCombinedJSON()
|
||||
Json::Value contractData(Json::objectValue);
|
||||
if (requests.count("abi"))
|
||||
contractData["abi"] = dev::jsonCompactPrint(m_compiler->interface(contractName));
|
||||
if (requests.count("metadata"))
|
||||
contractData["metadata"] = m_compiler->onChainMetadata(contractName);
|
||||
if (requests.count("bin"))
|
||||
contractData["bin"] = m_compiler->object(contractName).toHex();
|
||||
if (requests.count("bin-runtime"))
|
||||
@ -918,6 +934,7 @@ void CommandLineInterface::outputCompilationResults()
|
||||
|
||||
handleBytecode(contract);
|
||||
handleSignatureHashes(contract);
|
||||
handleOnChainMetadata(contract);
|
||||
handleMeta(DocumentationType::ABIInterface, contract);
|
||||
handleMeta(DocumentationType::NatspecDev, contract);
|
||||
handleMeta(DocumentationType::NatspecUser, contract);
|
||||
|
@ -63,6 +63,7 @@ private:
|
||||
void handleOpcode(std::string const& _contract);
|
||||
void handleBytecode(std::string const& _contract);
|
||||
void handleSignatureHashes(std::string const& _contract);
|
||||
void handleOnChainMetadata(std::string const& _contract);
|
||||
void handleMeta(DocumentationType _type, std::string const& _contract);
|
||||
void handleGasEstimation(std::string const& _contract);
|
||||
void handleFormal();
|
||||
|
@ -218,6 +218,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
||||
contractData["bytecode"] = compiler.object(contractName).toHex();
|
||||
contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
|
||||
contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode);
|
||||
contractData["metadata"] = compiler.onChainMetadata(contractName);
|
||||
contractData["functionHashes"] = functionHashes(compiler.contractDefinition(contractName));
|
||||
contractData["gasEstimates"] = estimateGas(compiler, contractName);
|
||||
auto sourceMap = compiler.sourceMapping(contractName);
|
||||
|
@ -279,7 +279,7 @@ protected:
|
||||
bytes data;
|
||||
};
|
||||
|
||||
size_t m_optimizeRuns = 200;
|
||||
unsigned m_optimizeRuns = 200;
|
||||
bool m_optimize = false;
|
||||
Address m_sender;
|
||||
Address m_contractAddress;
|
||||
|
@ -31,24 +31,24 @@ namespace test
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(SwarmHash)
|
||||
|
||||
string swarmHashHex(bytes const& _input)
|
||||
string swarmHashHex(string const& _input)
|
||||
{
|
||||
return toHex(swarmHash(_input).asBytes());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(test_zeros)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes()), string("011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x1000 - 1, 0)), string("32f0faabc4265ac238cd945087133ce3d7e9bb2e536053a812b5373c54043adb"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x1000, 0)), string("411dd45de7246e94589ff5888362c41e85bd3e582a92d0fda8f0e90b76439bec"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x1000 + 1, 0)), string("69754a0098432bbc2e84fe1205276870748a61a065ab6ef44d6a2e7b13ce044d"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x2000 - 1, 0)), string("69ad3c581043404f775ffa8d6f1b25ad4a9ee812971190e90209c0966116a321"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x2000, 0)), string("f00222373ff82d0a178dc6271c78953e9c88f74130a52d401f5ec51475f63c43"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x2000 + 1, 0)), string("86d6773e79e02fd8145ee1aedba89ace0c15f2566db1249654000039a9a134bf"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x80000, 0)), string("cc0854fe2c6b98e920d5c14b1a88e6d4223e55b8f78883f60939aa2485e361bf"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x80020, 0)), string("ee9ffca246e70d3704740ba4df450fa6988d14a1c2439c7e734c7a77a4eb6fd3"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x800020, 0)), string("78b90b20c90559fb904535181a7c28929ea2f30a2329dbc25232de579709f12f"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(bytes(2095104, 0)), string("a9958184589fc11b4027a4c233e777ebe2e99c66f96b74aef2a0638a94dd5439"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string()), string("011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x1000 - 1, 0)), string("32f0faabc4265ac238cd945087133ce3d7e9bb2e536053a812b5373c54043adb"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x1000, 0)), string("411dd45de7246e94589ff5888362c41e85bd3e582a92d0fda8f0e90b76439bec"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x1000 + 1, 0)), string("69754a0098432bbc2e84fe1205276870748a61a065ab6ef44d6a2e7b13ce044d"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x2000 - 1, 0)), string("69ad3c581043404f775ffa8d6f1b25ad4a9ee812971190e90209c0966116a321"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x2000, 0)), string("f00222373ff82d0a178dc6271c78953e9c88f74130a52d401f5ec51475f63c43"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x2000 + 1, 0)), string("86d6773e79e02fd8145ee1aedba89ace0c15f2566db1249654000039a9a134bf"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x80000, 0)), string("cc0854fe2c6b98e920d5c14b1a88e6d4223e55b8f78883f60939aa2485e361bf"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x80020, 0)), string("ee9ffca246e70d3704740ba4df450fa6988d14a1c2439c7e734c7a77a4eb6fd3"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(0x800020, 0)), string("78b90b20c90559fb904535181a7c28929ea2f30a2329dbc25232de579709f12f"));
|
||||
BOOST_CHECK_EQUAL(swarmHashHex(string(2095104, 0)), string("a9958184589fc11b4027a4c233e777ebe2e99c66f96b74aef2a0638a94dd5439"));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -75,7 +75,7 @@ eth::AssemblyItems compileContract(const string& _sourceCode)
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
{
|
||||
Compiler compiler;
|
||||
compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{});
|
||||
compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}, bytes());
|
||||
|
||||
return compiler.runtimeAssemblyItems();
|
||||
}
|
||||
|
@ -22,8 +22,11 @@
|
||||
|
||||
#include "../TestHelper.h"
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
#include <json/json.h>
|
||||
|
||||
#include <libdevcore/Exceptions.h>
|
||||
#include <libdevcore/SwarmHash.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -51,7 +54,7 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
CompilerStack m_compilerStack;
|
||||
Json::Reader m_reader;
|
||||
};
|
||||
@ -731,6 +734,26 @@ BOOST_AUTO_TEST_CASE(function_type)
|
||||
checkInterface(sourceCode, interface);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(metadata_stamp)
|
||||
{
|
||||
// Check that the metadata stamp is at the end of the runtime bytecode.
|
||||
char const* sourceCode = R"(
|
||||
pragma solidity >=0.0;
|
||||
contract test {
|
||||
function g(function(uint) external returns (uint) x) {}
|
||||
}
|
||||
)";
|
||||
BOOST_REQUIRE(m_compilerStack.compile(std::string(sourceCode)));
|
||||
bytes const& bytecode = m_compilerStack.runtimeObject("test").bytecode;
|
||||
bytes hash = dev::swarmHash(m_compilerStack.onChainMetadata("test")).asBytes();
|
||||
BOOST_REQUIRE(hash.size() == 32);
|
||||
BOOST_REQUIRE(bytecode.size() >= 2);
|
||||
size_t metadataCBORSize = (size_t(bytecode.end()[-2]) << 8) + size_t(bytecode.end()[-1]);
|
||||
BOOST_REQUIRE(metadataCBORSize < bytecode.size() - 2);
|
||||
bytes expectation = bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + hash;
|
||||
BOOST_CHECK(std::equal(expectation.begin(), expectation.end(), bytecode.end() - metadataCBORSize - 2));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
}
|
||||
|
@ -6547,17 +6547,6 @@ BOOST_AUTO_TEST_CASE(calldata_offset)
|
||||
BOOST_CHECK(callContractFunction("last()", encodeArgs()) == encodeDyn(string("nd")));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(version_stamp_for_libraries)
|
||||
{
|
||||
char const* sourceCode = "library lib {}";
|
||||
m_optimize = true;
|
||||
bytes runtimeCode = compileAndRun(sourceCode, 0, "lib");
|
||||
BOOST_CHECK(runtimeCode.size() >= 8);
|
||||
BOOST_CHECK_EQUAL(runtimeCode[0], int(Instruction::PUSH6)); // might change once we switch to 1.x.x
|
||||
BOOST_CHECK_EQUAL(runtimeCode[1], 4); // might change once we switch away from x.4.x
|
||||
BOOST_CHECK_EQUAL(runtimeCode[7], int(Instruction::POP));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(contract_binary_dependencies)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
@ -7831,13 +7820,6 @@ BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call)
|
||||
u160 cAddr = m_contractAddress;
|
||||
compileAndRun(sourceCode, 0, "D");
|
||||
BOOST_CHECK(callContractFunction("f(address)", cAddr) == encodeArgs(u256(7)));
|
||||
|
||||
m_optimize = true;
|
||||
|
||||
compileAndRun(sourceCode, 0, "C");
|
||||
u160 cAddrOpt = m_contractAddress;
|
||||
compileAndRun(sourceCode, 0, "D");
|
||||
BOOST_CHECK(callContractFunction("f(address)", cAddrOpt) == encodeArgs(u256(7)));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(calling_uninitialized_function)
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
std::string sourceCode = "pragma solidity >=0.0;\n" + _sourceCode;
|
||||
m_compiler.reset(false);
|
||||
m_compiler.addSource("", sourceCode);
|
||||
if (!m_compiler.compile(m_optimize, m_optimizeRuns))
|
||||
if (!m_compiler.compile(m_optimize, m_optimizeRuns, _libraryAddresses))
|
||||
{
|
||||
for (auto const& error: m_compiler.errors())
|
||||
SourceReferenceFormatter::printExceptionInformation(
|
||||
@ -68,7 +68,6 @@ public:
|
||||
BOOST_ERROR("Compiling contract failed");
|
||||
}
|
||||
eth::LinkerObject obj = m_compiler.object(_contractName);
|
||||
obj.link(_libraryAddresses);
|
||||
BOOST_REQUIRE(obj.linkReferences.empty());
|
||||
sendMessage(obj.bytecode + _arguments, true, _value);
|
||||
return m_output;
|
||||
|
@ -50,6 +50,25 @@ class OptimizerTestFramework: public SolidityExecutionFramework
|
||||
{
|
||||
public:
|
||||
OptimizerTestFramework() { }
|
||||
|
||||
bytes const& compileAndRunWithOptimizer(
|
||||
std::string const& _sourceCode,
|
||||
u256 const& _value = 0,
|
||||
std::string const& _contractName = "",
|
||||
bool const _optimize = true,
|
||||
unsigned const _optimizeRuns = 200
|
||||
)
|
||||
{
|
||||
bool const c_optimize = m_optimize;
|
||||
unsigned const c_optimizeRuns = m_optimizeRuns;
|
||||
m_optimize = _optimize;
|
||||
m_optimizeRuns = _optimizeRuns;
|
||||
bytes const& ret = compileAndRun(_sourceCode, _value, _contractName);
|
||||
m_optimize = c_optimize;
|
||||
m_optimizeRuns = c_optimizeRuns;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Compiles the source code with and without optimizing.
|
||||
void compileBothVersions(
|
||||
std::string const& _sourceCode,
|
||||
@ -57,22 +76,16 @@ public:
|
||||
std::string const& _contractName = ""
|
||||
)
|
||||
{
|
||||
m_optimize = false;
|
||||
bytes nonOptimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
|
||||
bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false);
|
||||
m_nonOptimizedContract = m_contractAddress;
|
||||
m_optimize = true;
|
||||
bytes optimizedBytecode = compileAndRun(_sourceCode, _value, _contractName);
|
||||
size_t nonOptimizedSize = 0;
|
||||
solidity::eachInstruction(nonOptimizedBytecode, [&](Instruction, u256 const&) {
|
||||
nonOptimizedSize++;
|
||||
});
|
||||
size_t optimizedSize = 0;
|
||||
solidity::eachInstruction(optimizedBytecode, [&](Instruction, u256 const&) {
|
||||
optimizedSize++;
|
||||
});
|
||||
bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true);
|
||||
size_t nonOptimizedSize = numInstructions(nonOptimizedBytecode);
|
||||
size_t optimizedSize = numInstructions(optimizedBytecode);
|
||||
BOOST_CHECK_MESSAGE(
|
||||
nonOptimizedSize > optimizedSize,
|
||||
"Optimizer did not reduce bytecode size."
|
||||
optimizedSize < nonOptimizedSize,
|
||||
string("Optimizer did not reduce bytecode size. Non-optimized size: ") +
|
||||
std::to_string(nonOptimizedSize) + " - optimized size: " +
|
||||
std::to_string(optimizedSize)
|
||||
);
|
||||
m_optimizedContract = m_contractAddress;
|
||||
}
|
||||
@ -156,6 +169,22 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
/// @returns the number of intructions in the given bytecode, not taking the metadata hash
|
||||
/// into account.
|
||||
size_t numInstructions(bytes const& _bytecode)
|
||||
{
|
||||
BOOST_REQUIRE(_bytecode.size() > 5);
|
||||
size_t metadataSize = (_bytecode[_bytecode.size() - 2] << 8) + _bytecode[_bytecode.size() - 1];
|
||||
BOOST_REQUIRE_MESSAGE(metadataSize == 0x29, "Invalid metadata size");
|
||||
BOOST_REQUIRE(_bytecode.size() >= metadataSize + 2);
|
||||
bytes realCode = bytes(_bytecode.begin(), _bytecode.end() - metadataSize - 2);
|
||||
size_t instructions = 0;
|
||||
solidity::eachInstruction(realCode, [&](Instruction, u256 const&) {
|
||||
instructions++;
|
||||
});
|
||||
return instructions;
|
||||
}
|
||||
|
||||
Address m_optimizedContract;
|
||||
Address m_nonOptimizedContract;
|
||||
};
|
||||
@ -315,8 +344,7 @@ BOOST_AUTO_TEST_CASE(retain_information_in_branches)
|
||||
compareVersions("f(uint256,bytes32)", 8, "def");
|
||||
compareVersions("f(uint256,bytes32)", 10, "ghi");
|
||||
|
||||
m_optimize = true;
|
||||
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
|
||||
bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "c", true);
|
||||
size_t numSHA3s = 0;
|
||||
eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
|
||||
if (_instr == Instruction::SHA3)
|
||||
@ -359,8 +387,7 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions)
|
||||
compileBothVersions(sourceCode);
|
||||
compareVersions("f(uint256,bytes32)", 7, "abc");
|
||||
|
||||
m_optimize = true;
|
||||
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "test");
|
||||
bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "test", true);
|
||||
size_t numSHA3s = 0;
|
||||
eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
|
||||
if (_instr == Instruction::SHA3)
|
||||
@ -1187,9 +1214,7 @@ BOOST_AUTO_TEST_CASE(computing_constants)
|
||||
compareVersions("set()");
|
||||
compareVersions("get()");
|
||||
|
||||
m_optimize = true;
|
||||
m_optimizeRuns = 1;
|
||||
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
|
||||
bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "c", true, 1);
|
||||
bytes complicatedConstant = toBigEndian(u256("0x817416927846239487123469187231298734162934871263941234127518276"));
|
||||
unsigned occurrences = 0;
|
||||
for (auto iter = optimizedBytecode.cbegin(); iter < optimizedBytecode.cend(); ++occurrences)
|
||||
|
Loading…
Reference in New Issue
Block a user