ethash 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.gitmodules +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +21 -0
- data/README.md +31 -0
- data/Rakefile +15 -0
- data/ethash.gemspec +23 -0
- data/ext/ethash/ethash.c +75 -0
- data/ext/ethash/extconf.rb +7 -0
- data/ext/ethash/headers/compiler.h +33 -0
- data/ext/ethash/headers/data_sizes.h +812 -0
- data/ext/ethash/headers/endian.h +79 -0
- data/ext/ethash/headers/ethash.h +147 -0
- data/ext/ethash/headers/fnv.h +43 -0
- data/ext/ethash/headers/internal.h +179 -0
- data/ext/ethash/headers/io.h +202 -0
- data/ext/ethash/headers/mmap.h +47 -0
- data/ext/ethash/headers/sha3.h +31 -0
- data/ext/ethash/headers/sha3_cryptopp.h +18 -0
- data/ext/ethash/headers/util.h +47 -0
- data/ext/ethash/internal.c +508 -0
- data/ext/ethash/io.c +119 -0
- data/ext/ethash/sha3.c +151 -0
- data/lib/ethash.rb +3 -0
- data/lib/ethash/version.rb +5 -0
- data/test/ethash_test.rb +26 -0
- metadata +130 -0
@@ -0,0 +1,202 @@
|
|
1
|
+
/*
|
2
|
+
This file is part of ethash.
|
3
|
+
|
4
|
+
ethash is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
ethash is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with ethash. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
*/
|
17
|
+
/** @file io.h
|
18
|
+
* @author Lefteris Karapetsas <lefteris@ethdev.com>
|
19
|
+
* @date 2015
|
20
|
+
*/
|
21
|
+
#pragma once
|
22
|
+
#include <stdlib.h>
|
23
|
+
#include <stdint.h>
|
24
|
+
#include <stdbool.h>
|
25
|
+
#include <stdio.h>
|
26
|
+
#ifdef __cplusplus
|
27
|
+
#define __STDC_FORMAT_MACROS 1
|
28
|
+
#endif
|
29
|
+
#include <inttypes.h>
|
30
|
+
#include "endian.h"
|
31
|
+
#include "ethash.h"
|
32
|
+
|
33
|
+
#ifdef __cplusplus
|
34
|
+
extern "C" {
|
35
|
+
#endif
|
36
|
+
// Maximum size for mutable part of DAG file name
|
37
|
+
// 6 is for "full-R", the suffix of the filename
|
38
|
+
// 10 is for maximum number of digits of a uint32_t (for REVISION)
|
39
|
+
// 1 is for - and 16 is for the first 16 hex digits for first 8 bytes of
|
40
|
+
// the seedhash and last 1 is for the null terminating character
|
41
|
+
// Reference: https://github.com/ethereum/wiki/wiki/Ethash-DAG
|
42
|
+
#define DAG_MUTABLE_NAME_MAX_SIZE (6 + 10 + 1 + 16 + 1)
|
43
|
+
/// Possible return values of @see ethash_io_prepare
|
44
|
+
enum ethash_io_rc {
|
45
|
+
ETHASH_IO_FAIL = 0, ///< There has been an IO failure
|
46
|
+
ETHASH_IO_MEMO_SIZE_MISMATCH, ///< DAG with revision/hash match, but file size was wrong.
|
47
|
+
ETHASH_IO_MEMO_MISMATCH, ///< The DAG file did not exist or there was revision/hash mismatch
|
48
|
+
ETHASH_IO_MEMO_MATCH, ///< DAG file existed and revision/hash matched. No need to do anything
|
49
|
+
};
|
50
|
+
|
51
|
+
// small hack for windows. I don't feel I should use va_args and forward just
|
52
|
+
// to have this one function properly cross-platform abstracted
|
53
|
+
#if defined(_WIN32) && !defined(__GNUC__)
|
54
|
+
#define snprintf(...) sprintf_s(__VA_ARGS__)
|
55
|
+
#endif
|
56
|
+
|
57
|
+
/**
|
58
|
+
* Logs a critical error in important parts of ethash. Should mostly help
|
59
|
+
* figure out what kind of problem (I/O, memory e.t.c.) causes a NULL
|
60
|
+
* ethash_full_t
|
61
|
+
*/
|
62
|
+
#ifdef ETHASH_PRINT_CRITICAL_OUTPUT
|
63
|
+
#define ETHASH_CRITICAL(...) \
|
64
|
+
do \
|
65
|
+
{ \
|
66
|
+
printf("ETHASH CRITICAL ERROR: "__VA_ARGS__); \
|
67
|
+
printf("\n"); \
|
68
|
+
fflush(stdout); \
|
69
|
+
} while (0)
|
70
|
+
#else
|
71
|
+
#define ETHASH_CRITICAL(...)
|
72
|
+
#endif
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Prepares io for ethash
|
76
|
+
*
|
77
|
+
* Create the DAG directory and the DAG file if they don't exist.
|
78
|
+
*
|
79
|
+
* @param[in] dirname A null terminated c-string of the path of the ethash
|
80
|
+
* data directory. If it does not exist it's created.
|
81
|
+
* @param[in] seedhash The seedhash of the current block number, used in the
|
82
|
+
* naming of the file as can be seen from the spec at:
|
83
|
+
* https://github.com/ethereum/wiki/wiki/Ethash-DAG
|
84
|
+
* @param[out] output_file If there was no failure then this will point to an open
|
85
|
+
* file descriptor. User is responsible for closing it.
|
86
|
+
* In the case of memo match then the file is open on read
|
87
|
+
* mode, while on the case of mismatch a new file is created
|
88
|
+
* on write mode
|
89
|
+
* @param[in] file_size The size that the DAG file should have on disk
|
90
|
+
* @param[out] force_create If true then there is no check to see if the file
|
91
|
+
* already exists
|
92
|
+
* @return For possible return values @see enum ethash_io_rc
|
93
|
+
*/
|
94
|
+
enum ethash_io_rc ethash_io_prepare(
|
95
|
+
char const* dirname,
|
96
|
+
ethash_h256_t const seedhash,
|
97
|
+
FILE** output_file,
|
98
|
+
uint64_t file_size,
|
99
|
+
bool force_create
|
100
|
+
);
|
101
|
+
|
102
|
+
/**
|
103
|
+
* An fopen wrapper for no-warnings crossplatform fopen.
|
104
|
+
*
|
105
|
+
* Msvc compiler considers fopen to be insecure and suggests to use their
|
106
|
+
* alternative. This is a wrapper for this alternative. Another way is to
|
107
|
+
* #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does
|
108
|
+
* not sound like a good idea.
|
109
|
+
*
|
110
|
+
* @param file_name The path to the file to open
|
111
|
+
* @param mode Opening mode. Check fopen()
|
112
|
+
* @return The FILE* or NULL in failure
|
113
|
+
*/
|
114
|
+
FILE* ethash_fopen(char const* file_name, char const* mode);
|
115
|
+
|
116
|
+
/**
|
117
|
+
* An strncat wrapper for no-warnings crossplatform strncat.
|
118
|
+
*
|
119
|
+
* Msvc compiler considers strncat to be insecure and suggests to use their
|
120
|
+
* alternative. This is a wrapper for this alternative. Another way is to
|
121
|
+
* #define _CRT_SECURE_NO_WARNINGS, but disabling all security warnings does
|
122
|
+
* not sound like a good idea.
|
123
|
+
*
|
124
|
+
* @param des Destination buffer
|
125
|
+
* @param dest_size Maximum size of the destination buffer. This is the
|
126
|
+
* extra argument for the MSVC secure strncat
|
127
|
+
* @param src Souce buffer
|
128
|
+
* @param count Number of bytes to copy from source
|
129
|
+
* @return If all is well returns the dest buffer. If there is an
|
130
|
+
* error returns NULL
|
131
|
+
*/
|
132
|
+
char* ethash_strncat(char* dest, size_t dest_size, char const* src, size_t count);
|
133
|
+
|
134
|
+
/**
|
135
|
+
* A cross-platform mkdir wrapper to create a directory or assert it's there
|
136
|
+
*
|
137
|
+
* @param dirname The full path of the directory to create
|
138
|
+
* @return true if the directory was created or if it already
|
139
|
+
* existed
|
140
|
+
*/
|
141
|
+
bool ethash_mkdir(char const* dirname);
|
142
|
+
|
143
|
+
/**
|
144
|
+
* Get a file's size
|
145
|
+
*
|
146
|
+
* @param[in] f The open file stream whose size to get
|
147
|
+
* @param[out] size Pass a size_t by reference to contain the file size
|
148
|
+
* @return true in success and false if there was a failure
|
149
|
+
*/
|
150
|
+
bool ethash_file_size(FILE* f, size_t* ret_size);
|
151
|
+
|
152
|
+
/**
|
153
|
+
* Get a file descriptor number from a FILE stream
|
154
|
+
*
|
155
|
+
* @param f The file stream whose fd to get
|
156
|
+
* @return Platform specific fd handler
|
157
|
+
*/
|
158
|
+
int ethash_fileno(FILE* f);
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Create the filename for the DAG.
|
162
|
+
*
|
163
|
+
* @param dirname The directory name in which the DAG file should reside
|
164
|
+
* If it does not end with a directory separator it is appended.
|
165
|
+
* @param filename The actual name of the file
|
166
|
+
* @param filename_length The length of the filename in bytes
|
167
|
+
* @return A char* containing the full name. User must deallocate.
|
168
|
+
*/
|
169
|
+
char* ethash_io_create_filename(
|
170
|
+
char const* dirname,
|
171
|
+
char const* filename,
|
172
|
+
size_t filename_length
|
173
|
+
);
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Gets the default directory name for the DAG depending on the system
|
177
|
+
*
|
178
|
+
* The spec defining this directory is here: https://github.com/ethereum/wiki/wiki/Ethash-DAG
|
179
|
+
*
|
180
|
+
* @param[out] strbuf A string buffer of sufficient size to keep the
|
181
|
+
* null termninated string of the directory name
|
182
|
+
* @param[in] buffsize Size of @a strbuf in bytes
|
183
|
+
* @return true for success and false otherwise
|
184
|
+
*/
|
185
|
+
bool ethash_get_default_dirname(char* strbuf, size_t buffsize);
|
186
|
+
|
187
|
+
static inline bool ethash_io_mutable_name(
|
188
|
+
uint32_t revision,
|
189
|
+
ethash_h256_t const* seed_hash,
|
190
|
+
char* output
|
191
|
+
)
|
192
|
+
{
|
193
|
+
uint64_t hash = *((uint64_t*)seed_hash);
|
194
|
+
#if LITTLE_ENDIAN == BYTE_ORDER
|
195
|
+
hash = ethash_swap_u64(hash);
|
196
|
+
#endif
|
197
|
+
return snprintf(output, DAG_MUTABLE_NAME_MAX_SIZE, "full-R%u-%016" PRIx64, revision, hash) >= 0;
|
198
|
+
}
|
199
|
+
|
200
|
+
#ifdef __cplusplus
|
201
|
+
}
|
202
|
+
#endif
|
@@ -0,0 +1,47 @@
|
|
1
|
+
/*
|
2
|
+
This file is part of ethash.
|
3
|
+
|
4
|
+
ethash is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
ethash is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with ethash. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
*/
|
17
|
+
/** @file mmap.h
|
18
|
+
* @author Lefteris Karapetsas <lefteris@ethdev.com>
|
19
|
+
* @date 2015
|
20
|
+
*/
|
21
|
+
#pragma once
|
22
|
+
#if defined(__MINGW32__) || defined(_WIN32)
|
23
|
+
#include <sys/types.h>
|
24
|
+
|
25
|
+
#define PROT_READ 0x1
|
26
|
+
#define PROT_WRITE 0x2
|
27
|
+
/* This flag is only available in WinXP+ */
|
28
|
+
#ifdef FILE_MAP_EXECUTE
|
29
|
+
#define PROT_EXEC 0x4
|
30
|
+
#else
|
31
|
+
#define PROT_EXEC 0x0
|
32
|
+
#define FILE_MAP_EXECUTE 0
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#define MAP_SHARED 0x01
|
36
|
+
#define MAP_PRIVATE 0x02
|
37
|
+
#define MAP_ANONYMOUS 0x20
|
38
|
+
#define MAP_ANON MAP_ANONYMOUS
|
39
|
+
#define MAP_FAILED ((void *) -1)
|
40
|
+
|
41
|
+
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
|
42
|
+
void munmap(void* addr, size_t length);
|
43
|
+
#else // posix, yay! ^_^
|
44
|
+
#include <sys/mman.h>
|
45
|
+
#endif
|
46
|
+
|
47
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#ifdef __cplusplus
|
4
|
+
extern "C" {
|
5
|
+
#endif
|
6
|
+
|
7
|
+
#include "compiler.h"
|
8
|
+
#include <stdint.h>
|
9
|
+
#include <stdlib.h>
|
10
|
+
|
11
|
+
struct ethash_h256;
|
12
|
+
|
13
|
+
#define decsha3(bits) \
|
14
|
+
int sha3_##bits(uint8_t*, size_t, uint8_t const*, size_t);
|
15
|
+
|
16
|
+
decsha3(256)
|
17
|
+
decsha3(512)
|
18
|
+
|
19
|
+
static inline void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t const size)
|
20
|
+
{
|
21
|
+
sha3_256((uint8_t*)ret, 32, data, size);
|
22
|
+
}
|
23
|
+
|
24
|
+
static inline void SHA3_512(uint8_t* ret, uint8_t const* data, size_t const size)
|
25
|
+
{
|
26
|
+
sha3_512(ret, 64, data, size);
|
27
|
+
}
|
28
|
+
|
29
|
+
#ifdef __cplusplus
|
30
|
+
}
|
31
|
+
#endif
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#pragma once
|
2
|
+
|
3
|
+
#include "compiler.h"
|
4
|
+
#include <stdint.h>
|
5
|
+
#include <stdlib.h>
|
6
|
+
|
7
|
+
#ifdef __cplusplus
|
8
|
+
extern "C" {
|
9
|
+
#endif
|
10
|
+
|
11
|
+
struct ethash_h256;
|
12
|
+
|
13
|
+
void SHA3_256(struct ethash_h256 const* ret, uint8_t const* data, size_t size);
|
14
|
+
void SHA3_512(uint8_t* const ret, uint8_t const* data, size_t size);
|
15
|
+
|
16
|
+
#ifdef __cplusplus
|
17
|
+
}
|
18
|
+
#endif
|
@@ -0,0 +1,47 @@
|
|
1
|
+
/*
|
2
|
+
This file is part of ethash.
|
3
|
+
|
4
|
+
ethash is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
ethash is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with ethash. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
*/
|
17
|
+
/** @file util.h
|
18
|
+
* @author Tim Hughes <tim@twistedfury.com>
|
19
|
+
* @date 2015
|
20
|
+
*/
|
21
|
+
#pragma once
|
22
|
+
#include <stdint.h>
|
23
|
+
#include "compiler.h"
|
24
|
+
|
25
|
+
#ifdef __cplusplus
|
26
|
+
extern "C" {
|
27
|
+
#endif
|
28
|
+
|
29
|
+
#ifdef _MSC_VER
|
30
|
+
void debugf(char const* str, ...);
|
31
|
+
#else
|
32
|
+
#define debugf printf
|
33
|
+
#endif
|
34
|
+
|
35
|
+
static inline uint32_t min_u32(uint32_t a, uint32_t b)
|
36
|
+
{
|
37
|
+
return a < b ? a : b;
|
38
|
+
}
|
39
|
+
|
40
|
+
static inline uint32_t clamp_u32(uint32_t x, uint32_t min_, uint32_t max_)
|
41
|
+
{
|
42
|
+
return x < min_ ? min_ : (x > max_ ? max_ : x);
|
43
|
+
}
|
44
|
+
|
45
|
+
#ifdef __cplusplus
|
46
|
+
}
|
47
|
+
#endif
|
@@ -0,0 +1,508 @@
|
|
1
|
+
/*
|
2
|
+
This file is part of ethash.
|
3
|
+
|
4
|
+
ethash is free software: you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation, either version 3 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
ethash is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License
|
15
|
+
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
*/
|
17
|
+
/** @file internal.c
|
18
|
+
* @author Tim Hughes <tim@twistedfury.com>
|
19
|
+
* @author Matthew Wampler-Doty
|
20
|
+
* @date 2015
|
21
|
+
*/
|
22
|
+
|
23
|
+
#include <assert.h>
|
24
|
+
#include <inttypes.h>
|
25
|
+
#include <stddef.h>
|
26
|
+
#include <errno.h>
|
27
|
+
#include <math.h>
|
28
|
+
|
29
|
+
#include "headers/mmap.h"
|
30
|
+
#include "headers/ethash.h"
|
31
|
+
#include "headers/fnv.h"
|
32
|
+
#include "headers/endian.h"
|
33
|
+
#include "headers/internal.h"
|
34
|
+
#include "headers/data_sizes.h"
|
35
|
+
#include "headers/io.h"
|
36
|
+
|
37
|
+
#ifdef WITH_CRYPTOPP
|
38
|
+
|
39
|
+
#include "headers/sha3_cryptopp.h"
|
40
|
+
|
41
|
+
#else
|
42
|
+
#include "headers/sha3.h"
|
43
|
+
#endif // WITH_CRYPTOPP
|
44
|
+
|
45
|
+
uint64_t ethash_get_datasize(uint64_t const block_number)
|
46
|
+
{
|
47
|
+
assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
|
48
|
+
return dag_sizes[block_number / ETHASH_EPOCH_LENGTH];
|
49
|
+
}
|
50
|
+
|
51
|
+
uint64_t ethash_get_cachesize(uint64_t const block_number)
|
52
|
+
{
|
53
|
+
assert(block_number / ETHASH_EPOCH_LENGTH < 2048);
|
54
|
+
return cache_sizes[block_number / ETHASH_EPOCH_LENGTH];
|
55
|
+
}
|
56
|
+
|
57
|
+
// Follows Sergio's "STRICT MEMORY HARD HASHING FUNCTIONS" (2014)
|
58
|
+
// https://bitslog.files.wordpress.com/2013/12/memohash-v0-3.pdf
|
59
|
+
// SeqMemoHash(s, R, N)
|
60
|
+
bool static ethash_compute_cache_nodes(
|
61
|
+
node* const nodes,
|
62
|
+
uint64_t cache_size,
|
63
|
+
ethash_h256_t const* seed
|
64
|
+
)
|
65
|
+
{
|
66
|
+
if (cache_size % sizeof(node) != 0) {
|
67
|
+
return false;
|
68
|
+
}
|
69
|
+
uint32_t const num_nodes = (uint32_t) (cache_size / sizeof(node));
|
70
|
+
|
71
|
+
SHA3_512(nodes[0].bytes, (uint8_t*)seed, 32);
|
72
|
+
|
73
|
+
for (uint32_t i = 1; i != num_nodes; ++i) {
|
74
|
+
SHA3_512(nodes[i].bytes, nodes[i - 1].bytes, 64);
|
75
|
+
}
|
76
|
+
|
77
|
+
for (uint32_t j = 0; j != ETHASH_CACHE_ROUNDS; j++) {
|
78
|
+
for (uint32_t i = 0; i != num_nodes; i++) {
|
79
|
+
uint32_t const idx = nodes[i].words[0] % num_nodes;
|
80
|
+
node data;
|
81
|
+
data = nodes[(num_nodes - 1 + i) % num_nodes];
|
82
|
+
for (uint32_t w = 0; w != NODE_WORDS; ++w) {
|
83
|
+
data.words[w] ^= nodes[idx].words[w];
|
84
|
+
}
|
85
|
+
SHA3_512(nodes[i].bytes, data.bytes, sizeof(data));
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
// now perform endian conversion
|
90
|
+
fix_endian_arr32(nodes->words, num_nodes * NODE_WORDS);
|
91
|
+
return true;
|
92
|
+
}
|
93
|
+
|
94
|
+
void ethash_calculate_dag_item(
|
95
|
+
node* const ret,
|
96
|
+
uint32_t node_index,
|
97
|
+
ethash_light_t const light
|
98
|
+
)
|
99
|
+
{
|
100
|
+
uint32_t num_parent_nodes = (uint32_t) (light->cache_size / sizeof(node));
|
101
|
+
node const* cache_nodes = (node const *) light->cache;
|
102
|
+
node const* init = &cache_nodes[node_index % num_parent_nodes];
|
103
|
+
memcpy(ret, init, sizeof(node));
|
104
|
+
ret->words[0] ^= node_index;
|
105
|
+
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
|
106
|
+
#if defined(_M_X64) && ENABLE_SSE
|
107
|
+
__m128i const fnv_prime = _mm_set1_epi32(FNV_PRIME);
|
108
|
+
__m128i xmm0 = ret->xmm[0];
|
109
|
+
__m128i xmm1 = ret->xmm[1];
|
110
|
+
__m128i xmm2 = ret->xmm[2];
|
111
|
+
__m128i xmm3 = ret->xmm[3];
|
112
|
+
#endif
|
113
|
+
|
114
|
+
for (uint32_t i = 0; i != ETHASH_DATASET_PARENTS; ++i) {
|
115
|
+
uint32_t parent_index = fnv_hash(node_index ^ i, ret->words[i % NODE_WORDS]) % num_parent_nodes;
|
116
|
+
node const *parent = &cache_nodes[parent_index];
|
117
|
+
|
118
|
+
#if defined(_M_X64) && ENABLE_SSE
|
119
|
+
{
|
120
|
+
xmm0 = _mm_mullo_epi32(xmm0, fnv_prime);
|
121
|
+
xmm1 = _mm_mullo_epi32(xmm1, fnv_prime);
|
122
|
+
xmm2 = _mm_mullo_epi32(xmm2, fnv_prime);
|
123
|
+
xmm3 = _mm_mullo_epi32(xmm3, fnv_prime);
|
124
|
+
xmm0 = _mm_xor_si128(xmm0, parent->xmm[0]);
|
125
|
+
xmm1 = _mm_xor_si128(xmm1, parent->xmm[1]);
|
126
|
+
xmm2 = _mm_xor_si128(xmm2, parent->xmm[2]);
|
127
|
+
xmm3 = _mm_xor_si128(xmm3, parent->xmm[3]);
|
128
|
+
|
129
|
+
// have to write to ret as values are used to compute index
|
130
|
+
ret->xmm[0] = xmm0;
|
131
|
+
ret->xmm[1] = xmm1;
|
132
|
+
ret->xmm[2] = xmm2;
|
133
|
+
ret->xmm[3] = xmm3;
|
134
|
+
}
|
135
|
+
#else
|
136
|
+
{
|
137
|
+
for (unsigned w = 0; w != NODE_WORDS; ++w) {
|
138
|
+
ret->words[w] = fnv_hash(ret->words[w], parent->words[w]);
|
139
|
+
}
|
140
|
+
}
|
141
|
+
#endif
|
142
|
+
}
|
143
|
+
SHA3_512(ret->bytes, ret->bytes, sizeof(node));
|
144
|
+
}
|
145
|
+
|
146
|
+
bool ethash_compute_full_data(
|
147
|
+
void* mem,
|
148
|
+
uint64_t full_size,
|
149
|
+
ethash_light_t const light,
|
150
|
+
ethash_callback_t callback
|
151
|
+
)
|
152
|
+
{
|
153
|
+
if (full_size % (sizeof(uint32_t) * MIX_WORDS) != 0 ||
|
154
|
+
(full_size % sizeof(node)) != 0) {
|
155
|
+
return false;
|
156
|
+
}
|
157
|
+
uint32_t const max_n = (uint32_t)(full_size / sizeof(node));
|
158
|
+
node* full_nodes = mem;
|
159
|
+
double const progress_change = 1.0f / max_n;
|
160
|
+
double progress = 0.0f;
|
161
|
+
// now compute full nodes
|
162
|
+
for (uint32_t n = 0; n != max_n; ++n) {
|
163
|
+
if (callback &&
|
164
|
+
n % (max_n / 100) == 0 &&
|
165
|
+
callback((unsigned int)(ceil(progress * 100.0f))) != 0) {
|
166
|
+
|
167
|
+
return false;
|
168
|
+
}
|
169
|
+
progress += progress_change;
|
170
|
+
ethash_calculate_dag_item(&(full_nodes[n]), n, light);
|
171
|
+
}
|
172
|
+
return true;
|
173
|
+
}
|
174
|
+
|
175
|
+
static bool ethash_hash(
|
176
|
+
ethash_return_value_t* ret,
|
177
|
+
node const* full_nodes,
|
178
|
+
ethash_light_t const light,
|
179
|
+
uint64_t full_size,
|
180
|
+
ethash_h256_t const header_hash,
|
181
|
+
uint64_t const nonce
|
182
|
+
)
|
183
|
+
{
|
184
|
+
if (full_size % MIX_WORDS != 0) {
|
185
|
+
return false;
|
186
|
+
}
|
187
|
+
|
188
|
+
// pack hash and nonce together into first 40 bytes of s_mix
|
189
|
+
assert(sizeof(node) * 8 == 512);
|
190
|
+
node s_mix[MIX_NODES + 1];
|
191
|
+
memcpy(s_mix[0].bytes, &header_hash, 32);
|
192
|
+
fix_endian64(s_mix[0].double_words[4], nonce);
|
193
|
+
|
194
|
+
// compute sha3-512 hash and replicate across mix
|
195
|
+
SHA3_512(s_mix->bytes, s_mix->bytes, 40);
|
196
|
+
fix_endian_arr32(s_mix[0].words, 16);
|
197
|
+
|
198
|
+
node* const mix = s_mix + 1;
|
199
|
+
for (uint32_t w = 0; w != MIX_WORDS; ++w) {
|
200
|
+
mix->words[w] = s_mix[0].words[w % NODE_WORDS];
|
201
|
+
}
|
202
|
+
|
203
|
+
unsigned const page_size = sizeof(uint32_t) * MIX_WORDS;
|
204
|
+
unsigned const num_full_pages = (unsigned) (full_size / page_size);
|
205
|
+
|
206
|
+
for (unsigned i = 0; i != ETHASH_ACCESSES; ++i) {
|
207
|
+
uint32_t const index = fnv_hash(s_mix->words[0] ^ i, mix->words[i % MIX_WORDS]) % num_full_pages;
|
208
|
+
|
209
|
+
for (unsigned n = 0; n != MIX_NODES; ++n) {
|
210
|
+
node const* dag_node;
|
211
|
+
if (full_nodes) {
|
212
|
+
dag_node = &full_nodes[MIX_NODES * index + n];
|
213
|
+
} else {
|
214
|
+
node tmp_node;
|
215
|
+
ethash_calculate_dag_item(&tmp_node, index * MIX_NODES + n, light);
|
216
|
+
dag_node = &tmp_node;
|
217
|
+
}
|
218
|
+
|
219
|
+
#if defined(_M_X64) && ENABLE_SSE
|
220
|
+
{
|
221
|
+
__m128i fnv_prime = _mm_set1_epi32(FNV_PRIME);
|
222
|
+
__m128i xmm0 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[0]);
|
223
|
+
__m128i xmm1 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[1]);
|
224
|
+
__m128i xmm2 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[2]);
|
225
|
+
__m128i xmm3 = _mm_mullo_epi32(fnv_prime, mix[n].xmm[3]);
|
226
|
+
mix[n].xmm[0] = _mm_xor_si128(xmm0, dag_node->xmm[0]);
|
227
|
+
mix[n].xmm[1] = _mm_xor_si128(xmm1, dag_node->xmm[1]);
|
228
|
+
mix[n].xmm[2] = _mm_xor_si128(xmm2, dag_node->xmm[2]);
|
229
|
+
mix[n].xmm[3] = _mm_xor_si128(xmm3, dag_node->xmm[3]);
|
230
|
+
}
|
231
|
+
#else
|
232
|
+
{
|
233
|
+
for (unsigned w = 0; w != NODE_WORDS; ++w) {
|
234
|
+
mix[n].words[w] = fnv_hash(mix[n].words[w], dag_node->words[w]);
|
235
|
+
}
|
236
|
+
}
|
237
|
+
#endif
|
238
|
+
}
|
239
|
+
|
240
|
+
}
|
241
|
+
|
242
|
+
// compress mix
|
243
|
+
for (uint32_t w = 0; w != MIX_WORDS; w += 4) {
|
244
|
+
uint32_t reduction = mix->words[w + 0];
|
245
|
+
reduction = reduction * FNV_PRIME ^ mix->words[w + 1];
|
246
|
+
reduction = reduction * FNV_PRIME ^ mix->words[w + 2];
|
247
|
+
reduction = reduction * FNV_PRIME ^ mix->words[w + 3];
|
248
|
+
mix->words[w / 4] = reduction;
|
249
|
+
}
|
250
|
+
|
251
|
+
fix_endian_arr32(mix->words, MIX_WORDS / 4);
|
252
|
+
memcpy(&ret->mix_hash, mix->bytes, 32);
|
253
|
+
// final Keccak hash
|
254
|
+
SHA3_256(&ret->result, s_mix->bytes, 64 + 32); // Keccak-256(s + compressed_mix)
|
255
|
+
return true;
|
256
|
+
}
|
257
|
+
|
258
|
+
void ethash_quick_hash(
|
259
|
+
ethash_h256_t* return_hash,
|
260
|
+
ethash_h256_t const* header_hash,
|
261
|
+
uint64_t const nonce,
|
262
|
+
ethash_h256_t const* mix_hash
|
263
|
+
)
|
264
|
+
{
|
265
|
+
uint8_t buf[64 + 32];
|
266
|
+
memcpy(buf, header_hash, 32);
|
267
|
+
fix_endian64_same(nonce);
|
268
|
+
memcpy(&(buf[32]), &nonce, 8);
|
269
|
+
SHA3_512(buf, buf, 40);
|
270
|
+
memcpy(&(buf[64]), mix_hash, 32);
|
271
|
+
SHA3_256(return_hash, buf, 64 + 32);
|
272
|
+
}
|
273
|
+
|
274
|
+
ethash_h256_t ethash_get_seedhash(uint64_t block_number)
|
275
|
+
{
|
276
|
+
ethash_h256_t ret;
|
277
|
+
ethash_h256_reset(&ret);
|
278
|
+
uint64_t const epochs = block_number / ETHASH_EPOCH_LENGTH;
|
279
|
+
for (uint32_t i = 0; i < epochs; ++i)
|
280
|
+
SHA3_256(&ret, (uint8_t*)&ret, 32);
|
281
|
+
return ret;
|
282
|
+
}
|
283
|
+
|
284
|
+
bool ethash_quick_check_difficulty(
|
285
|
+
ethash_h256_t const* header_hash,
|
286
|
+
uint64_t const nonce,
|
287
|
+
ethash_h256_t const* mix_hash,
|
288
|
+
ethash_h256_t const* boundary
|
289
|
+
)
|
290
|
+
{
|
291
|
+
|
292
|
+
ethash_h256_t return_hash;
|
293
|
+
ethash_quick_hash(&return_hash, header_hash, nonce, mix_hash);
|
294
|
+
return ethash_check_difficulty(&return_hash, boundary);
|
295
|
+
}
|
296
|
+
|
297
|
+
ethash_light_t ethash_light_new_internal(uint64_t cache_size, ethash_h256_t const* seed)
|
298
|
+
{
|
299
|
+
struct ethash_light *ret;
|
300
|
+
ret = calloc(sizeof(*ret), 1);
|
301
|
+
if (!ret) {
|
302
|
+
return NULL;
|
303
|
+
}
|
304
|
+
ret->cache = malloc((size_t)cache_size);
|
305
|
+
if (!ret->cache) {
|
306
|
+
goto fail_free_light;
|
307
|
+
}
|
308
|
+
node* nodes = (node*)ret->cache;
|
309
|
+
if (!ethash_compute_cache_nodes(nodes, cache_size, seed)) {
|
310
|
+
goto fail_free_cache_mem;
|
311
|
+
}
|
312
|
+
ret->cache_size = cache_size;
|
313
|
+
return ret;
|
314
|
+
|
315
|
+
fail_free_cache_mem:
|
316
|
+
free(ret->cache);
|
317
|
+
fail_free_light:
|
318
|
+
free(ret);
|
319
|
+
return NULL;
|
320
|
+
}
|
321
|
+
|
322
|
+
ethash_light_t ethash_light_new(uint64_t block_number)
|
323
|
+
{
|
324
|
+
ethash_h256_t seedhash = ethash_get_seedhash(block_number);
|
325
|
+
ethash_light_t ret;
|
326
|
+
ret = ethash_light_new_internal(ethash_get_cachesize(block_number), &seedhash);
|
327
|
+
ret->block_number = block_number;
|
328
|
+
return ret;
|
329
|
+
}
|
330
|
+
|
331
|
+
void ethash_light_delete(ethash_light_t light)
|
332
|
+
{
|
333
|
+
if (light->cache) {
|
334
|
+
free(light->cache);
|
335
|
+
}
|
336
|
+
free(light);
|
337
|
+
}
|
338
|
+
|
339
|
+
ethash_return_value_t ethash_light_compute_internal(
|
340
|
+
ethash_light_t light,
|
341
|
+
uint64_t full_size,
|
342
|
+
ethash_h256_t const header_hash,
|
343
|
+
uint64_t nonce
|
344
|
+
)
|
345
|
+
{
|
346
|
+
ethash_return_value_t ret;
|
347
|
+
ret.success = true;
|
348
|
+
if (!ethash_hash(&ret, NULL, light, full_size, header_hash, nonce)) {
|
349
|
+
ret.success = false;
|
350
|
+
}
|
351
|
+
return ret;
|
352
|
+
}
|
353
|
+
|
354
|
+
ethash_return_value_t ethash_light_compute(
|
355
|
+
ethash_light_t light,
|
356
|
+
ethash_h256_t const header_hash,
|
357
|
+
uint64_t nonce
|
358
|
+
)
|
359
|
+
{
|
360
|
+
uint64_t full_size = ethash_get_datasize(light->block_number);
|
361
|
+
return ethash_light_compute_internal(light, full_size, header_hash, nonce);
|
362
|
+
}
|
363
|
+
|
364
|
+
static bool ethash_mmap(struct ethash_full* ret, FILE* f)
|
365
|
+
{
|
366
|
+
int fd;
|
367
|
+
char* mmapped_data;
|
368
|
+
errno = 0;
|
369
|
+
ret->file = f;
|
370
|
+
if ((fd = ethash_fileno(ret->file)) == -1) {
|
371
|
+
return false;
|
372
|
+
}
|
373
|
+
mmapped_data= mmap(
|
374
|
+
NULL,
|
375
|
+
(size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE,
|
376
|
+
PROT_READ | PROT_WRITE,
|
377
|
+
MAP_SHARED,
|
378
|
+
fd,
|
379
|
+
0
|
380
|
+
);
|
381
|
+
if (mmapped_data == MAP_FAILED) {
|
382
|
+
return false;
|
383
|
+
}
|
384
|
+
ret->data = (node*)(mmapped_data + ETHASH_DAG_MAGIC_NUM_SIZE);
|
385
|
+
return true;
|
386
|
+
}
|
387
|
+
|
388
|
+
ethash_full_t ethash_full_new_internal(
|
389
|
+
char const* dirname,
|
390
|
+
ethash_h256_t const seed_hash,
|
391
|
+
uint64_t full_size,
|
392
|
+
ethash_light_t const light,
|
393
|
+
ethash_callback_t callback
|
394
|
+
)
|
395
|
+
{
|
396
|
+
struct ethash_full* ret;
|
397
|
+
FILE *f = NULL;
|
398
|
+
ret = calloc(sizeof(*ret), 1);
|
399
|
+
if (!ret) {
|
400
|
+
return NULL;
|
401
|
+
}
|
402
|
+
ret->file_size = (size_t)full_size;
|
403
|
+
switch (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, false)) {
|
404
|
+
case ETHASH_IO_FAIL:
|
405
|
+
// ethash_io_prepare will do all ETHASH_CRITICAL() logging in fail case
|
406
|
+
goto fail_free_full;
|
407
|
+
case ETHASH_IO_MEMO_MATCH:
|
408
|
+
if (!ethash_mmap(ret, f)) {
|
409
|
+
ETHASH_CRITICAL("mmap failure()");
|
410
|
+
goto fail_close_file;
|
411
|
+
}
|
412
|
+
return ret;
|
413
|
+
case ETHASH_IO_MEMO_SIZE_MISMATCH:
|
414
|
+
// if a DAG of same filename but unexpected size is found, silently force new file creation
|
415
|
+
if (ethash_io_prepare(dirname, seed_hash, &f, (size_t)full_size, true) != ETHASH_IO_MEMO_MISMATCH) {
|
416
|
+
ETHASH_CRITICAL("Could not recreate DAG file after finding existing DAG with unexpected size.");
|
417
|
+
goto fail_free_full;
|
418
|
+
}
|
419
|
+
// fallthrough to the mismatch case here, DO NOT go through match
|
420
|
+
case ETHASH_IO_MEMO_MISMATCH:
|
421
|
+
if (!ethash_mmap(ret, f)) {
|
422
|
+
ETHASH_CRITICAL("mmap failure()");
|
423
|
+
goto fail_close_file;
|
424
|
+
}
|
425
|
+
break;
|
426
|
+
}
|
427
|
+
|
428
|
+
if (!ethash_compute_full_data(ret->data, full_size, light, callback)) {
|
429
|
+
ETHASH_CRITICAL("Failure at computing DAG data.");
|
430
|
+
goto fail_free_full_data;
|
431
|
+
}
|
432
|
+
|
433
|
+
// after the DAG has been filled then we finalize it by writting the magic number at the beginning
|
434
|
+
if (fseek(f, 0, SEEK_SET) != 0) {
|
435
|
+
ETHASH_CRITICAL("Could not seek to DAG file start to write magic number.");
|
436
|
+
goto fail_free_full_data;
|
437
|
+
}
|
438
|
+
uint64_t const magic_num = ETHASH_DAG_MAGIC_NUM;
|
439
|
+
if (fwrite(&magic_num, ETHASH_DAG_MAGIC_NUM_SIZE, 1, f) != 1) {
|
440
|
+
ETHASH_CRITICAL("Could not write magic number to DAG's beginning.");
|
441
|
+
goto fail_free_full_data;
|
442
|
+
}
|
443
|
+
if (fflush(f) != 0) {// make sure the magic number IS there
|
444
|
+
ETHASH_CRITICAL("Could not flush memory mapped data to DAG file. Insufficient space?");
|
445
|
+
goto fail_free_full_data;
|
446
|
+
}
|
447
|
+
return ret;
|
448
|
+
|
449
|
+
fail_free_full_data:
|
450
|
+
// could check that munmap(..) == 0 but even if it did not can't really do anything here
|
451
|
+
munmap(ret->data, (size_t)full_size);
|
452
|
+
fail_close_file:
|
453
|
+
fclose(ret->file);
|
454
|
+
fail_free_full:
|
455
|
+
free(ret);
|
456
|
+
return NULL;
|
457
|
+
}
|
458
|
+
|
459
|
+
ethash_full_t ethash_full_new(ethash_light_t light, ethash_callback_t callback)
|
460
|
+
{
|
461
|
+
char strbuf[256];
|
462
|
+
if (!ethash_get_default_dirname(strbuf, 256)) {
|
463
|
+
return NULL;
|
464
|
+
}
|
465
|
+
uint64_t full_size = ethash_get_datasize(light->block_number);
|
466
|
+
ethash_h256_t seedhash = ethash_get_seedhash(light->block_number);
|
467
|
+
return ethash_full_new_internal(strbuf, seedhash, full_size, light, callback);
|
468
|
+
}
|
469
|
+
|
470
|
+
void ethash_full_delete(ethash_full_t full)
|
471
|
+
{
|
472
|
+
// could check that munmap(..) == 0 but even if it did not can't really do anything here
|
473
|
+
munmap(full->data, (size_t)full->file_size);
|
474
|
+
if (full->file) {
|
475
|
+
fclose(full->file);
|
476
|
+
}
|
477
|
+
free(full);
|
478
|
+
}
|
479
|
+
|
480
|
+
ethash_return_value_t ethash_full_compute(
|
481
|
+
ethash_full_t full,
|
482
|
+
ethash_h256_t const header_hash,
|
483
|
+
uint64_t nonce
|
484
|
+
)
|
485
|
+
{
|
486
|
+
ethash_return_value_t ret;
|
487
|
+
ret.success = true;
|
488
|
+
if (!ethash_hash(
|
489
|
+
&ret,
|
490
|
+
(node const*)full->data,
|
491
|
+
NULL,
|
492
|
+
full->file_size,
|
493
|
+
header_hash,
|
494
|
+
nonce)) {
|
495
|
+
ret.success = false;
|
496
|
+
}
|
497
|
+
return ret;
|
498
|
+
}
|
499
|
+
|
500
|
+
void const* ethash_full_dag(ethash_full_t full)
|
501
|
+
{
|
502
|
+
return full->data;
|
503
|
+
}
|
504
|
+
|
505
|
+
uint64_t ethash_full_dag_size(ethash_full_t full)
|
506
|
+
{
|
507
|
+
return full->file_size;
|
508
|
+
}
|