|
[Бесплатное обучение] Майнинг. Фермы для майнинга Бесплатное обучение майнингу криптовалют. Сборки ферм для майнинга. Как собрать майнинг ферму. Обзор оборудования для майнинга. |
Опции темы |
24.11.2021, 13:45 | #1 |
Регистрация: 01.01.2018
Сообщений: 168
|
Увеличить хэшрейт майнинг ферм - Bitcoin hashrate boosting
Увеличить хэшрейт майнинг ферм - Bitcoin hashrate boosting
0. Intro Всем привет. Это новый выпуск передачи "Очумелые ручки" и сегодня мы будем увеличивать хэшрейт наших майнинг ферм и майнинг ботнетов без дополнительных финансовых вложений. Каким же образом мы это сделаем? Для этого нам понадобятся исходники софта для майнинга используемого в нашей ферме или нашем ботнете и много свободного времени. Да, да, мои любимые подписчики. Мы будем править сорсы и оптимизировать некоторые алгоритмы используемые в майнинге криптовалюты. В качестве подопытного кролика я взял популярную и дорогую криптовалюту, дедушку криптовалютного мира, детище Сатоши Накамото: всем известный биткоин. Для тех кому не терпится узнать вышло ли из этого чего-то стоящее или же хэшрейт приподнялся на 1-2% - загляните в раздел 6 этого экскурса в глубины SHA и узнайте как глубока кролячья нора. Как многим из нас известно, в биткоине для подтверждения блока используется хэш в начале которого присутствует целевое количество нулевых бит. Хэш вычисляется из заголовка блока по алгоритму SHA256. И в один прекрасный день у меня родилась мысль: а нельзя ли как-либо ускорить процесс вычисления хэша. Почитав немного информации о структуре заголовка блока и алгоритме SHA256 я понял - можно. И даже сразу увидел несколько потенциальных возможностей сделать задуманное. Проведя несколько дней в состояние глубокого исследования задачи мной были найдены пути и решения которые могли бы увеличить хэшрейт. Но назревал следующий вопрос: а не реализовал ли кто-то это уже до меня? Пришлось найти исходники некоторых популярных и не очень майнеров на CPU и GPU, а затем покопаться в них. Некоторые из найденных образцов использовали простое хэширование без каких-либо попыток ускорить процесс, другие же старались оптимизировать код SHA256 как только можно. Одним из майнеров старающихся интегрировать как можно больше методов ускорения хэшрейта за счет кода оказался CUDA майнер. Выдаваемый им хэшрейт было решено взять за базовый и стараться улучшить это значение всеми возможными способами. Так как моим любимым языком программирования является Delphi, то весь код в дальнейшем будет именно на нём. 1. Клонируем код CUDA Мной были найдены исходники CUDA майнера и первой поставленной задачей было провести объективный тест замеряющий хэшрейт данного софта. Так как на C-ях я кодить не люблю и весь дальнейших код будет именно на Delphi, то кодовую базу CUDA пришлось транслировать на тот язык с которым буду работать в данной статье. Предоставляю этот код дабы желающие могли убедиться, что я не пытаюсь как-либо замедлить его работу и специально получить более низкий хэшрейт. Код майнера: Код:
unit CUDA; interface uses SysUtils,Dialogs,Windows; type TUINT64 = record case Byte of 0:(Value64:UINT64); 1:(LoDWord, HiDWord: Cardinal); end; const UnixStartDate: TDateTime = 25569.0; var cpu_H256:array [0..7] of Cardinal=($6A09E667, $BB67AE85, $3C6EF372, $A54FF53A,$510E527F, $9B05688C, $1F83D9AB, $5BE0CD19); cpu_K:array [0..63] of Cardinal=($428a2f98,$71374491,$b5c0fbcf,$e9b5dba5,$3956c25b,$59f111f1,$923f82a4,$ab1c5ed5, $d807aa98,$12835b01,$243185be,$550c7dc3,$72be5d74,$80deb1fe,$9bdc06a7,$c19bf174, $e49b69c1,$efbe4786,$0fc19dc6,$240ca1cc,$2de92c6f,$4a7484aa,$5cb0a9dc,$76f988da, $983e5152,$a831c66d,$b00327c8,$bf597fc7,$c6e00bf3,$d5a79147,$06ca6351,$14292967, $27b70a85,$2e1b2138,$4d2c6dfc,$53380d13,$650a7354,$766a0abb,$81c2c92e,$92722c85, $a2bfe8a1,$a81a664b,$c24b8b70,$c76c51a3,$d192e819,$d6990624,$f40e3585,$106aa070, $19a4c116,$1e376c08,$2748774c,$34b0bcb5,$391c0cb3,$4ed8aa4a,$5b9cca4f,$682e6ff3, $748f82ee,$78a5636f,$84c87814,$8cc70208,$90befffa,$a4506ceb,$bef9a3f7,$c67178f2); c_midstate76:array [0..7] of Cardinal; c_dataEnd80:array [0..3] of Cardinal; c_target:array [0..1] of Cardinal; d_target:TUINT64; function sha256d_hash_80(pdata,ptarget:array of Cardinal;var hash:array of Cardinal;var nonce:Cardinal):boolean; implementation function DateTimeToUnix(ConvDate: TDateTime): Longint; begin Result := Round((ConvDate - UnixStartDate) * 86400); end; function ROTR(x:Cardinal;n:byte):Cardinal; begin result:=(x shr n) or (x shl (32-n)); end; function cuda_swab32(x:Cardinal):Cardinal; begin result:=((x shl 24) and $ff000000) or ((x shl 8) and $00ff0000) or ((x shr 8) and $0000ff00) or ((x shr 24) and $000000ff); end; procedure sha256_step1_host(a:Cardinal;b:Cardinal;c:Cardinal;var d:Cardinal;e:Cardinal;f:Cardinal;g:Cardinal;var h:Cardinal;a_in:Cardinal;Kshared:Cardinal); var t1,t2,vxandx,bsg21,bsg20,andorv:Cardinal; begin vxandx := ((f xor g) and e) xor g; bsg21 := ROTR(e, 6) xor ROTR(e, 11) xor ROTR(e, 25); bsg20 := ROTR(a, 2) xor ROTR(a, 13) xor ROTR(a, 22); andorv := (b and c) or ((b or c) and a); t1 := h + bsg21 + vxandx + Kshared + a_in; t2 := bsg20 + andorv; d := d + t1; h := t1 + t2; end; procedure sha256_step2_host(a:Cardinal;b:Cardinal;c:Cardinal;var d:Cardinal;e:Cardinal;f:Cardinal;g:Cardinal;var h:Cardinal;var a_in:array of Cardinal;pc:Cardinal;Kshared:Cardinal); var t1,t2:Cardinal; pcidx1,pcidx2,pcidx3:integer; inx0,inx1,inx2,inx3:Cardinal; ssg21,ssg20,vxandx,bsg21,bsg20,andorv:Cardinal; begin pcidx1 := (pc-2) and $F; pcidx2 := (pc-7) and $F; pcidx3 := (pc-15) and $F; inx0 := a_in[pc]; inx1 := a_in[pcidx1]; inx2 := a_in[pcidx2]; inx3 := a_in[pcidx3]; ssg21 := ROTR(inx1, 17) xor ROTR(inx1, 19) xor (inx1 shr 10); ssg20 := ROTR(inx3, 7) xor ROTR(inx3, 18) xor (inx3 shr 3); vxandx := ((f xor g) and e) xor g; bsg21 := ROTR(e, 6) xor ROTR(e, 11) xor ROTR(e, 25); bsg20 := ROTR(a, 2) xor ROTR(a, 13) xor ROTR(a, 22); andorv := (b and c) or ((b or c) and a); a_in[pc] := ssg21 + inx2 + ssg20 + inx0; t1 := h + bsg21 + vxandx + Kshared + a_in[pc]; t2 := bsg20 + andorv; d := d + t1; h := t1 + t2; end; procedure sha256_round_body_host(a_in:array of Cardinal;var state:array of Cardinal;Kshared:array of Cardinal); var a,b,c,d,e,f,g,h:Cardinal; i:integer; begin a:=state[0]; b:=state[1]; c:=state[2]; d:=state[3]; e:=state[4]; f:=state[5]; g:=state[6]; h:=state[7]; sha256_step1_host(a,b,c,d,e,f,g,h,a_in[0], Kshared[0]); sha256_step1_host(h,a,b,c,d,e,f,g,a_in[1], Kshared[1]); sha256_step1_host(g,h,a,b,c,d,e,f,a_in[2], Kshared[2]); sha256_step1_host(f,g,h,a,b,c,d,e,a_in[3], Kshared[3]); sha256_step1_host(e,f,g,h,a,b,c,d,a_in[4], Kshared[4]); sha256_step1_host(d,e,f,g,h,a,b,c,a_in[5], Kshared[5]); sha256_step1_host(c,d,e,f,g,h,a,b,a_in[6], Kshared[6]); sha256_step1_host(b,c,d,e,f,g,h,a,a_in[7], Kshared[7]); sha256_step1_host(a,b,c,d,e,f,g,h,a_in[8], Kshared[8]); sha256_step1_host(h,a,b,c,d,e,f,g,a_in[9], Kshared[9]); sha256_step1_host(g,h,a,b,c,d,e,f,a_in[10], Kshared[10]); sha256_step1_host(f,g,h,a,b,c,d,e,a_in[11], Kshared[11]); sha256_step1_host(e,f,g,h,a,b,c,d,a_in[12], Kshared[12]); sha256_step1_host(d,e,f,g,h,a,b,c,a_in[13], Kshared[13]); sha256_step1_host(c,d,e,f,g,h,a,b,a_in[14], Kshared[14]); sha256_step1_host(b,c,d,e,f,g,h,a,a_in[15], Kshared[15]); for i:=0 to 2 do begin sha256_step2_host(a,b,c,d,e,f,g,h,a_in,0, Kshared[16+16*i]); sha256_step2_host(h,a,b,c,d,e,f,g,a_in,1, Kshared[17+16*i]); sha256_step2_host(g,h,a,b,c,d,e,f,a_in,2, Kshared[18+16*i]); sha256_step2_host(f,g,h,a,b,c,d,e,a_in,3, Kshared[19+16*i]); sha256_step2_host(e,f,g,h,a,b,c,d,a_in,4, Kshared[20+16*i]); sha256_step2_host(d,e,f,g,h,a,b,c,a_in,5, Kshared[21+16*i]); sha256_step2_host(c,d,e,f,g,h,a,b,a_in,6, Kshared[22+16*i]); sha256_step2_host(b,c,d,e,f,g,h,a,a_in,7, Kshared[23+16*i]); sha256_step2_host(a,b,c,d,e,f,g,h,a_in,8, Kshared[24+16*i]); sha256_step2_host(h,a,b,c,d,e,f,g,a_in,9, Kshared[25+16*i]); sha256_step2_host(g,h,a,b,c,d,e,f,a_in,10,Kshared[26+16*i]); sha256_step2_host(f,g,h,a,b,c,d,e,a_in,11,Kshared[27+16*i]); sha256_step2_host(e,f,g,h,a,b,c,d,a_in,12,Kshared[28+16*i]); sha256_step2_host(d,e,f,g,h,a,b,c,a_in,13,Kshared[29+16*i]); sha256_step2_host(c,d,e,f,g,h,a,b,a_in,14,Kshared[30+16*i]); sha256_step2_host(b,c,d,e,f,g,h,a,a_in,15,Kshared[31+16*i]); end; state[0] := state[0]+a; state[1] := state[1]+b; state[2] := state[2]+c; state[3] := state[3]+d; state[4] := state[4]+e; state[5] := state[5]+f; state[6] := state[6]+g; state[7] := state[7]+h; end; procedure sha256d_setBlock_80(pdata,ptarget:array of Cardinal); var a_in:array [0..15] of Cardinal; a_buf:array [0..7] of Cardinal; a_end:array [0..3] of Cardinal; i:integer; begin for i:=0 to 15 do a_in[i] := cuda_swab32(pdata[i]); for i:=0 to 7 do a_buf[i] := cpu_H256[i]; for i:=0 to 3 do a_end[i] := cuda_swab32(pdata[16+i]); sha256_round_body_host(a_in, a_buf, cpu_K); Move(a_buf[0],c_midstate76[0],32); Move(a_end[0],c_dataEnd80,16); Move(ptarget[6],c_target[0],8); Move(ptarget[6],d_target,8); end; function xandx(a,b,c:Cardinal):Cardinal; begin result:=((b xor c) and a) xor c; end; function xor3b(a,b,c:Cardinal):Cardinal; begin result:=a xor b xor c; end; function bsg2_1(x:Cardinal):Cardinal; begin result:=xor3b(ROTR(x,6),ROTR(x,11),ROTR(x,25)); end; function bsg2_0(x:Cardinal):Cardinal; begin result:=xor3b(ROTR(x,2),ROTR(x,13),ROTR(x,22)); end; function andor32(a,b,c:Cardinal):Cardinal; begin result:=(b and c) or ((b or c) and a); end; procedure sha2_step1(a:Cardinal;b:Cardinal;c:Cardinal;var d:Cardinal;e:Cardinal;f:Cardinal;g:Cardinal;var h:Cardinal;a_in:Cardinal;Kshared:Cardinal); var t1,t2,vxandx,bsg21,bsg20,andorv:Cardinal; begin vxandx := xandx(e, f, g); bsg21 := bsg2_1(e); bsg20 := bsg2_0(a); andorv := andor32(a,b,c); t1 := h + bsg21 + vxandx + Kshared + a_in; t2 := bsg20 + andorv; d := d + t1; h := t1 + t2; end; function ssg2_0(x:Cardinal):Cardinal; begin result:=xor3b(ROTR(x,7),ROTR(x,18),x shr 3); end; function ssg2_1(x:Cardinal):Cardinal; begin result:=xor3b(ROTR(x,17),ROTR(x,19),x shr 10); end; procedure sha2_step2(a:Cardinal;b:Cardinal; c:Cardinal;var d:Cardinal; e:Cardinal; f:Cardinal; g:Cardinal; var h:Cardinal;var a_in:array of Cardinal; pc:Cardinal; Kshared:Cardinal); var t1,t2:Cardinal; pcidx1,pcidx2,pcidx3:integer; inx0,inx1,inx2,inx3:Cardinal; ssg21,ssg20,vxandx,bsg21,bsg20,andorv:Cardinal; begin pcidx1 := (pc-2) and $F; pcidx2 := (pc-7) and $F; pcidx3 := (pc-15) and $F; inx0 := a_in[pc]; inx1 := a_in[pcidx1]; inx2 := a_in[pcidx2]; inx3 := a_in[pcidx3]; ssg21 := ssg2_1(inx1); ssg20 := ssg2_0(inx3); vxandx := xandx(e, f, g); bsg21 := bsg2_1(e); bsg20 := bsg2_0(a); andorv := andor32(a,b,c); a_in[pc] := ssg21 + inx2 + ssg20 + inx0; t1 := h + bsg21 + vxandx + Kshared + a_in[pc]; t2 := bsg20 + andorv; d := d + t1; h := t1 + t2; end; procedure sha256_round_body(var a_in:array of Cardinal;var state:array of Cardinal;var Kshared:array of Cardinal); var a,b,c,d,e,f,g,h:Cardinal; i:integer; begin a:=state[0]; b:=state[1]; c:=state[2]; d:=state[3]; e:=state[4]; f:=state[5]; g:=state[6]; h:=state[7]; sha2_step1(a,b,c,d,e,f,g,h,a_in[0], Kshared[0]); sha2_step1(h,a,b,c,d,e,f,g,a_in[1], Kshared[1]); sha2_step1(g,h,a,b,c,d,e,f,a_in[2], Kshared[2]); sha2_step1(f,g,h,a,b,c,d,e,a_in[3], Kshared[3]); sha2_step1(e,f,g,h,a,b,c,d,a_in[4], Kshared[4]); sha2_step1(d,e,f,g,h,a,b,c,a_in[5], Kshared[5]); sha2_step1(c,d,e,f,g,h,a,b,a_in[6], Kshared[6]); sha2_step1(b,c,d,e,f,g,h,a,a_in[7], Kshared[7]); sha2_step1(a,b,c,d,e,f,g,h,a_in[8], Kshared[8]); sha2_step1(h,a,b,c,d,e,f,g,a_in[9], Kshared[9]); sha2_step1(g,h,a,b,c,d,e,f,a_in[10], Kshared[10]); sha2_step1(f,g,h,a,b,c,d,e,a_in[11], Kshared[11]); sha2_step1(e,f,g,h,a,b,c,d,a_in[12], Kshared[12]); sha2_step1(d,e,f,g,h,a,b,c,a_in[13], Kshared[13]); sha2_step1(c,d,e,f,g,h,a,b,a_in[14], Kshared[14]); sha2_step1(b,c,d,e,f,g,h,a,a_in[15], Kshared[15]); for i:=0 to 2 do begin sha2_step2(a,b,c,d,e,f,g,h,a_in,0, Kshared[16+16*i]); sha2_step2(h,a,b,c,d,e,f,g,a_in,1, Kshared[17+16*i]); sha2_step2(g,h,a,b,c,d,e,f,a_in,2, Kshared[18+16*i]); sha2_step2(f,g,h,a,b,c,d,e,a_in,3, Kshared[19+16*i]); sha2_step2(e,f,g,h,a,b,c,d,a_in,4, Kshared[20+16*i]); sha2_step2(d,e,f,g,h,a,b,c,a_in,5, Kshared[21+16*i]); sha2_step2(c,d,e,f,g,h,a,b,a_in,6, Kshared[22+16*i]); sha2_step2(b,c,d,e,f,g,h,a,a_in,7, Kshared[23+16*i]); sha2_step2(a,b,c,d,e,f,g,h,a_in,8, Kshared[24+16*i]); sha2_step2(h,a,b,c,d,e,f,g,a_in,9, Kshared[25+16*i]); sha2_step2(g,h,a,b,c,d,e,f,a_in,10,Kshared[26+16*i]); sha2_step2(f,g,h,a,b,c,d,e,a_in,11,Kshared[27+16*i]); sha2_step2(e,f,g,h,a,b,c,d,a_in,12,Kshared[28+16*i]); sha2_step2(d,e,f,g,h,a,b,c,a_in,13,Kshared[29+16*i]); sha2_step2(c,d,e,f,g,h,a,b,a_in,14,Kshared[30+16*i]); sha2_step2(b,c,d,e,f,g,h,a,a_in,15,Kshared[31+16*i]); end; state[0] := state[0]+a; state[1] := state[1]+b; state[2] := state[2]+c; state[3] := state[3]+d; state[4] := state[4]+e; state[5] := state[5]+f; state[6] := state[6]+g; state[7] := state[7]+h; end; procedure sha256_round_last(var a_in:array of Cardinal;var state:array of Cardinal;var Kshared:array of Cardinal); var a,b,c,d,e,f,g,h:Cardinal; i:integer; begin a := state[0]; b := state[1]; c := state[2]; d := state[3]; e := state[4]; f := state[5]; g := state[6]; h := state[7]; sha2_step1(a,b,c,d, e,f,g,h, a_in[ 0], Kshared[ 0]); sha2_step1(h,a,b,c, d,e,f,g, a_in[ 1], Kshared[ 1]); sha2_step1(g,h,a,b, c,d,e,f, a_in[ 2], Kshared[ 2]); sha2_step1(f,g,h,a, b,c,d,e, a_in[ 3], Kshared[ 3]); sha2_step1(e,f,g,h, a,b,c,d, a_in[ 4], Kshared[ 4]); sha2_step1(d,e,f,g, h,a,b,c, a_in[ 5], Kshared[ 5]); sha2_step1(c,d,e,f, g,h,a,b, a_in[ 6], Kshared[ 6]); sha2_step1(b,c,d,e, f,g,h,a, a_in[ 7], Kshared[ 7]); sha2_step1(a,b,c,d, e,f,g,h, a_in[ 8], Kshared[ 8]); sha2_step1(h,a,b,c, d,e,f,g, a_in[ 9], Kshared[ 9]); sha2_step1(g,h,a,b, c,d,e,f, a_in[10], Kshared[10]); sha2_step1(f,g,h,a, b,c,d,e, a_in[11], Kshared[11]); sha2_step1(e,f,g,h, a,b,c,d, a_in[12], Kshared[12]); sha2_step1(d,e,f,g, h,a,b,c, a_in[13], Kshared[13]); sha2_step1(c,d,e,f, g,h,a,b, a_in[14], Kshared[14]); sha2_step1(b,c,d,e, f,g,h,a, a_in[15], Kshared[15]); for i:=0 to 1 do begin sha2_step2(a,b,c,d, e,f,g,h, a_in, 0, Kshared[16+16*i]); sha2_step2(h,a,b,c, d,e,f,g, a_in, 1, Kshared[17+16*i]); sha2_step2(g,h,a,b, c,d,e,f, a_in, 2, Kshared[18+16*i]); sha2_step2(f,g,h,a, b,c,d,e, a_in, 3, Kshared[19+16*i]); sha2_step2(e,f,g,h, a,b,c,d, a_in, 4, Kshared[20+16*i]); sha2_step2(d,e,f,g, h,a,b,c, a_in, 5, Kshared[21+16*i]); sha2_step2(c,d,e,f, g,h,a,b, a_in, 6, Kshared[22+16*i]); sha2_step2(b,c,d,e, f,g,h,a, a_in, 7, Kshared[23+16*i]); sha2_step2(a,b,c,d, e,f,g,h, a_in, 8, Kshared[24+16*i]); sha2_step2(h,a,b,c, d,e,f,g, a_in, 9, Kshared[25+16*i]); sha2_step2(g,h,a,b, c,d,e,f, a_in,10, Kshared[26+16*i]); sha2_step2(f,g,h,a, b,c,d,e, a_in,11, Kshared[27+16*i]); sha2_step2(e,f,g,h, a,b,c,d, a_in,12, Kshared[28+16*i]); sha2_step2(d,e,f,g, h,a,b,c, a_in,13, Kshared[29+16*i]); sha2_step2(c,d,e,f, g,h,a,b, a_in,14, Kshared[30+16*i]); sha2_step2(b,c,d,e, f,g,h,a, a_in,15, Kshared[31+16*i]); end; sha2_step2(a,b,c,d, e,f,g,h, a_in, 0, Kshared[16+16*2]); sha2_step2(h,a,b,c, d,e,f,g, a_in, 1, Kshared[17+16*2]); sha2_step2(g,h,a,b, c,d,e,f, a_in, 2, Kshared[18+16*2]); sha2_step2(f,g,h,a, b,c,d,e, a_in, 3, Kshared[19+16*2]); sha2_step2(e,f,g,h, a,b,c,d, a_in, 4, Kshared[20+16*2]); sha2_step2(d,e,f,g, h,a,b,c, a_in, 5, Kshared[21+16*2]); sha2_step2(c,d,e,f, g,h,a,b, a_in, 6, Kshared[22+16*2]); sha2_step2(b,c,d,e, f,g,h,a, a_in, 7, Kshared[23+16*2]); sha2_step2(a,b,c,d, e,f,g,h, a_in, 8, Kshared[24+16*2]); sha2_step2(h,a,b,c, d,e,f,g, a_in, 9, Kshared[25+16*2]); sha2_step2(g,h,a,b, c,d,e,f, a_in,10, Kshared[26+16*2]); sha2_step2(f,g,h,a, b,c,d,e, a_in,11, Kshared[27+16*2]); sha2_step2(e,f,g,h, a,b,c,d, a_in,12, Kshared[28+16*2]); sha2_step2(d,e,f,g, h,a,b,c, a_in,13, Kshared[29+16*2]); state[6]:=state[6]+g; state[7]:=state[7]+h; end; function cuda_swab32ll(x:TUINT64):TUINT64; begin result.LoDWord:=cuda_swab32(x.LoDWord); result.HiDWord:=cuda_swab32(x.HiDWord) end; function sha256d_gpu_hash_shared(nonce:Cardinal):boolean; var dat:array [0..15] of Cardinal; buf:array [0..7] of Cardinal; i:integer; high:TUINT64; begin dat[0]:=c_dataEnd80[0]; dat[1]:=c_dataEnd80[1]; dat[2]:=c_dataEnd80[2]; dat[3]:=nonce; dat[4]:=2147483648; dat[15]:=640; for i:=5 to 14 do dat[i] := 0; for i:=0 to 7 do buf[i]:=c_midstate76[i]; sha256_round_body(dat, buf, cpu_K); for i:=0 to 7 do dat[i]:=buf[i]; dat[8]:=2147483648; for i:=9 to 14 do dat[i]:=0; dat[15]:=256; for i:=0 to 7 do buf[i]:=cpu_H256[i]; sha256_round_last(dat, buf, cpu_K); high.HiDWord:=buf[6]; high.LoDWord:=buf[7]; high := cuda_swab32ll(high); if high.Value64<=d_target.Value64 then result:=true else result:=false; end; function sha256d_hash_80(pdata,ptarget:array of Cardinal;var hash:array of Cardinal;var nonce:Cardinal):boolean; var dt:longint; begin result:=true; dt:=Now; sha256d_setBlock_80(pdata,ptarget); for nonce:=0 to 4294967295 do begin if sha256d_gpu_hash_shared(nonce) then Exit; if nonce=10000001 then Showmessage(inttostr(DateTimeToUnix(now)-dt)); end; result:=false; end; end. Теперь нужно подготовить тестовый заголовок блока, назначить цель и запустить майнинг. Так как формирование заголовка блока из транзакций и расчет цели выходят за рамки этой статьи, то я их установлю в качестве фиксированных значений. В качестве образца был взят реально существующий блок цепи блокчейна биткоина под номером 125552. Nonce удовлетворяющий условиям цели для данной транзакции 1117865621. Я специально не менял таймстамп метку начала майнинга для текущего блока чтобы можно было проверить правильность вычисления золотой шары для указанного выше блока с имеющимся nonce. Код:
function SwapString(s:string):string; var i:integer; begin result:=''; i:=length(s)-1; while i>0 do begin result:=result+copy(s,i,2); i:=i-2; end; end; function HexToInt(Str : string): integer; var i, r : integer; begin val('$'+Trim(Str),r, i); if i<>0 then HexToInt := 0 else HexToInt := r; end; function HexToString(s:string):string; var i:integer; begin result:=''; i:=1; while i<=length(s) do begin result:=result+char(HexToInt(s[i]+s[i+1])); i:=i+2; end; end; procedure InitAndStart; var data:array of Cardinal; hash:array of Cardinal; prevblock,rootHash:string; nonce:Cardinal; target:array of Cardinal; begin setlength(hash,8); setlength(target,8); target[0]:=0; target[1]:=0; target[2]:=0; target[3]:=0; target[4]:=0; target[5]:=4060086272; target[6]:=17593; target[7]:=0; prevblock:=HEXToString(SwapString('00000000000008a3a41b85b8b29ad444def299fee21793cd8b9e567eab02cd81')); rootHash:=HEXToString(SwapString('2b12fcf1b09288fcaff797d71e950e71ae42b91e8bdb2304758dfcffc2b620e3')); setlength(data,20); data[0]:=1; Move(prevblock[1],data[1],32); Move(rootHash[1],data[9],32); data[17]:=1305998791; data[18]:=440711666; data[19]:=0; if sha256d_hash_80(data,target,hash,nonce) then Showmessage(inttostr(nonce)) else Showmessage('Not found'); end; 2. Базовая теоретическая информация: алгоритм SHA256, Merkle Root, block header, golden share 2.0 Вводная в алгоритм майнинга Для дальнейшего понимания что я делаю и как вообще всё это работает надо разобраться с структурой блока биткоина и процессом вычисления подтверждения блока. Подтверждение блока делится на несколько этапов: а) Наращиваем extranonce в coinbase транзакции, обнуляем nonce б) Вычисляем Merkle Root с новой coinbase и формируем заголовок блока в) Вычисляем двойной хэш от заголовка блока. Если хэш удовлетворяет заданным условиям - выходим из функции. Если хэш не удовлетворяет заданным условиям - в заголовке блока наращиваем nonce на 1 и повторяем пункт в). Если после наращивания nonce стало больше чем 4294967295, то возвращаемся к пункту а) В рамках данной статьи я ограничусь методами ускорения вычисления подтверждения блока на этапе перебора nonce и не буду рассказывать о том как меняется coinbase транзакция если nonce превышает пределы допустимого диапазона, как вычисляется Merkle Root и как можно ещё сильней увеличить скорость майнинга. Т.е. мы сейчас работаем только с пунктом в) описанного выше алгоритма. Хотя в пунктах а) и б) тоже есть векторы для исследования. 2.1 Block header и golden share Как я уже писал выше, подтверждение блока это SHA256 хэш в начале которого присутствует нужное количество нулевых бит или больше. Но это не простой хэш, а хэш вычисленный из хэша от заголовка блока. Выглядит это так: sha256(sha256(block_header)). Хэш удовлетворяющий найденным условиям называется золотой шарой. Для того чтобы найти такую золотую шару нужно провести множество вычислений меняя некий nonce постоянно наращивая его на 1 и вычисляя новый двойной хэш, а затем проверять его на условие достижения цели. Но где же прячется этот nonce и что он из себя представляет? Nonce находится внутри block_header и представляет из себя целое беззнаковое число размером 32 бита. Для далеких от кодинга людей, а также для "кодеров" умеющих двигать только кнопочки по форме разжую: nonce может из себя представлять число в диапазоне от 0 до 4294967295. В начале поиска золотой шары nonce равен 0 и с каждым новым вычисленным хэшем не удовлетворяющим заданному условию он увеличивается на 1. Теперь посмотрим во внутренности заголовка блока и узнаем из чего он состоит: а) Версия - версия актуальной сети биткоина. Размер 4 байта б) Хэш прошлого блока - размер 32 байта в) Merkle Root - тоже хэш, но о нем поговорим позже. Размер 32 байта г) Таймстамп - UTC время начала майнинга. Полные ноды не принимают блоки с хэадерами в которых таймстамп отличен от их времени более чем на 2 часа. Среднее время майнинга блока должно составлять 10 минут. Размер 4 байта д) Сложность - Сколько нулевых бит должно быть в начале золотой шары. Размер 4 байта ж) Nonce - выбирается перебором. Размер 4 байта 2.2 Merkle Root Не буду давать полное описание этого хэша. Скажу только что Merkle Root - это вычисленный по определенным правилам хэш из всех транзакций присутствующих в блоке. 2.3 SHA256 Для дальнейшей разработки нам потребуется изучить сам алгоритм хэширования SHA256 и подготовить его исходники для того чтобы было с чем работать дальше. Также не буду давать полное описание данного алгоритма, так как это сильно раздует статью. Для тех кому интерено: tproger.ru/translations/sha-2-step-by-step/ 3. CUDA: борьба за хэшрейт Майнинг на классическом алгоритме SHA подразумевает из себя полноценное вычисление хэша из заголовка блока и рассчет хэша из прошлого хэша. Далее происходит тестирование на достижение цели. Первое хэширование имеет два раунда особенность которых в том, что первый раунд заканчивается всегда одним и тем-же результатом. Это происходит за счет того, что в заголовке блока хэшируемая в первом раунде часть не меняется пока не будет изменен сам заголовок блока. Из-за этой особенности можно вынести первый раунд хэширования за пределы цикла перебора nonce и вычислить это значение всего один раз. Разработчики CUDA учли эту возможность и тем самым подняли хэшрейт приблизительно в 1.33 раза от того что могло бы быть при классической реализации. Но по неизвестным мне причинам многие другие софты для майнинга не используют этот маленький легальный чит. Кроме того в CUDA хэширование ускоряется за счет не полного рассчета хэша, а окончательное вычисление лишь последних двух двойных слов и тестирование их на достижение цели. Это можно увидеть в последних двух строчках процедуры рассчета второго хэша sha256_round_last: Код:
state[6]:=state[6]+g; state[7]:=state[7]+h; Во время второго хэширования у нас также есть статические данные. Длинна хэшируемых данных 32 байта, Длинна массива после расширения 16 32-битных беззнаковых числа. Единичный бит записывается в позицию 8. Размер хэшируемых данных 256 бит и он записывается в позицию 15 расширенного массива. Таким образом это всё можно рассчитать всего один раз и выделить нужный объем памяти без лишних телодвижений. В CUDA майнере это всё реализованно. За счет выше описанных методов хэшрейт CUDA майнера держится на уровне 107526 х/с. Можно ли улучшить это значение? Давайте постараемся в этом разобраться. 4. Обнаруженные методы бустинга хэшрейта В процессе долгого и кропотливого исследования мне удалось обнаружить ещё несколько мест которые можно оптимизировать и выдавить из SHA дополнительные процентики к столь желанному хэшрейту. Для реализации задуманного пришлось отказаться от кода хэширования предложенного в CUDA майнере и реализовать SHA256 с нуля в его классическом виде, а уже затем постепенно интегрировать все уже имеющиеся и новые задумки влияющие на скорость. Самое первое на что я обратил внимание ещё до того как нашел код CUDA, так это то, что нет смысла вычислять второй хэш в полном объеме. Но если в CUDA вычисляются последние 2 части хэша, то я считаю, что в этом нет смысла и хватит одной. Обосную: В последнее время сложность майнинга уже на столько высока, что требуемая цель сильно превышает 32 нулевых бита. Вероятность того, что сложность упадет до значения эквивалентного менее чем 32 битам стремится к нулю. Значит мы можем спокойно вычислять только последний элемент хэша и сравнивать его с 0. Если он больше 0, то этот хэш гарантированно не является искомым и дальнейшие вычисления, а также последующий тест на достижения цели являются бессмысленными и следует сразу же брать новый nonce. Также в процессе каждого раунда хэширования заголовка блока соответствующая функция должна генерировать очередь сообщений размером 64 32-битных беззнаковых числа. При вычисление первого хэша четверть этой очереди берется из хэшируемых данных, а оставшаяся рассчитывается в процессе. Так как мы знаем как будут меняться хэшируемые данные на каждом этапе перебора nonce, то мы можем предсказать как будет меняться очередь сообщений. Таким образом теряется смысл в постоянной генерации очереди сообщений в полном объеме. Её мы можем сгенерировать один раз и передавать в нужные функции. Этим мы избавляемся от некоторых операций выделения, копирования и изменения памяти. Более детальное изучение процесса генерации очереди сообщений для хэширования второй части заголовка выявило, что при неизменном timestamp 16 и 17 элементы данной очереди будут фиксированы и неизменны на длительном промежутке времени. Так как таймстамп нет смысла менять каждую интерацию цикла смены nonce и даже нет смысла менять его каждую секунду, а вполне хватит замены времени начала майнинга в периоде к примеру раз в пол часа или и того реже, то эти элементы очереди также можно пересчитывать только в момент изменения timestamp, а в остальное время просто брать их из памяти. Кроме того посмотрим на код вычисления элементов очереди сообщений в традиционном SHA: Код:
for i:=16 to 63 do begin s0 := rightrotate(w[i-15],7) xor rightrotate(w[i-15],18) xor (w[i-15] shr 3); s1 := rightrotate(w[i-2],17) xor rightrotate(w[i-2],19) xor (w[i-2] shr 10); w [i] := w[i-16] + s0 + w[i-7] + s1; end; Есть ещё один момент влияющий на хэшрейт. После формирования полной очереди сообщений используемой для рассчета хэша из второй части заголовка блока выполняются 64 раунда самого хэширования. Код классического хэширования будет выглядеть так: Код:
for i:=0 to 63 do begin S1 := rightrotate(e,6) xor rightrotate(e,11) xor rightrotate(e,25); ch := (e and f) xor ((not e) and g); temp1 := h + S1 + ch + k[i] + w[i]; S0 := rightrotate(a,2) xor rightrotate(a,13) xor rightrotate(a,22); maj := (a and b) xor (a and c) xor (b and c); temp2 := S0 + maj; h := g; g := f; f := e; e := d + temp1; d := c; c := b; b := a; a := temp1 + temp2; end; Таким образом можно вычислить состояние переменных a, b, c, d, e, f, g и h для первых трех раундов, провести для них четвертый раунд хэширования считая что w[3] равно 0 и отправлять их в функцию хэширования второй части заголовка блока в неизменном виде. Уже в этой функции добавлять к a и e текущий nonce и начинать хэширование с 5-ого раунда тем самым экономя 1/16 времени работы этого куска кода. Ещё одно отличие от кода CUDA внесенное в мою наработку - изменение timestamp и перерасчет всех фиксированных значений при изменение global_timestamp. global_timestamp должен будет инициализироваться при запуске майнинга и меняться раз в пол часа в отдельном потоке, но в предоставленном ниже коде код потока изменяющего global_timestamp не указан. Для проверки правильности хэширования вы можете изменить nonce на nonce золотой шары тестового блока и установить global_timestamp в timestamp метку указанную в заголовке тестового блока. 5. Код ускоренного майнинга Модуль для майнинга после всех внесенных изменений будет выглядеть так: Код:
unit BoostedMiner; interface uses Windows,SysUtils,Dialogs; type Tdata=array of Cardinal; const UnixStartDate: TDateTime = 25569.0; var k:array [0..63] of Cardinal=($428a2f98,$71374491,$b5c0fbcf,$e9b5dba5,$3956c25b,$59f111f1,$923f82a4,$ab1c5ed5, $d807aa98,$12835b01,$243185be,$550c7dc3,$72be5d74,$80deb1fe,$9bdc06a7,$c19bf174, $e49b69c1,$efbe4786,$0fc19dc6,$240ca1cc,$2de92c6f,$4a7484aa,$5cb0a9dc,$76f988da, $983e5152,$a831c66d,$b00327c8,$bf597fc7,$c6e00bf3,$d5a79147,$06ca6351,$14292967, $27b70a85,$2e1b2138,$4d2c6dfc,$53380d13,$650a7354,$766a0abb,$81c2c92e,$92722c85, $a2bfe8a1,$a81a664b,$c24b8b70,$c76c51a3,$d192e819,$d6990624,$f40e3585,$106aa070, $19a4c116,$1e376c08,$2748774c,$34b0bcb5,$391c0cb3,$4ed8aa4a,$5b9cca4f,$682e6ff3, $748f82ee,$78a5636f,$84c87814,$8cc70208,$90befffa,$a4506ceb,$bef9a3f7,$c67178f2); fs0:array [0..13] of Cardinal; fs1:array [0..1] of Cardinal; round4:array [0..7] of Cardinal; global_timestamp:Cardinal; function nonce_search(inp:Tdata;target:Tdata;var nonce:Cardinal):boolean; function cuda_swab32(x:Cardinal):Cardinal; function DateTimeToUnix(ConvDate: TDateTime): Cardinal; implementation function DateTimeToUnix(ConvDate: TDateTime): Cardinal; begin Result := Round((ConvDate - UnixStartDate) * 86400); end; function cuda_swab32(x:Cardinal):Cardinal; begin result:=((x shl 24) and $ff000000) or ((x shl 8) and $00ff0000) or ((x shr 8) and $0000ff00) or ((x shr 24) and $000000ff); end; function RightRotate(w:Cardinal;l:byte):Cardinal; begin result:=(w shr l)+(w shl (32-l)); end; function CheckToTarget(var hash:array of Cardinal;var target:Tdata):boolean; var i:integer; begin result:=true; for i:=7 downto 0 do begin if hash[i]<target[i] then Exit; if hash[i]>target[i] then begin result:=false; Exit; end; end; end; procedure SHA_dynamic_header_hashing(w:array of Cardinal;var buf:Tdata;timestamp:Cardinal;nonce:Cardinal;var hash:array of Cardinal); var h0,h1,h2,h3,h4,h5,h6,h7:Cardinal; i:integer; s0,s1,ch,temp1,temp2,maj:Cardinal; a,b,c,d,e,f,g,h:Cardinal; begin w[1]:=Cardinal(timestamp); w[3]:=nonce; h0 := buf[0]; h1 := buf[1]; h2 := buf[2]; h3 := buf[3]; h4 := buf[4]; h5 := buf[5]; h6 := buf[6]; h7 := buf[7]; s0 := rightrotate(w[3],7) xor rightrotate(w[3],18) xor (w[3] shr 3); w [18] := w[2] + s0 + w[11] + fs1[0]; w [19] := w[3] + fs0[0] + w[12] + fs1[1]; for i:=20 to 32 do begin s1 := rightrotate(w[i-2],17) xor rightrotate(w[i-2],19) xor (w[i-2] shr 10); w[i] := w[i-16] + fs0[i-19] + w[i-7] + s1; end; for i:=33 to 63 do begin s0 := rightrotate(w[i-15],7) xor rightrotate(w[i-15],18) xor (w[i-15] shr 3); s1 := rightrotate(w[i-2],17) xor rightrotate(w[i-2],19) xor (w[i-2] shr 10); w[i] := w[i-16] + s0 + w[i-7] + s1; end; a:=round4[0]; b:=round4[1]; c:=round4[2]; d:=round4[3]; e:=round4[4]; f:=round4[5]; g:=round4[6]; h:=round4[7]; a:=a+nonce; e:=e+nonce; for i:=4 to 63 do begin S1 := rightrotate(e,6) xor rightrotate(e,11) xor rightrotate(e,25); ch := (e and f) xor ((not e) and g); temp1 := h + S1 + ch + k[i] + w[i]; S0 := rightrotate(a,2) xor rightrotate(a,13) xor rightrotate(a,22); maj := (a and b) xor (a and c) xor (b and c); temp2 := S0 + maj; h := g; g := f; f := e; e := d + temp1; d := c; c := b; b := a; a := temp1 + temp2; end; hash[0]:=h0+a; hash[1]:=h1+b; hash[2]:=h2+c; hash[3]:=h3+d; hash[4]:=h4+e; hash[5]:=h5+f; hash[6]:=h6+g; hash[7]:=h7+h; end; function SHA_second_hashing(var w:array of Cardinal):boolean; var h0,h1,h2,h3,h4,h5,h6,h7:Cardinal; i:integer; s0,s1,ch,temp1,temp2,maj:Cardinal; a,b,c,d,e,f,g,h:Cardinal; begin h0 := $6a09e667; h1 := $bb67ae85; h2 := $3c6ef372; h3 := $a54ff53a; h4 := $510e527f; h5 := $9b05688c; h6 := $1f83d9ab; h7 := $5be0cd19; for i:=16 to 63 do begin s0 := rightrotate(w[i-15],7) xor rightrotate(w[i-15],18) xor (w[i-15] shr 3); s1 := rightrotate(w[i-2],17) xor rightrotate(w[i-2],19) xor (w[i-2] shr 10); w[i] := w[i-16] + s0 + w[i-7] + s1; end; a:=h0; b:=h1; c:=h2; d:=h3; e:=h4; f:=h5; g:=h6; h:=h7; for i:=0 to 63 do begin S1 := rightrotate(e,6) xor rightrotate(e,11) xor rightrotate(e,25); ch := (e and f) xor ((not e) and g); temp1 := h + S1 + ch + k[i] + w[i]; S0 := rightrotate(a,2) xor rightrotate(a,13) xor rightrotate(a,22); maj := (a and b) xor (a and c) xor (b and c); temp2 := S0 + maj; h := g; g := f; f := e; e := d + temp1; d := c; c := b; b := a; a := temp1 + temp2; end; w[7]:=h7+h; if w[7]<>0 then begin result:=false; Exit; end; w[6]:=h6+g; w[5]:=h6+f; w[4]:=h6+e; w[3]:=h6+d; w[2]:=h6+c; w[1]:=h6+b; w[0]:=h6+a; result:=true; end; function sha256_doublehash(var buf,target:Tdata;var w2:array of Cardinal;w3:array of Cardinal;timestamp:Cardinal;nonce:Cardinal):boolean; begin SHA_dynamic_header_hashing(w2,buf,timestamp,nonce,w3); if not SHA_second_hashing(w3) then result:=false else if CheckToTarget(w3,target) then result:=true else result:=false; end; procedure SHA_stable_header_hashing(var w:array of Cardinal;var buf:Tdata); var h0,h1,h2,h3,h4,h5,h6,h7:Cardinal; i:integer; s0,s1,ch,temp1,temp2,maj:Cardinal; a,b,c,d,e,f,g,h:Cardinal; begin h0 := $6a09e667; h1 := $bb67ae85; h2 := $3c6ef372; h3 := $a54ff53a; h4 := $510e527f; h5 := $9b05688c; h6 := $1f83d9ab; h7 := $5be0cd19; for i:=16 to 63 do begin s0 := rightrotate(w[i-15],7) xor rightrotate(w[i-15],18) xor (w[i-15] shr 3); s1 := rightrotate(w[i-2],17) xor rightrotate(w[i-2],19) xor (w[i-2] shr 10); w[i] := w[i-16] + s0 + w[i-7] + s1; end; a:=h0; b:=h1; c:=h2; d:=h3; e:=h4; f:=h5; g:=h6; h:=h7; for i:=0 to 63 do begin S1 := rightrotate(e,6) xor rightrotate(e,11) xor rightrotate(e,25); ch := (e and f) xor ((not e) and g); temp1 := h + S1 + ch + k[i] + w[i]; S0 := rightrotate(a,2) xor rightrotate(a,13) xor rightrotate(a,22); maj := (a and b) xor (a and c) xor (b and c); temp2 := S0 + maj; h := g; g := f; f := e; e := d + temp1; d := c; c := b; b := a; a := temp1 + temp2; end; buf[0]:=h0+a; buf[1]:=h1+b; buf[2]:=h2+c; buf[3]:=h3+d; buf[4]:=h4+e; buf[5]:=h5+f; buf[6]:=h6+g; buf[7]:=h7+h; end; procedure get_round4_calculation(var w:array of Cardinal;buf:Tdata;timestamp:Cardinal); var a,b,c,d,e,f,g,h:Cardinal; i:integer; s0,s1,ch,maj,temp1,temp2:Cardinal; begin w[1]:=timestamp; w[3]:=0; a:=buf[0]; b:=buf[1]; c:=buf[2]; d:=buf[3]; e:=buf[4]; f:=buf[5]; g:=buf[6]; h:=buf[7]; for i:=0 to 3 do begin S1 := rightrotate(e,6) xor rightrotate(e,11) xor rightrotate(e,25); ch := (e and f) xor ((not e) and g); temp1 := h + S1 + ch + k[i] + w[i]; S0 := rightrotate(a,2) xor rightrotate(a,13) xor rightrotate(a,22); maj := (a and b) xor (a and c) xor (b and c); temp2 := S0 + maj; h := g; g := f; f := e; e := d + temp1; d := c; c := b; b := a; a := temp1 + temp2; end; round4[0]:=a; round4[1]:=b; round4[2]:=c; round4[3]:=d; round4[4]:=e; round4[5]:=f; round4[6]:=g; round4[7]:=h; end; function nonce_search(inp:Tdata;target:Tdata;var nonce:Cardinal):boolean; var dt:Cardinal; buf:Tdata; w1,w2,w3:array [0..63] of Cardinal; timestamp:Cardinal; s0,s1:Cardinal; i:integer; begin result:=true; timestamp:=0; dt:=DateTimeToUnix(Now); setlength(buf,8); Move(inp[0],w1[0],64); ZeroMemory(@w2[0],256); Move(inp[16],w2[0],16); w2[4]:=2147483648; w2[15]:=640; ZeroMemory(@w3[0],256); w3[8]:=2147483648; w3[15]:=256; SHA_stable_header_hashing(w1,buf); for nonce:=0 to 4294967295 do begin if timestamp<>global_timestamp then begin timestamp:=global_timestamp; for i:=16 to 17 do begin s0 := rightrotate(w2[i-15],7) xor rightrotate(w2[i-15],18) xor (w2[i-15] shr 3); s1 := rightrotate(w2[i-2],17) xor rightrotate(w2[i-2],19) xor (w2[i-2] shr 10); w2 [i] := w2[i-16] + s0 + w2[i-7] + s1; end; for i:=0 to 13 do fs0[i] := rightrotate(w2[i+4],7) xor rightrotate(w2[i+4],18) xor (w2[i+4] shr 3); for i:=0 to 1 do fs1[i]:=rightrotate(w2[i+16],17) xor rightrotate(w2[i+16],19) xor (w2[i+16] shr 10); get_round4_calculation(w2,buf,timestamp); end; if sha256_doublehash(buf,target,w2,w3,timestamp,nonce) then Exit; if nonce=10000001 then Showmessage(inttostr(DateTimeToUnix(now)-dt)); end; result:=false; end; end. Код:
function SwapString(s:string):string; var i:integer; begin result:=''; i:=length(s)-1; while i>0 do begin result:=result+copy(s,i,2); i:=i-2; end; end; function HexToInt(Str : string): integer; var i, r : integer; begin val('$'+Trim(Str),r, i); if i<>0 then HexToInt := 0 else HexToInt := r; end; function HexToString(s:string):string; var i:integer; begin result:=''; i:=1; while i<=length(s) do begin result:=result+char(HexToInt(s[i]+s[i+1])); i:=i+2; end; end; procedure InitAndStart; var data:Tdata; prevblock,rootHash:string; nonce:Cardinal; target:Tdata; i:integer; begin global_timestamp:=DateTimeToUnix(now); setlength(target,8); target[0]:=0; target[1]:=0; target[2]:=0; target[3]:=0; target[4]:=0; target[5]:=4060086272; target[6]:=17593; target[7]:=0; prevblock:=HEXToString(SwapString('00000000000008a3a41b85b8b29ad444def299fee21793cd8b9e567eab02cd81')); rootHash:=HEXToString(SwapString('2b12fcf1b09288fcaff797d71e950e71ae42b91e8bdb2304758dfcffc2b620e3')); setlength(data,20); data[0]:=1; Move(prevblock[1],data[1],32); Move(rootHash[1],data[9],32); data[17]:=1305998791; data[18]:=440711666; data[19]:=0; for i:=0 to 19 do data[i]:=cuda_swab32(data[i]); if nonce_search(data,target,nonce) then Showmessage(inttostr(nonce)) else Showmessage('Not found'); end; 6. Замеры и выводы После запуска и замеров моей вариации майнера при тех же условиях и на том же железе был получен результат 144927 х/c. Таким образом коэффициент увеличения скорости майнинга на этапе перебора nonce составил x1.3478 от результата CUDA, что можно считать вполне приемлемым исходом исследования. Если же посчитать коэффициент буста хэшрейта по сравнению с классической реализацией SHA, то он составит приблизительно x1.7246. Думаю дополнительные исследования позволят ещё немного увеличить скорость. Кроме того ресерч надо проводить и для этапов хэширования codebase транзакции вместе с формированием Merkle Root, так как эти вычисления при больших мощностях будут проходить достаточно часто и могут тормозить основной процесс майнинга. Более того, подобные методы ускорения майнинга могут найтись и в других криптовалютах. Хотите чтобы я провел дополнительные исследования и выложил результаты на форуме? Голосуйте за меня и да пусть победит темная сторона силы. |
24.11.2021, 13:48 | #2 |
Регистрация: 01.01.2018
Сообщений: 168
|
Дополнение 1: ещё один обнаруженный способ
Всякие интересные решения часто приходят перед сном и это не исключение. Кто-то пытаясь заснуть считает овец. Я же думал об алгоритме хэширования. И обнаружил ещё один интересующий меня участок кода. Функция SHA_second_hashing рассчитывает второй хэш из первого и, как мы ранее обсуждали, в ней нет смысла делать рассчет хэша до кноца. В CUDA свою окончательную форму принимают лишь последние 2 32-битных элемента хэша. У меня же проводится тестирование последнего элемента на равенство 0 и вылету из функции при не соблюдение данного условия.. Но в процессе 64 раундов хэширования очереди сообщений так сильно необходимый в состояние 0 последний элемент не меняет своё состояние в последних 3-ех раундах. Он лишь меняет свою позицию. Посмотрите сюда: Код:
for i:=0 to 63 do begin S1 := rightrotate(e,6) xor rightrotate(e,11) xor rightrotate(e,25); ch := (e and f) xor ((not e) and g); temp1 := h + S1 + ch + k[i] + w[i]; S0 := rightrotate(a,2) xor rightrotate(a,13) xor rightrotate(a,22); maj := (a and b) xor (a and c) xor (b and c); temp2 := S0 + maj; h := g; g := f; f := e; e := d + temp1; d := c; c := b; b := a; a := temp1 + temp2; end; w[7]:=h7+h; if w[7]<>0 then begin result:=false; Exit; end; Внесем изменения в исследуемую функцию: Код:
function SHA_second_hashing(var w:array of Cardinal):boolean; var h0,h1,h2,h3,h4,h5,h6,h7:Cardinal; i:integer; s0,s1,ch,temp1,temp2,maj:Cardinal; a,b,c,d,e,f,g,h:Cardinal; begin h0 := $6a09e667; h1 := $bb67ae85; h2 := $3c6ef372; h3 := $a54ff53a; h4 := $510e527f; h5 := $9b05688c; h6 := $1f83d9ab; h7 := $5be0cd19; for i:=16 to 63 do begin s0 := rightrotate(w[i-15],7) xor rightrotate(w[i-15],18) xor (w[i-15] shr 3); s1 := rightrotate(w[i-2],17) xor rightrotate(w[i-2],19) xor (w[i-2] shr 10); w[i] := w[i-16] + s0 + w[i-7] + s1; end; a:=h0; b:=h1; c:=h2; d:=h3; e:=h4; f:=h5; g:=h6; h:=h7; for i:=0 to 60 do begin S1 := rightrotate(e,6) xor rightrotate(e,11) xor rightrotate(e,25); ch := (e and f) xor ((not e) and g); temp1 := h + S1 + ch + k[i] + w[i]; S0 := rightrotate(a,2) xor rightrotate(a,13) xor rightrotate(a,22); maj := (a and b) xor (a and c) xor (b and c); temp2 := S0 + maj; h := g; g := f; f := e; e := d + temp1; d := c; c := b; b := a; a := temp1 + temp2; end; if h7+e<>0 then begin result:=false; Exit; end; for i:=61 to 63 do begin S1 := rightrotate(e,6) xor rightrotate(e,11) xor rightrotate(e,25); ch := (e and f) xor ((not e) and g); temp1 := h + S1 + ch + k[i] + w[i]; S0 := rightrotate(a,2) xor rightrotate(a,13) xor rightrotate(a,22); maj := (a and b) xor (a and c) xor (b and c); temp2 := S0 + maj; h := g; g := f; f := e; e := d + temp1; d := c; c := b; b := a; a := temp1 + temp2; end; w[7]:=h7+h; if w[7]<>0 then begin result:=false; Exit; end; w[6]:=h6+g; w[5]:=h6+f; w[4]:=h6+e; w[3]:=h6+d; w[2]:=h6+c; w[1]:=h6+b; w[0]:=h6+a; result:=true; end; |