Merge pull request #4764 from ethereum/cbor-version

Store compiler version in CBOR metadata
This commit is contained in:
chriseth 2019-05-09 10:10:50 +02:00 committed by GitHub
commit 0852ccc318
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 188 additions and 221 deletions

View File

@ -8,6 +8,7 @@ Compiler Features:
* SMTChecker: Support inherited state variables.
* SMTChecker: Support tuples and function calls with multiple return values.
* SMTChecker: Support ``delete``.
* Assembler: Encode the compiler version in the deployed bytecode.
Bugfixes:

View File

@ -37,6 +37,9 @@ function(create_build_info NAME)
-DETH_BUILD_COMPILER="${ETH_BUILD_COMPILER}"
-DETH_BUILD_PLATFORM="${ETH_BUILD_PLATFORM}"
-DPROJECT_VERSION="${PROJECT_VERSION}"
-DPROJECT_VERSION_MAJOR="${PROJECT_VERSION_MAJOR}"
-DPROJECT_VERSION_MINOR="${PROJECT_VERSION_MINOR}"
-DPROJECT_VERSION_PATCH="${PROJECT_VERSION_PATCH}"
-P "${ETH_SCRIPTS_DIR}/buildinfo.cmake"
)
include_directories("${PROJECT_BINARY_DIR}/include")

View File

@ -1,6 +1,9 @@
#pragma once
#define ETH_PROJECT_VERSION "@PROJECT_VERSION@"
#define ETH_PROJECT_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define ETH_PROJECT_VERSION_MINOR @PROJECT_VERSION_MINOR@
#define ETH_PROJECT_VERSION_PATCH @PROJECT_VERSION_PATCH@
#define SOL_COMMIT_HASH "@SOL_COMMIT_HASH@"
#define ETH_BUILD_TYPE "@ETH_BUILD_TYPE@"
#define ETH_BUILD_OS "@ETH_BUILD_OS@"

View File

@ -124,30 +124,35 @@ 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
the mapping ``{"bzzr0": <Swarm hash>, "solc": <compiler version>}`` is stored
`CBOR <https://tools.ietf.org/html/rfc7049>`_-encoded. Since the mapping might
contain more keys (see below) and 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 usually 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
0xa2
0x65 'b' 'z' 'z' 'r' '0' 0x58 0x20 <32 bytes swarm hash>
0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding>
0x00 0x32
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.
Whereas release builds of solc use a 3 byte encoding of the version as shown
above (one byte each for major, minor and patch version number), prerelease builds
will instead use a complete version string including commit hash and build date.
.. note::
The CBOR mapping can also contain other keys, so it is better to fully
decode the data instead of relying on it starting with ``0xa165``.
decode the data instead of relying on it starting with ``0xa265``.
For example, if any experimental features that affect code generation
are used, the mapping will also contain ``"experimental": true``.
Furthermore, we are planning to add the compiler version to the mapping
to ease source-verification and scanning for bugs.
.. note::
The compiler currently uses the "swarm version 0" hash of the metadata,
but this might change in the future, so do not rely on this sequence
to start with ``0xa1 0x65 'b' 'z' 'z' 'r' '0'``. We might also
to start with ``0xa2 0x65 'b' 'z' 'z' 'r' '0'``. We might also
add additional data to this CBOR structure, so the
best option is to use a proper CBOR parser.

View File

@ -1177,6 +1177,10 @@ bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimen
encoder.pushBytes("bzzr0", dev::swarmHash(_metadata).asBytes());
if (_experimentalMode)
encoder.pushBool("experimental", true);
if (m_release)
encoder.pushBytes("solc", VersionCompactBytes);
else
encoder.pushString("solc", VersionStringStrict);
return encoder.serialise();
}

View File

