Skip to content

Instantly share code, notes, and snippets.

@kariy
Last active May 1, 2022 20:51
Show Gist options
  • Save kariy/ef9222d07c17c9be8a2ae3ca683b12a9 to your computer and use it in GitHub Desktop.
Save kariy/ef9222d07c17c9be8a2ae3ca683b12a9 to your computer and use it in GitHub Desktop.
ZK University

A. Conceptual Knowledge

1. What is a smart contract? How are they deployed? You should be able to describe how a smart contract is deployed and the necessary steps.

Smart contract is essentially a piece of code or a program that resides in the blockchain. These contracts can be called directly by an external user or through other smart contracts. Smart contracts are primarily written in Solidity and must be compiled to its bytecode equivalent that can be understood by the EVM. To deploy the contract, a transaction must be made to the blockchain with the transaction’s receiver being the zero address. The bytecode is then appended to the transaction’s data field. The bytecode used in the process of a new contract deployment is referred to as the creation bytecode. Transaction with zero address as its receiver will be understood by the EVM as a contract creation transaction.

2. What is gas? Why is gas optimization such a big focus when building smart contracts?

Gas is a mechanism used by the EVM to solve what is called the ‘Halting Problem’. Its usage is akin to fuel for running a vehicle. Once the fuel is finished, the vehicle will stop running. Gas for smart contracts is important as it ensures that the execution of smart contracts will not last indefinitely and essentially hog all the computation resources in the Ethereum network. As users pay for gas in Ethereum using ether, gas optimization is important so as to make it as cheap as possible for users to make a transaction to a smart contract.

3. What is a hash? Why do people use hashing to hide information?

Hash is the product of running arbitrary data into a hash function which would return a fixed-size value. For a good hash function, each unique data would correspond to its unique hash value. Because hashing is a one-way process, a hash cannot be decoded back to its original data. A hash is basically a random value and does not hold any resemblance of its original data.

4. How would you prove to a colorblind person that two different colored objects are actually of different colors? You could check out Avi Wigderson talk about a similar problem here.

Firstly, the blind person will hold the objects, each on different hands. Then, show to the prover the initial position of the object (which object belongs to which hand). The blind person then hides their hands behind their back and swap (or not) the objects. After that, the blind person shows the objects to the prover. The prover then tells the blind person if the objects have been swapped or not. This process can then be done as many times as the person wants so as to increase their confidence in the prover.

B. You sure you’re solid with Solidity?

1. A super simple "Hello World" smart contract.

include a screenshot of the Remix UI once deployed

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0;

contract Contract {
    uint private _number;

    function storeNumber(uint number) public {
        _number = number;
    }

    function retrieveNumber() public view returns (uint) {
        return _number;
    }
}
  1. Ammended VotingContract

screenshots showing the time of contract deployment as well as the transaction being reverted once past the voting period.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
/// @title Voting with delegation.
contract Ballot {
    // This declares a new complex type which will
    // be used for variables later.
    // It will represent a single voter.
    struct Voter {
        uint weight; // weight is accumulated by delegation
        bool voted;  // if true, that person already voted
        address delegate; // person delegated to
        uint vote;   // index of the voted proposal
    }

    // This is a type for a single proposal.
    struct Proposal {
        bytes32 name;   // short name (up to 32 bytes)
        uint voteCount; // number of accumulated votes
    }

    address public chairperson;

    // This declares a state variable that
    // stores a `Voter` struct for each possible address.
    mapping(address => Voter) public voters;

    // A dynamically-sized array of `Proposal` structs.
    Proposal[] public proposals;

    uint public startTime;

    modifier voteEnded() {  
        // 5 minutes == 500 seconds
        require(block.timestamp < startTime + 500, "Voting period has ended.");
        _;
    }

    /// Create a new ballot to choose one of `proposalNames`.
    constructor(bytes32[] memory proposalNames) {
        chairperson = msg.sender;
        voters[chairperson].weight = 1;
        startTime = block.timestamp;

        // For each of the provided proposal names,
        // create a new proposal object and add it
        // to the end of the array.
        for (uint i = 0; i < proposalNames.length; i++) {
            // `Proposal({...})` creates a temporary
            // Proposal object and `proposals.push(...)`
            // appends it to the end of `proposals`.
            proposals.push(Proposal({
                name: proposalNames[i],
                voteCount: 0
            }));
        }
    }

    // Give `voter` the right to vote on this ballot.
    // May only be called by `chairperson`.
    function giveRightToVote(address voter) external {
        // If the first argument of `require` evaluates
        // to `false`, execution terminates and all
        // changes to the state and to Ether balances
        // are reverted.
        // This used to consume all gas in old EVM versions, but
        // not anymore.
        // It is often a good idea to use `require` to check if
        // functions are called correctly.
        // As a second argument, you can also provide an
        // explanation about what went wrong.
        require(
            msg.sender == chairperson,
            "Only chairperson can give right to vote."
        );
        require(
            !voters[voter].voted,
            "The voter already voted."
        );
        require(voters[voter].weight == 0);
        voters[voter].weight = 1;
    }

    /// Delegate your vote to the voter `to`.
    function delegate(address to) external {
        // assigns reference
        Voter storage sender = voters[msg.sender];
        require(!sender.voted, "You already voted.");

        require(to != msg.sender, "Self-delegation is disallowed.");

        // Forward the delegation as long as
        // `to` also delegated.
        // In general, such loops are very dangerous,
        // because if they run too long, they might
        // need more gas than is available in a block.
        // In this case, the delegation will not be executed,
        // but in other situations, such loops might
        // cause a contract to get "stuck" completely.
        while (voters[to].delegate != address(0)) {
            to = voters[to].delegate;

            // We found a loop in the delegation, not allowed.
            require(to != msg.sender, "Found loop in delegation.");
        }

        // Since `sender` is a reference, this
        // modifies `voters[msg.sender].voted`
        sender.voted = true;
        sender.delegate = to;
        Voter storage delegate_ = voters[to];
        if (delegate_.voted) {
            // If the delegate already voted,
            // directly add to the number of votes
            proposals[delegate_.vote].voteCount += sender.weight;
        } else {
            // If the delegate did not vote yet,
            // add to her weight.
            delegate_.weight += sender.weight;
        }
    }

    /// Give your vote (including votes delegated to you)
    /// to proposal `proposals[proposal].name`.
    function vote(uint proposal) external voteEnded {
        Voter storage sender = voters[msg.sender];
        require(sender.weight != 0, "Has no right to vote");
        require(!sender.voted, "Already voted.");
        sender.voted = true;
        sender.vote = proposal;

        // If `proposal` is out of the range of the array,
        // this will throw automatically and revert all
        // changes.
        proposals[proposal].voteCount += sender.weight;
    }

    /// @dev Computes the winning proposal taking all
    /// previous votes into account.
    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;
            }
        }
    }

    // Calls winningProposal() function to get the index
    // of the winner contained in the proposals array and then
    // returns the name of the winner
    function winnerName() external view
            returns (bytes32 winnerName_)
    {
        winnerName_ = proposals[winningProposal()].name;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment