Skip to content

Commit 1cf8649

Browse files
committed
Adds the functionality to delete an election
Adds the functionality to delete an election. Even if everything is on the blockchain, deleting an election will remove it from the global state. Updates all components (smart contract, proxy, web-backend, web-frontend, API doc) accordingly.
1 parent d431e0e commit 1cf8649

File tree

12 files changed

+321
-10
lines changed

12 files changed

+321
-10
lines changed

contracts/evoting/controller/action.go

+1
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ func (a *RegisterAction) Execute(ctx node.Context) error {
164164
router.HandleFunc("/evoting/elections/{electionID}", ep.Election).Methods("GET")
165165
router.HandleFunc("/evoting/elections/{electionID}", ep.EditElection).Methods("PUT")
166166
router.HandleFunc("/evoting/elections/{electionID}", eproxy.AllowCORS).Methods("OPTIONS")
167+
router.HandleFunc("/evoting/elections/{electionID}", ep.DeleteElection).Methods("DELETE")
167168
router.HandleFunc("/evoting/elections/{electionID}/vote", ep.NewElectionVote).Methods("POST")
168169

169170
router.NotFoundHandler = http.HandlerFunc(eproxy.NotFoundHandler)

contracts/evoting/evoting.go

+56
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contracts/evoting/json/transaction.go

+17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package json
22

33
import (
44
"encoding/json"
5+
56
"github.com/dedis/d-voting/contracts/evoting/types"
67
"go.dedis.ch/dela/serde"
78
"golang.org/x/xerrors"
@@ -111,6 +112,12 @@ func (transactionFormat) Encode(ctx serde.Context, msg serde.Message) ([]byte, e
111112
}
112113

113114
m = TransactionJSON{CancelElection: &ce}
115+
case types.DeleteElection:
116+
de := DeleteElectionJSON{
117+
ElectionID: t.ElectionID,
118+
}
119+
120+
m = TransactionJSON{DeleteElection: &de}
114121
default:
115122
return nil, xerrors.Errorf("unknown type: '%T", msg)
116123
}
@@ -178,6 +185,10 @@ func (transactionFormat) Decode(ctx serde.Context, data []byte) (serde.Message,
178185
ElectionID: m.CancelElection.ElectionID,
179186
UserID: m.CancelElection.UserID,
180187
}, nil
188+
case m.DeleteElection != nil:
189+
return types.DeleteElection{
190+
ElectionID: m.DeleteElection.ElectionID,
191+
}, nil
181192
}
182193

183194
return nil, xerrors.Errorf("empty type: %s", data)
@@ -194,6 +205,7 @@ type TransactionJSON struct {
194205
RegisterPubShares *RegisterPubSharesJSON `json:",omitempty"`
195206
CombineShares *CombineSharesJSON `json:",omitempty"`
196207
CancelElection *CancelElectionJSON `json:",omitempty"`
208+
DeleteElection *DeleteElectionJSON `json:",omitempty"`
197209
}
198210

199211
// CreateElectionJSON is the JSON representation of a CreateElection transaction
@@ -251,6 +263,11 @@ type CancelElectionJSON struct {
251263
UserID string
252264
}
253265

