mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2114 from ethereum/compilerstack-gasestimate
Move gasEstimate into CompilerStack
This commit is contained in:
commit
138c952a1a
@ -38,6 +38,7 @@
|
|||||||
#include <libsolidity/analysis/SyntaxChecker.h>
|
#include <libsolidity/analysis/SyntaxChecker.h>
|
||||||
#include <libsolidity/codegen/Compiler.h>
|
#include <libsolidity/codegen/Compiler.h>
|
||||||
#include <libsolidity/interface/InterfaceHandler.h>
|
#include <libsolidity/interface/InterfaceHandler.h>
|
||||||
|
#include <libsolidity/interface/GasEstimator.h>
|
||||||
#include <libsolidity/formal/Why3Translator.h>
|
#include <libsolidity/formal/Why3Translator.h>
|
||||||
|
|
||||||
#include <libevmasm/Exceptions.h>
|
#include <libevmasm/Exceptions.h>
|
||||||
@ -841,3 +842,88 @@ string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) con
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
|
||||||
|
{
|
||||||
|
if (_gas.isInfinite)
|
||||||
|
return Json::Value("infinite");
|
||||||
|
else
|
||||||
|
return Json::Value(toString(_gas.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
||||||
|
{
|
||||||
|
if (!assemblyItems(_contractName) && !runtimeAssemblyItems(_contractName))
|
||||||
|
return Json::Value();
|
||||||
|
|
||||||
|
using Gas = GasEstimator::GasConsumption;
|
||||||
|
Json::Value output(Json::objectValue);
|
||||||
|
|
||||||
|
if (eth::AssemblyItems const* items = assemblyItems(_contractName))
|
||||||
|
{
|
||||||
|
Gas executionGas = GasEstimator::functionalEstimation(*items);
|
||||||
|
u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
|
||||||
|
Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
|
||||||
|
|
||||||
|
Json::Value creation(Json::objectValue);
|
||||||
|
creation["codeDepositCost"] = gasToJson(codeDepositGas);
|
||||||
|
creation["executionCost"] = gasToJson(executionGas);
|
||||||
|
/// TODO: implement + overload to avoid the need of +=
|
||||||
|
executionGas += codeDepositGas;
|
||||||
|
creation["totalCost"] = gasToJson(executionGas);
|
||||||
|
output["creation"] = creation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eth::AssemblyItems const* items = runtimeAssemblyItems(_contractName))
|
||||||
|
{
|
||||||
|
/// External functions
|
||||||
|
ContractDefinition const& contract = contractDefinition(_contractName);
|
||||||
|
Json::Value externalFunctions(Json::objectValue);
|
||||||
|
for (auto it: contract.interfaceFunctions())
|
||||||
|
{
|
||||||
|
string sig = it.second->externalSignature();
|
||||||
|
externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contract.fallbackFunction())
|
||||||
|
/// This needs to be set to an invalid signature in order to trigger the fallback,
|
||||||
|
/// without the shortcut (of CALLDATSIZE == 0), and therefore to receive the upper bound.
|
||||||
|
/// An empty string ("") would work to trigger the shortcut only.
|
||||||
|
externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
|
||||||
|
|
||||||
|
if (!externalFunctions.empty())
|
||||||
|
output["external"] = externalFunctions;
|
||||||
|
|
||||||
|
/// Internal functions
|
||||||
|
Json::Value internalFunctions(Json::objectValue);
|
||||||
|
for (auto const& it: contract.definedFunctions())
|
||||||
|
{
|
||||||
|
/// Exclude externally visible functions, constructor and the fallback function
|
||||||
|
if (it->isPartOfExternalInterface() || it->isConstructor() || it->name().empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
size_t entry = functionEntryPoint(_contractName, *it);
|
||||||
|
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
|
||||||
|
if (entry > 0)
|
||||||
|
gas = GasEstimator::functionalEstimation(*items, entry, *it);
|
||||||
|
|
||||||
|
FunctionType type(*it);
|
||||||
|
string sig = it->name() + "(";
|
||||||
|
auto paramTypes = type.parameterTypes();
|
||||||
|
for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
|
||||||
|
sig += (*it)->toString() + (it + 1 == paramTypes.end() ? "" : ",");
|
||||||
|
sig += ")";
|
||||||
|
internalFunctions[sig] = gasToJson(gas);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!internalFunctions.empty())
|
||||||
|
output["internal"] = internalFunctions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
@ -173,6 +173,9 @@ public:
|
|||||||
std::string const& onChainMetadata(std::string const& _contractName) const;
|
std::string const& onChainMetadata(std::string const& _contractName) const;
|
||||||
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
|
void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; }
|
||||||
|
|
||||||
|
/// @returns a JSON representing the estimated gas usage for contract creation, internal and external functions
|
||||||
|
Json::Value gasEstimates(std::string const& _contractName) const;
|
||||||
|
|
||||||
/// @returns the previously used scanner, useful for counting lines during error reporting.
|
/// @returns the previously used scanner, useful for counting lines during error reporting.
|
||||||
Scanner const& scanner(std::string const& _sourceName = "") const;
|
Scanner const& scanner(std::string const& _sourceName = "") const;
|
||||||
/// @returns the parsed source unit with the supplied name.
|
/// @returns the parsed source unit with the supplied name.
|
||||||
|
@ -315,49 +315,40 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co
|
|||||||
|
|
||||||
void CommandLineInterface::handleGasEstimation(string const& _contract)
|
void CommandLineInterface::handleGasEstimation(string const& _contract)
|
||||||
{
|
{
|
||||||
using Gas = GasEstimator::GasConsumption;
|
Json::Value estimates = m_compiler->gasEstimates(_contract);
|
||||||
if (!m_compiler->assemblyItems(_contract) && !m_compiler->runtimeAssemblyItems(_contract))
|
|
||||||
return;
|
|
||||||
cout << "Gas estimation:" << endl;
|
cout << "Gas estimation:" << endl;
|
||||||
if (eth::AssemblyItems const* items = m_compiler->assemblyItems(_contract))
|
|
||||||
|
if (estimates["creation"].isObject())
|
||||||
{
|
{
|
||||||
Gas gas = GasEstimator::functionalEstimation(*items);
|
Json::Value creation = estimates["creation"];
|
||||||
u256 bytecodeSize(m_compiler->runtimeObject(_contract).bytecode.size());
|
|
||||||
cout << "construction:" << endl;
|
cout << "construction:" << endl;
|
||||||
cout << " " << gas << " + " << (bytecodeSize * eth::GasCosts::createDataGas) << " = ";
|
cout << " " << creation["executionCost"].asString();
|
||||||
gas += bytecodeSize * eth::GasCosts::createDataGas;
|
cout << " + " << creation["codeDepositCost"].asString();
|
||||||
cout << gas << endl;
|
cout << " = " << creation["totalCost"].asString() << endl;
|
||||||
}
|
}
|
||||||
if (eth::AssemblyItems const* items = m_compiler->runtimeAssemblyItems(_contract))
|
|
||||||
|
if (estimates["external"].isObject())
|
||||||
{
|
{
|
||||||
ContractDefinition const& contract = m_compiler->contractDefinition(_contract);
|
Json::Value externalFunctions = estimates["external"];
|
||||||
cout << "external:" << endl;
|
cout << "external:" << endl;
|
||||||
for (auto it: contract.interfaceFunctions())
|
for (auto const& name: externalFunctions.getMemberNames())
|
||||||
{
|
{
|
||||||
string sig = it.second->externalSignature();
|
if (name.empty())
|
||||||
GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, sig);
|
cout << " fallback:\t";
|
||||||
cout << " " << sig << ":\t" << gas << endl;
|
else
|
||||||
}
|
cout << " " << name << ":\t";
|
||||||
if (contract.fallbackFunction())
|
cout << externalFunctions[name].asString() << endl;
|
||||||
{
|
|
||||||
GasEstimator::GasConsumption gas = GasEstimator::functionalEstimation(*items, "INVALID");
|
|
||||||
cout << " fallback:\t" << gas << endl;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (estimates["internal"].isObject())
|
||||||
|
{
|
||||||
|
Json::Value internalFunctions = estimates["internal"];
|
||||||
cout << "internal:" << endl;
|
cout << "internal:" << endl;
|
||||||
for (auto const& it: contract.definedFunctions())
|
for (auto const& name: internalFunctions.getMemberNames())
|
||||||
{
|
{
|
||||||
if (it->isPartOfExternalInterface() || it->isConstructor())
|
cout << " " << name << ":\t";
|
||||||
continue;
|
cout << internalFunctions[name].asString() << endl;
|
||||||
size_t entry = m_compiler->functionEntryPoint(_contract, *it);
|
|
||||||
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
|
|
||||||
if (entry > 0)
|
|
||||||
gas = GasEstimator::functionalEstimation(*items, entry, *it);
|
|
||||||
FunctionType type(*it);
|
|
||||||
cout << " " << it->name() << "(";
|
|
||||||
auto paramTypes = type.parameterTypes();
|
|
||||||
for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
|
|
||||||
cout << (*it)->toString() << (it + 1 == paramTypes.end() ? "" : ",");
|
|
||||||
cout << "):\t" << gas << endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,61 +58,45 @@ Json::Value functionHashes(ContractDefinition const& _contract)
|
|||||||
return functionHashes;
|
return functionHashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value gasToJson(GasEstimator::GasConsumption const& _gas)
|
/// Translates a gas value as a string to a JSON number or null
|
||||||
|
Json::Value gasToJson(Json::Value const& _value)
|
||||||
{
|
{
|
||||||
if (_gas.isInfinite || _gas.value > std::numeric_limits<Json::LargestUInt>::max())
|
if (_value.isObject())
|
||||||
|
{
|
||||||
|
Json::Value ret = Json::objectValue;
|
||||||
|
for (auto const& sig: _value.getMemberNames())
|
||||||
|
ret[sig] = gasToJson(_value[sig]);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_value == "infinite")
|
||||||
|
return Json::Value(Json::nullValue);
|
||||||
|
|
||||||
|
u256 value(_value.asString());
|
||||||
|
if (value > std::numeric_limits<Json::LargestUInt>::max())
|
||||||
return Json::Value(Json::nullValue);
|
return Json::Value(Json::nullValue);
|
||||||
else
|
else
|
||||||
return Json::Value(Json::LargestUInt(_gas.value));
|
return Json::Value(Json::LargestUInt(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
|
Json::Value estimateGas(CompilerStack const& _compiler, string const& _contract)
|
||||||
{
|
{
|
||||||
Json::Value gasEstimates(Json::objectValue);
|
Json::Value estimates = _compiler.gasEstimates(_contract);
|
||||||
using Gas = GasEstimator::GasConsumption;
|
Json::Value output(Json::objectValue);
|
||||||
if (!_compiler.assemblyItems(_contract) && !_compiler.runtimeAssemblyItems(_contract))
|
|
||||||
return gasEstimates;
|
if (estimates["creation"].isObject())
|
||||||
if (eth::AssemblyItems const* items = _compiler.assemblyItems(_contract))
|
|
||||||
{
|
{
|
||||||
Gas gas = GasEstimator::functionalEstimation(*items);
|
Json::Value creation(Json::arrayValue);
|
||||||
u256 bytecodeSize(_compiler.runtimeObject(_contract).bytecode.size());
|
creation[0] = gasToJson(estimates["creation"]["executionCost"]);
|
||||||
Json::Value creationGas(Json::arrayValue);
|
creation[1] = gasToJson(estimates["creation"]["codeDepositCost"]);
|
||||||
creationGas[0] = gasToJson(gas);
|
output["creation"] = creation;
|
||||||
creationGas[1] = gasToJson(bytecodeSize * eth::GasCosts::createDataGas);
|
|
||||||
gasEstimates["creation"] = creationGas;
|
|
||||||
}
|
}
|
||||||
if (eth::AssemblyItems const* items = _compiler.runtimeAssemblyItems(_contract))
|
else
|
||||||
{
|
output["creation"] = Json::objectValue;
|
||||||
ContractDefinition const& contract = _compiler.contractDefinition(_contract);
|
output["external"] = gasToJson(estimates.get("external", Json::objectValue));
|
||||||
Json::Value externalFunctions(Json::objectValue);
|
output["internal"] = gasToJson(estimates.get("internal", Json::objectValue));
|
||||||
for (auto it: contract.interfaceFunctions())
|
|
||||||
{
|
return output;
|
||||||
string sig = it.second->externalSignature();
|
|
||||||
externalFunctions[sig] = gasToJson(GasEstimator::functionalEstimation(*items, sig));
|
|
||||||
}
|
|
||||||
if (contract.fallbackFunction())
|
|
||||||
externalFunctions[""] = gasToJson(GasEstimator::functionalEstimation(*items, "INVALID"));
|
|
||||||
gasEstimates["external"] = externalFunctions;
|
|
||||||
Json::Value internalFunctions(Json::objectValue);
|
|
||||||
for (auto const& it: contract.definedFunctions())
|
|
||||||
{
|
|
||||||
if (it->isPartOfExternalInterface() || it->isConstructor())
|
|
||||||
continue;
|
|
||||||
size_t entry = _compiler.functionEntryPoint(_contract, *it);
|
|
||||||
GasEstimator::GasConsumption gas = GasEstimator::GasConsumption::infinite();
|
|
||||||
if (entry > 0)
|
|
||||||
gas = GasEstimator::functionalEstimation(*items, entry, *it);
|
|
||||||
FunctionType type(*it);
|
|
||||||
string sig = it->name() + "(";
|
|
||||||
auto paramTypes = type.parameterTypes();
|
|
||||||
for (auto it = paramTypes.begin(); it != paramTypes.end(); ++it)
|
|
||||||
sig += (*it)->toString() + (it + 1 == paramTypes.end() ? "" : ",");
|
|
||||||
sig += ")";
|
|
||||||
internalFunctions[sig] = gasToJson(gas);
|
|
||||||
}
|
|
||||||
gasEstimates["internal"] = internalFunctions;
|
|
||||||
}
|
|
||||||
return gasEstimates;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback _readCallback)
|
string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback _readCallback)
|
||||||
|
Loading…
Reference in New Issue
Block a user