@ -25,6 +25,7 @@
#include <libsolidity/interface/ReadFile.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <libsolidity/interface/Version.h>
#include <liblangutil/ErrorReporter.h>
#include <liblangutil/EVMVersion.h>
@ -261,6 +262,8 @@ public:
/// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions
Json::Value gasEstimates(std::string const& _contractName) const;
/// Overwrites the release/prerelease flag. Should only be used for testing.
void overwriteReleaseFlag(bool release) { m_release = release; }
private:
/// The state per source unit. Filled gradually during parsing.
struct Source
@ -333,7 +336,7 @@ private:
std::string createMetadata(Contract const& _contract) const;
/// @returns the metadata CBOR for the given serialised metadata JSON.
static bytes createCBORMetadata(std::string const& _metadata, bool _experimentalMode);
bytes createCBORMetadata(std::string const& _metadata, bool _experimentalMode);
/// @returns the computer source mapping string.
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
@ -382,6 +385,7 @@ private:
langutil::ErrorReporter m_errorReporter;
bool m_metadataLiteralSources = false;
State m_stackState = Empty;
bool m_release = VersionIsRelease;
};
}

View File

@ -44,36 +44,10 @@ string const dev::solidity::VersionStringStrict =
(string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) +
(string(SOL_VERSION_COMMIT).empty() ? "" : "+" + string(SOL_VERSION_COMMIT));
bytes dev::solidity::binaryVersion()
{
bytes ret{0};
size_t i = 0;
auto parseDecimal = [&]()
{
size_t ret = 0;
solAssert('0' <= VersionString[i] && VersionString[i] <= '9', "");
for (; i < VersionString.size() && '0' <= VersionString[i] && VersionString[i] <= '9'; ++i)
ret = ret * 10 + (VersionString[i] - '0');
return ret;
};
ret.push_back(uint8_t(parseDecimal()));
solAssert(i < VersionString.size() && VersionString[i] == '.', "");
++i;
ret.push_back(uint8_t(parseDecimal()));
solAssert(i < VersionString.size() && VersionString[i] == '.', "");
++i;
ret.push_back(uint8_t(parseDecimal()));
solAssert(i < VersionString.size() && (VersionString[i] == '-' || VersionString[i] == '+'), "");
++i;
size_t commitpos = VersionString.find("commit.");
solAssert(commitpos != string::npos, "");
i = commitpos + 7;
solAssert(i + 7 < VersionString.size(), "");
bytes commitHash = fromHex(VersionString.substr(i, 8));
solAssert(!commitHash.empty(), "");
ret += commitHash;
solAssert(ret.size() == 1 + 3 + 4, "");
return ret;
}
bytes const dev::solidity::VersionCompactBytes = {
ETH_PROJECT_VERSION_MAJOR,
ETH_PROJECT_VERSION_MINOR,
ETH_PROJECT_VERSION_PATCH
};
bool const dev::solidity::VersionIsRelease = string(SOL_VERSION_PRERELEASE).empty();

View File

@ -33,11 +33,8 @@ namespace solidity
extern char const* VersionNumber;
extern std::string const VersionString;
extern std::string const VersionStringStrict;
/// @returns a binary form of the version string, where A.B.C-HASH is encoded such that
/// the first byte is zero, the following three bytes encode A B and C (interpreted as decimals)
/// and HASH is interpreted as 8 hex digits and encoded into the last four bytes.
bytes binaryVersion();
extern bytes const VersionCompactBytes;
extern bool const VersionIsRelease;
}
}

View File

@ -41,9 +41,9 @@ bytes onlyMetadata(bytes const& _bytecode)
size_t metadataSize = (_bytecode[size - 2] << 8) + _bytecode[size - 1];
if (size < (metadataSize + 2))
return bytes{};
// Sanity check: assume the first byte is a fixed-size CBOR array with either 1 or 2 entries
// Sanity check: assume the first byte is a fixed-size CBOR array with either 2 or 3 entries
unsigned char firstByte = _bytecode[size - metadataSize - 2];
if (firstByte != 0xa1 && firstByte != 0xa2)
if (firstByte != 0xa2 && firstByte != 0xa3)
return bytes{};
return bytes(_bytecode.end() - metadataSize - 2, _bytecode.end() - 2);
}

