From 3efcc49bdfdc751dc17a1fc63f6337d1e7b00bad Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 31 Mar 2015 18:54:50 +0200 Subject: [PATCH] Upgrade to latest ethhash API. --- libethash/ethash.h | 78 +++++++++++++++++++++++++---------------- libethash/internal.c | 30 ++++++++-------- libethash/internal.h | 6 ++-- libethash/io.c | 2 +- libethash/io_posix.c | 2 +- libethash/io_win32.c | 2 +- libethcore/Ethasher.cpp | 25 ++++++++++--- libethcore/Ethasher.h | 27 ++++++-------- test/dagger.cpp | 4 +-- 9 files changed, 100 insertions(+), 76 deletions(-) diff --git a/libethash/ethash.h b/libethash/ethash.h index eb3097307..7594fc835 100644 --- a/libethash/ethash.h +++ b/libethash/ethash.h @@ -24,19 +24,20 @@ #include #include #include +#include #include "compiler.h" -#define REVISION 23 -#define DATASET_BYTES_INIT 1073741824U // 2**30 -#define DATASET_BYTES_GROWTH 8388608U // 2**23 -#define CACHE_BYTES_INIT 1073741824U // 2**24 -#define CACHE_BYTES_GROWTH 131072U // 2**17 -#define EPOCH_LENGTH 30000U -#define MIX_BYTES 128 -#define HASH_BYTES 64 -#define DATASET_PARENTS 256 -#define CACHE_ROUNDS 3 -#define ACCESSES 64 +#define ETHASH_REVISION 23 +#define ETHASH_DATASET_BYTES_INIT 1073741824U // 2**30 +#define ETHASH_DATASET_BYTES_GROWTH 8388608U // 2**23 +#define ETHASH_CACHE_BYTES_INIT 1073741824U // 2**24 +#define ETHASH_CACHE_BYTES_GROWTH 131072U // 2**17 +#define ETHASH_EPOCH_LENGTH 30000U +#define ETHASH_MIX_BYTES 128 +#define ETHASH_HASH_BYTES 64 +#define ETHASH_DATASET_PARENTS 256 +#define ETHASH_CACHE_ROUNDS 3 +#define ETHASH_ACCESSES 64 #ifdef __cplusplus extern "C" { @@ -61,34 +62,49 @@ static inline void ethash_params_init(ethash_params *params, const uint32_t bloc params->cache_size = ethash_get_cachesize(block_number); } -typedef struct ethash_cache { - void *mem; -} ethash_cache; +/*********************************** + * OLD API ************************* + *********************************** + ******************** (deprecated) * + ***********************************/ -void ethash_mkcache(ethash_cache *cache, ethash_params const *params, const uint8_t seed[32]); -void ethash_compute_full_data(void *mem, ethash_params const *params, ethash_cache const *cache); -void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); -void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number); +void ethash_mkcache(void *cache, ethash_params const *params, const uint8_t seed[32]); +void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); +void ethash_compute_full_data(void *mem, ethash_params const *params, void const *cache); +void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); -static inline void ethash_prep_light(void *cache, ethash_params const *params, const uint8_t seed[32]) { - ethash_cache c; - c.mem = cache; - ethash_mkcache(&c, params, seed); -} +/*********************************** + * NEW API ************************* + ***********************************/ + +// TODO: compute params and seed in ethash_new_light; it should take only block_number +// TODO: store params in ethash_light_t/ethash_full_t to avoid having to repass into compute/new_full + +typedef uint8_t const ethash_seedhash_t[32]; -static inline void ethash_compute_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { - ethash_cache c; - c.mem = (void *) cache; - ethash_light(ret, &c, params, header_hash, nonce); +typedef void const* ethash_light_t; +static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) { + void* ret = malloc(params->cache_size); + ethash_mkcache(ret, params, seed); + return ret; +} +static inline void ethash_compute_light(ethash_return_value *ret, ethash_light_t light, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { + ethash_light(ret, light, params, header_hash, nonce); +} +static inline void ethash_delete_light(ethash_light_t light) { + free((void*)light); } +typedef void const* ethash_full_t; +static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) { + void* ret = malloc(params->full_size); + ethash_compute_full_data(ret, params, light); + return ret; +} static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) { - ethash_cache c; - c.mem = (void *) cache; - ethash_compute_full_data(full, params, &c); + ethash_compute_full_data(full, params, cache); } - static inline void ethash_compute_full(ethash_return_value *ret, void const *full, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { ethash_full(ret, full, params, header_hash, nonce); } diff --git a/libethash/internal.c b/libethash/internal.c index ae9b95065..130ca13c3 100644 --- a/libethash/internal.c +++ b/libethash/internal.c @@ -38,13 +38,13 @@ #endif // WITH_CRYPTOPP uint64_t ethash_get_datasize(const uint32_t block_number) { - assert(block_number / EPOCH_LENGTH < 2048); - return dag_sizes[block_number / EPOCH_LENGTH]; + assert(block_number / ETHASH_EPOCH_LENGTH < 2048); + return dag_sizes[block_number / ETHASH_EPOCH_LENGTH]; } uint64_t ethash_get_cachesize(const uint32_t block_number) { - assert(block_number / EPOCH_LENGTH < 2048); - return cache_sizes[block_number / EPOCH_LENGTH]; + assert(block_number / ETHASH_EPOCH_LENGTH < 2048); + return cache_sizes[block_number / ETHASH_EPOCH_LENGTH]; } // Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014) @@ -63,7 +63,7 @@ void static ethash_compute_cache_nodes( SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64); } - for (unsigned j = 0; j != CACHE_ROUNDS; j++) { + for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) { for (unsigned i = 0; i != num_nodes; i++) { uint32_t const idx = nodes[i].words[0] % num_nodes; node data; @@ -85,10 +85,10 @@ void static ethash_compute_cache_nodes( } void ethash_mkcache( - ethash_cache *cache, + void *cache, ethash_params const *params, const uint8_t seed[32]) { - node *nodes = (node *) cache->mem; + node *nodes = (node *) cache; ethash_compute_cache_nodes(nodes, params, seed); } @@ -96,10 +96,10 @@ void ethash_calculate_dag_item( node *const ret, const unsigned node_index, const struct ethash_params *params, - const struct ethash_cache *cache) { + const void *cache) { uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node)); - node const *cache_nodes = (node const *) cache->mem; + node const *cache_nodes = (node const *) cache; node const *init = &cache_nodes[node_index % num_parent_nodes]; memcpy(ret, init, sizeof(node)); @@ -114,7 +114,7 @@ void ethash_calculate_dag_item( __m128i xmm3 = ret->xmm[3]; #endif - for (unsigned i = 0; i != DATASET_PARENTS; ++i) { + for (unsigned i = 0; i != ETHASH_DATASET_PARENTS; ++i) { uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes; node const *parent = &cache_nodes[parent_index]; @@ -150,7 +150,7 @@ void ethash_calculate_dag_item( void ethash_compute_full_data( void *mem, ethash_params const *params, - ethash_cache const *cache) { + void const *cache) { assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0); assert((params->full_size % sizeof(node)) == 0); node *full_nodes = mem; @@ -164,7 +164,7 @@ void ethash_compute_full_data( static void ethash_hash( ethash_return_value *ret, node const *full_nodes, - ethash_cache const *cache, + void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce) { @@ -201,7 +201,7 @@ static void ethash_hash( num_full_pages = (unsigned) (params->full_size / page_size); - for (unsigned i = 0; i != ACCESSES; ++i) { + for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) { uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages; for (unsigned n = 0; n != MIX_NODES; ++n) { @@ -275,7 +275,7 @@ void ethash_quick_hash( void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) { memset(seedhash, 0, 32); - const uint32_t epochs = block_number / EPOCH_LENGTH; + const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH; for (uint32_t i = 0; i < epochs; ++i) SHA3_256(seedhash, seedhash, 32); } @@ -295,6 +295,6 @@ void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params c ethash_hash(ret, (node const *) full_mem, NULL, params, previous_hash, nonce); } -void ethash_light(ethash_return_value *ret, ethash_cache const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { +void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { ethash_hash(ret, NULL, cache, params, previous_hash, nonce); } diff --git a/libethash/internal.h b/libethash/internal.h index ddd06e8f4..dec7e6b13 100644 --- a/libethash/internal.h +++ b/libethash/internal.h @@ -15,7 +15,7 @@ extern "C" { // compile time settings #define NODE_WORDS (64/4) -#define MIX_WORDS (MIX_BYTES/4) +#define MIX_WORDS (ETHASH_MIX_BYTES/4) #define MIX_NODES (MIX_WORDS / NODE_WORDS) #include @@ -34,7 +34,7 @@ void ethash_calculate_dag_item( node *const ret, const unsigned node_index, ethash_params const *params, - ethash_cache const *cache + void const *cache ); void ethash_quick_hash( @@ -45,4 +45,4 @@ void ethash_quick_hash( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/libethash/io.c b/libethash/io.c index dd4f1f9e8..0e935fa59 100644 --- a/libethash/io.c +++ b/libethash/io.c @@ -71,7 +71,7 @@ bool ethash_io_write(char const *dirname, goto fail_free; } - ethash_io_serialize_info(REVISION, seedhash, info_buffer); + ethash_io_serialize_info(ETHASH_REVISION, seedhash, info_buffer); if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) { goto fail_free; } diff --git a/libethash/io_posix.c b/libethash/io_posix.c index 693bdf750..9d9ccac69 100644 --- a/libethash/io_posix.c +++ b/libethash/io_posix.c @@ -56,7 +56,7 @@ enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seed goto close; } - ethash_io_serialize_info(REVISION, seedhash, expect_buffer); + ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer); if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) { // we have different memo contents so delete the memo file if (unlink(memofile) != 0) { diff --git a/libethash/io_win32.c b/libethash/io_win32.c index 2cabc939a..77367fdd9 100644 --- a/libethash/io_win32.c +++ b/libethash/io_win32.c @@ -53,7 +53,7 @@ enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seed goto close; } - ethash_io_serialize_info(REVISION, seedhash, expect_buffer); + ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer); if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) { // we have different memo contents so delete the memo file if (_unlink(memofile) != 0) { diff --git a/libethcore/Ethasher.cpp b/libethcore/Ethasher.cpp index 880bc139a..64db69187 100644 --- a/libethcore/Ethasher.cpp +++ b/libethcore/Ethasher.cpp @@ -41,7 +41,23 @@ using namespace eth; Ethasher* dev::eth::Ethasher::s_this = nullptr; -bytes const& Ethasher::cache(BlockInfo const& _header) +Ethasher::~Ethasher() +{ + while (!m_caches.empty()) + killCache(m_caches.begin()->first); +} + +void Ethasher::killCache(h256 const& _s) +{ + RecursiveGuard l(x_this); + if (m_caches.count(_s)) + { + ethash_delete_light(m_caches.at(_s)); + m_caches.erase(_s); + } +} + +void const* Ethasher::cache(BlockInfo const& _header) { RecursiveGuard l(x_this); if (_header.number > c_ethashEpochLength * 2048) @@ -54,8 +70,7 @@ bytes const& Ethasher::cache(BlockInfo const& _header) if (!m_caches.count(_header.seedHash())) { ethash_params p = params((unsigned)_header.number); - m_caches[_header.seedHash()].resize(p.cache_size); - ethash_prep_light(m_caches[_header.seedHash()].data(), &p, _header.seedHash().data()); + m_caches[_header.seedHash()] = ethash_new_light(&p, _header.seedHash().data()); } return m_caches[_header.seedHash()]; } @@ -84,7 +99,7 @@ bytesConstRef Ethasher::full(BlockInfo const& _header) ethash_params p = params((unsigned)_header.number); m_fulls[_header.seedHash()] = bytesRef(new byte[p.full_size], p.full_size); auto c = cache(_header); - ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c.data()); + ethash_prep_full(m_fulls[_header.seedHash()].data(), &p, c); writeFile(memoFile, m_fulls[_header.seedHash()]); writeFile(memoFile + ".info", info); } @@ -147,7 +162,7 @@ Ethasher::Result Ethasher::eval(BlockInfo const& _header, Nonce const& _nonce) { auto p = Ethasher::params(_header); ethash_return_value r; - ethash_compute_light(&r, Ethasher::get()->cache(_header).data(), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); + ethash_compute_light(&r, Ethasher::get()->cache(_header), &p, _header.headerHash(WithoutNonce).data(), (uint64_t)(u64)_nonce); // cdebug << "Ethasher::eval sha3(cache):" << sha3(Ethasher::get()->cache(_header)) << "hh:" << _header.headerHash(WithoutNonce) << "nonce:" << _nonce << " => " << h256(r.result, h256::ConstructFromPointer); return Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; } diff --git a/libethcore/Ethasher.h b/libethcore/Ethasher.h index c160d38da..a10c206d1 100644 --- a/libethcore/Ethasher.h +++ b/libethcore/Ethasher.h @@ -30,21 +30,8 @@ #include #include #include // TODO: REMOVE once everything merged into this class and an opaque API can be provided. -static const unsigned c_ethashRevision = REVISION; -static const unsigned c_ethashEpochLength = EPOCH_LENGTH; -#undef REVISION -#undef DATASET_BYTES_INIT -#undef DATASET_BYTES_GROWTH -#undef CACHE_BYTES_INIT -#undef CACHE_BYTES_GROWTH -#undef DAGSIZE_BYTES_INIT -#undef DAG_GROWTH -#undef EPOCH_LENGTH -#undef MIX_BYTES -#undef HASH_BYTES -#undef DATASET_PARENTS -#undef CACHE_ROUNDS -#undef ACCESSES +static const unsigned c_ethashRevision = ETHASH_REVISION; +static const unsigned c_ethashEpochLength = ETHASH_EPOCH_LENGTH; #include "Common.h" #include "BlockInfo.h" @@ -57,10 +44,14 @@ class Ethasher { public: Ethasher() {} + ~Ethasher(); static Ethasher* get() { if (!s_this) s_this = new Ethasher(); return s_this; } - bytes const& cache(BlockInfo const& _header); + using LightType = void const*; + using FullType = void const*; + + LightType cache(BlockInfo const& _header); bytesConstRef full(BlockInfo const& _header); static ethash_params params(BlockInfo const& _header); static ethash_params params(unsigned _n); @@ -104,9 +95,11 @@ public: }; private: + void killCache(h256 const& _s); + static Ethasher* s_this; RecursiveMutex x_this; - std::map m_caches; + std::map m_caches; std::map m_fulls; }; diff --git a/test/dagger.cpp b/test/dagger.cpp index f7230f705..4dda9c4fc 100644 --- a/test/dagger.cpp +++ b/test/dagger.cpp @@ -63,8 +63,8 @@ BOOST_AUTO_TEST_CASE(basic_test) unsigned cacheSize(o["cache_size"].get_int()); h256 cacheHash(o["cache_hash"].get_str()); - BOOST_REQUIRE_EQUAL(Ethasher::get()->cache(header).size(), cacheSize); - BOOST_REQUIRE_EQUAL(sha3(Ethasher::get()->cache(header)), cacheHash); + BOOST_REQUIRE_EQUAL(Ethasher::get()->params(header).cache_size, cacheSize); + BOOST_REQUIRE_EQUAL(sha3(bytesConstRef((byte const*)Ethasher::get()->cache(header), cacheSize)), cacheHash); #if TEST_FULL unsigned fullSize(o["full_size"].get_int());