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. 56
      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)
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()
list(APPEND FILES io_posix.c)
endif()

221
ethash.h

@ -37,35 +37,15 @@
#define ETHASH_DATASET_PARENTS 256
#define ETHASH_CACHE_ROUNDS 3
#define ETHASH_ACCESSES 64
#define ETHASH_DAG_MAGIC_NUM_SIZE 8
#define ETHASH_DAG_MAGIC_NUM 0xFEE1DEADBADDCAFE
#ifdef __cplusplus
extern "C" {
#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.
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
// usage:
@ -81,55 +61,20 @@ struct ethash_full;
typedef struct ethash_full* ethash_full_t;
typedef int(*ethash_callback_t)(unsigned);
// LTODO: for consistency's sake maybe use ethash_return_value_t?
typedef struct ethash_return_value {
ethash_h256_t result;
ethash_h256_t mix_hash;
} ethash_return_value;
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);
bool success;
} ethash_return_value_t;
/**
* Allocate and initialize a new ethash_light handler
*
* @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_light handler or NULL in case of
* ERRNOMEM or invalid parameters used for @ref ethash_compute_cache_nodes()
* @param block_number The block number for which to create the handler
* @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
* @param light The light handler to free
@ -138,63 +83,33 @@ void ethash_light_delete(ethash_light_t light);
/**
* 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 params The parameters to use
* @param header_hash The header hash 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
* parameters given.
* @return an object of ethash_return_value_t holding the return values
*/
bool ethash_light_compute(
ethash_return_value* ret,
ethash_return_value_t ethash_light_compute(
ethash_light_t light,
ethash_params const* params,
const ethash_h256_t* header_hash,
uint64_t const nonce
ethash_h256_t const header_hash,
uint64_t 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
*
* @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 params The parameters to initialize it with. We are interested in
* the full_size from here
* @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
* 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()
* @param light The light handler containing the cache.
* @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.
* Be advised. A progress value of 100 means that DAG creation is
* almost complete and that this function will soon return succesfully.
* It does not mean that the function has already had a succesfull return.
* @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(
char const* dirname,
ethash_h256_t const* seed_hash,
ethash_params const* params,
ethash_cache const* cache,
ethash_callback_t callback
);
ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback);
/**
* Frees a previously allocated ethash_full handler
* @param full The light handler to free
@ -203,97 +118,29 @@ void ethash_full_delete(ethash_full_t full);
/**
* 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 params The parameters to use
* @param header_hash The header hash 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
* parameters given or if there was a callback given and
* at some point return a non-zero value
* @return An object of ethash_return_value to hold the return value
*/
bool ethash_full_compute(
ethash_return_value* ret,
ethash_return_value_t ethash_full_compute(
ethash_full_t full,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
ethash_h256_t const header_hash,
uint64_t nonce
);
/**
* Get a pointer to the cache object held by the full client
*
* @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
* Get a pointer to the full DAG data
*/
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
*
* @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
* Get the size of the DAG data
*/
ethash_cache* ethash_full_acquire_cache(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
);
uint64_t ethash_full_dag_size(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);
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
* Calculate the seedhash for a given block number
*/
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
}

347
internal.c

@ -41,13 +41,13 @@
#include "sha3.h"
#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);
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);
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)
bool static ethash_compute_cache_nodes(
node* const nodes,
ethash_params const* params,
uint64_t cache_size,
ethash_h256_t const* seed
)
{
if (params->cache_size % sizeof(node) != 0) {
if (cache_size % sizeof(node) != 0) {
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);
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);
}
for (unsigned j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (unsigned i = 0; i != num_nodes; i++) {
for (uint32_t j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
for (uint32_t i = 0; i != num_nodes; i++) {
uint32_t const idx = nodes[i].words[0] % num_nodes;
node data;
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];
}
SHA3_512(nodes[i].bytes, data.bytes, sizeof(data));
@ -90,46 +90,14 @@ bool static ethash_compute_cache_nodes(
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(
node* const ret,
const unsigned node_index,
const struct ethash_params *params,
const struct ethash_cache *cache
uint32_t node_index,
ethash_light_t const light
)
{
uint32_t num_parent_nodes = (uint32_t) (params->cache_size / sizeof(node));
node const* cache_nodes = (node const *) cache->mem;
uint32_t num_parent_nodes = (uint32_t) (light->cache_size / sizeof(node));
node const* cache_nodes = (node const *) light->cache;
node const* init = &cache_nodes[node_index % num_parent_nodes];
memcpy(ret, init, sizeof(node));
ret->words[0] ^= node_index;
@ -142,8 +110,8 @@ void ethash_calculate_dag_item(
__m128i xmm3 = ret->xmm[3];
#endif
for (unsigned i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
uint32_t parent_index = ((node_index ^ i) * FNV_PRIME ^ ret->words[i % NODE_WORDS]) % num_parent_nodes;
for (uint32_t i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
uint32_t parent_index = fnv_hash(node_index ^ i, ret->words[i % NODE_WORDS]) % num_parent_nodes;
node const *parent = &cache_nodes[parent_index];
#if defined(_M_X64) && ENABLE_SSE
@ -176,40 +144,50 @@ void ethash_calculate_dag_item(
bool ethash_compute_full_data(
void* mem,
ethash_params const* params,
ethash_cache const* cache
uint64_t full_size,
ethash_light_t const light,
ethash_callback_t callback
)
{
if (params->full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 ||
(params->full_size % sizeof(node)) != 0) {
if (full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 ||
(full_size % sizeof(node)) != 0) {
return false;
}
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 (unsigned n = 0; n != (params->full_size / sizeof(node)); ++n) {
ethash_calculate_dag_item(&(full_nodes[n]), n, params, cache);
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 bool ethash_hash(
ethash_return_value* ret,
ethash_return_value_t* ret,
node const* full_nodes,
ethash_cache const* cache,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce,
ethash_callback_t callback
ethash_light_t const light,
uint64_t full_size,
ethash_h256_t const header_hash,
uint64_t const nonce
)
{
if (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);
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
@ -217,30 +195,23 @@ static bool ethash_hash(
fix_endian_arr32(s_mix[0].words, 16);
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];
}
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) {
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) {
node const* dag_node;
if (callback &&
callback((unsigned int)(ceil(progress * 100.0f))) != 0) {
return false;
}
progress += progress_change;
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, params, cache);
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light);
dag_node = &tmp_node;
}
@ -268,7 +239,7 @@ static bool ethash_hash(
}
// 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];
reduction = reduction * FNV_PRIME ^ mix->words[w + 1];
reduction = reduction * FNV_PRIME ^ mix->words[w + 2];
@ -299,12 +270,14 @@ void ethash_quick_hash(
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);
const uint32_t epochs = block_number / ETHASH_EPOCH_LENGTH;
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(seedhash, (uint8_t*)seedhash, 32);
SHA3_256(&ret, (uint8_t*)&ret, 32);
return ret;
}
int ethash_quick_check_difficulty(
@ -320,111 +293,150 @@ int ethash_quick_check_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;
ret = calloc(sizeof(*ret), 1);
if (!ret) {
return NULL;
}
ret->cache = ethash_cache_new(params, seed);
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;
}
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)
{
if (light->cache) {
ethash_cache_delete(light->cache);
free(light->cache);
}
free(light);
}
bool ethash_light_compute(
ethash_return_value* ret,
ethash_return_value_t ethash_light_compute_internal(
ethash_light_t light,
ethash_params const* params,
const ethash_h256_t* header_hash,
uint64_t const nonce
uint64_t full_size,
ethash_h256_t const header_hash,
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;
light->cache = 0;
return ret;
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;
}
ethash_full_t ethash_full_new(
ethash_full_t ethash_full_new_internal(
char const* dirname,
ethash_h256_t const* seed_hash,
ethash_params const* params,
ethash_cache const* cache,
ethash_h256_t const seed_hash,
uint64_t full_size,
ethash_light_t const light,
ethash_callback_t callback
)
{
struct ethash_full* ret;
int fd;
FILE *f = NULL;
bool match = false;
ret = calloc(sizeof(*ret), 1);
if (!ret) {
return NULL;
}
ret->cache = (ethash_cache*)cache;
ret->file_size = (size_t)params->full_size;
switch (ethash_io_prepare(dirname, *seed_hash, &f, (size_t)params->full_size)) {
ret->file_size = (size_t)full_size;
switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) {
case ETHASH_IO_FAIL:
case ETHASH_IO_MEMO_SIZE_MISMATCH:
goto fail_free_full;
case ETHASH_IO_MEMO_MATCH:
match = true;
case ETHASH_IO_MEMO_MISMATCH:
ret->file = f;
if ((fd = ethash_fileno(ret->file)) == -1) {
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;
}
ret->data = mmap(
NULL,
(size_t)params->full_size,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (ret->data == MAP_FAILED) {
// 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;
}
if (match) {
return ret;
}
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;
}
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;
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)params->full_size);
munmap(ret->data, (size_t)full_size);
fail_close_file:
fclose(ret->file);
fail_free_full:
@ -432,82 +444,53 @@ fail_free_full:
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) {
ethash_cache_delete(full->cache);
char strbuf[256];
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
munmap(full->data, full->file_size);
munmap(full->data, (size_t)full->file_size);
if (full->file) {
fclose(full->file);
}
free(full);
}
bool ethash_full_compute(
ethash_return_value* ret,
ethash_return_value_t ethash_full_compute(
ethash_full_t full,
ethash_params const* params,
ethash_h256_t const* header_hash,
uint64_t const nonce
ethash_h256_t const header_hash,
uint64_t nonce
)
{
return ethash_hash(ret,
(node const*)full->data,
NULL,
params,
header_hash,
nonce,
full->callback);
}
ethash_cache* ethash_full_get_cache(ethash_full_t full)
{
return full->cache;
}
ethash_cache* ethash_full_acquire_cache(ethash_full_t full)
{
ethash_cache* ret = full->cache;
full->cache = 0;
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;
}
/**
* =========================
* = 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 const* ethash_full_dag(ethash_full_t full)
{
node* nodes = (node*) cache->mem;
ethash_compute_cache_nodes(nodes, params, seed);
return full->data;
}
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
)
{
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
)
uint64_t ethash_full_dag_size(ethash_full_t full)
{
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;
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 {
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 {
FILE* file;
size_t file_size;
ethash_cache* cache;
uint64_t file_size;
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(
node* const ret,
const unsigned node_index,
ethash_params const* params,
ethash_cache const* cache
uint32_t node_index,
ethash_light_t const cache
);
void ethash_quick_hash(
@ -57,6 +146,25 @@ void ethash_quick_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
}
#endif

75
io.c

@ -26,7 +26,8 @@ enum ethash_io_rc ethash_io_prepare(
char const* dirname,
ethash_h256_t const seedhash,
FILE** output_file,
size_t file_size
uint64_t file_size,
bool force_create
)
{
char mutable_name[DAG_MUTABLE_NAME_MAX_SIZE];
@ -43,35 +44,53 @@ enum ethash_io_rc ethash_io_prepare(
goto end;
}
// try to open the file
FILE* f = ethash_fopen(tmpfile, "rb+");
if (f) {
size_t found_size;
if (!ethash_file_size(f, &found_size)) {
fclose(f);
goto free_memo;
FILE *f;
if (!force_create) {
// 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;
}
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;
set_file:

56
io.h

@ -69,13 +69,16 @@ enum ethash_io_rc {
* mode, while on the case of mismatch a new file is created
* on write mode
* @param[in] file_size The size that the DAG file should have on disk
* @param[out] force_create If true then there is no check to see if the file
* already exists
* @return For possible return values @see enum ethash_io_rc
*/
enum ethash_io_rc ethash_io_prepare(
char const* dirname,
ethash_h256_t const seedhash,
FILE** output_file,
size_t file_size
uint64_t file_size,
bool force_create
);
/**
@ -112,7 +115,7 @@ char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count
/**
* A cross-platform mkdir wrapper to create a directory or assert it's there
*
*
* @param dirname The full path of the directory to create
* @return true if the directory was created or if it already
* existed
@ -130,12 +133,39 @@ 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,
@ -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;
}
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
}
#endif

42
io_posix.c

@ -48,6 +48,31 @@ int ethash_fileno(FILE *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)
{
struct stat st;
@ -58,3 +83,20 @@ bool ethash_file_size(FILE* f, size_t* ret_size)
*ret_size = st.st_size;
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 <sys/stat.h>
#include <sys/types.h>
#include <Shlobj.h>
FILE* ethash_fopen(char const* file_name, char const* mode)
{
@ -48,6 +49,31 @@ int ethash_fileno(FILE* 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)
{
struct _stat st;
@ -58,3 +84,17 @@ bool ethash_file_size(FILE* f, size_t* ret_size)
*ret_size = st.st_size;
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