Skip to content
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
6 changes: 5 additions & 1 deletion src/contract/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ set(CONTRACT_SOURCES
${CMAKE_SOURCE_DIR}/src/contract/common.cpp
${CMAKE_SOURCE_DIR}/src/contract/executioncontext.cpp
${CMAKE_SOURCE_DIR}/src/contract/evmcontractexecutor.cpp
${CMAKE_SOURCE_DIR}/src/contract/precompiles.cpp
${CMAKE_SOURCE_DIR}/src/contract/precompiles/ecrecover.cpp
${CMAKE_SOURCE_DIR}/src/contract/precompiles/sha256.cpp
${CMAKE_SOURCE_DIR}/src/contract/precompiles/ripemd160.cpp
${CMAKE_SOURCE_DIR}/src/contract/precompiles/blake2f.cpp
${CMAKE_SOURCE_DIR}/src/contract/precompiles/modexp.cpp
${CMAKE_SOURCE_DIR}/src/contract/precompiledcontractexecutor.cpp
${CMAKE_SOURCE_DIR}/src/contract/templates/ownable.cpp
${CMAKE_SOURCE_DIR}/src/contract/templates/erc20.cpp
Expand Down
26 changes: 26 additions & 0 deletions src/contract/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ namespace ABI {
template<> struct TypeName<std::string> { static std::string get() { return "string"; }};
template<> struct TypeName<Hash> { static std::string get() { return "bytes32"; }};

template<size_t N> struct TypeName<FixedBytes<N>> { static std::string get() { return "bytes" + std::to_string(N); } };

/// Enum types are encoded as uint8_t
template<typename T>
requires std::is_enum_v<T> struct TypeName<T> {
Expand Down Expand Up @@ -414,6 +416,17 @@ namespace ABI {
return len;
}
};

template <size_t N> struct TypeEncoder<FixedBytes<N>> {
static_assert(N <= 32);

static Bytes encode(const FixedBytes<N>& bytes) {
Bytes result(32);
std::ranges::copy(bytes, result.begin());
return result;
}
};

template <> struct TypeEncoder<std::string> {
static Bytes encode(const std::string& str) {
View<Bytes> bytes = Utils::create_view_span(str);
Expand Down Expand Up @@ -818,6 +831,19 @@ namespace ABI {
}
};

template<size_t N> struct TypeDecoder<FixedBytes<N>> {
static_assert(N <= 32);

static FixedBytes<N> decode(const View<Bytes>& bytes, uint64_t& index) {
if (index + 32 > bytes.size())
throw std::length_error("Data too short for bytes");

FixedBytes<N> result;
std::ranges::copy(bytes.subspan(index, N), result.begin());
return result;
}
};

template <> struct TypeDecoder<std::string> {
static std::string decode(const View<Bytes>& bytes, uint64_t& index) {
if (index + 32 > bytes.size()) throw std::length_error("Data too short for string 1");
Expand Down
1 change: 1 addition & 0 deletions src/contract/gas.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef BDK_MESSAGES_GAS_H
#define BDK_MESSAGES_GAS_H

#include "utils/evmcconv.h"
#include "outofgas.h"

class Gas {
Expand Down
49 changes: 38 additions & 11 deletions src/contract/precompiledcontractexecutor.cpp
Original file line number Diff line number Diff line change
@@ -1,26 +1,53 @@
#include "precompiledcontractexecutor.h"
#include "precompiles.h"
#include "precompiles/precompiles.h"
#include "bytes/hex.h"
#include <algorithm>
#include <ranges>

constexpr Address RANDOM_GENERATOR_ADDRESS = bytes::hex("0x1000000000000000000000000000100000000001");
constexpr Address ECRECOVER_ADDRESS = bytes::hex("0x0000000000000000000000000000000000000001");

constexpr auto ECRECOVER_CALL_COST = 3'000;

Bytes PrecompiledContractExecutor::execute(EncodedStaticCallMessage& msg) {
if (msg.to() == RANDOM_GENERATOR_ADDRESS) {
return Utils::makeBytes(UintConv::uint256ToBytes(std::invoke(randomGen_)));
}

if (msg.to() == ECRECOVER_ADDRESS) {
msg.gas().use(ECRECOVER_CALL_COST);
const auto [hash, v, r, s] = ABI::Decoder::decodeData<Hash, uint8_t, Hash, Hash>(msg.input());
return ABI::Encoder::encodeData(ecrecover(hash, v, r, s));
}
// assuming isPrecompiled() was already called
switch (msg.to()[19]) {
case 0x01:
return precompiles::ecrecover(msg.input(), msg.gas());

case 0x02:
return precompiles::sha256(msg.input(), msg.gas());

case 0x03:
return ABI::Encoder::encodeData(Address(precompiles::ripemd160(msg.input(), msg.gas())));

case 0x04: {
const uint64_t dynamicGasCost = ((msg.input().size() + 31) / 32) * 3;
msg.gas().use(15 + dynamicGasCost);
return Bytes(msg.input());
return Bytes(msg.input());
}

case 0x05:
return precompiles::modexp(msg.input(), msg.gas());

throw DynamicException("Precompiled contract not found");
case 0x09:
return precompiles::blake2f(msg.input(), msg.gas());

default:
throw DynamicException("Precompiled contract not found");
}
}

bool PrecompiledContractExecutor::isPrecompiled(View<Address> address) const {
return address == RANDOM_GENERATOR_ADDRESS || address == ECRECOVER_ADDRESS;
if (address == RANDOM_GENERATOR_ADDRESS) {
return true;
}

if (std::ranges::any_of(address | std::views::take(19), [] (Byte b) { return b != 0; })) {
return false;
}

return address[19] <= 0x05 || address[19] == 0x09;
}
9 changes: 0 additions & 9 deletions src/contract/precompiles.h

This file was deleted.

224 changes: 224 additions & 0 deletions src/contract/precompiles/blake2f.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
#include "precompiles.h"
#include <ranges>
#include <algorithm>

namespace {

constexpr uint64_t iv[8] = {
0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1,
0x510E527FADE682D1, 0x9B05688C2B3E6C1F, 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179
};

constexpr Byte precomputed[10][16] = {
{0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15},
{14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3},
{11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4},
{7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8},
{9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13},
{2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9},
{12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11},
{13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10},
{6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5},
{10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0}
};

constexpr size_t sizeInBytes(const std::ranges::contiguous_range auto& range) {
return std::ranges::size(range) * sizeof(std::ranges::range_value_t<decltype(range)>);
}

} // namespace

void precompiles::blake2f(std::span<uint64_t, 8> h, std::span<const uint64_t, 16> m,
uint64_t c0, uint64_t c1, bool flag, uint32_t rounds) {

uint64_t v0 = h[0];
uint64_t v1 = h[1];
uint64_t v2 = h[2];
uint64_t v3 = h[3];
uint64_t v4 = h[4];
uint64_t v5 = h[5];
uint64_t v6 = h[6];
uint64_t v7 = h[7];
uint64_t v8 = iv[0];
uint64_t v9 = iv[1];
uint64_t v10 = iv[2];
uint64_t v11 = iv[3];
uint64_t v12 = iv[4];
uint64_t v13 = iv[5];
uint64_t v14 = iv[6];
uint64_t v15 = iv[7];

v12 ^= c0;
v13 ^= c1;

if (flag) {
v14 ^= 0xFFFFFFFFFFFFFFFF;
}

for (uint32_t i = 0; i < rounds; i++) {
const auto* s = precomputed[i % 10];

v0 += m[s[0]];
v0 += v4;
v12 ^= v0;
v12 = std::rotr(v12, 32);
v8 += v12;
v4 ^= v8;
v4 = std::rotr(v4, 24);
v1 += m[s[1]];
v1 += v5;
v13 ^= v1;
v13 = std::rotr(v13, 32);
v9 += v13;
v5 ^= v9;
v5 = std::rotr(v5, 24);
v2 += m[s[2]];
v2 += v6;
v14 ^= v2;
v14 = std::rotr(v14, 32);
v10 += v14;
v6 ^= v10;
v6 = std::rotr(v6, 24);
v3 += m[s[3]];
v3 += v7;
v15 ^= v3;
v15 = std::rotr(v15, 32);
v11 += v15;
v7 ^= v11;
v7 = std::rotr(v7, 24);

v0 += m[s[4]];
v0 += v4;
v12 ^= v0;
v12 = std::rotr(v12, 16);
v8 += v12;
v4 ^= v8;
v4 = std::rotr(v4, 63);
v1 += m[s[5]];
v1 += v5;
v13 ^= v1;
v13 = std::rotr(v13, 16);
v9 += v13;
v5 ^= v9;
v5 = std::rotr(v5, 63);
v2 += m[s[6]];
v2 += v6;
v14 ^= v2;
v14 = std::rotr(v14, 16);
v10 += v14;
v6 ^= v10;
v6 = std::rotr(v6, 63);
v3 += m[s[7]];
v3 += v7;
v15 ^= v3;
v15 = std::rotr(v15, 16);
v11 += v15;
v7 ^= v11;
v7 = std::rotr(v7, 63);

v0 += m[s[8]];
v0 += v5;
v15 ^= v0;
v15 = std::rotr(v15, 32);
v10 += v15;
v5 ^= v10;
v5 = std::rotr(v5, 24);
v1 += m[s[9]];
v1 += v6;
v12 ^= v1;
v12 = std::rotr(v12, 32);
v11 += v12;
v6 ^= v11;
v6 = std::rotr(v6, 24);
v2 += m[s[10]];
v2 += v7;
v13 ^= v2;
v13 = std::rotr(v13, 32);
v8 += v13;
v7 ^= v8;
v7 = std::rotr(v7, 24);
v3 += m[s[11]];
v3 += v4;
v14 ^= v3;
v14 = std::rotr(v14, 32);
v9 += v14;
v4 ^= v9;
v4 = std::rotr(v4, 24);

v0 += m[s[12]];
v0 += v5;
v15 ^= v0;
v15 = std::rotr(v15, 16);
v10 += v15;
v5 ^= v10;
v5 = std::rotr(v5, 63);
v1 += m[s[13]];
v1 += v6;
v12 ^= v1;
v12 = std::rotr(v12, 16);
v11 += v12;
v6 ^= v11;
v6 = std::rotr(v6, 63);
v2 += m[s[14]];
v2 += v7;
v13 ^= v2;
v13 = std::rotr(v13, 16);
v8 += v13;
v7 ^= v8;
v7 = std::rotr(v7, 63);
v3 += m[s[15]];
v3 += v4;
v14 ^= v3;
v14 = std::rotr(v14, 16);
v9 += v14;
v4 ^= v9;
v4 = std::rotr(v4, 63);
}

h[0] ^= v0 ^ v8;
h[1] ^= v1 ^ v9;
h[2] ^= v2 ^ v10;
h[3] ^= v3 ^ v11;
h[4] ^= v4 ^ v12;
h[5] ^= v5 ^ v13;
h[6] ^= v6 ^ v14;
h[7] ^= v7 ^ v15;
}

Bytes precompiles::blake2f(View<Bytes> input, Gas& gas) {
if (input.size() != 213) {
throw std::invalid_argument("Blake2F requires exacly 213 bytes");
}

std::array<uint64_t, 8> h;
std::array<uint64_t, 16> m;
std::array<uint64_t, 2> t;
uint8_t flag;
uint32_t rounds;

const Byte *in = input.data();

std::memcpy(&rounds, in, sizeof(rounds));
in += sizeof(rounds);

std::memcpy(&h, in, sizeInBytes(h));
in += sizeInBytes(h);

std::memcpy(&m, in, sizeInBytes(m));
in += sizeInBytes(m);

std::memcpy(&t, in, sizeInBytes(t));
in += sizeInBytes(t);

std::memcpy(&flag, in, sizeof(flag));

std::reverse(reinterpret_cast<Byte *>(&rounds), reinterpret_cast<Byte *>(&rounds + 1));

gas.use(rounds);

blake2f(h, m, t[0], t[1], flag, rounds);

Bytes output(sizeInBytes(h));
std::memcpy(output.data(), h.data(), output.size());
return output;
}
Loading
Loading