Browse Source

Merge pull request #1816 from LefterisJP/ethash_reloaded

Ethash reloaded
cl-refactor
Gav Wood 10 years ago
parent
commit
0bb058aed6
  1. 4
      eth/main.cpp
  2. 4
      ethminer/main.cpp
  3. 4
      libethash-cl/ethash_cl_miner.cpp
  4. 9
      libethash/CMakeLists.txt
  5. 1476
      libethash/data_sizes.h
  6. 74
      libethash/endian.h
  7. 184
      libethash/ethash.h
  8. 7
      libethash/fnv.h
  9. 636
      libethash/internal.c
  10. 144
      libethash/internal.h
  11. 129
      libethash/io.c
  12. 201
      libethash/io.h
  13. 102
      libethash/io_posix.c
  14. 103
      libethash/io_win32.c
  15. 47
      libethash/mmap.h
  16. 84
      libethash/mmap_win32.c
  17. 194
      libethash/sha3.c
  18. 16
      libethash/sha3.h
  19. 15
      libethash/sha3_cryptopp.cpp
  20. 9
      libethash/sha3_cryptopp.h
  21. 4
      libethash/util.h
  22. 38
      libethash/util_win32.c
  23. 26
      libethcore/Ethash.cpp
  24. 3
      libethcore/Ethash.h
  25. 170
      libethcore/EthashAux.cpp
  26. 39
      libethcore/EthashAux.h
  27. 3
      libethcore/Exceptions.h
  28. 6
      test/libethcore/dagger.cpp

4
eth/main.cpp

@ -848,7 +848,7 @@ int main(int argc, char** argv)
auto boundary = bi.boundary(); auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i])); m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m); bi.nonce = h64(m);
auto r = EthashAux::eval(seedHash, powHash, bi.nonce); auto r = EthashAux::eval((uint64_t)bi.number, powHash, bi.nonce);
bool valid = r.value < boundary; bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl; cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl; cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
@ -857,7 +857,7 @@ int main(int argc, char** argv)
cout << " with seed as " << seedHash << endl; cout << " with seed as " << seedHash << endl;
if (valid) if (valid)
cout << "(mixHash = " << r.mixHash << ")" << endl; cout << "(mixHash = " << r.mixHash << ")" << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light(seedHash)->data()) << endl; cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light((uint64_t)bi.number)->data()) << endl;
exit(0); exit(0);
} }
catch (...) catch (...)

4
ethminer/main.cpp

@ -417,7 +417,7 @@ int main(int argc, char** argv)
auto boundary = bi.boundary(); auto boundary = bi.boundary();
m = boost::to_lower_copy(string(argv[++i])); m = boost::to_lower_copy(string(argv[++i]));
bi.nonce = h64(m); bi.nonce = h64(m);
auto r = EthashAux::eval(seedHash, powHash, bi.nonce); auto r = EthashAux::eval((uint64_t)bi.number, powHash, bi.nonce);
bool valid = r.value < boundary; bool valid = r.value < boundary;
cout << (valid ? "VALID :-)" : "INVALID :-(") << endl; cout << (valid ? "VALID :-)" : "INVALID :-(") << endl;
cout << r.value << (valid ? " < " : " >= ") << boundary << endl; cout << r.value << (valid ? " < " : " >= ") << boundary << endl;
@ -426,7 +426,7 @@ int main(int argc, char** argv)
cout << " with seed as " << seedHash << endl; cout << " with seed as " << seedHash << endl;
if (valid) if (valid)
cout << "(mixHash = " << r.mixHash << ")" << endl; cout << "(mixHash = " << r.mixHash << ")" << endl;
cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light(seedHash)->data()) << endl; cout << "SHA3( light(seed) ) = " << sha3(EthashAux::light((uint64_t)bi.number)->data()) << endl;
exit(0); exit(0);
} }
catch (...) catch (...)

4
libethash-cl/ethash_cl_miner.cpp

@ -120,9 +120,7 @@ unsigned ethash_cl_miner::get_num_devices(unsigned _platformId)
void ethash_cl_miner::finish() void ethash_cl_miner::finish()
{ {
if (m_queue()) if (m_queue())
{
m_queue.finish(); m_queue.finish();
}
} }
bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned workgroup_size, unsigned _platformId, unsigned _deviceId) bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned workgroup_size, unsigned _platformId, unsigned _deviceId)
@ -162,9 +160,7 @@ bool ethash_cl_miner::init(uint8_t const* _dag, uint64_t _dagSize, unsigned work
return false; return false;
} }
if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0) if (strncmp("OpenCL 1.1", device_version.c_str(), 10) == 0)
{
m_opencl_1_1 = true; m_opencl_1_1 = true;
}
// create context // create context
m_context = cl::Context(std::vector<cl::Device>(&device, &device + 1)); m_context = cl::Context(std::vector<cl::Device>(&device, &device + 1));

9
libethash/CMakeLists.txt

@ -10,8 +10,7 @@ if (NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
endif() endif()
set(FILES util.c set(FILES util.h
util.h
io.c io.c
internal.c internal.c
ethash.h ethash.h
@ -21,7 +20,7 @@ set(FILES util.c
data_sizes.h) data_sizes.h)
if (MSVC) if (MSVC)
list(APPEND FILES io_win32.c) list(APPEND FILES util_win32.c io_win32.c mmap_win32.c)
else() else()
list(APPEND FILES io_posix.c) list(APPEND FILES io_posix.c)
endif() endif()
@ -43,7 +42,3 @@ add_library(${LIBRARY} ${FILES})
if (CRYPTOPP_FOUND) if (CRYPTOPP_FOUND)
TARGET_LINK_LIBRARIES(${LIBRARY} ${CRYPTOPP_LIBRARIES}) TARGET_LINK_LIBRARIES(${LIBRARY} ${CRYPTOPP_LIBRARIES})
endif() endif()
if (NOT ETHASHCL)
install( TARGETS ${LIBRARY} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
endif ()

1476
libethash/data_sizes.h

File diff suppressed because it is too large

74
libethash/endian.h

@ -3,38 +3,6 @@
#include <stdint.h> #include <stdint.h>
#include "compiler.h" #include "compiler.h"
static const uint8_t BitReverseTable256[] =
{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
static inline uint32_t bitfn_swap32(uint32_t a) {
return (BitReverseTable256[a & 0xff] << 24) |
(BitReverseTable256[(a >> 8) & 0xff] << 16) |
(BitReverseTable256[(a >> 16) & 0xff] << 8) |
(BitReverseTable256[(a >> 24) & 0xff]);
}
static inline uint64_t bitfn_swap64(uint64_t a) {
return ((uint64_t) bitfn_swap32((uint32_t) (a >> 32))) |
(((uint64_t) bitfn_swap32((uint32_t) a)) << 32);
}
#if defined(__MINGW32__) || defined(_WIN32) #if defined(__MINGW32__) || defined(_WIN32)
# define LITTLE_ENDIAN 1234 # define LITTLE_ENDIAN 1234
# define BYTE_ORDER LITTLE_ENDIAN # define BYTE_ORDER LITTLE_ENDIAN
@ -53,22 +21,52 @@ static inline uint64_t bitfn_swap64(uint64_t a) {
# define BIG_ENDIAN 1234 # define BIG_ENDIAN 1234
# define BYTE_ORDER BIG_ENDIAN # define BYTE_ORDER BIG_ENDIAN
#else #else
# include <endian.h> # include <endian.h>
#endif
#if defined(_WIN32)
#include <stdlib.h>
#define ethash_swap_u32(input_) _byteswap_ulong(input_)
#define ethash_swap_u64(input_) _byteswap_uint64(input_)
#elif defined(__APPLE__)
#include <libkern/OSByteOrder.h>
#define ethash_swap_u32(input_) OSSwapInt32(input_)
#define ethash_swap_u64(input_) OSSwapInt64(input_)
#else // posix
#include <byteswap.h>
#define ethash_swap_u32(input_) __bswap_32(input_)
#define ethash_swap_u64(input_) __bswap_64(input_)
#endif #endif
#if LITTLE_ENDIAN == BYTE_ORDER #if LITTLE_ENDIAN == BYTE_ORDER
#define fix_endian32(x) (x) #define fix_endian32(dst_ ,src_) dst_ = src_
#define fix_endian64(x) (x) #define fix_endian32_same(val_)
#define fix_endian64(dst_, src_) dst_ = src_
#define fix_endian64_same(val_)
#define fix_endian_arr32(arr_, size_)
#define fix_endian_arr64(arr_, size_)
#elif BIG_ENDIAN == BYTE_ORDER #elif BIG_ENDIAN == BYTE_ORDER
#define fix_endian32(x) bitfn_swap32(x) #define fix_endian32(dst_, src_) dst_ = ethash_swap_u32(src_)
#define fix_endian64(x) bitfn_swap64(x) #define fix_endian32_same(val_) val_ = ethash_swap_u32(val_)
#define fix_endian64(dst_, src_) dst_ = ethash_swap_u64(src_
#define fix_endian64_same(val_) val_ = ethash_swap_u64(val_)
#define fix_endian_arr32(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
arr_[i_] = ethash_swap_u32(arr_[i_]); \
} \
while (0)
#define fix_endian_arr64(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
arr_[i_] = ethash_swap_u64(arr_[i_]); \
} \
while (0) \
#else #else
# error "endian not supported" # error "endian not supported"
#endif // BYTE_ORDER #endif // BYTE_ORDER

184
libethash/ethash.h

@ -24,7 +24,6 @@
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h>
#include "compiler.h" #include "compiler.h"
#define ETHASH_REVISION 23 #define ETHASH_REVISION 23
@ -38,101 +37,110 @@
#define ETHASH_DATASET_PARENTS 256 #define ETHASH_DATASET_PARENTS 256
#define ETHASH_CACHE_ROUNDS 3 #define ETHASH_CACHE_ROUNDS 3
#define ETHASH_ACCESSES 64 #define ETHASH_ACCESSES 64
#define ETHASH_DAG_MAGIC_NUM_SIZE 8
#define ETHASH_DAG_MAGIC_NUM 0xFEE1DEADBADDCAFE
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
typedef struct ethash_params { /// Type of a seedhash/blockhash e.t.c.
uint64_t full_size; // Size of full data set (in bytes, multiple of mix size (128)). typedef struct ethash_h256 { uint8_t b[32]; } ethash_h256_t;
uint64_t cache_size; // Size of compute cache (in bytes, multiple of node size (64)).
} ethash_params;
typedef struct ethash_return_value { // convenience macro to statically initialize an h256_t
uint8_t result[32]; // usage:
uint8_t mix_hash[32]; // ethash_h256_t a = ethash_h256_static_init(1, 2, 3, ... )
} ethash_return_value; // have to provide all 32 values. If you don't provide all the rest
// will simply be unitialized (not guranteed to be 0)
uint64_t ethash_get_datasize(const uint32_t block_number); #define ethash_h256_static_init(...) \
uint64_t ethash_get_cachesize(const uint32_t block_number); { {__VA_ARGS__} }
// initialize the parameters
static inline void ethash_params_init(ethash_params *params, const uint32_t block_number) {
params->full_size = ethash_get_datasize(block_number);
params->cache_size = ethash_get_cachesize(block_number);
}
/***********************************
* OLD API *************************
***********************************
******************** (deprecated) *
***********************************/
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number); struct ethash_light;
void ethash_mkcache(void *cache, ethash_params const *params, const uint8_t seed[32]); typedef struct ethash_light* ethash_light_t;
void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t header_hash[32], const uint64_t nonce); struct ethash_full;
void ethash_compute_full_data(void *mem, ethash_params const *params, void const *cache); typedef struct ethash_full* ethash_full_t;
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); typedef int(*ethash_callback_t)(unsigned);
/*********************************** typedef struct ethash_return_value {
* NEW API ************************* ethash_h256_t result;
***********************************/ ethash_h256_t mix_hash;
bool success;
// TODO: compute params and seed in ethash_new_light; it should take only block_number } ethash_return_value_t;
// 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]; * Allocate and initialize a new ethash_light handler
*
typedef void const* ethash_light_t; * @param block_number The block number for which to create the handler
static inline ethash_light_t ethash_new_light(ethash_params const* params, ethash_seedhash_t seed) { * @return Newly allocated ethash_light handler or NULL in case of
void* ret = malloc((size_t)params->cache_size); * ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
ethash_mkcache(ret, params, seed); */
return ret; ethash_light_t ethash_light_new(uint64_t block_number);
} /**
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) { * Frees a previously allocated ethash_light handler
ethash_light(ret, light, params, header_hash, nonce); * @param light The light handler to free
} */
static inline void ethash_delete_light(ethash_light_t light) { void ethash_light_delete(ethash_light_t light);
free((void*)light); /**
} * Calculate the light client data
*
typedef void const* ethash_full_t; * @param light The light client handler
static inline ethash_full_t ethash_new_full(ethash_params const* params, ethash_light_t light) { * @param header_hash The header hash to pack into the mix
void* ret = malloc((size_t)params->full_size); * @param nonce The nonce to pack into the mix
ethash_compute_full_data(ret, params, light); * @return an object of ethash_return_value_t holding the return values
return ret; */
} ethash_return_value_t ethash_light_compute(
static inline void ethash_prep_full(void *full, ethash_params const *params, void const *cache) { ethash_light_t light,
ethash_compute_full_data(full, params, cache); ethash_h256_t const header_hash,
} uint64_t nonce
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);
} /**
* Allocate and initialize a new ethash_full handler
/// @brief Compare two s256-bit big-endian values. *
/// @returns 1 if @a a is less than or equal to @a b, 0 otherwise. * @param light The light handler containing the cache.
/// Both parameters are 256-bit big-endian values. * @param callback A callback function with signature of @ref ethash_callback_t
static inline int ethash_leq_be256(const uint8_t a[32], const uint8_t b[32]) { * It accepts an unsigned with which a progress of DAG calculation
// Boundary is big endian * can be displayed. If all goes well the callback should return 0.
for (int i = 0; i < 32; i++) { * If a non-zero value is returned then DAG generation will stop.
if (a[i] == b[i]) * Be advised. A progress value of 100 means that DAG creation is
continue; * almost complete and that this function will soon return succesfully.
return a[i] < b[i]; * It does not mean that the function has already had a succesfull return.
} * @return Newly allocated ethash_full handler or NULL in case of
return 1; * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data()
} */
ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback);
/// Perofrms a cursory check on the validity of the nonce.
/// @returns 1 if the nonce may possibly be valid for the given header_hash & boundary. /**
/// @p boundary equivalent to 2 ^ 256 / block_difficulty, represented as a 256-bit big-endian. * Frees a previously allocated ethash_full handler
int ethash_preliminary_check_boundary( * @param full The light handler to free
const uint8_t header_hash[32], */
const uint64_t nonce, void ethash_full_delete(ethash_full_t full);
const uint8_t mix_hash[32], /**
const uint8_t boundary[32]); * Calculate the full client data
*
#define ethash_quick_check_difficulty ethash_preliminary_check_boundary * @param full The full client handler
#define ethash_check_difficulty ethash_leq_be256 * @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix
* @return An object of ethash_return_value to hold the return value
*/
ethash_return_value_t ethash_full_compute(
ethash_full_t full,
ethash_h256_t const header_hash,
uint64_t nonce
);
/**
* Get a pointer to the full DAG data
*/
void const* ethash_full_dag(ethash_full_t full);
/**
* Get the size of the DAG data
*/
uint64_t ethash_full_dag_size(ethash_full_t full);
/**
* Calculate the seedhash for a given block number
*/
ethash_h256_t ethash_get_seedhash(uint64_t block_number);
#ifdef __cplusplus #ifdef __cplusplus
} }

