solidity/test/libsolidity/GasMeter.cpp
Paul b3230b0bdc deprecate using namespace std
test: updat filereader test

deprecate namespace std

deprecate namespace std

deprecate namespace std

deprecate namespace std

deprecate namespace std

deprecate namespace std

deprecate namespace std

deprecate namespace std

deprecated std namespace

deprecated std namespace

deprecated std namespace

deprecated std namespace

deprecated std namespace

deprecated std namespace

deprecated std namespace

deprecated std namespace

deprecated std namespace

depecrate namespace std

deprecated namespace std

check ci

clean line

Co-authored-by: Nikola Matić <nikola.matic@ethereum.org>

purge line

pure line

deprecate std

deprecate std

deprecate std

deprecate std

deprecate std

deprecate

deprecate std

bye namespace
2023-09-04 10:12:07 +02:00

343 lines
10 KiB
C++

/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* @author Christian <c@ethdev.com>
* @date 2015
* Unit tests for the gas estimator.
*/
#include <test/libsolidity/SolidityExecutionFramework.h>
#include <libevmasm/GasMeter.h>
#include <libevmasm/KnownState.h>
#include <libevmasm/PathGasMeter.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/interface/GasEstimator.h>
using namespace solidity::langutil;
using namespace solidity::evmasm;
using namespace solidity::frontend;
using namespace solidity::frontend::test;
namespace solidity::frontend::test
{
class GasMeterTestFramework: public SolidityExecutionFramework
{
public:
void compile(std::string const& _sourceCode)
{
m_compiler.reset();
m_compiler.setSources({{"", "pragma solidity >=0.0;\n"
"// SPDX-License-Identifier: GPL-3.0\n" + _sourceCode}});
m_compiler.setOptimiserSettings(solidity::test::CommonOptions::get().optimize);
m_compiler.setEVMVersion(m_evmVersion);
BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed");
}
void testCreationTimeGas(std::string const& _sourceCode, u256 const& _tolerance = u256(0))
{
compileAndRun(_sourceCode);
auto state = std::make_shared<KnownState>();
PathGasMeter meter(*m_compiler.assemblyItems(m_compiler.lastContractName()), solidity::test::CommonOptions::get().evmVersion());
GasMeter::GasConsumption gas = meter.estimateMax(0, state);
u256 bytecodeSize(m_compiler.runtimeObject(m_compiler.lastContractName()).bytecode.size());
// costs for deployment
gas += bytecodeSize * GasCosts::createDataGas;
// costs for transaction
gas += gasForTransaction(m_compiler.object(m_compiler.lastContractName()).bytecode, true);
// Skip the tests when we use ABIEncoderV2.
// TODO: We should enable this again once the yul optimizer is activated.
if (solidity::test::CommonOptions::get().useABIEncoderV1)
{
BOOST_REQUIRE(!gas.isInfinite);
BOOST_CHECK_LE(m_gasUsed, gas.value);
BOOST_CHECK_LE(gas.value - _tolerance, m_gasUsed);
}
}
/// Compares the gas computed by PathGasMeter for the given signature (but unknown arguments)
/// against the actual gas usage computed by the VM on the given set of argument variants.
void testRunTimeGas(std::string const& _sig, std::vector<bytes> _argumentVariants, u256 const& _tolerance = u256(0))
{
u256 gasUsed = 0;
GasMeter::GasConsumption gas;
util::FixedHash<4> hash = util::selectorFromSignatureH32(_sig);
for (bytes const& arguments: _argumentVariants)
{
sendMessage(hash.asBytes() + arguments, false, 0);
BOOST_CHECK(m_transactionSuccessful);
gasUsed = std::max(gasUsed, m_gasUsed);
gas = std::max(gas, gasForTransaction(hash.asBytes() + arguments, false));
}
gas += GasEstimator(solidity::test::CommonOptions::get().evmVersion()).functionalEstimation(
*m_compiler.runtimeAssemblyItems(m_compiler.lastContractName()),
_sig
);
// Skip the tests when we use ABIEncoderV2.
// TODO: We should enable this again once the yul optimizer is activated.
if (solidity::test::CommonOptions::get().useABIEncoderV1)
{
BOOST_REQUIRE(!gas.isInfinite);
BOOST_CHECK_LE(m_gasUsed, gas.value);
BOOST_CHECK_LE(gas.value - _tolerance, m_gasUsed);
}
}
static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation)
{
auto evmVersion = solidity::test::CommonOptions::get().evmVersion();
GasMeter::GasConsumption gas = _isCreation ? GasCosts::txCreateGas : GasCosts::txGas;
for (auto i: _data)
gas += i != 0 ? GasCosts::txDataNonZeroGas(evmVersion) : GasCosts::txDataZeroGas;
return gas;
}
};
BOOST_FIXTURE_TEST_SUITE(GasMeterTests, GasMeterTestFramework)
BOOST_AUTO_TEST_CASE(simple_contract)
{
// Tests a simple "deploy contract" code without constructor. The actual contract is not relevant.
char const* sourceCode = R"(
contract test {
bytes32 public shaValue;
function f(uint a) public {
shaValue = keccak256(abi.encodePacked(a));
}
}
)";
testCreationTimeGas(sourceCode);
}
BOOST_AUTO_TEST_CASE(store_keccak256)
{
char const* sourceCode = R"(
contract test {
bytes32 public shaValue;
constructor() {
shaValue = keccak256(abi.encodePacked(this));
}
}
)";
testCreationTimeGas(sourceCode);
}
BOOST_AUTO_TEST_CASE(updating_store)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
constructor() {
data = 1;
data = 2;
data2 = 0;
}
}
)";
testCreationTimeGas(sourceCode, m_evmVersion < langutil::EVMVersion::constantinople() ? u256(0) : u256(9600));
}
BOOST_AUTO_TEST_CASE(branches)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) public {
if (x > 7)
data2 = 1;
else
data = 1;
}
}
)";
testCreationTimeGas(sourceCode, 1);
testRunTimeGas("f(uint256)", std::vector<bytes>{encodeArgs(2), encodeArgs(8)}, 1);
}
BOOST_AUTO_TEST_CASE(function_calls)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) public {
if (x > 7)
{ unchecked { data2 = g(x**8) + 1; } }
else
data = 1;
}
function g(uint x) internal returns (uint) {
return data2;
}
}
)";
testCreationTimeGas(sourceCode);
// In f, data2 is accessed twice, so there is a reduction of 2200 to 100 in actual costs.
// However, GasMeter always assumes cold costs.
testRunTimeGas(
"f(uint256)",
std::vector<bytes>{encodeArgs(2), encodeArgs(8)},
m_evmVersion < EVMVersion::berlin() ?
u256(0) :
u256(2100)
);
}
BOOST_AUTO_TEST_CASE(multiple_external_functions)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) public {
if (x > 7)
{ unchecked { data2 = g(x**8) + 1; } }
else
data = 1;
}
function g(uint x) public returns (uint) {
return data2;
}
}
)";
testCreationTimeGas(sourceCode);
// In f, data2 is accessed twice, so there is a reduction of 2200 to 100 in actual costs.
// However, GasMeter always assumes cold costs.
testRunTimeGas(
"f(uint256)",
std::vector<bytes>{encodeArgs(2), encodeArgs(8)},
m_evmVersion < EVMVersion::berlin() ?
u256(0) :
u256(2100)
);
testRunTimeGas("g(uint256)", std::vector<bytes>{encodeArgs(2)});
}
BOOST_AUTO_TEST_CASE(exponent_size)
{
char const* sourceCode = R"(
contract A {
function f(uint x) public returns (uint) {
unchecked { return x ** 0; }
}
function g(uint x) public returns (uint) {
unchecked { return x ** 0x100; }
}
function h(uint x) public returns (uint) {
unchecked { return x ** 0x10000; }
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("f(uint256)", std::vector<bytes>{encodeArgs(2)});
testRunTimeGas("g(uint256)", std::vector<bytes>{encodeArgs(2)});
testRunTimeGas("h(uint256)", std::vector<bytes>{encodeArgs(2)});
}
BOOST_AUTO_TEST_CASE(balance_gas)
{
char const* sourceCode = R"(
contract A {
function lookup_balance(address a) public returns (uint) {
return a.balance;
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("lookup_balance(address)", std::vector<bytes>{encodeArgs(2), encodeArgs(100)});
}
BOOST_AUTO_TEST_CASE(extcodesize_gas)
{
char const* sourceCode = R"(
contract A {
function f() public returns (uint _s) {
assembly {
_s := extcodesize(0x30)
}
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("f()", std::vector<bytes>{encodeArgs()});
}
BOOST_AUTO_TEST_CASE(regular_functions_exclude_fallback)
{
// A bug in the estimator caused the costs for a specific function
// to always include the costs for the fallback.
char const* sourceCode = R"(
contract A {
uint public x;
fallback() external { x = 2; }
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("x()", std::vector<bytes>{encodeArgs()});
}
BOOST_AUTO_TEST_CASE(complex_control_flow)
{
// This crashed the gas estimator previously (or took a very long time).
// Now we do not follow branches if they start out with lower gas costs than the ones
// we previously considered. This of course reduces accuracy.
char const* sourceCode = R"(
contract log {
function ln(int128 x) public pure returns (int128 result) {
unchecked {
int128 t = x / 256;
int128 y = 5545177;
x = t;
t = x * 16; if (t <= 1000000) { x = t; y = y - 2772588; }
t = x * 4; if (t <= 1000000) { x = t; y = y - 1386294; }
t = x * 2; if (t <= 1000000) { x = t; y = y - 693147; }
t = x + x / 2; if (t <= 1000000) { x = t; y = y - 405465; }
t = x + x / 4; if (t <= 1000000) { x = t; y = y - 223144; }
t = x + x / 8; if (t <= 1000000) { x = t; y = y - 117783; }
t = x + x / 16; if (t <= 1000000) { x = t; y = y - 60624; }
t = x + x / 32; if (t <= 1000000) { x = t; y = y - 30771; }
t = x + x / 64; if (t <= 1000000) { x = t; y = y - 15504; }
t = x + x / 128; if (t <= 1000000) { x = t; y = y - 7782; }
t = x + x / 256; if (t <= 1000000) { x = t; y = y - 3898; }
t = x + x / 512; if (t <= 1000000) { x = t; y = y - 1951; }
t = x + x / 1024; if (t <= 1000000) { x = t; y = y - 976; }
t = x + x / 2048; if (t <= 1000000) { x = t; y = y - 488; }
t = x + x / 4096; if (t <= 1000000) { x = t; y = y - 244; }
t = x + x / 8192; if (t <= 1000000) { x = t; y = y - 122; }
t = x + x / 16384; if (t <= 1000000) { x = t; y = y - 61; }
t = x + x / 32768; if (t <= 1000000) { x = t; y = y - 31; }
t = x + x / 65536; if (t <= 1000000) { y = y - 15; }
return y;
}
}
}
)";
testCreationTimeGas(sourceCode);
// max gas is used for small x
testRunTimeGas("ln(int128)", std::vector<bytes>{encodeArgs(0), encodeArgs(10), encodeArgs(105), encodeArgs(30000)});
}
BOOST_AUTO_TEST_SUITE_END()
}