Форум о криптовалютах | Майнинг криптовалют | Торговля криптовалютой | Инвестиции в криптовалюту
Вернуться   Форум о криптовалютах | Майнинг криптовалют | Торговля криптовалютой | Инвестиции в криптовалюту > Майнинг криптовалют > [Бесплатное обучение] Майнинг. Фермы для майнинга

[Бесплатное обучение] Майнинг. Фермы для майнинга Бесплатное обучение майнингу криптовалют. Сборки ферм для майнинга. Как собрать майнинг ферму. Обзор оборудования для майнинга.

Advertising

Ответ
Опции темы
Непрочитано 24.11.2021, 13:45   #1
ADMINISTRATOR
 
Регистрация: 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.
В самом майнере я добавил код подсчитывающий время майнинга 10000000 хэшей. На этой основе будет расчитан хэшрейт.
Теперь нужно подготовить тестовый заголовок блока, назначить цель и запустить майнинг. Так как формирование заголовка блока из транзакций и расчет цели выходят за рамки этой статьи, то я их установлю в качестве фиксированных значений. В качестве образца был взят реально существующий блок цепи блокчейна биткоина под номером 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;
Результат замера скорости CUDA майнера в одном потоке на CPU: 107526 хэша в секунду.


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;
И последнее что реализовано в CUDA майнере - стабильная генерация расширенного блока. Алгоритм SHA подразумевает изменение входных данных перед тем как начать процесс рассчета хэша. Во-первых, в конец хэшируемых данных добавляется бит 1. Во-вторых, к хэшируемым данным добавляются нулевые биты пока длинна данных не станет кратна 512. И в-третьих, в последние 64 бита расширенного массива данных записывается длинна хэшируемых данных без изменений в битах. Таким образом при использование чистого SHA у нас происходят множество операций с памятью, рассчеты длинны, операции деления и умножения которые совершенно не нужны. Размер заголовка блока у нас фиксирован и всегда равен 80 байтам. Т.е. после расширения хэшируемый массив данных будет иметь длинну 32 32-битных беззнаковых числа. Позиция единичного бита будет фиксирована и располагаться он будет в 20 элементе расширенного массива. Также длинна хэшируемых данных фиксирована и равна 640 битам. Длинну записываем также в фиксированное место, а именно в 31 элемент.
Во время второго хэширования у нас также есть статические данные. Длинна хэшируемых данных 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;
Для рассчета каждого следующего элемента очереди производится калькуляция промежуточных значений s0 и s1 которые формируются из прошлых элементов очереди сообщений w[i-15] и w[i-2]. Для нас интересен тот факт, что при неизменном timestamp для рассчета 18 и 19 элементов очереди сообщений генерируемой для хэширования второй части заголовка s1 будет всегда постоянным. А для рассчета элементов с 19 по 32 постоянными будут s0. Тогда теряется смысл делать их постоянную калькуляцию. Это можно делать также один раз при изменение timestamp.

Есть ещё один момент влияющий на хэшрейт. После формирования полной очереди сообщений используемой для рассчета хэша из второй части заголовка блока выполняются 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;
При неизменном в длительном периоде timestamp первые три элемента очереди сообщений у нас одинаковы и независимы от nonce, а значит результат вычисления в первых трех раундах будет одинаковым. Но, кроме того, на четвертом раунде nonce играет не значительную роль и его изменением можно пренебречь (считать nonce равным 0) и добавить текущий nonce к переменным a и e уже после основных вычислений в этом раунде.

Таким образом можно вычислить состояние переменных 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, так как эти вычисления при больших мощностях будут проходить достаточно часто и могут тормозить основной процесс майнинга. Более того, подобные методы ускорения майнинга могут найтись и в других криптовалютах.
Хотите чтобы я провел дополнительные исследования и выложил результаты на форуме? Голосуйте за меня и да пусть победит темная сторона силы.
ADMINISTRATOR вне форума   Ответить с цитированием
Непрочитано 24.11.2021, 13:48   #2
ADMINISTRATOR
 
Регистрация: 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;
На последнем 64-ом раунде h принимает свое окончательное значение, затем уже вне цикла складывается с изначальным состоянием h7 и тестируется на соответствие нулю. Но посмотрите что же такое h на последнем раунде на самом деле. Это состояние g на предпоследнем раунде. Т.е. h по факту не изменился, но изменилось его положение в памяти. Последнее фактическое изменение h происходит на 60-ом раунде. Тогда зачем нам нужно ещё 3 раунда хэширования если на 60-ом раунде сравнение с целью не даст нужный результат? Добавим в функцию тест на достижение цели после 60-ого раунда учитывая тот факт, что реальное местоположение h находится в области памяти выделенной под e.

Внесем изменения в исследуемую функцию:

Код:
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;
Делаем замер и получаем прирост к хэшрейту до состояния 147058 х/с. Коэффициент увеличения хэшрейта х1.3676
ADMINISTRATOR вне форума   Ответить с цитированием
Ответ

Опции темы



Текущее время: 12:34. Часовой пояс GMT +2.