7
libethash/fnv.h

@ -29,10 +29,11 @@ extern "C" {
#define FNV_PRIME 0x01000193 #define FNV_PRIME 0x01000193
static inline uint32_t fnv_hash(const uint32_t x, const uint32_t y) { static inline uint32_t fnv_hash(uint32_t const x, uint32_t const y)
return x*FNV_PRIME ^ y; {
return x * FNV_PRIME ^ y;
} }
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

636
libethash/internal.c

@ -8,11 +8,11 @@
ethash is distributed in the hope that it will be useful, ethash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** @file internal.c /** @file internal.c
* @author Tim Hughes <tim@twistedfury.com> * @author Tim Hughes <tim@twistedfury.com>
@ -23,11 +23,15 @@
#include <assert.h> #include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stddef.h> #include <stddef.h>
#include <errno.h>
#include <math.h>
#include "mmap.h"
#include "ethash.h" #include "ethash.h"
#include "fnv.h" #include "fnv.h"
#include "endian.h" #include "endian.h"
#include "internal.h" #include "internal.h"
#include "data_sizes.h" #include "data_sizes.h"
#include "io.h"
#ifdef WITH_CRYPTOPP #ifdef WITH_CRYPTOPP
@ -37,264 +41,456 @@
#include "sha3.h" #include "sha3.h"
#endif // WITH_CRYPTOPP #endif // WITH_CRYPTOPP
uint64_t ethash_get_datasize(const uint32_t block_number) { uint64_t ethash_get_datasize(uint64_t const block_number)
assert(block_number / ETHASH_EPOCH_LENGTH < 2048); {
return dag_sizes[block_number / ETHASH_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) { uint64_t ethash_get_cachesize(uint64_t const block_number)
assert(block_number / ETHASH_EPOCH_LENGTH < 2048); {
return cache_sizes[block_number / ETHASH_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) // Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014)
// https://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf // https://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf
// SeqMemoHash(s, R, N) // SeqMemoHash(s, R, N)
void static ethash_compute_cache_nodes( bool static ethash_compute_cache_nodes(
node *const nodes, node* const nodes,
ethash_params const *params, uint64_t cache_size,
const uint8_t seed[32]) { ethash_h256_t const* seed
assert((params->cache_size % sizeof(node)) == 0); )
uint32_t const num_nodes = (uint32_t) (params->cache_size / sizeof(node)); {
if (cache_size % sizeof(node) != 0) {
SHA3_512(nodes[0].bytes, seed, 32); return false;
}
for (unsigned i = 1; i != num_nodes; ++i) { uint32_t const num_nodes = (uint32_t) (cache_size / sizeof(node));
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
} SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32);
for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) { for (uint32_t i = 1; i != num_nodes; ++i) {
for (unsigned i = 0; i != num_nodes; i++) { SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
uint32_t const idx = nodes[i].words[0] % num_nodes; }
node data;
data = nodes[(num_nodes - 1 + i) % num_nodes]; for (uint32_t j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (unsigned w = 0; w != NODE_WORDS; ++w) { for (uint32_t i = 0; i != num_nodes; i++) {
data.words[w] ^= nodes[idx].words[w]; uint32_t const idx = nodes[i].words[0] % num_nodes;
} node data;
SHA3_512(nodes[i].bytes, data.bytes, sizeof(data)); data = nodes[(num_nodes - 1 + i) % num_nodes];
} for (uint32_t w = 0; w != NODE_WORDS; ++w) {
} data.words[w] ^= nodes[idx].words[w];
}
// now perform endian conversion SHA3_512(nodes[i].bytes, data.bytes, sizeof(data));
#if BYTE_ORDER != LITTLE_ENDIAN }
for (unsigned w = 0; w != (num_nodes*NODE_WORDS); ++w) }
{
nodes->words[w] = fix_endian32(nodes->words[w]); // now perform endian conversion
} fix_endian_arr32(nodes->words, num_nodes * NODE_WORDS);
#endif return true;
}
void ethash_mkcache(
void *cache,
ethash_params const *params,
const uint8_t seed[32]) {
node *nodes = (node *) cache;
ethash_compute_cache_nodes(nodes, params, seed);
} }
void ethash_calculate_dag_item( void ethash_calculate_dag_item(
node *const ret, node* const ret,
const unsigned node_index, uint32_t node_index,
const struct ethash_params *params, ethash_light_t const light
const void *cache) { )
{
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node)); uint32_t num_parent_nodes = (uint32_t) (light->cache_size / sizeof(node));
node const *cache_nodes = (node const *) cache; node const* cache_nodes = (node const *) light->cache;
node const *init = &cache_nodes[node_index % num_parent_nodes]; node const* init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
memcpy(ret, init, sizeof(node)); ret->words[0] ^= node_index;
ret->words[0] ^= node_index; SHA3_512(ret->bytes, ret->bytes, sizeof(node));
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
#if defined(_M_X64) && ENABLE_SSE #if defined(_M_X64) && ENABLE_SSE
__m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME); __m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME);
__m128i xmm0 = ret->xmm[0]; __m128i xmm0 = ret->xmm[0];
__m128i xmm1 = ret->xmm[1]; __m128i xmm1 = ret->xmm[1];
__m128i xmm2 = ret->xmm[2]; __m128i xmm2 = ret->xmm[2];
__m128i xmm3 = ret->xmm[3]; __m128i xmm3 = ret->xmm[3];
#endif #endif
for (unsigned i = 0; i != ETHASH_DATASET_PARENTS; ++i) { for (uint32_t i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes; uint32_t parent_index = fnv_hash(node_index ^ i, ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index]; node const *parent = &cache_nodes[parent_index];
#if defined(_M_X64) && ENABLE_SSE #if defined(_M_X64) && ENABLE_SSE
{ {
xmm0 = _mm_mullo_epi32(xmm0, fnv_prime); xmm0 = _mm_mullo_epi32(xmm0, fnv_prime);
xmm1 = _mm_mullo_epi32(xmm1, fnv_prime); xmm1 = _mm_mullo_epi32(xmm1, fnv_prime);
xmm2 = _mm_mullo_epi32(xmm2, fnv_prime); xmm2 = _mm_mullo_epi32(xmm2, fnv_prime);
xmm3 = _mm_mullo_epi32(xmm3, fnv_prime); xmm3 = _mm_mullo_epi32(xmm3, fnv_prime);
xmm0 = _mm_xor_si128(xmm0, parent->xmm[0]); xmm0 = _mm_xor_si128(xmm0, parent->xmm[0]);
xmm1 = _mm_xor_si128(xmm1, parent->xmm[1]); xmm1 = _mm_xor_si128(xmm1, parent->xmm[1]);
xmm2 = _mm_xor_si128(xmm2, parent->xmm[2]); xmm2 = _mm_xor_si128(xmm2, parent->xmm[2]);
xmm3 = _mm_xor_si128(xmm3, parent->xmm[3]); xmm3 = _mm_xor_si128(xmm3, parent->xmm[3]);
// have to write to ret as values are used to compute index // have to write to ret as values are used to compute index
ret->xmm[0] = xmm0; ret->xmm[0] = xmm0;
ret->xmm[1] = xmm1; ret->xmm[1] = xmm1;
ret->xmm[2] = xmm2; ret->xmm[2] = xmm2;
ret->xmm[3] = xmm3; ret->xmm[3] = xmm3;
} }
#else #else
{ {
for (unsigned w = 0; w != NODE_WORDS; ++w) { for (unsigned w = 0; w != NODE_WORDS; ++w) {
ret->words[w] = fnv_hash(ret->words[w], parent->words[w]); ret->words[w] = fnv_hash(ret->words[w], parent->words[w]);
} }
} }
#endif #endif
} }
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
} }
void ethash_compute_full_data( bool ethash_compute_full_data(
void *mem, void* mem,
ethash_params const *params, uint64_t full_size,
void const *cache) { ethash_light_t const light,
assert((params->full_size % (sizeof(uint32_t) * MIX_WORDS)) == 0); ethash_callback_t callback
assert((params->full_size % sizeof(node)) == 0); )
node *full_nodes = mem; {
if (full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 ||
// now compute full nodes (full_size % sizeof(node)) != 0) {
for (unsigned n = 0; n != (params->full_size / sizeof(node)); ++n) { return false;
ethash_calculate_dag_item(&(full_nodes[n]), n, params, cache); }
} uint32_t const max_n = (uint32_t)(full_size / sizeof(node));
node* full_nodes = mem;
double const progress_change = 1.0f / max_n;
double progress = 0.0f;
// now compute full nodes
for (uint32_t n = 0; n != max_n; ++n) {
if (callback &&
n % (max_n / 100) == 0 &&
callback((unsigned int)(ceil(progress * 100.0f))) != 0) {
return false;
}
progress += progress_change;
ethash_calculate_dag_item(&(full_nodes[n]), n, light);
}
return true;
} }
static void ethash_hash( static bool ethash_hash(
ethash_return_value *ret, ethash_return_value_t* ret,
node const *full_nodes, node const* full_nodes,
void const *cache, ethash_light_t const light,
ethash_params const *params, uint64_t full_size,
const uint8_t header_hash[32], ethash_h256_t const header_hash,
const uint64_t nonce) { uint64_t const nonce
)
assert((params->full_size % MIX_WORDS) == 0); {
if (full_size % MIX_WORDS != 0) {
return false;
}
// pack hash and nonce together into first 40 bytes of s_mix
assert(sizeof(node) * 8 == 512);
node s_mix[MIX_NODES + 1];
memcpy(s_mix[0].bytes, &header_hash, 32);
fix_endian64(s_mix[0].double_words[4], nonce);
// compute sha3-512 hash and replicate across mix
SHA3_512(s_mix->bytes, s_mix->bytes, 40);
fix_endian_arr32(s_mix[0].words, 16);
node* const mix = s_mix + 1;
for (uint32_t w = 0; w != MIX_WORDS; ++w) {
mix->words[w] = s_mix[0].words[w % NODE_WORDS];
}
unsigned const page_size = sizeof(uint32_t) * MIX_WORDS;
unsigned const num_full_pages = (unsigned) (full_size / page_size);
for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) {
uint32_t const index = fnv_hash(s_mix->words[0] ^ i, mix->words[i % MIX_WORDS]) % num_full_pages;
for (unsigned n = 0; n != MIX_NODES; ++n) {
node const* dag_node;
if (full_nodes) {
dag_node = &full_nodes[MIX_NODES * index + n];
} else {
node tmp_node;
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light);
dag_node = &tmp_node;
}
// pack hash and nonce together into first 40 bytes of s_mix #if defined(_M_X64) && ENABLE_SSE
assert(sizeof(node) * 8 == 512); {
node s_mix[MIX_NODES + 1]; __m128i fnv_prime = _mm_set1_epi32(FNV_PRIME);
memcpy(s_mix[0].bytes, header_hash, 32); __m128i xmm0 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[0]);
__m128i xmm1 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[1]);
#if BYTE_ORDER != LITTLE_ENDIAN __m128i xmm2 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[2]);
s_mix[0].double_words[4] = fix_endian64(nonce); __m128i xmm3 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[3]);
#else mix[n].xmm[0] = _mm_xor_si128(xmm0, dag_node->xmm[0]);
s_mix[0].double_words[4] = nonce; mix[n].xmm[1] = _mm_xor_si128(xmm1, dag_node->xmm[1]);
mix[n].xmm[2] = _mm_xor_si128(xmm2, dag_node->xmm[2]);
mix[n].xmm[3] = _mm_xor_si128(xmm3, dag_node->xmm[3]);
}
#else
{
for (unsigned w = 0; w != NODE_WORDS; ++w) {
mix[n].words[w] = fnv_hash(mix[n].words[w], dag_node->words[w]);
}
}
#endif #endif
}
}
// compress mix
for (uint32_t w = 0; w != MIX_WORDS; w += 4) {
uint32_t reduction = mix->words[w + 0];
reduction = reduction * FNV_PRIME ^ mix->words[w + 1];
reduction = reduction * FNV_PRIME ^ mix->words[w + 2];
reduction = reduction * FNV_PRIME ^ mix->words[w + 3];
mix->words[w / 4] = reduction;
}
fix_endian_arr32(mix->words, MIX_WORDS / 4);
memcpy(&ret->mix_hash, mix->bytes, 32);
// final Keccak hash
SHA3_256(&ret->result, s_mix->bytes, 64 + 32); // Keccak-256(s + compressed_mix)
return true;
}
// compute sha3-512 hash and replicate across mix void ethash_quick_hash(
SHA3_512(s_mix->bytes, s_mix->bytes, 40); ethash_h256_t* return_hash,
ethash_h256_t const* header_hash,
#if BYTE_ORDER != LITTLE_ENDIAN uint64_t const nonce,
for (unsigned w = 0; w != 16; ++w) { ethash_h256_t const* mix_hash
s_mix[0].words[w] = fix_endian32(s_mix[0].words[w]); )
} {
#endif uint8_t buf[64 + 32];
memcpy(buf, header_hash, 32);
fix_endian64_same(nonce);
memcpy(&(buf[32]), &nonce, 8);
SHA3_512(buf, buf, 40);
memcpy(&(buf[64]), mix_hash, 32);
SHA3_256(return_hash, buf, 64 + 32);
}
node *const mix = s_mix + 1; ethash_h256_t ethash_get_seedhash(uint64_t block_number)
for (unsigned w = 0; w != MIX_WORDS; ++w) { {
mix->words[w] = s_mix[0].words[w % NODE_WORDS]; ethash_h256_t ret;
} ethash_h256_reset(&ret);
uint64_t const epochs = block_number / ETHASH_EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(&ret, (uint8_t*)&ret, 32);
return ret;
}
unsigned const bool ethash_quick_check_difficulty(
page_size = sizeof(uint32_t) * MIX_WORDS, ethash_h256_t const* header_hash,
num_full_pages = (unsigned) (params->full_size / page_size); uint64_t const nonce,
ethash_h256_t const* mix_hash,
ethash_h256_t const* difficulty
)
{
ethash_h256_t return_hash;
ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash);
return ethash_check_difficulty(&return_hash, difficulty);
}
ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed)
{
struct ethash_light *ret;
ret = calloc(sizeof(*ret), 1);
if (!ret) {
return NULL;
}
ret->cache = malloc((size_t)cache_size);
if (!ret->cache) {
goto fail_free_light;
}
node* nodes = (node*)ret->cache;
if (!ethash_compute_cache_nodes(nodes, cache_size, seed)) {
goto fail_free_cache_mem;
}
ret->cache_size = cache_size;
return ret;
fail_free_cache_mem:
free(ret->cache);
fail_free_light:
free(ret);
return NULL;
}
for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) { ethash_light_t ethash_light_new(uint64_t block_number)
uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages; {
ethash_h256_t seedhash = ethash_get_seedhash(block_number);
ethash_light_t ret;
ret = ethash_light_new_internal(ethash_get_cachesize(block_number), &seedhash);
ret->block_number = block_number;
return ret;
}
for (unsigned n = 0; n != MIX_NODES; ++n) { void ethash_light_delete(ethash_light_t light)
const node *dag_node = &full_nodes[MIX_NODES * index + n]; {
if (light->cache) {
free(light->cache);
}
free(light);
}
if (!full_nodes) { ethash_return_value_t ethash_light_compute_internal(
node tmp_node; ethash_light_t light,
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, params, cache); uint64_t full_size,
dag_node = &tmp_node; ethash_h256_t const header_hash,
} uint64_t nonce
)
{
ethash_return_value_t ret;
ret.success = true;
if (!ethash_hash(&ret, NULL, light, full_size, header_hash, nonce)) {
ret.success = false;
}
return ret;
}
#if defined(_M_X64) && ENABLE_SSE ethash_return_value_t ethash_light_compute(
{ ethash_light_t light,
__m128i fnv_prime = _mm_set1_epi32(FNV_PRIME); ethash_h256_t const header_hash,
__m128i xmm0 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[0]); uint64_t nonce
__m128i xmm1 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[1]); )
__m128i xmm2 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[2]); {
__m128i xmm3 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[3]); uint64_t full_size = ethash_get_datasize(light->block_number);
mix[n].xmm[0] = _mm_xor_si128(xmm0, dag_node->xmm[0]); return ethash_light_compute_internal(light, full_size, header_hash, nonce);
mix[n].xmm[1] = _mm_xor_si128(xmm1, dag_node->xmm[1]); }
mix[n].xmm[2] = _mm_xor_si128(xmm2, dag_node->xmm[2]);
mix[n].xmm[3] = _mm_xor_si128(xmm3, dag_node->xmm[3]);
}
#else
{
for (unsigned w = 0; w != NODE_WORDS; ++w) {
mix[n].words[w] = fnv_hash(mix[n].words[w], dag_node->words[w]);
}
}
#endif
}
}
// compress mix
for (unsigned w = 0; w != MIX_WORDS; w += 4) {
uint32_t reduction = mix->words[w + 0];
reduction = reduction * FNV_PRIME ^ mix->words[w + 1];
reduction = reduction * FNV_PRIME ^ mix->words[w + 2];
reduction = reduction * FNV_PRIME ^ mix->words[w + 3];
mix->words[w / 4] = reduction;
}
#if BYTE_ORDER != LITTLE_ENDIAN
for (unsigned w = 0; w != MIX_WORDS/4; ++w) {
mix->words[w] = fix_endian32(mix->words[w]);
}
#endif
memcpy(ret->mix_hash, mix->bytes, 32); static bool ethash_mmap(struct ethash_full* ret, FILE* f)
// final Keccak hash {
SHA3_256(ret->result, s_mix->bytes, 64 + 32); // Keccak-256(s + compressed_mix) int fd;
char* mmapped_data;
ret->file = f;
if ((fd = ethash_fileno(ret->file)) == -1) {
return false;
}
mmapped_data= mmap(
NULL,
(size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (mmapped_data == MAP_FAILED) {
return false;
}
ret->data = (node*)(mmapped_data + ETHASH_DAG_MAGIC_NUM_SIZE);
return true;
} }
void ethash_quick_hash( ethash_full_t ethash_full_new_internal(
uint8_t return_hash[32], char const* dirname,
const uint8_t header_hash[32], ethash_h256_t const seed_hash,
const uint64_t nonce, uint64_t full_size,
const uint8_t mix_hash[32]) { ethash_light_t const light,
ethash_callback_t callback
uint8_t buf[64 + 32]; )
memcpy(buf, header_hash, 32); {
#if BYTE_ORDER != LITTLE_ENDIAN struct ethash_full* ret;
nonce = fix_endian64(nonce); FILE *f = NULL;
#endif ret = calloc(sizeof(*ret), 1);
memcpy(&(buf[32]), &nonce, 8); if (!ret) {
SHA3_512(buf, buf, 40); return NULL;
memcpy(&(buf[64]), mix_hash, 32); }
SHA3_256(return_hash, buf, 64 + 32); ret->file_size = (size_t)full_size;
switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) {
case ETHASH_IO_FAIL:
goto fail_free_full;
case ETHASH_IO_MEMO_MATCH:
if (!ethash_mmap(ret, f)) {
goto fail_close_file;
}
return ret;
case ETHASH_IO_MEMO_SIZE_MISMATCH:
// if a DAG of same filename but unexpected size is found, silently force new file creation
if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) {
goto fail_free_full;
}
// fallthrough to the mismatch case here, DO NOT go through match
case ETHASH_IO_MEMO_MISMATCH:
if (!ethash_mmap(ret, f)) {
goto fail_close_file;
}
break;
}
if (!ethash_compute_full_data(ret->data, full_size, light, callback)) {
goto fail_free_full_data;
}
// after the DAG has been filled then we finalize it by writting the magic number at the beginning
if (fseek(f, 0, SEEK_SET) != 0) {
goto fail_free_full_data;
}
uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM;
if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
goto fail_free_full_data;
}
fflush(f); // make sure the magic number IS there
return ret;
fail_free_full_data:
// could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(ret->data, (size_t)full_size);
fail_close_file:
fclose(ret->file);
fail_free_full:
free(ret);
return NULL;
} }
void ethash_get_seedhash(uint8_t seedhash[32], const uint32_t block_number) { ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback)
memset(seedhash, 0, 32); {
const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH; char strbuf[256];
for (uint32_t i = 0; i < epochs; ++i) if (!ethash_get_default_dirname(strbuf, 256)) {
SHA3_256(seedhash, seedhash, 32); return NULL;
}
uint64_t full_size = ethash_get_datasize(light->block_number);
ethash_h256_t seedhash = ethash_get_seedhash(light->block_number);
return ethash_full_new_internal(strbuf, seedhash, full_size, light, callback);
} }
int ethash_preliminary_check_boundary( void ethash_full_delete(ethash_full_t full)
const uint8_t header_hash[32], {
const uint64_t nonce, // could check that munmap(..) == 0 but even if it did not can't really do anything here
const uint8_t mix_hash[32], munmap(full->data, (size_t)full->file_size);
const uint8_t difficulty[32]) { if (full->file) {
fclose(full->file);
}
free(full);
}
uint8_t return_hash[32]; ethash_return_value_t ethash_full_compute(
ethash_quick_hash(return_hash, header_hash, nonce, mix_hash); ethash_full_t full,
return ethash_leq_be256(return_hash, difficulty); ethash_h256_t const header_hash,
uint64_t nonce
)
{
ethash_return_value_t ret;
ret.success = true;
if (!ethash_hash(
&ret,
(node const*)full->data,
NULL,
full->file_size,
header_hash,
nonce)) {
ret.success = false;
}
return ret;
} }
void ethash_full(ethash_return_value *ret, void const *full_mem, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { void const* ethash_full_dag(ethash_full_t full)
ethash_hash(ret, (node const *) full_mem, NULL, params, previous_hash, nonce); {
return full->data;
} }
void ethash_light(ethash_return_value *ret, void const *cache, ethash_params const *params, const uint8_t previous_hash[32], const uint64_t nonce) { uint64_t ethash_full_dag_size(ethash_full_t full)
ethash_hash(ret, NULL, cache, params, previous_hash, nonce); {
return full->file_size;
} }

144
libethash/internal.h

@ -2,6 +2,7 @@
#include "compiler.h" #include "compiler.h"
#include "endian.h" #include "endian.h"
#include "ethash.h" #include "ethash.h"
#include <stdio.h>
#define ENABLE_SSE 0 #define ENABLE_SSE 0
@ -20,9 +21,9 @@ extern "C" {
#include <stdint.h> #include <stdint.h>
typedef union node { typedef union node {
uint8_t bytes[NODE_WORDS * 4]; uint8_t bytes[NODE_WORDS * 4];
uint32_t words[NODE_WORDS]; uint32_t words[NODE_WORDS];
uint64_t double_words[NODE_WORDS / 2]; uint64_t double_words[NODE_WORDS / 2];
#if defined(_M_X64) && ENABLE_SSE #if defined(_M_X64) && ENABLE_SSE
__m128i xmm[NODE_WORDS/4]; __m128i xmm[NODE_WORDS/4];
@ -30,18 +31,139 @@ typedef union node {
} node; } node;
static inline uint8_t ethash_h256_get(ethash_h256_t const* hash, unsigned int i)
{
return hash->b[i];
}
static inline void ethash_h256_set(ethash_h256_t* hash, unsigned int i, uint8_t v)
{
hash->b[i] = v;
}
static inline void ethash_h256_reset(ethash_h256_t* hash)
{
memset(hash, 0, 32);
}
// Returns if hash is less than or equal to difficulty
static inline bool ethash_check_difficulty(
ethash_h256_t const* hash,
ethash_h256_t const* difficulty
)
{
// Difficulty is big endian
for (int i = 0; i < 32; i++) {
if (ethash_h256_get(hash, i) == ethash_h256_get(difficulty, i)) {
continue;
}
return ethash_h256_get(hash, i) < ethash_h256_get(difficulty, i);
}
return true;
}
bool ethash_quick_check_difficulty(
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_h256_t const* mix_hash,
ethash_h256_t const* difficulty
);
struct ethash_light {
void* cache;
uint64_t cache_size;
uint64_t block_number;
};
/**
* Allocate and initialize a new ethash_light handler. Internal version
*
* @param cache_size The size of the cache in bytes
* @param seed Block seedhash to be used during the computation of the
* cache nodes
* @return Newly allocated ethash_light handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
*/
ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed);
/**
* Calculate the light client data. Internal version.
*
* @param light The light client handler
* @param full_size The size of the full data in bytes.
* @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix
* @return The resulting hash.
*/
ethash_return_value_t ethash_light_compute_internal(
ethash_light_t light,
uint64_t full_size,
ethash_h256_t const header_hash,
uint64_t nonce
);
struct ethash_full {
FILE* file;
uint64_t file_size;
node* data;
};
/**
* Allocate and initialize a new ethash_full handler. Internal version.
*
* @param dirname The directory in which to put the DAG file.
* @param seedhash The seed hash of the block. Used in the DAG file naming.
* @param full_size The size of the full data in bytes.
* @param cache A cache object to use that was allocated with @ref ethash_cache_new().
* Iff this function succeeds the ethash_full_t will take memory
* memory ownership of the cache and free it at deletion. If
* not then the user still has to handle freeing of the cache himself.
* @param callback A callback function with signature of @ref ethash_callback_t
* It accepts an unsigned with which a progress of DAG calculation
* can be displayed. If all goes well the callback should return 0.
* If a non-zero value is returned then DAG generation will stop.
* @return Newly allocated ethash_full handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data()
*/
ethash_full_t ethash_full_new_internal(
char const* dirname,
ethash_h256_t const seed_hash,
uint64_t full_size,
ethash_light_t const light,
ethash_callback_t callback
);
void ethash_calculate_dag_item( void ethash_calculate_dag_item(
node *const ret, node* const ret,
const unsigned node_index, uint32_t node_index,
ethash_params const *params, ethash_light_t const cache
void const *cache
); );
void ethash_quick_hash( void ethash_quick_hash(
uint8_t return_hash[32], ethash_h256_t* return_hash,
const uint8_t header_hash[32], ethash_h256_t const* header_hash,
const uint64_t nonce, const uint64_t nonce,
const uint8_t mix_hash[32]); ethash_h256_t const* mix_hash
);
uint64_t ethash_get_datasize(uint64_t const block_number);
uint64_t ethash_get_cachesize(uint64_t const block_number);
/**
* Compute the memory data for a full node's memory
*
* @param mem A pointer to an ethash full's memory
* @param full_size The size of the full data in bytes
* @param cache A cache object to use in the calculation
* @param callback The callback function. Check @ref ethash_full_new() for details.
* @return true if all went fine and false for invalid parameters
*/
bool ethash_compute_full_data(
void* mem,
uint64_t full_size,
ethash_light_t const light,
ethash_callback_t callback
);
#ifdef __cplusplus #ifdef __cplusplus
} }

