Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native implementation of BLAKE2bf precompile #857

Merged
merged 3 commits into from
Apr 11, 2024
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
2 changes: 1 addition & 1 deletion circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ jobs:
command: >
EVMONE_PRECOMPILES_STUB=~/project/test/state/precompiles_stub.json
bin/evmone-blockchaintest
--gtest_filter='*:-VMTests/vmPerformance.*:*.*Call50000_sha256'
--gtest_filter='*:-VMTests/vmPerformance.*:*.*Call50000_sha256:*.CALLBlake2f_MaxRounds'
~/tests/BlockchainTests/GeneralStateTests
- run:
name: "Blockchain tests (ValidBlocks)"
Expand Down
2 changes: 2 additions & 0 deletions lib/evmone_precompiles/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ add_library(evmone::precompiles ALIAS evmone_precompiles)
target_link_libraries(evmone_precompiles PUBLIC evmc::evmc_cpp PRIVATE evmone::evmmax)
target_sources(
evmone_precompiles PRIVATE
blake2b.hpp
blake2b.cpp
bn254.hpp
bn254.cpp
ecc.hpp
Expand Down
74 changes: 74 additions & 0 deletions lib/evmone_precompiles/blake2b.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2024 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "blake2b.hpp"
#include <array>
#include <bit>

namespace evmone::crypto
{
void blake2b_compress(
uint32_t rounds, uint64_t h[8], const uint64_t m[16], const uint64_t t[2], bool last) noexcept
{
// Message Schedule SIGMA.
// https://datatracker.ietf.org/doc/html/rfc7693#section-2.7
static constexpr uint8_t sigma[10][16]{
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
};

// Mixing Function G.
// https://datatracker.ietf.org/doc/html/rfc7693#section-3.1
//
// The G primitive function mixes two input words, "x" and "y", into
// four words indexed by "a", "b", "c", and "d" in the working vector v[0..15].
static constexpr auto g = [](uint64_t v[16], size_t a, size_t b, size_t c, size_t d, uint64_t x,
uint64_t y) noexcept {
v[a] = v[a] + v[b] + x;
v[d] = std::rotr(v[d] ^ v[a], 32);
v[c] = v[c] + v[d];
v[b] = std::rotr(v[b] ^ v[c], 24);
v[a] = v[a] + v[b] + y;
v[d] = std::rotr(v[d] ^ v[a], 16);
v[c] = v[c] + v[d];
v[b] = std::rotr(v[b] ^ v[c], 63);
};

// Initialize local work vector v[0..15].
uint64_t v[16]{h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7], // First half from state.
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, // Second half from IV.
0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1 ^ t[0], // Low word of the offset.
0x9b05688c2b3e6c1f ^ t[1], // High word.
0x1f83d9abfb41bd6b ^ (0 - uint64_t{last}), // Last block flag? Invert all bits.
0x5be0cd19137e2179};

// Cryptographic mixing.
for (size_t i = 0; i < rounds; ++i)
{
// Message word selection permutation for this round.
const auto& s = sigma[i % std::size(sigma)];

g(v, 0, 4, 8, 12, m[s[0]], m[s[1]]);
g(v, 1, 5, 9, 13, m[s[2]], m[s[3]]);
g(v, 2, 6, 10, 14, m[s[4]], m[s[5]]);
g(v, 3, 7, 11, 15, m[s[6]], m[s[7]]);
g(v, 0, 5, 10, 15, m[s[8]], m[s[9]]);
g(v, 1, 6, 11, 12, m[s[10]], m[s[11]]);
g(v, 2, 7, 8, 13, m[s[12]], m[s[13]]);
g(v, 3, 4, 9, 14, m[s[14]], m[s[15]]);
}

for (size_t i = 0; i < 8; ++i) // XOR the two halves.
h[i] ^= v[i] ^ v[i + 8];
}
} // namespace evmone::crypto
20 changes: 20 additions & 0 deletions lib/evmone_precompiles/blake2b.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2024 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <cstddef>
#include <cstdint>

namespace evmone::crypto
{
/// BLAKE2b compress function F.
/// https://datatracker.ietf.org/doc/html/rfc7693#section-3.2
///
/// @param rounds the number of rounds to perform
/// @param[in,out] h the state vector
/// @param m the block vector
/// @param t the 128-bit offset counter, {low word, high word}
/// @param last the final block indicator flag "f"
void blake2b_compress(
uint32_t rounds, uint64_t h[8], const uint64_t m[16], const uint64_t t[2], bool last) noexcept;
} // namespace evmone::crypto
37 changes: 35 additions & 2 deletions test/state/precompiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "precompiles.hpp"
#include "precompiles_cache.hpp"
#include "precompiles_internal.hpp"
#include <evmone_precompiles/blake2b.hpp>
#include <evmone_precompiles/bn254.hpp>
#include <evmone_precompiles/ripemd160.hpp>
#include <evmone_precompiles/secp256k1.hpp>
Expand Down Expand Up @@ -251,6 +252,38 @@
return {EVMC_SUCCESS, input_size};
}

