193 lines
8.8 KiB
Erlang
193 lines
8.8 KiB
Erlang
%%%=============================================================================
|
|
%%% @copyright (C) 2025, QPQ AG
|
|
%%% @copyright (C) 2019, Aeternity Anstalt
|
|
%%% @doc
|
|
%%% Unit tests for the gmminer_pow_cuckoo module
|
|
%%% @end
|
|
%%%=============================================================================
|
|
-module(gmminer_pow_cuckoo_tests).
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
-include("gmminer.hrl").
|
|
|
|
-define(TEST_MODULE, gmminer_pow_cuckoo).
|
|
-define(POW_MODULE, gmminer_pow).
|
|
|
|
-define(TEST_BIN, <<"wsffgujnjkqhduihsahswgdf">>).
|
|
-define(TEST_HIGH_NONCE, 38). %% Nonce with solution with high target.
|
|
-define(EDGE_BITS_15, 15).
|
|
-define(EDGE_BITS_29, 29).
|
|
|
|
pow_test_() ->
|
|
[{"Generate with a winning nonce and high target threshold, verify it",
|
|
{timeout, 60,
|
|
fun() ->
|
|
Target = ?HIGHEST_TARGET_SCI,
|
|
Nonce = ?TEST_HIGH_NONCE,
|
|
Config = fast_and_deterministic_cuckoo_pow(),
|
|
Res = spawn_worker(fun() -> ?TEST_MODULE:generate(?TEST_BIN, Target, Nonce, Config, undefined) end),
|
|
{ok, {Nonce, Soln}} = Res,
|
|
?assertMatch(L when length(L) == ?SOLUTION_SIZE, Soln),
|
|
|
|
%% verify the nonce and the solution
|
|
?assert(?TEST_MODULE:verify(?TEST_BIN, Nonce, Soln, Target, ?EDGE_BITS_15)),
|
|
?assert(?TEST_MODULE:verify_proof(?TEST_BIN, Nonce, Soln, ?EDGE_BITS_15)),
|
|
|
|
SolnTarget = ?TEST_MODULE:get_target(Soln, ?EDGE_BITS_15),
|
|
?assert(SolnTarget =< ?HIGHEST_TARGET_INT)
|
|
end}
|
|
},
|
|
{"Generate with a winning nonce but low target threshold, shall fail",
|
|
{timeout, 90,
|
|
fun() ->
|
|
Target = 16#01010000,
|
|
TargetInt = ?POW_MODULE:scientific_to_integer(Target),
|
|
Nonce = ?TEST_HIGH_NONCE,
|
|
Config = fast_and_deterministic_cuckoo_pow(),
|
|
Res1 = spawn_worker(fun() ->
|
|
?TEST_MODULE:generate(?TEST_BIN, Target, Nonce, Config, undefined)
|
|
end),
|
|
?assertEqual({error, no_solution}, Res1),
|
|
|
|
%% Any attempts to verify such nonce with a solution
|
|
%% found with high target threshold shall fail.
|
|
%%
|
|
%% Obtain solution with high target threshold ...
|
|
HighTarget = ?HIGHEST_TARGET_SCI,
|
|
Res2 = spawn_worker(fun() ->
|
|
?TEST_MODULE:generate(?TEST_BIN, HighTarget, Nonce, Config, undefined)
|
|
end),
|
|
{ok, {Nonce, Soln2}} = Res2,
|
|
?assertMatch(L when length(L) == ?SOLUTION_SIZE, Soln2),
|
|
%% ... then attempt to verify such solution (and
|
|
%% nonce) with the low target threshold (shall fail).
|
|
?assertNot(?TEST_MODULE:verify(?TEST_BIN, Nonce, Soln2, Target, ?EDGE_BITS_15)),
|
|
%% The solution is still valid though.
|
|
?assert(?TEST_MODULE:verify_proof(?TEST_BIN, Nonce, Soln2, ?EDGE_BITS_15)),
|
|
|
|
%% The target of the solution shall be higher than the low target.
|
|
?assert(?TEST_MODULE:get_target(Soln2, ?EDGE_BITS_15) > TargetInt)
|
|
end}
|
|
},
|
|
{"Attempt to verify wrong solution for nonce that has a solution shall fail",
|
|
fun() ->
|
|
Target = ?HIGHEST_TARGET_SCI,
|
|
Nonce = ?TEST_HIGH_NONCE,
|
|
Config = fast_and_deterministic_cuckoo_pow(),
|
|
Res = spawn_worker(fun() -> ?TEST_MODULE:generate(?TEST_BIN, Target, Nonce, Config, undefined) end),
|
|
{ok, {Nonce, Soln}} = Res,
|
|
?assertMatch(L when length(L) == ?SOLUTION_SIZE, Soln),
|
|
|
|
WrongSoln = lists:seq(0, 41),
|
|
?assertMatch(L when length(L) == ?SOLUTION_SIZE, WrongSoln),
|
|
?assertNotEqual(Soln, WrongSoln),
|
|
?assertNot(?TEST_MODULE:verify(?TEST_BIN, Nonce, WrongSoln, Target, ?EDGE_BITS_15)),
|
|
?assertNot(?TEST_MODULE:verify_proof(?TEST_BIN, Nonce, WrongSoln, ?EDGE_BITS_15))
|
|
end},
|
|
{"Attempt to verify nonce that does not have a solution (providing a dummy solution) shall fail",
|
|
fun() ->
|
|
Target = ?HIGHEST_TARGET_SCI,
|
|
Nonce = 1,
|
|
Config = fast_and_deterministic_cuckoo_pow(),
|
|
?assertMatch({error, no_solution},
|
|
spawn_worker(fun() -> ?TEST_MODULE:generate(?TEST_BIN, Target, Nonce, Config, undefined) end)),
|
|
|
|
DummySoln = lists:seq(0, 41),
|
|
?assertMatch(L when length(L) =:= ?SOLUTION_SIZE, DummySoln),
|
|
?assertNot(?TEST_MODULE:verify(?TEST_BIN, Nonce, DummySoln, Target, ?EDGE_BITS_15)),
|
|
?assertNot(?TEST_MODULE:verify_proof(?TEST_BIN, Nonce, DummySoln, ?EDGE_BITS_15))
|
|
end},
|
|
{"Attempt to verify nonce that is too big shall fail gracefully",
|
|
fun() ->
|
|
% this is a premined working solution for size 27
|
|
Hash = <<83,237,15,231,60,2,35,26,173,64,55,84,59,100,88,146,91,
|
|
124,171,211,193,86,167,83,17,153,168,99,84,72,33,186>>,
|
|
Pow = [2253069,4506519,4850569,8551070,9391218,15176443,22052028,
|
|
24045664,29484700,31332105,38588547,39046239,43427572,
|
|
53979472,58387992,60256309,62282050,67357873,68186886,
|
|
69815968,71809484,73494956,74992447,76953489,82132560,
|
|
84075861,84934950,85804033,87920415,96539757,96818481,
|
|
98049225,98464641,98907580,110711166,115480621,117062778,
|
|
117537386,120015599,125293300,125684682,129332159],
|
|
Nonce = 17654096256755765485,
|
|
Target = 536940240,
|
|
?assertNot(?TEST_MODULE:verify(Hash, Nonce, Pow, Target, ?EDGE_BITS_15)),
|
|
?assertNot(?TEST_MODULE:verify_proof(Hash, Nonce, Pow, ?EDGE_BITS_15))
|
|
end}
|
|
].
|
|
|
|
misc_test_() ->
|
|
[{"Conversion of a solution to binary",
|
|
fun() ->
|
|
Soln = [5936046,6000450,9980569,10770186,11256679,11557293,
|
|
12330374,16556162,25308926,27241299,29693321,31019885,
|
|
38091840,44351975,46970870,55597976,57712943,76763622,
|
|
78513115,78670397,82776188,82841920,84299614,86421603,
|
|
87878232,87913313,92453652,93430969,94032236,94428148,
|
|
97119256,102408900,104747553,108943266,112048126,
|
|
112561693,118817859,118965199,121744219,122178237,
|
|
132944539,133889045],
|
|
NodeSize = ?TEST_MODULE:get_node_size(?EDGE_BITS_15),
|
|
?assertEqual(42*NodeSize, size(?TEST_MODULE:solution_to_binary(
|
|
lists:sort(Soln), NodeSize * 8)))
|
|
end}
|
|
].
|
|
|
|
kill_ospid_miner_test_() ->
|
|
[ {"Run miner in OS and kill it by killing parent",
|
|
fun() ->
|
|
Config = default_cuckoo_pow(),
|
|
Self = self(),
|
|
Pid = spawn(fun() ->
|
|
Self ! {?TEST_MODULE:generate(?TEST_BIN, 12837272, 128253, Config, undefined), self()}
|
|
end),
|
|
timer:sleep(200), %% give some time to start the miner OS pid
|
|
%% We did create a new one.
|
|
?assertNotMatch([], os:cmd("ps -e | grep mean29- | grep -v grep")),
|
|
exit(Pid, shutdown),
|
|
timer:sleep(1000), %% give it some time to kill the miner OS pid
|
|
?assertMatch([], os:cmd("ps -e | grep mean29- | grep -v grep"))
|
|
end}
|
|
].
|
|
|
|
% This code is partially from aec_conductor
|
|
|
|
spawn_worker(Fun) ->
|
|
Wrapper = wrap_worker_fun(Fun),
|
|
{Pid, _Ref} = spawn_monitor(Wrapper),
|
|
receive
|
|
{worker_reply, Pid, Res} ->
|
|
Res
|
|
end.
|
|
|
|
prebuilt_miner_test_() ->
|
|
[{"Err if absent prebuilt miner",
|
|
fun() ->
|
|
Target = ?HIGHEST_TARGET_SCI,
|
|
Nonce = 1,
|
|
Config = prebuilt_cuckoo_pow(),
|
|
Res = spawn_worker(fun() -> ?TEST_MODULE:generate(?TEST_BIN, Target, Nonce, Config, undefined) end),
|
|
?assertMatch({error,{runtime,{port_error,{error,enoent}}}}, Res)
|
|
end}
|
|
].
|
|
|
|
wrap_worker_fun(Fun) ->
|
|
Server = self(),
|
|
fun() ->
|
|
Server ! {worker_reply, self(), Fun()}
|
|
end.
|
|
|
|
fast_and_deterministic_cuckoo_pow() ->
|
|
?TEST_MODULE:config(<<"mean15-generic">>, <<"aecuckoo">>, <<>>, false, 10,
|
|
?EDGE_BITS_15, undefined).
|
|
|
|
prebuilt_cuckoo_pow() ->
|
|
?TEST_MODULE:config(<<"nonexistingminer">>, <<"aecuckooprebuilt">>, <<>>,
|
|
false, 1, ?EDGE_BITS_15, undefined).
|
|
|
|
default_cuckoo_pow() ->
|
|
?TEST_MODULE:config(<<"mean29-generic">>, <<"aecuckoo">>, <<>>, false, 1,
|
|
?EDGE_BITS_29, undefined).
|
|
|