以太坊DAO之流動民主智慧合約
Decentralized Autonomous Organization,簡稱DAO,以太坊中重要的概念。一般翻譯為去中心化的自治組織。
投票支付合約的所有費用和行動需要時間,並要求使用者始終保持活躍,知情和專注。另一個有趣的方法是選擇一個可以控制合約的指定賬戶,然後能夠迅速做出決定。
流動民主合約
我們將實施一種通常稱為流動民主Liquid Democracy
的版本,這是一種更靈活的代議制民主。在這種民主制度下,任何選民都可以成為潛在的代表:你只需說出你信任哪位選民就可以為你處理這一決定。你的投票權重被委託給他們,他們可以將其委託給他們信任的另一個選民,依此類推。最終結果應該是投票最多的賬戶是與最大數量的選民有信任關係的賬戶。
程式碼:
pragma solidity >=0.4.22 <0.6.0; contract token { mapping (address => uint256) public balanceOf; } contract LiquidDemocracy { token public votingToken; bool underExecution; address public appointee; mapping (address => uint) public voterId; mapping (address => uint256) public voteWeight; uint public delegatedPercent; uint public lastWeightCalculation; uint public numberOfDelegationRounds; uint public numberOfVotes; DelegatedVote[] public delegatedVotes; string public forbiddenFunction; event NewAppointee(address newAppointee, bool changed); struct DelegatedVote { address nominee; address voter; } /** * Constructor */ constructor( address votingWeightToken, string memory forbiddenFunctionCall, uint percentLossInEachRound ) public { votingToken = token(votingWeightToken); delegatedVotes.length++; delegatedVotes[0] = DelegatedVote({nominee: address(0), voter: address(0)}); forbiddenFunction = forbiddenFunctionCall; delegatedPercent = 100 - percentLossInEachRound; if (delegatedPercent > 100) delegatedPercent = 100; } /** * Vote for an address * * Send your vote weight to another address * * @param nominatedAddress the destination address receiving the sender's vote */ function vote(address nominatedAddress) public returns (uint voteIndex) { if (voterId[msg.sender]== 0) { voterId[msg.sender] = delegatedVotes.length; numberOfVotes++; voteIndex = delegatedVotes.length++; numberOfVotes = voteIndex; } else { voteIndex = voterId[msg.sender]; } delegatedVotes[voteIndex] = DelegatedVote({nominee: nominatedAddress, voter: msg.sender}); return voteIndex; } /** * Perform Executive Action * * @param target the destination address to interact with * @param valueInWei the amount of ether to send along with the transaction * @param bytecode the data bytecode for the transaction */ function execute(address target, uint valueInWei, bytes32 bytecode) public { require(msg.sender == appointee // If caller is the current appointee, && !underExecution // if the call is being executed, && bytes4(bytecode) != bytes4(keccak256(abi.encodePacked(forbiddenFunction))) // and it's not trying to do the forbidden function && numberOfDelegationRounds >= 4); // and delegation has been calculated enough underExecution = true; (bool success, ) = target.call.value(valueInWei)(abi.encode(bytecode)); // Then execute the command. require(success); underExecution = false; } /** * Calculate Votes * * Go thruogh all the delegated vote logs and tally up each address's total rank */ function calculateVotes() public returns (address winner) { address currentWinner = appointee; uint currentMax = 0; uint weight = 0; DelegatedVote storage v = delegatedVotes[0]; if (now > lastWeightCalculation + 90 minutes) { numberOfDelegationRounds = 0; lastWeightCalculation = now; // Distribute the initial weight for (uint i=1; i< delegatedVotes.length; i++) { voteWeight[delegatedVotes[i].nominee] = 0; } for (uint i=1; i< delegatedVotes.length; i++) { voteWeight[delegatedVotes[i].voter] = votingToken.balanceOf(delegatedVotes[i].voter); } } else { numberOfDelegationRounds++; uint lossRatio = 100 * delegatedPercent ** numberOfDelegationRounds / 100 ** numberOfDelegationRounds; if (lossRatio > 0) { for (uint i=1; i< delegatedVotes.length; i++){ v = delegatedVotes[i]; if (v.nominee != v.voter && voteWeight[v.voter] > 0) { weight = voteWeight[v.voter] * lossRatio / 100; voteWeight[v.voter] -= weight; voteWeight[v.nominee] += weight; } if (numberOfDelegationRounds>3 && voteWeight[v.nominee] > currentMax) { currentWinner = v.nominee; currentMax = voteWeight[v.nominee]; } } } } if (numberOfDelegationRounds > 3) { emit NewAppointee(currentWinner, appointee == currentWinner); appointee = currentWinner; } return currentWinner; } }
部署
首先,你需要一個代幣。如果你已按照上面的股東協會Shareholder association
教程,你可以使用與之前相同的代幣,否則只需部署新代幣並在某些帳戶中分配。複製代幣地址。
部署民主合約,並將代幣地址放在投票權重代幣上 ,將75作為每輪中的百分比損失,並將轉讓所有權(地址)(沒有任何空格或額外字元!)作為禁止功能 。
選擇代表
現在部署Liquid民主並轉到其頁面。首先,任何股東都會投票決定他們信任誰代表本合約做出決定。如果你想成為最終決策者,你可以自己投票,或者如果你寧願沒有人代表你擔任這個角色,你可以在零地址上投票。
在足夠多的人投票後,你可以執行計算投票功能,以便計算每個人的投票權重。這個功能需要多次執行,所以第一次執行它只會將每個人的體重設定為所選代幣中的餘額。在下一輪中,投票權重將轉到你投票指定的人,在下一輪它會去由你選擇的人投票的人等等。為了防止無限迴圈的投票授權,每次投票都會轉發,它會失去一些權力,在percentLossInEachRound合約啟動時設定。因此,如果損失設定為75%,則意味著你投票的人獲得了100%的體重,但如果他們將投票委託給其他人,則只有75%的體重被轉發。那個人可以委託給別人,但他們只能獲得56%的投票權,依此類推。如果比率低於100%,將會有一個有限的時刻,重新計算投票授權將不再改變結果,但如果它是100%,則意味著投票權重將簡單地圍繞任何潛在的迴圈進行迴圈。
如果這一輪呼叫計算投票開始已超過一個半小時,所有權重將重置並將根據原始代幣餘額重新計算,因此如果你最近收到更多代幣,則應再次執行此功能。
眾議機構
投票代表團的所有優點是什麼?例如,你可以在關聯上使用它而不是代幣權重。首先,獲取股東協會的程式碼,但替換描述代幣的第一行:
contract Token {
mapping (address => uint256) public balanceOf;
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
}
進入這個:
contract Token {
mapping (address => uint256) public voteWeight;
uint public numberOfDelegationRounds;
function balanceOf(address member) public view returns (uint256 balance) {
if (numberOfDelegationRounds < 3)
return 0;
else
return this.voteWeight(member);
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
}
在編寫合約時,你可以描述主合約使用的多個其他合約。有些可能是已在目標合約上定義的函式和變數,如voteWeight
和numberOfDelegationRounds
。但請注意,balanceOf
是一個新功能,它在Liquid Democracy
或Association
合約上都不存在,我們現在正在定義它,作為一個函式,如果至少計算了三輪,它將返回voteWeight
。
使用Liquid democracy
作為代幣地址而不是原始代幣,並像往常一樣繼續部署股東協會。就像以前一樣,使用者可以針對這些問題建立新的提案或投票,但現在,我們使用委託流程,不是使用代幣餘額作為投票權。所以,如果你是一個代幣持有者,你可以選擇一個你信任的人並指定他們,而不是讓你自己不斷地通知所有問題,然後他們可以選擇他們信任的人:結果就是你的代表,而不是被限制在給定的任意地理位置附近,將是你社交距離的人。
此外,這意味著你可以隨時切換投票:如果你的代表在某些問題上投票反對你的利益,你可以在提案投票結算之前,轉換你的被任命者,或者只是選擇代表你自己處理問題並投自己投票。
行政部門
代議制民主國家是選擇代表的一種很好的方式,但對於一些重要或簡單的決策,對個別提案進行投票可能太慢:這就是為什麼大多數民主政府都有一個行政部門,而被任命的人有權代表國家。
在四輪代表之後,更多權重的地址將被設定為被任命者。如果有許多委託投票,那麼可能需要再多幾輪計算投票才能在最終指定的地址中解決。
被任命者是唯一可以呼叫Execute函式的地址,它可以執行(幾乎)任何代表整個民主的函式。如果液體民主合約中存有任何以太或代幣,被任命者將被允許在任何地方移動。
如果你遵循我們的示例並使用此流動民主作為代幣建立了股東協會 ,那麼你應該能夠以有趣的方式使用行政部門:轉到主協會地址並執行轉移所有權功能到流動民主。
傳輸完成後,將功能切換為更改投票規則。這允許你更改一些基本的投票規則,例如投票通過所需的最低法定人數或新提案需要留在場上的時間。嘗試更改這些設定並單擊執行:當彈出確認視窗時,它將告訴你該交易無法執行。當然,這種情況發生,因為只有設定為所有者的地址才能更改這些設定,合約將拒絕此交易嘗試。因此,不是鍵入密碼而是複製資料欄位上的程式碼並將其儲存到文字檔案中。單擊取消,滾動到頂部並單擊複製地址,並將其儲存到文字檔案。
現在轉到Liquid民族頁面並選擇執行。在目標上放置關聯合約的地址,將ether amount
保留為0並將先前複製的程式碼貼上到位元組碼資料欄位中。確保你從作為被任命者設定的帳戶執行它,然後單擊執行。
一旦交易被收回,Liquid民主將把訂單傳遞給協會,並且新的投票規則可能適用。被任命者有絕對的權力去做液體民主合約可以執行的任何事情。你可以使用相同的技術建立委派民主所擁有的Mintable代幣,然後允許被任命者填寫代幣或凍結帳戶。
為了防止濫用權力,你可以設定一個被禁止的人無法做的禁止功能。如果你按照我們的例子,禁止的功能是transferOwnership
(地址),以防止被任命者將協會的所有權轉讓給他們自己(在政治上,當總統利用他的行政權力轉移給自己以前屬於總統,這是政變或貪汙)。
======================================================================
分享一些以太坊、EOS、比特幣等區塊鏈相關的互動式線上程式設計實戰教程:
- java以太坊開發教程,主要是針對java和android程式設計師進行區塊鏈以太坊開發的web3j詳解。
- python以太坊,主要是針對python工程師使用web3.py進行區塊鏈以太坊開發的詳解。
- php以太坊,主要是介紹使用php進行智慧合約開發互動,進行賬號建立、交易、轉賬、代幣開發以及過濾器和交易等內容。
- 以太坊入門教程,主要介紹智慧合約與dapp應用開發,適合入門。
- 以太坊開發進階教程,主要是介紹使用node.js、mongodb、區塊鏈、ipfs實現去中心化電商DApp實戰,適合進階。
- C#以太坊,主要講解如何使用C#開發基於.Net的以太坊應用,包括賬戶管理、狀態與交易、智慧合約開發與互動、過濾器和交易等。
- EOS教程,本課程幫助你快速入門EOS區塊鏈去中心化應用的開發,內容涵蓋EOS工具鏈、賬戶與錢包、發行代幣、智慧合約開發與部署、使用程式碼與智慧合約互動等核心知識點,最後綜合運用各知識點完成一個便籤DApp的開發。
- java比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Java程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Java工程師不可多得的比特幣開發學習課程。
- php比特幣開發教程,本課程面向初學者,內容即涵蓋比特幣的核心概念,例如區塊鏈儲存、去中心化共識機制、金鑰與指令碼、交易與UTXO等,同時也詳細講解如何在Php程式碼中整合比特幣支援功能,例如建立地址、管理錢包、構造裸交易等,是Php工程師不可多得的比特幣開發學習課程。
- tendermint區塊鏈開發詳解,本課程適合希望使用tendermint進行區塊鏈開發的工程師,課程內容即包括tendermint應用開發模型中的核心概念,例如ABCI介面、默克爾樹、多版本狀態庫等,也包括代幣發行等豐富的實操程式碼,是go語言工程師快速入門區塊鏈開發的最佳選擇。
匯智網原創翻譯,轉載請標明出處。這裡是原文以太坊DAO之流動民主智慧合約