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