View File

@ -1,17 +0,0 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
}
},
"settings":
{
"outputSelection":
{
"*": { "*": ["evm.gasEstimates"] }
}
}
}

View File

@ -1 +0,0 @@
{"contracts":{"A":{"C":{"evm":{"gasEstimates":{"creation":{"codeDepositCost":"19800","executionCost":"75","totalCost":"19875"},"external":{"f()":"122"}}}}}},"sources":{"A":{"id":0}}}

View File

@ -66,20 +66,21 @@ BOOST_AUTO_TEST_CASE(string_storage)
}
}
)";
m_compiler.overwriteReleaseFlag(true);
compileAndRun(sourceCode);
if (Options::get().evmVersion() <= EVMVersion::byzantium())
CHECK_GAS(133899, 130591, 100);
CHECK_GAS(136247, 132939, 100);
// This is only correct on >=Constantinople.
else if (Options::get().useABIEncoderV2)
{
if (Options::get().optimizeYul)
CHECK_GAS(151283, 128285, 100);
CHECK_GAS(153631, 130633, 100);
else
CHECK_GAS(151283, 136003, 100);
CHECK_GAS(153631, 138351, 100);
}
else
CHECK_GAS(126689, 120159, 100);
CHECK_GAS(129037, 122500, 100);
if (Options::get().evmVersion() >= EVMVersion::byzantium())
{
callContractFunction("f()");

View File

@ -34,22 +34,6 @@ using namespace std;
namespace fs = boost::filesystem;
using namespace boost::unit_test;
namespace
{
u256 parseGasCost(string::iterator& _it, string::iterator _end)
{
if (_it == _end || !isdigit(*_it))
throw runtime_error("Invalid test expectation: expected gas cost.");
auto begin = _it;
while (_it != _end && isdigit(*_it))
++_it;
return u256(std::string(begin, _it));
}
}
GasTest::GasTest(string const& _filename)
{
ifstream file(_filename);
@ -83,42 +67,28 @@ GasTest::GasTest(string const& _filename)
void GasTest::parseExpectations(std::istream& _stream)
{
std::map<std::string, std::string>* currentKind = nullptr;
std::string line;
map<std::string, std::string>* currentKind = nullptr;
string line;
while (getline(_stream, line))
if (boost::starts_with(line, "// creation:"))
if (!boost::starts_with(line, "// "))
BOOST_THROW_EXCEPTION(runtime_error("Invalid expectation: expected \"// \"."));
else if (boost::ends_with(line, ":"))
{
auto it = line.begin() + 12;
skipWhitespace(it, line.end());
m_creationCost.executionCost = parseGasCost(it, line.end());
skipWhitespace(it, line.end());
if (*it++ != '+')
BOOST_THROW_EXCEPTION(runtime_error("Invalid expectation: expected \"+\"-"));
skipWhitespace(it, line.end());
m_creationCost.codeDepositCost = parseGasCost(it, line.end());
skipWhitespace(it, line.end());
if (*it++ != '=')
BOOST_THROW_EXCEPTION(runtime_error("Invalid expectation: expected \"+\"-"));
skipWhitespace(it, line.end());
m_creationCost.totalCost = parseGasCost(it, line.end());
string kind = line.substr(3, line.length() - 4);
boost::trim(kind);
currentKind = &m_expectations[move(kind)];
}
else if (line == "// external:")
currentKind = &m_externalFunctionCosts;
else if (line == "// internal:")
currentKind = &m_internalFunctionCosts;
else if (!currentKind)
BOOST_THROW_EXCEPTION(runtime_error("No function kind specified. Expected \"external:\" or \"internal:\"."));
BOOST_THROW_EXCEPTION(runtime_error("No function kind specified. Expected \"creation:\", \"external:\" or \"internal:\"."));
else
{
if (!boost::starts_with(line, "// "))
BOOST_THROW_EXCEPTION(runtime_error("Invalid expectation: expected \"// \"."));
auto it = line.begin() + 3;
skipWhitespace(it, line.end());
auto functionNameBegin = it;
while (it != line.end() && *it != ':')
++it;
std::string functionName(functionNameBegin, it);
string functionName(functionNameBegin, it);
if (functionName == "fallback")
functionName.clear();
expect(it, line.end(), ':');
@ -129,39 +99,32 @@ void GasTest::parseExpectations(std::istream& _stream)
}
}
void GasTest::printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const
void GasTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const
{
Json::Value estimates = compiler().gasEstimates(compiler().lastContractName());
_stream << _linePrefix
<< "creation: "
<< estimates["creation"]["executionCost"].asString()
<< " + "
<< estimates["creation"]["codeDepositCost"].asString()
<< " = "
<< estimates["creation"]["totalCost"].asString()
<< std::endl;
for (auto kind: {"external", "internal"})
if (estimates[kind])
for (auto groupIt = estimates.begin(); groupIt != estimates.end(); ++groupIt)
{
_stream << _linePrefix << groupIt.key().asString() << ":" << std::endl;
for (auto it = groupIt->begin(); it != groupIt->end(); ++it)
{
_stream << _linePrefix << kind << ":" << std::endl;
for (auto it = estimates[kind].begin(); it != estimates[kind].end(); ++it)
{
_stream << _linePrefix << " ";
if (it.key().asString().empty())
_stream << "fallback";
else
_stream << it.key().asString();
_stream << ": " << it->asString() << std::endl;
}
_stream << _linePrefix << " ";
if (it.key().asString().empty())
_stream << "fallback";
else
_stream << it.key().asString();
_stream << ": " << it->asString() << std::endl;
}
}
}
TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
{
string const versionPragma = "pragma solidity >=0.0;\n";
compiler().reset();
// Prerelease CBOR metadata varies in size due to changing version numbers and build dates.
// This leads to volatile creation cost estimates. Therefore we force the compiler to
// release mode for testing gas estimates.
compiler().overwriteReleaseFlag(true);
OptimiserSettings settings = m_optimise ? OptimiserSettings::standard() : OptimiserSettings::minimal();
if (m_optimiseYul)
{
@ -180,59 +143,42 @@ TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, b
return TestResult::FatalError;
}
Json::Value estimates = compiler().gasEstimates(compiler().lastContractName());
auto creation = estimates["creation"];
bool success =
(creation["codeDepositCost"].asString() == toString(m_creationCost.codeDepositCost)) &&
(creation["executionCost"].asString() == toString(m_creationCost.executionCost)) &&
(creation["totalCost"].asString() == toString(m_creationCost.totalCost));
auto check = [&](map<string, string> const& _a, Json::Value const& _b) {
for (auto& entry: _a)
success &= _b[entry.first].asString() == entry.second;
};
check(m_internalFunctionCosts, estimates["internal"]);
check(m_externalFunctionCosts, estimates["external"]);
if (!success)
Json::Value estimateGroups = compiler().gasEstimates(compiler().lastContractName());
if (
m_expectations.size() == estimateGroups.size() &&
boost::all(m_expectations, [&](auto const& expectations) {
auto const& estimates = estimateGroups[expectations.first];
return estimates.size() == expectations.second.size() &&
boost::all(expectations.second, [&](auto const& entry) {
return entry.second == estimates[entry.first].asString();
});
})
)
return TestResult::Success;
else
{
_stream << _linePrefix << "Expected:" << std::endl;
_stream << _linePrefix
<< " creation: "
<< toString(m_creationCost.executionCost)
<< " + "
<< toString(m_creationCost.codeDepositCost)
<< " = "
<< toString(m_creationCost.totalCost)
<< std::endl;
auto printExpected = [&](std::string const& _kind, auto const& _expectations)
for (auto const& expectations: m_expectations)
{
_stream << _linePrefix << " " << _kind << ":" << std::endl;
for (auto const& entry: _expectations)
_stream << _linePrefix << " " << expectations.first << ":" << std::endl;
for (auto const& entry: expectations.second)
_stream << _linePrefix
<< " "
<< (entry.first.empty() ? "fallback" : entry.first)
<< ": "
<< entry.second
<< std::endl;
};
if (!m_externalFunctionCosts.empty())
printExpected("external", m_externalFunctionCosts);
if (!m_internalFunctionCosts.empty())
printExpected("internal", m_internalFunctionCosts);
}
_stream << _linePrefix << "Obtained:" << std::endl;
printUpdatedExpectations(_stream, _linePrefix + " ");
return TestResult::Failure;
}
return success ? TestResult::Success : TestResult::Failure;
}
void GasTest::printSource(ostream& _stream, string const& _linePrefix, bool) const
{
std::string line;
std::istringstream input(m_source);
string line;
istringstream input(m_source);
while (getline(input, line))
_stream << _linePrefix << line << std::endl;
}

View File

@ -48,20 +48,11 @@ public:
private:
void parseExpectations(std::istream& _stream);
struct CreationCost
{
u256 executionCost{0};
u256 codeDepositCost{0};
u256 totalCost{0};
};
bool m_optimise = false;
bool m_optimiseYul = false;
size_t m_optimiseRuns = 200;
std::string m_source;
CreationCost m_creationCost;
std::map<std::string, std::string> m_externalFunctionCosts;
std::map<std::string, std::string> m_internalFunctionCosts;
std::map<std::string, std::map<std::string, std::string>> m_expectations;
};
}