129
libethash/io.c

@ -22,68 +22,81 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
// silly macro to save some typing enum ethash_io_rc ethash_io_prepare(
#define PASS_ARR(c_) (c_), sizeof(c_) char const* dirname,
ethash_h256_t const seedhash,
static bool ethash_io_write_file(char const *dirname, FILE** output_file,
char const* filename, uint64_t file_size,
size_t filename_length, bool force_create
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,
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
size_t *data_size)
{ {
char info_buffer[DAG_MEMO_BYTESIZE]; char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
// allocate the bytes enum ethash_io_rc ret = ETHASH_IO_FAIL;
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)) { // assert directory exists
goto fail_free; if (!ethash_mkdir(dirname)) {
} goto end;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, info_buffer); ethash_io_mutable_name(ETHASH_REVISION, &seedhash, mutable_name);
if (!ethash_io_write_file(dirname, PASS_ARR(DAG_MEMO_NAME), info_buffer, DAG_MEMO_BYTESIZE)) { char* tmpfile = ethash_io_create_filename(dirname, mutable_name, strlen(mutable_name));
goto fail_free; if (!tmpfile) {
} goto end;
}
*data = temp_data_ptr; FILE *f;
*data_size = (size_t)params->full_size; if (!force_create) {
return true; // try to open the file
f = ethash_fopen(tmpfile, "rb+");
if (f) {
size_t found_size;
if (!ethash_file_size(f, &found_size)) {
fclose(f);
goto free_memo;
}
if (file_size != found_size - ETHASH_DAG_MAGIC_NUM_SIZE) {
fclose(f);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
// compare the magic number, no need to care about endianess since it's local
uint64_t magic_num;
if (fread(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
// I/O error
fclose(f);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
if (magic_num != ETHASH_DAG_MAGIC_NUM) {
fclose(f);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
ret = ETHASH_IO_MEMO_MATCH;
goto set_file;
}
}
// file does not exist, will need to be created
f = ethash_fopen(tmpfile, "wb+");
if (!f) {
goto free_memo;
}
// make sure it's of the proper size
if (fseek(f, (long int)(file_size + ETHASH_DAG_MAGIC_NUM_SIZE - 1), SEEK_SET) != 0) {
fclose(f);
goto free_memo;
}
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
goto set_file;
fail_free: ret = ETHASH_IO_MEMO_MATCH;
free(temp_data_ptr); set_file:
*output_file = f;
free_memo:
free(tmpfile);
end: end:
return false; return ret;
} }
#undef PASS_ARR

201
libethash/io.h

@ -22,94 +22,163 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h>
#ifdef __cplusplus
#define __STDC_FORMAT_MACROS 1
#endif
#include <inttypes.h>
#include "endian.h"
#include "ethash.h" #include "ethash.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
// Maximum size for mutable part of DAG file name
typedef struct ethash_blockhash { uint8_t b[32]; } ethash_blockhash_t; // 6 is for "full-R", the suffix of the filename
// 10 is for maximum number of digits of a uint32_t (for REVISION)
static const char DAG_FILE_NAME[] = "full"; // 1 is for - and 16 is for the first 16 hex digits for first 8 bytes of
static const char DAG_MEMO_NAME[] = "full.info"; // the seedhash and last 1 is for the null terminating character
// MSVC thinks that "static const unsigned int" is not a compile time variable. Sorry for the #define :( // Reference: https://github.com/ethereum/wiki/wiki/Ethash-DAG
#define DAG_MEMO_BYTESIZE 36 #define DAG_MUTABLE_NAME_MAX_SIZE (6 + 10 + 1 + 16 + 1)
/// Possible return values of @see ethash_io_prepare /// Possible return values of @see ethash_io_prepare
enum ethash_io_rc { enum ethash_io_rc {
ETHASH_IO_FAIL = 0, ///< There has been an IO failure 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_SIZE_MISMATCH, ///< DAG with revision/hash match, but file size was wrong.
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
}; };
// 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
/** /**
* Prepares io for ethash * Prepares io for ethash
* *
* Create the DAG directory if it does not exist, and check if the memo file matches. * Create the DAG directory and the DAG file if they don't exist.
* 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 * @param[in] dirname A null terminated c-string of the path of the ethash
* data directory. If it does not exist it's created. * 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
* @return For possible return values @see enum ethash_io_rc * 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_blockhash_t seedhash); 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
);
/** /**
* Fully computes data and writes it to the file on disk. * An fopen wrapper for no-warnings crossplatform fopen.
* *
* This function should be called after @see ethash_io_prepare() and only if * Msvc compiler considers fopen to be insecure and suggests to use their
* its return value is @c ETHASH_IO_MEMO_MISMATCH. Will write both the full data * alternative. This is a wrapper for this alternative. Another way is to
* and the memo file. * #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does
* not sound like a good idea.
* *
* @param[in] dirname A null terminated c-string of the path of the ethash * @param file_name The path to the file to open
* data directory. Has to exist. * @param mode Opening mode. Check fopen()
* @param[in] params An ethash_params object containing the full size * @return The FILE* or NULL in failure
* 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 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, FILE* ethash_fopen(char const* file_name, char const* mode);
ethash_params const* params,
ethash_blockhash_t seedhash,
void const* cache,
uint8_t **data,
size_t *data_size);
static inline void ethash_io_serialize_info(uint32_t revision, /**
ethash_blockhash_t seed_hash, * An strncat wrapper for no-warnings crossplatform strncat.
char *output) *
{ * Msvc compiler considers strncat to be insecure and suggests to use their
// if .info is only consumed locally we don't really care about endianess * alternative. This is a wrapper for this alternative. Another way is to
memcpy(output, &revision, 4); * #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does
memcpy(output + 4, &seed_hash, 32); * 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);
static inline char *ethash_io_create_filename(char const *dirname, /**
char const* filename, * A cross-platform mkdir wrapper to create a directory or assert it's there
size_t filename_length) *
{ * @param dirname The full path of the directory to create
// in C the cast is not needed, but a C++ compiler will complain for invalid conversion * @return true if the directory was created or if it already
char *name = (char*)malloc(strlen(dirname) + filename_length); * existed
if (!name) { */
return NULL; bool ethash_mkdir(char const* dirname);
}
name[0] = '\0'; /**
strcat(name, dirname); * Get a file's size
strcat(name, filename); *
return name; * @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 #ifdef __cplusplus
} }

102
libethash/io_posix.c

@ -27,50 +27,76 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash) FILE* ethash_fopen(char const* file_name, char const* mode)
{ {
char read_buffer[DAG_MEMO_BYTESIZE]; return fopen(file_name, mode);
char expect_buffer[DAG_MEMO_BYTESIZE]; }
enum ethash_io_rc ret = ETHASH_IO_FAIL;
// assert directory exists, full owner permissions and read/search for others char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count)
int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); {
if (rc == -1 && errno != EEXIST) { return strlen(dest) + count + 1 <= dest_size ? strncat(dest, src, count) : NULL;
goto end; }
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME)); bool ethash_mkdir(char const* dirname)
if (!memofile) { {
goto end; int rc = mkdir(dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
} return rc != -1 || errno == EEXIST;
}
// try to open memo file int ethash_fileno(FILE *f)
FILE *f = fopen(memofile, "rb"); {
if (!f) { return fileno(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) { char* ethash_io_create_filename(
goto close; char const* dirname,
} char const* filename,
size_t filename_length
)
{
size_t dirlen = strlen(dirname);
size_t dest_size = dirlen + filename_length + 1;
if (dirname[dirlen] != '/') {
dest_size += 1;
}
char* name = malloc(dest_size);
if (!name) {
return NULL;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer); name[0] = '\0';
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) { ethash_strncat(name, dest_size, dirname, dirlen);
// we have different memo contents so delete the memo file if (dirname[dirlen] != '/') {
if (unlink(memofile) != 0) { ethash_strncat(name, dest_size, "/", 1);
goto close; }
} ethash_strncat(name, dest_size, filename, filename_length);
ret = ETHASH_IO_MEMO_MISMATCH; return name;
} }
ret = ETHASH_IO_MEMO_MATCH; bool ethash_file_size(FILE* f, size_t* ret_size)
{
struct stat st;
int fd;
if ((fd = fileno(f)) == -1 || fstat(fd, &st) != 0) {
return false;
}
*ret_size = st.st_size;
return true;
}
close: bool ethash_get_default_dirname(char* strbuf, size_t buffsize)
fclose(f); {
free_memo: static const char dir_suffix[] = ".ethash/";
free(memofile); strbuf[0] = '\0';
end: char* home_dir = getenv("HOME");
return ret; size_t len = strlen(home_dir);
if (!ethash_strncat(strbuf, buffsize, home_dir, len)) {
return false;
}
if (home_dir[len] != '/') {
if (!ethash_strncat(strbuf, buffsize, "/", 1)) {
return false;
}
}
return ethash_strncat(strbuf, buffsize, dir_suffix, sizeof(dir_suffix));
} }

103
libethash/io_win32.c

@ -23,51 +23,78 @@
#include <direct.h> #include <direct.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <shlobj.h>
enum ethash_io_rc ethash_io_prepare(char const *dirname, ethash_blockhash_t seedhash) FILE* ethash_fopen(char const* file_name, char const* mode)
{ {
char read_buffer[DAG_MEMO_BYTESIZE]; FILE* f;
char expect_buffer[DAG_MEMO_BYTESIZE]; return fopen_s(&f, file_name, mode) == 0 ? f : NULL;
enum ethash_io_rc ret = ETHASH_IO_FAIL; }
// assert directory exists char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count)
int rc = _mkdir(dirname); {
if (rc == -1 && errno != EEXIST) { return strncat_s(dest, dest_size, src, count) == 0 ? dest : NULL;
goto end; }
}
char *memofile = ethash_io_create_filename(dirname, DAG_MEMO_NAME, sizeof(DAG_MEMO_NAME)); bool ethash_mkdir(char const* dirname)
if (!memofile) { {
goto end; int rc = _mkdir(dirname);
} return rc != -1 || errno == EEXIST;
}
// try to open memo file int ethash_fileno(FILE* f)
FILE *f = fopen(memofile, "rb"); {
if (!f) { return _fileno(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) { char* ethash_io_create_filename(
goto close; char const* dirname,
} char const* filename,
size_t filename_length
)
{
size_t dirlen = strlen(dirname);
size_t dest_size = dirlen + filename_length + 1;
if (dirname[dirlen] != '\\' || dirname[dirlen] != '/') {
dest_size += 1;
}
char* name = malloc(dest_size);
if (!name) {
return NULL;
}
name[0] = '\0';
ethash_strncat(name, dest_size, dirname, dirlen);
if (dirname[dirlen] != '\\' || dirname[dirlen] != '/') {
ethash_strncat(name, dest_size, "\\", 1);
}
ethash_strncat(name, dest_size, filename, filename_length);
return name;
}
ethash_io_serialize_info(ETHASH_REVISION, seedhash, expect_buffer); bool ethash_file_size(FILE* f, size_t* ret_size)
if (memcmp(read_buffer, expect_buffer, DAG_MEMO_BYTESIZE) != 0) { {
// we have different memo contents so delete the memo file struct _stat st;
if (_unlink(memofile) != 0) { int fd;
goto close; if ((fd = _fileno(f)) == -1 || _fstat(fd, &st) != 0) {
} return false;
ret = ETHASH_IO_MEMO_MISMATCH; }
} *ret_size = st.st_size;
return true;
}
ret = ETHASH_IO_MEMO_MATCH; bool ethash_get_default_dirname(char* strbuf, size_t buffsize)
{
static const char dir_suffix[] = "Appdata\\Ethash\\";
strbuf[0] = '\0';
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, (WCHAR*)strbuf))) {
return false;
}
if (!ethash_strncat(strbuf, buffsize, "\\", 1)) {
return false;
}
close: return ethash_strncat(strbuf, buffsize, dir_suffix, sizeof(dir_suffix));
fclose(f);
free_memo:
free(memofile);
end:
return ret;
} }

47
libethash/mmap.h

@ -0,0 +1,47 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
/** @file mmap.h
* @author Lefteris Karapetsas <lefteris@ethdev.com>
* @date 2015
*/
#pragma once
#if defined(__MINGW32__) || defined(_WIN32)
#include <sys/types.h>
#define PROT_READ 0x1
#define PROT_WRITE 0x2
/* This flag is only available in WinXP+ */
#ifdef FILE_MAP_EXECUTE
#define PROT_EXEC 0x4
#else
#define PROT_EXEC 0x0
#define FILE_MAP_EXECUTE 0
#endif
#define MAP_SHARED 0x01
#define MAP_PRIVATE 0x02
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *) -1)
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
void munmap(void* addr, size_t length);
#else // posix, yay! ^_^
#include <sys/mman.h>
#endif

