This repository has been archived by the owner on Oct 28, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
aleth-precompiles library #5855
Open
gumb0
wants to merge
10
commits into
master
Choose a base branch
from
libaleth-precompiles
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
49b3d65
Introduce aleth-precompiles EVMC module library and use it in Executive
gumb0 811a339
Add to EVMC class new exec method to execute without access to state
gumb0 e1a95d8
Add the rest of precompiles to libaleth-precompiles
gumb0 eb1db33
Remove unnecessary code
gumb0 f273ee8
Fix delegate calls to precompiles and gas calculation in case of error
gumb0 96aa5d2
Fix returning out of gas
gumb0 ba28bf7
Don't recreate PrecompilesVM on each call
gumb0 c570e3b
PrecompilesVM returns EVMC_REJECTD when address out of precompile range
gumb0 1a02c4a
Remove old precompile definitions and PrecompileRegistrar
gumb0 2f1e907
Add dependency libraries to libaleth-precompiles CMakeLists.txt
gumb0 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <aleth/version.h> | ||
#include <evmc/evmc.hpp> | ||
#include <libdevcore/Common.h> | ||
#include <libdevcore/SHA3.h> | ||
#include <libdevcrypto/Blake2.h> | ||
#include <libdevcrypto/Common.h> | ||
#include <libdevcrypto/Hash.h> | ||
#include <libdevcrypto/LibSnark.h> | ||
|
||
namespace dev | ||
{ | ||
namespace | ||
{ | ||
using Pricer = bigint (*)(bytesConstRef, evmc_revision); | ||
using Executor = std::pair<bool, bytes> (*)(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<bool, bytes> 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<bool, bytes> sha256(bytesConstRef _in) | ||
{ | ||
return {true, dev::sha256(_in).asBytes()}; | ||
} | ||
|
||
bigint ripemd160Price(bytesConstRef _in, evmc_revision) | ||
{ | ||
return linearPrice(600, 120, _in); | ||
} | ||
|
||
std::pair<bool, bytes> 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<bool, bytes> 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<size_t>::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<bigint>(cropped); | ||
// shift as if we had right-padding zeroes | ||
assert(count - cropped.count() <= std::numeric_limits<size_t>::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<bigint>(adjustedExpLength, 1) / 20; | ||
} | ||
|
||
std::pair<bool, bytes> 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<size_t>::max() / 8); // Otherwise gas should be too | ||
// expensive. | ||
assert(baseLength <= std::numeric_limits<size_t>::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<size_t>::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<bool, bytes> 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<bool, bytes> 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<bool, bytes> alt_bn128_pairing_product(bytesConstRef _in) | ||
{ | ||
return dev::crypto::alt_bn128_pairing_product(_in); | ||
} | ||
|
||
bigint blake2CompressionPrice(bytesConstRef _in, evmc_revision) | ||
{ | ||
auto const rounds = fromBigEndian<uint32_t>(_in.cropped(0, 4)); | ||
return rounds; | ||
} | ||
|
||
std::pair<bool, bytes> 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<uint32_t>(_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<Pricer, Executor> 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<int64_t>(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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <evmc/evmc.h> | ||
#include <evmc/utils.h> | ||
|
||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
EVMC_EXPORT struct evmc_vm* evmc_create_aleth_precompiles_vm() EVMC_NOEXCEPT; | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the library name is
aleth-precompiles
this should be namedevmc_create_aleth_precompiles
for EVMC loader to work.