Browse Source

Merge pull request #41 from LefterisJP/finalize_and_use_new_api

Finalize and use new api
cl-refactor
Gav Wood 10 years ago
parent
commit
3f72ae8c14
  1. 2
      CMakeLists.txt
  2. 221
      ethash.h
  3. 347
      internal.c
  4. 122
      internal.h
  5. 75
      io.c
  6. 52
      io.h
  7. 42
      io_posix.c
  8. 40
      io_win32.c
  9. 0
      util_win32.c

2
CMakeLists.txt

@ -20,7 +20,7 @@ set(FILES util.h
data_sizes.h) data_sizes.h)
if (MSVC) if (MSVC)
list(APPEND FILES util.c io_win32.c mmap_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()

221
ethash.h

@ -37,35 +37,15 @@
#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
// LTODO: for consistency's sake maybe use ethash_params_t?
typedef struct ethash_params {
/// Size of full data set (in bytes, multiple of mix size (128)).
uint64_t full_size;
/// Size of compute cache (in bytes, multiple of node size (64)).
uint64_t cache_size;
} ethash_params;
/// Type of a seedhash/blockhash e.t.c. /// Type of a seedhash/blockhash e.t.c.
typedef struct ethash_h256 { uint8_t b[32]; } ethash_h256_t; typedef struct ethash_h256 { uint8_t b[32]; } ethash_h256_t;
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);
}
// convenience macro to statically initialize an h256_t // convenience macro to statically initialize an h256_t
// usage: // usage:
@ -81,55 +61,20 @@ struct ethash_full;
typedef struct ethash_full* ethash_full_t; typedef struct ethash_full* ethash_full_t;
typedef int(*ethash_callback_t)(unsigned); typedef int(*ethash_callback_t)(unsigned);
// LTODO: for consistency's sake maybe use ethash_return_value_t?
typedef struct ethash_return_value { typedef struct ethash_return_value {
ethash_h256_t result; ethash_h256_t result;
ethash_h256_t mix_hash; ethash_h256_t mix_hash;
} ethash_return_value; bool success;
} ethash_return_value_t;
uint64_t ethash_get_datasize(uint32_t const block_number);
uint64_t ethash_get_cachesize(uint32_t const block_number);
// initialize the parameters
static inline void ethash_params_init(ethash_params* params, uint32_t const block_number)
{
params->full_size = ethash_get_datasize(block_number);
params->cache_size = ethash_get_cachesize(block_number);
}
// LTODO: for consistency's sake maybe use ethash_cache_t?
typedef struct ethash_cache {
void* mem;
} ethash_cache;
/**
* Allocate and initialize a new ethash_cache object
*
* @param params The parameters to initialize it with. We are interested in
* the cache_size from here
* @param seed Block seedhash to be used during the computation of the
* cache nodes
* @return Newly allocated ethash_cache on success or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
*/
ethash_cache* ethash_cache_new(ethash_params const* params, ethash_h256_t const* seed);
/**
* Frees a previously allocated ethash_cache
* @param c The object to free
*/
void ethash_cache_delete(ethash_cache* c);
/** /**
* Allocate and initialize a new ethash_light handler * Allocate and initialize a new ethash_light handler
* *
* @param params The parameters to initialize it with. We are interested in * @param block_number The block number for which to create the handler
* the cache_size from here * @return Newly allocated ethash_light handler or NULL in case of
* @param seed Block seedhash to be used during the computation of the * ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
* 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(ethash_params const* params, ethash_h256_t const* seed); ethash_light_t ethash_light_new(uint64_t block_number);
/** /**
* Frees a previously allocated ethash_light handler * Frees a previously allocated ethash_light handler
* @param light The light handler to free * @param light The light handler to free
@ -138,63 +83,33 @@ void ethash_light_delete(ethash_light_t light);
/** /**
* Calculate the light client data * Calculate the light client data
* *
* @param ret An object of ethash_return_value to hold the return value
* @param light The light client handler * @param light The light client handler
* @param params The parameters to use
* @param header_hash The header hash to pack into the mix * @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix * @param nonce The nonce to pack into the mix
* @return true if all went well and false if there were invalid * @return an object of ethash_return_value_t holding the return values
* parameters given.
*/ */
bool ethash_light_compute( ethash_return_value_t ethash_light_compute(
ethash_return_value* ret,
ethash_light_t light, ethash_light_t light,
ethash_params const* params, ethash_h256_t const header_hash,
const ethash_h256_t* header_hash, uint64_t nonce
uint64_t const nonce
); );
/**
* Get a pointer to the cache object held by the light client
*
* @param light The light client whose cache to request
* @return A pointer to the cache held by the light client or NULL if
* there was no cache in the first place
*/
ethash_cache* ethash_light_get_cache(ethash_light_t light);
/**
* Move the memory ownership of the cache somewhere else
*
* @param light The light client whose cache's memory ownership to acquire.
* After this function concludes it will no longer have a cache.
* @return A pointer to the moved cache or NULL if there was no cache in the first place
*/
ethash_cache* ethash_light_acquire_cache(ethash_light_t light);
/** /**
* Allocate and initialize a new ethash_full handler * Allocate and initialize a new ethash_full handler
* *
* @param dirname The directory in which to put the DAG file. * @param light The light handler containing the cache.
* @param seedhash The seed hash of the block. Used in the DAG file naming. * @param callback A callback function with signature of @ref ethash_callback_t
* @param params The parameters to initialize it with. We are interested in * It accepts an unsigned with which a progress of DAG calculation
* the full_size from here * can be displayed. If all goes well the callback should return 0.
* @param cache A cache object to use that was allocated with @ref ethash_cache_new(). * If a non-zero value is returned then DAG generation will stop.
* Iff this function succeeds the ethash_full_t will take memory * Be advised. A progress value of 100 means that DAG creation is
* ownership of the cache and free it at deletion. If not then the user * almost complete and that this function will soon return succesfully.
* still has to handle freeing of the cache himself. * It does not mean that the function has already had a succesfull return.
* @param callback A callback function with signature of @ref ethash_callback_t * @return Newly allocated ethash_full handler or NULL in case of
* It accepts an unsigned with which a progress of DAG calculation * ERRNOMEM or invalid parameters used for @ref ethash_compute_full_data()
* 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( ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback);
char const* dirname,
ethash_h256_t const* seed_hash,
ethash_params const* params,
ethash_cache const* cache,
ethash_callback_t callback
);
/** /**
* Frees a previously allocated ethash_full handler * Frees a previously allocated ethash_full handler
* @param full The light handler to free * @param full The light handler to free
@ -203,97 +118,29 @@ void ethash_full_delete(ethash_full_t full);
/** /**
* Calculate the full client data * Calculate the full client data
* *
* @param ret An object of ethash_return_value to hold the return value
* @param full The full client handler * @param full The full client handler
* @param params The parameters to use
* @param header_hash The header hash to pack into the mix * @param header_hash The header hash to pack into the mix
* @param nonce The nonce to pack into the mix * @param nonce The nonce to pack into the mix
* @return true if all went well and false if there were invalid * @return An object of ethash_return_value to hold the return value
* parameters given or if there was a callback given and
* at some point return a non-zero value
*/ */
bool ethash_full_compute( ethash_return_value_t ethash_full_compute(
ethash_return_value* ret,
ethash_full_t full, ethash_full_t full,
ethash_params const* params, ethash_h256_t const header_hash,
ethash_h256_t const* header_hash, uint64_t nonce
uint64_t const nonce
); );
/** /**
* Get a pointer to the cache object held by the full client * Get a pointer to the full DAG data
*
* @param full The full client whose cache to request
* @return A pointer to the cache held by the full client or NULL
* if there was no cache in the first place
*/ */
ethash_cache* ethash_full_get_cache(ethash_full_t full); void const* ethash_full_dag(ethash_full_t full);
/** /**
* Move the memory ownership of the cache somewhere else * Get the size of the DAG data
*
* @param full The full client whose cache's memory ownership to acquire.
* After this function concludes it will no longer have a cache.
* @return A pointer to the moved cache or NULL if there was no cache in the first place
*/ */
ethash_cache* ethash_full_acquire_cache(ethash_full_t full); uint64_t ethash_full_dag_size(ethash_full_t full);
void ethash_get_seedhash(ethash_h256_t *seedhash, const uint32_t block_number);
// Returns if hash is less than or equal to difficulty
static inline int 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 1;
}
int 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
);
/** /**
* ========================= * Calculate the seedhash for a given block number
* = DEPRECATED API =
* =========================
*
* Kept for backwards compatibility with whoever still uses it. Please consider
* switching to the new API (look above)
*/
void ethash_mkcache(ethash_cache* cache, ethash_params const* params, ethash_h256_t const* seed);
void ethash_full(
ethash_return_value* ret,
void const* full_mem,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
);
void ethash_light(
ethash_return_value* ret,
ethash_cache const* cache,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
);
/**
* Compute the memory data for a full node's memory
*
* @param mem A pointer to an ethash full's memory
* @param params The parameters to compute the data with
* @param cache A cache object to use in the calculation
* @return true if all went fine and false for invalid parameters
*/ */
bool ethash_compute_full_data(void* mem, ethash_params const* params, ethash_cache const* cache); ethash_h256_t ethash_get_seedhash(uint64_t block_number);
#ifdef __cplusplus #ifdef __cplusplus
} }