84
libethash/mmap_win32.c

@ -0,0 +1,84 @@
/* mmap() replacement for Windows
*
* Author: Mike Frysinger <vapier@gentoo.org>
* Placed into the public domain
*/
/* References:
* CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
* CloseHandle: http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
* MapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
* UnmapViewOfFile: http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
*/
#include <io.h>
#include <windows.h>
#include "mmap.h"
#ifdef __USE_FILE_OFFSET64
# define DWORD_HI(x) (x >> 32)
# define DWORD_LO(x) ((x) & 0xffffffff)
#else
# define DWORD_HI(x) (0)
# define DWORD_LO(x) (x)
#endif
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset)
{
if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
return MAP_FAILED;
if (fd == -1) {
if (!(flags & MAP_ANON) || offset)
return MAP_FAILED;
} else if (flags & MAP_ANON)
return MAP_FAILED;
DWORD flProtect;
if (prot & PROT_WRITE) {
if (prot & PROT_EXEC)
flProtect = PAGE_EXECUTE_READWRITE;
else
flProtect = PAGE_READWRITE;
} else if (prot & PROT_EXEC) {
if (prot & PROT_READ)
flProtect = PAGE_EXECUTE_READ;
else if (prot & PROT_EXEC)
flProtect = PAGE_EXECUTE;
} else
flProtect = PAGE_READONLY;
off_t end = length + offset;
HANDLE mmap_fd, h;
if (fd == -1)
mmap_fd = INVALID_HANDLE_VALUE;
else
mmap_fd = (HANDLE)_get_osfhandle(fd);
h = CreateFileMapping(mmap_fd, NULL, flProtect, DWORD_HI(end), DWORD_LO(end), NULL);
if (h == NULL)
return MAP_FAILED;
DWORD dwDesiredAccess;
if (prot & PROT_WRITE)
dwDesiredAccess = FILE_MAP_WRITE;
else
dwDesiredAccess = FILE_MAP_READ;
if (prot & PROT_EXEC)
dwDesiredAccess |= FILE_MAP_EXECUTE;
if (flags & MAP_PRIVATE)
dwDesiredAccess |= FILE_MAP_COPY;
void *ret = MapViewOfFile(h, dwDesiredAccess, DWORD_HI(offset), DWORD_LO(offset), length);
if (ret == NULL) {
ret = MAP_FAILED;
}
// since we are handling the file ourselves with fd, close the Windows Handle here
CloseHandle(h);
return ret;
}
void munmap(void* addr, size_t length)
{
UnmapViewOfFile(addr);
}
#undef DWORD_HI
#undef DWORD_LO

194
libethash/sha3.c

@ -17,65 +17,65 @@
/*** Constants. ***/ /*** Constants. ***/
static const uint8_t rho[24] = \ static const uint8_t rho[24] = \
{ 1, 3, 6, 10, 15, 21, { 1, 3, 6, 10, 15, 21,
28, 36, 45, 55, 2, 14, 28, 36, 45, 55, 2, 14,
27, 41, 56, 8, 25, 43, 27, 41, 56, 8, 25, 43,
62, 18, 39, 61, 20, 44}; 62, 18, 39, 61, 20, 44};
static const uint8_t pi[24] = \ static const uint8_t pi[24] = \
{10, 7, 11, 17, 18, 3, {10, 7, 11, 17, 18, 3,
5, 16, 8, 21, 24, 4, 5, 16, 8, 21, 24, 4,
15, 23, 19, 13, 12, 2, 15, 23, 19, 13, 12, 2,
20, 14, 22, 9, 6, 1}; 20, 14, 22, 9, 6, 1};
static const uint64_t RC[24] = \ static const uint64_t RC[24] = \
{1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL,
0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL,
0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL,
0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL,
0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL,
0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL};
/*** Helper macros to unroll the permutation. ***/ /*** Helper macros to unroll the permutation. ***/
#define rol(x, s) (((x) << s) | ((x) >> (64 - s))) #define rol(x, s) (((x) << s) | ((x) >> (64 - s)))
#define REPEAT6(e) e e e e e e #define REPEAT6(e) e e e e e e
#define REPEAT24(e) REPEAT6(e e e e) #define REPEAT24(e) REPEAT6(e e e e)
#define REPEAT5(e) e e e e e #define REPEAT5(e) e e e e e
#define FOR5(v, s, e) \ #define FOR5(v, s, e) \
v = 0; \ v = 0; \
REPEAT5(e; v += s;) REPEAT5(e; v += s;)
/*** Keccak-f[1600] ***/ /*** Keccak-f[1600] ***/
static inline void keccakf(void* state) { static inline void keccakf(void* state) {
uint64_t* a = (uint64_t*)state; uint64_t* a = (uint64_t*)state;
uint64_t b[5] = {0}; uint64_t b[5] = {0};
uint64_t t = 0; uint64_t t = 0;
uint8_t x, y; uint8_t x, y;
for (int i = 0; i < 24; i++) { for (int i = 0; i < 24; i++) {
// Theta // Theta
FOR5(x, 1, FOR5(x, 1,
b[x] = 0; b[x] = 0;
FOR5(y, 5, FOR5(y, 5,
b[x] ^= a[x + y]; )) b[x] ^= a[x + y]; ))
FOR5(x, 1, FOR5(x, 1,
FOR5(y, 5, FOR5(y, 5,
a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); ))
// Rho and pi // Rho and pi
t = a[1]; t = a[1];
x = 0; x = 0;
REPEAT24(b[0] = a[pi[x]]; REPEAT24(b[0] = a[pi[x]];
a[pi[x]] = rol(t, rho[x]); a[pi[x]] = rol(t, rho[x]);
t = b[0]; t = b[0];
x++; ) x++; )
// Chi // Chi
FOR5(y, FOR5(y,
5, 5,
FOR5(x, 1, FOR5(x, 1,
b[x] = a[y + x];) b[x] = a[y + x];)
FOR5(x, 1, FOR5(x, 1,
a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); ))
// Iota // Iota
a[0] ^= RC[i]; a[0] ^= RC[i];
} }
} }
/******** The FIPS202-defined functions. ********/ /******** The FIPS202-defined functions. ********/
@ -83,20 +83,20 @@ static inline void keccakf(void* state) {
/*** Some helper macros. ***/ /*** Some helper macros. ***/
#define _(S) do { S } while (0) #define _(S) do { S } while (0)
#define FOR(i, ST, L, S) \ #define FOR(i, ST, L, S) \
_(for (size_t i = 0; i < L; i += ST) { S; }) _(for (size_t i = 0; i < L; i += ST) { S; })
#define mkapply_ds(NAME, S) \ #define mkapply_ds(NAME, S) \
static inline void NAME(uint8_t* dst, \ static inline void NAME(uint8_t* dst, \
const uint8_t* src, \ const uint8_t* src, \
size_t len) { \ size_t len) { \
FOR(i, 1, len, S); \ FOR(i, 1, len, S); \
} }
#define mkapply_sd(NAME, S) \ #define mkapply_sd(NAME, S) \
static inline void NAME(const uint8_t* src, \ static inline void NAME(const uint8_t* src, \
uint8_t* dst, \ uint8_t* dst, \
size_t len) { \ size_t len) { \
FOR(i, 1, len, S); \ FOR(i, 1, len, S); \
} }
mkapply_ds(xorin, dst[i] ^= src[i]) // xorin mkapply_ds(xorin, dst[i] ^= src[i]) // xorin
mkapply_sd(setout, dst[i] = src[i]) // setout mkapply_sd(setout, dst[i] = src[i]) // setout
@ -105,47 +105,47 @@ mkapply_sd(setout, dst[i] = src[i]) // setout
#define Plen 200 #define Plen 200
// Fold P*F over the full blocks of an input. // Fold P*F over the full blocks of an input.
#define foldP(I, L, F) \ #define foldP(I, L, F) \
while (L >= rate) { \ while (L >= rate) { \
F(a, I, rate); \ F(a, I, rate); \
P(a); \ P(a); \
I += rate; \ I += rate; \
L -= rate; \ L -= rate; \
} }
/** The sponge-based hash construction. **/ /** The sponge-based hash construction. **/
static inline int hash(uint8_t* out, size_t outlen, static inline int hash(uint8_t* out, size_t outlen,
const uint8_t* in, size_t inlen, const uint8_t* in, size_t inlen,
size_t rate, uint8_t delim) { size_t rate, uint8_t delim) {
if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) { if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) {
return -1; return -1;
} }
uint8_t a[Plen] = {0}; uint8_t a[Plen] = {0};
// Absorb input. // Absorb input.
foldP(in, inlen, xorin); foldP(in, inlen, xorin);
// Xor in the DS and pad frame. // Xor in the DS and pad frame.
a[inlen] ^= delim; a[inlen] ^= delim;
a[rate - 1] ^= 0x80; a[rate - 1] ^= 0x80;
// Xor in the last block. // Xor in the last block.
xorin(a, in, inlen); xorin(a, in, inlen);
// Apply P // Apply P
P(a); P(a);
// Squeeze output. // Squeeze output.
foldP(out, outlen, setout); foldP(out, outlen, setout);
setout(a, out, outlen); setout(a, out, outlen);
memset(a, 0, 200); memset(a, 0, 200);
return 0; return 0;
} }
#define defsha3(bits) \ #define defsha3(bits) \
int sha3_##bits(uint8_t* out, size_t outlen, \ int sha3_##bits(uint8_t* out, size_t outlen, \
const uint8_t* in, size_t inlen) { \ const uint8_t* in, size_t inlen) { \
if (outlen > (bits/8)) { \ if (outlen > (bits/8)) { \
return -1; \ return -1; \
} \ } \
return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \
} }
/*** FIPS202 SHA3 FOFs ***/ /*** FIPS202 SHA3 FOFs ***/
defsha3(256) defsha3(256)
defsha3(512) defsha3(512)

