Merge pull request #7740 from ethereum/istanbul

Enable support for Istanbul
This commit is contained in:
chriseth 2019-11-20 10:49:27 +01:00 committed by GitHub
commit 5deaac0849
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 115 additions and 43 deletions

View File

@ -29,7 +29,7 @@ set -e
REPODIR="$(realpath $(dirname $0)/..)" REPODIR="$(realpath $(dirname $0)/..)"
for OPTIMIZE in 0 1; do for OPTIMIZE in 0 1; do
for EVM in homestead byzantium constantinople petersburg; do for EVM in homestead byzantium constantinople petersburg istanbul; do
EVM=$EVM OPTIMIZE=$OPTIMIZE ${REPODIR}/.circleci/soltest.sh EVM=$EVM OPTIMIZE=$OPTIMIZE ${REPODIR}/.circleci/soltest.sh
done done
done done

View File

@ -96,7 +96,7 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
{ {
assertThrow(_data.size() > 0, OptimizerException, "Empty bytecode generated."); assertThrow(_data.size() > 0, OptimizerException, "Empty bytecode generated.");
return bigint(GasMeter::dataGas(_data, m_params.isCreation)); return bigint(GasMeter::dataGas(_data, m_params.isCreation, m_params.evmVersion));
} }
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items) size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
@ -131,7 +131,7 @@ bigint LiteralMethod::gasNeeded() const
return combineGas( return combineGas(
simpleRunGas({Instruction::PUSH1}), simpleRunGas({Instruction::PUSH1}),
// PUSHX plus data // PUSHX plus data
(m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)), (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)),
0 0
); );
} }
@ -142,7 +142,7 @@ bigint CodeCopyMethod::gasNeeded() const
// Run gas: we ignore memory increase costs // Run gas: we ignore memory increase costs
simpleRunGas(copyRoutine()) + GasCosts::copyGas, simpleRunGas(copyRoutine()) + GasCosts::copyGas,
// Data gas for copy routines: Some bytes are zero, but we ignore them. // Data gas for copy routines: Some bytes are zero, but we ignore them.
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas), bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
// Data gas for data itself // Data gas for data itself
dataGas(toBigEndian(m_value)) dataGas(toBigEndian(m_value))
); );
@ -322,7 +322,7 @@ bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) const
return combineGas( return combineGas(
simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)), simpleRunGas(_routine) + numExps * (GasCosts::expGas + GasCosts::expByteGas(m_params.evmVersion)),
// Data gas for routine: Some bytes are zero, but we ignore them. // Data gas for routine: Some bytes are zero, but we ignore them.
bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas : GasCosts::createDataGas), bytesRequired(_routine) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
0 0
); );
} }

View File

