From 532d7b44717b1707d4ec2a15a68a918f8c217775 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Sun, 23 Mar 2025 21:55:45 +0100 Subject: [PATCH] Add entrypoints for returning all solutions found --- src/gmminer_pow_cuckoo.erl | 79 ++++++++++++++++++++++++++++++-------- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/src/gmminer_pow_cuckoo.erl b/src/gmminer_pow_cuckoo.erl index 110f954..307db8b 100644 --- a/src/gmminer_pow_cuckoo.erl +++ b/src/gmminer_pow_cuckoo.erl @@ -27,11 +27,14 @@ -export([hash_data/1, generate/5, + generate_all/5, generate_from_hash/5, + generate_from_hash/6, verify/5, verify_proof/4, verify_proof_from_hash/4, - get_target/2 + get_target/2, + test_target/3 ]). -export([ set_edge_bits/2 ]). @@ -88,6 +91,7 @@ | undefined. -type solution() :: [integer()]. +-type solutions() :: [{nonce(), solution()}]. -type output_parser_fun() :: fun(([string()], state()) -> {ok, term(), term()} | {error, term()}). @@ -105,12 +109,14 @@ -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() + os_pid :: integer() | undefined, + port :: port() | undefined, + buffer = [] :: string(), + solutions = [] :: solutions(), + keep_going = false :: boolean(), + target :: sci_target() | undefined, + edge_bits :: edge_bits(), + parser :: output_parser_fun() }). -type state() :: #state{}. @@ -190,15 +196,30 @@ generate(Data, Target, Nonce, Config, Instance) when Hash = gmminer_blake2b_256:hash(Data), generate_from_hash(Hash, Target, Nonce, Config, Instance). +-spec generate_all(hashable(), sci_target(), nonce(), config(), instance()) -> + {ok, solutions()} | {error, no_solution} | {error, {runtime, term()}}. +generate_all(Data, Target, Nonce, Config, Instance) when + Nonce >= 0, Nonce =< ?MAX_NONCE -> + Hash = gmminer_blake2b_256:hash(Data), + generate_from_hash(Hash, Target, Nonce, Config, Instance, true). + -spec generate_from_hash(hash(), sci_target(), nonce(), config(), instance()) -> {ok, {nonce(), solution()}} | {error, no_solution} | {error, {runtime, term()}}. generate_from_hash(Hash, Target, Nonce, Config, Instance) -> + generate_from_hash(Hash, Target, Nonce, Config, Instance, false). + + +-spec generate_from_hash(hash(), sci_target(), nonce(), config(), instance(), boolean()) -> + {ok, {nonce(), solution()}} | {ok, solutions()} | {error, no_solution} | {error, {runtime, term()}}. +generate_from_hash(Hash, Target, Nonce, Config, Instance, KeepGoing) -> Hash64 = base64:encode_to_string(Hash), ?log(debug, "Generating solution for data hash ~p and nonce ~p with target ~p.", [Hash, Nonce, Target]), - case generate_int(Hash64, Nonce, Target, Config, Instance) of + case generate_int(Hash64, Nonce, Target, Config, Instance, KeepGoing) of {ok, Nonce1, Soln} -> {ok, {Nonce1, Soln}}; + {ok, _} = Ok -> + Ok; {error, no_value} -> ?log(debug, "No cuckoo solution found", []), {error, no_solution}; @@ -251,7 +272,7 @@ get_target(Soln, EdgeBits) when is_list(Soln), length(Soln) =:= ?SOLUTION_SIZE - generate_int(Hash, Nonce, Target, #config{exec = Exec0, extra_args = ExtraArgs0, - hex_enc_header = HexEncHdr} = Config, Instance) -> + hex_enc_header = HexEncHdr} = Config, Instance, KeepGoing) -> ExtraArgs = case is_miner_instance_addressation_enabled(Config) of true -> binary_to_list(ExtraArgs0) ++ " -d " ++ integer_to_list(Instance); @@ -264,10 +285,10 @@ generate_int(Hash, Nonce, Target, end, ExecBinDir = exec_bin_dir(Config), Exec = binary_to_list(Exec0), - generate_int(EncodedHash, Nonce, Target, ExecBinDir, Exec, ExtraArgs, Config). + generate_int(EncodedHash, Nonce, Target, ExecBinDir, Exec, ExtraArgs, Config, KeepGoing). generate_int(Hash, Nonce, Target, MinerBinDir, MinerBin, MinerExtraArgs, - #config{repeats = Repeats0, edge_bits = EdgeBits}) -> + #config{repeats = Repeats0, edge_bits = EdgeBits}, KeepGoing) -> Repeats = integer_to_list(Repeats0), Args = ["-h", Hash, "-n", integer_to_list(Nonce), "-r", Repeats | string:tokens(MinerExtraArgs, " ")], ?log(info, "Executing cmd '~s ~s'", [MinerBin, lists:concat(lists:join(" ", Args))]), @@ -278,6 +299,7 @@ generate_int(Hash, Nonce, Target, MinerBinDir, MinerBin, MinerExtraArgs, port = Port, buffer = [], target = Target, + keep_going = KeepGoing, edge_bits = EdgeBits, parser = fun parse_generation_result/2}); {error, _Rsn} = Err -> @@ -453,7 +475,21 @@ wait_for_result(#state{os_pid = OsPid, port = Port, buffer = Buffer} = State) -> exit(shutdown); {'EXIT', Port, normal} -> %% Process ended but no value found - {error, no_value}; + case State#state.solutions of + [] -> + {error, no_value}; + + [_|_] = Sols0 -> + Sols = lists:reverse(Sols0), + case State#state.keep_going of + false -> + %% For compatibility, pick the one first found + [{N, S}|_] = Sols, + {ok, N, S}; + true -> + {ok, Sols} + end + end; _Other -> wait_for_result(State) end. @@ -495,16 +531,29 @@ handle_fragmented_lines(Str, Buffer) -> parse_generation_result([], State) -> wait_for_result(State); parse_generation_result(["Solution" ++ NonceValuesStr | Rest], - #state{os_pid = OsPid, edge_bits = EdgeBits, target = Target} = State) -> + #state{os_pid = OsPid, edge_bits = EdgeBits, + target = Target, keep_going = KeepGoing} = State) -> [NonceStr | SolStrs] = string:tokens(NonceValuesStr, " "), Soln = [list_to_integer(string:trim(V, both, [$\r]), 16) || V <- SolStrs], case {length(Soln), test_target(Soln, Target, EdgeBits)} of {?SOLUTION_SIZE, true} -> - stop_execution(OsPid), + ?log(debug, "Solution found, KeepGoing = ~p", [KeepGoing]), + case KeepGoing of + true -> ok; + false -> + stop_execution(OsPid) + end, case parse_nonce_str(NonceStr) of {ok, Nonce} -> ?log(debug, "Solution found: ~p", [Soln]), - {ok, Nonce, Soln}; + case KeepGoing of + true -> + Sols = State#state.solutions, + State1 = State#state{solutions = [{Nonce, Soln}|Sols]}, + parse_generation_result(Rest, State1); + false -> + {ok, Nonce, Soln} + end; Err = {error, _} -> ?log(debug, "Bad nonce: ~p", [Err]), Err