Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes initialisation process on the web frontend #166

Merged
merged 11 commits into from
Sep 22, 2022
8 changes: 8 additions & 0 deletions services/dkg/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ const (
Setup StatusCode = 1
// Failed is when the actor failed to set up
Failed StatusCode = 2
// Dealing is when the actor is sending its deals
Dealing = 3
// Responding is when the actor sends its responses on the deals
Responding = 4
// Certifying is when the actor is validating its responses
Certifying = 5
// Certified is then the actor is certified
Certified = 6
)

// DKG defines the primitive to start a DKG protocol
Expand Down
11 changes: 10 additions & 1 deletion services/dkg/pedersen/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

etypes "github.com/dedis/d-voting/contracts/evoting/types"
"github.com/dedis/d-voting/internal/testing/fake"
"github.com/dedis/d-voting/services/dkg"
"github.com/dedis/d-voting/services/dkg/pedersen/types"
"go.dedis.ch/dela"
"go.dedis.ch/dela/core/ordering"
Expand Down Expand Up @@ -70,12 +71,14 @@ type Handler struct {

log zerolog.Logger
running bool

status *dkg.Status
}

// NewHandler creates a new handler
func NewHandler(me mino.Address, service ordering.Service, pool pool.Pool,
txnmngr txn.Manager, pubSharesSigner crypto.Signer, handlerData HandlerData,
context serde.Context, electionFac serde.Factory) *Handler {
context serde.Context, electionFac serde.Factory, status *dkg.Status) *Handler {

privKey := handlerData.PrivKey
pubKey := handlerData.PubKey
Expand All @@ -101,6 +104,8 @@ func NewHandler(me mino.Address, service ordering.Service, pool pool.Pool,

log: log,
running: false,

status: status,
}
}