@ -266,13 +266,13 @@ unsigned GasMeter::runGas(Instruction _instruction)
return 0; return 0;
} }
u256 GasMeter::dataGas(bytes const& _data, bool _inCreation) u256 GasMeter::dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion)
{ {
bigint gas = 0; bigint gas = 0;
if (_inCreation) if (_inCreation)
{ {
for (auto b: _data) for (auto b: _data)
gas += (b != 0) ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; gas += (b != 0) ? GasCosts::txDataNonZeroGas(_evmVersion) : GasCosts::txDataZeroGas;
} }
else else
gas = bigint(GasCosts::createDataGas) * _data.size(); gas = bigint(GasCosts::createDataGas) * _data.size();

View File

@ -53,7 +53,12 @@ namespace GasCosts
} }
inline unsigned balanceGas(langutil::EVMVersion _evmVersion) inline unsigned balanceGas(langutil::EVMVersion _evmVersion)
{ {
return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 400 : 20; if (_evmVersion >= langutil::EVMVersion::istanbul())
return 700;
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
return 400;
else
return 20;
} }
static unsigned const expGas = 10; static unsigned const expGas = 10;
inline unsigned expByteGas(langutil::EVMVersion _evmVersion) inline unsigned expByteGas(langutil::EVMVersion _evmVersion)
@ -64,7 +69,12 @@ namespace GasCosts
static unsigned const keccak256WordGas = 6; static unsigned const keccak256WordGas = 6;
inline unsigned sloadGas(langutil::EVMVersion _evmVersion) inline unsigned sloadGas(langutil::EVMVersion _evmVersion)
{ {
return _evmVersion >= langutil::EVMVersion::tangerineWhistle() ? 200 : 50; if (_evmVersion >= langutil::EVMVersion::istanbul())
return 800;
else if (_evmVersion >= langutil::EVMVersion::tangerineWhistle())
return 200;
else
return 50;
} }
static unsigned const sstoreSetGas = 20000; static unsigned const sstoreSetGas = 20000;
static unsigned const sstoreResetGas = 5000; static unsigned const sstoreResetGas = 5000;
@ -92,7 +102,10 @@ namespace GasCosts
static unsigned const txGas = 21000; static unsigned const txGas = 21000;
static unsigned const txCreateGas = 53000; static unsigned const txCreateGas = 53000;
static unsigned const txDataZeroGas = 4; static unsigned const txDataZeroGas = 4;
static unsigned const txDataNonZeroGas = 68; inline unsigned txDataNonZeroGas(langutil::EVMVersion _evmVersion)
{
return _evmVersion >= langutil::EVMVersion::istanbul() ? 16 : 68;
}
static unsigned const copyGas = 3; static unsigned const copyGas = 3;
} }
@ -139,7 +152,7 @@ public:
/// @returns the gas cost of the supplied data, depending whether it is in creation code, or not. /// @returns the gas cost of the supplied data, depending whether it is in creation code, or not.
/// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas /// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas
/// otherwise code will be stored and have to pay "createDataGas" cost. /// otherwise code will be stored and have to pay "createDataGas" cost.
static u256 dataGas(bytes const& _data, bool _inCreation); static u256 dataGas(bytes const& _data, bool _inCreation, langutil::EVMVersion _evmVersion);
private: private:
/// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise.

View File

@ -1410,7 +1410,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
if (eth::AssemblyItems const* items = assemblyItems(_contractName)) if (eth::AssemblyItems const* items = assemblyItems(_contractName))
{ {
Gas executionGas = gasEstimator.functionalEstimation(*items); Gas executionGas = gasEstimator.functionalEstimation(*items);
Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false)}; Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false, m_evmVersion)};
Json::Value creation(Json::objectValue); Json::Value creation(Json::objectValue);
creation["codeDepositCost"] = gasToJson(codeDepositGas); creation["codeDepositCost"] = gasToJson(codeDepositGas);

View File

@ -96,7 +96,7 @@ void GasMeterVisitor::operator()(Literal const& _lit)
m_runGas += dev::eth::GasMeter::runGas(dev::eth::Instruction::PUSH1); m_runGas += dev::eth::GasMeter::runGas(dev::eth::Instruction::PUSH1);
m_dataGas += m_dataGas +=
singleByteDataGas() + singleByteDataGas() +
size_t(dev::eth::GasMeter::dataGas(dev::toCompactBigEndian(valueOfLiteral(_lit), 1), m_isCreation)); size_t(dev::eth::GasMeter::dataGas(dev::toCompactBigEndian(valueOfLiteral(_lit), 1), m_isCreation, m_dialect.evmVersion()));
} }
void GasMeterVisitor::operator()(Identifier const&) void GasMeterVisitor::operator()(Identifier const&)
@ -108,7 +108,7 @@ void GasMeterVisitor::operator()(Identifier const&)
size_t GasMeterVisitor::singleByteDataGas() const size_t GasMeterVisitor::singleByteDataGas() const
{ {
if (m_isCreation) if (m_isCreation)
return dev::eth::GasCosts::txDataNonZeroGas; return dev::eth::GasCosts::txDataNonZeroGas(m_dialect.evmVersion());
else else
return dev::eth::GasCosts::createDataGas; return dev::eth::GasCosts::createDataGas;
} }

