Skip to content

Commit

Permalink
Add native implementation of BLAKE2bf precompile (#857)
Browse files Browse the repository at this point in the history
- Adds the implementation of the BLAKE2b compress function F following
the [RFC 7693](https://datatracker.ietf.org/doc/html/rfc7693).
- Implements [EIP-152](https://eips.ethereum.org/EIPS/eip-152) "BLAKE2
compression function `F` precompile" by exposing the
`evmone::crypto::blake2b_compress()`.
- Do not cache this precompile.
  • Loading branch information
battlmonstr authored Apr 11, 2024
2 parents cd1f3f6 + b2af042 commit 0b648e3
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 31 deletions.
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 @@ ExecutionResult identity_execute(const uint8_t* input, size_t input_size, uint8_
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);
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);

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

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 @@ inline constexpr auto traits = []() noexcept {
{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 @@ inline constexpr auto traits = []() noexcept {
// 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);
}

0 comments on commit 0b648e3

Please sign in to comment.