diff --git a/CMakeLists.txt b/CMakeLists.txt index 4eddd53a436..363151e78a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,8 @@ include_directories("${CMAKE_SOURCE_DIR}") option(ALETH_INTERPRETER_SHARED "Build aleth-interpreter as a shared library" OFF) add_subdirectory(libaleth-interpreter) +add_subdirectory(libaleth-precompiles) + add_subdirectory(libdevcore) add_subdirectory(libdevcrypto) add_subdirectory(libp2p) diff --git a/libaleth-precompiles/CMakeLists.txt b/libaleth-precompiles/CMakeLists.txt new file mode 100644 index 00000000000..7fffc55b3a4 --- /dev/null +++ b/libaleth-precompiles/CMakeLists.txt @@ -0,0 +1,21 @@ +# Aleth: Ethereum C++ client, tools and libraries. +# Copyright 2018-2019 Aleth Authors. +# Licensed under the GNU General Public License, Version 3. See the LICENSE file. + +set( + sources + PrecompilesVM.h + PrecompilesVM.cpp +) + +set( + dependencies + aleth-buildinfo + evmc::evmc + Boost::boost + devcore + devcrypto +) + +add_library(aleth-precompiles STATIC ${sources}) +target_link_libraries(aleth-precompiles PRIVATE ${dependencies}) diff --git a/libaleth-precompiles/PrecompilesVM.cpp b/libaleth-precompiles/PrecompilesVM.cpp new file mode 100644 index 00000000000..7d93d211177 --- /dev/null +++ b/libaleth-precompiles/PrecompilesVM.cpp @@ -0,0 +1,309 @@ +// Aleth: Ethereum C++ client, tools and libraries. +// Copyright 2018-2019 Aleth Authors. +// Licensed under the GNU General Public License, Version 3. + +#include "PrecompilesVM.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace dev +{ +namespace +{ +using Pricer = bigint (*)(bytesConstRef, evmc_revision); +using Executor = std::pair (*)(bytesConstRef); + +bigint linearPrice(unsigned _base, unsigned _word, bytesConstRef _in) +{ + bigint const s = _in.size(); + bigint const b = _base; + bigint const w = _word; + return b + (s + 31) / 32 * w; +} + +bigint ecrecoverPrice(bytesConstRef, evmc_revision) +{ + return 3000; +} + +std::pair ecrecover(bytesConstRef _in) +{ + struct + { + h256 hash; + h256 v; + h256 r; + h256 s; + } in; + + memcpy(&in, _in.data(), std::min(_in.size(), sizeof(in))); + + h256 ret; + u256 v = (u256)in.v; + if (v >= 27 && v <= 28) + { + SignatureStruct sig(in.r, in.s, (byte)((int)v - 27)); + if (sig.isValid()) + { + try + { + if (Public rec = recover(sig, in.hash)) + { + ret = dev::sha3(rec); + memset(ret.data(), 0, 12); + return {true, ret.asBytes()}; + } + } + catch (...) + { + } + } + } + return {true, {}}; +} + +bigint sha256Price(bytesConstRef _in, evmc_revision) +{ + return linearPrice(60, 12, _in); +} + +std::pair sha256(bytesConstRef _in) +{ + return {true, dev::sha256(_in).asBytes()}; +} + +bigint ripemd160Price(bytesConstRef _in, evmc_revision) +{ + return linearPrice(600, 120, _in); +} + +std::pair ripemd160(bytesConstRef _in) +{ + return {true, h256(dev::ripemd160(_in), h256::AlignRight).asBytes()}; +} + +bigint identityPrice(bytesConstRef _in, evmc_revision) +{ + return linearPrice(15, 3, _in); +} + +std::pair identity(bytesConstRef _in) +{ + return {true, _in.toBytes()}; +} + +// Parse _count bytes of _in starting with _begin offset as big endian int. +// If there's not enough bytes in _in, consider it infinitely right-padded with zeroes. +bigint parseBigEndianRightPadded(bytesConstRef _in, bigint const& _begin, bigint const& _count) +{ + if (_begin > _in.count()) + return 0; + assert(_count <= std::numeric_limits::max() / 8); // Otherwise, the return value would + // not fit in the memory. + + size_t const begin{_begin}; + size_t const count{_count}; + + // crop _in, not going beyond its size + bytesConstRef cropped = _in.cropped(begin, std::min(count, _in.count() - begin)); + + bigint ret = fromBigEndian(cropped); + // shift as if we had right-padding zeroes + assert(count - cropped.count() <= std::numeric_limits::max() / 8); + ret <<= 8 * (count - cropped.count()); + + return ret; +} + +bigint expLengthAdjust(bigint const& _expOffset, bigint const& _expLength, bytesConstRef _in) +{ + if (_expLength <= 32) + { + bigint const exp(parseBigEndianRightPadded(_in, _expOffset, _expLength)); + return exp ? msb(exp) : 0; + } + else + { + bigint const expFirstWord(parseBigEndianRightPadded(_in, _expOffset, 32)); + size_t const highestBit(expFirstWord ? msb(expFirstWord) : 0); + return 8 * (_expLength - 32) + highestBit; + } +} + +bigint multComplexity(bigint const& _x) +{ + if (_x <= 64) + return _x * _x; + if (_x <= 1024) + return (_x * _x) / 4 + 96 * _x - 3072; + else + return (_x * _x) / 16 + 480 * _x - 199680; +} + +bigint modexpPrice(bytesConstRef _in, evmc_revision) +{ + bigint const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + bigint const expLength(parseBigEndianRightPadded(_in, 32, 32)); + bigint const modLength(parseBigEndianRightPadded(_in, 64, 32)); + + bigint const maxLength(max(modLength, baseLength)); + bigint const adjustedExpLength(expLengthAdjust(baseLength + 96, expLength, _in)); + + return multComplexity(maxLength) * std::max(adjustedExpLength, 1) / 20; +} + +std::pair modexp(bytesConstRef _in) +{ + bigint const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + bigint const expLength(parseBigEndianRightPadded(_in, 32, 32)); + bigint const modLength(parseBigEndianRightPadded(_in, 64, 32)); + assert(modLength <= std::numeric_limits::max() / 8); // Otherwise gas should be too + // expensive. + assert(baseLength <= std::numeric_limits::max() / 8); // Otherwise, gas should be too + // expensive. + if (modLength == 0 && baseLength == 0) + return {true, bytes{}}; // This is a special case where expLength can be very big. + assert(expLength <= std::numeric_limits::max() / 8); + + bigint const base(parseBigEndianRightPadded(_in, 96, baseLength)); + bigint const exp(parseBigEndianRightPadded(_in, 96 + baseLength, expLength)); + bigint const mod(parseBigEndianRightPadded(_in, 96 + baseLength + expLength, modLength)); + + bigint const result = mod != 0 ? boost::multiprecision::powm(base, exp, mod) : bigint{0}; + + size_t const retLength(modLength); + bytes ret(retLength); + toBigEndian(result, ret); + + return {true, ret}; +} + +bigint alt_bn128_G1_addPrice(bytesConstRef, evmc_revision _rev) +{ + return _rev < EVMC_ISTANBUL ? 500 : 150; +} + +std::pair alt_bn128_G1_add(bytesConstRef _in) +{ + return dev::crypto::alt_bn128_G1_add(_in); +} + +bigint alt_bn128_G1_mulPrice(bytesConstRef, evmc_revision _rev) +{ + return _rev < EVMC_ISTANBUL ? 40000 : 6000; +} + +std::pair alt_bn128_G1_mul(bytesConstRef _in) +{ + return dev::crypto::alt_bn128_G1_mul(_in); +} + +bigint alt_bn128_pairing_productPrice(bytesConstRef _in, evmc_revision _rev) +{ + auto const k = _in.size() / 192; + return _rev < EVMC_ISTANBUL ? 100000 + k * 80000 : 45000 + k * 34000; +} + +std::pair alt_bn128_pairing_product(bytesConstRef _in) +{ + return dev::crypto::alt_bn128_pairing_product(_in); +} + +bigint blake2CompressionPrice(bytesConstRef _in, evmc_revision) +{ + auto const rounds = fromBigEndian(_in.cropped(0, 4)); + return rounds; +} + +std::pair blake2Compression(bytesConstRef _in) +{ + static constexpr size_t roundsSize = 4; + static constexpr size_t stateVectorSize = 8 * 8; + static constexpr size_t messageBlockSize = 16 * 8; + static constexpr size_t offsetCounterSize = 8; + static constexpr size_t finalBlockIndicatorSize = 1; + static constexpr size_t totalInputSize = roundsSize + stateVectorSize + messageBlockSize + + 2 * offsetCounterSize + finalBlockIndicatorSize; + + if (_in.size() != totalInputSize) + return {false, {}}; + + auto const rounds = fromBigEndian(_in.cropped(0, roundsSize)); + auto const stateVector = _in.cropped(roundsSize, stateVectorSize); + auto const messageBlockVector = _in.cropped(roundsSize + stateVectorSize, messageBlockSize); + auto const offsetCounter0 = + _in.cropped(roundsSize + stateVectorSize + messageBlockSize, offsetCounterSize); + auto const offsetCounter1 = _in.cropped( + roundsSize + stateVectorSize + messageBlockSize + offsetCounterSize, offsetCounterSize); + uint8_t const finalBlockIndicator = + _in[roundsSize + stateVectorSize + messageBlockSize + 2 * offsetCounterSize]; + + if (finalBlockIndicator != 0 && finalBlockIndicator != 1) + return {false, {}}; + + return {true, dev::crypto::blake2FCompression(rounds, stateVector, offsetCounter0, + offsetCounter1, finalBlockIndicator, messageBlockVector)}; +} + +evmc_result execute(evmc_vm*, const evmc_host_interface*, evmc_host_context*, + enum evmc_revision _rev, const evmc_message* _msg, const uint8_t*, size_t) noexcept +{ + static constexpr std::pair c_precompiles[] = {{ecrecoverPrice, ecrecover}, + {sha256Price, sha256}, {ripemd160Price, ripemd160}, {identityPrice, identity}, + {modexpPrice, modexp}, {alt_bn128_G1_addPrice, alt_bn128_G1_add}, + {alt_bn128_G1_mulPrice, alt_bn128_G1_mul}, + {alt_bn128_pairing_productPrice, alt_bn128_pairing_product}, + {blake2CompressionPrice, blake2Compression}}; + static constexpr size_t c_precompilesSize = sizeof(c_precompiles) / sizeof(c_precompiles[0]); + static_assert(c_precompilesSize < 0xFF, "Assuming no more than 256 precompiles"); + static constexpr evmc::address c_maxPrecompileAddress = + evmc_address{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, c_precompilesSize}}; + + // check that address is within the range of defined precompiles + auto const destination = evmc::address{_msg->destination}; + if (destination == evmc::address{} || c_maxPrecompileAddress < destination) + return evmc::make_result(EVMC_REJECTED, 0, nullptr, 0); + + // convert address to array index + auto const precompileAddressLSB = _msg->destination.bytes[sizeof(_msg->destination) - 1]; + auto const precompile = c_precompiles[precompileAddressLSB - 1]; + + bytesConstRef input{_msg->input_data, _msg->input_size}; + + // Check the gas cost. + auto const gasCost = precompile.first(input, _rev); + auto const gasLeft = _msg->gas - gasCost; + if (gasLeft < 0) + return evmc::make_result(EVMC_OUT_OF_GAS, 0, nullptr, 0); + + // Execute. + auto const res = precompile.second(input); + if (!res.first) + return evmc::make_result(EVMC_PRECOMPILE_FAILURE, 0, nullptr, 0); + + // Return the result. + return evmc::make_result( + EVMC_SUCCESS, static_cast(gasLeft), res.second.data(), res.second.size()); +} +} // namespace +} // namespace dev + +evmc_vm* evmc_create_aleth_precompiles_vm() noexcept +{ + static evmc_vm vm = { + EVMC_ABI_VERSION, + "precompiles_vm", + aleth_version, + [](evmc_vm*) {}, + dev::execute, + [](evmc_vm*) { return evmc_capabilities_flagset{EVMC_CAPABILITY_PRECOMPILES}; }, + nullptr, + }; + return &vm; +} diff --git a/libaleth-precompiles/PrecompilesVM.h b/libaleth-precompiles/PrecompilesVM.h new file mode 100644 index 00000000000..4e287e1c94f --- /dev/null +++ b/libaleth-precompiles/PrecompilesVM.h @@ -0,0 +1,17 @@ +// Aleth: Ethereum C++ client, tools and libraries. +// Copyright 2018-2019 Aleth Authors. +// Licensed under the GNU General Public License, Version 3. +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +EVMC_EXPORT struct evmc_vm* evmc_create_aleth_precompiles_vm() EVMC_NOEXCEPT; + +#ifdef __cplusplus +} +#endif diff --git a/libethcore/ChainOperationParams.cpp b/libethcore/ChainOperationParams.cpp index 560714ac99b..ed2c0b0e1ff 100644 --- a/libethcore/ChainOperationParams.cpp +++ b/libethcore/ChainOperationParams.cpp @@ -11,13 +11,6 @@ using namespace std; using namespace dev; using namespace eth; -PrecompiledContract::PrecompiledContract( - std::string const& _name, u256 const& _startingBlock /*= 0*/) - : m_cost(PrecompiledRegistrar::pricer(_name)), - m_execute(PrecompiledRegistrar::executor(_name)), - m_startingBlock(_startingBlock) -{} - ChainOperationParams::ChainOperationParams(): m_blockReward("0x4563918244F40000"), minGasLimit(0x1388), diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index 9c8a8182cae..40dd91b9690 100644 --- a/libethcore/ChainOperationParams.h +++ b/libethcore/ChainOperationParams.h @@ -7,7 +7,6 @@ #include #include -#include #include "Common.h" @@ -17,26 +16,6 @@ namespace eth { struct EVMSchedule; -class PrecompiledContract -{ -public: - PrecompiledContract(std::string const& _name, u256 const& _startingBlock = 0); - - bigint cost( - bytesConstRef _in, ChainOperationParams const& _chainParams, u256 const& _blockNumber) const - { - return m_cost(_in, _chainParams, _blockNumber); - } - std::pair execute(bytesConstRef _in) const { return m_execute(_in); } - - u256 const& startingBlock() const { return m_startingBlock; } - -private: - PrecompiledPricer m_cost; - PrecompiledExecutor m_execute; - u256 m_startingBlock = 0; -}; - constexpr int64_t c_infiniteBlockNumber = std::numeric_limits::max(); struct AdditionalEIPs @@ -106,8 +85,8 @@ struct ChainOperationParams u256 durationLimit; bool allowFutureBlocks = false; - /// Precompiled contracts as specified in the chain params. - std::unordered_map precompiled; + /// Precompiled contracts' starting blocks + std::unordered_map precompiledStartingBlocks; EVMSchedule lastForkWithAdditionalEIPsSchedule; }; diff --git a/libethcore/Precompiled.cpp b/libethcore/Precompiled.cpp deleted file mode 100644 index a30260d729d..00000000000 --- a/libethcore/Precompiled.cpp +++ /dev/null @@ -1,274 +0,0 @@ -// Aleth: Ethereum C++ client, tools and libraries. -// Copyright 2014-2019 Aleth Authors. -// Licensed under the GNU General Public License, Version 3. - - -#include "Precompiled.h" -#include "ChainOperationParams.h" -#include -#include -#include -#include -#include -#include -#include -using namespace std; -using namespace dev; -using namespace dev::eth; - -PrecompiledRegistrar* PrecompiledRegistrar::s_this = nullptr; - -PrecompiledExecutor const& PrecompiledRegistrar::executor(std::string const& _name) -{ - if (!get()->m_execs.count(_name)) - BOOST_THROW_EXCEPTION(ExecutorNotFound()); - return get()->m_execs[_name]; -} - -PrecompiledPricer const& PrecompiledRegistrar::pricer(std::string const& _name) -{ - if (!get()->m_pricers.count(_name)) - BOOST_THROW_EXCEPTION(PricerNotFound()); - return get()->m_pricers[_name]; -} - -namespace -{ -bigint linearPricer(unsigned _base, unsigned _word, bytesConstRef _in) -{ - bigint const s = _in.size(); - bigint const b = _base; - bigint const w = _word; - return b + (s + 31) / 32 * w; -} - -ETH_REGISTER_PRECOMPILED_PRICER(ecrecover) -(bytesConstRef /*_in*/, ChainOperationParams const& /*_chainParams*/, u256 const& /*_blockNumber*/) -{ - return 3000; -} - -ETH_REGISTER_PRECOMPILED(ecrecover)(bytesConstRef _in) -{ - struct - { - h256 hash; - h256 v; - h256 r; - h256 s; - } in; - - memcpy(&in, _in.data(), min(_in.size(), sizeof(in))); - - h256 ret; - u256 v = (u256)in.v; - if (v >= 27 && v <= 28) - { - SignatureStruct sig(in.r, in.s, (byte)((int)v - 27)); - if (sig.isValid()) - { - try - { - if (Public rec = recover(sig, in.hash)) - { - ret = dev::sha3(rec); - memset(ret.data(), 0, 12); - return {true, ret.asBytes()}; - } - } - catch (...) {} - } - } - return {true, {}}; -} - -ETH_REGISTER_PRECOMPILED_PRICER(sha256) -(bytesConstRef _in, ChainOperationParams const& /*_chainParams*/, u256 const& /*_blockNumber*/) -{ - return linearPricer(60, 12, _in); -} - -ETH_REGISTER_PRECOMPILED(sha256)(bytesConstRef _in) -{ - return {true, dev::sha256(_in).asBytes()}; -} - -ETH_REGISTER_PRECOMPILED_PRICER(ripemd160) -(bytesConstRef _in, ChainOperationParams const& /*_chainParams*/, u256 const& /*_blockNumber*/) -{ - return linearPricer(600, 120, _in); -} - -ETH_REGISTER_PRECOMPILED(ripemd160)(bytesConstRef _in) -{ - return {true, h256(dev::ripemd160(_in), h256::AlignRight).asBytes()}; -} - -ETH_REGISTER_PRECOMPILED_PRICER(identity) -(bytesConstRef _in, ChainOperationParams const& /*_chainParams*/, u256 const& /*_blockNumber*/) -{ - return linearPricer(15, 3, _in); -} - -ETH_REGISTER_PRECOMPILED(identity)(bytesConstRef _in) -{ - return {true, _in.toBytes()}; -} - -// Parse _count bytes of _in starting with _begin offset as big endian int. -// If there's not enough bytes in _in, consider it infinitely right-padded with zeroes. -bigint parseBigEndianRightPadded(bytesConstRef _in, bigint const& _begin, bigint const& _count) -{ - if (_begin > _in.count()) - return 0; - assert(_count <= numeric_limits::max() / 8); // Otherwise, the return value would not fit in the memory. - - size_t const begin{_begin}; - size_t const count{_count}; - - // crop _in, not going beyond its size - bytesConstRef cropped = _in.cropped(begin, min(count, _in.count() - begin)); - - bigint ret = fromBigEndian(cropped); - // shift as if we had right-padding zeroes - assert(count - cropped.count() <= numeric_limits::max() / 8); - ret <<= 8 * (count - cropped.count()); - - return ret; -} - -ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) -{ - bigint const baseLength(parseBigEndianRightPadded(_in, 0, 32)); - bigint const expLength(parseBigEndianRightPadded(_in, 32, 32)); - bigint const modLength(parseBigEndianRightPadded(_in, 64, 32)); - assert(modLength <= numeric_limits::max() / 8); // Otherwise gas should be too expensive. - assert(baseLength <= numeric_limits::max() / 8); // Otherwise, gas should be too expensive. - if (modLength == 0 && baseLength == 0) - return {true, bytes{}}; // This is a special case where expLength can be very big. - assert(expLength <= numeric_limits::max() / 8); - - bigint const base(parseBigEndianRightPadded(_in, 96, baseLength)); - bigint const exp(parseBigEndianRightPadded(_in, 96 + baseLength, expLength)); - bigint const mod(parseBigEndianRightPadded(_in, 96 + baseLength + expLength, modLength)); - - bigint const result = mod != 0 ? boost::multiprecision::powm(base, exp, mod) : bigint{0}; - - size_t const retLength(modLength); - bytes ret(retLength); - toBigEndian(result, ret); - - return {true, ret}; -} - -namespace -{ - bigint expLengthAdjust(bigint const& _expOffset, bigint const& _expLength, bytesConstRef _in) - { - if (_expLength <= 32) - { - bigint const exp(parseBigEndianRightPadded(_in, _expOffset, _expLength)); - return exp ? msb(exp) : 0; - } - else - { - bigint const expFirstWord(parseBigEndianRightPadded(_in, _expOffset, 32)); - size_t const highestBit(expFirstWord ? msb(expFirstWord) : 0); - return 8 * (_expLength - 32) + highestBit; - } - } - - bigint multComplexity(bigint const& _x) - { - if (_x <= 64) - return _x * _x; - if (_x <= 1024) - return (_x * _x) / 4 + 96 * _x - 3072; - else - return (_x * _x) / 16 + 480 * _x - 199680; - } -} - -ETH_REGISTER_PRECOMPILED_PRICER(modexp)(bytesConstRef _in, ChainOperationParams const&, u256 const&) -{ - bigint const baseLength(parseBigEndianRightPadded(_in, 0, 32)); - bigint const expLength(parseBigEndianRightPadded(_in, 32, 32)); - bigint const modLength(parseBigEndianRightPadded(_in, 64, 32)); - - bigint const maxLength(max(modLength, baseLength)); - bigint const adjustedExpLength(expLengthAdjust(baseLength + 96, expLength, _in)); - - return multComplexity(maxLength) * max(adjustedExpLength, 1) / 20; -} - -ETH_REGISTER_PRECOMPILED(alt_bn128_G1_add)(bytesConstRef _in) -{ - return dev::crypto::alt_bn128_G1_add(_in); -} - -ETH_REGISTER_PRECOMPILED_PRICER(alt_bn128_G1_add) -(bytesConstRef /*_in*/, ChainOperationParams const& _chainParams, u256 const& _blockNumber) -{ - return _blockNumber < _chainParams.istanbulForkBlock ? 500 : 150; -} - -ETH_REGISTER_PRECOMPILED(alt_bn128_G1_mul)(bytesConstRef _in) -{ - return dev::crypto::alt_bn128_G1_mul(_in); -} - -ETH_REGISTER_PRECOMPILED_PRICER(alt_bn128_G1_mul) -(bytesConstRef /*_in*/, ChainOperationParams const& _chainParams, u256 const& _blockNumber) -{ - return _blockNumber < _chainParams.istanbulForkBlock ? 40000 : 6000; -} - -ETH_REGISTER_PRECOMPILED(alt_bn128_pairing_product)(bytesConstRef _in) -{ - return dev::crypto::alt_bn128_pairing_product(_in); -} - -ETH_REGISTER_PRECOMPILED_PRICER(alt_bn128_pairing_product) -(bytesConstRef _in, ChainOperationParams const& _chainParams, u256 const& _blockNumber) -{ - auto const k = _in.size() / 192; - return _blockNumber < _chainParams.istanbulForkBlock ? 100000 + k * 80000 : 45000 + k * 34000; -} - -ETH_REGISTER_PRECOMPILED(blake2_compression)(bytesConstRef _in) -{ - static constexpr size_t roundsSize = 4; - static constexpr size_t stateVectorSize = 8 * 8; - static constexpr size_t messageBlockSize = 16 * 8; - static constexpr size_t offsetCounterSize = 8; - static constexpr size_t finalBlockIndicatorSize = 1; - static constexpr size_t totalInputSize = roundsSize + stateVectorSize + messageBlockSize + - 2 * offsetCounterSize + finalBlockIndicatorSize; - - if (_in.size() != totalInputSize) - return {false, {}}; - - auto const rounds = fromBigEndian(_in.cropped(0, roundsSize)); - auto const stateVector = _in.cropped(roundsSize, stateVectorSize); - auto const messageBlockVector = _in.cropped(roundsSize + stateVectorSize, messageBlockSize); - auto const offsetCounter0 = - _in.cropped(roundsSize + stateVectorSize + messageBlockSize, offsetCounterSize); - auto const offsetCounter1 = _in.cropped( - roundsSize + stateVectorSize + messageBlockSize + offsetCounterSize, offsetCounterSize); - uint8_t const finalBlockIndicator = - _in[roundsSize + stateVectorSize + messageBlockSize + 2 * offsetCounterSize]; - - if (finalBlockIndicator != 0 && finalBlockIndicator != 1) - return {false, {}}; - - return {true, dev::crypto::blake2FCompression(rounds, stateVector, offsetCounter0, - offsetCounter1, finalBlockIndicator, messageBlockVector)}; -} - -ETH_REGISTER_PRECOMPILED_PRICER(blake2_compression) -(bytesConstRef _in, ChainOperationParams const&, u256 const&) -{ - auto const rounds = fromBigEndian(_in.cropped(0, 4)); - return rounds; -} -} diff --git a/libethcore/Precompiled.h b/libethcore/Precompiled.h deleted file mode 100644 index b057683bf81..00000000000 --- a/libethcore/Precompiled.h +++ /dev/null @@ -1,63 +0,0 @@ -// Aleth: Ethereum C++ client, tools and libraries. -// Copyright 2015-2019 Aleth Authors. -// Licensed under the GNU General Public License, Version 3. - - -#pragma once - -#include -#include -#include -#include - -namespace dev -{ -namespace eth -{ -struct ChainOperationParams; - -using PrecompiledExecutor = std::function(bytesConstRef _in)>; -using PrecompiledPricer = std::function; - -DEV_SIMPLE_EXCEPTION(ExecutorNotFound); -DEV_SIMPLE_EXCEPTION(PricerNotFound); - -class PrecompiledRegistrar -{ -public: - /// Get the executor object for @a _name function or @throw ExecutorNotFound if not found. - static PrecompiledExecutor const& executor(std::string const& _name); - - /// Get the price calculator object for @a _name function or @throw PricerNotFound if not found. - static PrecompiledPricer const& pricer(std::string const& _name); - - /// Register an executor. In general just use ETH_REGISTER_PRECOMPILED. - static PrecompiledExecutor registerExecutor(std::string const& _name, PrecompiledExecutor const& _exec) { return (get()->m_execs[_name] = _exec); } - /// Unregister an executor. Shouldn't generally be necessary. - static void unregisterExecutor(std::string const& _name) { get()->m_execs.erase(_name); } - - /// Register a pricer. In general just use ETH_REGISTER_PRECOMPILED_PRICER. - static PrecompiledPricer registerPricer(std::string const& _name, PrecompiledPricer const& _exec) { return (get()->m_pricers[_name] = _exec); } - /// Unregister a pricer. Shouldn't generally be necessary. - static void unregisterPricer(std::string const& _name) { get()->m_pricers.erase(_name); } - -private: - static PrecompiledRegistrar* get() { if (!s_this) s_this = new PrecompiledRegistrar; return s_this; } - - std::unordered_map m_execs; - std::unordered_map m_pricers; - static PrecompiledRegistrar* s_this; -}; - -// TODO: unregister on unload with a static object. -#define ETH_REGISTER_PRECOMPILED(Name) static std::pair __eth_registerPrecompiledFunction ## Name(bytesConstRef _in); static PrecompiledExecutor __eth_registerPrecompiledFactory ## Name = ::dev::eth::PrecompiledRegistrar::registerExecutor(#Name, &__eth_registerPrecompiledFunction ## Name); static std::pair __eth_registerPrecompiledFunction ## Name -#define ETH_REGISTER_PRECOMPILED_PRICER(Name) \ - static bigint __eth_registerPricerFunction##Name( \ - bytesConstRef _in, ChainOperationParams const& _chainParams, u256 const& _blockNumber); \ - static PrecompiledPricer __eth_registerPricerFactory##Name = \ - ::dev::eth::PrecompiledRegistrar::registerPricer( \ - #Name, &__eth_registerPricerFunction##Name); \ - static bigint __eth_registerPricerFunction##Name -} -} diff --git a/libethcore/SealEngine.h b/libethcore/SealEngine.h index d0c4c0d7a9e..ce4df06306c 100644 --- a/libethcore/SealEngine.h +++ b/libethcore/SealEngine.h @@ -67,14 +67,9 @@ class SealEngineFace virtual bool isPrecompiled(Address const& _a, u256 const& _blockNumber) const { - return m_params.precompiled.count(_a) != 0 && _blockNumber >= m_params.precompiled.at(_a).startingBlock(); + return m_params.precompiledStartingBlocks.count(_a) != 0 && + _blockNumber >= m_params.precompiledStartingBlocks.at(_a); } - virtual bigint costOfPrecompiled( - Address const& _a, bytesConstRef _in, u256 const& _blockNumber) const - { - return m_params.precompiled.at(_a).cost(_in, m_params, _blockNumber); - } - virtual std::pair executePrecompiled(Address const& _a, bytesConstRef _in, u256 const&) const { return m_params.precompiled.at(_a).execute(_in); } protected: virtual bool onOptionChanging(std::string const&, bytes const&) { return true; } diff --git a/libethereum/CMakeLists.txt b/libethereum/CMakeLists.txt index 28e214768d5..659deff167e 100644 --- a/libethereum/CMakeLists.txt +++ b/libethereum/CMakeLists.txt @@ -3,4 +3,4 @@ file(GLOB sources "*.cpp" "*.h") add_library(ethereum ${sources}) target_include_directories(ethereum PRIVATE "${UTILS_INCLUDE_DIR}") -target_link_libraries(ethereum PUBLIC evm ethcore p2p devcrypto devcore PRIVATE jsoncpp_lib_static Snappy::snappy Boost::fiber Boost::context) +target_link_libraries(ethereum PUBLIC evm ethcore p2p devcrypto devcore aleth-precompiles PRIVATE jsoncpp_lib_static Snappy::snappy Boost::fiber Boost::context) diff --git a/libethereum/ChainParams.cpp b/libethereum/ChainParams.cpp index c4382412d7d..e2cb87e7d02 100644 --- a/libethereum/ChainParams.cpp +++ b/libethereum/ChainParams.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -48,10 +47,10 @@ ChainParams::ChainParams() for (unsigned i = 1; i <= 4; ++i) genesisState[Address(i)] = Account(0, 1); // Setup default precompiled contracts as equal to genesis of Frontier. - precompiled.insert(make_pair(Address(1), PrecompiledContract("ecrecover"))); - precompiled.insert(make_pair(Address(2), PrecompiledContract("sha256"))); - precompiled.insert(make_pair(Address(3), PrecompiledContract("ripemd160"))); - precompiled.insert(make_pair(Address(4), PrecompiledContract("identity"))); + precompiledStartingBlocks.insert(make_pair(Address(1), 0)); + precompiledStartingBlocks.insert(make_pair(Address(2), 0)); + precompiledStartingBlocks.insert(make_pair(Address(3), 0)); + precompiledStartingBlocks.insert(make_pair(Address(4), 0)); } ChainParams::ChainParams( @@ -152,17 +151,15 @@ void ChainParams::loadConfig( genesisState = jsonToAccountMap(genesisStateStr, accountStartNonce, nullptr, _configPath); } - precompiled.insert({Address{0x1}, PrecompiledContract{"ecrecover"}}); - precompiled.insert({Address{0x2}, PrecompiledContract{"sha256"}}); - precompiled.insert({Address{0x3}, PrecompiledContract{"ripemd160"}}); - precompiled.insert({Address{0x4}, PrecompiledContract{"identity"}}); - precompiled.insert({Address{0x5}, PrecompiledContract{"modexp", byzantiumForkBlock}}); - precompiled.insert({Address{0x6}, PrecompiledContract{"alt_bn128_G1_add", byzantiumForkBlock}}); - precompiled.insert({Address{0x7}, PrecompiledContract{"alt_bn128_G1_mul", byzantiumForkBlock}}); - precompiled.insert( - {Address{0x8}, PrecompiledContract{"alt_bn128_pairing_product", byzantiumForkBlock}}); - precompiled.insert( - {Address{0x9}, PrecompiledContract{"blake2_compression", istanbulForkBlock}}); + precompiledStartingBlocks.insert({Address{0x1}, 0}); + precompiledStartingBlocks.insert({Address{0x2}, 0}); + precompiledStartingBlocks.insert({Address{0x3}, 0}); + precompiledStartingBlocks.insert({Address{0x4}, 0}); + precompiledStartingBlocks.insert({Address{0x5}, byzantiumForkBlock}); + precompiledStartingBlocks.insert({Address{0x6}, byzantiumForkBlock}); + precompiledStartingBlocks.insert({Address{0x7}, byzantiumForkBlock}); + precompiledStartingBlocks.insert({Address{0x8}, byzantiumForkBlock}); + precompiledStartingBlocks.insert({Address{0x9}, istanbulForkBlock}); stateRoot = _stateRoot ? _stateRoot : calculateStateRoot(true); } diff --git a/libethereum/Executive.cpp b/libethereum/Executive.cpp index 1b29bed91e5..db483311078 100644 --- a/libethereum/Executive.cpp +++ b/libethereum/Executive.cpp @@ -9,8 +9,10 @@ #include "Interface.h" #include "StandardTrace.h" #include "State.h" +#include #include #include +#include #include #include @@ -42,6 +44,13 @@ std::string dumpStorage(ExtVM const& _ext) o << showbase << hex << i.second.first << ": " << i.second.second << "\n"; return o.str(); }; + +EVMC& precompilesVM() +{ + static EVMC precompilesVM{evmc_create_aleth_precompiles_vm(), {}}; + return precompilesVM; +} + } // namespace Executive::Executive(Block& _s, BlockChain const& _bc, unsigned _level) @@ -183,27 +192,30 @@ bool Executive::call(CallParameters const& _p, u256 const& _gasPrice, Address co if (_p.receiveAddress == c_RipemdPrecompiledAddress) m_s.unrevertableTouch(_p.codeAddress); - bigint g = m_sealEngine.costOfPrecompiled(_p.codeAddress, _p.data, m_envInfo.number()); - if (_p.gas < g) + try + { + auto gas = _p.gas; + auto const& schedule = m_sealEngine.evmSchedule(m_envInfo.number()); + bool const isCreate = false; + m_output = precompilesVM().exec(gas, _p.codeAddress, _p.senderAddress, _p.apparentValue, + _p.data, m_depth, isCreate, _p.staticCall, schedule); + m_gas = gas; + } + catch (OutOfGas const&) { m_excepted = TransactionException::OutOfGasBase; - // Bail from exception. return true; // true actually means "all finished - nothing more to be done regarding go(). } - else + catch (PrecompileFailure const&) { - m_gas = (u256)(_p.gas - g); - bytes output; - bool success; - tie(success, output) = m_sealEngine.executePrecompiled(_p.codeAddress, _p.data, m_envInfo.number()); - size_t outputSize = output.size(); - m_output = owning_bytes_ref{std::move(output), 0, outputSize}; - if (!success) - { - m_gas = 0; - m_excepted = TransactionException::OutOfGas; - return true; // true means no need to run go(). - } + m_gas = 0; + m_excepted = TransactionException::OutOfGas; + return true; // true means no need to run go(). + } + catch (std::exception const& _e) + { + cerror << "Unexpected std::exception in VM." << _e.what(); + exit(1); } } else diff --git a/libevm/EVMC.cpp b/libevm/EVMC.cpp index d68d6637e76..be80a3e419c 100644 --- a/libevm/EVMC.cpp +++ b/libevm/EVMC.cpp @@ -30,70 +30,39 @@ evmc_revision toRevision(EVMSchedule const& _schedule) noexcept return EVMC_HOMESTEAD; return EVMC_FRONTIER; } -} // namespace - -EVMC::EVMC(evmc_vm* _vm, std::vector> const& _options) noexcept - : evmc::VM(_vm) -{ - assert(_vm != nullptr); - assert(is_abi_compatible()); - - // Set the options. - for (auto& pair : _options) - { - auto result = set_option(pair.first.c_str(), pair.second.c_str()); - switch (result) - { - case EVMC_SET_OPTION_SUCCESS: - break; - case EVMC_SET_OPTION_INVALID_NAME: - cwarn << "Unknown EVMC option '" << pair.first << "'"; - break; - case EVMC_SET_OPTION_INVALID_VALUE: - cwarn << "Invalid value '" << pair.second << "' for EVMC option '" << pair.first << "'"; - break; - default: - cwarn << "Unknown error when setting EVMC option '" << pair.first << "'"; - } - } -} -owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp) +evmc_message createMessage(u256 const& _gas, Address const& _myAddress, Address const& _caller, + u256 const& _value, bytesConstRef _data, unsigned _depth, bool _isCreate, + bool _isStaticCall) noexcept { - assert(_ext.envInfo().number() >= 0); - assert(_ext.envInfo().timestamp() >= 0); - - constexpr int64_t int64max = std::numeric_limits::max(); - // TODO: The following checks should be removed by changing the types // used for gas, block number and timestamp. - (void)int64max; - assert(io_gas <= int64max); - assert(_ext.envInfo().gasLimit() <= int64max); - assert(_ext.depth <= static_cast(std::numeric_limits::max())); + assert(_gas <= std::numeric_limits::max()); + assert(_depth <= static_cast(std::numeric_limits::max())); - auto gas = static_cast(io_gas); + auto gas = static_cast(_gas); - auto mode = toRevision(_ext.evmSchedule()); - evmc_call_kind kind = _ext.isCreate ? EVMC_CREATE : EVMC_CALL; - uint32_t flags = _ext.staticCall ? EVMC_STATIC : 0; + evmc_call_kind kind = _isCreate ? EVMC_CREATE : EVMC_CALL; + uint32_t flags = _isStaticCall ? EVMC_STATIC : 0; assert(flags != EVMC_STATIC || kind == EVMC_CALL); // STATIC implies a CALL. - evmc_message msg = {kind, flags, static_cast(_ext.depth), gas, toEvmC(_ext.myAddress), - toEvmC(_ext.caller), _ext.data.data(), _ext.data.size(), toEvmC(_ext.value), - toEvmC(0x0_cppui256)}; - EvmCHost host{_ext}; - auto r = execute(host, mode, msg, _ext.code.data(), _ext.code.size()); + return {kind, flags, static_cast(_depth), gas, toEvmC(_myAddress), toEvmC(_caller), + _data.data(), _data.size(), toEvmC(_value), toEvmC(0x0_cppui256)}; +} + +owning_bytes_ref processResult(u256& o_gas, evmc::result const& _r) +{ // FIXME: Copy the output for now, but copyless version possible. - auto output = owning_bytes_ref{{&r.output_data[0], &r.output_data[r.output_size]}, 0, r.output_size}; + auto output = + owning_bytes_ref{{&_r.output_data[0], &_r.output_data[_r.output_size]}, 0, _r.output_size}; - switch (r.status_code) + switch (_r.status_code) { case EVMC_SUCCESS: - io_gas = r.gas_left; + o_gas = _r.gas_left; return output; case EVMC_REVERT: - io_gas = r.gas_left; + o_gas = _r.gas_left; throw RevertInstruction{std::move(output)}; case EVMC_OUT_OF_GAS: @@ -119,19 +88,81 @@ owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp case EVMC_STATIC_MODE_VIOLATION: BOOST_THROW_EXCEPTION(DisallowedStateChange()); - case EVMC_REJECTED: - cwarn << "Execution rejected by EVMC, executing with default VM implementation"; - return VMFactory::create(VMKind::Legacy)->exec(io_gas, _ext, _onOp); + case EVMC_PRECOMPILE_FAILURE: + BOOST_THROW_EXCEPTION(PrecompileFailure()); case EVMC_INTERNAL_ERROR: default: - if (r.status_code <= EVMC_INTERNAL_ERROR) - BOOST_THROW_EXCEPTION(InternalVMError{} << errinfo_evmcStatusCode(r.status_code)); + if (_r.status_code <= EVMC_INTERNAL_ERROR) + BOOST_THROW_EXCEPTION(InternalVMError{} << errinfo_evmcStatusCode(_r.status_code)); else // These cases aren't really internal errors, just more specific // error codes returned by the VM. Map all of them to OOG. BOOST_THROW_EXCEPTION(OutOfGas()); } } + +} // namespace + +EVMC::EVMC(evmc_vm* _vm, std::vector> const& _options) noexcept + : evmc::VM(_vm) +{ + assert(_vm != nullptr); + assert(is_abi_compatible()); + + // Set the options. + for (auto& pair : _options) + { + auto result = set_option(pair.first.c_str(), pair.second.c_str()); + switch (result) + { + case EVMC_SET_OPTION_SUCCESS: + break; + case EVMC_SET_OPTION_INVALID_NAME: + cwarn << "Unknown EVMC option '" << pair.first << "'"; + break; + case EVMC_SET_OPTION_INVALID_VALUE: + cwarn << "Invalid value '" << pair.second << "' for EVMC option '" << pair.first << "'"; + break; + default: + cwarn << "Unknown error when setting EVMC option '" << pair.first << "'"; + } + } +} + +owning_bytes_ref EVMC::exec(u256& io_gas, ExtVMFace& _ext, const OnOpFunc& _onOp) +{ + assert(_ext.envInfo().number() >= 0); + assert(_ext.envInfo().timestamp() >= 0); + assert(_ext.envInfo().gasLimit() <= std::numeric_limits::max()); + + evmc_message const msg = createMessage(io_gas, _ext.myAddress, _ext.caller, _ext.value, + _ext.data, _ext.depth, _ext.isCreate, _ext.staticCall); + auto const mode = toRevision(_ext.evmSchedule()); + + EvmCHost host{_ext}; + auto r = execute(host, mode, msg, _ext.code.data(), _ext.code.size()); + + if (r.status_code == EVMC_REJECTED) + { + cwarn << "Execution rejected by EVMC, executing with default VM implementation"; + return VMFactory::create(VMKind::Legacy)->exec(io_gas, _ext, _onOp); + } + + return processResult(io_gas, r); +} + +owning_bytes_ref EVMC::exec(u256& io_gas, Address const& _myAddress, Address const& _caller, + u256 const& _value, bytesConstRef _data, unsigned _depth, bool _isCreate, bool _isStaticCall, + EVMSchedule const& _schedule) +{ + evmc_message const msg = + createMessage(io_gas, _myAddress, _caller, _value, _data, _depth, _isCreate, _isStaticCall); + auto const mode = toRevision(_schedule); + + auto r = execute(mode, msg, nullptr, 0); + + return processResult(io_gas, r); +} } // namespace eth } // namespace dev diff --git a/libevm/EVMC.h b/libevm/EVMC.h index 3e7e9547952..09b78919b78 100644 --- a/libevm/EVMC.h +++ b/libevm/EVMC.h @@ -20,6 +20,10 @@ class EVMC : public evmc::VM, public VMFace EVMC(evmc_vm* _vm, std::vector> const& _options) noexcept; owning_bytes_ref exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) final; + + owning_bytes_ref exec(u256& io_gas, Address const& _myAddress, Address const& _caller, + u256 const& _value, bytesConstRef _data, unsigned _depth, bool _isCreate, + bool _isStaticCall, EVMSchedule const& _schedule) final; }; } // namespace eth } // namespace dev diff --git a/libevm/LegacyVM.h b/libevm/LegacyVM.h index 9b2b840162f..4ba10963552 100644 --- a/libevm/LegacyVM.h +++ b/libevm/LegacyVM.h @@ -16,7 +16,13 @@ namespace eth class LegacyVM: public VMFace { public: - virtual owning_bytes_ref exec(u256& _io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; + owning_bytes_ref exec(u256& _io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; + + owning_bytes_ref exec(u256&, Address const&, Address const&, u256 const&, bytesConstRef, + unsigned, bool, bool, EVMSchedule const&) override final + { + BOOST_THROW_EXCEPTION(InterfaceNotSupported{}); + } #if EIP_615 // invalid code will throw an exeption diff --git a/libevm/VMFace.h b/libevm/VMFace.h index 60c4ec7db42..87c5f33eb3f 100644 --- a/libevm/VMFace.h +++ b/libevm/VMFace.h @@ -22,6 +22,7 @@ ETH_SIMPLE_EXCEPTION_VM(OutOfStack); ETH_SIMPLE_EXCEPTION_VM(StackUnderflow); ETH_SIMPLE_EXCEPTION_VM(DisallowedStateChange); ETH_SIMPLE_EXCEPTION_VM(BufferOverrun); +ETH_SIMPLE_EXCEPTION_VM(PrecompileFailure); /// Reports VM internal error. This is not based on VMException because it must be handled /// differently than defined consensus exceptions. @@ -58,6 +59,14 @@ class VMFace /// VM implementation virtual owning_bytes_ref exec(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) = 0; + + /// Executes code without providing it access to the state. + /// + /// This method is useful for special cases like calling precompiles from EVMC module, where + /// execution is guaranteed not to require any Host access. + virtual owning_bytes_ref exec(u256& io_gas, Address const& _myAddress, Address const& _caller, + u256 const& _value, bytesConstRef _data, unsigned _depth, bool _isCreate, + bool _isStaticCall, EVMSchedule const& _schedule) = 0; }; /// Helpers: diff --git a/test/unittests/libethcore/PrecompiledTest.cpp b/test/unittests/libethcore/PrecompiledTest.cpp index 8951a0d0229..ac2fdae9a22 100644 --- a/test/unittests/libethcore/PrecompiledTest.cpp +++ b/test/unittests/libethcore/PrecompiledTest.cpp @@ -4,9 +4,10 @@ /// @file /// Precompiled contract implemetations testing. -#include +#include +#include #include -#include +#include using namespace std; using namespace dev; @@ -14,12 +15,152 @@ using namespace dev::eth; using namespace dev::test; namespace ut = boost::unit_test; -BOOST_FIXTURE_TEST_SUITE(PrecompiledTests, TestOutputHelperFixture) +namespace +{ +Address const c_ecrecover{0x1}; +Address const c_modexp{0x5}; +Address const c_alt_bn128_G1_add{0x6}; +Address const c_alt_bn128_G1_mul{0x7}; +Address const c_alt_bn128_pairing_product{0x8}; +Address const c_blake2_compression{0x9}; -BOOST_AUTO_TEST_CASE(modexpFermatTheorem) +struct PrecompiledTest +{ + const char* input; + const char* expected; + const char* name; +}; + +class PrecompiledFixture : public TestOutputHelperFixture { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); +public: + std::pair exec(Address const& _address, bytesConstRef _data) + { + try + { + owning_bytes_ref res = precompilesVM.exec( + gas, _address, sender, value, _data, depth, isCreate, staticCall, schedule); + + return {true, res.takeBytes()}; + } + catch (Exception const&) + { + return {false, {}}; + } + } + u256 price(Address const& _address, bytesConstRef _data) + { + u256 gasBefore = gas; + try + { + precompilesVM.exec( + gas, _address, sender, value, _data, depth, isCreate, staticCall, schedule); + } + catch (Exception const&) + { + } + return gasBefore - gas; + } + + bool runsOutOfGas(Address const& _address, bytesConstRef _data) + { + try + { + precompilesVM.exec( + gas, _address, sender, value, _data, depth, isCreate, staticCall, schedule); + } + catch (OutOfGas const&) + { + return true; // true actually means "all finished - nothing more to be done regarding + // go(). + } + catch (Exception const&) + { + } + return false; + } + + void benchmarkPrecompiled( + Address const& _address, vector_ref tests, int n) + { + if (!Options::get().all) + { + std::cout << "Skipping benchmark test because --all option is not specified.\n"; + return; + } + + + Timer timer; + + for (auto&& test : tests) + { + bytes input = fromHex(test.input); + bytesConstRef inputRef = &input; + + auto res = exec(_address, inputRef); + BOOST_REQUIRE_MESSAGE(res.first, test.name); + BOOST_REQUIRE_EQUAL(toHex(res.second), test.expected); + + timer.restart(); + for (int i = 0; i < n; ++i) + exec(_address, inputRef); + auto d = timer.duration() / n; + + auto t = std::chrono::duration_cast(d).count(); + std::cout << ut::framework::current_test_case().p_name << "/" << test.name << ": " << t + << " ns\n"; + } + } + + void testPrecompiled(Address const& _address, vector_ref tests) + { + for (auto&& test : tests) + { + bytes input = fromHex(test.input); + bytesConstRef inputRef = &input; + + auto res = exec(_address, inputRef); + BOOST_REQUIRE_MESSAGE(res.first, test.name); + BOOST_REQUIRE_EQUAL(toHex(res.second), test.expected); + + std::cout << ut::framework::current_test_case().p_name << "/" << test.name + << " PASSED\n"; + } + } + + void testPrecompiledFail(Address const& _address, vector_ref tests) + { + for (auto&& test : tests) + { + bytes input = fromHex(test.input); + bytesConstRef inputRef = &input; + + auto res = exec(_address, inputRef); + BOOST_REQUIRE_MESSAGE(!res.first, test.name); + + std::cout << ut::framework::current_test_case().p_name << "/" << test.name + << " PASSED\n"; + } + } + + + EVMC precompilesVM{evmc_create_aleth_precompiles_vm(), {}}; + u256 gas = std::numeric_limits::max(); + Address sender; + int value = 0; + int depth = 0; + bool isCreate = false; + bool staticCall = true; + EVMSchedule schedule = DefaultSchedule; +}; +} // namespace + + +BOOST_FIXTURE_TEST_SUITE(PrecompiledTests, PrecompiledFixture) + +BOOST_AUTO_TEST_CASE(modexpFermatTheorem) +{ bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000020" @@ -27,7 +168,7 @@ BOOST_AUTO_TEST_CASE(modexpFermatTheorem) "03" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000001"); @@ -36,15 +177,13 @@ BOOST_AUTO_TEST_CASE(modexpFermatTheorem) BOOST_AUTO_TEST_CASE(modexpZeroBase) { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000020" "0000000000000000000000000000000000000000000000000000000000000020" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000000"); @@ -53,8 +192,6 @@ BOOST_AUTO_TEST_CASE(modexpZeroBase) BOOST_AUTO_TEST_CASE(modexpExtraByteIgnored) { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000002" @@ -63,7 +200,7 @@ BOOST_AUTO_TEST_CASE(modexpExtraByteIgnored) "ffff" "8000000000000000000000000000000000000000000000000000000000000000" "07"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); bytes expected = fromHex("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); @@ -72,8 +209,6 @@ BOOST_AUTO_TEST_CASE(modexpExtraByteIgnored) BOOST_AUTO_TEST_CASE(modexpRightPadding) { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000002" @@ -81,7 +216,7 @@ BOOST_AUTO_TEST_CASE(modexpRightPadding) "03" "ffff" "80"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); bytes expected = fromHex("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); @@ -90,14 +225,12 @@ BOOST_AUTO_TEST_CASE(modexpRightPadding) BOOST_AUTO_TEST_CASE(modexpMissingValues) { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000002" "0000000000000000000000000000000000000000000000000000000000000020" "03"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000000"); @@ -106,15 +239,13 @@ BOOST_AUTO_TEST_CASE(modexpMissingValues) BOOST_AUTO_TEST_CASE(modexpEmptyValue) { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000020" "03" "8000000000000000000000000000000000000000000000000000000000000000"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000001"); @@ -123,8 +254,6 @@ BOOST_AUTO_TEST_CASE(modexpEmptyValue) BOOST_AUTO_TEST_CASE(modexpZeroPowerZero) { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000001" @@ -132,7 +261,7 @@ BOOST_AUTO_TEST_CASE(modexpZeroPowerZero) "00" "00" "80"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000001"); @@ -141,8 +270,6 @@ BOOST_AUTO_TEST_CASE(modexpZeroPowerZero) BOOST_AUTO_TEST_CASE(modexpZeroPowerZeroModZero) { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000001" @@ -150,7 +277,7 @@ BOOST_AUTO_TEST_CASE(modexpZeroPowerZeroModZero) "00" "00" "00"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000000"); @@ -159,15 +286,13 @@ BOOST_AUTO_TEST_CASE(modexpZeroPowerZeroModZero) BOOST_AUTO_TEST_CASE(modexpModLengthZero) { - PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000000" "01" "01"); - auto res = exec(bytesConstRef(in.data(), in.size())); + auto res = exec(c_modexp, ref(in)); BOOST_REQUIRE(res.first); BOOST_REQUIRE(res.second.empty()); @@ -175,8 +300,6 @@ BOOST_AUTO_TEST_CASE(modexpModLengthZero) BOOST_AUTO_TEST_CASE(modexpCostFermatTheorem) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000001" "0000000000000000000000000000000000000000000000000000000000000020" @@ -184,30 +307,24 @@ BOOST_AUTO_TEST_CASE(modexpCostFermatTheorem) "03" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); - auto res = cost(ref(in), {}, {}); + auto res = price(c_modexp, ref(in)); BOOST_REQUIRE_EQUAL(static_cast(res), 13056); } BOOST_AUTO_TEST_CASE(modexpCostTooLarge) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000000" "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" "0000000000000000000000000000000000000000000000000000000000000020" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd"); - auto res = cost(ref(in), {}, {}); - - BOOST_REQUIRE_MESSAGE(res == bigint{"47428439751604713645494675459558567056699385719046375030561826409641217900517324"}, "Got: " + toString(res)); + BOOST_REQUIRE(runsOutOfGas(c_modexp, ref(in))); } BOOST_AUTO_TEST_CASE(modexpCostEmptyExponent) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000008" // length of B "0000000000000000000000000000000000000000000000000000000000000000" // length of E @@ -217,15 +334,13 @@ BOOST_AUTO_TEST_CASE(modexpCostEmptyExponent) "998877665544332211998877665544332211" // M "9978" // Garbage that should be ignored ); - auto res = cost(ref(in), {}, {}); + auto res = price(c_modexp, ref(in)); BOOST_REQUIRE_MESSAGE(res == bigint{"12"}, "Got: " + toString(res)); } BOOST_AUTO_TEST_CASE(modexpCostZeroExponent) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000000" // length of B "0000000000000000000000000000000000000000000000000000000000000003" // length of E @@ -234,15 +349,13 @@ BOOST_AUTO_TEST_CASE(modexpCostZeroExponent) "000000" // E "112233445566778899aa" // M ); - auto res = cost(ref(in), {}, {}); + auto res = price(c_modexp, ref(in)); BOOST_REQUIRE_MESSAGE(res == bigint{"5"}, "Got: " + toString(res)); } BOOST_AUTO_TEST_CASE(modexpCostApproximated) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000003" // length of B "0000000000000000000000000000000000000000000000000000000000000021" // length of E @@ -251,15 +364,13 @@ BOOST_AUTO_TEST_CASE(modexpCostApproximated) "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" // E "112233445566778899aa" // M ); - auto res = cost(ref(in), {}, {}); + auto res = price(c_modexp, ref(in)); BOOST_REQUIRE_MESSAGE(res == bigint{"1315"}, "Got: " + toString(res)); } BOOST_AUTO_TEST_CASE(modexpCostApproximatedPartialByte) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000003" // length of B "0000000000000000000000000000000000000000000000000000000000000021" // length of E @@ -268,15 +379,13 @@ BOOST_AUTO_TEST_CASE(modexpCostApproximatedPartialByte) "02ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" // E "112233445566778899aa" // M ); - auto res = cost(ref(in), {}, {}); + auto res = price(c_modexp, ref(in)); BOOST_REQUIRE_MESSAGE(res == bigint{"1285"}, "Got: " + toString(res)); } BOOST_AUTO_TEST_CASE(modexpCostApproximatedGhost) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000003" // length of B "0000000000000000000000000000000000000000000000000000000000000021" // length of E @@ -285,15 +394,13 @@ BOOST_AUTO_TEST_CASE(modexpCostApproximatedGhost) "000000000000000000000000000000000000000000000000000000000000000000" // E "112233445566778899aa" // M ); - auto res = cost(ref(in), {}, {}); + auto res = price(c_modexp, ref(in)); BOOST_REQUIRE_MESSAGE(res == bigint{"40"}, "Got: " + toString(res)); } BOOST_AUTO_TEST_CASE(modexpCostMidRange) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000003" // length of B "0000000000000000000000000000000000000000000000000000000000000021" // length of E @@ -302,15 +409,13 @@ BOOST_AUTO_TEST_CASE(modexpCostMidRange) "000000000000000000000000000000000000000000000000000000000000000000" // E "112233445566778899aa" // M ); - auto res = cost(ref(in), {}, {}); + auto res = price(c_modexp, ref(in)); BOOST_REQUIRE_MESSAGE(res == ((74 * 74 / 4 + 96 * 74 - 3072) * 8) / 20, "Got: " + toString(res)); } BOOST_AUTO_TEST_CASE(modexpCostHighRange) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); - bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000003" // length of B "0000000000000000000000000000000000000000000000000000000000000021" // length of E @@ -319,7 +424,7 @@ BOOST_AUTO_TEST_CASE(modexpCostHighRange) "000000000000000000000000000000000000000000000000000000000000000000" // E "112233445566778899aa" // M ); - auto res = cost(ref(in), {}, {}); + auto res = price(c_modexp, ref(in)); BOOST_REQUIRE_MESSAGE(res == ((1025 * 1025 / 16 + 480 * 1025 - 199680) * 8) / 20, "Got: " + toString(res)); } @@ -331,13 +436,6 @@ BOOST_AUTO_TEST_CASE(modexpCostHighRange) /// https://github.com/ethereum/go-ethereum/blob/master/core/vm/contracts_test.go. /// @{ -struct PrecompiledTest -{ - const char* input; - const char* expected; - const char* name; -}; - constexpr PrecompiledTest ecrecoverTests[] = { { @@ -638,154 +736,72 @@ constexpr PrecompiledTest bn256PairingTests[] = }, }; -namespace -{ -void benchmarkPrecompiled(char const name[], vector_ref tests, int n) -{ - if (!Options::get().all) - { - std::cout << "Skipping benchmark test because --all option is not specified.\n"; - return; - } - - PrecompiledExecutor exec = PrecompiledRegistrar::executor(name); - Timer timer; - - for (auto&& test : tests) - { - bytes input = fromHex(test.input); - bytesConstRef inputRef = &input; - - auto res = exec(inputRef); - BOOST_REQUIRE_MESSAGE(res.first, test.name); - BOOST_REQUIRE_EQUAL(toHex(res.second), test.expected); - - timer.restart(); - for (int i = 0; i < n; ++i) - exec(inputRef); - auto d = timer.duration() / n; - - auto t = std::chrono::duration_cast(d).count(); - std::cout << ut::framework::current_test_case().p_name << "/" << test.name << ": " << t << " ns\n"; - } -} - -void testPrecompiled(char const name[], vector_ref tests) -{ - PrecompiledExecutor exec = PrecompiledRegistrar::executor(name); - - for (auto&& test : tests) - { - bytes input = fromHex(test.input); - bytesConstRef inputRef = &input; - - auto res = exec(inputRef); - BOOST_REQUIRE_MESSAGE(res.first, test.name); - BOOST_REQUIRE_EQUAL(toHex(res.second), test.expected); - - std::cout << ut::framework::current_test_case().p_name << "/" << test.name << " PASSED\n"; - } -} - -void testPrecompiledFail(char const name[], vector_ref tests) -{ - PrecompiledExecutor exec = PrecompiledRegistrar::executor(name); - - for (auto&& test : tests) - { - bytes input = fromHex(test.input); - bytesConstRef inputRef = &input; - - auto res = exec(inputRef); - BOOST_REQUIRE_MESSAGE(!res.first, test.name); - - std::cout << ut::framework::current_test_case().p_name << "/" << test.name << " PASSED\n"; - } -} -} - /// @} BOOST_AUTO_TEST_CASE(bench_ecrecover, *ut::label("bench")) { vector_ref tests{ecrecoverTests, sizeof(ecrecoverTests) / sizeof(ecrecoverTests[0])}; - benchmarkPrecompiled("ecrecover", tests, 100000); + benchmarkPrecompiled(c_ecrecover, tests, 100000); } BOOST_AUTO_TEST_CASE(bench_modexp, *ut::label("bench")) { vector_ref tests{modexpTests, sizeof(modexpTests) / sizeof(modexpTests[0])}; - benchmarkPrecompiled("modexp", tests, 10000); + benchmarkPrecompiled(c_modexp, tests, 10000); } BOOST_AUTO_TEST_CASE(bench_bn256Add, *ut::label("bench")) { vector_ref tests{bn256AddTests, sizeof(bn256AddTests) / sizeof(bn256AddTests[0])}; - benchmarkPrecompiled("alt_bn128_G1_add", tests, 1000000); + benchmarkPrecompiled(c_alt_bn128_G1_add, tests, 1000000); } BOOST_AUTO_TEST_CASE(bench_bn256ScalarMul, *ut::label("bench")) { vector_ref tests{bn256ScalarMulTests, sizeof(bn256ScalarMulTests) / sizeof(bn256ScalarMulTests[0])}; - benchmarkPrecompiled("alt_bn128_G1_mul", tests, 10000); + benchmarkPrecompiled(c_alt_bn128_G1_mul, tests, 10000); } BOOST_AUTO_TEST_CASE(bench_bn256Pairing, *ut::label("bench")) { vector_ref tests{bn256PairingTests, sizeof(bn256PairingTests) / sizeof(bn256PairingTests[0])}; - benchmarkPrecompiled("alt_bn128_pairing_product", tests, 1000); + benchmarkPrecompiled(c_alt_bn128_pairing_product, tests, 1000); } BOOST_AUTO_TEST_CASE(ecaddCostBeforeIstanbul) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("alt_bn128_G1_add"); - - ChainParams chainParams{genesisInfo(eth::Network::IstanbulTransitionTest)}; - - auto res = cost({}, chainParams, 1); + schedule = ConstantinopleSchedule; + auto res = price(c_alt_bn128_G1_add, {}); BOOST_REQUIRE_EQUAL(static_cast(res), 500); } BOOST_AUTO_TEST_CASE(ecaddCostIstanbul) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("alt_bn128_G1_add"); - - ChainParams chainParams{genesisInfo(eth::Network::IstanbulTransitionTest)}; - - auto res = cost({}, chainParams, 2); + schedule = IstanbulSchedule; + auto res = price(c_alt_bn128_G1_add, {}); BOOST_REQUIRE_EQUAL(static_cast(res), 150); } BOOST_AUTO_TEST_CASE(ecmulBeforeIstanbul) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("alt_bn128_G1_mul"); - - ChainParams chainParams{genesisInfo(eth::Network::IstanbulTransitionTest)}; - - auto res = cost({}, chainParams, 1); + schedule = ConstantinopleSchedule; + auto res = price(c_alt_bn128_G1_mul, {}); BOOST_REQUIRE_EQUAL(static_cast(res), 40000); } BOOST_AUTO_TEST_CASE(ecmulCostIstanbul) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("alt_bn128_G1_mul"); - - ChainParams chainParams{genesisInfo(eth::Network::IstanbulTransitionTest)}; - - auto res = cost({}, chainParams, 2); + schedule = IstanbulSchedule; + auto res = price(c_alt_bn128_G1_mul, {}); BOOST_REQUIRE_EQUAL(static_cast(res), 6000); } BOOST_AUTO_TEST_CASE(ecpairingCost) { - PrecompiledPricer cost = PrecompiledRegistrar::pricer("alt_bn128_pairing_product"); - - ChainParams chainParams{genesisInfo(eth::Network::IstanbulTransitionTest)}; - bytes in{fromHex( "0x1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f593034dd2920f673e204fee281" "1c678745fc819b55d3e9d294e45c9b03a76aef41209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b" @@ -797,10 +813,12 @@ BOOST_AUTO_TEST_CASE(ecpairingCost) "bd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6d" "eb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa")}; - auto costBeforeIstanbul = cost(ref(in), chainParams, 1); + schedule = ConstantinopleSchedule; + auto costBeforeIstanbul = price(c_alt_bn128_pairing_product, ref(in)); BOOST_CHECK_EQUAL(static_cast(costBeforeIstanbul), in.size() / 192 * 80000 + 100000); - auto costIstanbul = cost(ref(in), chainParams, 2); + schedule = IstanbulSchedule; + auto costIstanbul = price(c_alt_bn128_pairing_product, ref(in)); BOOST_CHECK_EQUAL(static_cast(costIstanbul), in.size() / 192 * 34000 + 45000); } @@ -896,28 +914,28 @@ BOOST_AUTO_TEST_CASE(blake2compression) { vector_ref tests{blake2FCompressionTests, sizeof(blake2FCompressionTests) / sizeof(blake2FCompressionTests[0])}; - testPrecompiled("blake2_compression", tests); + testPrecompiled(c_blake2_compression, tests); } BOOST_AUTO_TEST_CASE(blake2compressionFail) { vector_ref tests{blake2FCompressionFailTests, sizeof(blake2FCompressionFailTests) / sizeof(blake2FCompressionFailTests[0])}; - testPrecompiledFail("blake2_compression", tests); + testPrecompiledFail(c_blake2_compression, tests); } BOOST_AUTO_TEST_CASE(bench_blake2compression, *ut::label("bench")) { vector_ref tests{blake2FCompressionTests, sizeof(blake2FCompressionTests) / sizeof(blake2FCompressionTests[0])}; - benchmarkPrecompiled("blake2_compression", tests, 100000); + benchmarkPrecompiled(c_blake2_compression, tests, 100000); } BOOST_AUTO_TEST_CASE(bench_blake2compression_maxrounds, *ut::label("bench")) { vector_ref tests{blake2FCompressionLargeTests, sizeof(blake2FCompressionLargeTests) / sizeof(blake2FCompressionLargeTests[0])}; - benchmarkPrecompiled("blake2_compression", tests, 1); + benchmarkPrecompiled(c_blake2_compression, tests, 1); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/unittests/libethereum/ClientTest.cpp b/test/unittests/libethereum/ClientTest.cpp index 52c3b32380f..f85d418bae2 100644 --- a/test/unittests/libethereum/ClientTest.cpp +++ b/test/unittests/libethereum/ClientTest.cpp @@ -89,9 +89,8 @@ BOOST_AUTO_TEST_CASE(ClientTest_setChainParamsPrecompilesAreIgnored) auto const ecrecoverAddress = Address{0x01}; auto const sha256Address = Address{0x02}; - BOOST_CHECK_EQUAL( - testClient->chainParams().precompiled.at(ecrecoverAddress).startingBlock(), 0); - BOOST_CHECK(contains(testClient->chainParams().precompiled, sha256Address)); + BOOST_CHECK_EQUAL(testClient->chainParams().precompiledStartingBlocks.at(ecrecoverAddress), 0); + BOOST_CHECK(contains(testClient->chainParams().precompiledStartingBlocks, sha256Address)); std::string const configWithCustomPrecompiles = R"({ "sealEngine": "NoProof", @@ -121,9 +120,8 @@ BOOST_AUTO_TEST_CASE(ClientTest_setChainParamsPrecompilesAreIgnored) testClient->setChainParams(configWithCustomPrecompiles); - BOOST_CHECK_EQUAL( - testClient->chainParams().precompiled.at(ecrecoverAddress).startingBlock(), 0); - BOOST_CHECK(contains(testClient->chainParams().precompiled, sha256Address)); + BOOST_CHECK_EQUAL(testClient->chainParams().precompiledStartingBlocks.at(ecrecoverAddress), 0); + BOOST_CHECK(contains(testClient->chainParams().precompiledStartingBlocks, sha256Address)); } BOOST_AUTO_TEST_SUITE_END()