Une dapp pour un contrat de vote en solidiy, par Guilhain Averlant & Pierre-Olivier Mauguet.
Déploiement sur voting-dapp-sage.vercel.app.
Vidéo de démonstration.
Le code de ce repository utilise un template issu de la box Truffle officielle pour React (référénce).
git clone https://github.com/pom421/VotingDapp.git
cd VotingDapp
npm install
ganache
)npm run truffle:migrate
npm run client:start
Pour l'exemple, nous allons déployer le contrat sur Sepolia (mais il existe les mêmes scripts pour déployer sur goerli et mumbai).
Cloner le fichier .env.dist
en .env
dans le répertoire truffle et remplir les variables d'environnement.
npm run truffle:migrate:sepolia
Le fichier .env
doit être correctement configuré (i. e. avoir la mnémonique d'un wallet fourni en ETH sepolia).
Pour d'autres réseaux, ajouter la configuration dans truffle-config.js et le script dans package.json.
Afin que le front se déploie bien sur Vercel, nous avons dû procéder à de légère customisation dans le dashboard.
npm run truffle:migrate:sepolia
.Aller sur https://sepolia-faucet.pk910.de/.
Pour des raisons de sécurité, nous avons supprimé la function tallyVotes
car elle présentait une faille de sécurité de
type DOS GAS LIMIT. Puisque si un attaquant publie un nombre important de proposals
, la boucle qui calcule les
résultats du vote peut atteindre la limite de gas autorisée par le réseau.
Pour combler cette faille de sécurité, il a été décidé de déplacer le calcul de la proposal
victorieuse dans la
fonction setVote
. Ainsi à chaque fois qu'un votant vote, la variable winningProposalID
est mise à jour en comparant
la valeur voteCount
stockée dans la struct Proposal
.
setVote
).Proposal
ne nous parait pas pertinente, car rien ne justifie sur une
application de vote de limiter le nombre de candidatures.Les optimisations suite à la suprression de TallyVotes
La suppression de la fonction tallyVotes
invite à faire certaines modifications.
VotesTallied
de l'enum WorkflowStatus
n'a plus de sens, elle a donc été supprimée.Proposal
dans un mapping
plutôt que dans un array
.
Du coup on est obligé en contre partie d'ajouter une variable de type uint proposalID
pour stocker l'identifiant de
chaque proposal (utile également dans les require
utilisés dans les fonction setVote
et getOneProposal
).Quid : propsoal GENESIS ?
On a gardé la notion de proposal GENESIS tel qu'implémenté dans la version initiale du code.
Et la consomation en gas ?
Vous trouverez ci-dessous les rapports de consomations de gas des sessions de tests de Voting.sol :
Contract Method | Min | Max | Avg | # calls |
---|---|---|---|---|
Voting addProposal | - | - | 59316 | 40 |
Voting addVoter | - | - | 50220 | 38 |
Voting endProposalsRegistering | - | - | 30599 | 40 |
Voting endVotingSession | - | - | 30533 | 39 |
Voting setVote | 60913 | 78013 | 61727 | 42 |
Voting startProposalsRegistering | - | - | 95032 | 4 |
Voting startVotingSession | - | - | 30554 | 4 |
Voting tallyVotes | - | - | 66469 | 39 |
Voting | - | - | 2 077 402 | 30.9 % |
Contract Method | Min | Max | Avg | # calls |
---|---|---|---|---|
Voting addProposal | - | - | 59316 | 37 |
Voting addVoter | - | - | 50220 | 35 |
Voting endProposalsRegistering | - | - | 30577 | 37 |
Voting endVotingSession | - | - | 30599 | 36 |
Voting setVote | 63633 | 102747 | 65639 | 39 |
Voting startProposalsRegistering | - | - | 95010 | 4 |
Voting startVotingSession | - | - | 30554 | 4 |
Voting | - | - | 1 983 006 | 29.5 % |
Contract Method | Min | Max | Avg | # calls |
---|---|---|---|---|
Voting addProposal | 59388 | 76488 | 60312 | 37 |
Voting addVoter | - | - | 50220 | 35 |
Voting endProposalsRegistering | - | - | 30577 | 37 |
Voting endVotingSession | - | - | 30599 | 36 |
Voting setVote | 63289 | 102403 | 65295 | 39 |
Voting startProposalsRegistering | - | - | 72863 | 4 |
Voting startVotingSession | - | - | 30554 | 4 |
Voting | - | 1 967 236 | 29.3 % |
On observe que le stockage dans un mapping consomme moins de gas dans sa globalité.
Il a fallu créer une variable uint proposalID
pour stocker les identifiants de Proposal
. Par conséquent,
nous avons deux variables uint, ce qui rend possible une petite optimisation en gas en restreignant la taille des uint
(par défaut uint256
) en uint64
, ce qui techniquement restreint le nombre de proposal à (2^64)-1 soit
18446744073709551615 proposals
, ce qui nous semble raisonnable.
Voici le résultat des tests en gas
Contract Method | Min | Max | Avg | # calls |
---|---|---|---|---|
Voting addProposal | 59941 | 77041 | 60865 | 37 |
Voting addVoter | - | - | 50220 | 35 |
Voting endProposalsRegistering | - | - | 30577 | 37 |
Voting endVotingSession | - | - | 30599 | 36 |
Voting setVote | 39956 | 62123 | 41093 | 39 |
Voting startProposalsRegistering | - | - | 72936 | 4 |
Voting startVotingSession | - | - | 30554 | 4 |
Voting | - | - | 2200063 | 32.7 % |
Nous observons une sur-consomation en gas cette option ne sera pas retenue.
Documentation générée à partir des commentaires NatSpec du code Solidity.0
For NatSpec see smart contract Voting.sol
This is a smart contract for conducting a voting process using blockchain technology. It allows voters to register, submit proposals, and vote on proposals. The voting process has multiple stages that are controlled by the contract owner. Voters can only vote once and can only vote on proposals that have been registered. The winning proposal is the one with the highest number of votes.
setVote(uint _id): allows a registered voter to vote for a proposal with a given ID.