View File

@ -81,7 +81,7 @@ EVM_VERSIONS="homestead byzantium"
if [ -z "$CI" ] if [ -z "$CI" ]
then then
EVM_VERSIONS+=" constantinople petersburg" EVM_VERSIONS+=" constantinople petersburg istanbul"
fi fi
# And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer # And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer
@ -91,9 +91,9 @@ do
for vm in $EVM_VERSIONS for vm in $EVM_VERSIONS
do do
FORCE_ABIV2_RUNS="no" FORCE_ABIV2_RUNS="no"
if [[ "$vm" == "constantinople" ]] if [[ "$vm" == "istanbul" ]]
then then
FORCE_ABIV2_RUNS="no yes" # run both in constantinople FORCE_ABIV2_RUNS="no yes" # run both in istanbul
fi fi
for abiv2 in $FORCE_ABIV2_RUNS for abiv2 in $FORCE_ABIV2_RUNS
do do

View File

@ -64,7 +64,8 @@ evmc::VM* EVMHost::getVM(string const& _path)
} }
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm): EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm):
m_vm(_vm) m_vm(_vm),
m_evmVersion(_evmVersion)
{ {
if (!m_vm) if (!m_vm)
{ {
@ -73,21 +74,21 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm):
} }
if (_evmVersion == langutil::EVMVersion::homestead()) if (_evmVersion == langutil::EVMVersion::homestead())
m_evmVersion = EVMC_HOMESTEAD; m_evmRevision = EVMC_HOMESTEAD;
else if (_evmVersion == langutil::EVMVersion::tangerineWhistle()) else if (_evmVersion == langutil::EVMVersion::tangerineWhistle())
m_evmVersion = EVMC_TANGERINE_WHISTLE; m_evmRevision = EVMC_TANGERINE_WHISTLE;
else if (_evmVersion == langutil::EVMVersion::spuriousDragon()) else if (_evmVersion == langutil::EVMVersion::spuriousDragon())
m_evmVersion = EVMC_SPURIOUS_DRAGON; m_evmRevision = EVMC_SPURIOUS_DRAGON;
else if (_evmVersion == langutil::EVMVersion::byzantium()) else if (_evmVersion == langutil::EVMVersion::byzantium())
m_evmVersion = EVMC_BYZANTIUM; m_evmRevision = EVMC_BYZANTIUM;
else if (_evmVersion == langutil::EVMVersion::constantinople()) else if (_evmVersion == langutil::EVMVersion::constantinople())
m_evmVersion = EVMC_CONSTANTINOPLE; m_evmRevision = EVMC_CONSTANTINOPLE;
else if (_evmVersion == langutil::EVMVersion::istanbul()) else if (_evmVersion == langutil::EVMVersion::istanbul())
m_evmVersion = EVMC_ISTANBUL; m_evmRevision = EVMC_ISTANBUL;
else if (_evmVersion == langutil::EVMVersion::berlin()) else if (_evmVersion == langutil::EVMVersion::berlin())
assertThrow(false, Exception, "Berlin is not supported yet."); assertThrow(false, Exception, "Berlin is not supported yet.");
else //if (_evmVersion == langutil::EVMVersion::petersburg()) else //if (_evmVersion == langutil::EVMVersion::petersburg())
m_evmVersion = EVMC_PETERSBURG; m_evmRevision = EVMC_PETERSBURG;
} }
evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept evmc_storage_status EVMHost::set_storage(const evmc::address& _addr, const evmc::bytes32& _key, const evmc::bytes32& _value) noexcept
@ -146,7 +147,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
{ {
message.gas -= message.kind == EVMC_CREATE ? eth::GasCosts::txCreateGas : eth::GasCosts::txGas; message.gas -= message.kind == EVMC_CREATE ? eth::GasCosts::txCreateGas : eth::GasCosts::txGas;
for (size_t i = 0; i < message.input_size; ++i) for (size_t i = 0; i < message.input_size; ++i)
message.gas -= message.input_data[i] == 0 ? eth::GasCosts::txDataZeroGas : eth::GasCosts::txDataNonZeroGas; message.gas -= message.input_data[i] == 0 ? eth::GasCosts::txDataZeroGas : eth::GasCosts::txDataNonZeroGas(m_evmVersion);
if (message.gas < 0) if (message.gas < 0)
{ {
evmc::result result({}); evmc::result result({});
@ -191,7 +192,7 @@ evmc::result EVMHost::call(evmc_message const& _message) noexcept
evmc::address currentAddress = m_currentAddress; evmc::address currentAddress = m_currentAddress;
m_currentAddress = message.destination; m_currentAddress = message.destination;
evmc::result result = m_vm->execute(*this, m_evmVersion, message, code.data(), code.size()); evmc::result result = m_vm->execute(*this, m_evmRevision, message, code.data(), code.size());
m_currentAddress = currentAddress; m_currentAddress = currentAddress;
if (message.kind == EVMC_CREATE) if (message.kind == EVMC_CREATE)

View File

@ -180,7 +180,10 @@ private:
static evmc::result resultWithGas(evmc_message const& _message, bytes const& _data) noexcept; static evmc::result resultWithGas(evmc_message const& _message, bytes const& _data) noexcept;
evmc::VM* m_vm = nullptr; evmc::VM* m_vm = nullptr;
evmc_revision m_evmVersion; // EVM version requested by the testing tool
langutil::EVMVersion m_evmVersion;
// EVM version requested from EVMC (matches the above)
evmc_revision m_evmRevision;
}; };

View File

@ -207,7 +207,7 @@ function run_test
for optimize in "${optimizer_settings[@]}" for optimize in "${optimizer_settings[@]}"
do do
clean clean
force_solc_settings "$CONFIG" "$optimize" "petersburg" force_solc_settings "$CONFIG" "$optimize" "istanbul"
# Force ABIEncoderV2 in the last step. Has to be the last because code is modified. # Force ABIEncoderV2 in the last step. Has to be the last because code is modified.
if [ "$FORCE_ABIv2" = true ]; then if [ "$FORCE_ABIv2" = true ]; then
[[ "$optimize" =~ yul ]] && force_abi_v2 [[ "$optimize" =~ yul ]] && force_abi_v2

View File

@ -19,6 +19,7 @@
*/ */
#include <test/libsolidity/SolidityExecutionFramework.h> #include <test/libsolidity/SolidityExecutionFramework.h>
#include <liblangutil/EVMVersion.h>
#include <libdevcore/SwarmHash.h> #include <libdevcore/SwarmHash.h>
#include <libevmasm/GasMeter.h> #include <libevmasm/GasMeter.h>
@ -37,10 +38,10 @@ namespace solidity
namespace test namespace test
{ {
#define CHECK_DEPLOY_GAS(_gasNoOpt, _gasOpt) \ #define CHECK_DEPLOY_GAS(_gasNoOpt, _gasOpt, _evmVersion) \
do \ do \
{ \ { \
u256 bzzr1Cost = GasMeter::dataGas(dev::bzzr1Hash(m_compiler.metadata(m_compiler.lastContractName())).asBytes(), true); \ u256 bzzr1Cost = GasMeter::dataGas(dev::bzzr1Hash(m_compiler.metadata(m_compiler.lastContractName())).asBytes(), true, _evmVersion); \
u256 gasOpt{_gasOpt}; \ u256 gasOpt{_gasOpt}; \
u256 gasNoOpt{_gasNoOpt}; \ u256 gasNoOpt{_gasNoOpt}; \
u256 gas = m_optimiserSettings == OptimiserSettings::minimal() ? gasNoOpt : gasOpt; \ u256 gas = m_optimiserSettings == OptimiserSettings::minimal() ? gasNoOpt : gasOpt; \
@ -95,33 +96,61 @@ BOOST_AUTO_TEST_CASE(string_storage)
m_compiler.overwriteReleaseFlag(true); m_compiler.overwriteReleaseFlag(true);
compileAndRun(sourceCode); compileAndRun(sourceCode);
if (Options::get().evmVersion() <= EVMVersion::byzantium()) auto evmVersion = dev::test::Options::get().evmVersion();
CHECK_DEPLOY_GAS(134071, 130763);
if (evmVersion <= EVMVersion::byzantium())
CHECK_DEPLOY_GAS(134071, 130763, evmVersion);
// 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_DEPLOY_GAS(151455, 127653); {
// Costs with 0 are cases which cannot be triggered in tests.
if (evmVersion < EVMVersion::istanbul())
CHECK_DEPLOY_GAS(0, 127653, evmVersion);
else
CHECK_DEPLOY_GAS(0, 113821, evmVersion);
}
else else
CHECK_DEPLOY_GAS(151455, 135371); {
if (evmVersion < EVMVersion::istanbul())
CHECK_DEPLOY_GAS(0, 135371, evmVersion);
else
CHECK_DEPLOY_GAS(0, 120083, evmVersion);
}
} }
else if (evmVersion < EVMVersion::istanbul())
CHECK_DEPLOY_GAS(126861, 119591, evmVersion);
else else
CHECK_DEPLOY_GAS(126861, 119591); CHECK_DEPLOY_GAS(114173, 107163, evmVersion);
if (Options::get().evmVersion() >= EVMVersion::byzantium())
if (evmVersion >= EVMVersion::byzantium())
{ {
callContractFunction("f()"); callContractFunction("f()");
if (Options::get().evmVersion() == EVMVersion::byzantium()) if (evmVersion == EVMVersion::byzantium())
CHECK_GAS(21551, 21526, 20); CHECK_GAS(21551, 21526, 20);
// 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(21713, 21567, 20); {
if (evmVersion < EVMVersion::istanbul())
CHECK_GAS(0, 21567, 20);
else
CHECK_GAS(0, 21351, 20);
}
else else
CHECK_GAS(21713, 21635, 20); {
if (evmVersion < EVMVersion::istanbul())
CHECK_GAS(0, 21635, 20);
else
CHECK_GAS(0, 21431, 20);
}
} }
else else if (evmVersion < EVMVersion::istanbul())
CHECK_GAS(21546, 21526, 20); CHECK_GAS(21546, 21526, 20);
else
CHECK_GAS(21332, 21322, 20);
} }
} }

