From fbf7fc5b660dc91d4557de63958076fbd98f969a Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Mon, 13 Apr 2015 15:41:29 +0200 Subject: [PATCH] Memoization is done with the DAG filename --- ethash.h | 8 ++++++ internal.h | 2 ++ io.c | 39 --------------------------- io.h | 78 +++++++++++++++++++----------------------------------- io_posix.c | 36 +++++++++---------------- 5 files changed, 49 insertions(+), 114 deletions(-) diff --git a/ethash.h b/ethash.h index 4c38ea409..a92dd5b08 100644 --- a/ethash.h +++ b/ethash.h @@ -67,6 +67,14 @@ static inline void ethash_h256_reset(ethash_h256_t *hash) memset(hash, 0, 32); } +// convenience macro to statically initialize an h256_t +// usage: +// ethash_h256_t a = ethash_h256_static_init(1, 2, 3, ... ) +// have to provide all 32 values. If you don't provide all the rest +// will simply be unitialized (not guranteed to be 0) +#define ethash_h256_static_init(...) \ + {.b = {__VA_ARGS__} } + struct ethash_light; typedef struct ethash_light* ethash_light_t; struct ethash_full; diff --git a/internal.h b/internal.h index 95e132d7c..fadcf3fe7 100644 --- a/internal.h +++ b/internal.h @@ -2,6 +2,7 @@ #include "compiler.h" #include "endian.h" #include "ethash.h" +#include #define ENABLE_SSE 0 @@ -35,6 +36,7 @@ struct ethash_light { }; struct ethash_full { + FILE *file; ethash_cache *cache; node *data; ethash_callback_t callback; diff --git a/io.c b/io.c index f1a9d7012..0a1b8731a 100644 --- a/io.c +++ b/io.c @@ -22,9 +22,6 @@ #include #include -// silly macro to save some typing -#define PASS_ARR(c_) (c_), sizeof(c_) - static bool ethash_io_write_file(char const *dirname, char const* filename, size_t filename_length, @@ -51,39 +48,3 @@ free_name: free(fullname); return ret; } - -bool ethash_io_write(char const *dirname, - ethash_params const* params, - ethash_h256_t seedhash, - void const* cache, - uint8_t **data, - uint64_t *data_size) -{ - char info_buffer[DAG_MEMO_BYTESIZE]; - // allocate the bytes - uint8_t *temp_data_ptr = malloc((size_t)params->full_size); - if (!temp_data_ptr) { - goto end; - } - ethash_compute_full_data(temp_data_ptr, params, cache); - - if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, (size_t)params->full_size)) { - goto fail_free; - } - - ethash_io_serialize_info(REVISION, seedhash, info_buffer); - if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) { - goto fail_free; - } - - *data = temp_data_ptr; - *data_size = params->full_size; - return true; - -fail_free: - free(temp_data_ptr); -end: - return false; -} - -#undef PASS_ARR diff --git a/io.h b/io.h index 1884ed193..29c8eea1b 100644 --- a/io.h +++ b/io.h @@ -23,67 +23,41 @@ #include #include #include +#include "endian.h" #include "ethash.h" #ifdef __cplusplus extern "C" { #endif - -static const char DAG_FILE_NAME[] = "full"; -static const char DAG_MEMO_NAME[] = "full.info"; -// MSVC thinks that "static const unsigned int" is not a compile time variable. Sorry for the #define :( -#define DAG_MEMO_BYTESIZE 36 - +// Maximum size for mutable part of DAG file name +// 10 is for maximum number of digits of a uint32_t (for REVISION) +// 1 is for _ and 16 is for the first 16 hex digits for first 8 bytes of +// the seedhash and last 1 is for the null terminating character +// Reference: https://github.com/ethereum/wiki/wiki/Ethash-DAG +#define DAG_MUTABLE_NAME_MAX_SIZE (10 + 1 + 16 + 1) /// Possible return values of @see ethash_io_prepare enum ethash_io_rc { ETHASH_IO_FAIL = 0, ///< There has been an IO failure - ETHASH_IO_MEMO_MISMATCH, ///< Memo file either did not exist or there was content mismatch - ETHASH_IO_MEMO_MATCH, ///< Memo file existed and contents matched. No need to do anything + ETHASH_IO_MEMO_MISMATCH, ///< The DAG file did not exist or there was revision/hash mismatch + ETHASH_IO_MEMO_MATCH, ///< DAG file existed and revision/hash matched. No need to do anything }; /** * Prepares io for ethash * - * Create the DAG directory if it does not exist, and check if the memo file matches. - * If it does not match then it's deleted to pave the way for @ref ethash_io_write() + * Create the DAG directory and the DAG file if they don't exist. * - * @param dirname A null terminated c-string of the path of the ethash + * @param[in] dirname A null terminated c-string of the path of the ethash * data directory. If it does not exist it's created. - * @param seedhash The seedhash of the current block number + * @param[in] seedhash The seedhash of the current block number, used in the + * naming of the file as can be seen from the spec at: + * https://github.com/ethereum/wiki/wiki/Ethash-DAG + * @param[out] f If the hash/revision combo matched then this will point + * to an opened file handler for that file. User will then + * have to close it. * @return For possible return values @see enum ethash_io_rc */ -enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_h256_t seedhash); - -/** - * Fully computes data and writes it to the file on disk. - * - * This function should be called after @see ethash_io_prepare() and only if - * its return value is @c ETHASH_IO_MEMO_MISMATCH. Will write both the full data - * and the memo file. - * - * @param[in] dirname A null terminated c-string of the path of the ethash - * data directory. Has to exist. - * @param[in] params An ethash_params object containing the full size - * and the cache size - * @param[in] seedhash The seedhash of the current block number - * @param[in] cache The cache data. Would have usually been calulated by - * @see ethash_prep_light(). - * @param[out] data Pass a pointer to uint8_t by reference here. If the - * function is succesfull then this point to the allocated - * data calculated by @see ethash_prep_full(). Memory - * ownership is transfered to the callee. Remember that - * you eventually need to free this with a call to free(). - * @param[out] data_size Pass a uint64_t by value. If the function is succesfull - * then this will contain the number of bytes allocated - * for @a data. - * @return True for success and false in case of failure. - */ -bool ethash_io_write(char const *dirname, - ethash_params const* params, - ethash_h256_t seedhash, - void const* cache, - uint8_t **data, - uint64_t *data_size); +enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_h256_t seedhash, FILE **f); /** * An fopen wrapper for no-warnings crossplatform fopen. @@ -99,7 +73,7 @@ bool ethash_io_write(char const *dirname, */ FILE *ethash_fopen(const char *file_name, const char *mode); /** - * An stncat wrapper for no-warnings crossplatform strncat. + * An strncat wrapper for no-warnings crossplatform strncat. * * Msvc compiler considers strncat to be insecure and suggests to use their * alternative. This is a wrapper for this alternative. Another way is to @@ -116,13 +90,15 @@ FILE *ethash_fopen(const char *file_name, const char *mode); */ char *ethash_strncat(char *dest, size_t dest_size, const char *src, size_t count); -static inline void ethash_io_serialize_info(uint32_t revision, - ethash_h256_t seed_hash, - char *output) +static inline bool ethash_io_mutable_name(uint32_t revision, + ethash_h256_t *seed_hash, + char *output) { - // if .info is only consumed locally we don't really care about endianess - memcpy(output, &revision, 4); - memcpy(output + 4, &seed_hash, 32); + uint64_t hash = *((uint64_t*)seed_hash); +#if LITTLE_ENDIAN == BYTE_ORDER + hash = ethash_swap_u64(hash); +#endif + return snprintf(output, DAG_MUTABLE_NAME_MAX_SIZE, "%u_%016lx", revision, hash) >= 0; } static inline char *ethash_io_create_filename(char const *dirname, diff --git a/io_posix.c b/io_posix.c index c2feee2b4..8fff0bb0d 100644 --- a/io_posix.c +++ b/io_posix.c @@ -37,10 +37,11 @@ char *ethash_strncat(char *dest, size_t dest_size, const char *src, size_t count return strlen(dest) + count + 1 <= dest_size ? strncat(dest, src, count) : NULL; } -enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_h256_t seedhash) +enum ethash_io_rc ethash_io_prepare(char const *dirname, + ethash_h256_t seedhash, + FILE **output_file) { - char read_buffer[DAG_MEMO_BYTESIZE]; - char expect_buffer[DAG_MEMO_BYTESIZE]; + char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE]; enum ethash_io_rc ret = ETHASH_IO_FAIL; // assert directory exists, full owner permissions and read/search for others @@ -49,38 +50,25 @@ enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_h256_t seedhash) goto end; } - char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME)); - if (!memofile) { + ethash_io_mutable_name(REVISION, &seedhash, mutable_name); + char *tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name)); + if (!tmpfile) { goto end; } - // try to open memo file - FILE *f = ethash_fopen(memofile, "rb"); + // try to open the file + FILE *f = fopen(tmpfile, "rb"); if (!f) { - // file does not exist, so no checking happens. All is fine. + // file does not exist, will need to be created ret = ETHASH_IO_MEMO_MISMATCH; goto free_memo; } - if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) { - goto close; - } - - ethash_io_serialize_info(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) { - goto close; - } - ret = ETHASH_IO_MEMO_MISMATCH; - } - ret = ETHASH_IO_MEMO_MATCH; -close: - fclose(f); + *output_file = f; free_memo: - free(memofile); + free(tmpfile); end: return ret; }