ExecutionResult blake2bf_execute(const uint8_t* input, [[maybe_unused]] size_t input_size,
uint8_t* output, [[maybe_unused]] size_t output_size) noexcept
{
static_assert(std::endian::native == std::endian::little,
"blake2bf only works correctly on little-endian architectures");
assert(input_size >= 213);
assert(output_size >= 64);

const auto rounds = intx::be::unsafe::load<uint32_t>(input);

Check warning on line 263 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L263

Added line #L263 was not covered by tests
input += sizeof(rounds);

uint64_t h[8];
std::memcpy(h, input, sizeof(h));
input += sizeof(h);

uint64_t m[16];
std::memcpy(m, input, sizeof(m));
input += sizeof(m);

Check warning on line 272 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L272

Added line #L272 was not covered by tests

uint64_t t[2];
std::memcpy(t, input, sizeof(t));
input += sizeof(t);

Check warning on line 276 in test/state/precompiles.cpp

View check run for this annotation

Codecov / codecov/patch

test/state/precompiles.cpp#L276

Added line #L276 was not covered by tests

const auto f = *input;
if (f != 0 && f != 1) [[unlikely]]
return {EVMC_PRECOMPILE_FAILURE, 0};

crypto::blake2b_compress(rounds, h, m, t, f != 0);
std::memcpy(output, h, sizeof(h));
return {EVMC_SUCCESS, sizeof(h)};
}

