From e8ef1340d15de95c0792ee438a4f3d557bf5d026 Mon Sep 17 00:00:00 2001 From: Rahul Garg Date: Thu, 26 Jul 2018 17:48:32 -0700 Subject: [PATCH 1/7] major refactor, WIP, XXX: ready count does not update --- src/dkg_hybriddkg.erl | 331 +++++++++++++++++++++++++---------- src/dkg_hybridvss.erl | 12 +- test/dkg_hybridvss_SUITE.erl | 8 +- 3 files changed, 245 insertions(+), 106 deletions(-) diff --git a/src/dkg_hybriddkg.erl b/src/dkg_hybriddkg.erl index 742ffc8..2b8238b 100644 --- a/src/dkg_hybriddkg.erl +++ b/src/dkg_hybriddkg.erl @@ -5,12 +5,11 @@ -export([handle_msg/3]). -type session() :: {Leader :: pos_integer(), Round :: pos_integer()}. +-type qhat() :: #{{D :: pos_integer(), Session :: session()} => {C :: dkg_commitment:commitment(), Si :: erlang_pbc:element(), SignedReadyMsgs :: any()}}. +-type qbar() :: #{{D :: pos_integer(), Session :: session()} => #{signed_ready => any(), signed_echo => any(), signed_leader_change => any()}}. - %% Q hat in the protocol --type qhat() :: #{pos_integer() => {dkg_commitment:commitment(), erlang_pbc:element()}}. --type qbar() :: #{pos_integer() => erlang_pbc:element()}. - --record(state, { +-record(dkg, { + state = leader_unconfirmed :: leader_unconfirmed | functional | agreement_started | agreement_completed | leader_change_started | dkg_completed, id :: pos_integer(), n :: pos_integer(), f :: pos_integer(), @@ -18,13 +17,13 @@ u :: erlang_pbc:element(), u2 :: erlang_pbc:element(), vss_map :: #{pos_integer() => dkg_hybridvss:vss()}, - echoes_this_round = [] :: [non_neg_integer()], %% eL,Q in the protocol, echoes seen this round - readies_this_round = [] :: [non_neg_integer()], %% rL,Q in the protocol, readies seen this round - vss_done_this_round = #{} :: qhat(), - vss_done_last_round = #{} :: qbar(), - leader = 1 :: pos_integer(), + qhat = #{} :: qhat(), + qbar = #{} :: qbar(), session :: session(), - started = false :: boolean() + lc_flag = false :: boolean(), + leader :: pos_integer(), + l_next :: pos_integer(), + leader_change = #{} :: map() }). %% upon initialization: @@ -40,16 +39,31 @@ init(Id, N, F, T, G1, G2, Round) -> erlang_pbc:element_pp_init(G2), Session = {1, Round}, VSSes = lists:foldl(fun(E, Map) -> - VSS = dkg_hybridvss:init(Id, N, F, T, G1, G2, {E, Round}), - maps:put(E, VSS, Map) - end, #{}, dkg_util:allnodes(N)), - #state{id=Id, n=N, f=F, t=T, u=G1, u2=G2, session=Session, vss_map=VSSes}. - -start(State = #state{started=false, id=Id, u=Generator}) -> - MyVSS = maps:get(Id, State#state.vss_map), - Secret = erlang_pbc:element_random(erlang_pbc:element_new('Zr', Generator)), + VSS = dkg_hybridvss:init(Id, N, F, T, G1, G2, {E, Round}), + maps:put(E, VSS, Map) + end, #{}, dkg_util:allnodes(N)), + + LeaderChange = lists:foldl(fun(E, Map) -> + maps:put(E, 0, Map) + end, #{}, dkg_util:allnodes(N)), + + #dkg{id=Id, + n=N, + f=F, + t=T, + u=G1, + u2=G2, + leader=1, + l_next=N, + leader_change=LeaderChange, + session=Session, + vss_map=VSSes}. + +start(DKG = #dkg{id=Id, u=G1}) -> + MyVSS = maps:get(Id, DKG#dkg.vss_map), + Secret = erlang_pbc:element_random(erlang_pbc:element_new('Zr', G1)), {NewVSS, {send, ToSend}} = dkg_hybridvss:input(MyVSS, Secret), - {State#state{vss_map=maps:put(Id, NewVSS, State#state.vss_map), started=true}, {send, dkg_util:wrap({vss, Id, State#state.session}, ToSend)}}. + {DKG#dkg{vss_map=maps:put(Id, NewVSS, DKG#dkg.vss_map), state=functional}, {send, dkg_util:wrap({vss, Id, DKG#dkg.session}, ToSend)}}. %% upon (Pd, τ, out, shared, Cd , si,d , Rd ) (first time): %% Qhat ← {Pd}; Rhat ← {Rd} @@ -58,61 +72,74 @@ start(State = #state{started=false, id=Id, u=Generator}) -> %% send the message (L, τ, send, Qhat, Rhat) to each Pj %% else %% delay ← delay(T); start timer(delay) -handle_msg(State = #state{session=Session={Leader, _}}, Sender, {{vss, VssID, Session}, VssMSG}) -> - case dkg_hybridvss:handle_msg(maps:get(VssID, State#state.vss_map), Sender, VssMSG) of +handle_msg(DKG = #dkg{id=Id, session=Session={Leader, _}}, Sender, {{vss, VSSId, Session}, VssMSG}) -> + case dkg_hybridvss:handle_msg(maps:get(VSSId, DKG#dkg.vss_map), Sender, VssMSG) of {NewVSS, ok} -> - {State#state{vss_map=maps:put(VssID, NewVSS, State#state.vss_map)}, ok}; + {DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), state=agreement_started}, ok}; {NewVSS, {send, ToSend}} -> - {State#state{vss_map=maps:put(VssID, NewVSS, State#state.vss_map)}, {send, dkg_util:wrap({vss, VssID, Session}, ToSend)}}; - {NewVSS, {result, {_Session, Commitment, Si}}} -> + {DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), state=agreement_started}, {send, dkg_util:wrap({vss, VSSId, Session}, ToSend)}}; + {NewVSS, {result, {_Session, _Commitment, _Si, _Rd}}=Res} -> + %% upon (Pd, τ, out, shared, Cd , si,d , Rd ) (first time): + %% Qhat ← {Pd}; Rhat ← {Rd} + %% if |Qhat| = t + 1 and Qbar = ∅ then + %% if Pi = L then + %% send the message (L, τ, send, Qhat, Rhat) to each Pj + %% else + %% delay ← delay(T); start timer(delay) %% TODO 'extended' VSS should output signed ready messages - VSSDoneThisRound = maps:put(VssID, {Commitment, Si}, State#state.vss_done_this_round), - case maps:size(VSSDoneThisRound) == State#state.t + 1 andalso maps:size(State#state.vss_done_last_round) == 0 of - true -> - case State#state.id == Leader of + case update_qhat(DKG, VSSId, Res) of + false -> + {DKG, ok}; + {true, NewDKG} -> + %% ct:pal("DKG: ~p, VSS: ~p, update_qhat", [Id, VSSId]), + ct:pal("DKG: ~p, VSS: ~p, count_ready: ~p", [Id, VSSId, count_ready(NewDKG)]), + case count_ready(NewDKG) == NewDKG#dkg.t + 1 andalso maps:size(NewDKG#dkg.qbar) == 0 of true -> - %% this is not multicast in the protocol, but we have multicast support, sooooooo.... - {State#state{vss_map=maps:put(VssID, NewVSS, State#state.vss_map), - vss_done_this_round=VSSDoneThisRound}, - {send, [{multicast, {send, Session, maps:keys(VSSDoneThisRound)}}]}}; + ct:pal("DKG: ~p, VSS: ~p, count_ready: ~p", [Id, VSSId, count_ready(NewDKG)]), + case NewDKG#dkg.id == Leader of + true -> + %% send, send message + {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, {send, [{multicast, {send, Session, maps:keys(NewDKG#dkg.qhat)}}]}}; + false -> + %% start timer + {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, start_timer} + end; false -> - {State#state{vss_map=maps:put(VssID, NewVSS, State#state.vss_map), vss_done_this_round=VSSDoneThisRound}, ok} - end; - false -> - {State#state{vss_map=maps:put(VssID, NewVSS, State#state.vss_map), vss_done_this_round=VSSDoneThisRound}, ok} + {NewDKG, ok} + end end end; %% upon a message (L, τ, send, Q, R/M) from L (first time): %% if verify-signature(Q, R/M) and (Qbar = ∅ or Qbar = Q) then %% send the message (L, τ, echo, Q)sign to each Pj -handle_msg(State = #state{session=Session={Leader, _}}, Sender, {send, Session, VSSDone}) when Sender == Leader -> +handle_msg(DKG = #dkg{session=Session={Leader, _}}, Sender, {send, Session, VSSDone}) when Sender == Leader -> %% TODO verify signatures - case maps:size(State#state.vss_done_last_round) == 0 orelse lists:sort(maps:keys(State#state.vss_done_this_round)) == lists:sort(VSSDone) of + case maps:size(DKG#dkg.qbar) == 0 orelse maps:size(DKG#dkg.qbar) == length(VSSDone) of true -> - {State, {send, [{multicast, {echo, Session, VSSDone}}]}}; + {DKG, {send, [{multicast, {echo, Session, VSSDone}}]}}; false -> - {State, ok} + {DKG, ok} end; + %% upon a message (L, τ, echo, Q)sign from Pm (first time): %% e(L,Q) ← e(L,Q) + 1 %% if e(L,Q) = ceil((n+t+1)/2) and r(L,Q) < t + 1 then %% Qbar ← Q; Mbar ← ceil((n+t+1)/2) signed echo messages for Q %% send the message (L, τ, ready, Q)sign to each Pj -handle_msg(State = #state{session=Session, n=N, t=T}, Sender, {echo, Session, VSSDone}) -> - case lists:member(Sender, State#state.echoes_this_round) of +handle_msg(DKG = #dkg{session=Session, n=N, t=T}, Sender, {echo, Session, VSSDone}=EchoMsg) -> + case store_echo(DKG, Sender, EchoMsg) of false -> - EchoesThisRound = [Sender | State#state.echoes_this_round], - case length(EchoesThisRound) == ceil((N + T + 1) / 2) andalso length(State#state.readies_this_round) < T + 1 of + {DKG, ok}; + {true, NewDKG} -> + case count_echo(NewDKG) == ceil((N+T+1)/2) andalso count_ready(NewDKG) < T+1 of true -> - %% TODO QBar <- Q, MBar <- ... - {State#state{echoes_this_round=EchoesThisRound}, {send, [{multicast, {ready, Session, VSSDone}}]}}; + %% send ready message + {NewDKG, {send, [{multicast, {ready, Session, VSSDone}}]}}; false -> - {State#state{echoes_this_round=EchoesThisRound}, ok} - end; - true -> - {State, ok} + {NewDKG, ok} + end end; %% upon a message (L, τ, ready, Q)sign from Pm (first time): @@ -125,63 +152,175 @@ handle_msg(State = #state{session=Session, n=N, t=T}, Sender, {echo, Session, VS %% WAIT for shared output-messages for each Pd ∈ Q %% si ← SUM(si,d) ∀Pd ∈ Q; ∀p,q : C ← MUL(Cd)p,q ∀Pd ∈ Q %% output (L, τ, DKG-completed, C, si) -handle_msg(State = #state{n=N, t=T, f=F}, Sender, {ready, Session, VSSDone}) -> - case lists:member(Sender, State#state.readies_this_round) of +handle_msg(DKG = #dkg{n=N, t=T, f=F}, Sender, {ready, Session, VSSDone}=ReadyMsg) -> + case store_ready(DKG, Sender, ReadyMsg) of false -> - ReadiesThisRound = [Sender | State#state.readies_this_round], - case length(ReadiesThisRound) == T + 1 andalso length(State#state.echoes_this_round) < ceil((N + T + 1) /2) of + {DKG, ok}; + {true, NewDKG} -> + case count_ready(NewDKG) == T+1 andalso count_echo(NewDKG) < ceil((N+T+1)/2) of true -> - %% TODO QBar <- Q, MBar <- ... - {State#state{readies_this_round=ReadiesThisRound}, {send, [{multicast, {ready, Session, VSSDone}}]}}; + {NewDKG, {ready, Session, VSSDone}}; false -> - case length(ReadiesThisRound) == N - T - F of + case count_ready(NewDKG) == N-T-F of true -> - case lists:all(fun(E) -> maps:is_key(E, State#state.vss_done_this_round) end, ReadiesThisRound) of - true -> - %% TODO presumably we need to check this when we get a VSS result as well? - OutputCommitment = output_commitment(State, VSSDone), - PublicKeyShares = public_key_shares(State, OutputCommitment), - VerificationKey = verification_key(OutputCommitment), - Shard = shard(State, VSSDone), - {State#state{readies_this_round=ReadiesThisRound}, {result, {Shard, VerificationKey, PublicKeyShares}}}; - false -> - {State#state{readies_this_round=ReadiesThisRound}, ok} - end; + %% TODO stop timer + %% TODO presumably we need to check this when we get a VSS result as well? + OutputCommitment = output_commitment(NewDKG), + PublicKeyShares = public_key_shares(NewDKG, OutputCommitment), + VerificationKey = verification_key(OutputCommitment), + Shard = shard(NewDKG), + {NewDKG#dkg{state=dkg_completed}, {result, {Shard, VerificationKey, PublicKeyShares}}}; false -> - {State#state{readies_this_round=ReadiesThisRound}, ok} + {NewDKG#dkg{state=agreement_started}, ok} end - end; - true -> - {State, ok} + end end; -handle_msg(State, _Sender, Msg) -> - {State, {unhandled_msg, Msg}}. + +%% upon timeout: +%% if lcflag = false then +%% if Q = ∅ then +%% lcflag ← true; send msg (τ, lead-ch, L + 1, Qhat, Rhat)sign to each Pj +%% else +%% lcflag ← true; send msg (τ, lead-ch, L + 1, Qbar, Mbar)sign to each Pj +handle_msg(DKG=#dkg{lc_flag=false, + qbar=Qbar, + qhat=Qhat, + session={CurrentLeader, Round}}, _Sender, timeout) -> + NewDKG = DKG#dkg{lc_flag=true}, + Msg = case maps:size(Qbar) == 0 of + true -> + {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, {qhat, Qhat}}}]}; + false -> + {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, {qbar, Qbar}}}]} + end, + {NewDKG, Msg}; + +%% TODO +%% Leader-change for node Pi: session (τ ) and leader L +%% upon a msg (τ, lead-ch, Lbar, Q, R/M)sign from Pj (first time): +%% if Lbar > L and verify-signature(Q, R/M) then +%% lcL ← lcL + 1 +%% Lnext ← min (Lnext , L) +%% if R/M = R then +%% Qhat ← Q; Rhat <- R +%% else +%% Qbar ← Q; Mbar ← M +%% if (SUM(lcL) = t + f + 1 and lcflag = false) then +%% if Q = ∅ then +%% send the msg (τ, lead-ch, Lnext, Qhat, Rhat) to each Pj +%% else +%% send the msg (τ, lead-ch, Lnext, Qbar, Mbar) to each Pj +%% else if (lcL = n − t − f ) then +%% Mbar ← R <- n − t − f signed lead-ch messages for Lbar +%% L ← Lbar; Lnext ← L − 1 +%% lcL ← 0; lcflag = false +%% if Pi = L then +%% if Q = ∅ then +%% send the message (L, τ, send, Qhat, Rhat) to each Pj +%% else +%% send the message (L, τ, send, Qbar, Mbar) to each Pj +%% else +%% delay ← delay(T ) +%% start timer(delay) + +%% handle_msg(DKG=#dkg{leader=Leader, n=N, t=T, f=F, qbar=Qbar, l_next=LNext}, Sender, {leader_change, {Lbar, Round}, {qhat, Qhat}}=LeaderChangeMsg) when Lbar > Leader -> +%% NewLNext = min(LNext, Lbar), +%% case store_leader_change(DKG, Sender, LeaderChangeMsg) of +%% false -> +%% %% already received leader-change +%% {DKG, ok}; +%% {true, NewDKG} -> +%% case lists:sum(maps:values(NewDKG#dkg.leader_change)) == T+F+1 andalso not DKG#dkg.lc_flag of +%% true -> +%% Msg = case maps:size(Qbar) == 0 of +%% true -> +%% {send, [{multicast, {leader_change, {NewLNext, Round}, {qhat, Qhat}}}]}; +%% false -> +%% {send, [{multicast, {leader_change, {NewLNext, Round}, {qbar, Qbar}}}]} +%% end, +%% {NewDKG, Msg}; +%% false -> +%% case leader_change(NewDKG, Lbar) == N-T-F of +%% true -> +%% ok; +%% false -> +%% ok +%% end +%% end +%% end; +%% handle_msg(DKG=#dkg{leader=Leader}, _Sender, {leader_change, {Lbar, _Round}, {qbar, Qbar}}=LeaderChangeMsg) when Lbar > Leader -> +%% ok; + + +handle_msg(DKG, _Sender, Msg) -> + {DKG, {unhandled_msg, Msg}}. %% helper functions --spec output_commitment(#state{}, [non_neg_integer()]) -> dkg_commitment:commitment(). -output_commitment(_State=#state{vss_done_this_round=VSSDoneThisRound}, VSSDone) -> - [FirstCommitment | RemainingCommitments] = lists:foldl(fun(VSSId, Acc) -> - {Commitment, _} = maps:get(VSSId, VSSDoneThisRound), - [Commitment | Acc] - end, [], VSSDone), - lists:foldl(fun(Commitment, Acc) -> - dkg_commitment:mul(Acc, Commitment) - end, FirstCommitment, RemainingCommitments). - --spec public_key_shares(#state{}, dkg_commitment:commitment()) -> [erlang_pbc:element()]. -public_key_shares(_State=#state{n=N}, OutputCommitment) -> +-spec output_commitment(#dkg{}) -> dkg_commitment:commitment(). +output_commitment(_DKG=#dkg{qhat=Qhat, u=U, t=T, n=N}) -> + maps:fold(fun(_K, {Commitment, _, _}, Acc) -> + dkg_commitment:mul(Commitment, Acc) + end, dkg_commitment:new(lists:seq(1, N), U, T), Qhat). + +-spec public_key_shares(#dkg{}, dkg_commitment:commitment()) -> [erlang_pbc:element()]. +public_key_shares(_DKG=#dkg{n=N}, OutputCommitment) -> [dkg_commitment:public_key_share(OutputCommitment, NodeID) || NodeID <- dkg_util:allnodes(N)]. -spec verification_key(dkg_commitment:commitment()) -> erlang_pbc:element(). verification_key(OutputCommitment) -> dkg_commitmentmatrix:lookup([1, 1], dkg_commitment:matrix(OutputCommitment)). --spec shard(#state{}, [non_neg_integer()]) -> erlang_pbc:element(). -shard(_State=#state{vss_done_this_round=VSSDoneThisRound}, VSSDone) -> - [FirstShare | RemainingShares] = lists:foldl(fun(VSSId, Acc) -> - {_, Share} = maps:get(VSSId, VSSDoneThisRound), - [Share | Acc] - end, [], VSSDone), - lists:foldl(fun(Share, Acc) -> - erlang_pbc:element_add(Acc, Share) - end, FirstShare, RemainingShares). +-spec shard(#dkg{}) -> erlang_pbc:element(). +shard(_DKG=#dkg{qhat=Qhat, u=U}) -> + Zero = erlang_pbc:element_set(erlang_pbc:element_new('Zr', U), 0), + maps:fold(fun(_K, {_, Si, _}, Acc) -> + erlang_pbc:element_add(Acc, Si) + end, Zero, Qhat). + +store_echo(DKG, Sender, EchoMsg) -> + store_msg(DKG, Sender, {signed_echo, EchoMsg}). + +store_ready(DKG, Sender, ReadyMsg) -> + store_msg(DKG, Sender, {signed_ready, ReadyMsg}). + +%% store_leader_change(DKG, Sender, LeaderChangeMsg) -> + %% store_msg(DKG, Sender, {signed_leader_change, LeaderChangeMsg}). + +store_msg(DKG, Sender, {MsgType, Msg}) -> + QbarSender = maps:get({Sender, DKG#dkg.session}, DKG#dkg.qbar, #{}), + case maps:is_key(MsgType, QbarSender) of + true -> + false; + false -> + NewQbar = maps:put({Sender, DKG#dkg.session}, maps:put(MsgType, Msg, QbarSender), DKG#dkg.qbar), + {true, DKG#dkg{qbar=NewQbar}} + end. + +count_echo(DKG) -> count_msg(DKG, signed_echo). +count_ready(DKG) -> count_msg(DKG, signed_ready). +%% count_leader_change(DKG) -> count_msg(DKG, signed_leader_change). + +count_msg(DKG=#dkg{session=Session0}, MsgType) -> + maps:fold(fun({_, Session}, V, Acc) when Session == Session0 -> + case maps:is_key(MsgType, V) of + true -> + Acc + 1; + false -> + Acc + end; + (_, _, Acc) -> + Acc + end, 0, DKG#dkg.qbar). + +%% leader_change(DKG, L) -> + %% maps:get(L, DKG#dkg.leader_change, 0). + +update_qhat(DKG=#dkg{id=_Id, qhat=Qhat}, VSSId, {result, {Session, Commitment, Si, Rd}}) -> + %% ct:pal("DKG: ~p, VSS: ~p, update_qhat", [Id, VSSId]), + case maps:is_key({VSSId, Session}, Qhat) of + true -> + false; + false -> + NewQhat = maps:put({VSSId, Session}, {Commitment, Si, Rd}, Qhat), + {true, DKG#dkg{qhat=NewQhat}} + end. diff --git a/src/dkg_hybridvss.erl b/src/dkg_hybridvss.erl index c2ed144..512583d 100644 --- a/src/dkg_hybridvss.erl +++ b/src/dkg_hybridvss.erl @@ -27,7 +27,7 @@ -type send_msg() :: {unicast, pos_integer(), {send, {session(), dkg_commitmentmatrix:serialized_matrix(), dkg_polynomial:polynomial()}}}. -type echo_msg() :: {unicast, pos_integer(), {echo, {session(), dkg_commitmentmatrix:serialized_matrix(), binary()}}}. -type ready_msg() :: {unicast, pos_integer(), {ready, {session(), dkg_commitmentmatrix:serialized_matrix(), binary()}}}. --type result() :: {result, {session(), dkg_commitment:commitment(), [erlang_pbc:element()]}}. +-type result() :: {result, {session(), dkg_commitment:commitment(), [erlang_pbc:element()], map()}}. -export_type([vss/0, session/0]). @@ -132,7 +132,7 @@ handle_msg(State=#state{echoes=Echoes, id=Id, n=N, t=T, session=Session}, Sender %% send the message (Pd, τ, ready, C, a(j)) to Pj %% else if rC = n − t − f then %% si ← a(0); output (Pd , τ, out, shared, C, si ) -handle_msg(State=#state{readies=Readies, n=N, t=T, f=F, id=Id, commitment=Commitment}, Sender, {ready, {Session, SerializedCommitmentMatrix0, SA}}) -> +handle_msg(State=#state{readies=Readies, n=N, t=T, f=F, id=Id, commitment=Commitment}, Sender, {ready, {Session, SerializedCommitmentMatrix0, SA}}=ReadyMsg) -> CommitmentMatrix0 = dkg_commitmentmatrix:deserialize(SerializedCommitmentMatrix0, State#state.u), A = erlang_pbc:binary_to_element(State#state.u, SA), case dkg_commitmentmatrix:verify_point(State#state.u2, CommitmentMatrix0, Sender, Id, A) of @@ -150,16 +150,16 @@ handle_msg(State=#state{readies=Readies, n=N, t=T, f=F, id=Id, commitment=Commit Msgs = lists:map(fun(Node) -> {unicast, Node, {ready, {Session, SerializedCommitmentMatrix0, erlang_pbc:element_to_binary(lists:nth(Node+1, SubShares))}}} end, dkg_util:allnodes(N)), - NewState = State#state{readies=maps:put(Sender, true, Readies), commitment=NewCommitment, received_commitment=true}, + NewState = State#state{readies=maps:put(Sender, ReadyMsg, Readies), commitment=NewCommitment, received_commitment=true}, {NewState, {send, Msgs}}; false -> case dkg_commitment:num_readies(NewCommitment) == (N-T-F) of true-> [SubShare] = dkg_commitment:interpolate(NewCommitment, ready, []), - NewState = State#state{readies=maps:put(Sender, true, Readies), commitment=NewCommitment, received_commitment=true}, - {NewState, {result, {Session, Commitment, SubShare}}}; + NewState = State#state{readies=maps:put(Sender, ReadyMsg, Readies), commitment=NewCommitment, received_commitment=true}, + {NewState, {result, {Session, Commitment, SubShare, NewState#state.readies}}}; false -> - NewState = State#state{readies=maps:put(Sender, true, Readies), commitment=NewCommitment, received_commitment=true}, + NewState = State#state{readies=maps:put(Sender, ReadyMsg, Readies), commitment=NewCommitment, received_commitment=true}, {NewState, ok} end end; diff --git a/test/dkg_hybridvss_SUITE.erl b/test/dkg_hybridvss_SUITE.erl index befd1fe..6c5481d 100644 --- a/test/dkg_hybridvss_SUITE.erl +++ b/test/dkg_hybridvss_SUITE.erl @@ -49,11 +49,11 @@ init_test(Config) -> {_FinalStates, ConvergedResults} = dkg_test_utils:do_send_outer(Module, [{1, {send, MsgsToSend}}], StatesWithId, sets:new()), %% check that the shares from nodes can be interpolated to calculate the original secret back - NodesAndShares = lists:foldl(fun({result, {Node, {_Session, _Commitment, Share}}}, Acc) -> + NodesAndShares = lists:foldl(fun({result, {Node, {_Session, _Commitment, Share, _Rd}}}, Acc) -> maps:put(Node, Share, Acc) end, #{}, sets:to_list(ConvergedResults)), - AllCommitments = [Commitment || {result, {_Node, {_Session, Commitment, _Share}}} <- sets:to_list(ConvergedResults)], + AllCommitments = [Commitment || {result, {_Node, {_Session, Commitment, _Share, _Rd}}} <- sets:to_list(ConvergedResults)], OutputCommitment = hd(AllCommitments), %[VerificationKey | PublicKeyShares] = dkg_commitment:interpolate(OutputCommitment, ready, lists:seq(1, N)), @@ -121,11 +121,11 @@ mnt224_test(Config) -> {_FinalStates, ConvergedResults} = dkg_test_utils:do_send_outer(Module, [{1, {send, MsgsToSend}}], StatesWithId, sets:new()), %% check that the shares from nodes can be interpolated to calculate the original secret back - NodesAndShares = lists:foldl(fun({result, {Node, {_Session, _Commitment, Share}}}, Acc) -> + NodesAndShares = lists:foldl(fun({result, {Node, {_Session, _Commitment, Share, _Rd}}}, Acc) -> maps:put(Node, Share, Acc) end, #{}, sets:to_list(ConvergedResults)), - AllCommitments = [Commitment || {result, {_Node, {_Session, Commitment, _Share}}} <- sets:to_list(ConvergedResults)], + AllCommitments = [Commitment || {result, {_Node, {_Session, Commitment, _Share, _Rd}}} <- sets:to_list(ConvergedResults)], OutputCommitment = hd(AllCommitments), %[VerificationKey | PublicKeyShares] = dkg_commitment:interpolate(OutputCommitment, ready, lists:seq(1, N)), From f2d804322fa1844663d23bedb6b027cf98a04efc Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Thu, 26 Jul 2018 21:19:06 -0700 Subject: [PATCH 2/7] Fix the test --- src/dkg_hybriddkg.erl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/dkg_hybriddkg.erl b/src/dkg_hybriddkg.erl index 2b8238b..26f5c3d 100644 --- a/src/dkg_hybriddkg.erl +++ b/src/dkg_hybriddkg.erl @@ -93,7 +93,7 @@ handle_msg(DKG = #dkg{id=Id, session=Session={Leader, _}}, Sender, {{vss, VSSId, {true, NewDKG} -> %% ct:pal("DKG: ~p, VSS: ~p, update_qhat", [Id, VSSId]), ct:pal("DKG: ~p, VSS: ~p, count_ready: ~p", [Id, VSSId, count_ready(NewDKG)]), - case count_ready(NewDKG) == NewDKG#dkg.t + 1 andalso maps:size(NewDKG#dkg.qbar) == 0 of + case count_vss_ready(NewDKG) == NewDKG#dkg.t + 1 andalso maps:size(NewDKG#dkg.qbar) == 0 of true -> ct:pal("DKG: ~p, VSS: ~p, count_ready: ~p", [Id, VSSId, count_ready(NewDKG)]), case NewDKG#dkg.id == Leader of @@ -102,7 +102,7 @@ handle_msg(DKG = #dkg{id=Id, session=Session={Leader, _}}, Sender, {{vss, VSSId, {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, {send, [{multicast, {send, Session, maps:keys(NewDKG#dkg.qhat)}}]}}; false -> %% start timer - {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, start_timer} + {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, ok} %start_timer} end; false -> {NewDKG, ok} @@ -257,10 +257,10 @@ handle_msg(DKG, _Sender, Msg) -> %% helper functions -spec output_commitment(#dkg{}) -> dkg_commitment:commitment(). -output_commitment(_DKG=#dkg{qhat=Qhat, u=U, t=T, n=N}) -> +output_commitment(_DKG=#dkg{qhat=Qhat, u2=U2, t=T, n=N}) -> maps:fold(fun(_K, {Commitment, _, _}, Acc) -> dkg_commitment:mul(Commitment, Acc) - end, dkg_commitment:new(lists:seq(1, N), U, T), Qhat). + end, dkg_commitment:new(lists:seq(1, N), U2, T), Qhat). -spec public_key_shares(#dkg{}, dkg_commitment:commitment()) -> [erlang_pbc:element()]. public_key_shares(_DKG=#dkg{n=N}, OutputCommitment) -> @@ -324,3 +324,7 @@ update_qhat(DKG=#dkg{id=_Id, qhat=Qhat}, VSSId, {result, {Session, Commitment, S NewQhat = maps:put({VSSId, Session}, {Commitment, Si, Rd}, Qhat), {true, DKG#dkg{qhat=NewQhat}} end. + +count_vss_ready(DKG) -> + maps:size(DKG#dkg.qhat). + From d113fdb2a660f22adec7476d833a9a8b28ad2aa1 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Fri, 27 Jul 2018 11:36:56 -0700 Subject: [PATCH 3/7] Make progress on leader election --- src/dkg_hybriddkg.erl | 146 ++++++++++++++++++++++++----------- test/dkg_hybriddkg_SUITE.erl | 2 +- test/dkg_test_utils.erl | 30 ++++++- 3 files changed, 128 insertions(+), 50 deletions(-) diff --git a/src/dkg_hybriddkg.erl b/src/dkg_hybriddkg.erl index 26f5c3d..e0049c7 100644 --- a/src/dkg_hybriddkg.erl +++ b/src/dkg_hybriddkg.erl @@ -43,10 +43,6 @@ init(Id, N, F, T, G1, G2, Round) -> maps:put(E, VSS, Map) end, #{}, dkg_util:allnodes(N)), - LeaderChange = lists:foldl(fun(E, Map) -> - maps:put(E, 0, Map) - end, #{}, dkg_util:allnodes(N)), - #dkg{id=Id, n=N, f=F, @@ -55,7 +51,6 @@ init(Id, N, F, T, G1, G2, Round) -> u2=G2, leader=1, l_next=N, - leader_change=LeaderChange, session=Session, vss_map=VSSes}. @@ -102,7 +97,7 @@ handle_msg(DKG = #dkg{id=Id, session=Session={Leader, _}}, Sender, {{vss, VSSId, {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, {send, [{multicast, {send, Session, maps:keys(NewDKG#dkg.qhat)}}]}}; false -> %% start timer - {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, ok} %start_timer} + {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, start_timer} end; false -> {NewDKG, ok} @@ -121,6 +116,9 @@ handle_msg(DKG = #dkg{session=Session={Leader, _}}, Sender, {send, Session, VSSD false -> {DKG, ok} end; +handle_msg(DKG, Sender, {send, _Session, _VSSDone}) -> + ct:pal("~p got message for different leader ~p", [DKG#dkg.id, Sender]), + {DKG, ok}; %% upon a message (L, τ, echo, Q)sign from Pm (first time): @@ -183,17 +181,34 @@ handle_msg(DKG = #dkg{n=N, t=T, f=F}, Sender, {ready, Session, VSSDone}=ReadyMsg %% else %% lcflag ← true; send msg (τ, lead-ch, L + 1, Qbar, Mbar)sign to each Pj handle_msg(DKG=#dkg{lc_flag=false, + t=T, + n=N, qbar=Qbar, qhat=Qhat, session={CurrentLeader, Round}}, _Sender, timeout) -> NewDKG = DKG#dkg{lc_flag=true}, Msg = case maps:size(Qbar) == 0 of true -> - {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, {qhat, Qhat}}}]}; + {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, {signed_vss_ready, Qhat}}}]}; false -> - {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, {qbar, Qbar}}}]} + MBar = case count_ready(NewDKG) == T + 1 of + true -> + %% Mbar is t+1 signed ready messages + {signed_ready, get_msg(NewDKG, signed_ready)}; + false -> + case count_echo(NewDKG) == ceil((N+T+1)/2) of + true -> + {signed_echo, get_msg(NewDKG, signed_echo)}; + false -> + erlang:error(wtf) + end + end, + {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, MBar}}]} end, {NewDKG, Msg}; +handle_msg(DKG, _Sender, timeout) -> + %% lc_flag is true + {DKG, ok}; %% TODO %% Leader-change for node Pi: session (τ ) and leader L @@ -206,7 +221,7 @@ handle_msg(DKG=#dkg{lc_flag=false, %% else %% Qbar ← Q; Mbar ← M %% if (SUM(lcL) = t + f + 1 and lcflag = false) then -%% if Q = ∅ then +%% if Qbar = ∅ then %% send the msg (τ, lead-ch, Lnext, Qhat, Rhat) to each Pj %% else %% send the msg (τ, lead-ch, Lnext, Qbar, Mbar) to each Pj @@ -223,33 +238,62 @@ handle_msg(DKG=#dkg{lc_flag=false, %% delay ← delay(T ) %% start timer(delay) -%% handle_msg(DKG=#dkg{leader=Leader, n=N, t=T, f=F, qbar=Qbar, l_next=LNext}, Sender, {leader_change, {Lbar, Round}, {qhat, Qhat}}=LeaderChangeMsg) when Lbar > Leader -> -%% NewLNext = min(LNext, Lbar), -%% case store_leader_change(DKG, Sender, LeaderChangeMsg) of -%% false -> -%% %% already received leader-change -%% {DKG, ok}; -%% {true, NewDKG} -> -%% case lists:sum(maps:values(NewDKG#dkg.leader_change)) == T+F+1 andalso not DKG#dkg.lc_flag of -%% true -> -%% Msg = case maps:size(Qbar) == 0 of -%% true -> -%% {send, [{multicast, {leader_change, {NewLNext, Round}, {qhat, Qhat}}}]}; -%% false -> -%% {send, [{multicast, {leader_change, {NewLNext, Round}, {qbar, Qbar}}}]} -%% end, -%% {NewDKG, Msg}; -%% false -> -%% case leader_change(NewDKG, Lbar) == N-T-F of -%% true -> -%% ok; -%% false -> -%% ok -%% end -%% end -%% end; -%% handle_msg(DKG=#dkg{leader=Leader}, _Sender, {leader_change, {Lbar, _Round}, {qbar, Qbar}}=LeaderChangeMsg) when Lbar > Leader -> -%% ok; +handle_msg(DKG=#dkg{leader=Leader, n=N, t=T, f=F, qbar=Qbar, l_next=LNext}, Sender, {leader_change, {Lbar, Round}, Checkpoint}=LeaderChangeMsg) when Lbar > Leader -> + NewLNext = min(LNext, Lbar), + %% XXX is the leader change message unique by sender or by the Q,R/M attachment, or both? + %% TODO we need to verify these messages are signed + case store_leader_change(DKG, Sender, LeaderChangeMsg) of + false -> + %% already received leader-change + {DKG, ok}; + {true, NewDKG0} -> + %% we need to *update* QHat or QBar here + NewDKG = case Checkpoint of + {signed_vss_ready, SignedVSSReady} -> + %% I *think* we merge the sets here? + NewDKG0#dkg{qhat=maps:merge(NewDKG0#dkg.qhat, SignedVSSReady)}; + {signed_echo, SignedEchoes} -> + lists:foldl(fun({S, Msg}, Acc) -> + store_echo(Acc, S, Msg) + end, NewDKG0, maps:to_list(SignedEchoes)); + {signed_ready, SignedReadies} -> + lists:foldl(fun({S, Msg}, Acc) -> + store_ready(Acc, S, Msg) + end, NewDKG0, maps:to_list(SignedReadies)) + %% TODO can these be leader change messages too? + end, + case count_leader_change(NewDKG) == T+F+1 andalso not DKG#dkg.lc_flag of + true -> + Msg = case maps:size(NewDKG#dkg.qbar) == 0 of + true -> + {send, [{multicast, {leader_change, {NewLNext, Round}, {signed_vss_ready, NewDKG#dkg.qhat}}}]}; + false -> + {send, [{multicast, {leader_change, {NewLNext, Round}, {qbar, Qbar}}}]} + end, + {NewDKG, Msg}; + false -> + case leader_change_count(NewDKG, Lbar) == N - T - F of + true -> + MBar = maps:get(Lbar, DKG#dkg.leader_change), + case DKG#dkg.id == Lbar of + true -> + Msg = case maps:size(NewDKG#dkg.qbar) == 0 of + true -> + {send, [{multicast, {send, {NewLNext, Round}, NewDKG#dkg.qhat}}]}; + false -> + {send, [{multicast, {send, {NewLNext, Round}, MBar}}]} + end, + {NewDKG#dkg{leader=Lbar, lc_flag=false}, Msg}; + false -> + {NewDKG#dkg{leader=Lbar, lc_flag=false}, start_timer} + end; + false -> + {NewDKG, ok} + end + end + end; +handle_msg(DKG, _Sender, {leader_change, {_Lbar, _Round}, _}) -> + {DKG, ok}; handle_msg(DKG, _Sender, Msg) -> @@ -283,8 +327,16 @@ store_echo(DKG, Sender, EchoMsg) -> store_ready(DKG, Sender, ReadyMsg) -> store_msg(DKG, Sender, {signed_ready, ReadyMsg}). -%% store_leader_change(DKG, Sender, LeaderChangeMsg) -> - %% store_msg(DKG, Sender, {signed_leader_change, LeaderChangeMsg}). +store_leader_change(DKG, Sender, {leader_change, {Lbar, _Round}, _}=LeaderChangeMsg) -> + L = maps:get(Lbar, DKG#dkg.leader_change, []), + case lists:keyfind(Sender, 1, L) of + false -> + NewLCM = maps:put(Lbar, lists:keystore(Sender, 1, L, {Sender, LeaderChangeMsg}), DKG#dkg.leader_change), + {true, DKG#dkg{leader_change=NewLCM}}; + true -> + false + end. + %store_msg(DKG, Sender, {signed_leader_change, LeaderChangeMsg}). store_msg(DKG, Sender, {MsgType, Msg}) -> QbarSender = maps:get({Sender, DKG#dkg.session}, DKG#dkg.qbar, #{}), @@ -296,24 +348,24 @@ store_msg(DKG, Sender, {MsgType, Msg}) -> {true, DKG#dkg{qbar=NewQbar}} end. -count_echo(DKG) -> count_msg(DKG, signed_echo). -count_ready(DKG) -> count_msg(DKG, signed_ready). -%% count_leader_change(DKG) -> count_msg(DKG, signed_leader_change). +count_echo(DKG) -> maps:size(get_msg(DKG, signed_echo)). +count_ready(DKG) -> maps:size(get_msg(DKG, signed_ready)). +count_leader_change(DKG) -> length(lists:flatten(maps:keys(DKG#dkg.leader_change))). -count_msg(DKG=#dkg{session=Session0}, MsgType) -> - maps:fold(fun({_, Session}, V, Acc) when Session == Session0 -> +get_msg(DKG=#dkg{session=Session0}, MsgType) -> + maps:fold(fun({K, Session}, V, Acc) when Session == Session0 -> case maps:is_key(MsgType, V) of true -> - Acc + 1; + maps:put(K, maps:get(MsgType, V), Acc); false -> Acc end; (_, _, Acc) -> Acc - end, 0, DKG#dkg.qbar). + end, #{}, DKG#dkg.qbar). -%% leader_change(DKG, L) -> - %% maps:get(L, DKG#dkg.leader_change, 0). +leader_change_count(DKG, L) -> + length(maps:get(L, DKG#dkg.leader_change, [])). update_qhat(DKG=#dkg{id=_Id, qhat=Qhat}, VSSId, {result, {Session, Commitment, Si, Rd}}) -> %% ct:pal("DKG: ~p, VSS: ~p, update_qhat", [Id, VSSId]), diff --git a/test/dkg_hybriddkg_SUITE.erl b/test/dkg_hybriddkg_SUITE.erl index c4f7801..a978ed9 100644 --- a/test/dkg_hybriddkg_SUITE.erl +++ b/test/dkg_hybriddkg_SUITE.erl @@ -41,7 +41,7 @@ init_test(Config) -> {StatesWithId, Replies} = lists:unzip(lists:map(fun(E) -> {State, {send, Replies}} = Module:start(Module:init(E, N, F, T, G1, G2, {1, 0})), {{E, State}, {E, {send, Replies}}} - end, lists:seq(1, N))), + end, lists:seq(2, N))), {_FinalStates, ConvergedResults} = dkg_test_utils:do_send_outer(Module, Replies, StatesWithId, sets:new()), ct:pal("Results ~p", [sets:to_list(ConvergedResults)]), diff --git a/test/dkg_test_utils.erl b/test/dkg_test_utils.erl index ce5b3aa..e80e06d 100644 --- a/test/dkg_test_utils.erl +++ b/test/dkg_test_utils.erl @@ -2,15 +2,27 @@ -export([do_send_outer/4, random_n/2]). -do_send_outer(_Mod, [], States, Acc) -> - {States, Acc}; +do_send_outer(Mod, [], States, Acc) -> + case get_timers() of + [] -> + {States, Acc}; + Timers -> + ct:pal("Timers ~p", [Timers]), + {R, NewStates} = do_send(Mod, {0, {send, [ {unicast, J, timeout} || J <- Timers]}}, [], States), + erlang:put(timers, []), + do_send_outer(Mod, R, NewStates, Acc) + end; do_send_outer(Mod, [{result, {Id, Result}} | T], Pids, Acc) -> do_send_outer(Mod, T, Pids, sets:add_element({result, {Id, Result}}, Acc)); do_send_outer(Mod, [H|T], States, Acc) -> {R, NewStates} = do_send(Mod, H, [], States), do_send_outer(Mod, T++R, NewStates, Acc). +do_send(_Mod, {Id, start_timer}, Acc, States) -> + set_timer(Id), + {Acc, States}; do_send(_Mod, {Id, {result, Result}}, Acc, States) -> + cancel_timer(Id), {[{result, {Id, Result}} | Acc], States}; do_send(_Mod, {_, ok}, Acc, States) -> {Acc, States}; @@ -34,6 +46,20 @@ do_send(Mod, {Id, {send, [{multicast, Msg}|T]}}, Acc, States) -> do_send(_, Bleh, _, _) -> erlang:error(Bleh). +set_timer(Id) -> + Timers = get_timers(), + erlang:put(timers, lists:usort([Id|Timers])). + +cancel_timer(Id) -> + Timers = get_timers(), + erlang:put(timers, Timers -- [Id]). + +get_timers() -> + case erlang:get(timers) of + undefined -> []; + R -> R + end. + random_n(N, List) -> lists:sublist(shuffle(List), N). From 43079d4c22e459a528c824a6c78856b35b516db1 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Fri, 27 Jul 2018 14:38:34 -0700 Subject: [PATCH 4/7] Elections sort of work now --- src/dkg_hybriddkg.erl | 43 ++++++++++++++++++------------------ test/dkg_hybriddkg_SUITE.erl | 4 ++-- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/dkg_hybriddkg.erl b/src/dkg_hybriddkg.erl index e0049c7..1e6a35d 100644 --- a/src/dkg_hybriddkg.erl +++ b/src/dkg_hybriddkg.erl @@ -5,7 +5,6 @@ -export([handle_msg/3]). -type session() :: {Leader :: pos_integer(), Round :: pos_integer()}. --type qhat() :: #{{D :: pos_integer(), Session :: session()} => {C :: dkg_commitment:commitment(), Si :: erlang_pbc:element(), SignedReadyMsgs :: any()}}. -type qbar() :: #{{D :: pos_integer(), Session :: session()} => #{signed_ready => any(), signed_echo => any(), signed_leader_change => any()}}. -record(dkg, { @@ -17,7 +16,8 @@ u :: erlang_pbc:element(), u2 :: erlang_pbc:element(), vss_map :: #{pos_integer() => dkg_hybridvss:vss()}, - qhat = #{} :: qhat(), + vss_results = #{} :: #{pos_integer() => {C :: dkg_commitment:commitment(), Si :: erlang_pbc:element()}}, + qhat = #{} :: #{pos_integer() => [any()]}, qbar = #{} :: qbar(), session :: session(), lc_flag = false :: boolean(), @@ -67,13 +67,13 @@ start(DKG = #dkg{id=Id, u=G1}) -> %% send the message (L, τ, send, Qhat, Rhat) to each Pj %% else %% delay ← delay(T); start timer(delay) -handle_msg(DKG = #dkg{id=Id, session=Session={Leader, _}}, Sender, {{vss, VSSId, Session}, VssMSG}) -> +handle_msg(DKG = #dkg{session=Session={Leader, _}}, Sender, {{vss, VSSId, Session}, VssMSG}) -> case dkg_hybridvss:handle_msg(maps:get(VSSId, DKG#dkg.vss_map), Sender, VssMSG) of {NewVSS, ok} -> {DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), state=agreement_started}, ok}; {NewVSS, {send, ToSend}} -> {DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), state=agreement_started}, {send, dkg_util:wrap({vss, VSSId, Session}, ToSend)}}; - {NewVSS, {result, {_Session, _Commitment, _Si, _Rd}}=Res} -> + {NewVSS, {result, {_Session, Commitment, Si, _Rd}}=Res} -> %% upon (Pd, τ, out, shared, Cd , si,d , Rd ) (first time): %% Qhat ← {Pd}; Rhat ← {Rd} %% if |Qhat| = t + 1 and Qbar = ∅ then @@ -82,22 +82,20 @@ handle_msg(DKG = #dkg{id=Id, session=Session={Leader, _}}, Sender, {{vss, VSSId, %% else %% delay ← delay(T); start timer(delay) %% TODO 'extended' VSS should output signed ready messages - case update_qhat(DKG, VSSId, Res) of + NewDKG0 = DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), vss_results=maps:put(VSSId, {Commitment, Si}, DKG#dkg.vss_results)}, + case update_qhat(NewDKG0, VSSId, Res) of false -> - {DKG, ok}; + {NewDKG0, ok}; {true, NewDKG} -> - %% ct:pal("DKG: ~p, VSS: ~p, update_qhat", [Id, VSSId]), - ct:pal("DKG: ~p, VSS: ~p, count_ready: ~p", [Id, VSSId, count_ready(NewDKG)]), case count_vss_ready(NewDKG) == NewDKG#dkg.t + 1 andalso maps:size(NewDKG#dkg.qbar) == 0 of true -> - ct:pal("DKG: ~p, VSS: ~p, count_ready: ~p", [Id, VSSId, count_ready(NewDKG)]), case NewDKG#dkg.id == Leader of true -> %% send, send message - {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, {send, [{multicast, {send, Session, maps:keys(NewDKG#dkg.qhat)}}]}}; + {NewDKG, {send, [{multicast, {send, Session, NewDKG#dkg.qhat}}]}}; false -> %% start timer - {NewDKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map)}, start_timer} + {NewDKG, start_timer} end; false -> {NewDKG, ok} @@ -117,7 +115,7 @@ handle_msg(DKG = #dkg{session=Session={Leader, _}}, Sender, {send, Session, VSSD {DKG, ok} end; handle_msg(DKG, Sender, {send, _Session, _VSSDone}) -> - ct:pal("~p got message for different leader ~p", [DKG#dkg.id, Sender]), + ct:pal("~p got message for different leader ~p /= ~p", [DKG#dkg.id, Sender, DKG#dkg.leader]), {DKG, ok}; @@ -277,15 +275,16 @@ handle_msg(DKG=#dkg{leader=Leader, n=N, t=T, f=F, qbar=Qbar, l_next=LNext}, Send MBar = maps:get(Lbar, DKG#dkg.leader_change), case DKG#dkg.id == Lbar of true -> + ct:pal("I'm the leader now dawg ~p", [DKG#dkg.id]), Msg = case maps:size(NewDKG#dkg.qbar) == 0 of true -> {send, [{multicast, {send, {NewLNext, Round}, NewDKG#dkg.qhat}}]}; false -> {send, [{multicast, {send, {NewLNext, Round}, MBar}}]} end, - {NewDKG#dkg{leader=Lbar, lc_flag=false}, Msg}; + {NewDKG#dkg{leader=Lbar, lc_flag=false, session={Lbar, element(2, DKG#dkg.session)}}, Msg}; false -> - {NewDKG#dkg{leader=Lbar, lc_flag=false}, start_timer} + {NewDKG#dkg{leader=Lbar, lc_flag=false, session={Lbar, element(2, DKG#dkg.session)}}, start_timer} end; false -> {NewDKG, ok} @@ -301,10 +300,10 @@ handle_msg(DKG, _Sender, Msg) -> %% helper functions -spec output_commitment(#dkg{}) -> dkg_commitment:commitment(). -output_commitment(_DKG=#dkg{qhat=Qhat, u2=U2, t=T, n=N}) -> - maps:fold(fun(_K, {Commitment, _, _}, Acc) -> +output_commitment(_DKG=#dkg{vss_results=R, u2=U2, t=T, n=N}) -> + maps:fold(fun(_K, {Commitment, _}, Acc) -> dkg_commitment:mul(Commitment, Acc) - end, dkg_commitment:new(lists:seq(1, N), U2, T), Qhat). + end, dkg_commitment:new(lists:seq(1, N), U2, T), R). -spec public_key_shares(#dkg{}, dkg_commitment:commitment()) -> [erlang_pbc:element()]. public_key_shares(_DKG=#dkg{n=N}, OutputCommitment) -> @@ -315,11 +314,11 @@ verification_key(OutputCommitment) -> dkg_commitmentmatrix:lookup([1, 1], dkg_commitment:matrix(OutputCommitment)). -spec shard(#dkg{}) -> erlang_pbc:element(). -shard(_DKG=#dkg{qhat=Qhat, u=U}) -> +shard(_DKG=#dkg{vss_results=R, u=U}) -> Zero = erlang_pbc:element_set(erlang_pbc:element_new('Zr', U), 0), - maps:fold(fun(_K, {_, Si, _}, Acc) -> + maps:fold(fun(_K, {_, Si}, Acc) -> erlang_pbc:element_add(Acc, Si) - end, Zero, Qhat). + end, Zero, R). store_echo(DKG, Sender, EchoMsg) -> store_msg(DKG, Sender, {signed_echo, EchoMsg}). @@ -367,13 +366,13 @@ get_msg(DKG=#dkg{session=Session0}, MsgType) -> leader_change_count(DKG, L) -> length(maps:get(L, DKG#dkg.leader_change, [])). -update_qhat(DKG=#dkg{id=_Id, qhat=Qhat}, VSSId, {result, {Session, Commitment, Si, Rd}}) -> +update_qhat(DKG=#dkg{id=_Id, qhat=Qhat}, VSSId, {result, {Session, _Commitment, _Si, Rd}}) -> %% ct:pal("DKG: ~p, VSS: ~p, update_qhat", [Id, VSSId]), case maps:is_key({VSSId, Session}, Qhat) of true -> false; false -> - NewQhat = maps:put({VSSId, Session}, {Commitment, Si, Rd}, Qhat), + NewQhat = maps:put({VSSId, Session}, Rd, Qhat), {true, DKG#dkg{qhat=NewQhat}} end. diff --git a/test/dkg_hybriddkg_SUITE.erl b/test/dkg_hybriddkg_SUITE.erl index a978ed9..3d3cedc 100644 --- a/test/dkg_hybriddkg_SUITE.erl +++ b/test/dkg_hybriddkg_SUITE.erl @@ -17,8 +17,8 @@ all() -> init_per_testcase(_, Config) -> N = list_to_integer(os:getenv("N", "10")), - F = 3, - T = 1, + F = 0, + T = 3, Ph = 0, Module = dkg_hybriddkg, [{n, N}, {f, F}, {module, Module}, {t, T}, {ph, Ph} | Config]. From 2d1574888a8d9d89aeff301a2caf669fde133875 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Fri, 27 Jul 2018 16:38:40 -0700 Subject: [PATCH 5/7] Various small tweaks, still incomplete --- src/dkg_hybriddkg.erl | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/dkg_hybriddkg.erl b/src/dkg_hybriddkg.erl index 1e6a35d..06c7e4c 100644 --- a/src/dkg_hybriddkg.erl +++ b/src/dkg_hybriddkg.erl @@ -50,7 +50,7 @@ init(Id, N, F, T, G1, G2, Round) -> u=G1, u2=G2, leader=1, - l_next=N, + l_next=l_next(1, N), session=Session, vss_map=VSSes}. @@ -236,8 +236,7 @@ handle_msg(DKG, _Sender, timeout) -> %% delay ← delay(T ) %% start timer(delay) -handle_msg(DKG=#dkg{leader=Leader, n=N, t=T, f=F, qbar=Qbar, l_next=LNext}, Sender, {leader_change, {Lbar, Round}, Checkpoint}=LeaderChangeMsg) when Lbar > Leader -> - NewLNext = min(LNext, Lbar), +handle_msg(DKG=#dkg{leader=Leader, n=N, t=T, f=F, qbar=Qbar}, Sender, {leader_change, {Lbar, Round}, Checkpoint}=LeaderChangeMsg) when Lbar > Leader -> %% XXX is the leader change message unique by sender or by the Q,R/M attachment, or both? %% TODO we need to verify these messages are signed case store_leader_change(DKG, Sender, LeaderChangeMsg) of @@ -262,29 +261,36 @@ handle_msg(DKG=#dkg{leader=Leader, n=N, t=T, f=F, qbar=Qbar, l_next=LNext}, Send end, case count_leader_change(NewDKG) == T+F+1 andalso not DKG#dkg.lc_flag of true -> + NewLNext = min(DKG#dkg.l_next, Lbar), + ct:pal("Lnext is ~p", [NewLNext]), Msg = case maps:size(NewDKG#dkg.qbar) == 0 of true -> {send, [{multicast, {leader_change, {NewLNext, Round}, {signed_vss_ready, NewDKG#dkg.qhat}}}]}; false -> + %% XXX MBar should have some relationship to the kind of message we got? {send, [{multicast, {leader_change, {NewLNext, Round}, {qbar, Qbar}}}]} end, - {NewDKG, Msg}; + {NewDKG#dkg{l_next=NewLNext}, Msg}; false -> case leader_change_count(NewDKG, Lbar) == N - T - F of true -> MBar = maps:get(Lbar, DKG#dkg.leader_change), + NewLeaderChangeMap = maps:remove(Leader, DKG#dkg.leader_change), + NewLNext = Lbar + 1, %l_next(Lbar, DKG#dkg.n), + ct:pal("Lnext is ~p", [NewLNext]), case DKG#dkg.id == Lbar of true -> ct:pal("I'm the leader now dawg ~p", [DKG#dkg.id]), Msg = case maps:size(NewDKG#dkg.qbar) == 0 of true -> - {send, [{multicast, {send, {NewLNext, Round}, NewDKG#dkg.qhat}}]}; + {send, [{multicast, {send, {Lbar, Round}, {signed_vss_ready, NewDKG#dkg.qhat}}}]}; false -> - {send, [{multicast, {send, {NewLNext, Round}, MBar}}]} + {send, [{multicast, {send, {Lbar, Round}, {signed_leader_change, MBar}}}]} end, - {NewDKG#dkg{leader=Lbar, lc_flag=false, session={Lbar, element(2, DKG#dkg.session)}}, Msg}; + {NewDKG#dkg{leader=Lbar, l_next=NewLNext, lc_flag=false, session={Lbar, element(2, DKG#dkg.session)}, leader_change=NewLeaderChangeMap}, Msg}; false -> - {NewDKG#dkg{leader=Lbar, lc_flag=false, session={Lbar, element(2, DKG#dkg.session)}}, start_timer} + ct:pal("starting new timer"), + {NewDKG#dkg{leader=Lbar, l_next=NewLNext, lc_flag=false, session={Lbar, element(2, DKG#dkg.session)}, leader_change=NewLeaderChangeMap}, start_timer} end; false -> {NewDKG, ok} @@ -379,3 +385,10 @@ update_qhat(DKG=#dkg{id=_Id, qhat=Qhat}, VSSId, {result, {Session, _Commitment, count_vss_ready(DKG) -> maps:size(DKG#dkg.qhat). +l_next(L, N) -> + case L - 1 < 1 of + true -> + N; + false -> + L - 1 + end. From 1ce40ef3b6826dfa0a7e9082745f0d05eb1cd99f Mon Sep 17 00:00:00 2001 From: Rahul Garg Date: Mon, 30 Jul 2018 16:17:13 -0700 Subject: [PATCH 6/7] sender should not be the same as the receiver when handling echo,ready or timeout I think --- src/dkg_hybriddkg.erl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/dkg_hybriddkg.erl b/src/dkg_hybriddkg.erl index 06c7e4c..feada19 100644 --- a/src/dkg_hybriddkg.erl +++ b/src/dkg_hybriddkg.erl @@ -124,7 +124,7 @@ handle_msg(DKG, Sender, {send, _Session, _VSSDone}) -> %% if e(L,Q) = ceil((n+t+1)/2) and r(L,Q) < t + 1 then %% Qbar ← Q; Mbar ← ceil((n+t+1)/2) signed echo messages for Q %% send the message (L, τ, ready, Q)sign to each Pj -handle_msg(DKG = #dkg{session=Session, n=N, t=T}, Sender, {echo, Session, VSSDone}=EchoMsg) -> +handle_msg(DKG = #dkg{id=Id, session=Session, n=N, t=T}, Sender, {echo, Session, VSSDone}=EchoMsg) when Sender /= Id -> case store_echo(DKG, Sender, EchoMsg) of false -> {DKG, ok}; @@ -137,6 +137,8 @@ handle_msg(DKG = #dkg{session=Session, n=N, t=T}, Sender, {echo, Session, VSSDon {NewDKG, ok} end end; +handle_msg(DKG, _Sender, {echo, _Session, _VSSDone}=_EchoMsg) -> + {DKG, ok}; %% upon a message (L, τ, ready, Q)sign from Pm (first time): %% r(L,Q) ← r(L,Q) + 1 @@ -148,7 +150,7 @@ handle_msg(DKG = #dkg{session=Session, n=N, t=T}, Sender, {echo, Session, VSSDon %% WAIT for shared output-messages for each Pd ∈ Q %% si ← SUM(si,d) ∀Pd ∈ Q; ∀p,q : C ← MUL(Cd)p,q ∀Pd ∈ Q %% output (L, τ, DKG-completed, C, si) -handle_msg(DKG = #dkg{n=N, t=T, f=F}, Sender, {ready, Session, VSSDone}=ReadyMsg) -> +handle_msg(DKG = #dkg{id=Id, n=N, t=T, f=F}, Sender, {ready, Session, VSSDone}=ReadyMsg) when Sender /= Id -> case store_ready(DKG, Sender, ReadyMsg) of false -> {DKG, ok}; @@ -171,6 +173,9 @@ handle_msg(DKG = #dkg{n=N, t=T, f=F}, Sender, {ready, Session, VSSDone}=ReadyMsg end end end; +handle_msg(DKG, _Sender, {ready, _Session, _VSSDone}=_ReadyMsg) -> + %% DKG received ready message from itself, what to do? + {DKG, ok}; %% upon timeout: %% if lcflag = false then @@ -179,11 +184,12 @@ handle_msg(DKG = #dkg{n=N, t=T, f=F}, Sender, {ready, Session, VSSDone}=ReadyMsg %% else %% lcflag ← true; send msg (τ, lead-ch, L + 1, Qbar, Mbar)sign to each Pj handle_msg(DKG=#dkg{lc_flag=false, + id=Id, t=T, n=N, qbar=Qbar, qhat=Qhat, - session={CurrentLeader, Round}}, _Sender, timeout) -> + session={CurrentLeader, Round}}, Sender, timeout) when Sender /= Id -> NewDKG = DKG#dkg{lc_flag=true}, Msg = case maps:size(Qbar) == 0 of true -> @@ -236,7 +242,9 @@ handle_msg(DKG, _Sender, timeout) -> %% delay ← delay(T ) %% start timer(delay) -handle_msg(DKG=#dkg{leader=Leader, n=N, t=T, f=F, qbar=Qbar}, Sender, {leader_change, {Lbar, Round}, Checkpoint}=LeaderChangeMsg) when Lbar > Leader -> +handle_msg(DKG=#dkg{leader=Leader, id=Id, n=N, t=T, f=F, qbar=Qbar}, + Sender, + {leader_change, {Lbar, Round}, Checkpoint}=LeaderChangeMsg) when Lbar > Leader andalso Sender /= Id -> %% XXX is the leader change message unique by sender or by the Q,R/M attachment, or both? %% TODO we need to verify these messages are signed case store_leader_change(DKG, Sender, LeaderChangeMsg) of From 99c22d0093551bd0f49d510d9654f635a0e5233b Mon Sep 17 00:00:00 2001 From: Rahul Garg Date: Tue, 31 Jul 2018 18:04:46 -0700 Subject: [PATCH 7/7] * Working pessimistic phase! * Tests for pessimistic phase * Refactor a lot of code * better variable names all around --- src/dkg_hybriddkg.erl | 319 ++++++++++++++++----------------- test/dkg_distributed_SUITE.erl | 8 +- test/dkg_hybriddkg_SUITE.erl | 131 +++++++++++++- test/dkg_worker.erl | 1 - 4 files changed, 285 insertions(+), 174 deletions(-) diff --git a/src/dkg_hybriddkg.erl b/src/dkg_hybriddkg.erl index feada19..2156e89 100644 --- a/src/dkg_hybriddkg.erl +++ b/src/dkg_hybriddkg.erl @@ -5,7 +5,21 @@ -export([handle_msg/3]). -type session() :: {Leader :: pos_integer(), Round :: pos_integer()}. --type qbar() :: #{{D :: pos_integer(), Session :: session()} => #{signed_ready => any(), signed_echo => any(), signed_leader_change => any()}}. + +-type lead_ch() :: any(). +-type signed_echo() :: any(). +-type signed_ready() :: any(). +-type vss_ready() :: any(). + +-type leader_change() :: #{Leader :: pos_integer() => [{Sender :: pos_integer(), lead_ch()}]}. + +-type elq() :: #{{Leader :: pos_integer(), Q :: [pos_integer()]} => [{Sender :: pos_integer(), SignedEcho :: signed_echo()}]}. +-type rlq() :: #{{Leader :: pos_integer(), Q :: [pos_integer()]} => [{Sender :: pos_integer(), SignedReady :: signed_ready()}]}. + +-type rhat() :: [vss_ready()]. +-type qbar() :: [pos_integer()]. +-type qhat() :: [pos_integer()]. +-type mbar() :: [signed_ready() | signed_echo()]. -record(dkg, { state = leader_unconfirmed :: leader_unconfirmed | functional | agreement_started | agreement_completed | leader_change_started | dkg_completed, @@ -17,13 +31,17 @@ u2 :: erlang_pbc:element(), vss_map :: #{pos_integer() => dkg_hybridvss:vss()}, vss_results = #{} :: #{pos_integer() => {C :: dkg_commitment:commitment(), Si :: erlang_pbc:element()}}, - qhat = #{} :: #{pos_integer() => [any()]}, - qbar = #{} :: qbar(), + qbar = [] :: qbar(), + qhat = [] :: qhat(), + rhat = [] :: rhat(), + mbar = [] :: mbar(), + elq = #{} :: elq(), + rlq = #{} :: rlq(), session :: session(), lc_flag = false :: boolean(), leader :: pos_integer(), l_next :: pos_integer(), - leader_change = #{} :: map() + leader_change = #{} :: leader_change() }). %% upon initialization: @@ -60,6 +78,7 @@ start(DKG = #dkg{id=Id, u=G1}) -> {NewVSS, {send, ToSend}} = dkg_hybridvss:input(MyVSS, Secret), {DKG#dkg{vss_map=maps:put(Id, NewVSS, DKG#dkg.vss_map), state=functional}, {send, dkg_util:wrap({vss, Id, DKG#dkg.session}, ToSend)}}. + %% upon (Pd, τ, out, shared, Cd , si,d , Rd ) (first time): %% Qhat ← {Pd}; Rhat ← {Rd} %% if |Qhat| = t + 1 and Qbar = ∅ then @@ -73,7 +92,7 @@ handle_msg(DKG = #dkg{session=Session={Leader, _}}, Sender, {{vss, VSSId, Sessio {DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), state=agreement_started}, ok}; {NewVSS, {send, ToSend}} -> {DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), state=agreement_started}, {send, dkg_util:wrap({vss, VSSId, Session}, ToSend)}}; - {NewVSS, {result, {_Session, Commitment, Si, _Rd}}=Res} -> + {NewVSS, {result, {_Session, Commitment, Si, Rd}}} -> %% upon (Pd, τ, out, shared, Cd , si,d , Rd ) (first time): %% Qhat ← {Pd}; Rhat ← {Rd} %% if |Qhat| = t + 1 and Qbar = ∅ then @@ -82,40 +101,38 @@ handle_msg(DKG = #dkg{session=Session={Leader, _}}, Sender, {{vss, VSSId, Sessio %% else %% delay ← delay(T); start timer(delay) %% TODO 'extended' VSS should output signed ready messages - NewDKG0 = DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), vss_results=maps:put(VSSId, {Commitment, Si}, DKG#dkg.vss_results)}, - case update_qhat(NewDKG0, VSSId, Res) of - false -> - {NewDKG0, ok}; - {true, NewDKG} -> - case count_vss_ready(NewDKG) == NewDKG#dkg.t + 1 andalso maps:size(NewDKG#dkg.qbar) == 0 of + + NewDKG = DKG#dkg{vss_map=maps:put(VSSId, NewVSS, DKG#dkg.vss_map), + vss_results=maps:put(VSSId, {Commitment, Si}, DKG#dkg.vss_results), + qhat=[VSSId | DKG#dkg.qhat], + rhat=[{signed_vss_ready, Rd} | DKG#dkg.rhat] + }, + + case length(NewDKG#dkg.qhat) == NewDKG#dkg.t + 1 andalso length(NewDKG#dkg.qbar) == 0 of + true -> + case NewDKG#dkg.id == Leader of true -> - case NewDKG#dkg.id == Leader of - true -> - %% send, send message - {NewDKG, {send, [{multicast, {send, Session, NewDKG#dkg.qhat}}]}}; - false -> - %% start timer - {NewDKG, start_timer} - end; + {NewDKG, {send, [{multicast, {send, Session, NewDKG#dkg.qhat, {rhat, NewDKG#dkg.rhat}}}]}}; false -> - {NewDKG, ok} - end + {NewDKG, start_timer} + end; + false -> + {NewDKG, ok} end end; %% upon a message (L, τ, send, Q, R/M) from L (first time): %% if verify-signature(Q, R/M) and (Qbar = ∅ or Qbar = Q) then %% send the message (L, τ, echo, Q)sign to each Pj -handle_msg(DKG = #dkg{session=Session={Leader, _}}, Sender, {send, Session, VSSDone}) when Sender == Leader -> +handle_msg(DKG = #dkg{session=Session={_Leader, _}}, _Sender, {send, Session, Q, {rhat, _Rhat}}) -> %% TODO verify signatures - case maps:size(DKG#dkg.qbar) == 0 orelse maps:size(DKG#dkg.qbar) == length(VSSDone) of + case length(DKG#dkg.qbar) == 0 orelse lists:usort(DKG#dkg.qbar) == lists:usort(Q) of true -> - {DKG, {send, [{multicast, {echo, Session, VSSDone}}]}}; + {DKG, {send, [{multicast, {echo, Session, Q}}]}}; false -> {DKG, ok} end; -handle_msg(DKG, Sender, {send, _Session, _VSSDone}) -> - ct:pal("~p got message for different leader ~p /= ~p", [DKG#dkg.id, Sender, DKG#dkg.leader]), +handle_msg(DKG, _Sender, {send, _Session, _, _}) -> {DKG, ok}; @@ -124,20 +141,24 @@ handle_msg(DKG, Sender, {send, _Session, _VSSDone}) -> %% if e(L,Q) = ceil((n+t+1)/2) and r(L,Q) < t + 1 then %% Qbar ← Q; Mbar ← ceil((n+t+1)/2) signed echo messages for Q %% send the message (L, τ, ready, Q)sign to each Pj -handle_msg(DKG = #dkg{id=Id, session=Session, n=N, t=T}, Sender, {echo, Session, VSSDone}=EchoMsg) when Sender /= Id -> - case store_echo(DKG, Sender, EchoMsg) of +handle_msg(DKG = #dkg{id=_Id, session=Session, n=N, t=T}, Sender, {echo, Session, Q}=EchoMsg) -> + case update_elq(DKG, Sender, EchoMsg) of false -> {DKG, ok}; {true, NewDKG} -> - case count_echo(NewDKG) == ceil((N+T+1)/2) andalso count_ready(NewDKG) < T+1 of + case count_echo(NewDKG, Q) == ceil((N+T+1)/2) andalso count_ready(NewDKG, Q) < T+1 of true -> + %% update qbar + NewQbar = Q, + %% update mbar + NewMbar = get_echo(NewDKG, Q), %% send ready message - {NewDKG, {send, [{multicast, {ready, Session, VSSDone}}]}}; + {NewDKG#dkg{qbar=NewQbar, mbar=NewMbar}, {send, [{multicast, {ready, Session, Q}}]}}; false -> {NewDKG, ok} end end; -handle_msg(DKG, _Sender, {echo, _Session, _VSSDone}=_EchoMsg) -> +handle_msg(DKG, _Sender, {echo, _Session, _}=_EchoMsg) -> {DKG, ok}; %% upon a message (L, τ, ready, Q)sign from Pm (first time): @@ -150,16 +171,21 @@ handle_msg(DKG, _Sender, {echo, _Session, _VSSDone}=_EchoMsg) -> %% WAIT for shared output-messages for each Pd ∈ Q %% si ← SUM(si,d) ∀Pd ∈ Q; ∀p,q : C ← MUL(Cd)p,q ∀Pd ∈ Q %% output (L, τ, DKG-completed, C, si) -handle_msg(DKG = #dkg{id=Id, n=N, t=T, f=F}, Sender, {ready, Session, VSSDone}=ReadyMsg) when Sender /= Id -> - case store_ready(DKG, Sender, ReadyMsg) of +handle_msg(DKG = #dkg{id=_Id, n=N, t=T, f=F}, Sender, {ready, Session, Q}=ReadyMsg) -> + case update_rlq(DKG, Sender, ReadyMsg) of false -> {DKG, ok}; {true, NewDKG} -> - case count_ready(NewDKG) == T+1 andalso count_echo(NewDKG) < ceil((N+T+1)/2) of + case count_ready(NewDKG, Q) == T+1 andalso count_echo(NewDKG, Q) < ceil((N+T+1)/2) of true -> - {NewDKG, {ready, Session, VSSDone}}; + %% update qbar + NewQbar = Q, + %% update mbar + NewMbar = get_ready(NewDKG, Q), + %% send ready msg + {NewDKG#dkg{qbar=NewQbar, mbar=NewMbar}, {send, [{multicast, {ready, Session, Q}}]}}; false -> - case count_ready(NewDKG) == N-T-F of + case count_ready(NewDKG, Q) == N-T-F of true -> %% TODO stop timer %% TODO presumably we need to check this when we get a VSS result as well? @@ -184,30 +210,19 @@ handle_msg(DKG, _Sender, {ready, _Session, _VSSDone}=_ReadyMsg) -> %% else %% lcflag ← true; send msg (τ, lead-ch, L + 1, Qbar, Mbar)sign to each Pj handle_msg(DKG=#dkg{lc_flag=false, - id=Id, - t=T, - n=N, + id=_Id, qbar=Qbar, qhat=Qhat, - session={CurrentLeader, Round}}, Sender, timeout) when Sender /= Id -> + rhat=Rhat, + mbar=Mbar, + session={CurrentLeader, Round}}, _Sender, timeout) -> NewDKG = DKG#dkg{lc_flag=true}, - Msg = case maps:size(Qbar) == 0 of + + Msg = case length(Qbar) == 0 of true -> - {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, {signed_vss_ready, Qhat}}}]}; + {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, Qhat, {rhat, Rhat}}}]}; false -> - MBar = case count_ready(NewDKG) == T + 1 of - true -> - %% Mbar is t+1 signed ready messages - {signed_ready, get_msg(NewDKG, signed_ready)}; - false -> - case count_echo(NewDKG) == ceil((N+T+1)/2) of - true -> - {signed_echo, get_msg(NewDKG, signed_echo)}; - false -> - erlang:error(wtf) - end - end, - {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, MBar}}]} + {send, [{multicast, {leader_change, {CurrentLeader+1, Round}, Qbar, {mbar, Mbar}}}]} end, {NewDKG, Msg}; handle_msg(DKG, _Sender, timeout) -> @@ -241,74 +256,67 @@ handle_msg(DKG, _Sender, timeout) -> %% else %% delay ← delay(T ) %% start timer(delay) - -handle_msg(DKG=#dkg{leader=Leader, id=Id, n=N, t=T, f=F, qbar=Qbar}, +handle_msg(DKG=#dkg{leader=Leader, t=T, n=N, f=F, qhat=Qhat0, rhat=Rhat0, l_next=LNext}, Sender, - {leader_change, {Lbar, Round}, Checkpoint}=LeaderChangeMsg) when Lbar > Leader andalso Sender /= Id -> - %% XXX is the leader change message unique by sender or by the Q,R/M attachment, or both? - %% TODO we need to verify these messages are signed + {leader_change, {Lbar, Round}, Q, RorM}=LeaderChangeMsg) when Lbar > Leader -> + %% TODO: verify the signature(Q, R/M) case store_leader_change(DKG, Sender, LeaderChangeMsg) of - false -> - %% already received leader-change - {DKG, ok}; {true, NewDKG0} -> - %% we need to *update* QHat or QBar here - NewDKG = case Checkpoint of - {signed_vss_ready, SignedVSSReady} -> - %% I *think* we merge the sets here? - NewDKG0#dkg{qhat=maps:merge(NewDKG0#dkg.qhat, SignedVSSReady)}; - {signed_echo, SignedEchoes} -> - lists:foldl(fun({S, Msg}, Acc) -> - store_echo(Acc, S, Msg) - end, NewDKG0, maps:to_list(SignedEchoes)); - {signed_ready, SignedReadies} -> - lists:foldl(fun({S, Msg}, Acc) -> - store_ready(Acc, S, Msg) - end, NewDKG0, maps:to_list(SignedReadies)) - %% TODO can these be leader change messages too? + %% new lnext + NewLNext = min(LNext, Lbar), + NewDKG = case RorM of + {rhat, Rhat} -> + %% FIXME: do this better + NewQhat = lists:usort(Qhat0 ++ Q), + NewRhat = lists:usort(Rhat0 ++ Rhat), + NewDKG0#dkg{qhat=NewQhat, rhat=NewRhat}; + {mbar, Mbar} -> + NewDKG0#dkg{qbar=Q, mbar=Mbar} end, - case count_leader_change(NewDKG) == T+F+1 andalso not DKG#dkg.lc_flag of + case count_leader_change(NewDKG) == T+F+1 andalso not NewDKG#dkg.lc_flag of true -> - NewLNext = min(DKG#dkg.l_next, Lbar), - ct:pal("Lnext is ~p", [NewLNext]), - Msg = case maps:size(NewDKG#dkg.qbar) == 0 of - true -> - {send, [{multicast, {leader_change, {NewLNext, Round}, {signed_vss_ready, NewDKG#dkg.qhat}}}]}; - false -> - %% XXX MBar should have some relationship to the kind of message we got? - {send, [{multicast, {leader_change, {NewLNext, Round}, {qbar, Qbar}}}]} - end, - {NewDKG#dkg{l_next=NewLNext}, Msg}; + case length(NewDKG#dkg.qbar) == 0 of + true -> + {send, [{multicast, {leader_change, {NewLNext, Round}, NewDKG#dkg.qhat, {rhat, NewDKG#dkg.rhat}}}]}; + false -> + {send, [{multicast, {leader_change, {NewLNext, Round}, NewDKG#dkg.qbar, {mbar, NewDKG#dkg.mbar}}}]} + end; false -> - case leader_change_count(NewDKG, Lbar) == N - T - F of + LeaderChangeMsgs = maps:get(Lbar, NewDKG#dkg.leader_change, []), + case length(LeaderChangeMsgs) == N-T-F of true -> - MBar = maps:get(Lbar, DKG#dkg.leader_change), - NewLeaderChangeMap = maps:remove(Leader, DKG#dkg.leader_change), - NewLNext = Lbar + 1, %l_next(Lbar, DKG#dkg.n), - ct:pal("Lnext is ~p", [NewLNext]), - case DKG#dkg.id == Lbar of + NewerMbar = NewerRhat = LeaderChangeMsgs, + NewLeader = Lbar, + NewerLNext = l_next(Leader, N), + NewLeaderChangeMap = maps:put(NewLeader, [], NewDKG#dkg.leader_change), + NewSession = {NewLeader, Round}, + NewerDKG = NewDKG#dkg{lc_flag=false, + mbar=NewerMbar, + rhat=NewerRhat, + leader=NewLeader, + l_next=NewerLNext, + leader_change=NewLeaderChangeMap, + session=NewSession}, + case DKG#dkg.id == NewLeader of true -> - ct:pal("I'm the leader now dawg ~p", [DKG#dkg.id]), - Msg = case maps:size(NewDKG#dkg.qbar) == 0 of - true -> - {send, [{multicast, {send, {Lbar, Round}, {signed_vss_ready, NewDKG#dkg.qhat}}}]}; - false -> - {send, [{multicast, {send, {Lbar, Round}, {signed_leader_change, MBar}}}]} - end, - {NewDKG#dkg{leader=Lbar, l_next=NewLNext, lc_flag=false, session={Lbar, element(2, DKG#dkg.session)}, leader_change=NewLeaderChangeMap}, Msg}; + case length(NewerDKG#dkg.qbar) == 0 of + true -> + {NewerDKG, {send, [{multicast, {send, NewSession, NewerDKG#dkg.qhat, {rhat, NewerDKG#dkg.rhat}}}]}}; + false -> + {NewerDKG, {send, [{multicast, {send, NewSession, NewerDKG#dkg.qbar, {mbar, NewerDKG#dkg.mbar}}}]}} + end; false -> - ct:pal("starting new timer"), - {NewDKG#dkg{leader=Lbar, l_next=NewLNext, lc_flag=false, session={Lbar, element(2, DKG#dkg.session)}, leader_change=NewLeaderChangeMap}, start_timer} + {NewerDKG, start_timer} end; false -> {NewDKG, ok} end - end + end; + false -> + {DKG, ok} end; -handle_msg(DKG, _Sender, {leader_change, {_Lbar, _Round}, _}) -> +handle_msg(DKG, _Sender, {leader_change, {_Lbar, _Round}, _, _}) -> {DKG, ok}; - - handle_msg(DKG, _Sender, Msg) -> {DKG, {unhandled_msg, Msg}}. @@ -334,69 +342,56 @@ shard(_DKG=#dkg{vss_results=R, u=U}) -> erlang_pbc:element_add(Acc, Si) end, Zero, R). -store_echo(DKG, Sender, EchoMsg) -> - store_msg(DKG, Sender, {signed_echo, EchoMsg}). - -store_ready(DKG, Sender, ReadyMsg) -> - store_msg(DKG, Sender, {signed_ready, ReadyMsg}). - -store_leader_change(DKG, Sender, {leader_change, {Lbar, _Round}, _}=LeaderChangeMsg) -> - L = maps:get(Lbar, DKG#dkg.leader_change, []), - case lists:keyfind(Sender, 1, L) of - false -> - NewLCM = maps:put(Lbar, lists:keystore(Sender, 1, L, {Sender, LeaderChangeMsg}), DKG#dkg.leader_change), - {true, DKG#dkg{leader_change=NewLCM}}; +l_next(L, N) -> + case L - 1 < 1 of true -> - false + N; + false -> + L - 1 end. - %store_msg(DKG, Sender, {signed_leader_change, LeaderChangeMsg}). -store_msg(DKG, Sender, {MsgType, Msg}) -> - QbarSender = maps:get({Sender, DKG#dkg.session}, DKG#dkg.qbar, #{}), - case maps:is_key(MsgType, QbarSender) of - true -> - false; +update_elq(DKG=#dkg{elq=Elq}, Sender, {echo, _Session, Q0}=EchoMsg) -> + Q = lists:usort(Q0), + EchoForQAndLeader = maps:get({DKG#dkg.leader, Q}, Elq, []), + case lists:keyfind(Sender, 1, EchoForQAndLeader) of false -> - NewQbar = maps:put({Sender, DKG#dkg.session}, maps:put(MsgType, Msg, QbarSender), DKG#dkg.qbar), - {true, DKG#dkg{qbar=NewQbar}} + NewDKG = DKG#dkg{elq=maps:put({DKG#dkg.leader, Q}, [{Sender, EchoMsg} | EchoForQAndLeader], Elq)}, + {true, NewDKG}; + _ -> + %% already have this echo + false end. -count_echo(DKG) -> maps:size(get_msg(DKG, signed_echo)). -count_ready(DKG) -> maps:size(get_msg(DKG, signed_ready)). -count_leader_change(DKG) -> length(lists:flatten(maps:keys(DKG#dkg.leader_change))). +count_echo(_DKG=#dkg{elq=Elq, leader=Leader}, Q0) -> + Q = lists:usort(Q0), + length(maps:get({Leader, Q}, Elq, [])). +count_ready(_DKG=#dkg{rlq=Rlq, leader=Leader}, Q0) -> + Q = lists:usort(Q0), + length(maps:get({Leader, Q}, Rlq, [])). -get_msg(DKG=#dkg{session=Session0}, MsgType) -> - maps:fold(fun({K, Session}, V, Acc) when Session == Session0 -> - case maps:is_key(MsgType, V) of - true -> - maps:put(K, maps:get(MsgType, V), Acc); - false -> - Acc - end; - (_, _, Acc) -> - Acc - end, #{}, DKG#dkg.qbar). - -leader_change_count(DKG, L) -> - length(maps:get(L, DKG#dkg.leader_change, [])). - -update_qhat(DKG=#dkg{id=_Id, qhat=Qhat}, VSSId, {result, {Session, _Commitment, _Si, Rd}}) -> - %% ct:pal("DKG: ~p, VSS: ~p, update_qhat", [Id, VSSId]), - case maps:is_key({VSSId, Session}, Qhat) of - true -> - false; +get_echo(_DKG=#dkg{elq=Elq, leader=Leader}, Q) -> maps:get({Leader, Q}, Elq, []). +get_ready(_DKG=#dkg{rlq=Rlq, leader=Leader}, Q) -> maps:get({Leader, Q}, Rlq, []). + +update_rlq(DKG=#dkg{rlq=Rlq}, Sender, {ready, _Session, Q0}=ReadyMsg) -> + Q = lists:usort(Q0), + ReadyForQAndLeader = maps:get({DKG#dkg.leader, Q}, Rlq, []), + case lists:keyfind(Sender, 1, ReadyForQAndLeader) of false -> - NewQhat = maps:put({VSSId, Session}, Rd, Qhat), - {true, DKG#dkg{qhat=NewQhat}} + NewDKG = DKG#dkg{rlq=maps:put({DKG#dkg.leader, Q}, [{Sender, ReadyMsg} | ReadyForQAndLeader], Rlq)}, + {true, NewDKG}; + _ -> + %% already have this echo + false end. -count_vss_ready(DKG) -> - maps:size(DKG#dkg.qhat). - -l_next(L, N) -> - case L - 1 < 1 of - true -> - N; +store_leader_change(DKG, Sender, {leader_change, {Lbar, _Round}, _, _}=LeaderChangeMsg) -> + L = maps:get(Lbar, DKG#dkg.leader_change, []), + case lists:keyfind(Sender, 1, L) of false -> - L - 1 + NewLCM = maps:put(Lbar, lists:keystore(Sender, 1, L, {Sender, LeaderChangeMsg}), DKG#dkg.leader_change), + {true, DKG#dkg{leader_change=NewLCM}}; + _ -> + false end. + +count_leader_change(DKG) -> length(lists:flatten(maps:keys(DKG#dkg.leader_change))). diff --git a/test/dkg_distributed_SUITE.erl b/test/dkg_distributed_SUITE.erl index ed80bff..f90c6cb 100644 --- a/test/dkg_distributed_SUITE.erl +++ b/test/dkg_distributed_SUITE.erl @@ -32,8 +32,8 @@ end_per_suite(Config) -> Config. init_per_testcase(TestCase, Config) -> - %% assuming each testcase will work with 5 nodes for now - NodeNames = [eric, kenny, kyle, ike, stan, randy, butters, token, jimmy, timmy], + %% assuming each testcase will work with 7 nodes for now + NodeNames = [eric, kenny, kyle, ike, stan, randy, butters], Nodes = dkg_ct_utils:pmap(fun(Node) -> dkg_ct_utils:start_node(Node, Config, TestCase) end, NodeNames), @@ -41,8 +41,8 @@ init_per_testcase(TestCase, Config) -> _ = [dkg_ct_utils:connect(Node) || Node <- NodeNames], N = length(Nodes), - F = 3, - T = 1, + F = 0, + T = 2, {ok, _} = ct_cover:add_nodes(Nodes), [{nodes, Nodes}, {n, N}, {f, F}, {t, T} | Config]. diff --git a/test/dkg_hybriddkg_SUITE.erl b/test/dkg_hybriddkg_SUITE.erl index 3d3cedc..523e9e3 100644 --- a/test/dkg_hybriddkg_SUITE.erl +++ b/test/dkg_hybriddkg_SUITE.erl @@ -5,14 +5,18 @@ -export([all/0, init_per_testcase/2, end_per_testcase/2]). -export([ - init_test/1, - mnt224_test/1 + symmetric_test/1, + asymmetric_test/1, + leader_change_symmetric_test/1, + leader_change_asymmetric_test/1 ]). all() -> [ - init_test, - mnt224_test + symmetric_test, + asymmetric_test, + leader_change_symmetric_test, + leader_change_asymmetric_test ]. init_per_testcase(_, Config) -> @@ -26,7 +30,7 @@ init_per_testcase(_, Config) -> end_per_testcase(_, _Config) -> ok. -init_test(Config) -> +symmetric_test(Config) -> N = proplists:get_value(n, Config), F = proplists:get_value(f, Config), T = proplists:get_value(t, Config), @@ -41,7 +45,7 @@ init_test(Config) -> {StatesWithId, Replies} = lists:unzip(lists:map(fun(E) -> {State, {send, Replies}} = Module:start(Module:init(E, N, F, T, G1, G2, {1, 0})), {{E, State}, {E, {send, Replies}}} - end, lists:seq(2, N))), + end, lists:seq(1, N))), {_FinalStates, ConvergedResults} = dkg_test_utils:do_send_outer(Module, Replies, StatesWithId, sets:new()), ct:pal("Results ~p", [sets:to_list(ConvergedResults)]), @@ -86,7 +90,7 @@ init_test(Config) -> ?assertEqual(N, length(sets:to_list(ConvergedResults))), ok. -mnt224_test(Config) -> +asymmetric_test(Config) -> N = proplists:get_value(n, Config), F = proplists:get_value(f, Config), T = proplists:get_value(t, Config), @@ -137,3 +141,116 @@ mnt224_test(Config) -> ?assertEqual(N, length(sets:to_list(ConvergedResults))), ok. + +leader_change_symmetric_test(Config) -> + N = proplists:get_value(n, Config), + F = proplists:get_value(f, Config), + T = proplists:get_value(t, Config), + Module = proplists:get_value(module, Config), + Group = erlang_pbc:group_new('SS512'), + G1 = erlang_pbc:element_from_hash(erlang_pbc:element_new('G1', Group), crypto:strong_rand_bytes(32)), + G2 = case erlang_pbc:pairing_is_symmetric(Group) of + true -> G1; + false -> erlang_pbc:element_from_hash(erlang_pbc:element_new('G2', Group), crypto:strong_rand_bytes(32)) + end, + + {StatesWithId, Replies} = lists:unzip(lists:map(fun(E) -> + {State, {send, Replies}} = Module:start(Module:init(E, N, F, T, G1, G2, {1, 0})), + {{E, State}, {E, {send, Replies}}} + end, lists:seq(2, N))), + + {_FinalStates, ConvergedResults} = dkg_test_utils:do_send_outer(Module, Replies, StatesWithId, sets:new()), + ct:pal("Results ~p", [sets:to_list(ConvergedResults)]), + + %% XXX: this is the same as the pubkeyshare test, I'm sure there is more to it + SecretKeyShares = lists:keysort(1, [ {Node, SecretKey} || {result, {Node, {SecretKey, _VerificationKey, _VerificationKeys}}} <- sets:to_list(ConvergedResults)]), + VerificationKeys = lists:keysort(1, [ {Node, VerificationKey} || {result, {Node, {_SecretKey, VerificationKey, _VerificationKeys}}} <- sets:to_list(ConvergedResults)]), + VerificationKeyss = lists:keysort(1, [ {Node, VerificationKeyz} || {result, {Node, {_SecretKey, _VerificationKey, VerificationKeyz}}} <- sets:to_list(ConvergedResults)]), + ct:pal("Secret key shares ~p", [[ erlang_pbc:element_to_string(S) || {_, S} <- SecretKeyShares]]), + ct:pal("Public key shares ~p", [[ erlang_pbc:element_to_string(S) || {_, S} <- VerificationKeys]]), + ct:pal("Public key shares ~p", [[ lists:map(fun erlang_pbc:element_to_string/1, S) || {_, S} <- VerificationKeyss]]), + PublicKeySharePoly = [Share || Share <- element(2, hd(VerificationKeyss))], + KnownSecret = dkg_polynomial:evaluate(PublicKeySharePoly, 0), + Indices = [ erlang_pbc:element_set(erlang_pbc:element_new('Zr', G1), I) || I <- lists:seq(1, N) ], + Alpha = erlang_pbc:element_set(erlang_pbc:element_new('Zr', G1), 0), + CalculatedSecret = dkg_lagrange:interpolate(PublicKeySharePoly, Indices, Alpha), + ?assert(erlang_pbc:element_cmp(KnownSecret, CalculatedSecret)), + + %% attempt to construct some TPKE keys... + + PrivateKeys = lists:map(fun({result, {Node, {SK, VK, VKs}}}) -> + PK = tpke_pubkey:init(N, F, G1, G2, VK, VKs, 'SS512'), + tpke_privkey:init(PK, SK, Node-1) + end, sets:to_list(ConvergedResults)), + PubKey = tpke_privkey:public_key(hd(PrivateKeys)), + Msg = crypto:hash(sha256, crypto:strong_rand_bytes(12)), + MessageToSign = tpke_pubkey:hash_message(PubKey, Msg), + Signatures = [ tpke_privkey:sign(PrivKey, MessageToSign) || PrivKey <- PrivateKeys], + ct:pal("~p", [[tpke_pubkey:verify_signature_share(PubKey, Share, MessageToSign) || Share <- Signatures]]), + ?assert(lists:all(fun(X) -> X end, [tpke_pubkey:verify_signature_share(PubKey, Share, MessageToSign) || Share <- Signatures])), + {ok, Sig} = tpke_pubkey:combine_signature_shares(PubKey, dealer:random_n(T+1, Signatures), MessageToSign), + ?assert(tpke_pubkey:verify_signature(PubKey, Sig, MessageToSign)), + + Message = crypto:hash(sha256, <<"my hovercraft is full of eels">>), + CipherText = tpke_pubkey:encrypt(PubKey, Message), + ?assert(tpke_pubkey:verify_ciphertext(PubKey, CipherText)), + Shares = [ tpke_privkey:decrypt_share(SK, CipherText) || SK <- PrivateKeys ], + ct:pal("Decrypted shares ~p", [Shares]), + ?assert(lists:all(fun(X) -> X end, [tpke_pubkey:verify_share(PubKey, Share, CipherText) || Share <- Shares])), + ?assertEqual(Message, tpke_pubkey:combine_shares(PubKey, CipherText, dealer:random_n(T+1, Shares))), + + ?assertEqual(N-1, length(sets:to_list(ConvergedResults))), + ok. + +leader_change_asymmetric_test(Config) -> + N = proplists:get_value(n, Config), + F = proplists:get_value(f, Config), + T = proplists:get_value(t, Config), + Module = proplists:get_value(module, Config), + Group = erlang_pbc:group_new('MNT224'), + G1 = erlang_pbc:element_from_hash(erlang_pbc:element_new('G1', Group), crypto:strong_rand_bytes(32)), + G2 = case erlang_pbc:pairing_is_symmetric(Group) of + true -> G1; + false -> erlang_pbc:element_from_hash(erlang_pbc:element_new('G2', Group), crypto:strong_rand_bytes(32)) + end, + + {StatesWithId, Replies} = lists:unzip(lists:map(fun(E) -> + {State, {send, Replies}} = Module:start(Module:init(E, N, F, T, G1, G2, {1, 0})), + {{E, State}, {E, {send, Replies}}} + end, lists:seq(2, N))), + + {_FinalStates, ConvergedResults} = dkg_test_utils:do_send_outer(Module, Replies, StatesWithId, sets:new()), + %ct:pal("Results ~p", [sets:to_list(ConvergedResults)]), + + %% XXX: this is the same as the pubkeyshare test, I'm sure there is more to it + SecretKeyShares = lists:keysort(1, [ {Node, SecretKey} || {result, {Node, {SecretKey, _VerificationKey, _VerificationKeys}}} <- sets:to_list(ConvergedResults)]), + VerificationKeys = lists:keysort(1, [ {Node, VerificationKey} || {result, {Node, {_SecretKey, VerificationKey, _VerificationKeys}}} <- sets:to_list(ConvergedResults)]), + VerificationKeyss = lists:keysort(1, [ {Node, VerificationKeyz} || {result, {Node, {_SecretKey, _VerificationKey, VerificationKeyz}}} <- sets:to_list(ConvergedResults)]), + ct:pal("Secret key shares ~p", [[ erlang_pbc:element_to_string(S) || {_, S} <- SecretKeyShares]]), + ct:pal("Public key shares ~p", [[ erlang_pbc:element_to_string(S) || {_, S} <- VerificationKeys]]), + ct:pal("Public key shares ~p", [[ lists:map(fun erlang_pbc:element_to_string/1, S) || {_, S} <- VerificationKeyss]]), + PublicKeySharePoly = [Share || Share <- element(2, hd(VerificationKeyss))], + KnownSecret = dkg_polynomial:evaluate(PublicKeySharePoly, 0), + Indices = [ erlang_pbc:element_set(erlang_pbc:element_new('Zr', G1), I) || I <- lists:seq(1, N) ], + Alpha = erlang_pbc:element_set(erlang_pbc:element_new('Zr', G1), 0), + CalculatedSecret = dkg_lagrange:interpolate(PublicKeySharePoly, Indices, Alpha), + ?assert(erlang_pbc:element_cmp(KnownSecret, CalculatedSecret)), + + %% attempt to construct some TPKE keys... + + PrivateKeys = lists:map(fun({result, {Node, {SK, VK, VKs}}}) -> + PK = tpke_pubkey:init(N, F, G1, G2, VK, VKs, 'MNT224'), + tpke_privkey:init(PK, SK, Node-1) + end, sets:to_list(ConvergedResults)), + PubKey = tpke_privkey:public_key(hd(PrivateKeys)), + Msg = crypto:hash(sha256, crypto:strong_rand_bytes(12)), + MessageToSign = tpke_pubkey:hash_message(PubKey, Msg), + Signatures = [ tpke_privkey:sign(PrivKey, MessageToSign) || PrivKey <- PrivateKeys], + ct:pal("~p", [[tpke_pubkey:verify_signature_share(PubKey, Share, MessageToSign) || Share <- Signatures]]), + ?assert(lists:all(fun(X) -> X end, [tpke_pubkey:verify_signature_share(PubKey, Share, MessageToSign) || Share <- Signatures])), + {ok, Sig} = tpke_pubkey:combine_signature_shares(PubKey, dealer:random_n(T+1, Signatures), MessageToSign), + ?assert(tpke_pubkey:verify_signature(PubKey, Sig, MessageToSign)), + + ?assertEqual(N-1, length(sets:to_list(ConvergedResults))), + ok. + diff --git a/test/dkg_worker.erl b/test/dkg_worker.erl index 27e7b35..cdc6ea2 100644 --- a/test/dkg_worker.erl +++ b/test/dkg_worker.erl @@ -94,7 +94,6 @@ dispatch(Other, State) -> do_send([], _) -> ok; do_send([{unicast, Dest, Msg}|T], State) -> - %io:format("~p unicasting ~p to ~p~n", [State#state.id, Msg, global:whereis_name(name(Dest))]), gen_server:cast({global, name(Dest)}, {dkg, State#state.id, Msg}), do_send(T, State); do_send([{multicast, Msg}|T], State) ->