網(wǎng)站開(kāi)發(fā)女生可以做嗎淘寶指數(shù)網(wǎng)站
以下的合約有一些復(fù)雜,但展示了很多Solidity的語(yǔ)言特性。它實(shí)現(xiàn)了一個(gè)投票合約。 當(dāng)然,電子投票的主要問(wèn)題是如何將投票權(quán)分配給正確的人員以及如何防止被操縱。 我們不會(huì)在這里解決所有的問(wèn)題,但至少我們會(huì)展示如何進(jìn)行委托投票,同時(shí),計(jì)票又是 自動(dòng)和完全透明的 。
我們的想法是為每個(gè)(投票)表決創(chuàng)建一份合約,為每個(gè)選項(xiàng)提供簡(jiǎn)稱。 然后作為合約的創(chuàng)造者——即主席,將給予每個(gè)獨(dú)立的地址以投票權(quán)。
地址后面的人可以選擇自己投票,或者委托給他們信任的人來(lái)投票。
在投票時(shí)間結(jié)束時(shí),winningProposal() 將返回獲得最多投票的提案。
代碼如下:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;/// @title 委托投票
contract Ballot {// 定義投票者struct Voter {uint weight; // 計(jì)票的權(quán)重bool voted; // 若為真,代表該人已投票address delegate; // 被委托人uint vote; // 投票提案的索引}// 定義被投票者struct Proposal {bytes32 name; // 簡(jiǎn)稱(最長(zhǎng)32個(gè)字節(jié))uint voteCount; // 得票數(shù)}address public chairperson;// 這聲明了一個(gè)狀態(tài)變量,為每個(gè)可能的地址存儲(chǔ)一個(gè) `Voter`。mapping(address => Voter) public voters;// 一個(gè) `Proposal` 結(jié)構(gòu)類型的動(dòng)態(tài)數(shù)組Proposal[] public proposals;/// 為 `proposalNames` 中的每個(gè)提案,創(chuàng)建一個(gè)新的(投票)表決constructor(bytes32[] memory proposalNames) {chairperson = msg.sender;voters[chairperson].weight = 1;//對(duì)于提供的每個(gè)提案名稱,//創(chuàng)建一個(gè)新的 Proposal 對(duì)象并把它添加到數(shù)組的末尾。for (uint i = 0; i < proposalNames.length; i++) {// `Proposal({...})` 創(chuàng)建一個(gè)臨時(shí) Proposal 對(duì)象,// `proposals.push(...)` 將其添加到 `proposals` 的末尾proposals.push(Proposal({name: proposalNames[i],voteCount: 0}));}}// 授權(quán) `voter` 對(duì)這個(gè)(投票)表決進(jìn)行投票// 只有 `chairperson` 可以調(diào)用該函數(shù)。function giveRightToVote(address voter) external {// 若 `require` 的第一個(gè)參數(shù)的計(jì)算結(jié)果為 `false`,// 則終止執(zhí)行,撤銷所有對(duì)狀態(tài)和以太幣余額的改動(dòng)。// 在舊版的 EVM 中這曾經(jīng)會(huì)消耗所有 gas,但現(xiàn)在不會(huì)了。// 使用 require 來(lái)檢查函數(shù)是否被正確地調(diào)用,是一個(gè)好習(xí)慣。// 你也可以在 require 的第二個(gè)參數(shù)中提供一個(gè)對(duì)錯(cuò)誤情況的解釋。require(msg.sender == chairperson,"Only chairperson can give right to vote.");//判斷是否已經(jīng)投過(guò)票了require(!voters[voter].voted,"The voter already voted.");require(voters[voter].weight == 0);voters[voter].weight = 1;}/// 把你的投票委托到投票者 `to`。function delegate(address to) external {// 傳引用Voter storage sender = voters[msg.sender];require(sender.weight != 0, "You have no right to vote");require(!sender.voted, "You already voted.");require(to != msg.sender, "Self-delegation is disallowed.");// 委托是可以傳遞的,只要被委托者 `to` 也設(shè)置了委托。// 一般來(lái)說(shuō),這種循環(huán)委托是危險(xiǎn)的。因?yàn)?#xff0c;如果傳遞的鏈條太長(zhǎng),// 則可能需消耗的gas要多于區(qū)塊中剩余的(大于區(qū)塊設(shè)置的gasLimit),// 這種情況下,委托不會(huì)被執(zhí)行。// 而在另一些情況下,如果形成閉環(huán),則會(huì)讓合約完全卡住。while (voters[to].delegate != address(0)) {to = voters[to].delegate;// 不允許閉環(huán)委托require(to != msg.sender, "Found loop in delegation.");}// `sender` 是一個(gè)引用, 相當(dāng)于對(duì) `voters[msg.sender].voted` 進(jìn)行修改Voter storage delegate_ = voters[to];// Voters cannot delegate to accounts that cannot vote.require(delegate_.weight >= 1);// Since `sender` is a reference, this// modifies `voters[msg.sender]`.sender.voted = true;sender.delegate = to;if (delegate_.voted) {// 若被委托者已經(jīng)投過(guò)票了,直接增加得票數(shù)proposals[delegate_.vote].voteCount += sender.weight;} else {// 若被委托者還沒(méi)投票,增加委托者的權(quán)重delegate_.weight += sender.weight;}}/// 把你的票(包括委托給你的票),/// 投給提案 `proposals[proposal].name`.function vote(uint proposal) external {Voter storage sender = voters[msg.sender];require(!sender.voted, "Already voted.");sender.voted = true;sender.vote = proposal;// 如果 `proposal` 超過(guò)了數(shù)組的范圍,則會(huì)自動(dòng)拋出異常,并恢復(fù)所有的改動(dòng)proposals[proposal].voteCount += sender.weight;}/// @dev 結(jié)合之前所有的投票,計(jì)算出最終勝出的提案function winningProposal() public view returns (uint winningProposal_){uint winningVoteCount = 0;for (uint p = 0; p < proposals.length; p++) {if (proposals[p].voteCount > winningVoteCount) {winningVoteCount = proposals[p].voteCount;winningProposal_ = p;}}} // 計(jì)算獲勝的提案// function winningProposal() public view returns (uint winningProposal) {// uint winningVoteCount = 0;// for (uint p = 0; p < proposals.length; p++) {// if (proposals[p].voteCount > winningVoteCount) {// winningVoteCount = proposals[p].voteCount;// winningProposal = p;// }// }// }// 調(diào)用 winningProposal() 函數(shù)以獲取提案數(shù)組中獲勝者的索引,并以此返回獲勝者的名稱//winnerName 函數(shù)調(diào)用 winningProposal() 函數(shù),并使用其返回值來(lái)訪問(wèn) proposals 數(shù)組。function winnerName() public view returns (bytes32 winnerName_){winnerName_ = proposals[winningProposal()].name;}}
測(cè)試
在你的合約中,構(gòu)造函數(shù)需要一個(gè)參數(shù) proposalNames,它是一個(gè) bytes32 數(shù)組。因此,你需要在部署時(shí)提供這個(gè)參數(shù)。
在“Deploy & Run Transactions”面板中,找到“Deploy”按鈕下方的輸入框并輸入提案名稱列表。
例如,你可以輸入以下內(nèi)容:
["0x50726f706f73616c310000000000000000000000000000000000000000000000", "0x50726f706f73616c320000000000000000000000000000000000000000000000"]
部署成功:
查看生成的賬戶:
在 Deploy & Run Transactions 面板中,你會(huì)看到一個(gè) ACCOUNT 下拉菜單。這個(gè)菜單中列出了所有生成的賬戶地址及其余額。你可以從中選擇一個(gè)地址進(jìn)行測(cè)試。
選擇和復(fù)制賬戶地址:
點(diǎn)擊 ACCOUNT 下拉菜單,選擇一個(gè)賬戶。
復(fù)制選中的賬戶地址。
- 授權(quán)投票權(quán) (giveRightToVote)
在 giveRightToVote 的輸入框中輸入授權(quán)投票者的地址。
示例:
2. 委托投票 (delegate)
在 delegate 的輸入框中輸入被委托人的地址(可以是同一個(gè)賬戶或其他賬戶)
這里測(cè)試發(fā)現(xiàn)輸入的都會(huì)這個(gè)提示,這個(gè)待驗(yàn)證處理
- 投票 (vote)
在 vote 的輸入框中輸入提案索引。
4. 查看主席 (chairperson)
點(diǎn)擊 chairperson 按鈕查看合約的主席地址。
5. 查看提案信息 (proposals)
在 proposals 的輸入框中輸入提案索引。
點(diǎn)擊 proposals 按鈕查看提案的名稱和票數(shù)。
6. 查看投票者信息 (voters)
在 voters 的輸入框中輸入投票者的地址。
7. 獲勝提案名稱 (winnerName)
點(diǎn)擊 winnerName 按鈕查看當(dāng)前獲勝提案的名稱。
8. 計(jì)算獲勝提案 (winningProposal)
點(diǎn)擊 winningProposal 按鈕查看當(dāng)前獲勝提案的索引。