347
internal.c

@ -41,13 +41,13 @@
#include "sha3.h" #include "sha3.h"
#endif // WITH_CRYPTOPP #endif // WITH_CRYPTOPP
uint64_t ethash_get_datasize(uint32_t const block_number) uint64_t ethash_get_datasize(uint64_t const block_number)
{ {
assert(block_number / ETHASH_EPOCH_LENGTH < 2048); assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return dag_sizes[block_number / ETHASH_EPOCH_LENGTH]; return dag_sizes[block_number / ETHASH_EPOCH_LENGTH];
} }
uint64_t ethash_get_cachesize(uint32_t const block_number) uint64_t ethash_get_cachesize(uint64_t const block_number)
{ {
assert(block_number / ETHASH_EPOCH_LENGTH < 2048); assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
return cache_sizes[block_number / ETHASH_EPOCH_LENGTH]; return cache_sizes[block_number / ETHASH_EPOCH_LENGTH];
@ -58,27 +58,27 @@ uint64_t ethash_get_cachesize(uint32_t const block_number)
// SeqMemoHash(s, R, N) // SeqMemoHash(s, R, N)
bool 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,
ethash_h256_t const* seed ethash_h256_t const* seed
) )
{ {
if (params->cache_size % sizeof(node) != 0) { if (cache_size % sizeof(node) != 0) {
return false; return false;
} }
uint32_t const num_nodes = (uint32_t) (params->cache_size / sizeof(node)); uint32_t const num_nodes = (uint32_t) (cache_size / sizeof(node));
SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32); SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32);
for (unsigned i = 1; i != num_nodes; ++i) { for (uint32_t i = 1; i != num_nodes; ++i) {
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64); SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
} }
for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) { for (uint32_t j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) { for (uint32_t i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes; uint32_t const idx = nodes[i].words[0] % num_nodes;
node data; node data;
data = nodes[(num_nodes - 1 + i) % num_nodes]; data = nodes[(num_nodes - 1 + i) % num_nodes];
for (unsigned w = 0; w != NODE_WORDS; ++w) { for (uint32_t w = 0; w != NODE_WORDS; ++w) {
data.words[w] ^= nodes[idx].words[w]; data.words[w] ^= nodes[idx].words[w];
} }
SHA3_512(nodes[i].bytes, data.bytes, sizeof(data)); SHA3_512(nodes[i].bytes, data.bytes, sizeof(data));
@ -90,46 +90,14 @@ bool static ethash_compute_cache_nodes(
return true; return true;
} }
ethash_cache* ethash_cache_new(ethash_params const* params, ethash_h256_t const* seed)
{
ethash_cache* ret;
ret = malloc(sizeof(*ret));
if (!ret) {
return NULL;
}
ret->mem = malloc((size_t)params->cache_size);
if (!ret->mem) {
goto fail_free_cache;
}
node* nodes = (node*)ret->mem;
if (!ethash_compute_cache_nodes(nodes, params, seed)) {
goto fail_free_cache_mem;
}
return ret;
fail_free_cache_mem:
free(ret->mem);
fail_free_cache:
free(ret);
return NULL;
}
void ethash_cache_delete(ethash_cache* c)
{
free(c->mem);
free(c);
}
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 struct ethash_cache *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->mem; 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;
@ -142,8 +110,8 @@ void ethash_calculate_dag_item(
__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
@ -176,40 +144,50 @@ void ethash_calculate_dag_item(
bool ethash_compute_full_data( bool ethash_compute_full_data(
void* mem, void* mem,
ethash_params const* params, uint64_t full_size,
ethash_cache const* cache ethash_light_t const light,
ethash_callback_t callback
) )
{ {
if (params->full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 || if (full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 ||
(params->full_size % sizeof(node)) != 0) { (full_size % sizeof(node)) != 0) {
return false; return false;
} }
uint32_t const max_n = (uint32_t)(full_size / sizeof(node));
node* full_nodes = mem; node* full_nodes = mem;
double const progress_change = 1.0f / max_n;
double progress = 0.0f;
// now compute full nodes // now compute full nodes
for (unsigned n = 0; n != (params->full_size / sizeof(node)); ++n) { for (uint32_t n = 0; n != max_n; ++n) {
ethash_calculate_dag_item(&(full_nodes[n]), n, params, cache); 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; return true;
} }
static bool ethash_hash( static bool ethash_hash(
ethash_return_value* ret, ethash_return_value_t* ret,
node const* full_nodes, node const* full_nodes,
ethash_cache const* cache, ethash_light_t const light,
ethash_params const* params, uint64_t full_size,
ethash_h256_t const* header_hash, ethash_h256_t const header_hash,
uint64_t const nonce, uint64_t const nonce
ethash_callback_t callback
) )
{ {
if (params->full_size % MIX_WORDS != 0) { if (full_size % MIX_WORDS != 0) {
return false; return false;
} }
// pack hash and nonce together into first 40 bytes of s_mix // pack hash and nonce together into first 40 bytes of s_mix
assert(sizeof(node) * 8 == 512); assert(sizeof(node) * 8 == 512);
node s_mix[MIX_NODES + 1]; node s_mix[MIX_NODES + 1];
memcpy(s_mix[0].bytes, header_hash, 32); memcpy(s_mix[0].bytes, &header_hash, 32);
fix_endian64(s_mix[0].double_words[4], nonce); fix_endian64(s_mix[0].double_words[4], nonce);
// compute sha3-512 hash and replicate across mix // compute sha3-512 hash and replicate across mix
@ -217,30 +195,23 @@ static bool ethash_hash(
fix_endian_arr32(s_mix[0].words, 16); fix_endian_arr32(s_mix[0].words, 16);
node* const mix = s_mix + 1; node* const mix = s_mix + 1;
for (unsigned w = 0; w != MIX_WORDS; ++w) { for (uint32_t w = 0; w != MIX_WORDS; ++w) {
mix->words[w] = s_mix[0].words[w % NODE_WORDS]; mix->words[w] = s_mix[0].words[w % NODE_WORDS];
} }
unsigned const page_size = sizeof(uint32_t) * MIX_WORDS; unsigned const page_size = sizeof(uint32_t) * MIX_WORDS;
unsigned const num_full_pages = (unsigned) (params->full_size / page_size); unsigned const num_full_pages = (unsigned) (full_size / page_size);
double const progress_change = 1.0f / ETHASH_ACCESSES / MIX_NODES;
double progress = 0.0f;
for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) { for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) {
uint32_t const index = ((s_mix->words[0] ^ i) * FNV_PRIME ^ mix->words[i % MIX_WORDS]) % num_full_pages; 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) { for (unsigned n = 0; n != MIX_NODES; ++n) {
node const* dag_node; node const* dag_node;
if (callback &&
callback((unsigned int)(ceil(progress * 100.0f))) != 0) {
return false;
}
progress += progress_change;
if (full_nodes) { if (full_nodes) {
dag_node = &full_nodes[MIX_NODES * index + n]; dag_node = &full_nodes[MIX_NODES * index + n];
} else { } else {
node tmp_node; node tmp_node;
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, params, cache); ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light);
dag_node = &tmp_node; dag_node = &tmp_node;
} }
@ -268,7 +239,7 @@ static bool ethash_hash(
} }
// compress mix // compress mix
for (unsigned w = 0; w != MIX_WORDS; w += 4) { for (uint32_t w = 0; w != MIX_WORDS; w += 4) {
uint32_t reduction = mix->words[w + 0]; uint32_t reduction = mix->words[w + 0];
reduction = reduction * FNV_PRIME ^ mix->words[w + 1]; reduction = reduction * FNV_PRIME ^ mix->words[w + 1];
reduction = reduction * FNV_PRIME ^ mix->words[w + 2]; reduction = reduction * FNV_PRIME ^ mix->words[w + 2];
@ -299,12 +270,14 @@ void ethash_quick_hash(
SHA3_256(return_hash, buf, 64 + 32); SHA3_256(return_hash, buf, 64 + 32);
} }
void ethash_get_seedhash(ethash_h256_t* seedhash, const uint32_t block_number) ethash_h256_t ethash_get_seedhash(uint64_t block_number)
{ {
ethash_h256_reset(seedhash); ethash_h256_t ret;
const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH; ethash_h256_reset(&ret);
uint64_t const epochs = block_number / ETHASH_EPOCH_LENGTH;
for (uint32_t i = 0; i < epochs; ++i) for (uint32_t i = 0; i < epochs; ++i)
SHA3_256(seedhash, (uint8_t*)seedhash, 32); SHA3_256(&ret, (uint8_t*)&ret, 32);
return ret;
} }
int ethash_quick_check_difficulty( int ethash_quick_check_difficulty(
@ -320,111 +293,150 @@ int ethash_quick_check_difficulty(
return ethash_check_difficulty(&return_hash, difficulty); return ethash_check_difficulty(&return_hash, difficulty);
} }
ethash_light_t ethash_light_new(ethash_params const* params, ethash_h256_t const* seed) ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed)
{ {
struct ethash_light *ret; struct ethash_light *ret;
ret = calloc(sizeof(*ret), 1); ret = calloc(sizeof(*ret), 1);
if (!ret) { if (!ret) {
return NULL; return NULL;
} }
ret->cache = ethash_cache_new(params, seed); ret->cache = malloc((size_t)cache_size);
if (!ret->cache) { if (!ret->cache) {
goto fail_free_light; 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; return ret;
fail_free_cache_mem:
free(ret->cache);
fail_free_light: fail_free_light:
free(ret); free(ret);
return NULL; return NULL;
} }
ethash_light_t ethash_light_new(uint64_t block_number)
{
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;
}
void ethash_light_delete(ethash_light_t light) void ethash_light_delete(ethash_light_t light)
{ {
if (light->cache) { if (light->cache) {
ethash_cache_delete(light->cache); free(light->cache);
} }
free(light); free(light);
} }
bool ethash_light_compute( ethash_return_value_t ethash_light_compute_internal(
ethash_return_value* ret,
ethash_light_t light, ethash_light_t light,
ethash_params const* params, uint64_t full_size,
const ethash_h256_t* header_hash, ethash_h256_t const header_hash,
uint64_t const nonce uint64_t nonce
) )
{ {
return ethash_hash(ret, NULL, light->cache, params, header_hash, nonce, NULL); ethash_return_value_t ret;
ret.success = true;
if (!ethash_hash(&ret, NULL, light, full_size, header_hash, nonce)) {
ret.success = false;
}
return ret;
} }
ethash_cache *ethash_light_get_cache(ethash_light_t light) ethash_return_value_t ethash_light_compute(
ethash_light_t light,
ethash_h256_t const header_hash,
uint64_t nonce
)
{ {
return light->cache; uint64_t full_size = ethash_get_datasize(light->block_number);
return ethash_light_compute_internal(light, full_size, header_hash, nonce);
} }
ethash_cache *ethash_light_acquire_cache(ethash_light_t light) static bool ethash_mmap(struct ethash_full* ret, FILE* f)
{ {
ethash_cache* ret = light->cache; int fd;
light->cache = 0; char* mmapped_data;
return ret; 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;
} }
ethash_full_t ethash_full_new( ethash_full_t ethash_full_new_internal(
char const* dirname, char const* dirname,
ethash_h256_t const* seed_hash, ethash_h256_t const seed_hash,
ethash_params const* params, uint64_t full_size,
ethash_cache const* cache, ethash_light_t const light,
ethash_callback_t callback ethash_callback_t callback
) )
{ {
struct ethash_full* ret; struct ethash_full* ret;
int fd;
FILE *f = NULL; FILE *f = NULL;
bool match = false;
ret = calloc(sizeof(*ret), 1); ret = calloc(sizeof(*ret), 1);
if (!ret) { if (!ret) {
return NULL; return NULL;
} }
ret->file_size = (size_t)full_size;
ret->cache = (ethash_cache*)cache; switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) {
ret->file_size = (size_t)params->full_size;
switch (ethash_io_prepare(dirname, *seed_hash, &f, (size_t)params->full_size)) {
case ETHASH_IO_FAIL: case ETHASH_IO_FAIL:
case ETHASH_IO_MEMO_SIZE_MISMATCH:
goto fail_free_full; goto fail_free_full;
case ETHASH_IO_MEMO_MATCH: case ETHASH_IO_MEMO_MATCH:
match = true; if (!ethash_mmap(ret, f)) {
case ETHASH_IO_MEMO_MISMATCH: goto fail_close_file;
ret->file = f; }
if ((fd = ethash_fileno(ret->file)) == -1) { 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; goto fail_free_full;
} }
ret->data = mmap( // fallthrough to the mismatch case here, DO NOT go through match
NULL, case ETHASH_IO_MEMO_MISMATCH:
(size_t)params->full_size, if (!ethash_mmap(ret, f)) {
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (ret->data == MAP_FAILED) {
goto fail_close_file; goto fail_close_file;
} }
if (match) {
return ret;
}
break; break;
} }
if (!ethash_compute_full_data(ret->data, params, cache)) { 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; goto fail_free_full_data;
} }
ret->callback = callback; 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; return ret;
fail_free_full_data: fail_free_full_data:
// could check that munmap(..) == 0 but even if it did not can't really do anything here // could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(ret->data, (size_t)params->full_size); munmap(ret->data, (size_t)full_size);
fail_close_file: fail_close_file:
fclose(ret->file); fclose(ret->file);
fail_free_full: fail_free_full:
@ -432,82 +444,53 @@ fail_free_full:
return NULL; return NULL;
} }
void ethash_full_delete(ethash_full_t full) ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback)
{ {
if (full->cache) { char strbuf[256];
ethash_cache_delete(full->cache); if (!ethash_get_default_dirname(strbuf, 256)) {
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);
}
void ethash_full_delete(ethash_full_t full)
{
// could check that munmap(..) == 0 but even if it did not can't really do anything here // could check that munmap(..) == 0 but even if it did not can't really do anything here
munmap(full->data, full->file_size); munmap(full->data, (size_t)full->file_size);
if (full->file) { if (full->file) {
fclose(full->file); fclose(full->file);
} }
free(full); free(full);
} }
bool ethash_full_compute( ethash_return_value_t ethash_full_compute(
ethash_return_value* ret,
ethash_full_t full, ethash_full_t full,
ethash_params const* params, ethash_h256_t const header_hash,
ethash_h256_t const* header_hash, uint64_t nonce
uint64_t const nonce
) )
{ {
return ethash_hash(ret, ethash_return_value_t ret;
(node const*)full->data, ret.success = true;
NULL, if (!ethash_hash(
params, &ret,
header_hash, (node const*)full->data,
nonce, NULL,
full->callback); full->file_size,
} header_hash,
nonce)) {
ethash_cache* ethash_full_get_cache(ethash_full_t full) ret.success = false;
{ }
return full->cache;
}
ethash_cache* ethash_full_acquire_cache(ethash_full_t full)
{
ethash_cache* ret = full->cache;
full->cache = 0;
return ret; return ret;
} }
/** void const* ethash_full_dag(ethash_full_t full)
* =========================
* = DEPRECATED API =
* =========================
*
* Kept for backwards compatibility with whoever still uses it. Please consider
* switching to the new API (look above)
*/
void ethash_mkcache(
ethash_cache* cache,
ethash_params const* params,
ethash_h256_t const* seed
)
{ {
node* nodes = (node*) cache->mem; return full->data;
ethash_compute_cache_nodes(nodes, params, seed);
} }
void ethash_full(
ethash_return_value* ret, uint64_t ethash_full_dag_size(ethash_full_t full)
void const* full_mem,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
)
{
ethash_hash(ret, (node const *) full_mem, NULL, params, header_hash, nonce, NULL);
}
void ethash_light(
ethash_return_value* ret,
ethash_cache const* cache,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
)
{ {
ethash_hash(ret, NULL, cache, params, header_hash, nonce, NULL); return full->file_size;
} }

122
internal.h

@ -31,23 +31,112 @@ 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 int 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 1;
}
int 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 { struct ethash_light {
ethash_cache* cache; 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 { struct ethash_full {
FILE* file; FILE* file;
size_t file_size; uint64_t file_size;
ethash_cache* cache;
node* data; node* data;
ethash_callback_t callback;
}; };
/**
* 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
ethash_cache const* cache
); );
void ethash_quick_hash( void ethash_quick_hash(
@ -57,6 +146,25 @@ void ethash_quick_hash(
ethash_h256_t const* mix_hash 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
} }
#endif #endif

75
io.c

@ -26,7 +26,8 @@ enum ethash_io_rc ethash_io_prepare(
char const* dirname, char const* dirname,
ethash_h256_t const seedhash, ethash_h256_t const seedhash,
FILE** output_file, FILE** output_file,
size_t file_size uint64_t file_size,
bool force_create
) )
{ {
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE]; char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
@ -43,36 +44,54 @@ enum ethash_io_rc ethash_io_prepare(
goto end; goto end;
} }
// try to open the file FILE *f;
FILE* f = ethash_fopen(tmpfile, "rb+"); if (!force_create) {
if (f) { // try to open the file
size_t found_size; f = ethash_fopen(tmpfile, "rb+");
if (!ethash_file_size(f, &found_size)) { if (f) {
fclose(f); size_t found_size;
goto free_memo; 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;
} }
if (file_size != found_size) {
fclose(f);
ret = ETHASH_IO_MEMO_SIZE_MISMATCH;
goto free_memo;
}
} else {
// 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, file_size - 1, SEEK_SET) != 0) {
fclose(f);
goto free_memo;
}
fputc('\n', f);
fflush(f);
ret = ETHASH_IO_MEMO_MISMATCH;
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;
ret = ETHASH_IO_MEMO_MATCH; ret = ETHASH_IO_MEMO_MATCH;
set_file: set_file:
*output_file = f; *output_file = f;

52
io.h

@ -69,13 +69,16 @@ enum ethash_io_rc {
* mode, while on the case of mismatch a new file is created * mode, while on the case of mismatch a new file is created
* on write mode * on write mode
* @param[in] file_size The size that the DAG file should have on disk * @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 * @return For possible return values @see enum ethash_io_rc
*/ */
enum ethash_io_rc ethash_io_prepare( enum ethash_io_rc ethash_io_prepare(
char const* dirname, char const* dirname,
ethash_h256_t const seedhash, ethash_h256_t const seedhash,
FILE** output_file, FILE** output_file,
size_t file_size uint64_t file_size,
bool force_create
); );
/** /**
@ -136,6 +139,33 @@ bool ethash_file_size(FILE* f, size_t* ret_size);
*/ */
int ethash_fileno(FILE* f); 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( static inline bool ethash_io_mutable_name(
uint32_t revision, uint32_t revision,
ethash_h256_t const* seed_hash, ethash_h256_t const* seed_hash,
@ -149,26 +179,6 @@ static inline bool ethash_io_mutable_name(
return snprintf(output, DAG_MUTABLE_NAME_MAX_SIZE, "%u_%016" PRIx64, revision, hash) >= 0; return snprintf(output, DAG_MUTABLE_NAME_MAX_SIZE, "%u_%016" PRIx64, revision, hash) >= 0;
} }
static inline char* ethash_io_create_filename(
char const* dirname,
char const* filename,
size_t filename_length
)
{
size_t dirlen = strlen(dirname);
// in C the cast is not needed, but a C++ compiler will complain for invalid conversion
char* name = (char*)malloc(dirlen + filename_length + 1);
if (!name) {
return NULL;
}
name[0] = '\0';
ethash_strncat(name, dirlen + filename_length + 1, dirname, dirlen);
ethash_strncat(name, dirlen + filename_length + 1, filename, filename_length);
return name;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

42
io_posix.c

@ -48,6 +48,31 @@ int ethash_fileno(FILE *f)
return fileno(f); return fileno(f);
} }
char* ethash_io_create_filename(
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;
}
name[0] = '\0';
ethash_strncat(name, dest_size, dirname, dirlen);
if (dirname[dirlen] != '/') {
ethash_strncat(name, dest_size, "/", 1);
}
ethash_strncat(name, dest_size, filename, filename_length);
return name;
}
bool ethash_file_size(FILE* f, size_t* ret_size) bool ethash_file_size(FILE* f, size_t* ret_size)
{ {
struct stat st; struct stat st;
@ -58,3 +83,20 @@ bool ethash_file_size(FILE* f, size_t* ret_size)
*ret_size = st.st_size; *ret_size = st.st_size;
return true; return true;
} }
bool ethash_get_default_dirname(char* strbuf, size_t buffsize)
{
static const char dir_suffix[] = ".ethash/";
strbuf[0] = '\0';
char* home_dir = getenv("HOME");
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));
}

40
io_win32.c

@ -25,6 +25,7 @@
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <Shlobj.h>
FILE* ethash_fopen(char const* file_name, char const* mode) FILE* ethash_fopen(char const* file_name, char const* mode)
{ {
@ -48,6 +49,31 @@ int ethash_fileno(FILE* f)
return _fileno(f); return _fileno(f);
} }
char* ethash_io_create_filename(
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;
}
bool ethash_file_size(FILE* f, size_t* ret_size) bool ethash_file_size(FILE* f, size_t* ret_size)
{ {
struct _stat st; struct _stat st;
@ -58,3 +84,17 @@ bool ethash_file_size(FILE* f, size_t* ret_size)
*ret_size = st.st_size; *ret_size = st.st_size;
return true; return true;
} }
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;
}
return ethash_strncat(strbuf, buffsize, dir_suffix, sizeof(dir_suffix));
}

0
util.c → util_win32.c

Loading…
Cancel
Save