16
libethash/sha3.h

@ -8,20 +8,24 @@ extern "C" {
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
struct ethash_h256;
#define decsha3(bits) \ #define decsha3(bits) \
int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t); int sha3_##bits(uint8_t*, size_t, uint8_t const*, size_t);
decsha3(256) decsha3(256)
decsha3(512) decsha3(512)
static inline void SHA3_256(uint8_t * const ret, uint8_t const *data, const size_t size) { static inline void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t const size)
sha3_256(ret, 32, data, size); {
sha3_256((uint8_t*)ret, 32, data, size);
} }
static inline void SHA3_512(uint8_t * const ret, uint8_t const *data, const size_t size) { static inline void SHA3_512(uint8_t* ret, uint8_t const* data, size_t const size)
sha3_512(ret, 64, data, size); {
sha3_512(ret, 64, data, size);
} }
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

15
libethash/sha3_cryptopp.cpp

@ -19,16 +19,19 @@
* @author Tim Hughes <tim@twistedfury.com> * @author Tim Hughes <tim@twistedfury.com>
* @date 2015 * @date 2015
*/ */
#include <stdint.h> #include <stdint.h>
#include <cryptopp/sha3.h> #include <cryptopp/sha3.h>
extern "C" { extern "C" {
void SHA3_256(uint8_t *const ret, const uint8_t *data, size_t size) { struct ethash_h256;
CryptoPP::SHA3_256().CalculateDigest(ret, data, size); typedef struct ethash_h256 ethash_h256_t;
void SHA3_256(ethash_h256_t const* ret, uint8_t const* data, size_t size)
{
CryptoPP::SHA3_256().CalculateDigest((uint8_t*)ret, data, size);
} }
void SHA3_512(uint8_t *const ret, const uint8_t *data, size_t size) { void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size)
CryptoPP::SHA3_512().CalculateDigest(ret, data, size); {
CryptoPP::SHA3_512().CalculateDigest(ret, data, size);
}
} }
}