namespace
{
struct PrecompileTraits
Expand All @@ -277,7 +310,7 @@
{ecadd_analyze, ecadd_execute},
{ecmul_analyze, ecmul_execute},
{ecpairing_analyze, dummy_execute<PrecompileId::ecpairing>},
{blake2bf_analyze, dummy_execute<PrecompileId::blake2bf>},
{blake2bf_analyze, blake2bf_execute},
{point_evaluation_analyze, dummy_execute<PrecompileId::point_evaluation>},
}};
#ifdef EVMONE_PRECOMPILES_SILKPRE
Expand All @@ -288,7 +321,7 @@
// tbl[static_cast<size_t>(PrecompileId::ecadd)].execute = silkpre_ecadd_execute;
// tbl[static_cast<size_t>(PrecompileId::ecmul)].execute = silkpre_ecmul_execute;
tbl[static_cast<size_t>(PrecompileId::ecpairing)].execute = silkpre_ecpairing_execute;
tbl[static_cast<size_t>(PrecompileId::blake2bf)].execute = silkpre_blake2bf_execute;
// tbl[static_cast<size_t>(PrecompileId::blake2bf)].execute = silkpre_blake2bf_execute;
#endif
return tbl;
}();
Expand Down
1 change: 1 addition & 0 deletions test/state/precompiles_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void Cache::insert(PrecompileId id, bytes_view input, const evmc::Result& result
{
case PrecompileId::ripemd160:
case PrecompileId::identity:
case PrecompileId::blake2bf:
return; // Do not cache.
default:
const auto input_hash = keccak256(input);
Expand Down
2 changes: 2 additions & 0 deletions test/state/precompiles_internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ ExecutionResult ecadd_execute(
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept;
ExecutionResult ecmul_execute(
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept;
ExecutionResult blake2bf_execute(
const uint8_t* input, size_t input_size, uint8_t* output, size_t output_size) noexcept;
} // namespace evmone::state
29 changes: 1 addition & 28 deletions test/state/precompiles_stub.json
Original file line number Diff line number Diff line change
Expand Up @@ -664,34 +664,7 @@
"da9978f0bc086d41a18d2b99f16c51583c2a38ee4c9346aa834dc50e66b4964d": null,
"e45e2aa1bb354e722458c3425e59c37bb6a544397e5b4e8a5814ec97283dbfc7": null
},
{
"0537dab8bcfa337395fdfbd203d054ac25c08722e9416a0ac259b779a2c87721": "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923",
"05bd989b8f157e23dbb02b8bc54d344a60558de44a7ee3f804cf1dfc9e82bbda": "fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615",
"13d12ef157bef72bdae72d1981ac6bc16ab74176742f7fb689ecef08d3d6bf0d": "d82c6a670dc90af9d7f77644eacbeddfed91b760c65c927871784abceaab3f813759733a1736254fb1cfc515dbfee467930955af56e27ee435f836fc3e65969f",
"2699f65d0bde17d17b834840e823616c33fb78e1a63f31fb6613bd1b08b888b4": "68790ca7594dd6fc28f0a86b7ddce0a225a8ea8fc2637f910eb71f6e54d9f8fa3e6302691015f11b15b755076d316823e6ce2ee4dd4aef60efc9189f6bd21bfd",
"30e40ece3441a7de9a9451e74fab98b455c4823f2485b2373a43a8b6c928a9fb": null,
"44b96fb380480dfeed4c297b46250c1ecc1a09e3e2fc2db0c1fb62fbe396eae4": "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735",
"47a0bef21f27e645f4fcee8531e8ee64c91472c3c49f6dc12ba19eca9ab49e05": "cf087b9b6ae026d358a13c577a04a4af4e65293be70b7cb2a7e00bd1317440af94b896bca7c282001aeb86cc307973096917897eadc97ed8eefb0d2b3c309a8b",
"5440bab497f09b1558b910e1f0df278ae9ac9fedc88daa3648115e004cddc8ce": "59c7c9896cdc9fda0a77bd41adba14bdd6cb47cbbd7c338482f5382d7be16ac44a2ddfe6833bf9a15737dd66469d2d0495d39a9cb3c8ed93152684d92a74f8bc",
"72834c36258c46436fcd43d43893fb32103ebd8827774c031a2e64decbe71d6a": "a2c1eb780a6e1249156fe0751e5d4687ea9357b0651c78df660ab004cb4773636298bbbc683e4a0261574b6d857a6a99e06b2eea50b16f86343d2625ff222b98",
"7b12d31724098d4879b481a4d462ebc685bda0f24252af010a62ccc6f4ffc60c": "bc5e888ed71b546da7b1506179bdd6c184a6410c40de33f9c3302074177978895dbe74144468aefe5c2afce693c62dbca99e5e076dd467fe90a41278b16d691e",
"7ded868868aebb7359c79ef488bed027b61633040eb7da66493e546245a4a662": "38dfb111db6c59c39c9bea26c8c872620d89dec22fd7da93c47d0708a3973f522858fd9c60fae53f366d7e2820040a8662b336de6d859764f20747acbb8999fe",
"87d33c8eabd067a14a94f02cb8ccfdb7ecff66aedbc0f0b13ae4ab0e06a5c4c7": "689419d2bf32b5a9901a2c733b9946727026a60d8773117eabb35f04a52cdcf1b8fb4473454cf03d46c36a10b3f784aae4dc80a24424960e66a8ad5a8c2bfb30",
"8dea8fe46ba1734457ae75ffdbb4ef76cc7dfccbb059a8eed9612f2369c2ea8f": "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421",
"8ff18398e840c4041ffc0bcafac552cfb6c3440c67ce4075f3d370e227d14476": "7476f1f8e159b30b156b0e9fffbaee5badbb45abb488ea9cfa04f60d3a0964087535a649e438ba993f03cfc0d8d8676a792030a996b6a6fde5c29108b6bfb871",
"93e382390cb513261e11e03f772356de95ce13c4ff3d1d91e8db0bf8e10f516c": "afd469613835ad3c75a54cd3087160eb308f5d6cd2151f1490f51b182dc5d13016428bf21e474e2921023bbeec971429210a51f0b63741583b0153fe8f6c27b6",
"98e4e90f2de200ae941b184f513fe7cb2a07db782ef99db0552da0a88e6c08ce": "94e963dcaa85d33dad6c0043b6700f5e227a2d8bed804bd16970e64fa6f1e16307399239bcddf968612c8c9ba953d9b173575c31ef269c3a8721cb9bf1c02012",
"ac284f457255542cfce05626f14734707a56698403c663e598a9305ce509b665": "74097ae7b16ffd18c742aee5c55dc89d54b6f1a8a19e6139ccfb38afba56b6b02cc35c441c19c21194fefb6841e72202f7c9d05eb9c3cfd8f94c67aa77d473c1",
"af40afaebdf61faac67fca7412c81973e9b053a85195ebc80a6f10c6a4b5d233": "5d6ff04d5ebaee5687d634613ab21e9a7d36f782033c74f91d562669aaf9d592c86346cb2df390243a952834306b389e656876a67934e2c023bce4918a016d4e",
"b7c8e0e46ba3b6a9b345fd795b9e328c51f3eb535bc9d775517a965af6b72521": "f3e89a60ec4b0b1854744984e421d22b82f181bd4601fb9b1726b2662da61c29dff09e75814acb2639fd79e56616e55fc135f8476f0302b3dc8d44e082eb83a8",
"bed4c5ce99729a220c87ca0ef21acbbe1274b29855df4cccba2510fd58ae1353": "a65aadbf393aad57c4b06d6471134c5c01002c23dfe8290e115e024e05bc1bf1084d1651de54a83902ed582cb8f2ba381c69687cceaecea0cd8fe5529f86686e",
"de7f79eb16d7357b75271fd4aa93af1237cbdd2297894cdec0033c1efb33ab2a": "bc112be5618b20d24be64c9e1c6efd63fea38cc79d53692fad6568b16e953eb6128c1ec8ffaf9a2d69e3cb043d6e11e1c7afd48573311052b6e7ec0960371186",
"ded8b44cbafe26629d64d2019a702d6eea0d6bfe68e313beacfa7935a9433640": "7df6f69476a03ae29e944814846460b058d1762fffe77f938ea723d1033de0d5bb1f8234bd73afaf955622fa2cdde95594577a8d53191908eb69b316a53c985b",
"eea1b2d664c19b902ea74b63cc5166346e9ce4de1de41dd5b72dbc75709871a1": "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b",
"f352cd3242546c7cb1ee7c4dbf7c4cba948d505150b17fae38bff05efb7018fb": "a8ef8236e5f48a74af375df15681d128457891c1cc4706f30747b2d40300b2f49d19f80fbd0945fd87736e1fc1ff10a80fd85a7aa5125154f3aaa3789ddff673",
"f6f043c5d533a3310a6ccbc5ec11199f7038a750a570ff5be9e715eb3614c0b1": "6d2ce9e534d50e18ff866ae92d70cceba79bbcd14c63819fe48752c8aca87a4bb7dcc230d22a4047f0486cfcfb50a17b24b2899eb8fca370f22240adb5170189",
"fb59d1e38c2473ebc92cd9d9bded35e13a214ab589f11fb07ffc0703784eb33d": "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b"
},
null,
{
"00921f4c0512d38d3e47f51c61fe0462d685d7165b7a330a79e072daacf56d3b": null,
"0431ab5e28f24e5ee4c929cb3c1385d8f13c1318b58d2301854c6f326fecbcbe": null,
Expand Down
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ target_sources(
exportable_fixture.hpp
exportable_fixture.cpp
instructions_test.cpp
precompiles_blake2b_test.cpp
precompiles_ripemd160_test.cpp
state_block_test.cpp
state_bloom_filter_test.cpp
Expand Down
80 changes: 80 additions & 0 deletions test/unittests/precompiles_blake2b_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// evmone: Fast Ethereum Virtual Machine implementation
// Copyright 2024 The evmone Authors.
// SPDX-License-Identifier: Apache-2.0

#include "../utils/utils.hpp"
#include <evmc/hex.hpp>
#include <evmone_precompiles/blake2b.hpp>
#include <gtest/gtest.h>
#include <array>
#include <cstring>

using evmone::crypto::blake2b_compress;

// Initialization Vector.
// https://datatracker.ietf.org/doc/html/rfc7693#appendix-C.2
constexpr std::array<uint64_t, 8> blake2b_iv{
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179,
};

TEST(blake2b_compress, reference_test)
{
// The reference test from the RFC.
// https://datatracker.ietf.org/doc/html/rfc7693#appendix-A
// with some extensions by modifying the "rounds" and "last" values.

auto h_init = blake2b_iv;
h_init[0] ^= 0x01010000 ^ /*outlen = */ 64;

const std::string_view data = "abc";
uint64_t m[16]{};
std::memcpy(m, data.data(), data.size());

const uint64_t t[2]{data.size(), 0};

auto h = h_init;
blake2b_compress(12, h.data(), m, t, true);
EXPECT_EQ(hex({reinterpret_cast<const uint8_t*>(h.data()), sizeof(h)}),
"ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1"
"7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923");

// https://github.com/ethereum/tests/blob/v13.2/src/GeneralStateTestsFiller/stPreCompiledContracts/blake2BFiller.yml#L301-L302
h = h_init;
blake2b_compress(12, h.data(), m, t, false);
EXPECT_EQ(hex({reinterpret_cast<const uint8_t*>(h.data()), sizeof(h)}),
"75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d28752"
"98743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735");

// https://github.com/ethereum/tests/blob/v13.2/src/GeneralStateTestsFiller/stPreCompiledContracts/blake2BFiller.yml#L268-L269
h = h_init;
blake2b_compress(0, h.data(), m, t, true);
EXPECT_EQ(hex({reinterpret_cast<const uint8_t*>(h.data()), sizeof(h)}),
"08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5"
"d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b");

// this gives the same result because the xor zeros out the "last" flag
h = h_init;
blake2b_compress(0, h.data(), m, t, false);
EXPECT_EQ(hex({reinterpret_cast<const uint8_t*>(h.data()), sizeof(h)}),
"08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5"
"d282e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b");
}

TEST(blake2b_compress, null_input)
{
std::array<uint64_t, 8> h{};
const uint64_t t[2]{};

// the data block is unused so be pass nullptr.
blake2b_compress(0, h.data(), nullptr, t, false);

// For null input you get the IV as the result.
EXPECT_EQ(h, blake2b_iv);
}