From 18773f8acf5c9e423b3054fd30ad61972bd5188a Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 28 Mar 2017 15:31:37 +0200 Subject: [PATCH 1/3] EIP198: Implement bigint modexp precompiled contract --- libethashseal/genesis/metropolisTest.cpp | 1 + libethcore/Precompiled.cpp | 32 ++++++ test/unittests/libethcore/PrecompiledTest.cpp | 103 ++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 test/unittests/libethcore/PrecompiledTest.cpp diff --git a/libethashseal/genesis/metropolisTest.cpp b/libethashseal/genesis/metropolisTest.cpp index 33d5b610851..3a6efb3f303 100644 --- a/libethashseal/genesis/metropolisTest.cpp +++ b/libethashseal/genesis/metropolisTest.cpp @@ -55,6 +55,7 @@ R"E( "0000000000000000000000000000000000000002": { "wei": "1", "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, "0000000000000000000000000000000000000003": { "wei": "1", "precompiled": { "name": "ripemd160", "linear": { "base": 600, "word": 120 } } }, "0000000000000000000000000000000000000004": { "wei": "1", "precompiled": { "name": "identity", "linear": { "base": 15, "word": 3 } } } + "0000000000000000000000000000000000000005": { "wei": "1", "precompiled": { "name": "modexp" } } } } )E"; diff --git a/libethcore/Precompiled.cpp b/libethcore/Precompiled.cpp index 8aef8783085..666cca51fc9 100644 --- a/libethcore/Precompiled.cpp +++ b/libethcore/Precompiled.cpp @@ -90,4 +90,36 @@ ETH_REGISTER_PRECOMPILED(identity)(bytesConstRef _in) return {true, _in.toBytes()}; } +template +T parseBigEndianRightPadded(bytesConstRef _in, size_t _begin, size_t _count) +{ + if (_begin > _in.count()) + return 0; + + bytesConstRef cropped = _in.cropped(_begin, min(_count, _in.count() - _begin)); + + T ret = fromBigEndian(cropped); + ret <<= 8 * (_count - cropped.count()); + + return ret; +} + +ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) +{ + size_t const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + size_t const expLength(parseBigEndianRightPadded(_in, 32, 32)); + size_t const modLength(parseBigEndianRightPadded(_in, 64, 32)); + + 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 = boost::multiprecision::powm(base, exp, mod); + + bytes ret(modLength); + toBigEndian(result, ret); + + return {true, ret}; +} + } diff --git a/test/unittests/libethcore/PrecompiledTest.cpp b/test/unittests/libethcore/PrecompiledTest.cpp new file mode 100644 index 00000000000..d1abefbf183 --- /dev/null +++ b/test/unittests/libethcore/PrecompiledTest.cpp @@ -0,0 +1,103 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file PrecompiledTest.cpp + * Preompiled contract implemetations testing. + */ + +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace dev::test; + +BOOST_FIXTURE_TEST_SUITE(PrecompiledTests, TestOutputHelper) + +BOOST_AUTO_TEST_CASE(modexpFermatTheorem) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = fromHex( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000001"); + BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} +/* +BOOST_AUTO_TEST_CASE(modexpTooLarge) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = fromHex( + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000020" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_EQUAL(res.first, false); +} +*/ + +BOOST_AUTO_TEST_CASE(modexpExtraByteIgnored) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = fromHex( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "ffff" + "8000000000000000000000000000000000000000000000000000000000000000" + "07"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = fromHex("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); + BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpRightPadding) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = fromHex( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "ffff" + "80"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = fromHex("3b01b01ac41f2d6e917c6d6a221ce793802469026d9ab7578fa2e79e4da6aaab"); + BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_SUITE_END() From cea171f519c94257144bc55d485c6d5d5b3435c3 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 3 Apr 2017 11:30:21 +0200 Subject: [PATCH 2/3] EIP198: Custom cost function for modexp contract --- libethcore/ChainOperationParams.cpp | 4 +- libethcore/ChainOperationParams.h | 6 +-- libethcore/Precompiled.cpp | 18 +++++++++ libethcore/Precompiled.h | 19 ++++++++-- libethereum/Account.cpp | 57 ++++++++++++++++++----------- 5 files changed, 73 insertions(+), 31 deletions(-) diff --git a/libethcore/ChainOperationParams.cpp b/libethcore/ChainOperationParams.cpp index 9d051fa49d0..a93e9c2f1a7 100644 --- a/libethcore/ChainOperationParams.cpp +++ b/libethcore/ChainOperationParams.cpp @@ -32,9 +32,9 @@ PrecompiledContract::PrecompiledContract( PrecompiledExecutor const& _exec, u256 const& _startingBlock ): - PrecompiledContract([=](unsigned size) -> bigint + PrecompiledContract([=](bytesConstRef _in) -> bigint { - bigint s = size; + bigint s = _in.size(); bigint b = _base; bigint w = _word; return b + (s + 31) / 32 * w; diff --git a/libethcore/ChainOperationParams.h b/libethcore/ChainOperationParams.h index 234f7a7bce2..d3622d0a979 100644 --- a/libethcore/ChainOperationParams.h +++ b/libethcore/ChainOperationParams.h @@ -37,7 +37,7 @@ class PrecompiledContract public: PrecompiledContract() = default; PrecompiledContract( - std::function const& _cost, + PrecompiledPricer const& _cost, PrecompiledExecutor const& _exec, u256 const& _startingBlock = 0 ): @@ -52,13 +52,13 @@ class PrecompiledContract u256 const& _startingBlock = 0 ); - bigint cost(bytesConstRef _in) const { return m_cost(_in.size()); } + bigint cost(bytesConstRef _in) const { return m_cost(_in); } std::pair execute(bytesConstRef _in) const { return m_execute(_in); } u256 const& startingBlock() const { return m_startingBlock; } private: - std::function m_cost; + PrecompiledPricer m_cost; PrecompiledExecutor m_execute; u256 m_startingBlock = 0; }; diff --git a/libethcore/Precompiled.cpp b/libethcore/Precompiled.cpp index 666cca51fc9..23fe6a0f73b 100644 --- a/libethcore/Precompiled.cpp +++ b/libethcore/Precompiled.cpp @@ -38,6 +38,13 @@ PrecompiledExecutor const& PrecompiledRegistrar::executor(std::string const& _na 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 { @@ -122,4 +129,15 @@ ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) return {true, ret}; } +ETH_REGISTER_PRECOMPILED_PRICER(modexp)(bytesConstRef _in) +{ + u256 const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + u256 const expLength(parseBigEndianRightPadded(_in, 32, 32)); + u256 const modLength(parseBigEndianRightPadded(_in, 64, 32)); + + bigint const maxLength = max(modLength, baseLength); + + return maxLength * maxLength * max(expLength, 1) / 20; +} + } diff --git a/libethcore/Precompiled.h b/libethcore/Precompiled.h index e14ef931084..ef09d84f50f 100644 --- a/libethcore/Precompiled.h +++ b/libethcore/Precompiled.h @@ -32,8 +32,10 @@ namespace eth { using PrecompiledExecutor = std::function(bytesConstRef _in)>; +using PrecompiledPricer = std::function; DEV_SIMPLE_EXCEPTION(ExecutorNotFound); +DEV_SIMPLE_EXCEPTION(PricerNotFound); class PrecompiledRegistrar { @@ -41,20 +43,29 @@ class PrecompiledRegistrar /// 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 registerPrecompiled(std::string const& _name, PrecompiledExecutor const& _exec) { return (get()->m_execs[_name] = _exec); } + 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 an executor. Shouldn't generally be necessary. - static void unregisterPrecompiled(std::string const& _name) { get()->m_execs.erase(_name); } + 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::registerPrecompiled(#Name, &__eth_registerPrecompiledFunction ## Name); static std::pair __eth_registerPrecompiledFunction ## Name - +#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); static PrecompiledPricer __eth_registerPricerFactory ## Name = ::dev::eth::PrecompiledRegistrar::registerPricer(#Name, &__eth_registerPricerFunction ## Name); static bigint __eth_registerPricerFunction ## Name } } diff --git a/libethereum/Account.cpp b/libethereum/Account.cpp index d168865e75e..9a96e8a9c00 100644 --- a/libethereum/Account.cpp +++ b/libethereum/Account.cpp @@ -37,6 +37,9 @@ void Account::setNewCode(bytes&& _code) namespace js = json_spirit; +namespace +{ + uint64_t toUnsigned(js::mValue const& _v) { switch (_v.type()) @@ -47,6 +50,37 @@ uint64_t toUnsigned(js::mValue const& _v) } } +PrecompiledContract createPrecompiledContract(js::mObject& _precompiled) +{ + auto n = _precompiled["name"].get_str(); + try + { + u256 startingBlock = 0; + if (_precompiled.count("startingBlock")) + startingBlock = u256(_precompiled["startingBlock"].get_str()); + + if (!_precompiled.count("linear")) + return PrecompiledContract(PrecompiledRegistrar::pricer(n), PrecompiledRegistrar::executor(n), startingBlock); + + auto l = _precompiled["linear"].get_obj(); + unsigned base = toUnsigned(l["base"]); + unsigned word = toUnsigned(l["word"]); + return PrecompiledContract(base, word, PrecompiledRegistrar::executor(n), startingBlock); + } + catch (PricerNotFound const&) + { + cwarn << "Couldn't create a precompiled contract account. Missing a pricer called:" << n; + throw; + } + catch (ExecutorNotFound const&) + { + // Oh dear - missing a plugin? + cwarn << "Couldn't create a precompiled contract account. Missing an executor called:" << n; + throw; + } +} + +} AccountMap dev::eth::jsonToAccountMap(std::string const& _json, u256 const& _defaultNonce, AccountMaskMap* o_mask, PrecompiledContractMap* o_precompiled) { auto u256Safe = [](std::string const& s) -> u256 { @@ -115,28 +149,7 @@ AccountMap dev::eth::jsonToAccountMap(std::string const& _json, u256 const& _def if (o_precompiled && o.count("precompiled")) { js::mObject p = o["precompiled"].get_obj(); - auto n = p["name"].get_str(); - if (!p.count("linear")) - { - cwarn << "No gas cost given for precompiled contract " << n; - throw; - } - try - { - auto l = p["linear"].get_obj(); - u256 startingBlock = 0; - if (p.count("startingBlock")) - startingBlock = u256(p["startingBlock"].get_str()); - unsigned base = toUnsigned(l["base"]); - unsigned word = toUnsigned(l["word"]); - o_precompiled->insert(make_pair(a, PrecompiledContract(base, word, PrecompiledRegistrar::executor(n), startingBlock))); - } - catch (ExecutorNotFound) - { - // Oh dear - missing a plugin? - cwarn << "Couldn't create a precompiled contract account. Missing an executor called:" << n; - throw; - } + o_precompiled->insert(make_pair(a, createPrecompiledContract(p))); } } From aaf29c70afd7af1a330f30e5b415f9a514b42da6 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 4 Apr 2017 16:18:15 +0200 Subject: [PATCH 3/3] Minor improvements & more tests added --- libethcore/Precompiled.cpp | 29 ++-- libethcore/Precompiled.h | 2 +- test/unittests/libethcore/PrecompiledTest.cpp | 131 +++++++++++++++++- 3 files changed, 141 insertions(+), 21 deletions(-) diff --git a/libethcore/Precompiled.cpp b/libethcore/Precompiled.cpp index 23fe6a0f73b..c9a1c5735e7 100644 --- a/libethcore/Precompiled.cpp +++ b/libethcore/Precompiled.cpp @@ -97,15 +97,18 @@ ETH_REGISTER_PRECOMPILED(identity)(bytesConstRef _in) return {true, _in.toBytes()}; } -template -T parseBigEndianRightPadded(bytesConstRef _in, size_t _begin, size_t _count) +// 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, size_t _begin, size_t _count) { if (_begin > _in.count()) return 0; + // crop _in, not going beyond its size bytesConstRef cropped = _in.cropped(_begin, min(_count, _in.count() - _begin)); - T ret = fromBigEndian(cropped); + bigint ret = fromBigEndian(cropped); + // shift as if we had right-padding zeroes ret <<= 8 * (_count - cropped.count()); return ret; @@ -113,15 +116,15 @@ T parseBigEndianRightPadded(bytesConstRef _in, size_t _begin, size_t _count) ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) { - size_t const baseLength(parseBigEndianRightPadded(_in, 0, 32)); - size_t const expLength(parseBigEndianRightPadded(_in, 32, 32)); - size_t const modLength(parseBigEndianRightPadded(_in, 64, 32)); + size_t const baseLength(parseBigEndianRightPadded(_in, 0, 32)); + size_t const expLength(parseBigEndianRightPadded(_in, 32, 32)); + size_t const modLength(parseBigEndianRightPadded(_in, 64, 32)); - 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 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 = boost::multiprecision::powm(base, exp, mod); + bigint const result = mod != 0 ? boost::multiprecision::powm(base, exp, mod) : bigint{0}; bytes ret(modLength); toBigEndian(result, ret); @@ -131,9 +134,9 @@ ETH_REGISTER_PRECOMPILED(modexp)(bytesConstRef _in) ETH_REGISTER_PRECOMPILED_PRICER(modexp)(bytesConstRef _in) { - u256 const baseLength(parseBigEndianRightPadded(_in, 0, 32)); - u256 const expLength(parseBigEndianRightPadded(_in, 32, 32)); - u256 const modLength(parseBigEndianRightPadded(_in, 64, 32)); + 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); diff --git a/libethcore/Precompiled.h b/libethcore/Precompiled.h index ef09d84f50f..df2e34239b0 100644 --- a/libethcore/Precompiled.h +++ b/libethcore/Precompiled.h @@ -53,7 +53,7 @@ class PrecompiledRegistrar /// 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 an executor. Shouldn't generally be necessary. + /// Unregister a pricer. Shouldn't generally be necessary. static void unregisterPricer(std::string const& _name) { get()->m_pricers.erase(_name); } private: diff --git a/test/unittests/libethcore/PrecompiledTest.cpp b/test/unittests/libethcore/PrecompiledTest.cpp index d1abefbf183..81b474282b7 100644 --- a/test/unittests/libethcore/PrecompiledTest.cpp +++ b/test/unittests/libethcore/PrecompiledTest.cpp @@ -46,22 +46,23 @@ BOOST_AUTO_TEST_CASE(modexpFermatTheorem) bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000001"); BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); } -/* -BOOST_AUTO_TEST_CASE(modexpTooLarge) + +BOOST_AUTO_TEST_CASE(modexpZeroBase) { PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); bytes in = fromHex( "0000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000020" - "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" - "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd"); + "0000000000000000000000000000000000000000000000000000000000000020" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); auto res = exec(bytesConstRef(in.data(), in.size())); - BOOST_REQUIRE_EQUAL(res.first, false); + BOOST_REQUIRE(res.first); + bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000000"); + BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); } -*/ BOOST_AUTO_TEST_CASE(modexpExtraByteIgnored) { @@ -100,4 +101,120 @@ BOOST_AUTO_TEST_CASE(modexpRightPadding) BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); } +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())); + + BOOST_REQUIRE(res.first); + bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000000"); + BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +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())); + + BOOST_REQUIRE(res.first); + bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000001"); + BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpZeroPowerZero) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = fromHex( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "00" + "00" + "80"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000001"); + BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +BOOST_AUTO_TEST_CASE(modexpZeroPowerZeroModZero) +{ + PrecompiledExecutor exec = PrecompiledRegistrar::executor("modexp"); + + bytes in = fromHex( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "00" + "00" + "00"); + auto res = exec(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res.first); + bytes expected = fromHex("0000000000000000000000000000000000000000000000000000000000000000"); + BOOST_REQUIRE_EQUAL_COLLECTIONS(res.second.begin(), res.second.end(), expected.begin(), expected.end()); +} + +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())); + + BOOST_REQUIRE(res.first); + BOOST_REQUIRE(res.second.empty()); +} + +BOOST_AUTO_TEST_CASE(modexpCostFermatTheorem) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = fromHex( + "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000020" + "03" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE_EQUAL(static_cast(res), 1638); +} + +BOOST_AUTO_TEST_CASE(modexpCostTooLarge) +{ + PrecompiledPricer cost = PrecompiledRegistrar::pricer("modexp"); + + bytes in = fromHex( + "0000000000000000000000000000000000000000000000000000000000000000" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "0000000000000000000000000000000000000000000000000000000000000020" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe" + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd"); + auto res = cost(bytesConstRef(in.data(), in.size())); + + BOOST_REQUIRE(res == bigint{"5928554968950589205686834432444820882087423214880796878820228301205152237564672"}); +} + BOOST_AUTO_TEST_SUITE_END()