View File

@ -22,6 +22,7 @@
#include <test/Metadata.h>
#include <test/Options.h>
#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/Version.h>
#include <libdevcore/SwarmHash.h>
#include <libdevcore/JSON.h>
@ -58,20 +59,29 @@ BOOST_AUTO_TEST_CASE(metadata_stamp)
function g(function(uint) external returns (uint) x) public {}
}
)";
CompilerStack compilerStack;
compilerStack.setSources({{"", std::string(sourceCode)}});
compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
std::string const& metadata = compilerStack.metadata("test");
BOOST_CHECK(dev::test::isValidMetadata(metadata));
bytes hash = dev::swarmHash(metadata).asBytes();
BOOST_REQUIRE(hash.size() == 32);
auto const cborMetadata = requireParsedCBORMetadata(bytecode);
BOOST_CHECK(cborMetadata.size() == 1);
BOOST_CHECK(cborMetadata.count("bzzr0") == 1);
BOOST_CHECK(cborMetadata.at("bzzr0") == toHex(hash));
for (auto release: std::set<bool>{true, VersionIsRelease})
{
CompilerStack compilerStack;
compilerStack.overwriteReleaseFlag(release);
compilerStack.setSources({{"", std::string(sourceCode)}});
compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
std::string const& metadata = compilerStack.metadata("test");
BOOST_CHECK(dev::test::isValidMetadata(metadata));
bytes hash = dev::swarmHash(metadata).asBytes();
BOOST_REQUIRE(hash.size() == 32);
auto const cborMetadata = requireParsedCBORMetadata(bytecode);
BOOST_CHECK(cborMetadata.size() == 2);
BOOST_CHECK(cborMetadata.count("solc") == 1);
if (release)
BOOST_CHECK(cborMetadata.at("solc") == toHex(VersionCompactBytes));
else
BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict);
BOOST_CHECK(cborMetadata.count("bzzr0") == 1);
BOOST_CHECK(cborMetadata.at("bzzr0") == toHex(hash));
}
}
BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
@ -84,22 +94,31 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
function g(function(uint) external returns (uint) x) public {}
}
)";
CompilerStack compilerStack;
compilerStack.setSources({{"", std::string(sourceCode)}});
compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
std::string const& metadata = compilerStack.metadata("test");
BOOST_CHECK(dev::test::isValidMetadata(metadata));
bytes hash = dev::swarmHash(metadata).asBytes();
BOOST_REQUIRE(hash.size() == 32);
auto const cborMetadata = requireParsedCBORMetadata(bytecode);
BOOST_CHECK(cborMetadata.size() == 2);
BOOST_CHECK(cborMetadata.count("bzzr0") == 1);
BOOST_CHECK(cborMetadata.at("bzzr0") == toHex(hash));
BOOST_CHECK(cborMetadata.count("experimental") == 1);
BOOST_CHECK(cborMetadata.at("experimental") == "true");
for(auto release: std::set<bool>{true, VersionIsRelease})
{
CompilerStack compilerStack;
compilerStack.overwriteReleaseFlag(release);
compilerStack.setSources({{"", std::string(sourceCode)}});
compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed");
bytes const& bytecode = compilerStack.runtimeObject("test").bytecode;
std::string const& metadata = compilerStack.metadata("test");
BOOST_CHECK(dev::test::isValidMetadata(metadata));
bytes hash = dev::swarmHash(metadata).asBytes();
BOOST_REQUIRE(hash.size() == 32);
auto const cborMetadata = requireParsedCBORMetadata(bytecode);
BOOST_CHECK(cborMetadata.size() == 3);
BOOST_CHECK(cborMetadata.count("solc") == 1);
if (release)
BOOST_CHECK(cborMetadata.at("solc") == toHex(VersionCompactBytes));
else
BOOST_CHECK(cborMetadata.at("solc") == VersionStringStrict);
BOOST_CHECK(cborMetadata.count("bzzr0") == 1);
BOOST_CHECK(cborMetadata.at("bzzr0") == toHex(hash));
BOOST_CHECK(cborMetadata.count("experimental") == 1);
BOOST_CHECK(cborMetadata.at("experimental") == "true");
}
}
BOOST_AUTO_TEST_CASE(metadata_relevant_sources)

