Merge pull request #2006 from chriseth/sol_functionalGasEstimator

Functional gas estimator
This commit is contained in:
chriseth 2015-05-26 15:48:10 +02:00
commit cfaa99727a

View File

@ -23,8 +23,9 @@
#include <test/libsolidity/solidityExecutionFramework.h> #include <test/libsolidity/solidityExecutionFramework.h>
#include <libevmasm/GasMeter.h> #include <libevmasm/GasMeter.h>
#include <libevmasm/KnownState.h> #include <libevmasm/KnownState.h>
#include <libevmasm/PathGasMeter.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/StructuralGasEstimator.h> #include <libsolidity/GasEstimator.h>
#include <libsolidity/SourceReferenceFormatter.h> #include <libsolidity/SourceReferenceFormatter.h>
using namespace std; using namespace std;
@ -47,30 +48,47 @@ public:
m_compiler.setSource(_sourceCode); m_compiler.setSource(_sourceCode);
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed"); ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed");
StructuralGasEstimator estimator;
AssemblyItems const* items = m_compiler.getRuntimeAssemblyItems(""); AssemblyItems const* items = m_compiler.getRuntimeAssemblyItems("");
ASTNode const& sourceUnit = m_compiler.getAST(); ASTNode const& sourceUnit = m_compiler.getAST();
BOOST_REQUIRE(items != nullptr); BOOST_REQUIRE(items != nullptr);
m_gasCosts = estimator.breakToStatementLevel( m_gasCosts = GasEstimator::breakToStatementLevel(
estimator.performEstimation(*items, vector<ASTNode const*>({&sourceUnit})), GasEstimator::structuralEstimation(*items, vector<ASTNode const*>({&sourceUnit})),
{&sourceUnit} {&sourceUnit}
); );
} }
void testCreationTimeGas(string const& _sourceCode, string const& _contractName = "") void testCreationTimeGas(string const& _sourceCode)
{ {
compileAndRun(_sourceCode); compileAndRun(_sourceCode);
auto state = make_shared<KnownState>(); auto state = make_shared<KnownState>();
GasMeter meter(state); PathGasMeter meter(*m_compiler.getAssemblyItems());
GasMeter::GasConsumption gas; GasMeter::GasConsumption gas = meter.estimateMax(0, state);
for (AssemblyItem const& item: *m_compiler.getAssemblyItems(_contractName)) u256 bytecodeSize(m_compiler.getRuntimeBytecode().size());
gas += meter.estimateMax(item);
u256 bytecodeSize(m_compiler.getRuntimeBytecode(_contractName).size());
gas += bytecodeSize * c_createDataGas; gas += bytecodeSize * c_createDataGas;
BOOST_REQUIRE(!gas.isInfinite); BOOST_REQUIRE(!gas.isInfinite);
BOOST_CHECK(gas.value == m_gasUsed); BOOST_CHECK(gas.value == 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(string const& _sig, vector<bytes> _argumentVariants)
{
u256 gasUsed = 0;
FixedHash<4> hash(dev::sha3(_sig));
for (bytes const& arguments: _argumentVariants)
{
sendMessage(hash.asBytes() + arguments, false, 0);
gasUsed = max(gasUsed, m_gasUsed);
}
GasMeter::GasConsumption gas = GasEstimator::functionalEstimation(
*m_compiler.getRuntimeAssemblyItems(),
_sig
);
BOOST_REQUIRE(!gas.isInfinite);
BOOST_CHECK(gas.value == m_gasUsed);
}
protected: protected:
map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts; map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts;
}; };
@ -149,6 +167,67 @@ BOOST_AUTO_TEST_CASE(updating_store)
testCreationTimeGas(sourceCode); testCreationTimeGas(sourceCode);
} }
BOOST_AUTO_TEST_CASE(branches)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) {
if (x > 7)
data2 = 1;
else
data = 1;
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
}
BOOST_AUTO_TEST_CASE(function_calls)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) {
if (x > 7)
data2 = g(x**8) + 1;
else
data = 1;
}
function g(uint x) internal returns (uint) {
return data2;
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
}
BOOST_AUTO_TEST_CASE(multiple_external_functions)
{
char const* sourceCode = R"(
contract test {
uint data;
uint data2;
function f(uint x) {
if (x > 7)
data2 = g(x**8) + 1;
else
data = 1;
}
function g(uint x) returns (uint) {
return data2;
}
}
)";
testCreationTimeGas(sourceCode);
testRunTimeGas("f(uint256)", vector<bytes>{encodeArgs(2), encodeArgs(8)});
testRunTimeGas("g(uint256)", vector<bytes>{encodeArgs(2)});
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }