Line data Source code
1 : #ifndef HEADER_fd_src_ballet_sha256_fd_sha256_h
2 : #define HEADER_fd_src_ballet_sha256_fd_sha256_h
3 :
4 : /* fd_sha256 provides APIs for SHA-256 hashing of messages. */
5 :
6 : #include "../fd_ballet_base.h"
7 :
8 : /* FD_SHA256_{ALIGN,FOOTPRINT} describe the alignment and footprint needed
9 : for a memory region to hold a fd_sha256_t. ALIGN is a positive
10 : integer power of 2. FOOTPRINT is a multiple of align. ALIGN is
11 : recommended to be at least double cache line to mitigate various
12 : kinds of false sharing. These are provided to facilitate compile
13 : time declarations. */
14 :
15 1122054 : #define FD_SHA256_ALIGN (128UL)
16 561015 : #define FD_SHA256_FOOTPRINT (128UL)
17 :
18 : /* FD_SHA256_{LG_HASH_SZ,HASH_SZ} describe the size of a SHA256 hash
19 : in bytes. HASH_SZ==2^LG_HASH_SZ==32. */
20 :
21 : #define FD_SHA256_LG_HASH_SZ (5)
22 561273 : #define FD_SHA256_HASH_SZ (32UL) /* == 2^FD_SHA256_LG_HASH_SZ, explicit to workaround compiler limitations */
23 :
24 : /* FD_SHA256_{LG_BLOCK_SZ,BLOCK_SZ} describe the size of a SHA256
25 : hash block in byte. BLOCK_SZ==2^LG_BLOCK_SZ==64. */
26 :
27 55724783 : #define FD_SHA256_LG_BLOCK_SZ (6)
28 137672260 : #define FD_SHA256_BLOCK_SZ (64UL) /* == 2^FD_SHA256_LG_BLOCK_SZ, explicit to workaround compiler limitations */
29 :
30 : /* A fd_sha256_t should be treated as an opaque handle of a sha256
31 : calculation state. (It technically isn't here facilitate compile
32 : time declarations of fd_sha256_t memory.) */
33 :
34 561012 : #define FD_SHA256_MAGIC (0xF17EDA2CE54A2560) /* FIREDANCE SHA256 V0 */
35 :
36 : /* FD_SHA256_PRIVATE_{LG_BUF_MAX,BUF_MAX} describe the size of the
37 : internal buffer used by the sha256 computation object. This is for
38 : internal use only. BUF_MAX==2^LG_BUF_MAX==2*FD_SHA256_HASH_SZ==64. */
39 :
40 55724783 : #define FD_SHA256_PRIVATE_LG_BUF_MAX FD_SHA256_LG_BLOCK_SZ
41 98616670 : #define FD_SHA256_PRIVATE_BUF_MAX FD_SHA256_BLOCK_SZ
42 :
43 : struct __attribute__((aligned(FD_SHA256_ALIGN))) fd_sha256_private {
44 :
45 : /* This point is 128-byte aligned */
46 :
47 : uchar buf[ FD_SHA256_PRIVATE_BUF_MAX ];
48 :
49 : /* This point is 64-byte aligned */
50 :
51 : uint state[ FD_SHA256_HASH_SZ / sizeof(uint) ];
52 :
53 : /* This point is 32-byte aligned */
54 :
55 : ulong magic; /* ==FD_SHA256_MAGIC */
56 : ulong buf_used; /* Number of buffered bytes, in [0,FD_SHA256_BUF_MAX) */
57 : ulong bit_cnt; /* How many bits have been appended total */
58 :
59 : /* Padding to 128-byte here */
60 : };
61 :
62 : typedef struct fd_sha256_private fd_sha256_t;
63 :
64 : FD_PROTOTYPES_BEGIN
65 :
66 : /* fd_sha256_{align,footprint,new,join,leave,delete} usage is identical to
67 : that of their fd_sha512 counterparts. See ../sha512/fd_sha512.h */
68 :
69 : FD_FN_CONST ulong
70 : fd_sha256_align( void );
71 :
72 : FD_FN_CONST ulong
73 : fd_sha256_footprint( void );
74 :
75 : void *
76 : fd_sha256_new( void * shmem );
77 :
78 : fd_sha256_t *
79 : fd_sha256_join( void * shsha );
80 :
81 : void *
82 : fd_sha256_leave( fd_sha256_t * sha );
83 :
84 : void *
85 : fd_sha256_delete( void * shsha );
86 :
87 : /* fd_sha256_init starts a sha256 calculation. sha is assumed to be a
88 : current local join to a sha256 calculation state with no other
89 : concurrent operation that would modify the state while this is
90 : executing. Any preexisting state for an in-progress or recently
91 : completed calculation will be discarded. Returns sha (on return, sha
92 : will have the state of a new in-progress calculation). */
93 :
94 : fd_sha256_t *
95 : fd_sha256_init( fd_sha256_t * sha );
96 :
97 : /* fd_sha256_append adds sz bytes locally pointed to by data an
98 : in-progress sha256 calculation. sha, data and sz are assumed to be
99 : valid (i.e. sha is a current local join to a sha256 calculation state
100 : with no other concurrent operations that would modify the state while
101 : this is executing, data points to the first of the sz bytes and will
102 : be unmodified while this is running with no interest retained after
103 : return ... data==NULL is fine if sz==0). Returns sha (on return, sha
104 : will have the updated state of the in-progress calculation).
105 :
106 : It does not matter how the user group data bytes for a sha256
107 : calculation; the final hash will be identical. It is preferable for
108 : performance to try to append as many bytes as possible as a time
109 : though. It is also preferable for performance if sz is a multiple of
110 : 64 for all but the last append (it is also preferable if sz is less
111 : than 56 for the last append). */
112 :
113 : fd_sha256_t *
114 : fd_sha256_append( fd_sha256_t * sha,
115 : void const * data,
116 : ulong sz );
117 :
118 : /* fd_sha256_fini finishes a sha256 calculation. sha and hash are
119 : assumed to be valid (i.e. sha is a local join to a sha256 calculation
120 : state that has an in-progress calculation with no other concurrent
121 : operations that would modify the state while this is executing and
122 : hash points to the first byte of a 32-byte memory region where the
123 : result of the calculation should be stored). Returns hash (on
124 : return, there will be no calculation in-progress on sha and 32-byte
125 : buffer pointed to by hash will be populated with the calculation
126 : result). */
127 : /* FIXME: THIS SHOULD PROBABLY RETURN A FD_SHA256_T */
128 :
129 : void *
130 : fd_sha256_fini( fd_sha256_t * sha,
131 : void * hash );
132 :
133 : /* fd_sha256_hash is a streamlined implementation of:
134 :
135 : fd_sha256_t sha[1];
136 : return fd_sha256_fini( fd_sha256_append( fd_sha256_init( sha ), data, sz ), hash )
137 :
138 : This can be faster for small messages because it can eliminate
139 : function call overheads, branches, copies and data marshalling under
140 : the hood (things like binary Merkle tree construction were designed
141 : do lots of such operations). */
142 : /* FIXME: ADD NEW/JOIN/LEAVE/DELETE TO DOCUMENTATION */
143 : /* FIXME: PROBABLY SHOULD HAVE AN ABORT API */
144 : /* FIXME: UPDATE OTHER HASH FUNCTIONS SIMILARLY */
145 :
146 : void *
147 : fd_sha256_hash( void const * data,
148 : ulong sz,
149 : void * hash );
150 :
151 : FD_PROTOTYPES_END
152 :
153 : #if 0 /* SHA256 batch API details */
154 :
155 : /* FD_SHA256_BATCH_{ALIGN,FOOTPRINT} return the alignment and footprint
156 : in bytes required for a region of memory to can hold the state of an
157 : in-progress set of SHA-256 calculations. ALIGN will be an integer
158 : power of 2 and FOOTPRINT will be a multiple of ALIGN. These are to
159 : facilitate compile time declarations. */
160 :
161 : #define FD_SHA256_BATCH_ALIGN ...
162 : #define FD_SHA256_BATCH_FOOTPRINT ...
163 :
164 : /* FD_SHA256_BATCH_MAX returns the batch size used under the hood.
165 : Will be positive. Users should not normally need use this for
166 : anything. */
167 :
168 : #define FD_SHA256_BATCH_MAX ...
169 :
170 : /* A fd_sha256_batch_t is an opaque handle for a set of SHA-256
171 : calculations. */
172 :
173 : struct fd_sha256_private_batch;
174 : typedef struct fd_sha256_private_batch fd_sha256_batch_t;
175 :
176 : /* fd_sha256_batch_{align,footprint} return
177 : FD_SHA256_BATCH_{ALIGN,FOOTPRINT} respectively. */
178 :
179 : ulong fd_sha256_batch_align ( void );
180 : ulong fd_sha256_batch_footprint( void );
181 :
182 : /* fd_sha256_batch_init starts a new batch of SHA-256 calculations. The
183 : state of the in-progress calculation will be held in the memory
184 : region whose first byte in the local address space is pointed to by
185 : mem. The region should have the appropriate alignment and footprint
186 : and should not be read, changed or deleted until fini or abort is
187 : called on the in-progress calculation.
188 :
189 : Returns a handle to the in-progress batch calculation. As this is
190 : used in HPC contexts, does no input validation. */
191 :
192 : fd_sha256_batch_t *
193 : fd_sha256_batch_init( void * mem );
194 :
195 : /* fd_sha256_batch_add adds the sz byte message whose first byte in the
196 : local address space is pointed to by data to the in-progress batch
197 : calculation whose handle is batch. The result of the calculation
198 : will be stored at the 32-byte memory region whose first byte in the
199 : local address space is pointed to by hash.
200 :
201 : There are _no_ alignment restrictions on data and hash and _no_
202 : restrictions on sz. After a message is added, that message should
203 : not be changed or deleted until the fini or abort is called on the
204 : in-progress calculation. Likewise, the hash memory region shot not
205 : be read, written or deleted until the calculation has completed.
206 :
207 : Messages can overlap and/or be added to a batch multiple times. Each
208 : hash location added to a batch should not overlap any other hash
209 : location of calculation state or message region. (Hash reuse /
210 : overlap have indeterminant but non-crashing behavior as the
211 : implementation under the hood is free to execute the elements of the
212 : batch in whatever order it sees fit and potentially do those
213 : calculations incrementally / in the background / ... as the batch is
214 : assembled.)
215 :
216 : Depending on the implementation, it might help performance to cluster
217 : adds of similar sized messages together. Likewise, it can be
218 : advantageous to use aligned message regions, aligned hash regions and
219 : messages sizes that are a multiple of a SHA block size. None of this
220 : is required though.
221 :
222 : Returns batch (which will still be an in progress batch calculation).
223 : As this is used in HPC contexts, does no input validation. */
224 :
225 : fd_sha256_batch_t *
226 : fd_sha256_batch_add( fd_sha256_batch_t * batch,
227 : void const * data,
228 : ulong sz,
229 : void * hash );
230 :
231 : /* fd_sha256_batch_fini finishes a set of SHA-256 calculations. On
232 : return, all the hash memory regions will be populated with the
233 : corresponding message hash. Returns a pointer to the memory region
234 : used to hold the calculation state (contents undefined) and the
235 : calculation will no longer be in progress. As this is used in HPC
236 : contexts, does no input validation. */
237 :
238 : void *
239 : fd_sha256_batch_fini( fd_sha256_batch_t * batch );
240 :
241 : /* fd_sha256_batch_abort aborts an in-progress set of SHA-256
242 : calculations. There is no guarantee which individual messages (if
243 : any) had their hashes computed and the contents of the hash memory
244 : regions is undefined. Returns a pointer to the memory region used to
245 : hold the calculation state (contents undefined) and the calculation
246 : will no longer be in progress. As this is used in HPC contexts, does
247 : no input validation. */
248 :
249 : void *
250 : fd_sha256_batch_abort( fd_sha256_batch_t * batch );
251 :
252 : #endif
253 :
254 : #ifndef FD_SHA256_BATCH_IMPL
255 : #if FD_HAS_AVX512
256 : #define FD_SHA256_BATCH_IMPL 2
257 : #elif FD_HAS_AVX && !defined(__tune_znver1__) && !defined(__tune_znver2__) && !defined(__tune_znver3__)
258 : #define FD_SHA256_BATCH_IMPL 1
259 : #else
260 : #define FD_SHA256_BATCH_IMPL 0
261 : #endif
262 : #endif
263 :
264 : #if FD_SHA256_BATCH_IMPL==0 /* Reference batching implementation */
265 :
266 : #define FD_SHA256_BATCH_ALIGN (1UL)
267 : #define FD_SHA256_BATCH_FOOTPRINT (1UL)
268 : #define FD_SHA256_BATCH_MAX (1UL)
269 :
270 : typedef uchar fd_sha256_batch_t;
271 :
272 : FD_PROTOTYPES_BEGIN
273 :
274 : FD_FN_CONST static inline ulong fd_sha256_batch_align ( void ) { return alignof(fd_sha256_batch_t); }
275 : FD_FN_CONST static inline ulong fd_sha256_batch_footprint( void ) { return sizeof (fd_sha256_batch_t); }
276 :
277 : static inline fd_sha256_batch_t * fd_sha256_batch_init( void * mem ) { return (fd_sha256_batch_t *)mem; }
278 :
279 : static inline fd_sha256_batch_t *
280 : fd_sha256_batch_add( fd_sha256_batch_t * batch,
281 : void const * data,
282 : ulong sz,
283 : void * hash ) {
284 : fd_sha256_hash( data, sz, hash );
285 : return batch;
286 : }
287 :
288 : static inline void * fd_sha256_batch_fini ( fd_sha256_batch_t * batch ) { return (void *)batch; }
289 : static inline void * fd_sha256_batch_abort( fd_sha256_batch_t * batch ) { return (void *)batch; }
290 :
291 : FD_PROTOTYPES_END
292 :
293 : #elif FD_SHA256_BATCH_IMPL==1 /* AVX accelerated batching implementation */
294 :
295 : #define FD_SHA256_BATCH_ALIGN (128UL)
296 : #define FD_SHA256_BATCH_FOOTPRINT (256UL)
297 1386908 : #define FD_SHA256_BATCH_MAX (8UL)
298 :
299 : /* This is exposed here to facilitate inlining various operations */
300 :
301 : struct __attribute__((aligned(FD_SHA256_BATCH_ALIGN))) fd_sha256_private_batch {
302 : void const * data[ FD_SHA256_BATCH_MAX ]; /* AVX aligned */
303 : ulong sz [ FD_SHA256_BATCH_MAX ]; /* AVX aligned */
304 : void * hash[ FD_SHA256_BATCH_MAX ]; /* AVX aligned */
305 : ulong cnt;
306 : };
307 :
308 : typedef struct fd_sha256_private_batch fd_sha256_batch_t;
309 :
310 : FD_PROTOTYPES_BEGIN
311 :
312 : /* Internal use only */
313 :
314 : void
315 : fd_sha256_private_batch_avx( ulong batch_cnt, /* In [1,FD_SHA256_BATCH_MAX] */
316 : void const * batch_data, /* Indexed [0,FD_SHA256_BATCH_MAX), aligned 32,
317 : only [0,batch_cnt) used, essentially a msg_t const * const * */
318 : ulong const * batch_sz, /* Indexed [0,FD_SHA256_BATCH_MAX), aligned 32,
319 : only [0,batch_cnt) used */
320 : void * const * batch_hash ); /* Indexed [0,FD_SHA256_BATCH_MAX), aligned 32,
321 : only [0,batch_cnt) used */
322 :
323 2 : FD_FN_CONST static inline ulong fd_sha256_batch_align ( void ) { return alignof(fd_sha256_batch_t); }
324 2 : FD_FN_CONST static inline ulong fd_sha256_batch_footprint( void ) { return sizeof (fd_sha256_batch_t); }
325 :
326 : static inline fd_sha256_batch_t *
327 1525120 : fd_sha256_batch_init( void * mem ) {
328 1525120 : fd_sha256_batch_t * batch = (fd_sha256_batch_t *)mem;
329 1525120 : batch->cnt = 0UL;
330 1525120 : return batch;
331 1525120 : }
332 :
333 : static inline fd_sha256_batch_t *
334 : fd_sha256_batch_add( fd_sha256_batch_t * batch,
335 : void const * data,
336 : ulong sz,
337 14968388 : void * hash ) {
338 14968388 : ulong batch_cnt = batch->cnt;
339 14968388 : batch->data[ batch_cnt ] = data;
340 14968388 : batch->sz [ batch_cnt ] = sz;
341 14968388 : batch->hash[ batch_cnt ] = hash;
342 14968388 : batch_cnt++;
343 14968388 : if( FD_UNLIKELY( batch_cnt==FD_SHA256_BATCH_MAX ) ) {
344 1676678 : fd_sha256_private_batch_avx( batch_cnt, batch->data, batch->sz, batch->hash );
345 1676678 : batch_cnt = 0UL;
346 1676678 : }
347 14968388 : batch->cnt = batch_cnt;
348 14968388 : return batch;
349 14968388 : }
350 :
351 : static inline void *
352 1523052 : fd_sha256_batch_fini( fd_sha256_batch_t * batch ) {
353 1523052 : ulong batch_cnt = batch->cnt;
354 1523052 : if( FD_LIKELY( batch_cnt ) ) fd_sha256_private_batch_avx( batch_cnt, batch->data, batch->sz, batch->hash );
355 1523052 : return (void *)batch;
356 1523052 : }
357 :
358 : static inline void *
359 2068 : fd_sha256_batch_abort( fd_sha256_batch_t * batch ) {
360 2068 : return (void *)batch;
361 2068 : }
362 :
363 : FD_PROTOTYPES_END
364 :
365 : #elif FD_SHA256_BATCH_IMPL==2 /* AVX-512 accelerated batching implementation */
366 :
367 : #define FD_SHA256_BATCH_ALIGN (128UL)
368 : #define FD_SHA256_BATCH_FOOTPRINT (512UL)
369 573228 : #define FD_SHA256_BATCH_MAX (16UL)
370 :
371 : /* This is exposed here to facilitate inlining various operations */
372 :
373 : struct __attribute__((aligned(FD_SHA256_BATCH_ALIGN))) fd_sha256_private_batch {
374 : void const * data[ FD_SHA256_BATCH_MAX ]; /* AVX aligned */
375 : ulong sz [ FD_SHA256_BATCH_MAX ]; /* AVX aligned */
376 : void * hash[ FD_SHA256_BATCH_MAX ]; /* AVX aligned */
377 : ulong cnt;
378 : };
379 :
380 : typedef struct fd_sha256_private_batch fd_sha256_batch_t;
381 :
382 : FD_PROTOTYPES_BEGIN
383 :
384 : /* Internal use only */
385 :
386 : void
387 : fd_sha256_private_batch_avx512( ulong batch_cnt, /* In [1,FD_SHA256_BATCH_MAX] */
388 : void const * batch_data, /* Indexed [0,FD_SHA256_BATCH_MAX), aligned 32,
389 : only [0,batch_cnt) used, essentially a msg_t const * const * */
390 : ulong const * batch_sz, /* Indexed [0,FD_SHA256_BATCH_MAX), aligned 32,
391 : only [0,batch_cnt) used */
392 : void * const * batch_hash ); /* Indexed [0,FD_SHA256_BATCH_MAX), aligned 32,
393 : only [0,batch_cnt) used */
394 :
395 1 : FD_FN_CONST static inline ulong fd_sha256_batch_align ( void ) { return alignof(fd_sha256_batch_t); }
396 1 : FD_FN_CONST static inline ulong fd_sha256_batch_footprint( void ) { return sizeof (fd_sha256_batch_t); }
397 :
398 : static inline fd_sha256_batch_t *
399 669671 : fd_sha256_batch_init( void * mem ) {
400 669671 : fd_sha256_batch_t * batch = (fd_sha256_batch_t *)mem;
401 669671 : batch->cnt = 0UL;
402 669671 : return batch;
403 669671 : }
404 :
405 : static inline fd_sha256_batch_t *
406 : fd_sha256_batch_add( fd_sha256_batch_t * batch,
407 : void const * data,
408 : ulong sz,
409 7516970 : void * hash ) {
410 7516970 : ulong batch_cnt = batch->cnt;
411 7516970 : batch->data[ batch_cnt ] = data;
412 7516970 : batch->sz [ batch_cnt ] = sz;
413 7516970 : batch->hash[ batch_cnt ] = hash;
414 7516970 : batch_cnt++;
415 7516970 : if( FD_UNLIKELY( batch_cnt==FD_SHA256_BATCH_MAX ) ) {
416 397513 : fd_sha256_private_batch_avx512( batch_cnt, batch->data, batch->sz, batch->hash );
417 397513 : batch_cnt = 0UL;
418 397513 : }
419 7516970 : batch->cnt = batch_cnt;
420 7516970 : return batch;
421 7516970 : }
422 :
423 : static inline void *
424 668637 : fd_sha256_batch_fini( fd_sha256_batch_t * batch ) {
425 668637 : ulong batch_cnt = batch->cnt;
426 668637 : if( FD_LIKELY( batch_cnt ) ) fd_sha256_private_batch_avx512( batch_cnt, batch->data, batch->sz, batch->hash );
427 668637 : return (void *)batch;
428 668637 : }
429 :
430 : static inline void *
431 1034 : fd_sha256_batch_abort( fd_sha256_batch_t * batch ) {
432 1034 : return (void *)batch;
433 1034 : }
434 :
435 : FD_PROTOTYPES_END
436 :
437 : #else
438 : #error "Unsupported FD_SHA256_BATCH_IMPL"
439 : #endif
440 :
441 : #endif /* HEADER_fd_src_ballet_sha256_fd_sha256_h */
|