View File

@ -114,9 +114,10 @@ public:
static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation) static GasMeter::GasConsumption gasForTransaction(bytes const& _data, bool _isCreation)
{ {
auto evmVersion = dev::test::Options::get().evmVersion();
GasMeter::GasConsumption gas = _isCreation ? GasCosts::txCreateGas : GasCosts::txGas; GasMeter::GasConsumption gas = _isCreation ? GasCosts::txCreateGas : GasCosts::txGas;
for (auto i: _data) for (auto i: _data)
gas += i != 0 ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; gas += i != 0 ? GasCosts::txDataNonZeroGas(evmVersion) : GasCosts::txDataZeroGas;
return gas; return gas;
} }

View File

@ -0,0 +1,12 @@
contract C {
function f() public returns (uint id) {
assembly {
id := chainid()
}
}
}
// ====
// compileViaYul: also
// EVMVersion: >=istanbul
// ----
// f() -> 1

View File

@ -0,0 +1,12 @@
contract C {
function f() public payable returns (uint ret) {
assembly {
ret := selfbalance()
}
}
}
// ====
// EVMVersion: >=istanbul
// compileViaYul: also
// ----
// f(), 254 ether -> 254

View File

@ -34,7 +34,8 @@ static vector<string> s_evmVersions = {
"spuriousDragon", "spuriousDragon",
"byzantium", "byzantium",
"constantinople", "constantinople",
"petersburg" "petersburg",
"istanbul"
}; };
void FuzzerUtil::runCompiler(string const& _input, bool _quiet) void FuzzerUtil::runCompiler(string const& _input, bool _quiet)