gmminer/test/gmminer_pow_cuckoo_tests.erl
2025-03-12 09:51:42 +01:00

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).