Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

EIP198 - Bigint modexp precompiled contract #4028

Merged
merged 3 commits into from
Apr 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libethashseal/genesis/metropolisTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
4 changes: 2 additions & 2 deletions libethcore/ChainOperationParams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions libethcore/ChainOperationParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class PrecompiledContract
public:
PrecompiledContract() = default;
PrecompiledContract(
std::function<bigint(size_t)> const& _cost,
PrecompiledPricer const& _cost,
PrecompiledExecutor const& _exec,
u256 const& _startingBlock = 0
):
Expand All @@ -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<bool, bytes> execute(bytesConstRef _in) const { return m_execute(_in); }

u256 const& startingBlock() const { return m_startingBlock; }

private:
std::function<bigint(size_t)> m_cost;
PrecompiledPricer m_cost;
PrecompiledExecutor m_execute;
u256 m_startingBlock = 0;
};
Expand Down
53 changes: 53 additions & 0 deletions libethcore/Precompiled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{

Expand Down Expand Up @@ -90,4 +97,50 @@ 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, 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));

bigint ret = fromBigEndian<bigint>(cropped);
// shift as if we had right-padding zeroes
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 = mod != 0 ? boost::multiprecision::powm(base, exp, mod) : bigint{0};

bytes ret(modLength);
toBigEndian(result, ret);

return {true, ret};
}

ETH_REGISTER_PRECOMPILED_PRICER(modexp)(bytesConstRef _in)
{
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);

return maxLength * maxLength * max<bigint>(expLength, 1) / 20;
}

}
19 changes: 15 additions & 4 deletions libethcore/Precompiled.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,40 @@ namespace eth
{

using PrecompiledExecutor = std::function<std::pair<bool, bytes>(bytesConstRef _in)>;
using PrecompiledPricer = std::function<bigint(bytesConstRef _in)>;

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 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 unregisterPrecompiled(std::string const& _name) { get()->m_execs.erase(_name); }
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<std::string, PrecompiledExecutor> m_execs;
std::unordered_map<std::string, PrecompiledPricer> m_pricers;
static PrecompiledRegistrar* s_this;
};

// TODO: unregister on unload with a static object.
#define ETH_REGISTER_PRECOMPILED(Name) static std::pair<bool, bytes> __eth_registerPrecompiledFunction ## Name(bytesConstRef _in); static PrecompiledExecutor __eth_registerPrecompiledFactory ## Name = ::dev::eth::PrecompiledRegistrar::registerPrecompiled(#Name, &__eth_registerPrecompiledFunction ## Name); static std::pair<bool, bytes> __eth_registerPrecompiledFunction ## Name

#define ETH_REGISTER_PRECOMPILED(Name) static std::pair<bool, bytes> __eth_registerPrecompiledFunction ## Name(bytesConstRef _in); static PrecompiledExecutor __eth_registerPrecompiledFactory ## Name = ::dev::eth::PrecompiledRegistrar::registerExecutor(#Name, &__eth_registerPrecompiledFunction ## Name); static std::pair<bool, bytes> __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
}
}
57 changes: 35 additions & 22 deletions libethereum/Account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ void Account::setNewCode(bytes&& _code)

namespace js = json_spirit;

namespace
{

uint64_t toUnsigned(js::mValue const& _v)
{
switch (_v.type())
Expand All @@ -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 {
Expand Down Expand Up @@ -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)));
}
}

Expand Down
Loading