/* 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.h * @author Lefteris Karapetsas * @date 2015 */ #pragma once #include #include #include #include #ifdef __cplusplus #define __STDC_FORMAT_MACROS 1 #endif #include #include "endian.h" #include "ethash.h" #ifdef __cplusplus extern "C" { #endif // Maximum size for mutable part of DAG file name // 6 is for "full-R", the suffix of the filename // 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 (6 + 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_SIZE_MISMATCH, ///< DAG with revision/hash match, but file size was wrong. 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 }; // small hack for windows. I don't feel I should use va_args and forward just // to have this one function properly cross-platform abstracted #if defined(_WIN32) && !defined(__GNUC__) #define snprintf(...) sprintf_s(__VA_ARGS__) #endif /** * Logs a critical error in important parts of ethash. Should mostly help * figure out what kind of problem (I/O, memory e.t.c.) causes a NULL * ethash_full_t */ #ifdef ETHASH_PRINT_CRITICAL_OUTPUT #define ETHASH_CRITICAL(...) \ do \ { \ printf("ETHASH CRITICAL ERROR: "__VA_ARGS__); \ printf("\n"); \ fflush(stdout); \ } while (0) #else #define ETHASH_CRITICAL(...) #endif /** * Prepares io for ethash * * Create the DAG directory and the DAG file if they don't exist. * * @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[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] output_file If there was no failure then this will point to an open * file descriptor. User is responsible for closing it. * In the case of memo match then the file is open on read * mode, while on the case of mismatch a new file is created * on write mode * @param[in] file_size The size that the DAG file should have on disk * @param[out] force_create If true then there is no check to see if the file * already exists * @return For possible return values @see enum ethash_io_rc */ enum ethash_io_rc ethash_io_prepare( char const* dirname, ethash_h256_t const seedhash, FILE** output_file, uint64_t file_size, bool force_create ); /** * An fopen wrapper for no-warnings crossplatform fopen. * * Msvc compiler considers fopen to be insecure and suggests to use their * alternative. This is a wrapper for this alternative. Another way is to * #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does * not sound like a good idea. * * @param file_name The path to the file to open * @param mode Opening mode. Check fopen() * @return The FILE* or NULL in failure */ FILE* ethash_fopen(char const* file_name, char const* mode); /** * 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 * #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does * not sound like a good idea. * * @param des Destination buffer * @param dest_size Maximum size of the destination buffer. This is the * extra argument for the MSVC secure strncat * @param src Souce buffer * @param count Number of bytes to copy from source * @return If all is well returns the dest buffer. If there is an * error returns NULL */ char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count); /** * A cross-platform mkdir wrapper to create a directory or assert it's there * * @param dirname The full path of the directory to create * @return true if the directory was created or if it already * existed */ bool ethash_mkdir(char const* dirname); /** * Get a file's size * * @param[in] f The open file stream whose size to get * @param[out] size Pass a size_t by reference to contain the file size * @return true in success and false if there was a failure */ bool ethash_file_size(FILE* f, size_t* ret_size); /** * Get a file descriptor number from a FILE stream * * @param f The file stream whose fd to get * @return Platform specific fd handler */ int ethash_fileno(FILE* f); /** * Create the filename for the DAG. * * @param dirname The directory name in which the DAG file should reside * If it does not end with a directory separator it is appended. * @param filename The actual name of the file * @param filename_length The length of the filename in bytes * @return A char* containing the full name. User must deallocate. */ char* ethash_io_create_filename( char const* dirname, char const* filename, size_t filename_length ); /** * Gets the default directory name for the DAG depending on the system * * The spec defining this directory is here: https://github.com/ethereum/wiki/wiki/Ethash-DAG * * @param[out] strbuf A string buffer of sufficient size to keep the * null termninated string of the directory name * @param[in] buffsize Size of @a strbuf in bytes * @return true for success and false otherwise */ bool ethash_get_default_dirname(char* strbuf, size_t buffsize); static inline bool ethash_io_mutable_name( uint32_t revision, ethash_h256_t const* seed_hash, char* output ) { 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, "full-R%u-%016" PRIx64, revision, hash) >= 0; } #ifdef __cplusplus } #endif