From 383865e1914284db8089051701655552286fe68d Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Wed, 25 Mar 2015 13:28:50 +0100 Subject: [PATCH] ethash_io_write and win32 specific code - adding ethash_io_write() function - only ethash_io_prepare() invoke system dependent functions so it's the only one going in system specific source files. --- CMakeLists.txt | 1 + io.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ io.h | 64 ++++++++++++++++++++++++++++++++--- io_posix.c | 30 +++++++--------- io_win32.c | 73 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 238 insertions(+), 22 deletions(-) create mode 100644 io.c create mode 100644 io_win32.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9287cb70b..c92240086 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ endif() set(FILES util.c util.h + io.c internal.c ethash.h endian.h diff --git a/io.c b/io.c new file mode 100644 index 000000000..c28047bb2 --- /dev/null +++ b/io.c @@ -0,0 +1,92 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ +/** @file io.c + * @author Lefteris Karapetsas + * @date 2015 + */ +#include "io.h" +#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, + void const* data, + size_t data_size) +{ + bool ret = false; + char *fullname = ethash_io_create_filename(dirname, filename, filename_length); + if (!fullname) { + return false; + } + FILE *f = fopen(fullname, "wb"); + if (!f) { + goto free_name; + } + if (data_size != fwrite(data, 1, data_size, f)) { + goto close; + } + + ret = true; +close: + fclose(f); +free_name: + free(fullname); + return ret; +} + +bool ethash_io_write(char const *dirname, + uint32_t block_number, + void const* cache, + uint8_t **data, + size_t *data_size) +{ + ethash_params p; + char info_buffer[DAG_MEMO_BYTESIZE]; + + p.cache_size = ethash_get_cachesize(block_number); + p.full_size = ethash_get_datasize(block_number); + // allocate the bytes + uint8_t *temp_data_ptr = malloc(p.full_size); + if (!(*temp_data_ptr)) { + goto end; + } + ethash_prep_full(temp_data_ptr, &p, cache); + + if (!ethash_io_write_file(dirname, PASS_ARR(DAG_FILE_NAME), temp_data_ptr, p.full_size)) { + goto fail_free; + } + + ethash_io_serialize_info(REVISION, block_number, 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 = p.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 55fea239e..82e910713 100644 --- a/io.h +++ b/io.h @@ -28,16 +28,57 @@ extern "C" { #endif +static const char DAG_FILE_NAME[] = "full"; +static const char DAG_MEMO_NAME[] = "full.info"; +static const unsigned int DAG_MEMO_BYTESIZE = 36; + +/// 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 +}; + /** * 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() + * * @param dirname A null terminated c-string of the path of the ethash * data directory. If it does not exist it's created. * @param block_number The current block number. Used in seedhash calculation. - * @returns True if all went fine, and false if there was any kind - * of error + * @return For possible return values @see enum ethash_io_rc */ -bool ethash_io_prepare(char const *dirname, uint32_t block_number); -void ethash_io_write(); +enum ethash_io_rc ethash_io_prepare(char const *dirname, uint32_t block_number); +/** + * 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] block_number 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 size_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, + uint32_t block_number, + void const* cache, + uint8_t **data, + size_t *data_size); + static inline void ethash_io_serialize_info(uint32_t revision, uint32_t block_number, char *output) @@ -47,6 +88,21 @@ static inline void ethash_io_serialize_info(uint32_t revision, ethash_get_seedhash((uint8_t*)(output + 4), block_number); } +static inline char *ethash_io_create_filename(char const *dirname, + char const* filename, + size_t filename_length) +{ + char *name = malloc(strlen(dirname) + filename_length); + if (!name) { + return NULL; + } + + name[0] = '\0'; + strcat(name, dirname); + strcat(name, filename); + return name; +} + #ifdef __cplusplus } diff --git a/io_posix.c b/io_posix.c index 2944aeb37..5eeedf199 100644 --- a/io_posix.c +++ b/io_posix.c @@ -18,24 +18,20 @@ * @author Lefteris Karapetsas * @date 2015 */ + #include "io.h" #include #include #include #include -#include #include #include -static const char DAG_FILE_NAME[] = "full"; -static const char DAG_MEMO_NAME[] = "full.info"; -static const unsigned int DAG_MEMO_BYTESIZE = 36; - -bool ethash_io_prepare(char const *dirname, uint32_t block_number) +enum ethash_io_rc ethash_io_prepare(char const *dirname, uint32_t block_number) { char read_buffer[DAG_MEMO_BYTESIZE]; char expect_buffer[DAG_MEMO_BYTESIZE]; - bool ret = false; + enum ethash_io_rc ret = ETHASH_IO_FAIL; // assert directory exists, full owner permissions and read/search for others int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); @@ -43,40 +39,38 @@ bool ethash_io_prepare(char const *dirname, uint32_t block_number) goto end; } - // try to open memo file - char *memofile = malloc(strlen(dirname) + sizeof(DAG_MEMO_NAME)); + char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME)); if (!memofile) { goto end; } + // try to open memo file FILE *f = fopen(memofile, "rb"); if (!f) { // file does not exist, so no checking happens. All is fine. - ret = true; + ret = ETHASH_IO_MEMO_MISMATCH; goto free_memo; } if (fread(read_buffer, 1, DAG_MEMO_BYTESIZE, f) != DAG_MEMO_BYTESIZE) { - goto free_memo; + goto close; } ethash_io_serialize_info(REVISION, block_number, 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 free_memo; + ret = ETHASH_IO_MEMO_MISMATCH; + goto close; } } - ret = true; + ret = ETHASH_IO_MEMO_MATCH; +close: + fclose(f); free_memo: free(memofile); end: return ret; } - -void ethash_io_write() -{ -} - diff --git a/io_win32.c b/io_win32.c new file mode 100644 index 000000000..8e99321bc --- /dev/null +++ b/io_win32.c @@ -0,0 +1,73 @@ +/* + This file is part of ethash. + + ethash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + ethash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with ethash. If not, see . +*/ +/** @file io_win32.c + * @author Lefteris Karapetsas + * @date 2015 + */ + +#include "io.h" +#include +#include +#include + +enum ethash_io_rc ethash_io_prepare(char const *dirname, uint32_t block_number) +{ + char read_buffer[DAG_MEMO_BYTESIZE]; + char expect_buffer[DAG_MEMO_BYTESIZE]; + enum ethash_io_rc ret = ETHASH_IO_FAIL; + + // assert directory exists + int rc = _mkdir(dirname); + if (rc == -1 && errno != EEXIST) { + goto end; + } + + char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME)); + if (!memofile) { + goto end; + } + + // try to open memo file + FILE *f = fopen(memofile, "rb"); + if (!f) { + // file does not exist, so no checking happens. All is fine. + 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, block_number, 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) { + ret = ETHASH_IO_MEMO_MISMATCH; + goto close; + } + } + + ret = ETHASH_IO_MEMO_MATCH; + +close: + fclose(f); +free_memo: + free(memofile); +end: + return ret; +}