View File

@ -22,6 +22,7 @@
#include <string>
#include <boost/test/unit_test.hpp>
#include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/interface/Version.h>
#include <libdevcore/JSON.h>
#include <test/Metadata.h>
@ -326,7 +327,9 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()),
"6080604052348015600f57600080fd5b50603580601d6000396000f3fe6080604052600080fdfe"
string("6080604052348015600f57600080fd5b5060") +
(VersionIsRelease ? "3e" : toHex(bytes{uint8_t(60 + VersionStringStrict.size())})) +
"80601d6000396000f3fe6080604052600080fdfe"
);
BOOST_CHECK(contract["evm"]["assembly"].isString());
BOOST_CHECK(contract["evm"]["assembly"].asString().find(
@ -338,12 +341,19 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
"tag_1:\n /* \"fileA\":0:14 contract A { } */\n pop\n dataSize(sub_0)\n dup1\n "
"dataOffset(sub_0)\n 0x00\n codecopy\n 0x00\n return\nstop\n\nsub_0: assembly {\n "
"/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n 0x00\n "
"dup1\n revert\n\n auxdata: 0xa165627a7a72305820"
"dup1\n revert\n\n auxdata: 0xa265627a7a72305820"
) == 0);
BOOST_CHECK(contract["evm"]["gasEstimates"].isObject());
BOOST_CHECK_EQUAL(contract["evm"]["gasEstimates"].size(), 1);
BOOST_CHECK(contract["evm"]["gasEstimates"]["creation"].isObject());
BOOST_CHECK_EQUAL(contract["evm"]["gasEstimates"]["creation"].size(), 3);
BOOST_CHECK(contract["evm"]["gasEstimates"]["creation"]["codeDepositCost"].isString());
BOOST_CHECK(contract["evm"]["gasEstimates"]["creation"]["executionCost"].isString());
BOOST_CHECK(contract["evm"]["gasEstimates"]["creation"]["totalCost"].isString());
BOOST_CHECK_EQUAL(
dev::jsonCompactPrint(contract["evm"]["gasEstimates"]),
"{\"creation\":{\"codeDepositCost\":\"10600\",\"executionCost\":\"66\",\"totalCost\":\"10666\"}}"
u256(contract["evm"]["gasEstimates"]["creation"]["codeDepositCost"].asString()) +
u256(contract["evm"]["gasEstimates"]["creation"]["executionCost"].asString()),
u256(contract["evm"]["gasEstimates"]["creation"]["totalCost"].asString())
);
// Lets take the top level `.code` section (the "deployer code"), that should expose most of the features of
// the assembly JSON. What we want to check here is Operation, Push, PushTag, PushSub, PushSubSize and Tag.

