Skip to content

Commit

Permalink
Merge pull request #187 from chfast/error_code
Browse files Browse the repository at this point in the history
Use error code to provide more information about verification failure
  • Loading branch information
chfast authored Oct 4, 2021
2 parents 16696ce + c127933 commit d39e474
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 30 deletions.
3 changes: 2 additions & 1 deletion bindings/python/ethash/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ def verify(epoch_number, header_hash, mix_hash, nonce, boundary):
c_boundary = ffi.new('union ethash_hash256*')
c_boundary[0].str = boundary

return lib.ethash_verify(ctx, c_header_hash, c_mix_hash, nonce, c_boundary)
ec = lib.ethash_verify(ctx, c_header_hash, c_mix_hash, nonce, c_boundary)
return ec == 0
26 changes: 24 additions & 2 deletions include/ethash/ethash.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ extern "C" {
#define ETHASH_FULL_DATASET_ITEM_SIZE 128
#define ETHASH_NUM_DATASET_ACCESSES 64

/** Ethash error codes. */
enum ethash_errc
{
ETHASH_SUCCESS = 0,
ETHASH_INVALID_FINAL_HASH = 1,
ETHASH_INVALID_MIX_HASH = 2
};
typedef enum ethash_errc ethash_errc;


struct ethash_epoch_context
{
Expand Down Expand Up @@ -106,11 +115,24 @@ void ethash_destroy_epoch_context_full(struct ethash_epoch_context_full* context
struct ethash_result ethash_hash(const struct ethash_epoch_context* context,
const union ethash_hash256* header_hash, uint64_t nonce) NOEXCEPT;

bool ethash_verify(const struct ethash_epoch_context* context,
/**
* Verify Ethash validity of a header hash.
*
* @return Error code: ::ETHASH_SUCCESS if valid, ::ETHASH_INVALID_FINAL_HASH if the final hash is
* not within provided boundary, ::ETHASH_INVALID_MIX_HASH if the provided mix hash
* mismatches the computed one.
*/
ethash_errc ethash_verify(const struct ethash_epoch_context* context,
const union ethash_hash256* header_hash, const union ethash_hash256* mix_hash, uint64_t nonce,
const union ethash_hash256* boundary) NOEXCEPT;

bool ethash_verify_final_hash(const union ethash_hash256* header_hash,
/**
* Verify only the final hash. This can be performed quickly without accessing Ethash context.
*
* @return Error code: ::ETHASH_SUCCESS if valid, ::ETHASH_INVALID_FINAL_HASH if the final hash is
* not within provided boundary.
*/
ethash_errc ethash_verify_final_hash(const union ethash_hash256* header_hash,
const union ethash_hash256* mix_hash, uint64_t nonce,
const union ethash_hash256* boundary) NOEXCEPT;

Expand Down
54 changes: 51 additions & 3 deletions include/ethash/ethash.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@
#include <cstdint>
#include <cstring>
#include <memory>
#include <system_error>

namespace std
{
/// Template specialization of std::is_error_code_enum for ethash_errc.
/// This enabled implicit conversions from evmc::hex_errc to std::error_code.
template <>
struct is_error_code_enum<ethash_errc> : true_type
{
};
} // namespace std


namespace ethash
{
Expand Down Expand Up @@ -128,13 +140,13 @@ inline result hash(

result hash(const epoch_context_full& context, const hash256& header_hash, uint64_t nonce) noexcept;

inline bool verify_final_hash(const hash256& header_hash, const hash256& mix_hash, uint64_t nonce,
const hash256& boundary) noexcept
inline std::error_code verify_final_hash(const hash256& header_hash, const hash256& mix_hash,
uint64_t nonce, const hash256& boundary) noexcept
{
return ethash_verify_final_hash(&header_hash, &mix_hash, nonce, &boundary);
}

inline bool verify(const epoch_context& context, const hash256& header_hash,
inline std::error_code verify(const epoch_context& context, const hash256& header_hash,
const hash256& mix_hash, uint64_t nonce, const hash256& boundary) noexcept
{
return ethash_verify(&context, &header_hash, &mix_hash, nonce, &boundary);
Expand All @@ -157,4 +169,40 @@ search_result search(const epoch_context_full& context, const hash256& header_ha
/// @return The epoch number or -1 if not found.
int find_epoch_number(const hash256& seed) noexcept;


/// Obtains a reference to the static error category object for ethash errors.
inline const std::error_category& ethash_category() noexcept
{
struct ethash_category_impl : std::error_category
{
const char* name() const noexcept final { return "ethash"; }

std::string message(int ev) const final
{
switch (ev)
{
case ETHASH_SUCCESS:
return "";
case ETHASH_INVALID_FINAL_HASH:
return "invalid final hash";
case ETHASH_INVALID_MIX_HASH:
return "invalid mix hash";
default:
return "unknown error";
}
}
};

static ethash_category_impl category_instance;
return category_instance;
}
} // namespace ethash


/// Creates error_code object out of an Ethash error code value.
/// This is used by std::error_code to implement implicit conversion ethash_errc -> std::error_code,
/// therefore the definition is in the global namespace to match the definition of ethash_errc.
inline std::error_code make_error_code(ethash_errc errc) noexcept
{
return {errc, ethash::ethash_category()};
}
13 changes: 7 additions & 6 deletions lib/ethash/ethash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,22 +415,23 @@ ethash_result ethash_hash(
return {hash_final(seed, mix_hash), mix_hash};
}

bool ethash_verify_final_hash(const hash256* header_hash, const hash256* mix_hash, uint64_t nonce,
const hash256* boundary) noexcept
ethash_errc ethash_verify_final_hash(const hash256* header_hash, const hash256* mix_hash,
uint64_t nonce, const hash256* boundary) noexcept
{
const hash512 seed = hash_seed(*header_hash, nonce);
return less_equal(hash_final(seed, *mix_hash), *boundary);
return less_equal(hash_final(seed, *mix_hash), *boundary) ? ETHASH_SUCCESS :
ETHASH_INVALID_FINAL_HASH;
}

bool ethash_verify(const epoch_context* context, const hash256* header_hash,
ethash_errc ethash_verify(const epoch_context* context, const hash256* header_hash,
const hash256* mix_hash, uint64_t nonce, const hash256* boundary) noexcept
{
const hash512 seed = hash_seed(*header_hash, nonce);
if (!less_equal(hash_final(seed, *mix_hash), *boundary))
return false;
return ETHASH_INVALID_FINAL_HASH;

const hash256 expected_mix_hash = hash_kernel(*context, seed, calculate_dataset_item_1024);
return equal(expected_mix_hash, *mix_hash);
return equal(expected_mix_hash, *mix_hash) ? ETHASH_SUCCESS : ETHASH_INVALID_MIX_HASH;
}

} // extern "C"
65 changes: 51 additions & 14 deletions test/unittests/test_ethash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,35 @@ TEST(ethash, revision)
EXPECT_EQ(ethash::revision, (std::string{"23"}));
}

TEST(ethash, error_code)
{
std::ostringstream os;
std::error_code ec = ETHASH_SUCCESS;
EXPECT_FALSE(ec);
EXPECT_EQ(ec.message(), "");

ec = ETHASH_INVALID_FINAL_HASH;
EXPECT_TRUE(ec);
EXPECT_EQ(ec.message(), "invalid final hash");
os.str({});
os << ec;
EXPECT_EQ(os.str(), "ethash:1");

ec = ETHASH_INVALID_MIX_HASH;
EXPECT_TRUE(ec);
EXPECT_EQ(ec.message(), "invalid mix hash");
os.str({});
os << ec;
EXPECT_EQ(os.str(), "ethash:2");

ec = static_cast<ethash_errc>(3);
EXPECT_TRUE(ec);
EXPECT_EQ(ec.message(), "unknown error");
os.str({});
os << ec;
EXPECT_EQ(os.str(), "ethash:3");
}

TEST(hash, hash256_from_bytes)
{
const uint8_t bytes[32] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
Expand Down Expand Up @@ -545,20 +574,27 @@ TEST(ethash, verify_hash_light)
EXPECT_EQ(to_hex(r.final_hash), t.final_hash_hex);
EXPECT_EQ(to_hex(r.mix_hash), t.mix_hash_hex);

bool v = verify_final_hash(header_hash, mix_hash, nonce, boundary);
EXPECT_TRUE(v);
v = verify(*context, header_hash, mix_hash, nonce, boundary);
EXPECT_TRUE(v);
auto ec = verify_final_hash(header_hash, mix_hash, nonce, boundary);
EXPECT_EQ(ec, ETHASH_SUCCESS);
EXPECT_FALSE(ec);
EXPECT_EQ(ec.category(), ethash_category());
ec = verify(*context, header_hash, mix_hash, nonce, boundary);
EXPECT_EQ(ec, ETHASH_SUCCESS);

const bool within_significant_boundary = r.final_hash.bytes[0] == 0;
if (within_significant_boundary)
{
v = verify_final_hash(header_hash, mix_hash, nonce + 1, boundary);
EXPECT_FALSE(v) << t.final_hash_hex;
}
ec = verify_final_hash(header_hash, mix_hash, nonce + 1, boundary);
EXPECT_EQ(ec, ETHASH_INVALID_FINAL_HASH) << t.final_hash_hex;

v = verify(*context, header_hash, mix_hash, nonce + 1, boundary);
EXPECT_FALSE(v);
ec = verify(*context, header_hash, mix_hash, nonce + 1, boundary);
EXPECT_EQ(ec, ETHASH_INVALID_FINAL_HASH);
}
else
{
ec = verify(*context, header_hash, mix_hash, nonce + 1, boundary);
EXPECT_EQ(ec, ETHASH_INVALID_MIX_HASH);
}
}
}

Expand Down Expand Up @@ -604,8 +640,8 @@ TEST(ethash, verify_final_hash_only)
const hash256 boundary =
to_hash256("000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

EXPECT_TRUE(verify_final_hash(header_hash, mix_hash, nonce, boundary));
EXPECT_FALSE(verify(context, header_hash, mix_hash, nonce, boundary));
EXPECT_EQ(verify_final_hash(header_hash, mix_hash, nonce, boundary), ETHASH_SUCCESS);
EXPECT_EQ(verify(context, header_hash, mix_hash, nonce, boundary), ETHASH_INVALID_MIX_HASH);
}

TEST(ethash, verify_boundary)
Expand All @@ -631,9 +667,10 @@ TEST(ethash, verify_boundary)
EXPECT_EQ(r.final_hash, boundary_eq);
EXPECT_EQ(to_hex(r.final_hash), to_hex(boundary_eq));

EXPECT_TRUE(verify(context, example_header_hash, r.mix_hash, nonce, boundary_eq));
EXPECT_TRUE(verify(context, example_header_hash, r.mix_hash, nonce, boundary_gt));
EXPECT_FALSE(verify(context, example_header_hash, r.mix_hash, nonce, boundary_lt));
EXPECT_EQ(verify(context, example_header_hash, r.mix_hash, nonce, boundary_eq), ETHASH_SUCCESS);
EXPECT_EQ(verify(context, example_header_hash, r.mix_hash, nonce, boundary_gt), ETHASH_SUCCESS);
EXPECT_EQ(verify(context, example_header_hash, r.mix_hash, nonce, boundary_lt),
ETHASH_INVALID_FINAL_HASH);
}

TEST(ethash_multithreaded, small_dataset)
Expand Down
8 changes: 4 additions & 4 deletions test/unittests/test_global_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ TEST(managed_multithreaded, verify_all)
const hash256 boundary = final_hash;
const int epoch_number = get_epoch_number(t.block_number);
auto& context = get_global_epoch_context(epoch_number);
const bool valid = verify(context, header_hash, mix_hash, nonce, boundary);
EXPECT_TRUE(valid);
const auto ec = verify(context, header_hash, mix_hash, nonce, boundary);
EXPECT_EQ(ec, ETHASH_SUCCESS);
}
});
}
Expand All @@ -88,7 +88,7 @@ TEST(managed_multithreaded, verify_all)

TEST(managed_multithreaded, verify_parallel)
{
std::vector<std::future<bool>> futures;
std::vector<std::future<std::error_code>> futures;

for (const auto& t : hash_test_cases)
{
Expand All @@ -105,7 +105,7 @@ TEST(managed_multithreaded, verify_parallel)
}

for (auto& f : futures)
EXPECT_TRUE(f.get());
EXPECT_EQ(f.get(), ETHASH_SUCCESS);
}

TEST(managed_multithreaded, get_epoch_context_random)
Expand Down

0 comments on commit d39e474

Please sign in to comment.