9
libethash/sha3_cryptopp.h

@ -2,14 +2,17 @@
#include "compiler.h" #include "compiler.h"
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void SHA3_256(uint8_t *const ret, const uint8_t *data, size_t size); struct ethash_h256;
void SHA3_512(uint8_t *const ret, const uint8_t *data, size_t size);
void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t size);
void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

4
libethash/util.h

@ -27,9 +27,9 @@ extern "C" {
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
void debugf(const char *str, ...); void debugf(char const* str, ...);
#else #else
#define debugf(...) fprintf(stderr, __VA_ARGS__) #define debugf printf
#endif #endif
static inline uint32_t min_u32(uint32_t a, uint32_t b) static inline uint32_t min_u32(uint32_t a, uint32_t b)

38
libethash/util_win32.c

@ -0,0 +1,38 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum 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.
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file util.c
* @author Tim Hughes <tim@twistedfury.com>
* @date 2015
*/
#include <stdarg.h>
#include <stdio.h>
#include "util.h"
// foward declare without all of Windows.h
__declspec(dllimport) void __stdcall OutputDebugStringA(char const* lpOutputString);
void debugf(char const* str, ...)
{
va_list args;
va_start(args, str);
char buf[1<<16];
_vsnprintf_s(buf, sizeof(buf), sizeof(buf), str, args);
buf[sizeof(buf)-1] = '\0';
OutputDebugStringA(buf);
}

26
libethcore/Ethash.cpp

@ -36,6 +36,7 @@
#include <libdevcrypto/CryptoPP.h> #include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libethash/ethash.h> #include <libethash/ethash.h>
#include <libethash/internal.h>
#if ETH_ETHASHCL || !ETH_TRUE #if ETH_ETHASHCL || !ETH_TRUE
#include <libethash-cl/ethash_cl_miner.h> #include <libethash-cl/ethash_cl_miner.h>
#endif #endif
@ -71,6 +72,7 @@ Ethash::WorkPackage Ethash::package(BlockInfo const& _bi)
ret.boundary = _bi.boundary(); ret.boundary = _bi.boundary();
ret.headerHash = _bi.headerHash(WithoutNonce); ret.headerHash = _bi.headerHash(WithoutNonce);
ret.seedHash = _bi.seedHash(); ret.seedHash = _bi.seedHash();
ret.blockNumber = (uint64_t) _bi.number;
return ret; return ret;
} }
@ -87,10 +89,10 @@ bool Ethash::preVerify(BlockInfo const& _header)
h256 boundary = u256((bigint(1) << 256) / _header.difficulty); h256 boundary = u256((bigint(1) << 256) / _header.difficulty);
return !!ethash_quick_check_difficulty( return !!ethash_quick_check_difficulty(
_header.headerHash(WithoutNonce).data(), (ethash_h256_t const*)_header.headerHash(WithoutNonce).data(),
(uint64_t)(u64)_header.nonce, (uint64_t)(u64)_header.nonce,
_header.mixHash.data(), (ethash_h256_t const*)_header.mixHash.data(),
boundary.data()); (ethash_h256_t const*)boundary.data());
} }
bool Ethash::verify(BlockInfo const& _header) bool Ethash::verify(BlockInfo const& _header)
@ -133,17 +135,14 @@ void Ethash::CPUMiner::workLoop()
WorkPackage w = work(); WorkPackage w = work();
auto p = EthashAux::params(w.seedHash); auto dag = EthashAux::full(w.blockNumber);
auto dag = EthashAux::full(w.seedHash);
auto dagPointer = dag->data.data();
uint8_t const* headerHashPointer = w.headerHash.data();
h256 boundary = w.boundary; h256 boundary = w.boundary;
unsigned hashCount = 1; unsigned hashCount = 1;
for (; !shouldStop(); tryNonce++, hashCount++) for (; !shouldStop(); tryNonce++, hashCount++)
{ {
ethash_compute_full(&ethashReturn, dagPointer, &p, headerHashPointer, tryNonce); ethashReturn = ethash_full_compute(dag->full, *(ethash_h256_t*)w.headerHash.data(), tryNonce);
h256 value = h256(ethashReturn.result, h256::ConstructFromPointer); h256 value = h256((uint8_t*)&ethashReturn.result, h256::ConstructFromPointer);
if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256(ethashReturn.mix_hash, h256::ConstructFromPointer)})) if (value <= boundary && submitProof(Solution{(Nonce)(u64)tryNonce, h256((uint8_t*)&ethashReturn.mix_hash, h256::ConstructFromPointer)}))
break; break;
if (!(hashCount % 1000)) if (!(hashCount % 1000))
accumulateHashes(1000); accumulateHashes(1000);
@ -285,7 +284,7 @@ Ethash::GPUMiner::~GPUMiner()
bool Ethash::GPUMiner::report(uint64_t _nonce) bool Ethash::GPUMiner::report(uint64_t _nonce)
{ {
Nonce n = (Nonce)(u64)_nonce; Nonce n = (Nonce)(u64)_nonce;
Result r = EthashAux::eval(work().seedHash, work().headerHash, n); Result r = EthashAux::eval(work().blockNumber, work().headerHash, n);
if (r.value < work().boundary) if (r.value < work().boundary)
return submitProof(Solution{n, r.mixHash}); return submitProof(Solution{n, r.mixHash});
return false; return false;
@ -311,8 +310,9 @@ void Ethash::GPUMiner::workLoop()
unsigned device = instances() > 1 ? index() : s_deviceId; unsigned device = instances() > 1 ? index() : s_deviceId;
EthashAux::FullType dag = EthashAux::full(m_minerSeed); EthashAux::FullType dag = EthashAux::full(w.blockNumber);
m_miner->init(dag->data.data(), dag->data.size(), 32, s_platformId, device); bytesConstRef dagData = dag->data();
m_miner->init(dagData.data(), dagData.size(), 32, s_platformId, device);
} }
uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192); uint64_t upper64OfBoundary = (uint64_t)(u64)((u256)w.boundary >> 192);

3
libethcore/Ethash.h

@ -66,7 +66,8 @@ public:
h256 boundary; h256 boundary;
h256 headerHash; ///< When h256() means "pause until notified a new work package is available". h256 headerHash; ///< When h256() means "pause until notified a new work package is available".
h256 seedHash; h256 seedHash; /// LTODO: IS this needed now that we use the block number instead?
uint64_t blockNumber;
}; };
static const WorkPackage NullWorkPackage; static const WorkPackage NullWorkPackage;

170
libethcore/EthashAux.cpp