View File

@ -13,7 +13,10 @@ contract C {
function f8(uint[32] memory, string[] memory, uint32, address) public returns (uint[] memory, uint16[] memory) {}
}
// ----
// creation: 1160 + 1119000 = 1120160
// creation:
// codeDepositCost: 1120800
// executionCost: 1167
// totalCost: 1121967
// external:
// a(): 530
// b(uint256): infinite

View File

@ -16,7 +16,10 @@ contract C {
// optimize: true
// optimize-yul: true
// ----
// creation: 645 + 608800 = 609445
// creation:
// codeDepositCost: 610600
// executionCost: 645
// totalCost: 611245
// external:
// a(): 429
// b(uint256): 884

View File

@ -12,6 +12,9 @@ contract C {
}
}
// ----
// creation: 294 + 255000 = 255294
// creation:
// codeDepositCost: 256800
// executionCost: 300
// totalCost: 257100
// external:
// f(): 252

View File

@ -23,7 +23,10 @@ contract Large {
function g0(uint x) public payable returns (uint) { require(x > 10); }
}
// ----
// creation: 670 + 635000 = 635670
// creation:
// codeDepositCost: 636800
// executionCost: 670
// totalCost: 637470
// external:
// a(): 451
// b(uint256): 846

View File

@ -26,7 +26,10 @@ contract Large {
// optimize: true
// optimize-runs: 2
// ----
// creation: 300 + 260000 = 260300
// creation:
// codeDepositCost: 261800
// executionCost: 300
// totalCost: 262100
// external:
// a(): 398
// b(uint256): 1105

View File

@ -10,7 +10,10 @@ contract Medium {
function g0(uint x) public payable returns (uint) { require(x > 10); }
}
// ----
// creation: 294 + 251200 = 251494
// creation:
// codeDepositCost: 253000
// executionCost: 294
// totalCost: 253294
// external:
// a(): 428
// b(uint256): 846

View File

@ -13,7 +13,10 @@ contract Medium {
// optimize: true
// optimize-runs: 2
// ----
// creation: 183 + 140400 = 140583
// creation:
// codeDepositCost: 142200
// executionCost: 190
// totalCost: 142390
// external:
// a(): 398
// b(uint256): 863

View File

@ -5,7 +5,10 @@ contract Small {
function () external payable {}
}
// ----
// creation: 129 + 81800 = 81929
// creation:
// codeDepositCost: 83600
// executionCost: 135
// totalCost: 83735
// external:
// fallback: 118
// a(): 383

View File

@ -8,7 +8,10 @@ contract Small {
// optimize: true
// optimize-runs: 2
// ----
// creation: 111 + 63600 = 63711
// creation:
// codeDepositCost: 65400
// executionCost: 117
// totalCost: 65517
// external:
// fallback: 118
// a(): 376