Implementing an unbiased off-chain voting process using Lilypad and Bacalhau, with Drand Time-lock encryption
This example illustrates how DeFi Kicks leverages Lilypad, Bacalhau, and Drand to implement an unbiased off-chain voting mechanism. The purpose of using these technologies is to ensure that voting happens in an unbiased, decentralized manner, thus promoting true democracy within the DefiKicks ecosystem.
The contracts and docker images have also been added as an example in Lilypad Github
For more info on DeFi Kicks and their use of Lilypad, see the DefiKicks Github
The DeFi Kicks project utilizes a three-step mechanism to ensure an unbiased voting system. Here's a summarized outline of how it operates:
The voting process is performed off-chain to maintain efficiency and privacy. Users cast their votes by encrypting them on the front end, signing the message for authenticity, and then sending them to be stored. Time-lock encryption is utilized, making the vote only decryptable after a predetermined amount of time, which ensures fairness and protects against any attempts to tamper with the votes.
Once the voting phase for a proposal has ended, anyone can request a vote resolution. This action requires a certain fee and can only be performed if the vote is in the ResolutionToRequest state. This fee covers the usage of Lilypad and Bacalhau for off-chain vote resolution.
Once a vote resolution request has been submitted and the necessary fee paid, Bacalhau, via Lilypad, takes care of the off-chain resolution. It processes the vote data, decrypting the votes when the predetermined time has elapsed, and generates the final voting results. This off-chain resolution ensures a high level of security and accuracy, providing an unbiased outcome for the voting process.
In summary, this mechanism leverages the strengths of off-chain voting and resolution in a decentralized environment, guaranteeing privacy, fairness, and tamper resistance in the voting process, which are all critical to maintaining the trust and integrity of the DeFi Kicks project.
Vote details are packaged into a message object and signed cryptographically. This message is then encrypted using a time-lock mechanism, which ensures decryption only after a certain time. The encrypted vote, along with the user's account details and signature, are stored as a vote object. Lastly, the new vote is stored and the system is notified.
The following code snippet shows how the off-chain voting mechanism is implemented in the frontend of the DefiKicks application. The original code can be found in the DefiKicks Github
Once the off-chain voting phase has ended anyone can request it's resolution by calling the requestVoteResolution
function in the Governor smart contract. This function in turn will trigger a Lilypad job that will fetches all the votes and resolve the voting process.
The Lilypad job is defined as docker image that runs a javascript script that fetches all the votes and resolves the voting process. We will go into the details of the script in a later section.
The Lilypad job spec is dinamically built to send the proposal ID as environment variable together with a node url used to fetch the vote power of each voter. The node url is also used to fetch the proposal details from the chain.
See the original smart contract for more details.
The script run in Bacalhau perform the following steps:
Retrieve all the off-chain votes from a decentralised storage (Ceramic stream or IPFS)
Decrypt the votes using Drand timelock decryption function
Verify the signature of the decrypted vote
Compute the vote power of each voter
Compute the vote result using the vote power of each voter
Compute the inflationary rewards for each voter that voted for the winning option
Put the reward information in a Merkle tree and compute the Merkle root together with the proof for each voter
Encode all the information in a hex string and console.log it as this is the stdout that will be sent back to the Governor smart contract by the Bacalhau operators that run the job.
Here is the vote decryption and verification code, for the full script see the original script
Once the job is picked up by the Bacalhau operators and executed, the resolutions are brought on-chain and the Governor smart contract receives a lilypadFulfilled
callback from Lilypad.
The _result
string contains all the vote resolution information encoded in a hex string. The string is decoded and the resolution information is stored in the smart contract.
For more information on how the result is decoded see the original smart contract