@ -33,7 +33,9 @@
#include <libdevcrypto/CryptoPP.h> #include <libdevcrypto/CryptoPP.h>
#include <libdevcrypto/SHA3.h> #include <libdevcrypto/SHA3.h>
#include <libdevcrypto/FileSystem.h> #include <libdevcrypto/FileSystem.h>
#include <libethash/internal.h>
#include "BlockInfo.h" #include "BlockInfo.h"
#include "Exceptions.h"
using namespace std; using namespace std;
using namespace chrono; using namespace chrono;
using namespace dev; using namespace dev;
@ -45,17 +47,9 @@ EthashAux::~EthashAux()
{ {
} }
ethash_params EthashAux::params(BlockInfo const& _header) uint64_t EthashAux::cacheSize(BlockInfo const& _header)
{ {
return params((unsigned)_header.number); return ethash_get_cachesize((uint64_t)_header.number);
}
ethash_params EthashAux::params(unsigned _n)
{
ethash_params p;
p.cache_size = ethash_get_cachesize(_n);
p.full_size = ethash_get_datasize(_n);
return p;
} }
h256 EthashAux::seedHash(unsigned _number) h256 EthashAux::seedHash(unsigned _number)
@ -82,27 +76,6 @@ h256 EthashAux::seedHash(unsigned _number)
return get()->m_seedHashes[epoch]; return get()->m_seedHashes[epoch];
} }
ethash_params EthashAux::params(h256 const& _seedHash)
{
Guard l(get()->x_epochs);
unsigned epoch = 0;
auto epochIter = get()->m_epochs.find(_seedHash);
if (epochIter == get()->m_epochs.end())
{
// cdebug << "Searching for seedHash " << _seedHash;
for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {}
if (epoch == 2048)
{
std::ostringstream error;
error << "apparent block number for " << _seedHash << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048);
throw std::invalid_argument(error.str());
}
}
else
epoch = epochIter->second;
return params(epoch * ETHASH_EPOCH_LENGTH);
}
void EthashAux::killCache(h256 const& _s) void EthashAux::killCache(h256 const& _s)
{ {
RecursiveGuard l(x_this); RecursiveGuard l(x_this);
@ -111,114 +84,101 @@ void EthashAux::killCache(h256 const& _s)
EthashAux::LightType EthashAux::light(BlockInfo const& _header) EthashAux::LightType EthashAux::light(BlockInfo const& _header)
{ {
return light(_header.seedHash()); return light((uint64_t)_header.number);
} }
EthashAux::LightType EthashAux::light(h256 const& _seedHash) EthashAux::LightType EthashAux::light(uint64_t _blockNumber)
{ {
RecursiveGuard l(get()->x_this); RecursiveGuard l(get()->x_this);
LightType ret = get()->m_lights[_seedHash]; h256 seedHash = EthashAux::seedHash(_blockNumber);
return ret ? ret : (get()->m_lights[_seedHash] = make_shared<LightAllocation>(_seedHash)); LightType ret = get()->m_lights[seedHash];
return ret ? ret : (get()->m_lights[seedHash] = make_shared<LightAllocation>(_blockNumber));
} }
EthashAux::LightAllocation::LightAllocation(h256 const& _seed) EthashAux::LightAllocation::LightAllocation(uint64_t _blockNumber)
{ {
auto p = params(_seed); light = ethash_light_new(_blockNumber);
size = p.cache_size; size = ethash_get_cachesize(_blockNumber);
light = ethash_new_light(&p, _seed.data());
} }
EthashAux::LightAllocation::~LightAllocation() EthashAux::LightAllocation::~LightAllocation()
{ {
ethash_delete_light(light); ethash_light_delete(light);
} }
EthashAux::FullType EthashAux::full(BlockInfo const& _header, bytesRef _dest, bool _createIfMissing) bytesConstRef EthashAux::LightAllocation::data() const
{ {
return full(_header.seedHash(), _dest, _createIfMissing); return bytesConstRef((byte const*)light->cache, size);
} }
EthashAux::FullType EthashAux::full(h256 const& _seedHash, bytesRef _dest, bool _createIfMissing) EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb)
{ {
RecursiveGuard l(get()->x_this); full = ethash_full_new(_light, _cb);
FullType ret = get()->m_fulls[_seedHash].lock(); }
if (ret && _dest)
{
assert(ret->data.size() <= _dest.size());
ret->data.copyTo(_dest);
return FullType();
}
if (!ret)
{
// drop our last used cache sine we're allocating another 1GB.
get()->m_lastUsedFull.reset();
try { EthashAux::FullAllocation::~FullAllocation()
boost::filesystem::create_directories(getDataDir("ethash")); {
} catch (...) {} ethash_full_delete(full);
}
auto info = rlpList(Ethash::revision(), _seedHash); bytesConstRef EthashAux::FullAllocation::data() const
std::string oldMemoFile = getDataDir("ethash") + "/full"; {
std::string memoFile = getDataDir("ethash") + "/full-R" + toString(ETHASH_REVISION) + "-" + toHex(_seedHash.ref().cropped(0, 8)); return bytesConstRef((byte const*)ethash_full_dag(full), size());
if (boost::filesystem::exists(oldMemoFile) && contents(oldMemoFile + ".info") == info) }
{
// memofile valid - rename.
boost::filesystem::rename(oldMemoFile, memoFile);
}
DEV_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile)); EthashAux::FullType EthashAux::full(BlockInfo const& _header)
DEV_IGNORE_EXCEPTIONS(boost::filesystem::remove(oldMemoFile + ".info")); {
return full((uint64_t) _header.number);
}
ethash_params p = params(_seedHash); struct DAGChannel: public LogChannel { static const char* name(); static const int verbosity = 0; };
assert(!_dest || _dest.size() >= p.full_size); // must be big enough. const char* DAGChannel::name() { return EthGreen "DAG"; }
static int ethash_callback(unsigned int _progress)
{
clog(DAGChannel) << "Generating DAG file. Progress: " << toString(_progress) << "%";
return 0;
}
bytesRef r = contentsNew(memoFile, _dest); EthashAux::FullType EthashAux::full(uint64_t _blockNumber)
if (!r) {
{ RecursiveGuard l(get()->x_this);
if (!_createIfMissing) h256 seedHash = EthashAux::seedHash(_blockNumber);
return FullType(); FullType ret;
// file didn't exist. if ((ret = get()->m_fulls[seedHash].lock()))
if (_dest) {
// buffer was passed in - no insertion into cache nor need to allocate get()->m_lastUsedFull = ret;
r = _dest; return ret;
else
r = bytesRef(new byte[p.full_size], p.full_size);
ethash_prep_full(r.data(), &p, light(_seedHash)->light);
writeFile(memoFile, r);
}
if (_dest)
return FullType();
ret = make_shared<FullAllocation>(r);
get()->m_fulls[_seedHash] = ret;
} }
get()->m_lastUsedFull = ret; ret = get()->m_lastUsedFull = make_shared<FullAllocation>(light(_blockNumber)->light, ethash_callback);
get()->m_fulls[seedHash] = ret;
return ret; return ret;
} }
Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce) Ethash::Result EthashAux::FullAllocation::compute(h256 const& _headerHash, Nonce const& _nonce) const
{ {
return eval(_header.seedHash(), _header.headerHash(WithoutNonce), _nonce); ethash_return_value_t r = ethash_full_compute(full, *(ethash_h256_t*)_headerHash.data(), (uint64_t)(u64)_nonce);
if (!r.success)
BOOST_THROW_EXCEPTION(DAGCreationFailure());
return Ethash::Result{h256((uint8_t*)&r.result, h256::ConstructFromPointer), h256((uint8_t*)&r.mix_hash, h256::ConstructFromPointer)};
} }
Ethash::Result EthashAux::FullAllocation::compute(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) const Ethash::Result EthashAux::LightAllocation::compute(h256 const& _headerHash, Nonce const& _nonce) const
{ {
ethash_return_value r; ethash_return_value r = ethash_light_compute(light, *(ethash_h256_t*)_headerHash.data(), (uint64_t)(u64)_nonce);
auto p = EthashAux::params(_seedHash); if (!r.success)
ethash_compute_full(&r, data.data(), &p, _headerHash.data(), (uint64_t)(u64)_nonce); BOOST_THROW_EXCEPTION(DAGCreationFailure());
return Ethash::Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)}; return Ethash::Result{h256((uint8_t*)&r.result, h256::ConstructFromPointer), h256((uint8_t*)&r.mix_hash, h256::ConstructFromPointer)};
} }
Ethash::Result EthashAux::LightAllocation::compute(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) const Ethash::Result EthashAux::eval(BlockInfo const& _header, Nonce const& _nonce)
{ {
ethash_return_value r; return eval((uint64_t)_header.number, _header.headerHash(WithoutNonce), _nonce);
auto p = EthashAux::params(_seedHash);
ethash_compute_light(&r, light, &p, _headerHash.data(), (uint64_t)(u64)_nonce);
return Ethash::Result{h256(r.result, h256::ConstructFromPointer), h256(r.mix_hash, h256::ConstructFromPointer)};
} }
Ethash::Result EthashAux::eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) Ethash::Result EthashAux::eval(uint64_t _blockNumber, h256 const& _headerHash, Nonce const& _nonce)
{ {
if (auto dag = EthashAux::get()->full(_seedHash, bytesRef(), false)) h256 seedHash = EthashAux::seedHash(_blockNumber);
return dag->compute(_seedHash, _headerHash, _nonce); if (FullType dag = get()->m_fulls[seedHash].lock())
return EthashAux::get()->light(_seedHash)->compute(_seedHash, _headerHash, _nonce); return dag->compute(_headerHash, _nonce);
return EthashAux::get()->light(_blockNumber)->compute(_headerHash, _nonce);
} }

39
libethcore/EthashAux.h

@ -26,6 +26,7 @@ namespace dev
{ {
namespace eth{ namespace eth{
class EthashAux class EthashAux
{ {
public: public:
@ -33,39 +34,41 @@ public:
static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; } static EthashAux* get() { if (!s_this) s_this = new EthashAux(); return s_this; }
struct FullAllocation
{
FullAllocation(bytesConstRef _d): data(_d) {}
~FullAllocation() { delete [] data.data(); }
Ethash::Result compute(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) const;
bytesConstRef const data;
};
struct LightAllocation struct LightAllocation
{ {
LightAllocation(h256 const& _seed); LightAllocation(uint64_t _blockNumber);
~LightAllocation(); ~LightAllocation();
bytesConstRef data() const { return bytesConstRef((byte const*)light, size); } bytesConstRef data() const;
Ethash::Result compute(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce) const; Ethash::Result compute(h256 const& _headerHash, Nonce const& _nonce) const;
ethash_light_t light; ethash_light_t light;
uint64_t size; uint64_t size;
}; };
struct FullAllocation
{
FullAllocation(ethash_light_t _light, ethash_callback_t _cb);
~FullAllocation();
Ethash::Result compute(h256 const& _headerHash, Nonce const& _nonce) const;
bytesConstRef data() const;
uint64_t size() const { return ethash_full_dag_size(full); }
ethash_full_t full;
};
using LightType = std::shared_ptr<LightAllocation>; using LightType = std::shared_ptr<LightAllocation>;
using FullType = std::shared_ptr<FullAllocation>; using FullType = std::shared_ptr<FullAllocation>;
static h256 seedHash(unsigned _number); static h256 seedHash(unsigned _number);
static ethash_params params(BlockInfo const& _header); static uint64_t cacheSize(BlockInfo const& _header);
static ethash_params params(h256 const& _seedHash);
static ethash_params params(unsigned _n);
static LightType light(BlockInfo const& _header); static LightType light(BlockInfo const& _header);
static LightType light(h256 const& _seedHash); static LightType light(uint64_t _blockNumber);
static FullType full(BlockInfo const& _header, bytesRef _dest = bytesRef(), bool _createIfMissing = true); static FullType full(BlockInfo const& _header);
static FullType full(h256 const& _header, bytesRef _dest = bytesRef(), bool _createIfMissing = true); static FullType full(uint64_t _blockNumber);
static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); } static Ethash::Result eval(BlockInfo const& _header) { return eval(_header, _header.nonce); }
static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce); static Ethash::Result eval(BlockInfo const& _header, Nonce const& _nonce);
static Ethash::Result eval(h256 const& _seedHash, h256 const& _headerHash, Nonce const& _nonce); static Ethash::Result eval(uint64_t _blockNumber, h256 const& _headerHash, Nonce const& _nonce);
private: private:
EthashAux() {} EthashAux() {}

3
libethcore/Exceptions.h

@ -73,6 +73,7 @@ class InvalidBlockNonce: virtual public dev::Exception {};
struct InvalidParentHash: virtual dev::Exception {}; struct InvalidParentHash: virtual dev::Exception {};
struct InvalidNumber: virtual dev::Exception {}; struct InvalidNumber: virtual dev::Exception {};
struct InvalidContractAddress: virtual public dev::Exception {}; struct InvalidContractAddress: virtual public dev::Exception {};
struct DAGCreationFailure: virtual public dev::Exception {};
struct DAGComputeFailure: virtual public dev::Exception {};
} }
} }

6
test/libethcore/dagger.cpp

@ -63,14 +63,14 @@ BOOST_AUTO_TEST_CASE(basic_test)
unsigned cacheSize(o["cache_size"].get_int()); unsigned cacheSize(o["cache_size"].get_int());
h256 cacheHash(o["cache_hash"].get_str()); h256 cacheHash(o["cache_hash"].get_str());
BOOST_REQUIRE_EQUAL(EthashAux::get()->params(header).cache_size, cacheSize); BOOST_REQUIRE_EQUAL(EthashAux::get()->light(header)->size, cacheSize);
BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->light(header)->data()), cacheHash); BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->light(header)->data()), cacheHash);
#if TEST_FULL #if TEST_FULL
unsigned fullSize(o["full_size"].get_int()); unsigned fullSize(o["full_size"].get_int());
h256 fullHash(o["full_hash"].get_str()); h256 fullHash(o["full_hash"].get_str());
BOOST_REQUIRE_EQUAL(EthashAux::get()->full(header).size(), fullSize); BOOST_REQUIRE_EQUAL(EthashAux::get()->full(header)->size(), fullSize);
BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->full(header)), fullHash); BOOST_REQUIRE_EQUAL(sha3(EthashAux::get()->full(header)->data()), fullHash);
#endif #endif
h256 result(o["result"].get_str()); h256 result(o["result"].get_str());

Loading…
Cancel
Save