Lefteris Karapetsas
10 years ago
43 changed files with 6455 additions and 292 deletions
@ -0,0 +1,104 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum 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. |
||||
|
|
||||
|
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file GasMeter.cpp
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2015 |
||||
|
*/ |
||||
|
|
||||
|
#include "GasMeter.h" |
||||
|
#include <libevmcore/Params.h> |
||||
|
|
||||
|
using namespace std; |
||||
|
using namespace dev; |
||||
|
using namespace dev::eth; |
||||
|
|
||||
|
GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other) |
||||
|
{ |
||||
|
isInfinite = isInfinite || _other.isInfinite; |
||||
|
if (isInfinite) |
||||
|
return *this; |
||||
|
bigint v = bigint(value) + _other.value; |
||||
|
if (v > std::numeric_limits<u256>::max()) |
||||
|
isInfinite = true; |
||||
|
else |
||||
|
value = u256(v); |
||||
|
return *this; |
||||
|
} |
||||
|
|
||||
|
GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item) |
||||
|
{ |
||||
|
switch (_item.type()) { |
||||
|
case Push: |
||||
|
case PushTag: |
||||
|
return runGas(Instruction::PUSH1); |
||||
|
case Tag: |
||||
|
return runGas(Instruction::JUMPDEST); |
||||
|
case Operation: |
||||
|
{ |
||||
|
GasConsumption gas = runGas(_item.instruction()); |
||||
|
switch (_item.instruction()) |
||||
|
{ |
||||
|
case Instruction::SSTORE: |
||||
|
// @todo logic can be improved
|
||||
|
gas += c_sstoreSetGas; |
||||
|
break; |
||||
|
case Instruction::SLOAD: |
||||
|
gas += c_sloadGas; |
||||
|
break; |
||||
|
case Instruction::MSTORE: |
||||
|
case Instruction::MSTORE8: |
||||
|
case Instruction::MLOAD: |
||||
|
case Instruction::RETURN: |
||||
|
case Instruction::SHA3: |
||||
|
case Instruction::CALLDATACOPY: |
||||
|
case Instruction::CODECOPY: |
||||
|
case Instruction::EXTCODECOPY: |
||||
|
case Instruction::LOG0: |
||||
|
case Instruction::LOG1: |
||||
|
case Instruction::LOG2: |
||||
|
case Instruction::LOG3: |
||||
|
case Instruction::LOG4: |
||||
|
case Instruction::CALL: |
||||
|
case Instruction::CALLCODE: |
||||
|
case Instruction::CREATE: |
||||
|
case Instruction::EXP: |
||||
|
// @todo logic can be improved
|
||||
|
gas = GasConsumption::infinite(); |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
return gas; |
||||
|
break; |
||||
|
} |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return GasConsumption::infinite(); |
||||
|
} |
||||
|
|
||||
|
GasMeter::GasConsumption GasMeter::runGas(Instruction _instruction) |
||||
|
{ |
||||
|
if (_instruction == Instruction::JUMPDEST) |
||||
|
return GasConsumption(1); |
||||
|
|
||||
|
int tier = instructionInfo(_instruction).gasPriceTier; |
||||
|
return tier == InvalidTier ? GasConsumption::infinite() : c_tierStepGas[tier]; |
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,67 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum 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. |
||||
|
|
||||
|
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/** @file GasMeter.cpp
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2015 |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <ostream> |
||||
|
#include <libevmasm/AssemblyItem.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace eth |
||||
|
{ |
||||
|
|
||||
|
/**
|
||||
|
* Class that helps computing the maximum gas consumption for instructions. |
||||
|
*/ |
||||
|
class GasMeter |
||||
|
{ |
||||
|
public: |
||||
|
struct GasConsumption |
||||
|
{ |
||||
|
GasConsumption(u256 _value = 0, bool _infinite = false): value(_value), isInfinite(_infinite) {} |
||||
|
static GasConsumption infinite() { return GasConsumption(0, true); } |
||||
|
|
||||
|
GasConsumption& operator+=(GasConsumption const& _otherS); |
||||
|
std::ostream& operator<<(std::ostream& _str) const; |
||||
|
|
||||
|
u256 value; |
||||
|
bool isInfinite; |
||||
|
}; |
||||
|
|
||||
|
/// Returns an upper bound on the gas consumed by the given instruction.
|
||||
|
GasConsumption estimateMax(AssemblyItem const& _item); |
||||
|
|
||||
|
private: |
||||
|
static GasConsumption runGas(Instruction _instruction); |
||||
|
}; |
||||
|
|
||||
|
inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption const& _consumption) |
||||
|
{ |
||||
|
if (_consumption.isInfinite) |
||||
|
return _str << "inf"; |
||||
|
else |
||||
|
return _str << _consumption.value; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,110 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum 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. |
||||
|
|
||||
|
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2015 |
||||
|
* Gas consumption estimator working alongside the AST. |
||||
|
*/ |
||||
|
|
||||
|
#include "StructuralGasEstimator.h" |
||||
|
#include <map> |
||||
|
#include <functional> |
||||
|
#include <libsolidity/AST.h> |
||||
|
#include <libsolidity/ASTVisitor.h> |
||||
|
|
||||
|
using namespace std; |
||||
|
using namespace dev; |
||||
|
using namespace dev::eth; |
||||
|
using namespace dev::solidity; |
||||
|
|
||||
|
StructuralGasEstimator::ASTGasConsumptionSelfAccumulated StructuralGasEstimator::performEstimation( |
||||
|
AssemblyItems const& _items, |
||||
|
vector<ASTNode const*> const& _ast |
||||
|
) |
||||
|
{ |
||||
|
solAssert(std::count(_ast.begin(), _ast.end(), nullptr) == 0, ""); |
||||
|
map<SourceLocation, GasMeter::GasConsumption> particularCosts; |
||||
|
GasMeter meter; |
||||
|
|
||||
|
for (auto const& item: _items) |
||||
|
particularCosts[item.getLocation()] += meter.estimateMax(item); |
||||
|
|
||||
|
ASTGasConsumptionSelfAccumulated gasCosts; |
||||
|
auto onNode = [&](ASTNode const& _node) |
||||
|
{ |
||||
|
gasCosts[&_node][0] = gasCosts[&_node][1] = particularCosts[_node.getLocation()]; |
||||
|
return true; |
||||
|
}; |
||||
|
auto onEdge = [&](ASTNode const& _parent, ASTNode const& _child) |
||||
|
{ |
||||
|
gasCosts[&_parent][1] += gasCosts[&_child][1]; |
||||
|
}; |
||||
|
ASTReduce folder(onNode, onEdge); |
||||
|
for (ASTNode const* ast: _ast) |
||||
|
ast->accept(folder); |
||||
|
|
||||
|
return gasCosts; |
||||
|
} |
||||
|
|
||||
|
map<ASTNode const*, GasMeter::GasConsumption> StructuralGasEstimator::breakToStatementLevel( |
||||
|
ASTGasConsumptionSelfAccumulated const& _gasCosts, |
||||
|
vector<ASTNode const*> const& _roots |
||||
|
) |
||||
|
{ |
||||
|
solAssert(std::count(_roots.begin(), _roots.end(), nullptr) == 0, ""); |
||||
|
// first pass: statementDepth[node] is the distance from the deepend statement to node
|
||||
|
// in direction of the tree root (or undefined if not possible)
|
||||
|
map<ASTNode const*, int> statementDepth; |
||||
|
auto onNodeFirstPass = [&](ASTNode const& _node) |
||||
|
{ |
||||
|
if (dynamic_cast<Statement const*>(&_node)) |
||||
|
statementDepth[&_node] = 0; |
||||
|
return true; |
||||
|
}; |
||||
|
auto onEdgeFirstPass = [&](ASTNode const& _parent, ASTNode const& _child) |
||||
|
{ |
||||
|
if (statementDepth.count(&_child)) |
||||
|
statementDepth[&_parent] = max(statementDepth[&_parent], statementDepth[&_child] + 1); |
||||
|
}; |
||||
|
ASTReduce firstPass(onNodeFirstPass, onEdgeFirstPass); |
||||
|
for (ASTNode const* node: _roots) |
||||
|
node->accept(firstPass); |
||||
|
|
||||
|
// we use the location of a node if
|
||||
|
// - its statement depth is 0 or
|
||||
|
// - its statement depth is undefined but the parent's statement depth is at least 1
|
||||
|
map<ASTNode const*, GasMeter::GasConsumption> gasCosts; |
||||
|
auto onNodeSecondPass = [&](ASTNode const& _node) |
||||
|
{ |
||||
|
return statementDepth.count(&_node); |
||||
|
}; |
||||
|
auto onEdgeSecondPass = [&](ASTNode const& _parent, ASTNode const& _child) |
||||
|
{ |
||||
|
bool useNode = false; |
||||
|
if (statementDepth.count(&_child)) |
||||
|
useNode = statementDepth[&_child] == 0; |
||||
|
else |
||||
|
useNode = statementDepth.count(&_parent) && statementDepth.at(&_parent) > 0; |
||||
|
if (useNode) |
||||
|
gasCosts[&_child] = _gasCosts.at(&_child)[1]; |
||||
|
}; |
||||
|
ASTReduce secondPass(onNodeSecondPass, onEdgeSecondPass); |
||||
|
for (ASTNode const* node: _roots) |
||||
|
node->accept(secondPass); |
||||
|
// gasCosts should only contain non-overlapping locations
|
||||
|
return gasCosts; |
||||
|
} |
@ -0,0 +1,62 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum 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. |
||||
|
|
||||
|
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2015 |
||||
|
* Gas consumption estimator working alongside the AST. |
||||
|
*/ |
||||
|
|
||||
|
#pragma once |
||||
|
|
||||
|
#include <vector> |
||||
|
#include <map> |
||||
|
#include <array> |
||||
|
#include <libsolidity/ASTForward.h> |
||||
|
#include <libevmasm/GasMeter.h> |
||||
|
#include <libevmasm/Assembly.h> |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
|
||||
|
class StructuralGasEstimator |
||||
|
{ |
||||
|
public: |
||||
|
using ASTGasConsumption = std::map<ASTNode const*, eth::GasMeter::GasConsumption>; |
||||
|
using ASTGasConsumptionSelfAccumulated = |
||||
|
std::map<ASTNode const*, std::array<eth::GasMeter::GasConsumption, 2>>; |
||||
|
|
||||
|
/// Estimates the gas consumption for every assembly item in the given assembly and stores
|
||||
|
/// it by source location.
|
||||
|
/// @returns a mapping from each AST node to a pair of its particular and syntactically accumulated gas costs.
|
||||
|
ASTGasConsumptionSelfAccumulated performEstimation( |
||||
|
eth::AssemblyItems const& _items, |
||||
|
std::vector<ASTNode const*> const& _ast |
||||
|
); |
||||
|
/// @returns a mapping from nodes with non-overlapping source locations to gas consumptions such that
|
||||
|
/// the following source locations are part of the mapping:
|
||||
|
/// 1. source locations of statements that do not contain other statements
|
||||
|
/// 2. maximal source locations that do not overlap locations coming from the first rule
|
||||
|
ASTGasConsumption breakToStatementLevel( |
||||
|
ASTGasConsumptionSelfAccumulated const& _gasCosts, |
||||
|
std::vector<ASTNode const*> const& _roots |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
} |
||||
|
} |
@ -0,0 +1,98 @@ |
|||||
|
/*
|
||||
|
This file is part of cpp-ethereum. |
||||
|
|
||||
|
cpp-ethereum 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. |
||||
|
|
||||
|
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
*/ |
||||
|
/**
|
||||
|
* @author Christian <c@ethdev.com> |
||||
|
* @date 2015 |
||||
|
* Unit tests for the gas estimator. |
||||
|
*/ |
||||
|
|
||||
|
#include <test/libsolidity/solidityExecutionFramework.h> |
||||
|
#include <libsolidity/AST.h> |
||||
|
#include <libsolidity/StructuralGasEstimator.h> |
||||
|
#include <libsolidity/SourceReferenceFormatter.h> |
||||
|
|
||||
|
using namespace std; |
||||
|
using namespace dev::eth; |
||||
|
using namespace dev::solidity; |
||||
|
|
||||
|
namespace dev |
||||
|
{ |
||||
|
namespace solidity |
||||
|
{ |
||||
|
namespace test |
||||
|
{ |
||||
|
|
||||
|
class GasMeterTestFramework: public ExecutionFramework |
||||
|
{ |
||||
|
public: |
||||
|
GasMeterTestFramework() { } |
||||
|
void compile(string const& _sourceCode) |
||||
|
{ |
||||
|
m_compiler.setSource(_sourceCode); |
||||
|
ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(), "Compiling contract failed"); |
||||
|
|
||||
|
StructuralGasEstimator estimator; |
||||
|
AssemblyItems const* items = m_compiler.getRuntimeAssemblyItems(""); |
||||
|
ASTNode const& sourceUnit = m_compiler.getAST(); |
||||
|
BOOST_REQUIRE(items != nullptr); |
||||
|
m_gasCosts = estimator.breakToStatementLevel( |
||||
|
estimator.performEstimation(*items, vector<ASTNode const*>({&sourceUnit})), |
||||
|
{&sourceUnit} |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
protected: |
||||
|
dev::solidity::CompilerStack m_compiler; |
||||
|
map<ASTNode const*, eth::GasMeter::GasConsumption> m_gasCosts; |
||||
|
}; |
||||
|
|
||||
|
BOOST_FIXTURE_TEST_SUITE(GasMeterTests, GasMeterTestFramework) |
||||
|
|
||||
|
BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) |
||||
|
{ |
||||
|
char const* sourceCode = R"( |
||||
|
contract test { |
||||
|
bytes x; |
||||
|
function f(uint a) returns (uint b) { |
||||
|
x.length = a; |
||||
|
for (; a < 200; ++a) { |
||||
|
x[a] = 9; |
||||
|
b = a * a; |
||||
|
} |
||||
|
return f(a - 1); |
||||
|
} |
||||
|
} |
||||
|
)"; |
||||
|
compile(sourceCode); |
||||
|
for (auto first = m_gasCosts.cbegin(); first != m_gasCosts.cend(); ++first) |
||||
|
{ |
||||
|
auto second = first; |
||||
|
for (++second; second != m_gasCosts.cend(); ++second) |
||||
|
if (first->first->getLocation().intersects(second->first->getLocation())) |
||||
|
{ |
||||
|
BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!"); |
||||
|
SourceReferenceFormatter::printSourceLocation(cout, first->first->getLocation(), m_compiler.getScanner()); |
||||
|
SourceReferenceFormatter::printSourceLocation(cout, second->first->getLocation(), m_compiler.getScanner()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
BOOST_AUTO_TEST_SUITE_END() |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue