Add missing tests, refactor, improve type specs
This commit is contained in:
parent
c2b9873d10
commit
d6021504ad
8
Makefile
8
Makefile
@ -1,6 +1,6 @@
|
|||||||
REBAR = ./rebar3
|
REBAR = ./rebar3
|
||||||
|
|
||||||
.PHONY: all dialyzer test clean console
|
.PHONY: all dialyzer ct eunit clean distclean console
|
||||||
|
|
||||||
all:
|
all:
|
||||||
$(REBAR) compile
|
$(REBAR) compile
|
||||||
@ -12,7 +12,10 @@ dialyzer:
|
|||||||
$(REBAR) dialyzer
|
$(REBAR) dialyzer
|
||||||
|
|
||||||
ct: all
|
ct: all
|
||||||
$(REBAR) ct test/aecuckoo_SUITE
|
$(REBAR) ct --suite=test/aecuckoo_SUITE
|
||||||
|
|
||||||
|
eunit:
|
||||||
|
$(REBAR) eunit --module=aeminer_pow_tests,aeminer_pow_cuckoo_tests
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
$(REBAR) clean
|
$(REBAR) clean
|
||||||
@ -23,3 +26,4 @@ distclean: clean
|
|||||||
|
|
||||||
console:
|
console:
|
||||||
$(REBAR) shell
|
$(REBAR) shell
|
||||||
|
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
|
|
||||||
-define(HIGHEST_TARGET_SCI, 16#2100ffff).
|
-define(HIGHEST_TARGET_SCI, 16#2100ffff).
|
||||||
|
|
||||||
-define(HIGHEST_TARGET_INT, 16#ffff000000000000000000000000000000000000000000000000000000000000).
|
-define(HIGHEST_TARGET_INT, 16#ffff000000000000000000000000000000000000000000000000000000000000).
|
||||||
-define(NONCE_BITS, 64).
|
|
||||||
-define(MAX_NONCE, 16#ffffffffffffffff).
|
|
||||||
-define(DIFFICULTY_INTEGER_FACTOR, 16#1000000).
|
-define(DIFFICULTY_INTEGER_FACTOR, 16#1000000).
|
||||||
|
|
||||||
|
-define(MAX_NONCE, 16#ffffffffffffffff).
|
||||||
|
|
||||||
|
-define(SOLUTION_SIZE, 42).
|
||||||
|
|
||||||
|
23
rebar.config
23
rebar.config
@ -1,10 +1,20 @@
|
|||||||
{deps, [{aecuckooprebuilt,
|
|
||||||
|
{erl_opts, [{parse_transform, lager_transform}]}.
|
||||||
|
|
||||||
|
{deps, [
|
||||||
|
{lager, {git, "https://github.com/erlang-lager/lager.git",
|
||||||
|
{ref, "69b4ada"}}}, % tag: 3.6.7
|
||||||
|
|
||||||
|
%% Cuckoo prebuilt CUDA binaries.
|
||||||
|
{aecuckooprebuilt,
|
||||||
{aecuckooprebuilt_app_with_priv_from_git, {git, "https://github.com/aeternity/cuckoo-prebuilt.git",
|
{aecuckooprebuilt_app_with_priv_from_git, {git, "https://github.com/aeternity/cuckoo-prebuilt.git",
|
||||||
{ref, "90afb699dc9cc41d033a7c8551179d32b3bd569d"}}}},
|
{ref, "90afb699dc9cc41d033a7c8551179d32b3bd569d"}}}},
|
||||||
|
|
||||||
{aecuckoo,
|
%% Cuckoo CPU miners (not prebuilt).
|
||||||
{git, "https://github.com/aeternity/aecuckoo.git", {branch, "master"}}},
|
{aecuckoo, {git, "https://github.com/aeternity/aecuckoo.git",
|
||||||
|
{ref, "fa3d13e"}}},
|
||||||
|
|
||||||
|
%% This is used just in one place, just to get blake2b_256 hash.
|
||||||
{enacl, {git, "https://github.com/aeternity/enacl.git",
|
{enacl, {git, "https://github.com/aeternity/enacl.git",
|
||||||
{ref, "26180f4"}}}
|
{ref, "26180f4"}}}
|
||||||
]}.
|
]}.
|
||||||
@ -13,3 +23,10 @@
|
|||||||
{ref, "2b2f3b3cf969ee91ba41d8351f3808530a8bf28e"}}}
|
{ref, "2b2f3b3cf969ee91ba41d8351f3808530a8bf28e"}}}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
|
{profiles, [{test, [{deps, [{meck, "0.8.12"}]}]}]}.
|
||||||
|
|
||||||
|
{dialyzer, [{warnings, [unknown]},
|
||||||
|
{plt_apps, all_deps},
|
||||||
|
{base_plt_apps, [erts, kernel, stdlib, crypto]}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
12
rebar.lock
12
rebar.lock
@ -1,3 +1,4 @@
|
|||||||
|
{"1.1.0",
|
||||||
[{<<"aecuckoo">>,
|
[{<<"aecuckoo">>,
|
||||||
{git,"https://github.com/aeternity/aecuckoo.git",
|
{git,"https://github.com/aeternity/aecuckoo.git",
|
||||||
{ref,"fa3d13e8c7003589153223f634c851d389b61b93"}},
|
{ref,"fa3d13e8c7003589153223f634c851d389b61b93"}},
|
||||||
@ -10,4 +11,13 @@
|
|||||||
{<<"enacl">>,
|
{<<"enacl">>,
|
||||||
{git,"https://github.com/aeternity/enacl.git",
|
{git,"https://github.com/aeternity/enacl.git",
|
||||||
{ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}},
|
{ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}},
|
||||||
0}].
|
0},
|
||||||
|
{<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.9">>},1},
|
||||||
|
{<<"lager">>,
|
||||||
|
{git,"https://github.com/erlang-lager/lager.git",
|
||||||
|
{ref,"69b4ada2341b8ab2cf5c8e464ac936e5e4a9f62b"}},
|
||||||
|
0}]}.
|
||||||
|
[
|
||||||
|
{pkg_hash,[
|
||||||
|
{<<"goldrush">>, <<"F06E5D5F1277DA5C413E84D5A2924174182FB108DABB39D5EC548B27424CD106">>}]}
|
||||||
|
].
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
{application, aeminer,
|
{application, aeminer,
|
||||||
[{description, "Aeternity cuckoo miner"},
|
[{description, "Aeternity miner"},
|
||||||
{vsn, "1.0.0"},
|
{vsn, "1.0.0"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications,
|
{applications,
|
||||||
[kernel,
|
[kernel,
|
||||||
stdlib
|
stdlib,
|
||||||
|
lager,
|
||||||
|
enacl,
|
||||||
|
aecuckoo,
|
||||||
|
aecuckooprebuilt
|
||||||
]},
|
]},
|
||||||
{env,[]},
|
{env,[]},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
|
@ -2,6 +2,15 @@
|
|||||||
|
|
||||||
-export([hash/1]).
|
-export([hash/1]).
|
||||||
|
|
||||||
|
-export_type([hashable/0,
|
||||||
|
hash/0
|
||||||
|
]).
|
||||||
|
|
||||||
|
-type hashable() :: binary().
|
||||||
|
|
||||||
|
-type hash() :: binary().
|
||||||
|
|
||||||
|
-spec hash(hashable()) -> hash().
|
||||||
hash(Bin) ->
|
hash(Bin) ->
|
||||||
{ok, Hash} = enacl:generichash(32, Bin),
|
{ok, Hash} = enacl:generichash(32, Bin),
|
||||||
Hash.
|
Hash.
|
||||||
|
@ -14,19 +14,34 @@
|
|||||||
test_target/2,
|
test_target/2,
|
||||||
trim_nonce/2]).
|
trim_nonce/2]).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-export_type([nonce/0,
|
||||||
-compile([export_all, nowarn_export_all]).
|
int_target/0,
|
||||||
-endif.
|
sci_target/0,
|
||||||
|
bin_target/0,
|
||||||
|
difficulty/0,
|
||||||
|
instance/0
|
||||||
|
]).
|
||||||
|
|
||||||
-include_lib("aeminer/include/aeminer.hrl").
|
-include("aeminer.hrl").
|
||||||
|
|
||||||
|
-type nonce() :: 0..?MAX_NONCE.
|
||||||
|
|
||||||
|
-type int_target() :: integer().
|
||||||
|
|
||||||
|
-type sci_target() :: integer().
|
||||||
|
|
||||||
|
-type bin_target() :: <<_:256>>.
|
||||||
|
|
||||||
|
%% Difficulty: max threshold (0x00000000FFFF0000000000000000000000000000000000000000000000000000)
|
||||||
|
%% over the actual one. Always positive.
|
||||||
|
-type difficulty() :: integer().
|
||||||
|
|
||||||
|
-type instance() :: non_neg_integer().
|
||||||
|
|
||||||
|
-type config() :: aeminer_pow_cuckoo:config().
|
||||||
|
|
||||||
%% 10^24, approx. 2^80
|
%% 10^24, approx. 2^80
|
||||||
-define(NONCE_RANGE, 1000000000000000000000000).
|
-define(NONCE_RANGE, 1000000000000000000000000).
|
||||||
-define(POW_MODULE, aeminer_pow_cuckoo).
|
|
||||||
|
|
||||||
%% 0..?MAX_NONCE
|
|
||||||
-type nonce() :: 0..16#ffffffffffffffff.
|
|
||||||
-export_type([nonce/0]).
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Target threshold and difficulty
|
%% Target threshold and difficulty
|
||||||
@ -64,46 +79,12 @@
|
|||||||
%% significand (i.e., the int value is 0.<significand> * 8^<exponent>).
|
%% significand (i.e., the int value is 0.<significand> * 8^<exponent>).
|
||||||
%% https://en.bitcoin.it/wiki/Difficulty#How_is_difficulty_stored_in_blocks.3F)
|
%% https://en.bitcoin.it/wiki/Difficulty#How_is_difficulty_stored_in_blocks.3F)
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-type sci_int() :: integer().
|
|
||||||
|
|
||||||
%% Optional evidence for PoW verification
|
|
||||||
-type pow_evidence() :: 'no_value' | term().
|
|
||||||
-type pow_result() :: {'ok', {Nonce :: nonce(), Solution :: pow_evidence()}} |
|
|
||||||
{error, no_solution | {runtime, term()}}.
|
|
||||||
%% Difficulty: max threshold (0x00000000FFFF0000000000000000000000000000000000000000000000000000)
|
|
||||||
%% over the actual one. Always positive.
|
|
||||||
-type difficulty() :: integer().
|
|
||||||
|
|
||||||
-type miner_config() :: aeminer_pow_cuckoo:miner_config().
|
|
||||||
-type miner_instance() :: non_neg_integer().
|
|
||||||
|
|
||||||
-export_type([sci_int/0,
|
|
||||||
difficulty/0,
|
|
||||||
pow_evidence/0,
|
|
||||||
pow_result/0,
|
|
||||||
miner_instance/0,
|
|
||||||
miner_config/0]).
|
|
||||||
|
|
||||||
%%%=============================================================================
|
|
||||||
%%% Behaviour
|
|
||||||
%%%=============================================================================
|
|
||||||
|
|
||||||
-type hashable() :: binary().
|
|
||||||
|
|
||||||
-callback generate(Data :: hashable(), Target :: aeminer_pow:sci_int(),
|
|
||||||
Nonce :: aeminer_pow:nonce(), MinerConfig :: aeminer_pow:miner_config(),
|
|
||||||
MinerInstance :: aeminer_pow:miner_instance()) ->
|
|
||||||
aeminer_pow:pow_result().
|
|
||||||
|
|
||||||
-callback verify(Data :: hashable(), Nonce :: aeminer_pow:nonce(),
|
|
||||||
Evd :: aeminer_pow:pow_evidence(), Target :: aeminer_pow:sci_int()) ->
|
|
||||||
boolean().
|
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% API
|
%%% API
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
-spec scientific_to_integer(sci_int()) -> integer().
|
-spec scientific_to_integer(sci_target()) -> int_target().
|
||||||
scientific_to_integer(S) ->
|
scientific_to_integer(S) ->
|
||||||
{Exp, Significand} = break_up_scientific(S),
|
{Exp, Significand} = break_up_scientific(S),
|
||||||
E3 = Exp - 3,
|
E3 = Exp - 3,
|
||||||
@ -114,7 +95,7 @@ scientific_to_integer(S) ->
|
|||||||
Significand bsr (-8 * E3)
|
Significand bsr (-8 * E3)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec integer_to_scientific(integer()) -> sci_int().
|
-spec integer_to_scientific(int_target()) -> sci_target().
|
||||||
integer_to_scientific(I) ->
|
integer_to_scientific(I) ->
|
||||||
%% Find exponent and significand
|
%% Find exponent and significand
|
||||||
{Exp, Significand} = integer_to_scientific(I, 3),
|
{Exp, Significand} = integer_to_scientific(I, 3),
|
||||||
@ -129,22 +110,22 @@ integer_to_scientific(I) ->
|
|||||||
|
|
||||||
%% We want difficulty to be an integer, to still have enough precision using
|
%% We want difficulty to be an integer, to still have enough precision using
|
||||||
%% integer division we multiply by K (1 bsl 24).
|
%% integer division we multiply by K (1 bsl 24).
|
||||||
-spec target_to_difficulty(sci_int()) -> integer().
|
-spec target_to_difficulty(sci_target()) -> difficulty().
|
||||||
target_to_difficulty(SciTgt) ->
|
target_to_difficulty(SciTgt) ->
|
||||||
(?DIFFICULTY_INTEGER_FACTOR * ?HIGHEST_TARGET_INT)
|
(?DIFFICULTY_INTEGER_FACTOR * ?HIGHEST_TARGET_INT)
|
||||||
div scientific_to_integer(SciTgt).
|
div scientific_to_integer(SciTgt).
|
||||||
|
|
||||||
-spec next_nonce(aeminer_pow:nonce(), aeminer_pow:miner_config()) -> aeminer_pow:nonce().
|
-spec next_nonce(nonce(), config()) -> nonce().
|
||||||
next_nonce(Nonce, Cfg) ->
|
next_nonce(Nonce, Cfg) ->
|
||||||
Nonce + aeminer_pow_cuckoo:get_repeats(Cfg).
|
Nonce + aeminer_pow_cuckoo:repeats(Cfg).
|
||||||
|
|
||||||
-spec pick_nonce() -> aeminer_pow:nonce().
|
-spec pick_nonce() -> nonce().
|
||||||
pick_nonce() ->
|
pick_nonce() ->
|
||||||
rand:uniform(?NONCE_RANGE) band ?MAX_NONCE.
|
rand:uniform(?NONCE_RANGE) band ?MAX_NONCE.
|
||||||
|
|
||||||
-spec trim_nonce(aeminer_pow:nonce(), aeminer_pow:miner_config()) -> aeminer_pow:nonce().
|
-spec trim_nonce(nonce(), config()) -> nonce().
|
||||||
trim_nonce(Nonce, Cfg) ->
|
trim_nonce(Nonce, Cfg) ->
|
||||||
case Nonce + aeminer_pow_cuckoo:get_repeats(Cfg) < ?MAX_NONCE of
|
case Nonce + aeminer_pow_cuckoo:repeats(Cfg) < ?MAX_NONCE of
|
||||||
true -> Nonce;
|
true -> Nonce;
|
||||||
false -> 0
|
false -> 0
|
||||||
end.
|
end.
|
||||||
@ -152,12 +133,18 @@ trim_nonce(Nonce, Cfg) ->
|
|||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Test if binary is under the target threshold
|
%% Test if binary is under the target threshold
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec test_target(binary(), sci_int()) -> boolean().
|
-spec test_target(bin_target(), sci_target()) -> boolean().
|
||||||
test_target(Bin, Target) ->
|
test_target(Bin, Target) ->
|
||||||
Threshold = scientific_to_integer(Target),
|
Threshold = scientific_to_integer(Target),
|
||||||
<<Val:32/big-unsigned-integer-unit:8>> = Bin,
|
<<Val:32/big-unsigned-integer-unit:8>> = Bin,
|
||||||
Val < Threshold.
|
Val < Threshold.
|
||||||
|
|
||||||
|
%% TODO: get target
|
||||||
|
%Bin = solution_to_binary(lists:sort(Soln), NodeSize * 8, <<>>),
|
||||||
|
%%Hash = aec_hash:hash(pow, Bin),
|
||||||
|
%%<<Val:32/big-unsigned-integer-unit:8>> = Hash,
|
||||||
|
%%Val
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
@ -170,7 +157,7 @@ integer_to_scientific(I, Exp) ->
|
|||||||
%% Add the number of bytes in the significand
|
%% Add the number of bytes in the significand
|
||||||
{Exp, I}.
|
{Exp, I}.
|
||||||
|
|
||||||
%% Return the exponent and significand of a sci_int().
|
%% Return the exponent and significand of a sci_target().
|
||||||
break_up_scientific(S) ->
|
break_up_scientific(S) ->
|
||||||
SigMask = (1 bsl 24) - 1,
|
SigMask = (1 bsl 24) - 1,
|
||||||
Exp = ((S bxor SigMask) bsr 24),
|
Exp = ((S bxor SigMask) bsr 24),
|
||||||
@ -183,8 +170,3 @@ break_up_scientific(S) ->
|
|||||||
{-Exp, Significand - 16#800000}
|
{-Exp, Significand - 16#800000}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% ------------ GET TARGET ------------
|
|
||||||
%%Bin = solution_to_binary(lists:sort(Soln), NodeSize * 8, <<>>),
|
|
||||||
%%Hash = aec_hash:hash(pow, Bin),
|
|
||||||
%%<<Val:32/big-unsigned-integer-unit:8>> = Hash,
|
|
||||||
%%Val
|
|
||||||
|
@ -15,82 +15,138 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeminer_pow_cuckoo).
|
-module(aeminer_pow_cuckoo).
|
||||||
|
|
||||||
-behaviour(aeminer_pow).
|
-export([config/7,
|
||||||
|
addressed_instances/1,
|
||||||
|
repeats/1,
|
||||||
|
exec/1,
|
||||||
|
extra_args/1,
|
||||||
|
hex_enc_header/1,
|
||||||
|
get_node_size/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
-export([generate/5,
|
||||||
|
verify/5
|
||||||
|
]).
|
||||||
|
|
||||||
-export([check_env/0,
|
-export_type([hashable/0,
|
||||||
generate/5,
|
exec/0,
|
||||||
get_addressed_instances/1,
|
exec_group/0,
|
||||||
get_miner_configs/0,
|
extra_args/0,
|
||||||
get_repeats/1,
|
hex_enc_header/0,
|
||||||
verify/4]).
|
repeats/0,
|
||||||
|
edge_bits/0,
|
||||||
|
solution/0,
|
||||||
|
config/0
|
||||||
|
]).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-compile([export_all, nowarn_export_all]).
|
-export([verify_proof_/3,
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
solution_to_binary/2
|
||||||
|
]).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-include("aeminer.hrl").
|
-include("aeminer.hrl").
|
||||||
|
|
||||||
-define(debug(F, A), epoch_pow_cuckoo:debug(F, A)).
|
-type hashable() :: aeminer_blake2b_256:hashable().
|
||||||
-define(info(F, A), epoch_pow_cuckoo:info(F, A)).
|
|
||||||
-define(warning(F, A), epoch_pow_cuckoo:warning(F, A)).
|
|
||||||
-define(error(F, A), epoch_pow_cuckoo:error(F, A)).
|
|
||||||
|
|
||||||
-type os_pid() :: integer() | undefined.
|
-type nonce() :: aeminer_pow:nonce().
|
||||||
-type pow_cuckoo_solution() :: [integer()].
|
|
||||||
|
|
||||||
-record(state, {os_pid :: os_pid(),
|
-type sci_target() :: aeminer_pow:sci_target().
|
||||||
port :: port() | undefined,
|
|
||||||
buffer = [] :: string(),
|
|
||||||
target :: aeminer_pow:sci_int() | undefined,
|
|
||||||
parser :: output_parser_fun()}).
|
|
||||||
|
|
||||||
-type output_parser_fun() :: fun((list(string()), #state{}) ->
|
-type instance() :: aeminer_pow:instance()
|
||||||
{'ok', term(), term()} | {'error', term()}).
|
| undefined.
|
||||||
|
|
||||||
-define(DEFAULT_EXECUTABLE_GROUP , <<"aemineruckoo">>).
|
-type exec() :: string().
|
||||||
-define(DEFAULT_EXTRA_ARGS , <<>>).
|
|
||||||
-define(DEFAULT_HEX_ENCODED_HEADER , false).
|
|
||||||
-define(DEFAULT_REPEATS , 1).
|
|
||||||
-define(DEFAULT_EDGE_BITS , 29).
|
|
||||||
-define(DEFAULT_CUCKOO_ENV,
|
|
||||||
{?DEFAULT_EDGE_BITS,
|
|
||||||
[{<<"mean29-generic">>, ?DEFAULT_EXTRA_ARGS, ?DEFAULT_HEX_ENCODED_HEADER,
|
|
||||||
?DEFAULT_REPEATS, undefined, ?DEFAULT_EXECUTABLE_GROUP}]}).
|
|
||||||
|
|
||||||
-record(miner_config,
|
-type exec_group() :: binary().
|
||||||
{executable :: list(),
|
|
||||||
executable_group :: binary(),
|
-type extra_args() :: string().
|
||||||
extra_args :: list(),
|
|
||||||
hex_encoded_header :: boolean(),
|
-type hex_enc_header() :: boolean().
|
||||||
repeats :: non_neg_integer(),
|
|
||||||
instances :: list(aeminer_pow:miner_instance()) | 'undefined'}).
|
-type repeats() :: non_neg_integer().
|
||||||
-type miner_config() :: #miner_config{}.
|
|
||||||
-export_type([miner_config/0]).
|
-type edge_bits() :: pos_integer().
|
||||||
|
|
||||||
|
-type instances() :: [aeminer_pow:instance()]
|
||||||
|
| undefined.
|
||||||
|
|
||||||
|
-type solution() :: [integer()].
|
||||||
|
|
||||||
|
-type output_parser_fun() :: fun(([string()], state()) ->
|
||||||
|
{ok, term(), term()} | {error, term()}).
|
||||||
|
|
||||||
|
-record(config, {
|
||||||
|
exec :: exec(),
|
||||||
|
exec_group :: exec_group(),
|
||||||
|
extra_args :: extra_args(),
|
||||||
|
hex_enc_header :: hex_enc_header(),
|
||||||
|
repeats :: repeats(),
|
||||||
|
edge_bits :: edge_bits(),
|
||||||
|
instances :: instances()
|
||||||
|
}).
|
||||||
|
|
||||||
|
-opaque config() :: #config{}.
|
||||||
|
|
||||||
|
-record(state, {
|
||||||
|
os_pid :: integer() | undefined,
|
||||||
|
port :: port() | undefined,
|
||||||
|
buffer = [] :: string(),
|
||||||
|
target :: sci_target() | undefined,
|
||||||
|
edge_bits :: edge_bits(),
|
||||||
|
parser :: output_parser_fun()
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type state() :: #state{}.
|
||||||
|
|
||||||
|
-define(IS_CONFIG(Exec, ExecGroup, ExtraArgs, HexEncHdr, Repeats, EdgeBits, Instances),
|
||||||
|
is_binary(Exec) and is_binary(ExecGroup) and
|
||||||
|
is_binary(ExtraArgs) and is_boolean(HexEncHdr) and
|
||||||
|
(is_integer(Repeats) and (Repeats > 0)) and
|
||||||
|
(is_integer(EdgeBits) and (EdgeBits > 0)) and
|
||||||
|
(is_list(Instances) or (Instances =:= undefined))).
|
||||||
|
|
||||||
|
-define(LOG_MODULE, application:get_env(aeminer, log_module)).
|
||||||
|
|
||||||
|
-define(debug(F, A), lager:debug(F, A)).
|
||||||
|
-define(info(F, A), lager:info(F, A)).
|
||||||
|
-define(warning(F, A), lager:warning(F, A)).
|
||||||
|
-define(error(F, A), lager:error(F, A)).
|
||||||
|
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
%%% API
|
%%% API
|
||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
config(Exec, ExecGroup, ExtraArgs, HexEncHdr, Repeats, EdgeBits, Instances) when
|
||||||
%% Assert that configuration options 'mining > cuckoo > miners' and
|
?IS_CONFIG(Exec, ExecGroup, ExtraArgs, HexEncHdr, Repeats, EdgeBits, Instances) ->
|
||||||
%% 'mining > cuckoo > edge_bits' are not used together with deprecated
|
#config{
|
||||||
%% configuration property 'mining > cuckoo > miner'.
|
exec = binary_to_list(Exec),
|
||||||
%%------------------------------------------------------------------------------
|
exec_group = ExecGroup,
|
||||||
check_env() ->
|
extra_args = binary_to_list(ExtraArgs),
|
||||||
case {aeu_env:user_map([<<"mining">>, <<"cuckoo">>, <<"miners">>]),
|
hex_enc_header = HexEncHdr,
|
||||||
aeu_env:user_config([<<"mining">>, <<"cuckoo">>, <<"edge_bits">>])} of
|
repeats = Repeats,
|
||||||
{undefined, undefined} -> ok;
|
edge_bits = EdgeBits,
|
||||||
{_, _} ->
|
instances = Instances}.
|
||||||
case aeu_env:user_config([<<"mining">>, <<"cuckoo">>, <<"miner">>]) of
|
|
||||||
undefined -> ok;
|
-spec addressed_instances(config()) -> instances().
|
||||||
_ ->
|
addressed_instances(#config{instances = Instances}) ->
|
||||||
lager:error("Config error: deprecated property 'mining > cuckoo > miner' cannot be used "
|
Instances.
|
||||||
"together with 'mining > cuckoo > miners' or 'mining > cuckoo > edge_bits'"),
|
|
||||||
exit(cuckoo_config_validation_failed)
|
-spec repeats(config()) -> repeats().
|
||||||
end
|
repeats(#config{repeats = Repeats}) ->
|
||||||
end.
|
Repeats.
|
||||||
|
|
||||||
|
-spec exec(config()) -> exec().
|
||||||
|
exec(#config{exec = Exec}) ->
|
||||||
|
Exec.
|
||||||
|
|
||||||
|
-spec extra_args(config()) -> extra_args().
|
||||||
|
extra_args(#config{extra_args = ExtraArgs}) ->
|
||||||
|
ExtraArgs.
|
||||||
|
|
||||||
|
-spec hex_enc_header(config()) -> hex_enc_header().
|
||||||
|
hex_enc_header(#config{hex_enc_header = HexEncHdr}) ->
|
||||||
|
HexEncHdr.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Proof of Work generation with default settings
|
%% Proof of Work generation with default settings
|
||||||
@ -109,204 +165,61 @@ check_env() ->
|
|||||||
%%
|
%%
|
||||||
%% Very slow below 3 threads, not improving significantly above 5, let us take 5.
|
%% Very slow below 3 threads, not improving significantly above 5, let us take 5.
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
|
-spec generate(hashable(), sci_target(), nonce(), config(), instance()) ->
|
||||||
-type hashable() :: binary().
|
{ok, {nonce(), solution()}} | {error, no_solution} | {error, {runtime, term()}}.
|
||||||
|
generate(Data, Target, Nonce, Config, Instance) when
|
||||||
-spec generate(Data :: hashable(), Target :: aeminer_pow:sci_int(),
|
Nonce >= 0, Nonce =< ?MAX_NONCE ->
|
||||||
Nonce :: aeminer_pow:nonce(), MinerConfig :: aeminer_pow:miner_config(),
|
|
||||||
MinerInstance :: aeminer_pow:miner_instance() | 'undefined') -> aeminer_pow:pow_result().
|
|
||||||
generate(Data, Target, Nonce, MinerConfig, MinerInstance) when Nonce >= 0,
|
|
||||||
Nonce =< ?MAX_NONCE ->
|
|
||||||
%% Hash Data and convert the resulting binary to a base64 string for Cuckoo
|
%% Hash Data and convert the resulting binary to a base64 string for Cuckoo
|
||||||
%% Since this hash is purely internal, we don't use api encoding
|
%% Since this hash is purely internal, we don't use api encoding
|
||||||
Hash = aeminer_blake2b_256:hash(Data),
|
Hash = aeminer_blake2b_256:hash(Data),
|
||||||
Hash64 = base64:encode_to_string(Hash),
|
Hash64 = base64:encode_to_string(Hash),
|
||||||
?debug("Generating solution for data hash ~p and nonce ~p with target ~p.",
|
?debug("Generating solution for data hash ~p and nonce ~p with target ~p.",
|
||||||
[Hash, Nonce, Target]),
|
[Hash, Nonce, Target]),
|
||||||
case generate_int(Hash64, Nonce, Target, MinerConfig, MinerInstance) of
|
case generate_int(Hash64, Nonce, Target, Config, Instance) of
|
||||||
{ok, Nonce1, Soln} ->
|
{ok, Nonce1, Soln} ->
|
||||||
{ok, {Nonce1, Soln}};
|
{ok, {Nonce1, Soln}};
|
||||||
{error, no_value} ->
|
{error, no_value} ->
|
||||||
?debug("No cuckoo solution found", []),
|
?debug("No cuckoo solution found", []),
|
||||||
{error, no_solution};
|
{error, no_solution};
|
||||||
{error, Reason} ->
|
{error, Rsn} ->
|
||||||
%% Executable failed (segfault, not found, etc.): let miner decide
|
%% Exec failed (segfault, not found, etc.): let miner decide
|
||||||
{error, {runtime, Reason}}
|
{error, {runtime, Rsn}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% Proof of Work verification (with difficulty check)
|
%% Proof of Work verification (with difficulty check)
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec verify(Data :: hashable(), Nonce :: aeminer_pow:nonce(),
|
-spec verify(hashable(), nonce(), solution(), sci_target(), edge_bits()) ->
|
||||||
Evd :: aeminer_pow:pow_evidence(), Target :: aeminer_pow:sci_int()) ->
|
boolean().
|
||||||
boolean().
|
verify(Data, Nonce, Soln, Target, EdgeBits) when
|
||||||
verify(Data, Nonce, Evd, Target) when is_list(Evd),
|
is_list(Soln), Nonce >= 0, Nonce =< ?MAX_NONCE ->
|
||||||
Nonce >= 0, Nonce =< ?MAX_NONCE ->
|
|
||||||
Hash = aeminer_blake2b_256:hash(Data),
|
Hash = aeminer_blake2b_256:hash(Data),
|
||||||
case test_target(Evd, Target) of
|
case test_target(Soln, Target, EdgeBits) of
|
||||||
true ->
|
true ->
|
||||||
verify_proof(Hash, Nonce, Evd);
|
verify_proof(Hash, Nonce, Soln, EdgeBits);
|
||||||
false ->
|
false ->
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
%% Internal functions.
|
||||||
%% Read and parse miner configs.
|
|
||||||
%%
|
|
||||||
%% Miners defined in epoch.{json,yaml} user config file take precedence.
|
|
||||||
%% If there are no miners defined in the user config, sys.config cuckoo
|
|
||||||
%% miners are read. If there are neither user config nor sys.config miners
|
|
||||||
%% ?DEFAULT_CUCKOO_ENV is used as the last resort option (i.e. mean29-generic
|
|
||||||
%% without any extra args).
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec get_miner_configs() -> list(miner_config()).
|
|
||||||
get_miner_configs() ->
|
|
||||||
case get_miners_from_user_config() of
|
|
||||||
{ok, MinerConfigs} -> MinerConfigs;
|
|
||||||
undefined ->
|
|
||||||
case get_miners_from_deprecated_user_config() of
|
|
||||||
{ok, MinerConfigs} -> MinerConfigs;
|
|
||||||
undefined -> get_miners_from_sys_config()
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec get_addressed_instances(miner_config()) -> list(non_neg_integer()) | undefined.
|
generate_int(Hash, Nonce, Target,
|
||||||
get_addressed_instances(#miner_config{instances = Instances}) ->
|
#config{exec = Exec, extra_args = ExtraArgs0,
|
||||||
Instances.
|
hex_enc_header = HexEncHdr} = Config, Instance) ->
|
||||||
|
ExtraArgs = case is_miner_instance_addressation_enabled(Config) of
|
||||||
|
true -> ExtraArgs0 ++ " -d " ++ integer_to_list(Instance);
|
||||||
|
false -> ExtraArgs0
|
||||||
|
end,
|
||||||
|
EncodedHash = case HexEncHdr of
|
||||||
|
true -> hex_string(Hash);
|
||||||
|
false -> Hash
|
||||||
|
end,
|
||||||
|
ExecBinDir = exec_bin_dir(Config),
|
||||||
|
generate_int(EncodedHash, Nonce, Target, ExecBinDir, Exec, ExtraArgs, Config).
|
||||||
|
|
||||||
-spec get_repeats(miner_config()) -> non_neg_integer().
|
generate_int(Hash, Nonce, Target, MinerBinDir, MinerBin, MinerExtraArgs,
|
||||||
get_repeats(#miner_config{repeats = Repeats}) ->
|
#config{repeats = Repeats0, edge_bits = EdgeBits}) ->
|
||||||
Repeats.
|
Repeats = integer_to_list(Repeats0),
|
||||||
|
|
||||||
%%%=============================================================================
|
|
||||||
%%% Internal functions
|
|
||||||
%%%=============================================================================
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% Config handling
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
get_options() ->
|
|
||||||
{_, _} = aeu_env:get_env(aeminerore, aeminer_pow_cuckoo, ?DEFAULT_CUCKOO_ENV).
|
|
||||||
|
|
||||||
get_miners_from_user_config() ->
|
|
||||||
case aeu_env:user_map([<<"mining">>, <<"cuckoo">>, <<"miners">>]) of
|
|
||||||
{ok, MinerConfigMaps} ->
|
|
||||||
MinerConfigs =
|
|
||||||
lists:foldl(
|
|
||||||
fun(ConfigMap, Configs) ->
|
|
||||||
[build_miner_config(ConfigMap) | Configs]
|
|
||||||
end, [], MinerConfigMaps),
|
|
||||||
{ok, MinerConfigs};
|
|
||||||
undefined -> undefined
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_miners_from_deprecated_user_config() ->
|
|
||||||
case aeu_env:user_map([<<"mining">>, <<"cuckoo">>, <<"miner">>]) of
|
|
||||||
{ok, MinerConfigMap} ->
|
|
||||||
%% In the deprecated config 'mining > cuckoo > miner'
|
|
||||||
%% 'instances' is the property indicating the number of instances to be addressed.
|
|
||||||
%% Addressed instances list has to be generated accordingly (indexed from 0).
|
|
||||||
case maps:get(<<"instances">>, MinerConfigMap, undefined) of
|
|
||||||
undefined ->
|
|
||||||
MinerConfigs = [build_miner_config(MinerConfigMap)],
|
|
||||||
{ok, MinerConfigs};
|
|
||||||
InstancesCount ->
|
|
||||||
AddressedInstances = lists:seq(0, InstancesCount - 1),
|
|
||||||
MinerConfigMap1 = MinerConfigMap#{<<"addressed_instances">> => AddressedInstances},
|
|
||||||
MinerConfigs = [build_miner_config(MinerConfigMap1)],
|
|
||||||
{ok, MinerConfigs}
|
|
||||||
end;
|
|
||||||
undefined -> undefined
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_miners_from_sys_config() ->
|
|
||||||
{_, MinerConfigLists} = get_options(),
|
|
||||||
lists:foldl(
|
|
||||||
fun({_, _, _, _, _, _} = Config, Configs) ->
|
|
||||||
[build_miner_config(Config) | Configs]
|
|
||||||
end, [], MinerConfigLists).
|
|
||||||
|
|
||||||
build_miner_config(Config) when is_map(Config) ->
|
|
||||||
Executable = maps:get(<<"executable">> , Config),
|
|
||||||
ExecutableGroup = maps:get(<<"executable_group">> , Config, ?DEFAULT_EXECUTABLE_GROUP),
|
|
||||||
ExtraArgs = maps:get(<<"extra_args">> , Config, ?DEFAULT_EXTRA_ARGS),
|
|
||||||
HexEncodedHdr = maps:get(<<"hex_encoded_header">> , Config, ?DEFAULT_HEX_ENCODED_HEADER),
|
|
||||||
Repeats = maps:get(<<"repeats">> , Config, ?DEFAULT_REPEATS),
|
|
||||||
Instances = maps:get(<<"addressed_instances">>, Config, undefined),
|
|
||||||
#miner_config{
|
|
||||||
executable = binary_to_list(Executable),
|
|
||||||
executable_group = ExecutableGroup,
|
|
||||||
extra_args = binary_to_list(ExtraArgs),
|
|
||||||
hex_encoded_header = HexEncodedHdr,
|
|
||||||
repeats = Repeats,
|
|
||||||
instances = Instances};
|
|
||||||
build_miner_config({Executable, ExtraArgs, HexEncodedHeader, Repeats, Instances, ExecutableGroup}) ->
|
|
||||||
#miner_config{
|
|
||||||
executable = binary_to_list(Executable),
|
|
||||||
executable_group = ExecutableGroup,
|
|
||||||
extra_args = binary_to_list(ExtraArgs),
|
|
||||||
hex_encoded_header = HexEncodedHeader,
|
|
||||||
repeats = Repeats,
|
|
||||||
instances = Instances}.
|
|
||||||
|
|
||||||
get_edge_bits() ->
|
|
||||||
case aeu_env:user_config([<<"mining">>, <<"cuckoo">>, <<"edge_bits">>]) of
|
|
||||||
{ok, EdgeBits} -> EdgeBits;
|
|
||||||
undefined ->
|
|
||||||
%% Deprecated property 'mining' > 'cuckoo' > 'miner' > 'edge_bits'
|
|
||||||
case aeu_env:user_config([<<"mining">>, <<"cuckoo">>, <<"miner">>, <<"edge_bits">>]) of
|
|
||||||
{ok, EdgeBits} -> EdgeBits;
|
|
||||||
undefined ->
|
|
||||||
{EdgeBits, _} = get_options(),
|
|
||||||
EdgeBits
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_executable(#miner_config{executable = Executable}) ->
|
|
||||||
Executable.
|
|
||||||
|
|
||||||
get_extra_args(#miner_config{extra_args = ExtraArgs}) ->
|
|
||||||
ExtraArgs.
|
|
||||||
|
|
||||||
is_hex_encoded_header(#miner_config{hex_encoded_header = HexEncodedHeader}) ->
|
|
||||||
HexEncodedHeader.
|
|
||||||
|
|
||||||
is_miner_instance_addressation_enabled(#miner_config{instances = Instances}) ->
|
|
||||||
case Instances of
|
|
||||||
undefined -> false;
|
|
||||||
I when is_list(I) -> true
|
|
||||||
end.
|
|
||||||
|
|
||||||
miner_bin_dir(#miner_config{executable_group = ExecutableGroup}) ->
|
|
||||||
case ExecutableGroup of
|
|
||||||
<<"aemineruckoo">> -> aemineruckoo:bin_dir();
|
|
||||||
<<"aemineruckooprebuilt">> -> code:priv_dir(aemineruckooprebuilt)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
%% Proof of Work generation: use the hash provided
|
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec generate_int(Hash :: string(), Nonce :: aeminer_pow:nonce(),
|
|
||||||
Target :: aeminer_pow:sci_int(), aeminer_pow:miner_config(), Instance :: non_neg_integer()) ->
|
|
||||||
{'ok', Nonce2 :: aeminer_pow:nonce(), Solution :: pow_cuckoo_solution()} |
|
|
||||||
{'error', term()}.
|
|
||||||
generate_int(Hash, Nonce, Target, #miner_config{} = Config, Instance) ->
|
|
||||||
MinerBin = get_executable(Config),
|
|
||||||
MinerExtraArgs0 = get_extra_args(Config),
|
|
||||||
MinerExtraArgs = case is_miner_instance_addressation_enabled(Config) of
|
|
||||||
true -> MinerExtraArgs0 ++ " -d " ++ integer_to_list(Instance);
|
|
||||||
false -> MinerExtraArgs0
|
|
||||||
end,
|
|
||||||
EncodedHash = case is_hex_encoded_header(Config) of
|
|
||||||
true -> hex_string(Hash);
|
|
||||||
false -> Hash
|
|
||||||
end,
|
|
||||||
MinerBinDir = miner_bin_dir(Config),
|
|
||||||
generate_int(EncodedHash, Nonce, Target, MinerBinDir, MinerBin, MinerExtraArgs, Config).
|
|
||||||
|
|
||||||
generate_int(Hash, Nonce, Target, MinerBinDir, MinerBin, MinerExtraArgs, Config) ->
|
|
||||||
Repeats = integer_to_list(get_repeats(Config)),
|
|
||||||
Args = ["-h", Hash, "-n", integer_to_list(Nonce), "-r", Repeats | string:tokens(MinerExtraArgs, " ")],
|
Args = ["-h", Hash, "-n", integer_to_list(Nonce), "-r", Repeats | string:tokens(MinerExtraArgs, " ")],
|
||||||
?info("Executing cmd '~s ~s'", [MinerBin, lists:concat(lists:join(" ", Args))]),
|
?info("Executing cmd '~s ~s'", [MinerBin, lists:concat(lists:join(" ", Args))]),
|
||||||
Old = process_flag(trap_exit, true),
|
Old = process_flag(trap_exit, true),
|
||||||
@ -315,10 +228,11 @@ generate_int(Hash, Nonce, Target, MinerBinDir, MinerBin, MinerExtraArgs, Config)
|
|||||||
wait_for_result(#state{os_pid = OsPid,
|
wait_for_result(#state{os_pid = OsPid,
|
||||||
port = Port,
|
port = Port,
|
||||||
buffer = [],
|
buffer = [],
|
||||||
parser = fun parse_generation_result/2,
|
target = Target,
|
||||||
target = Target});
|
edge_bits = EdgeBits,
|
||||||
{error, _} = E ->
|
parser = fun parse_generation_result/2});
|
||||||
E
|
{error, _Rsn} = Err ->
|
||||||
|
Err
|
||||||
catch
|
catch
|
||||||
C:E ->
|
C:E ->
|
||||||
{error, {unknown, {C, E}}}
|
{error, {unknown, {C, E}}}
|
||||||
@ -330,29 +244,35 @@ generate_int(Hash, Nonce, Target, MinerBinDir, MinerBin, MinerExtraArgs, Config)
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec hex_string(string()) -> string().
|
|
||||||
hex_string(S) ->
|
hex_string(S) ->
|
||||||
Bin = list_to_binary(S),
|
Bin = list_to_binary(S),
|
||||||
lists:flatten([io_lib:format("~2.16.0B", [B]) || <<B:8>> <= Bin]).
|
lists:flatten([io_lib:format("~2.16.0B", [B]) || <<B:8>> <= Bin]).
|
||||||
|
|
||||||
-define(POW_OK, ok).
|
is_miner_instance_addressation_enabled(#config{instances = Instances}) ->
|
||||||
|
case Instances of
|
||||||
|
I when is_list(I) -> true;
|
||||||
|
undefined -> false
|
||||||
|
end.
|
||||||
|
|
||||||
|
exec_bin_dir(#config{exec_group = ExecGroup}) ->
|
||||||
|
case ExecGroup of
|
||||||
|
<<"aecuckoo">> -> aecuckoo:bin_dir();
|
||||||
|
<<"aecuckooprebuilt">> -> code:priv_dir(aecuckooprebuilt)
|
||||||
|
end.
|
||||||
|
|
||||||
-define(POW_TOO_BIG(Nonce), {error, {nonce_too_big, Nonce}}).
|
-define(POW_TOO_BIG(Nonce), {error, {nonce_too_big, Nonce}}).
|
||||||
-define(POW_TOO_SMALL(Nonce, PrevNonce), {error, {nonces_not_ascending, Nonce, PrevNonce}}).
|
-define(POW_TOO_SMALL(Nonce, PrevNonce), {error, {nonces_not_ascending, Nonce, PrevNonce}}).
|
||||||
-define(POW_NON_MATCHING, {error, endpoints_do_not_match_up}).
|
-define(POW_NON_MATCHING, {error, endpoints_do_not_match_up}).
|
||||||
-define(POW_BRANCH, {error, branch_in_cycle}).
|
-define(POW_BRANCH, {error, branch_in_cycle}).
|
||||||
-define(POW_DEAD_END, {error, cycle_dead_ends}).
|
-define(POW_DEAD_END, {error, cycle_dead_ends}).
|
||||||
-define(POW_SHORT_CYCLE, {error, cycle_too_short}).
|
-define(POW_SHORT_CYCLE, {error, cycle_too_short}).
|
||||||
-define(PROOFSIZE, 42).
|
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
%% @doc
|
%% @doc
|
||||||
%% Proof of Work verification (difficulty check should be done before calling
|
%% Proof of Work verification (difficulty check should be done before calling
|
||||||
%% this function)
|
%% this function)
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec verify_proof(Hash :: binary(), Nonce :: aeminer_pow:nonce(),
|
|
||||||
Solution :: aeminer_pow:pow_evidence()) -> boolean().
|
|
||||||
verify_proof(Hash, Nonce, Solution) ->
|
|
||||||
verify_proof(Hash, Nonce, Solution, get_edge_bits()).
|
|
||||||
|
|
||||||
verify_proof(Hash, Nonce, Solution, EdgeBits) ->
|
verify_proof(Hash, Nonce, Solution, EdgeBits) ->
|
||||||
%% Cuckoo has an 80 byte header, we have to use that as well
|
%% Cuckoo has an 80 byte header, we have to use that as well
|
||||||
@ -392,8 +312,8 @@ verify_proof_(Header, Solution, EdgeBits) ->
|
|||||||
throw(?POW_NON_MATCHING)
|
throw(?POW_NON_MATCHING)
|
||||||
end
|
end
|
||||||
catch
|
catch
|
||||||
throw:{error, Reason} ->
|
throw:{error, Rsn} ->
|
||||||
?info("Proof verification failed for ~p: ~p", [Solution, Reason]),
|
?info("Proof verification failed for ~p: ~p", [Solution, Rsn]),
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@ -409,8 +329,8 @@ check_cycle(Nodes0) ->
|
|||||||
UOdds = lists:usort(Odds),
|
UOdds = lists:usort(Odds),
|
||||||
%% Check that all nodes appear exactly twice (i.e. each node has
|
%% Check that all nodes appear exactly twice (i.e. each node has
|
||||||
%% exactly two edges).
|
%% exactly two edges).
|
||||||
case length(UEvens) == (?PROOFSIZE div 2) andalso
|
case length(UEvens) == (?SOLUTION_SIZE div 2) andalso
|
||||||
length(UOdds) == (?PROOFSIZE div 2) andalso
|
length(UOdds) == (?SOLUTION_SIZE div 2) andalso
|
||||||
UOdds == Odds -- UOdds andalso UEvens == Evens -- UEvens of
|
UOdds == Odds -- UOdds andalso UEvens == Evens -- UEvens of
|
||||||
false ->
|
false ->
|
||||||
{error, ?POW_BRANCH};
|
{error, ?POW_BRANCH};
|
||||||
@ -484,11 +404,7 @@ pack_header_and_nonce(Hash, Nonce) when byte_size(Hash) == 32 ->
|
|||||||
%% for the last line fragment w/o NL.
|
%% for the last line fragment w/o NL.
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec wait_for_result(#state{}) ->
|
wait_for_result(#state{os_pid = OsPid, port = Port, buffer = Buffer} = State) ->
|
||||||
{'ok', aeminer_pow:nonce(), pow_cuckoo_solution()} | {'error', term()}.
|
|
||||||
wait_for_result(#state{os_pid = OsPid,
|
|
||||||
port = Port,
|
|
||||||
buffer = Buffer} = State) ->
|
|
||||||
receive
|
receive
|
||||||
{Port, {data, Msg}} ->
|
{Port, {data, Msg}} ->
|
||||||
Str = binary_to_list(Msg),
|
Str = binary_to_list(Msg),
|
||||||
@ -514,7 +430,6 @@ wait_for_result(#state{os_pid = OsPid,
|
|||||||
%% end of Str.
|
%% end of Str.
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec handle_fragmented_lines(string(), string()) -> {list(string()), string()}.
|
|
||||||
handle_fragmented_lines(Str, Buffer) ->
|
handle_fragmented_lines(Str, Buffer) ->
|
||||||
Lines = string:tokens(Str, "\n"),
|
Lines = string:tokens(Str, "\n"),
|
||||||
|
|
||||||
@ -542,16 +457,13 @@ handle_fragmented_lines(Str, Buffer) ->
|
|||||||
%% Parse miner output
|
%% Parse miner output
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec parse_generation_result(list(string()), #state{}) ->
|
|
||||||
{'ok', Nonce :: aeminer_pow:nonce(), Solution :: pow_cuckoo_solution()} |
|
|
||||||
{'error', term()}.
|
|
||||||
parse_generation_result([], State) ->
|
parse_generation_result([], State) ->
|
||||||
wait_for_result(State);
|
wait_for_result(State);
|
||||||
parse_generation_result(["Solution" ++ NonceValuesStr | Rest], #state{os_pid = OsPid,
|
parse_generation_result(["Solution" ++ NonceValuesStr | Rest],
|
||||||
target = Target} = State) ->
|
#state{os_pid = OsPid, edge_bits = EdgeBits, target = Target} = State) ->
|
||||||
[NonceStr | SolStrs] = string:tokens(NonceValuesStr, " "),
|
[NonceStr | SolStrs] = string:tokens(NonceValuesStr, " "),
|
||||||
Soln = [list_to_integer(string:trim(V, both, [$\r]), 16) || V <- SolStrs],
|
Soln = [list_to_integer(string:trim(V, both, [$\r]), 16) || V <- SolStrs],
|
||||||
case {length(Soln), test_target(Soln, Target)} of
|
case {length(Soln), test_target(Soln, Target, EdgeBits)} of
|
||||||
{42, true} ->
|
{42, true} ->
|
||||||
stop_execution(OsPid),
|
stop_execution(OsPid),
|
||||||
case parse_nonce_str(NonceStr) of
|
case parse_nonce_str(NonceStr) of
|
||||||
@ -563,8 +475,8 @@ parse_generation_result(["Solution" ++ NonceValuesStr | Rest], #state{os_pid = O
|
|||||||
Err
|
Err
|
||||||
end;
|
end;
|
||||||
{N, _} when N /= 42 ->
|
{N, _} when N /= 42 ->
|
||||||
%% No nonce in solution, old miner executable?
|
|
||||||
?debug("Solution has wrong length (~p) should be 42", [N]),
|
?debug("Solution has wrong length (~p) should be 42", [N]),
|
||||||
|
%% No nonce in solution, old miner exec?
|
||||||
stop_execution(OsPid),
|
stop_execution(OsPid),
|
||||||
{error, bad_miner};
|
{error, bad_miner};
|
||||||
{_, false} ->
|
{_, false} ->
|
||||||
@ -585,7 +497,6 @@ parse_nonce_str(S) ->
|
|||||||
%% Stop the OS process
|
%% Stop the OS process
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec stop_execution(os_pid()) -> ok.
|
|
||||||
stop_execution(OsPid) ->
|
stop_execution(OsPid) ->
|
||||||
exec_kill(OsPid),
|
exec_kill(OsPid),
|
||||||
?debug("Mining OS process ~p stopped", [OsPid]),
|
?debug("Mining OS process ~p stopped", [OsPid]),
|
||||||
@ -597,24 +508,16 @@ stop_execution(OsPid) ->
|
|||||||
%% greater than 33 (when it needs u64 to store). Hash result for difficulty
|
%% greater than 33 (when it needs u64 to store). Hash result for difficulty
|
||||||
%% control accordingly.
|
%% control accordingly.
|
||||||
%% @end
|
%% @end
|
||||||
%%------------------------------------------------------------------------------
|
|
||||||
-spec get_node_size() -> non_neg_integer().
|
|
||||||
get_node_size() ->
|
|
||||||
node_size(get_edge_bits()).
|
|
||||||
|
|
||||||
%% Refs:
|
%% Refs:
|
||||||
%% * https://github.com/tromp/cuckoo/blob/488c03f5dbbfdac6d2d3a7e1d0746c9a7dafc48f/src/Makefile#L214-L215
|
%% * https://github.com/tromp/cuckoo/blob/488c03f5dbbfdac6d2d3a7e1d0746c9a7dafc48f/src/Makefile#L214-L215
|
||||||
%% * https://github.com/tromp/cuckoo/blob/488c03f5dbbfdac6d2d3a7e1d0746c9a7dafc48f/src/cuckoo.h#L26-L30
|
%% * https://github.com/tromp/cuckoo/blob/488c03f5dbbfdac6d2d3a7e1d0746c9a7dafc48f/src/cuckoo.h#L26-L30
|
||||||
-spec node_size(non_neg_integer()) -> non_neg_integer().
|
%%------------------------------------------------------------------------------
|
||||||
node_size(EdgeBits) when is_integer(EdgeBits), EdgeBits > 31 -> 8;
|
-spec get_node_size(pos_integer()) -> non_neg_integer().
|
||||||
node_size(EdgeBits) when is_integer(EdgeBits), EdgeBits > 0 -> 4.
|
get_node_size(EdgeBits) when is_integer(EdgeBits), EdgeBits > 31 -> 8;
|
||||||
|
get_node_size(EdgeBits) when is_integer(EdgeBits), EdgeBits > 0 -> 4.
|
||||||
|
|
||||||
-spec exec_run(string(), string(), list(string())) ->
|
|
||||||
{ok, Port :: port(), OsPid :: os_pid()} |
|
|
||||||
{error, {port_error, {term(), term()}}}.
|
|
||||||
exec_run(Cmd, Dir, Args) ->
|
exec_run(Cmd, Dir, Args) ->
|
||||||
PortSettings = [
|
PortSettings = [binary,
|
||||||
binary,
|
|
||||||
exit_status,
|
exit_status,
|
||||||
hide,
|
hide,
|
||||||
in,
|
in,
|
||||||
@ -639,7 +542,6 @@ exec_run(Cmd, Dir, Args) ->
|
|||||||
{error, {port_error, {C, E}}}
|
{error, {port_error, {C, E}}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec exec_kill(os_pid()) -> ok.
|
|
||||||
exec_kill(undefined) ->
|
exec_kill(undefined) ->
|
||||||
ok;
|
ok;
|
||||||
exec_kill(OsPid) ->
|
exec_kill(OsPid) ->
|
||||||
@ -652,7 +554,6 @@ exec_kill(OsPid) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec is_unix() -> boolean().
|
|
||||||
is_unix() ->
|
is_unix() ->
|
||||||
case erlang:system_info(system_architecture) of
|
case erlang:system_info(system_architecture) of
|
||||||
"win32" ->
|
"win32" ->
|
||||||
@ -666,13 +567,11 @@ is_unix() ->
|
|||||||
%% hash-based target is suggested: the sha256 hash of the cycle nonces
|
%% hash-based target is suggested: the sha256 hash of the cycle nonces
|
||||||
%% is restricted to be under the target value (0 < target < 2^256).
|
%% is restricted to be under the target value (0 < target < 2^256).
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec test_target(Soln :: pow_cuckoo_solution(), Target :: aeminer_pow:sci_int()) ->
|
test_target(Soln, Target, EdgeBits) ->
|
||||||
boolean().
|
test_target1(Soln, Target, get_node_size(EdgeBits)).
|
||||||
test_target(Soln, Target) ->
|
|
||||||
test_target(Soln, Target, get_node_size()).
|
|
||||||
|
|
||||||
test_target(Soln, Target, NodeSize) ->
|
test_target1(Soln, Target, NodeSize) ->
|
||||||
Bin = solution_to_binary(lists:sort(Soln), NodeSize * 8, <<>>),
|
Bin = solution_to_binary(lists:sort(Soln), NodeSize * 8),
|
||||||
Hash = aeminer_blake2b_256:hash(Bin),
|
Hash = aeminer_blake2b_256:hash(Bin),
|
||||||
aeminer_pow:test_target(Hash, Target).
|
aeminer_pow:test_target(Hash, Target).
|
||||||
|
|
||||||
@ -680,8 +579,9 @@ test_target(Soln, Target, NodeSize) ->
|
|||||||
%% Convert solution (a list of 42 numbers) to a binary
|
%% Convert solution (a list of 42 numbers) to a binary
|
||||||
%% in a languauge-independent way
|
%% in a languauge-independent way
|
||||||
%%------------------------------------------------------------------------------
|
%%------------------------------------------------------------------------------
|
||||||
-spec solution_to_binary(Soln :: pow_cuckoo_solution(), Bits :: integer(),
|
solution_to_binary(Soln, Bits) ->
|
||||||
Acc :: binary()) -> binary().
|
solution_to_binary(Soln, Bits, <<>>).
|
||||||
|
|
||||||
solution_to_binary([], _Bits, Acc) ->
|
solution_to_binary([], _Bits, Acc) ->
|
||||||
Acc;
|
Acc;
|
||||||
solution_to_binary([H | T], Bits, Acc) ->
|
solution_to_binary([H | T], Bits, Acc) ->
|
||||||
|
@ -28,10 +28,10 @@
|
|||||||
%%%=============================================================================
|
%%%=============================================================================
|
||||||
|
|
||||||
-spec create_keys(binary()) ->
|
-spec create_keys(binary()) ->
|
||||||
{aeu_siphash24:siphash_key(),
|
{aeminer_siphash24:siphash_key(),
|
||||||
aeu_siphash24:siphash_key(),
|
aeminer_siphash24:siphash_key(),
|
||||||
aeu_siphash24:siphash_key(),
|
aeminer_siphash24:siphash_key(),
|
||||||
aeu_siphash24:siphash_key()}.
|
aeminer_siphash24:siphash_key()}.
|
||||||
create_keys(Header) ->
|
create_keys(Header) ->
|
||||||
AuxHash = <<_:32/binary>> = aeminer_blake2b_256:hash(Header),
|
AuxHash = <<_:32/binary>> = aeminer_blake2b_256:hash(Header),
|
||||||
<<K0:64/little-unsigned,
|
<<K0:64/little-unsigned,
|
||||||
|
178
test/aeminer_pow_cuckoo_tests.erl
Normal file
178
test/aeminer_pow_cuckoo_tests.erl
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
%%%=============================================================================
|
||||||
|
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% Unit tests for the aeminer_pow_cuckoo module
|
||||||
|
%%% @end
|
||||||
|
%%%=============================================================================
|
||||||
|
-module(aeminer_pow_cuckoo_tests).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-include("aeminer.hrl").
|
||||||
|
|
||||||
|
-define(TEST_MODULE, aeminer_pow_cuckoo).
|
||||||
|
|
||||||
|
-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) == 42, Soln),
|
||||||
|
|
||||||
|
%% verify the nonce and the solution
|
||||||
|
Res2 = ?TEST_MODULE:verify(?TEST_BIN, Nonce, Soln, Target, ?EDGE_BITS_15),
|
||||||
|
?assert(Res2)
|
||||||
|
end}
|
||||||
|
},
|
||||||
|
{"Generate with a winning nonce but low target threshold, shall fail",
|
||||||
|
{timeout, 90,
|
||||||
|
fun() ->
|
||||||
|
Target = 16#01010000,
|
||||||
|
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) == 42, 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))
|
||||||
|
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) == 42, Soln),
|
||||||
|
|
||||||
|
WrongSoln = lists:seq(0, 41),
|
||||||
|
?assertMatch(L when length(L) == 42, WrongSoln),
|
||||||
|
?assertNotEqual(Soln, WrongSoln),
|
||||||
|
?assertNot(?TEST_MODULE:verify(?TEST_BIN, Nonce, WrongSoln, Target, ?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) == 42, DummySoln),
|
||||||
|
?assertNot(?TEST_MODULE:verify(?TEST_BIN, Nonce, DummySoln, Target, ?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))
|
||||||
|
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).
|
||||||
|
|
162
test/aeminer_pow_tests.erl
Normal file
162
test/aeminer_pow_tests.erl
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
%%%=============================================================================
|
||||||
|
%%% @copyright (C) 2019, Aeternity Anstalt
|
||||||
|
%%% @doc
|
||||||
|
%%% Unit tests for the aeminer_pow module
|
||||||
|
%%% @end
|
||||||
|
%%%=============================================================================
|
||||||
|
-module(aeminer_pow_tests).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
-define(TEST_MODULE, aeminer_pow).
|
||||||
|
|
||||||
|
-include("aeminer.hrl").
|
||||||
|
|
||||||
|
conversion_test_() ->
|
||||||
|
{setup,
|
||||||
|
fun setup/0,
|
||||||
|
fun teardown/1,
|
||||||
|
[{"Integer to scientific conversion",
|
||||||
|
fun() ->
|
||||||
|
%% 02: shifted up 2 bytes to reach the [0x7fffff, 0x008000] range,
|
||||||
|
%% 8: sign as shifted up, 10000: significand
|
||||||
|
?assertEqual(16#01010000, ?TEST_MODULE:integer_to_scientific(1)),
|
||||||
|
%% 01: shifted up 1 byte, 8: shifted up, 0ff00: significand
|
||||||
|
?assertEqual(16#0200ff00, ?TEST_MODULE:integer_to_scientific(255)),
|
||||||
|
?assertEqual(16#02010000, ?TEST_MODULE:integer_to_scientific(256)),
|
||||||
|
?assertEqual(16#02010100, ?TEST_MODULE:integer_to_scientific(257)),
|
||||||
|
%% iput: 1 more than the largest possible significand:
|
||||||
|
%% shifted up 1 byte, the smallest possible significand
|
||||||
|
?assertEqual(16#04008000, ?TEST_MODULE:integer_to_scientific(16#800000)),
|
||||||
|
%% same result: underflow
|
||||||
|
?assertEqual(16#04008000, ?TEST_MODULE:integer_to_scientific(16#800001)),
|
||||||
|
%% example from BitCoin Wiki:
|
||||||
|
%% https://en.bitcoin.it/wiki/Difficulty#How_is_difficulty_calculated.3F_What_is_the_difference_between_bdiff_and_pdiff.3F: (256-bit hash: 64 hex digits)
|
||||||
|
?assertEqual(16#1b0404cb,
|
||||||
|
?TEST_MODULE:integer_to_scientific(
|
||||||
|
16#00000000000404CB000000000000000000000000000000000000000000000000)),
|
||||||
|
%% highest possible target in bitcoin
|
||||||
|
?assertEqual(16#1d00ffff,
|
||||||
|
?TEST_MODULE:integer_to_scientific(16#00000000FFFF0000000000000000000000000000000000000000000000000000)),
|
||||||
|
%% highest expressible number
|
||||||
|
?assertEqual(?HIGHEST_TARGET_SCI,
|
||||||
|
?TEST_MODULE:integer_to_scientific(?HIGHEST_TARGET_INT))
|
||||||
|
end},
|
||||||
|
{"Scientific to integer conversion",
|
||||||
|
fun() -> ?assertEqual(1, ?TEST_MODULE:scientific_to_integer(16#01010000)),
|
||||||
|
?assertEqual(255, ?TEST_MODULE:scientific_to_integer(16#0200ff00)),
|
||||||
|
?assertEqual(16#800000, ?TEST_MODULE:scientific_to_integer(16#04008000)),
|
||||||
|
?assertEqual(?HIGHEST_TARGET_INT,
|
||||||
|
aeminer_pow:scientific_to_integer(?HIGHEST_TARGET_SCI))
|
||||||
|
end},
|
||||||
|
{"Integer to scientific and back",
|
||||||
|
fun() ->
|
||||||
|
%% can be converted w/o losing accuracy
|
||||||
|
?assertEqual(1, ?TEST_MODULE:scientific_to_integer(
|
||||||
|
?TEST_MODULE:integer_to_scientific(1))),
|
||||||
|
?assertEqual(255, ?TEST_MODULE:scientific_to_integer(
|
||||||
|
?TEST_MODULE:integer_to_scientific(255))),
|
||||||
|
?assertEqual(256, ?TEST_MODULE:scientific_to_integer(
|
||||||
|
?TEST_MODULE:integer_to_scientific(256))),
|
||||||
|
%% losing accuracy (last digit: 257 = 1 0000 0001_2)
|
||||||
|
?assertEqual(257, ?TEST_MODULE:scientific_to_integer(
|
||||||
|
?TEST_MODULE:integer_to_scientific(257))),
|
||||||
|
%% can be converted w/o losing accuracy
|
||||||
|
?assertEqual(16#800000, ?TEST_MODULE:scientific_to_integer(
|
||||||
|
?TEST_MODULE:integer_to_scientific(16#800000))),
|
||||||
|
%% can be converted w/o losing accuracy
|
||||||
|
?assertEqual(16#800000, ?TEST_MODULE:scientific_to_integer(
|
||||||
|
?TEST_MODULE:integer_to_scientific(16#800001))),
|
||||||
|
%% can be converted w/o losing accuracy
|
||||||
|
Num1 = 16#00000000000404CB000000000000000000000000000000000000000000000000,
|
||||||
|
?assertEqual(Num1, ?TEST_MODULE:scientific_to_integer(
|
||||||
|
?TEST_MODULE:integer_to_scientific(Num1))),
|
||||||
|
Num2 = 16#00000000FFFF0000000000000000000000000000000000000000000000000000,
|
||||||
|
%% 0x1230000 = 1 0010 0011 0000 0000 0000_2, we lose the last 1 in conversion
|
||||||
|
?assertEqual(Num2, ?TEST_MODULE:scientific_to_integer(
|
||||||
|
?TEST_MODULE:integer_to_scientific(Num2)))
|
||||||
|
end},
|
||||||
|
{"Testing difficulty",
|
||||||
|
fun() ->
|
||||||
|
%%----------------------------------------------------------------------
|
||||||
|
%% More than 3 nonzero bytes
|
||||||
|
%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
?assertEqual(true, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,0,0,0,16#04,16#04,16#ca,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>,
|
||||||
|
16#1b0404cb)),
|
||||||
|
?assertEqual(false, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,0,0,0,16#04,16#04,16#cc,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>>,
|
||||||
|
16#1b0404cb)),
|
||||||
|
?assertEqual(false, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,1,0,0,16#04,16#04,16#ca,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>>,
|
||||||
|
16#1b0404cb)),
|
||||||
|
%%----------------------------------------------------------------------
|
||||||
|
%% Less than 3 nonzero bytes
|
||||||
|
%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% 0403 < 0404
|
||||||
|
?assertEqual(true, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,16#04,16#03>>,
|
||||||
|
16#02040400)),
|
||||||
|
%% 0404 < 0404 fails
|
||||||
|
?assertEqual(false, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,16#04,16#04>>,
|
||||||
|
16#02040400)),
|
||||||
|
%% 0405 < 0404 fails
|
||||||
|
?assertEqual(false, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,16#04,16#05>>,
|
||||||
|
16#02040400)),
|
||||||
|
%% hide a 1 among zeros
|
||||||
|
?assertEqual(false, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,16#04,16#03>>,
|
||||||
|
16#020404cb)),
|
||||||
|
%% 03 < 04
|
||||||
|
?assertEqual(true, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,16#03>>,
|
||||||
|
16#01040000)),
|
||||||
|
%% 04 < 04 fails
|
||||||
|
?assertEqual(false, ?TEST_MODULE:test_target(
|
||||||
|
<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,16#04>>,
|
||||||
|
16#01040000)),
|
||||||
|
|
||||||
|
%%----------------------------------------------------------------------
|
||||||
|
%% Exp > size of binary
|
||||||
|
%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% fffe < ffff
|
||||||
|
?assertEqual(true, ?TEST_MODULE:test_target(
|
||||||
|
<<16#ff,16#fe,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0>>,
|
||||||
|
16#2100ffff)),
|
||||||
|
%% fffffe < ffff00 fails
|
||||||
|
?assertEqual(false, ?TEST_MODULE:test_target(
|
||||||
|
<<16#ff,16#ff,16#fe,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0>>,
|
||||||
|
16#2100ffff))
|
||||||
|
|
||||||
|
end},
|
||||||
|
{"Threshold to difficulty",
|
||||||
|
fun() ->
|
||||||
|
%% More than 3 nonzero bytes
|
||||||
|
Diff = ?TEST_MODULE:target_to_difficulty(16#1b0404cb),
|
||||||
|
?assert(Diff == 1175073517793766964014)
|
||||||
|
end}
|
||||||
|
]
|
||||||
|
}.
|
||||||
|
|
||||||
|
setup() ->
|
||||||
|
application:start(crypto).
|
||||||
|
|
||||||
|
teardown(_) ->
|
||||||
|
application:stop(crypto).
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user