Skip to content

Commit

Permalink
Merge pull request ethereum#4028 from ethereum/eip198
Browse files Browse the repository at this point in the history
EIP198 - Bigint modexp precompiled contract
  • Loading branch information
gumb0 authored Apr 7, 2017
2 parents 9045a9f + aaf29c7 commit 43bae2c
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 31 deletions.
1 change: 1 addition & 0 deletions libethashseal/genesis/metropolisTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,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

0 comments on commit 43bae2c

Please sign in to comment.