mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #4764 from ethereum/cbor-version
Store compiler version in CBOR metadata
This commit is contained in:
commit
0852ccc318
@ -8,6 +8,7 @@ Compiler Features:
|
|||||||
* SMTChecker: Support inherited state variables.
|
* SMTChecker: Support inherited state variables.
|
||||||
* SMTChecker: Support tuples and function calls with multiple return values.
|
* SMTChecker: Support tuples and function calls with multiple return values.
|
||||||
* SMTChecker: Support ``delete``.
|
* SMTChecker: Support ``delete``.
|
||||||
|
* Assembler: Encode the compiler version in the deployed bytecode.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -37,6 +37,9 @@ function(create_build_info NAME)
|
|||||||
-DETH_BUILD_COMPILER="${ETH_BUILD_COMPILER}"
|
-DETH_BUILD_COMPILER="${ETH_BUILD_COMPILER}"
|
||||||
-DETH_BUILD_PLATFORM="${ETH_BUILD_PLATFORM}"
|
-DETH_BUILD_PLATFORM="${ETH_BUILD_PLATFORM}"
|
||||||
-DPROJECT_VERSION="${PROJECT_VERSION}"
|
-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"
|
-P "${ETH_SCRIPTS_DIR}/buildinfo.cmake"
|
||||||
)
|
)
|
||||||
include_directories("${PROJECT_BINARY_DIR}/include")
|
include_directories("${PROJECT_BINARY_DIR}/include")
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define ETH_PROJECT_VERSION "@PROJECT_VERSION@"
|
#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 SOL_COMMIT_HASH "@SOL_COMMIT_HASH@"
|
||||||
#define ETH_BUILD_TYPE "@ETH_BUILD_TYPE@"
|
#define ETH_BUILD_TYPE "@ETH_BUILD_TYPE@"
|
||||||
#define ETH_BUILD_OS "@ETH_BUILD_OS@"
|
#define ETH_BUILD_OS "@ETH_BUILD_OS@"
|
||||||
|
@ -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,
|
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
|
`CBOR <https://tools.ietf.org/html/rfc7049>`_-encoded. Since the mapping might
|
||||||
contain more keys (see below) and the beginning of that
|
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 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
|
encoding. The current version of the Solidity compiler usually adds the following
|
||||||
to the end of the deployed bytecode::
|
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
|
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.
|
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::
|
.. note::
|
||||||
The CBOR mapping can also contain other keys, so it is better to fully
|
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
|
For example, if any experimental features that affect code generation
|
||||||
are used, the mapping will also contain ``"experimental": true``.
|
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::
|
.. note::
|
||||||
The compiler currently uses the "swarm version 0" hash of the metadata,
|
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
|
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
|
add additional data to this CBOR structure, so the
|
||||||
best option is to use a proper CBOR parser.
|
best option is to use a proper CBOR parser.
|
||||||
|
|
||||||
|
@ -1177,6 +1177,10 @@ bytes CompilerStack::createCBORMetadata(string const& _metadata, bool _experimen
|
|||||||
encoder.pushBytes("bzzr0", dev::swarmHash(_metadata).asBytes());
|
encoder.pushBytes("bzzr0", dev::swarmHash(_metadata).asBytes());
|
||||||
if (_experimentalMode)
|
if (_experimentalMode)
|
||||||
encoder.pushBool("experimental", true);
|
encoder.pushBool("experimental", true);
|
||||||
|
if (m_release)
|
||||||
|
encoder.pushBytes("solc", VersionCompactBytes);
|
||||||
|
else
|
||||||
|
encoder.pushString("solc", VersionStringStrict);
|
||||||
return encoder.serialise();
|
return encoder.serialise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/interface/ReadFile.h>
|
#include <libsolidity/interface/ReadFile.h>
|
||||||
#include <libsolidity/interface/OptimiserSettings.h>
|
#include <libsolidity/interface/OptimiserSettings.h>
|
||||||
|
#include <libsolidity/interface/Version.h>
|
||||||
|
|
||||||
#include <liblangutil/ErrorReporter.h>
|
#include <liblangutil/ErrorReporter.h>
|
||||||
#include <liblangutil/EVMVersion.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
|
/// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions
|
||||||
Json::Value gasEstimates(std::string const& _contractName) const;
|
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:
|
private:
|
||||||
/// The state per source unit. Filled gradually during parsing.
|
/// The state per source unit. Filled gradually during parsing.
|
||||||
struct Source
|
struct Source
|
||||||
@ -333,7 +336,7 @@ private:
|
|||||||
std::string createMetadata(Contract const& _contract) const;
|
std::string createMetadata(Contract const& _contract) const;
|
||||||
|
|
||||||
/// @returns the metadata CBOR for the given serialised metadata JSON.
|
/// @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.
|
/// @returns the computer source mapping string.
|
||||||
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
|
std::string computeSourceMapping(eth::AssemblyItems const& _items) const;
|
||||||
@ -382,6 +385,7 @@ private:
|
|||||||
langutil::ErrorReporter m_errorReporter;
|
langutil::ErrorReporter m_errorReporter;
|
||||||
bool m_metadataLiteralSources = false;
|
bool m_metadataLiteralSources = false;
|
||||||
State m_stackState = Empty;
|
State m_stackState = Empty;
|
||||||
|
bool m_release = VersionIsRelease;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -44,36 +44,10 @@ string const dev::solidity::VersionStringStrict =
|
|||||||
(string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) +
|
(string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) +
|
||||||
(string(SOL_VERSION_COMMIT).empty() ? "" : "+" + string(SOL_VERSION_COMMIT));
|
(string(SOL_VERSION_COMMIT).empty() ? "" : "+" + string(SOL_VERSION_COMMIT));
|
||||||
|
|
||||||
bytes dev::solidity::binaryVersion()
|
bytes const dev::solidity::VersionCompactBytes = {
|
||||||
{
|
ETH_PROJECT_VERSION_MAJOR,
|
||||||
bytes ret{0};
|
ETH_PROJECT_VERSION_MINOR,
|
||||||
size_t i = 0;
|
ETH_PROJECT_VERSION_PATCH
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
bool const dev::solidity::VersionIsRelease = string(SOL_VERSION_PRERELEASE).empty();
|
||||||
|
@ -33,11 +33,8 @@ namespace solidity
|
|||||||
extern char const* VersionNumber;
|
extern char const* VersionNumber;
|
||||||
extern std::string const VersionString;
|
extern std::string const VersionString;
|
||||||
extern std::string const VersionStringStrict;
|
extern std::string const VersionStringStrict;
|
||||||
|
extern bytes const VersionCompactBytes;
|
||||||
/// @returns a binary form of the version string, where A.B.C-HASH is encoded such that
|
extern bool const VersionIsRelease;
|
||||||
/// 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();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,9 +41,9 @@ bytes onlyMetadata(bytes const& _bytecode)
|
|||||||
size_t metadataSize = (_bytecode[size - 2] << 8) + _bytecode[size - 1];
|
size_t metadataSize = (_bytecode[size - 2] << 8) + _bytecode[size - 1];
|
||||||
if (size < (metadataSize + 2))
|
if (size < (metadataSize + 2))
|
||||||
return bytes{};
|
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];
|
unsigned char firstByte = _bytecode[size - metadataSize - 2];
|
||||||
if (firstByte != 0xa1 && firstByte != 0xa2)
|
if (firstByte != 0xa2 && firstByte != 0xa3)
|
||||||
return bytes{};
|
return bytes{};
|
||||||
return bytes(_bytecode.end() - metadataSize - 2, _bytecode.end() - 2);
|
return bytes(_bytecode.end() - metadataSize - 2, _bytecode.end() - 2);
|
||||||
}
|
}
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"language": "Solidity",
|
|
||||||
"sources":
|
|
||||||
{
|
|
||||||
"A":
|
|
||||||
{
|
|
||||||
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"settings":
|
|
||||||
{
|
|
||||||
"outputSelection":
|
|
||||||
{
|
|
||||||
"*": { "*": ["evm.gasEstimates"] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
{"contracts":{"A":{"C":{"evm":{"gasEstimates":{"creation":{"codeDepositCost":"19800","executionCost":"75","totalCost":"19875"},"external":{"f()":"122"}}}}}},"sources":{"A":{"id":0}}}
|
|
@ -66,20 +66,21 @@ BOOST_AUTO_TEST_CASE(string_storage)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
m_compiler.overwriteReleaseFlag(true);
|
||||||
compileAndRun(sourceCode);
|
compileAndRun(sourceCode);
|
||||||
|
|
||||||
if (Options::get().evmVersion() <= EVMVersion::byzantium())
|
if (Options::get().evmVersion() <= EVMVersion::byzantium())
|
||||||
CHECK_GAS(133899, 130591, 100);
|
CHECK_GAS(136247, 132939, 100);
|
||||||
// This is only correct on >=Constantinople.
|
// This is only correct on >=Constantinople.
|
||||||
else if (Options::get().useABIEncoderV2)
|
else if (Options::get().useABIEncoderV2)
|
||||||
{
|
{
|
||||||
if (Options::get().optimizeYul)
|
if (Options::get().optimizeYul)
|
||||||
CHECK_GAS(151283, 128285, 100);
|
CHECK_GAS(153631, 130633, 100);
|
||||||
else
|
else
|
||||||
CHECK_GAS(151283, 136003, 100);
|
CHECK_GAS(153631, 138351, 100);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
CHECK_GAS(126689, 120159, 100);
|
CHECK_GAS(129037, 122500, 100);
|
||||||
if (Options::get().evmVersion() >= EVMVersion::byzantium())
|
if (Options::get().evmVersion() >= EVMVersion::byzantium())
|
||||||
{
|
{
|
||||||
callContractFunction("f()");
|
callContractFunction("f()");
|
||||||
|
@ -34,22 +34,6 @@ using namespace std;
|
|||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
using namespace boost::unit_test;
|
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)
|
GasTest::GasTest(string const& _filename)
|
||||||
{
|
{
|
||||||
ifstream file(_filename);
|
ifstream file(_filename);
|
||||||
@ -83,42 +67,28 @@ GasTest::GasTest(string const& _filename)
|
|||||||
|
|
||||||
void GasTest::parseExpectations(std::istream& _stream)
|
void GasTest::parseExpectations(std::istream& _stream)
|
||||||
{
|
{
|
||||||
std::map<std::string, std::string>* currentKind = nullptr;
|
map<std::string, std::string>* currentKind = nullptr;
|
||||||
std::string line;
|
string line;
|
||||||
|
|
||||||
while (getline(_stream, line))
|
while (getline(_stream, line))
|
||||||
if (boost::starts_with(line, "// creation:"))
|
|
||||||
{
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
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:\"."));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!boost::starts_with(line, "// "))
|
if (!boost::starts_with(line, "// "))
|
||||||
BOOST_THROW_EXCEPTION(runtime_error("Invalid expectation: expected \"// \"."));
|
BOOST_THROW_EXCEPTION(runtime_error("Invalid expectation: expected \"// \"."));
|
||||||
|
else if (boost::ends_with(line, ":"))
|
||||||
|
{
|
||||||
|
string kind = line.substr(3, line.length() - 4);
|
||||||
|
boost::trim(kind);
|
||||||
|
currentKind = &m_expectations[move(kind)];
|
||||||
|
}
|
||||||
|
else if (!currentKind)
|
||||||
|
BOOST_THROW_EXCEPTION(runtime_error("No function kind specified. Expected \"creation:\", \"external:\" or \"internal:\"."));
|
||||||
|
else
|
||||||
|
{
|
||||||
auto it = line.begin() + 3;
|
auto it = line.begin() + 3;
|
||||||
skipWhitespace(it, line.end());
|
skipWhitespace(it, line.end());
|
||||||
auto functionNameBegin = it;
|
auto functionNameBegin = it;
|
||||||
while (it != line.end() && *it != ':')
|
while (it != line.end() && *it != ':')
|
||||||
++it;
|
++it;
|
||||||
std::string functionName(functionNameBegin, it);
|
string functionName(functionNameBegin, it);
|
||||||
if (functionName == "fallback")
|
if (functionName == "fallback")
|
||||||
functionName.clear();
|
functionName.clear();
|
||||||
expect(it, line.end(), ':');
|
expect(it, line.end(), ':');
|
||||||
@ -129,23 +99,13 @@ 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());
|
Json::Value estimates = compiler().gasEstimates(compiler().lastContractName());
|
||||||
_stream << _linePrefix
|
for (auto groupIt = estimates.begin(); groupIt != estimates.end(); ++groupIt)
|
||||||
<< "creation: "
|
|
||||||
<< estimates["creation"]["executionCost"].asString()
|
|
||||||
<< " + "
|
|
||||||
<< estimates["creation"]["codeDepositCost"].asString()
|
|
||||||
<< " = "
|
|
||||||
<< estimates["creation"]["totalCost"].asString()
|
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
for (auto kind: {"external", "internal"})
|
|
||||||
if (estimates[kind])
|
|
||||||
{
|
{
|
||||||
_stream << _linePrefix << kind << ":" << std::endl;
|
_stream << _linePrefix << groupIt.key().asString() << ":" << std::endl;
|
||||||
for (auto it = estimates[kind].begin(); it != estimates[kind].end(); ++it)
|
for (auto it = groupIt->begin(); it != groupIt->end(); ++it)
|
||||||
{
|
{
|
||||||
_stream << _linePrefix << " ";
|
_stream << _linePrefix << " ";
|
||||||
if (it.key().asString().empty())
|
if (it.key().asString().empty())
|
||||||
@ -157,11 +117,14 @@ void GasTest::printUpdatedExpectations(std::ostream& _stream, std::string const&
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
|
TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
|
||||||
{
|
{
|
||||||
string const versionPragma = "pragma solidity >=0.0;\n";
|
string const versionPragma = "pragma solidity >=0.0;\n";
|
||||||
compiler().reset();
|
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();
|
OptimiserSettings settings = m_optimise ? OptimiserSettings::standard() : OptimiserSettings::minimal();
|
||||||
if (m_optimiseYul)
|
if (m_optimiseYul)
|
||||||
{
|
{
|
||||||
@ -180,59 +143,42 @@ TestCase::TestResult GasTest::run(ostream& _stream, string const& _linePrefix, b
|
|||||||
return TestResult::FatalError;
|
return TestResult::FatalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value estimates = compiler().gasEstimates(compiler().lastContractName());
|
Json::Value estimateGroups = compiler().gasEstimates(compiler().lastContractName());
|
||||||
|
if (
|
||||||
|
m_expectations.size() == estimateGroups.size() &&
|
||||||
auto creation = estimates["creation"];
|
boost::all(m_expectations, [&](auto const& expectations) {
|
||||||
bool success =
|
auto const& estimates = estimateGroups[expectations.first];
|
||||||
(creation["codeDepositCost"].asString() == toString(m_creationCost.codeDepositCost)) &&
|
return estimates.size() == expectations.second.size() &&
|
||||||
(creation["executionCost"].asString() == toString(m_creationCost.executionCost)) &&
|
boost::all(expectations.second, [&](auto const& entry) {
|
||||||
(creation["totalCost"].asString() == toString(m_creationCost.totalCost));
|
return entry.second == estimates[entry.first].asString();
|
||||||
|
});
|
||||||
auto check = [&](map<string, string> const& _a, Json::Value const& _b) {
|
})
|
||||||
for (auto& entry: _a)
|
)
|
||||||
success &= _b[entry.first].asString() == entry.second;
|
return TestResult::Success;
|
||||||
};
|
else
|
||||||
check(m_internalFunctionCosts, estimates["internal"]);
|
|
||||||
check(m_externalFunctionCosts, estimates["external"]);
|
|
||||||
|
|
||||||
if (!success)
|
|
||||||
{
|
{
|
||||||
_stream << _linePrefix << "Expected:" << std::endl;
|
_stream << _linePrefix << "Expected:" << std::endl;
|
||||||
_stream << _linePrefix
|
for (auto const& expectations: m_expectations)
|
||||||
<< " creation: "
|
|
||||||
<< toString(m_creationCost.executionCost)
|
|
||||||
<< " + "
|
|
||||||
<< toString(m_creationCost.codeDepositCost)
|
|
||||||
<< " = "
|
|
||||||
<< toString(m_creationCost.totalCost)
|
|
||||||
<< std::endl;
|
|
||||||
auto printExpected = [&](std::string const& _kind, auto const& _expectations)
|
|
||||||
{
|
{
|
||||||
_stream << _linePrefix << " " << _kind << ":" << std::endl;
|
_stream << _linePrefix << " " << expectations.first << ":" << std::endl;
|
||||||
for (auto const& entry: _expectations)
|
for (auto const& entry: expectations.second)
|
||||||
_stream << _linePrefix
|
_stream << _linePrefix
|
||||||
<< " "
|
<< " "
|
||||||
<< (entry.first.empty() ? "fallback" : entry.first)
|
<< (entry.first.empty() ? "fallback" : entry.first)
|
||||||
<< ": "
|
<< ": "
|
||||||
<< entry.second
|
<< entry.second
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
};
|
}
|
||||||
if (!m_externalFunctionCosts.empty())
|
|
||||||
printExpected("external", m_externalFunctionCosts);
|
|
||||||
if (!m_internalFunctionCosts.empty())
|
|
||||||
printExpected("internal", m_internalFunctionCosts);
|
|
||||||
_stream << _linePrefix << "Obtained:" << std::endl;
|
_stream << _linePrefix << "Obtained:" << std::endl;
|
||||||
printUpdatedExpectations(_stream, _linePrefix + " ");
|
printUpdatedExpectations(_stream, _linePrefix + " ");
|
||||||
|
return TestResult::Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
return success ? TestResult::Success : TestResult::Failure;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GasTest::printSource(ostream& _stream, string const& _linePrefix, bool) const
|
void GasTest::printSource(ostream& _stream, string const& _linePrefix, bool) const
|
||||||
{
|
{
|
||||||
std::string line;
|
string line;
|
||||||
std::istringstream input(m_source);
|
istringstream input(m_source);
|
||||||
while (getline(input, line))
|
while (getline(input, line))
|
||||||
_stream << _linePrefix << line << std::endl;
|
_stream << _linePrefix << line << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -48,20 +48,11 @@ public:
|
|||||||
private:
|
private:
|
||||||
void parseExpectations(std::istream& _stream);
|
void parseExpectations(std::istream& _stream);
|
||||||
|
|
||||||
struct CreationCost
|
|
||||||
{
|
|
||||||
u256 executionCost{0};
|
|
||||||
u256 codeDepositCost{0};
|
|
||||||
u256 totalCost{0};
|
|
||||||
};
|
|
||||||
|
|
||||||
bool m_optimise = false;
|
bool m_optimise = false;
|
||||||
bool m_optimiseYul = false;
|
bool m_optimiseYul = false;
|
||||||
size_t m_optimiseRuns = 200;
|
size_t m_optimiseRuns = 200;
|
||||||
std::string m_source;
|
std::string m_source;
|
||||||
CreationCost m_creationCost;
|
std::map<std::string, std::map<std::string, std::string>> m_expectations;
|
||||||
std::map<std::string, std::string> m_externalFunctionCosts;
|
|
||||||
std::map<std::string, std::string> m_internalFunctionCosts;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <test/Metadata.h>
|
#include <test/Metadata.h>
|
||||||
#include <test/Options.h>
|
#include <test/Options.h>
|
||||||
#include <libsolidity/interface/CompilerStack.h>
|
#include <libsolidity/interface/CompilerStack.h>
|
||||||
|
#include <libsolidity/interface/Version.h>
|
||||||
#include <libdevcore/SwarmHash.h>
|
#include <libdevcore/SwarmHash.h>
|
||||||
#include <libdevcore/JSON.h>
|
#include <libdevcore/JSON.h>
|
||||||
|
|
||||||
@ -58,7 +59,10 @@ BOOST_AUTO_TEST_CASE(metadata_stamp)
|
|||||||
function g(function(uint) external returns (uint) x) public {}
|
function g(function(uint) external returns (uint) x) public {}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
for (auto release: std::set<bool>{true, VersionIsRelease})
|
||||||
|
{
|
||||||
CompilerStack compilerStack;
|
CompilerStack compilerStack;
|
||||||
|
compilerStack.overwriteReleaseFlag(release);
|
||||||
compilerStack.setSources({{"", std::string(sourceCode)}});
|
compilerStack.setSources({{"", std::string(sourceCode)}});
|
||||||
compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
|
compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
|
||||||
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
|
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
|
||||||
@ -69,9 +73,15 @@ BOOST_AUTO_TEST_CASE(metadata_stamp)
|
|||||||
bytes hash = dev::swarmHash(metadata).asBytes();
|
bytes hash = dev::swarmHash(metadata).asBytes();
|
||||||
BOOST_REQUIRE(hash.size() == 32);
|
BOOST_REQUIRE(hash.size() == 32);
|
||||||
auto const cborMetadata = requireParsedCBORMetadata(bytecode);
|
auto const cborMetadata = requireParsedCBORMetadata(bytecode);
|
||||||
BOOST_CHECK(cborMetadata.size() == 1);
|
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.count("bzzr0") == 1);
|
||||||
BOOST_CHECK(cborMetadata.at("bzzr0") == toHex(hash));
|
BOOST_CHECK(cborMetadata.at("bzzr0") == toHex(hash));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
|
BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
|
||||||
@ -84,7 +94,10 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
|
|||||||
function g(function(uint) external returns (uint) x) public {}
|
function g(function(uint) external returns (uint) x) public {}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
for(auto release: std::set<bool>{true, VersionIsRelease})
|
||||||
|
{
|
||||||
CompilerStack compilerStack;
|
CompilerStack compilerStack;
|
||||||
|
compilerStack.overwriteReleaseFlag(release);
|
||||||
compilerStack.setSources({{"", std::string(sourceCode)}});
|
compilerStack.setSources({{"", std::string(sourceCode)}});
|
||||||
compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
|
compilerStack.setEVMVersion(dev::test::Options::get().evmVersion());
|
||||||
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
|
compilerStack.setOptimiserSettings(dev::test::Options::get().optimize);
|
||||||
@ -95,11 +108,17 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental)
|
|||||||
bytes hash = dev::swarmHash(metadata).asBytes();
|
bytes hash = dev::swarmHash(metadata).asBytes();
|
||||||
BOOST_REQUIRE(hash.size() == 32);
|
BOOST_REQUIRE(hash.size() == 32);
|
||||||
auto const cborMetadata = requireParsedCBORMetadata(bytecode);
|
auto const cborMetadata = requireParsedCBORMetadata(bytecode);
|
||||||
BOOST_CHECK(cborMetadata.size() == 2);
|
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.count("bzzr0") == 1);
|
||||||
BOOST_CHECK(cborMetadata.at("bzzr0") == toHex(hash));
|
BOOST_CHECK(cborMetadata.at("bzzr0") == toHex(hash));
|
||||||
BOOST_CHECK(cborMetadata.count("experimental") == 1);
|
BOOST_CHECK(cborMetadata.count("experimental") == 1);
|
||||||
BOOST_CHECK(cborMetadata.at("experimental") == "true");
|
BOOST_CHECK(cborMetadata.at("experimental") == "true");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(metadata_relevant_sources)
|
BOOST_AUTO_TEST_CASE(metadata_relevant_sources)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
#include <libsolidity/interface/StandardCompiler.h>
|
#include <libsolidity/interface/StandardCompiler.h>
|
||||||
|
#include <libsolidity/interface/Version.h>
|
||||||
#include <libdevcore/JSON.h>
|
#include <libdevcore/JSON.h>
|
||||||
#include <test/Metadata.h>
|
#include <test/Metadata.h>
|
||||||
|
|
||||||
@ -326,7 +327,9 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
|
|||||||
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
|
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
|
||||||
BOOST_CHECK_EQUAL(
|
BOOST_CHECK_EQUAL(
|
||||||
dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()),
|
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"].isString());
|
||||||
BOOST_CHECK(contract["evm"]["assembly"].asString().find(
|
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 "
|
"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 "
|
"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 "
|
"/* \"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);
|
) == 0);
|
||||||
BOOST_CHECK(contract["evm"]["gasEstimates"].isObject());
|
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(
|
BOOST_CHECK_EQUAL(
|
||||||
dev::jsonCompactPrint(contract["evm"]["gasEstimates"]),
|
u256(contract["evm"]["gasEstimates"]["creation"]["codeDepositCost"].asString()) +
|
||||||
"{\"creation\":{\"codeDepositCost\":\"10600\",\"executionCost\":\"66\",\"totalCost\":\"10666\"}}"
|
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
|
// 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.
|
// the assembly JSON. What we want to check here is Operation, Push, PushTag, PushSub, PushSubSize and Tag.
|
||||||
|
@ -13,7 +13,10 @@ contract C {
|
|||||||
function f8(uint[32] memory, string[] memory, uint32, address) public returns (uint[] memory, uint16[] memory) {}
|
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:
|
// external:
|
||||||
// a(): 530
|
// a(): 530
|
||||||
// b(uint256): infinite
|
// b(uint256): infinite
|
||||||
|
@ -16,7 +16,10 @@ contract C {
|
|||||||
// optimize: true
|
// optimize: true
|
||||||
// optimize-yul: true
|
// optimize-yul: true
|
||||||
// ----
|
// ----
|
||||||
// creation: 645 + 608800 = 609445
|
// creation:
|
||||||
|
// codeDepositCost: 610600
|
||||||
|
// executionCost: 645
|
||||||
|
// totalCost: 611245
|
||||||
// external:
|
// external:
|
||||||
// a(): 429
|
// a(): 429
|
||||||
// b(uint256): 884
|
// b(uint256): 884
|
||||||
|
@ -12,6 +12,9 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// creation: 294 + 255000 = 255294
|
// creation:
|
||||||
|
// codeDepositCost: 256800
|
||||||
|
// executionCost: 300
|
||||||
|
// totalCost: 257100
|
||||||
// external:
|
// external:
|
||||||
// f(): 252
|
// f(): 252
|
||||||
|
@ -23,7 +23,10 @@ contract Large {
|
|||||||
function g0(uint x) public payable returns (uint) { require(x > 10); }
|
function g0(uint x) public payable returns (uint) { require(x > 10); }
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// creation: 670 + 635000 = 635670
|
// creation:
|
||||||
|
// codeDepositCost: 636800
|
||||||
|
// executionCost: 670
|
||||||
|
// totalCost: 637470
|
||||||
// external:
|
// external:
|
||||||
// a(): 451
|
// a(): 451
|
||||||
// b(uint256): 846
|
// b(uint256): 846
|
||||||
|
@ -26,7 +26,10 @@ contract Large {
|
|||||||
// optimize: true
|
// optimize: true
|
||||||
// optimize-runs: 2
|
// optimize-runs: 2
|
||||||
// ----
|
// ----
|
||||||
// creation: 300 + 260000 = 260300
|
// creation:
|
||||||
|
// codeDepositCost: 261800
|
||||||
|
// executionCost: 300
|
||||||
|
// totalCost: 262100
|
||||||
// external:
|
// external:
|
||||||
// a(): 398
|
// a(): 398
|
||||||
// b(uint256): 1105
|
// b(uint256): 1105
|
||||||
|
@ -10,7 +10,10 @@ contract Medium {
|
|||||||
function g0(uint x) public payable returns (uint) { require(x > 10); }
|
function g0(uint x) public payable returns (uint) { require(x > 10); }
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// creation: 294 + 251200 = 251494
|
// creation:
|
||||||
|
// codeDepositCost: 253000
|
||||||
|
// executionCost: 294
|
||||||
|
// totalCost: 253294
|
||||||
// external:
|
// external:
|
||||||
// a(): 428
|
// a(): 428
|
||||||
// b(uint256): 846
|
// b(uint256): 846
|
||||||
|
@ -13,7 +13,10 @@ contract Medium {
|
|||||||
// optimize: true
|
// optimize: true
|
||||||
// optimize-runs: 2
|
// optimize-runs: 2
|
||||||
// ----
|
// ----
|
||||||
// creation: 183 + 140400 = 140583
|
// creation:
|
||||||
|
// codeDepositCost: 142200
|
||||||
|
// executionCost: 190
|
||||||
|
// totalCost: 142390
|
||||||
// external:
|
// external:
|
||||||
// a(): 398
|
// a(): 398
|
||||||
// b(uint256): 863
|
// b(uint256): 863
|
||||||
|
@ -5,7 +5,10 @@ contract Small {
|
|||||||
function () external payable {}
|
function () external payable {}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// creation: 129 + 81800 = 81929
|
// creation:
|
||||||
|
// codeDepositCost: 83600
|
||||||
|
// executionCost: 135
|
||||||
|
// totalCost: 83735
|
||||||
// external:
|
// external:
|
||||||
// fallback: 118
|
// fallback: 118
|
||||||
// a(): 383
|
// a(): 383
|
||||||
|
@ -8,7 +8,10 @@ contract Small {
|
|||||||
// optimize: true
|
// optimize: true
|
||||||
// optimize-runs: 2
|
// optimize-runs: 2
|
||||||
// ----
|
// ----
|
||||||
// creation: 111 + 63600 = 63711
|
// creation:
|
||||||
|
// codeDepositCost: 65400
|
||||||
|
// executionCost: 117
|
||||||
|
// totalCost: 65517
|
||||||
// external:
|
// external:
|
||||||
// fallback: 118
|
// fallback: 118
|
||||||
// a(): 376
|
// a(): 376
|
||||||
|
Loading…
Reference in New Issue
Block a user