Expand Down Expand Up @@ -255,19 +260,23 @@ func (h *Handler) start(start types.Start, deals, resps *list.List, from mino.Ad
// doDKG calls the subsequent DKG steps
func (h *Handler) doDKG(deals, resps *list.List, out mino.Sender, from mino.Address) {
h.log.Info().Str("action", "deal").Msg("new state")
*h.status = dkg.Status{Status: dkg.Dealing}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, this is unsafe, because while we only start Stream once, we could still receive multiple types.Start message. If so, this will be called multiple times.
I believe we have the same issue in dela.

Concretely, the impact on this line is minimal, but I'm concerned about the multiple starts in parallel 🤔 am I missing something ? do we have a test that verifies that we'll ignore the second start (on the same stream) if one is already running ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That can technically happen if other nodes participating in the setup decide to act badly and send start messages.

h.deal(out)

h.log.Info().Str("action", "respond").Msg("new state")
*h.status = dkg.Status{Status: dkg.Responding}
h.respond(deals, out)

h.log.Info().Str("action", "certify").Msg("new state")
*h.status = dkg.Status{Status: dkg.Certifying}
err := h.certify(resps, out)
if err != nil {
dela.Logger.Error().Msgf("failed to certify: %v", err)
return
}

h.log.Info().Str("action", "finalize").Msg("new state")
*h.status = dkg.Status{Status: dkg.Certified}

// Send back the public DKG key
distKey, err := h.dkg.DistKeyShare()
Expand Down
14 changes: 8 additions & 6 deletions services/dkg/pedersen/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,11 @@ func (s *Pedersen) NewActor(electionIDBuf []byte, pool pool.Pool, txmngr txn.Man

ctx := jsonserde.NewContext()

status := &dkg.Status{Status: dkg.Initialized}

// link the actor to an RPC by the election ID
h := NewHandler(s.mino.GetAddress(), s.service, pool, txmngr, s.signer,
handlerData, ctx, s.electionFac)
handlerData, ctx, s.electionFac, status)

no := s.mino.WithSegment(electionID)
rpc := mino.MustCreateRPC(no, RPC, h, s.factory)
Expand All @@ -135,7 +137,7 @@ func (s *Pedersen) NewActor(electionIDBuf []byte, pool pool.Pool, txmngr txn.Man
electionFac: s.electionFac,
handler: h,
electionID: electionID,
status: dkg.Status{Status: dkg.Initialized},
status: status,
log: log,
}

Expand Down Expand Up @@ -167,12 +169,12 @@ type Actor struct {
electionFac serde.Factory
handler *Handler
electionID string
status dkg.Status
status *dkg.Status
log zerolog.Logger
}

func (a *Actor) setErr(err error, args map[string]interface{}) {
a.status = dkg.Status{
*a.status = dkg.Status{
Status: dkg.Failed,
Err: err,
Args: args,
Expand Down Expand Up @@ -313,7 +315,7 @@ func (a *Actor) Setup() (kyber.Point, error) {
a.log.Info().Msgf("ok for %s", addr.String())
}

a.status = dkg.Status{Status: dkg.Setup}
*a.status = dkg.Status{Status: dkg.Setup}
evoting.PromElectionDkgStatus.WithLabelValues(a.electionID).Set(float64(dkg.Setup))

return dkgPubKeys[0], nil
Expand Down Expand Up @@ -397,7 +399,7 @@ func (a *Actor) MarshalJSON() ([]byte, error) {

// Status implements dkg.Actor
func (a *Actor) Status() dkg.Status {
return a.status
return *a.status
}

func electionExists(service ordering.Service, electionIDBuf []byte) (ordering.Proof, bool) {
Expand Down
2 changes: 1 addition & 1 deletion services/dkg/pedersen/mod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func TestPedersen_InitNonEmptyMap(t *testing.T) {

otherActor := Actor{
handler: NewHandler(fake.NewAddress(0), &fake.Service{}, &fake.Pool{},
fake.Manager{}, fake.Signer{}, handlerData, serdecontext, electionFac),
fake.Manager{}, fake.Signer{}, handlerData, serdecontext, electionFac, nil),
}

requireActorsEqual(t, actor, &otherActor)
Expand Down
28 changes: 28 additions & 0 deletions web/frontend/src/components/utils/DKGStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,34 @@ const DKGStatus: FC<DKGStatusProps> = ({ status }) => {
<div>{t('failed')}</div>
</div>
);
case NodeStatus.Dealing:
return (
<div className="flex items-center">
<div className="block h-4 w-4 bg-blue-500 rounded-full mr-2"></div>
<div>{t('dealing')}</div>
</div>
);
case NodeStatus.Responding:
return (
<div className="flex items-center">
<div className="block h-4 w-4 bg-blue-500 rounded-full mr-2"></div>
<div>{t('responding')}</div>
</div>
);
case NodeStatus.Certifying:
return (
<div className="flex items-center">
<div className="block h-4 w-4 bg-blue-500 rounded-full mr-2"></div>
<div>{t('certifying')}</div>
</div>
);
case NodeStatus.Certified:
return (
<div className="flex items-center">
<div className="block h-4 w-4 bg-green-500 rounded-full mr-2"></div>
<div>{t('certified')}</div>
</div>
);
default:
return null;
}
Expand Down
52 changes: 48 additions & 4 deletions web/frontend/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const handlers = [
? {
lastname: 'Bobster',
firstname: 'Alice',
role: UserRole.Voter,
role: UserRole.Admin,
sciper: userId,
}
: {};
Expand Down Expand Up @@ -263,16 +263,60 @@ export const handlers = [
const newDKGStatus = new Map(mockDKG.get(ElectionID as string));
let node = '';

mockElections.get(ElectionID as string).Roster.forEach((n) => {
const roster = mockElections.get(ElectionID as string).Roster;

const INCREMENT = 1200;

roster.forEach((n) => {
const p = mockNodeProxyAddresses.get(n);
if (p === body.Proxy) {
node = n;
}
});

newDKGStatus.set(node, NodeStatus.Setup);
const setup = () => {
newDKGStatus.set(node, NodeStatus.Setup);
mockDKG.set(ElectionID as string, newDKGStatus);
};

const certified = () => {
roster.forEach((n) => {
newDKGStatus.set(n, NodeStatus.Certified);
});
mockDKG.set(ElectionID as string, newDKGStatus);

setTimeout(setup, INCREMENT);
};

const certifying = () => {
roster.forEach((n) => {
newDKGStatus.set(n, NodeStatus.Certifying);
});
mockDKG.set(ElectionID as string, newDKGStatus);

setTimeout(certified, INCREMENT);
};

const responding = () => {
roster.forEach((n) => {
newDKGStatus.set(n, NodeStatus.Responding);
});
mockDKG.set(ElectionID as string, newDKGStatus);

setTimeout(certifying, INCREMENT);
};

const dealing = () => {
roster.forEach((n) => {
newDKGStatus.set(n, NodeStatus.Dealing);
});
mockDKG.set(ElectionID as string, newDKGStatus);

setTimeout(responding, INCREMENT);
};

setTimeout(dealing, INCREMENT);

setTimeout(() => mockDKG.set(ElectionID as string, newDKGStatus), SETUP_TIMER);
break;
case Action.BeginDecryption:
setTimeout(
Expand Down
50 changes: 41 additions & 9 deletions web/frontend/src/pages/election/Show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Modal from 'components/modal/Modal';
import StatusTimeline from './components/StatusTimeline';
import Loading from 'pages/Loading';
import Action from './components/Action';
import { NodeStatus } from 'types/node';
import { InternalDKGInfo, NodeStatus } from 'types/node';
import useGetResults from './components/utils/useGetResults';
import UserIDTable from './components/UserIDTable';
import DKGStatusTable from './components/DKGStatusTable';
Expand Down Expand Up @@ -42,7 +42,7 @@ const ElectionShow: FC = () => {

const [nodeProxyAddresses, setNodeProxyAddresses] = useState<Map<string, string>>(new Map());
const [nodeToSetup, setNodeToSetup] = useState<[string, string]>(null);
// The status of each node
// The status of each node. Key is the node's address.
const [DKGStatuses, setDKGStatuses] = useState<Map<string, NodeStatus>>(new Map());

const [nodeLoading, setNodeLoading] = useState<Map<string, boolean>>(null);
Expand All @@ -51,6 +51,33 @@ const ElectionShow: FC = () => {
const ongoingItem = 'ongoingAction' + electionID;
const nodeToSetupItem = 'nodeToSetup' + electionID;

// called by a DKG row
const notifyDKGState = (node: string, info: InternalDKGInfo) => {
console.log('DKG node updated:', info);
switch (info.getStatus()) {
case NodeStatus.Failed:
console.log('DKG node failed');
setOngoingAction(OngoingAction.None);
break;
case NodeStatus.Setup:
setOngoingAction(OngoingAction.None);
setStatus(Status.Setup);
break;
}

const newDKGStatuses = new Map(DKGStatuses);
newDKGStatuses.set(node, info.getStatus());
setDKGStatuses(newDKGStatuses);
console.log('dkg statuses:', DKGStatuses);
};

// called by a DKG row
const notifyLoading = (node: string, l: boolean) => {
const newLoading = new Map(nodeLoading);
newLoading.set(node, l);
setNodeLoading(newLoading);
};

// Fetch result when available after a status change
useEffect(() => {
if (status === Status.ResultAvailable && isResultAvailable) {
Expand All @@ -76,6 +103,7 @@ const ElectionShow: FC = () => {
const storedOngoingAction = JSON.parse(window.localStorage.getItem(ongoingItem));

if (storedOngoingAction !== null) {
console.log('stored ongoing action:', storedOngoingAction);
setOngoingAction(storedOngoingAction);
}

Expand Down Expand Up @@ -141,14 +169,20 @@ const ElectionShow: FC = () => {

// TODO: can be modified such that if the majority of the node are
// initialized than the election status can still be set to initialized
if (statuses.includes(NodeStatus.NotInitialized)) return;
if (statuses.includes(NodeStatus.NotInitialized)) {
setOngoingAction(OngoingAction.None);
setStatus(Status.Initial);
return;
}

if (statuses.includes(NodeStatus.Setup)) {
setOngoingAction(OngoingAction.None);
setStatus(Status.Setup);
return;
}

if (statuses.includes(NodeStatus.Unreachable)) return;
if (statuses.includes(NodeStatus.Failed)) return;

setStatus(Status.Initialized);

Expand Down Expand Up @@ -220,8 +254,6 @@ const ElectionShow: FC = () => {
setOngoingAction={setOngoingAction}
nodeToSetup={nodeToSetup}
setNodeToSetup={setNodeToSetup}
DKGStatuses={DKGStatuses}
setDKGStatuses={setDKGStatuses}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I can't suggest changes on lines that were not modified). But line 225 and 239, it would be great to add:
DKGLoading && ongoingAction === OngoingAction.None &&
And line 230 and 240:
(!DKGLoading || ongoingAction !== OngoingAction.None) &&
So that the status bar still remain visible, same with the action during the initialization and setup (it currently disappears because of the change with the DKGLoading) ?

/>
)}
</div>
Expand All @@ -240,14 +272,14 @@ const ElectionShow: FC = () => {
<DKGStatusTable
roster={roster}
electionId={electionId}
loading={nodeLoading}
setLoading={setNodeLoading}
nodeProxyAddresses={nodeProxyAddresses}
setNodeProxyAddresses={setNodeProxyAddresses}
DKGStatuses={DKGStatuses}
setDKGStatuses={setDKGStatuses}
setTextModalError={setTextModalError}
setShowModalError={setShowModalError}
ongoingAction={ongoingAction}
notifyDKGState={notifyDKGState}
nodeToSetup={nodeToSetup}
notifyLoading={notifyLoading}
/>
</div>
</div>
Expand Down
9 changes: 1 addition & 8 deletions web/frontend/src/pages/election/components/Action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import PropTypes from 'prop-types';

import { ID } from 'types/configuration';
import { OngoingAction, Status } from 'types/election';
import { NodeStatus } from 'types/node';
import useChangeAction from './utils/useChangeAction';

type ActionProps = {
Expand All @@ -19,8 +18,6 @@ type ActionProps = {
setOngoingAction: (action: OngoingAction) => void;
nodeToSetup: [string, string];
setNodeToSetup: ([node, proxy]: [string, string]) => void;
DKGStatuses: Map<string, NodeStatus>;
setDKGStatuses: (dkgStatuses: Map<string, NodeStatus>) => void;
};

const Action: FC<ActionProps> = ({
Expand All @@ -36,8 +33,6 @@ const Action: FC<ActionProps> = ({
setOngoingAction,
nodeToSetup,
setNodeToSetup,
DKGStatuses,
setDKGStatuses,
}) => {
const { getAction, modalClose, modalCancel, modalDelete, modalSetup } = useChangeAction(
status,
Expand All @@ -51,9 +46,7 @@ const Action: FC<ActionProps> = ({
ongoingAction,
setOngoingAction,
nodeToSetup,
setNodeToSetup,
DKGStatuses,
setDKGStatuses
setNodeToSetup
);

return (
Expand Down
Loading