266+
// DeleteElectionJSON is the JSON representation of a DeleteElection transaction
267+
type DeleteElectionJSON struct {
268+
ElectionID string
269+
}
270+
254271
func decodeCastVote(ctx serde.Context, m CastVoteJSON) (serde.Message, error) {
255272
factory := ctx.GetFactory(types.CiphervoteKey{})
256273
if factory == nil {

contracts/evoting/mod.go

+10
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type commands interface {
5555
registerPubshares(snap store.Snapshot, step execution.Step) error
5656
combineShares(snap store.Snapshot, step execution.Step) error
5757
cancelElection(snap store.Snapshot, step execution.Step) error
58+
deleteElection(snap store.Snapshot, step execution.Step) error
5859
}
5960

6061
// Command defines a type of command for the value contract
@@ -72,12 +73,16 @@ const (
7273
// CmdShuffleBallots is the command to shuffle ballots
7374
CmdShuffleBallots Command = "SHUFFLE_BALLOTS"
7475

76+
// CmdRegisterPubShares is the command to register the pubshares
7577
CmdRegisterPubShares Command = "REGISTER_PUB_SHARES"
7678

7779
// CmdCombineShares is the command to decrypt ballots
7880
CmdCombineShares Command = "COMBINE_SHARES"
7981
// CmdCancelElection is the command to cancel an election
8082
CmdCancelElection Command = "CANCEL_ELECTION"
83+
84+
// CmdDelteElection is the command to delete an election
85+
CmdDelteElection Command = "DELETE_ELECTION"
8186
)
8287

8388
// NewCreds creates new credentials for a evoting contract execution. We might
@@ -200,6 +205,11 @@ func (c Contract) Execute(snap store.Snapshot, step execution.Step) error {
200205
if err != nil {
201206
return xerrors.Errorf("failed to cancel election: %v", err)
202207
}
208+
case CmdDelteElection:
209+
err := c.cmd.deleteElection(snap, step)
210+
if err != nil {
211+
return xerrors.Errorf("failed to delete election: %v", err)
212+
}
203213
default:
204214
return xerrors.Errorf("unknown command: %s", cmd)
205215
}

contracts/evoting/mod_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,10 @@ func (c fakeCmd) cancelElection(snap store.Snapshot, step execution.Step) error
13461346
return c.err
13471347
}
13481348

1349+
func (c fakeCmd) deleteElection(snap store.Snapshot, step execution.Step) error {
1350+
return c.err
1351+
}
1352+
13491353
func (c fakeCmd) registerPubshares(snap store.Snapshot, step execution.Step) error {
13501354
return c.err
13511355
}

contracts/evoting/types/transactions.go

+34-6
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,20 @@ type ElectionsMetadata struct {
2929
// ElectionIDs is a slice of hex-encoded election IDs
3030
type ElectionIDs []string
3131

32-
// Contains checks if el is present
33-
func (e ElectionIDs) Contains(el string) bool {
34-
for _, e1 := range e {
32+
// Contains checks if el is present. Return < 0 if not.
33+
func (e ElectionIDs) Contains(el string) int {
34+
for i, e1 := range e {
3535
if e1 == el {
36-
return true
36+
return i
3737
}
3838
}
3939

40-
return false
40+
return -1
4141
}
4242

4343
// Add adds an election ID or returns an error if already present
4444
func (e *ElectionIDs) Add(id string) error {
45-
if e.Contains(id) {
45+
if e.Contains(id) >= 0 {
4646
return xerrors.Errorf("id %q already exist", id)
4747
}
4848

@@ -51,6 +51,14 @@ func (e *ElectionIDs) Add(id string) error {
5151
return nil
5252
}
5353

54+
// Remove removes an election ID from the list, if it exists
55+
func (e *ElectionIDs) Remove(id string) {
56+
i := e.Contains(id)
57+
if i >= 0 {
58+
*e = append((*e)[:i], (*e)[i+1:]...)
59+
}
60+
}
61+
5462
// TransactionFactory provides the mean to deserialize a transaction.
5563
//
5664
// - implements serde.Factory
@@ -267,6 +275,26 @@ func (ce CancelElection) Serialize(ctx serde.Context) ([]byte, error) {
267275
return data, nil
268276
}
269277

278+
// DeleteElection defines the transaction to delete the election
279+
//
280+
// - implements serde.Message
281+
type DeleteElection struct {
282+
// ElectionID is hex-encoded
283+
ElectionID string
284+
}
285+
286+
// Serialize implements serde.Message
287+
func (ce DeleteElection) Serialize(ctx serde.Context) ([]byte, error) {
288+
format := transactionFormats.Get(ctx.GetFormat())
289+
290+
data, err := format.Encode(ctx, ce)
291+
if err != nil {
292+
return nil, xerrors.Errorf("failed to encode cancel election: %v", err)
293+
}
294+
295+
return data, nil
296+
}
297+
270298
// RandomID returns the hex encoding of a randomly created 32 byte ID.
271299
func RandomID() (string, error) {
272300
buf := make([]byte, 32)

docs/api.md

+26
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ SC6:CombineShares
5656
5757
SC2:ElectionGetInfo
5858
59+
60+
5961
```
6062

6163
In case of error:
@@ -267,6 +269,30 @@ Return:
267269
268270
```
269271

272+
# SC?: Election delete
273+
274+
| | |
275+
| ------- | --------------------------------- |
276+
| URL | `/evoting/elections/{ElectionID}` |
277+
| Method | `DELETE` |
278+
| Input | |
279+
| Headers | {Authorization: <token>} |
280+
281+
The <token> value must be the hex-encoded signature on the hex-encoded
282+
electionID:
283+
284+
```
285+
<token> = hex( sig( hex( electionID ) ) )
286+
```
287+
288+
Return:
289+
290+
`200 OK` `text/plain`
291+
292+
```
293+
294+
```
295+
270296
# SC?: Election get all infos
271297

272298
| | |

proxy/election.go

+63-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"go.dedis.ch/dela/core/txn/pool"
2323
"go.dedis.ch/dela/serde"
2424
"go.dedis.ch/kyber/v3"
25+
"go.dedis.ch/kyber/v3/sign/schnorr"
2526
"golang.org/x/xerrors"
2627
)
2728

@@ -147,7 +148,7 @@ func (h *election) NewElectionVote(w http.ResponseWriter, r *http.Request) {
147148
return
148149
}
149150

150-
if !elecMD.ElectionsIDs.Contains(electionID) {
151+
if elecMD.ElectionsIDs.Contains(electionID) < 0 {
151152
http.Error(w, "the election does not exist", http.StatusNotFound)
152153
return
153154
}
@@ -214,7 +215,7 @@ func (h *election) EditElection(w http.ResponseWriter, r *http.Request) {
214215
return
215216
}
216217

217-
if !elecMD.ElectionsIDs.Contains(electionID) {
218+
if elecMD.ElectionsIDs.Contains(electionID) < 0 {
218219
http.Error(w, "the election does not exist", http.StatusNotFound)
219220
return
220221
}
@@ -453,6 +454,61 @@ func (h *election) Elections(w http.ResponseWriter, r *http.Request) {
453454
}
454455
}
455456

457+
// DeleteElection implements proxy.Proxy
458+
func (h *election) DeleteElection(w http.ResponseWriter, r *http.Request) {
459+
vars := mux.Vars(r)
460+
461+
if vars == nil || vars["electionID"] == "" {
462+
http.Error(w, fmt.Sprintf("electionID not found: %v", vars), http.StatusInternalServerError)
463+
return
464+
}
465+
466+
electionID := vars["electionID"]
467+
468+
elecMD, err := h.getElectionsMetadata()
469+
if err != nil {
470+
http.Error(w, "failed to get election metadata", http.StatusNotFound)
471+
return
472+
}
473+
474+
if elecMD.ElectionsIDs.Contains(electionID) < 0 {
475+
http.Error(w, "the election does not exist", http.StatusNotFound)
476+
return
477+
}
478+
479+
// auth should contain the hex-encoded signature on the hex-encoded election
480+
// ID
481+
auth := r.Header.Get("Authorization")
482+
483+
sig, err := hex.DecodeString(auth)
484+
if err != nil {
485+
BadRequestError(w, r, xerrors.Errorf("failed to decode auth: %v", err), nil)
486+
return
487+
}
488+
489+
err = schnorr.Verify(suite, h.pk, []byte(electionID), sig)
490+
if err != nil {
491+
ForbiddenError(w, r, xerrors.Errorf("signature verification failed: %v", err), nil)
492+
return
493+
}
494+
495+
deleteElection := types.DeleteElection{
496+
ElectionID: electionID,
497+
}
498+
499+
data, err := deleteElection.Serialize(h.context)
500+
if err != nil {
501+
InternalError(w, r, xerrors.Errorf("failed to marshal DeleteElection: %v", err), nil)
502+
return
503+
}
504+
505+
_, err = h.submitAndWaitForTxn(r.Context(), evoting.CmdDelteElection, evoting.ElectionArg, data)
506+
if err != nil {
507+
http.Error(w, "failed to submit txn: "+err.Error(), http.StatusInternalServerError)
508+
return
509+
}
510+
}
511+
456512
// waitForTxnID blocks until `ID` is included or `events` is closed.
457513
func (h *election) waitForTxnID(events <-chan ordering.Event, ID []byte) error {
458514
for event := range events {
@@ -483,6 +539,11 @@ func (h *election) getElectionsMetadata() (types.ElectionsMetadata, error) {
483539
return md, nil
484540
}
485541

542+
// if there is not election created yet the metadata will be empty
543+
if len(proof.GetValue()) == 0 {
544+
return types.ElectionsMetadata{}, nil
545+
}
546+
486547
err = json.Unmarshal(proof.GetValue(), &md)
487548
if err != nil {
488549
return md, xerrors.Errorf("failed to unmarshal ElectionMetadata: %v", err)

0 commit comments

Comments
 (0)