QZ:

  1. Rethinking blockchain security: Position Paper, worked on the incident database for over 400 incidents
  2. The Security Reference Architecture for Blockchains: Towards a Standardized Model for Studying Vulnerabilities, Threats, and Defenses, contributed to the segment on smart contract security
  3. Work with the Blockchain Research Collective, coordinate efforts amongst universities
  4. Conducted courses with BlockFellows in 2018

SY:

Our objective for today is to make you conversant in blockchain technology. Therefore at the end of the day, you should be able to participate in a conversation about blockchain meaningfully.

This is for feedback

Yes No

For some of you, this is the first time you are interacting with blockchain technology. Let's start by making sure you have metamask installed.

Step 0

Install Google Chrome, you can do it from this link

Install metamask, you can do it from this link

Install node, please do not install version 12, you can use version 11 from this link

Step 1

Go to our download the required files to run the frontend locally

Step 2

Connect to our network via metamask.

Click the dropdown menu on the top and go to "Custom RPC". You will see this page. Fill it up with these details.

Network Name: BCDevWorkshop
New RPC URL: https://k0tifewhc0:AMYdoFjO2rD4Ch8ArKY6dEBTSpOFUMCDX5zti_NJp2g@k0mv66045h-k0r5t5e62r-rpc.kr0-aws.kaleido.io

Step 3

Add your account.

Choose a private key from the list here and add your name to the column so others don't take it. You are allowed to do anything with each others' accounts. Each account will be funded 1000 ETH.

As a bonus, the address with the highest ETH or points will win a prize.

Click the circular icon on the top right, then "Import Account".

Enter your private key.

Step 4

Open your cmd prompt/ console

cd app-users
npm install
npm run dev

Open your browser and go to http://localhost:8080

Step 5

Make your first blockchain transaction.

Click on "Register" and fill in your details!

You will get a metamask pop up, approve your transaction!

Now you can go back to "Statuses" or wait for the page to refresh when the block is mined.

What you'll learn

I like to use this analogy when explaining blockchain. It is a:

  1. Data structure - similar to a situation where all previous e-mail replies are kept in the e-mail chain. Except in a blockchain, you include a unique hash that is representative of all previous e-mails in the next e-mail.
  2. Technology - generally a suite of technologies that involve multiple parties that do not necessarily trust each other to collaborate. This relies on the underlying data structure of the blockchain which might not be a single continuously growing chain of blocks either.

Part 1

Every concept will follow this format:

  1. What this concept is summarised into a sentence or two
  2. Issues that make it challenging to achieve this concept in practice or some other effects it might cause
  3. Why this is important
  4. How this concept is achieved in blockchain technology explained through examples
  5. Which examples currently exist
  6. 10 minutes for questions and further discussion

Concepts to cover today:

We will finally put it all together with an example.

Part 2

We will now cover how it is like to work with blockchain technology in terms of development tools and debugging tools.

What

You can be decentralised in terms of architecture, culture, control, and decision-making.

Decentralization can be viewed from a blockchain perspective as a mechanism that provides a way to remodel existing applications and paradigms or build new applications in order to give full control to users. There are conflicting ideas on the importance of decentralisation but most agree that it removes the need for trust when parties collaborate. This gives rise to new types of goods and services that are still untested.

Issues

Scaling – there are challenges in keeping a network decentralized if one intends to scale. A common problem is the transaction speed, which plagued Ethereum for quite a while. Some solutions involve lowering levels of decentralization as a tradeoff for faster transactions, but it fundamentally lowers the level of security of the network due to the need for trusted parties (eg. Supernodes)

Economics – as a network grows, one cannot predict how its peers might interact when utilising the resources a network provides. This might lead to levels of centralization in a network. (Mining power centralization, Centralised exchanges)

Why

  1. It allows for greater participation between non-trust parties
  2. Better reliability in your network as you do not rely on central intermediaries (uptime, processes etc)

How this concept is achieved in blockchain technology

Using the forms of decentralisation as a guide:

  1. Architecture

Bitcoin - Full nodes, Light Nodes, Simple Payment Verification (SPV) Clients.

Hyperledger Fabric - Client, Peer, Orderer

Client

Peer

Orderer

Submits an actual transaction-invocation to the endorsers, and broadcasts transactions-proposals to the orderer

A node that commits transactions and maintains the state and copy of the ledger. Can also have a special endorser role

Node running the communication service that implements a delivery guarantee

  1. Culture

Open source - an ethos that values collaborating on community projects

Linux Foundation - arguably the most successful open source foundation. Their involvement in blockchain is through the Hyperledger project.

  1. Control

Ethereum Foundation - incorporated as a Swiss non-profit at Zug, Switzerland. Provides Ethereum Grants that pay for teams that work on projects.

  1. Decision-making

Ethereum Improvement Proposals (EIPs) - currently this is how all decisions are made.

Which examples currently exist

Self-sovereign identity where users can manage your own reputation, data, and digital assets

ID2020 - supports digital identity programs across the world

uPort - build tools for decentralised identity data management

Decentralised exchanges that requires no central coordinating party and all participants interact based on incentives

Kyber Network

Decentralised Autonomous Organisations allow participants to carry out democratic processes without trusting any particular institution as a central party. This could be through decentralised means to propose ideas, vote on ideas, making decisions for the organisation or individuals all carried out through smart contracts.

MakerDAO - provides a stable digital currency. The DAO sets the ‘stability fee', ‘collateralization ratio' and triggers an ‘emergency shutdown' if needed.

What

The structure of the blockchain itself affords the ability for individuals to independently verify the current state of the network. Each peer can compute the blockchain records for themselves to replicate the changes in the state of the network. A malicious peer will have to compute the consensus protocol for blocks already mined and then outcompete all the miners and broadcast their blocks first to every peer.

Consensus protocols cannot be easily changed and require a fork.

A fork can also be used to roll back records but it requires most peers to agree to the fork.

When records can no longer be easily changed by any single party, this creates a new type of good that is purely digital. This is because previously, people could simply make copies and replicate the good, thus no digital good is scarce. The immutability of the records achieved through decentralisation means that the state of such a good must be agreed upon by the entire network, analogous to physical goods that can be verified independently through sight or touch.

Issues

Hard forks - rewriting large portions of the blockchain means that records are not absolutely immutable

Irreversibility - wrong actions once accepted and confirmed by the network cannot be easily reversed. Data on the blockchain will stay on the blockchain. Vulnerable smart contracts can be easily decompiled and exploited. The infamous DAO attacker left his message to the Ethereum Community. See it for yourself in the blockchain explorer where multiple transactions were made resulting in a loss of $70M.

Why

  1. Sets forth a new paradigm by no longer requiring record-keeping intermediaries
  2. Overcomes the need for trust in records keptas you can verify independently

How

New records - Keep the hash of the previous block and a unique nonce, miners compete to find the nonce for the next block

Old records - easily verified that previous blocks are correct by hashing the blocks and checking that the nonces are correct

Which

ERC721 - To emulate rare, collectible items with Ethereum tokens by making these tokens non-fungible.

ERC20 - Allows smart contracts to act very similarly to a conventional cryptocurrency so that this, hosted on the Ethereum blockchain, can be sent, received, checked of its total supply and checked for the amount that is available on an individual address.

What

You can match the transaction to the address on the blockchain itself quite easily. When you go to a block explorer and view any block, you will see every transaction in the block, the miner, and any part of the block structure.

The pseudonymity that comes with most blockchains is that the address is not traced to a physical identity.

It's this pseudonymity that made blockchain technology an ideal method for illegal activity in its earlier days such as through Silk Road.

However once an address is linked to an individual, this pseudonymity is immediately broken and can be easily traced.

Issues

The problems with being traceable is the lack of privacy. One key concern is the General Data Protection Regulation (GDPR) that would make it impossible to conduct business on the blockchain.

Time - privacy schemes take longer to compute and might severely impact usability of blockchain applications.

That being said, there are new encryption schemes such as homomorphic encryption that might help resolve this.

Why

If privacy is implemented

  1. Potentially better control over personal data
  2. Information that requires secrecy can be conducted over public blockchains

If records are meant to be traceable

  1. Makes for a great audit trail for provenance
  2. Allows for fine analysis of actions that occurred over time by any peer

How

Making transactions traceable

Naive - you can just put your name on the transaction itself and you can be immediately traced to it. Block explorers greatly aid this process.

Data science - such as this work on analysing decentralised exchanges. Google has also made the Bitcoin and Ethereum blockchains available on BigQuery.

Making transactions untraceable

Zero Knowledge Proofs - let's you prove that you know some secret to another party without revealing the secret itself

Homomorphic encryption - introduced by Ronald L. Rivest and Len Alderman (the R and A of RSA). It allows one to perform computations on the data without ever decrypting it.

This is a BIG deal because it's a major stumbling block for practical blockchain applications.

Which

Making transactions traceable

From "When to chains combine; supply chain meets blockchain" by Deloitte

Making transactions untraceable

From the Princeton Bitcoin book

Nightfall - private transactions on the Ethereum blockchain using zk-snarks by EY

You cannot mention blockchain without first understanding Bitcoin. It sets forth the very first principles and ideas that made blockchain technology possible.

Bitcoin's consensus protocol was revolutionary at that time and has become contentious recently due to energy concerns, mining centralisation, and up-and-coming ‘new' consensus protocols. But let's start with the original.

Bitcoin also set forth the original protocol and all network participants have to follow. This was key as it was the first decentralised and public method for individuals to transfer value.

This segment references from "Learn Blockchains by Building One" by Daniel van Flymen.

  1. The blockchain itself

You need to represent the blockchain by defining what is a transaction, what is a block, and how the blocks reference previous blocks.

block = {
    'index': 1,
    'timestamp': 1506057125.900785,
    'transactions': [
        {
            'sender': "8527147fe1f5426f9dd545de4b27ee00",
            'recipient': "a77f5cdfa2934df3954a5c7c7da5df1f",
            'amount': 5,
        }
    ],
    'proof': 324984774000,
    'previous_hash': "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}
  1. Consensus algorithm

The guide implements a basic proof of work algorithm that is difficult to find but easy to verify. The consensus algorithm searches for hashes that end in a certain number of 0s.

import hashlib
import json

from time import time
from uuid import uuid4


class Blockchain(object):
    ...
        
    def proof_of_work(self, last_proof):
        """
        Simple Proof of Work Algorithm:
         - Find a number p' such that hash(pp') contains leading 4 zeroes, where p is the previous p'
         - p is the previous proof, and p' is the new proof
        :param last_proof: <int>
        :return: <int>
        """

        proof = 0
        while self.valid_proof(last_proof, proof) is False:
            proof += 1

        return proof

    @staticmethod
    def valid_proof(last_proof, proof):
        """
        Validates the Proof: Does hash(last_proof, proof) contain 4 leading zeroes?
        :param last_proof: <int> Previous Proof
        :param proof: <int> Current Proof
        :return: <bool> True if correct, False if not.
        """

        guess = f'{last_proof}{proof}'.encode()
        guess_hash = hashlib.sha256(guess).hexdigest()
        return guess_hash[:4] == "0000"
  1. Interacting through an API

The guide uses Flask so that peers can make transactions, mine, call the chain, add new nodes, resolve conflicting blocks.

...
import requests


class Blockchain(object)
    ...
    
    def valid_chain(self, chain):
        """
        Determine if a given blockchain is valid
        :param chain: <list> A blockchain
        :return: <bool> True if valid, False if not
        """

        last_block = chain[0]
        current_index = 1

        while current_index < len(chain):
            block = chain[current_index]
            print(f'{last_block}')
            print(f'{block}')
            print("\n-----------\n")
            # Check that the hash of the block is correct
            if block['previous_hash'] != self.hash(last_block):
                return False

            # Check that the Proof of Work is correct
            if not self.valid_proof(last_block['proof'], block['proof']):
                return False

            last_block = block
            current_index += 1

        return True

    def resolve_conflicts(self):
        """
        This is our Consensus Algorithm, it resolves conflicts
        by replacing our chain with the longest one in the network.
        :return: <bool> True if our chain was replaced, False if not
        """

        neighbours = self.nodes
        new_chain = None

        # We're only looking for chains longer than ours
        max_length = len(self.chain)

        # Grab and verify the chains from all the nodes in our network
        for node in neighbours:
            response = requests.get(f'http://{node}/chain')

            if response.status_code == 200:
                length = response.json()['length']
                chain = response.json()['chain']

                # Check if the length is longer and the chain is valid
                if length > max_length and self.valid_chain(chain):
                    max_length = length
                    new_chain = chain

        # Replace our chain if we discovered a new, valid chain longer than ours
        if new_chain:
            self.chain = new_chain
            return True

        return False
  1. Using the blockchain

Postman is used so that you can easily make API calls and try it out for yourself.

This is a really barebones and simple blockchain, nothing compared to the Bitcoin source code itself but it gives you just enough to understand how a blockchain works.

Dev tools are important as it reduces development time.

To interact with the blockchain, it is common to start with geth, which will install the whole ethereum blockchain on your computer. However this might take hours, which is not practical if you are trying out blockchain technology.

Therefore to allow you to work with the Ethereum blockchain without running a full node there are several methods such as;

  1. Infura, a hosted Ethereum node cluster that lets your users run your application without requiring them to set up their own Ethereum node or wallet
  2. Web3js, make requests to an individual Ethereum node with JSON RPC in order to read and write data to the network
  3. HDWalletProvider, for account management process required when signing transactions on the blockchain such as metamask

This list is not exhaustive as new and better ways are still being created.

To work even faster especially for proofs-of-concepts or hackathons, we can use Ganache. Ganache is a popular personal ethereum client which you can use to run tests, execute commands, and inspect state while controlling how the chain operates.

Most tutorials have their networks deployed on a mix of Ganache and Ethereum testnet(s) connected via infura.

To speed up the development and code testing, we have Truffle which is a development environment, testing framework and asset pipeline for Ethereum. truffle init creates the following file structure, which provides the base for our project.

File structure:

Get Solidity Syntax Plugin by Juan Blanco for Visual Studio Code as it is especially useful since many tutorials online have outdated solidity code and this handy code editor can remind you of the new solidity code syntax.

From the pace at which the development tools in blockchains are developed and new versions of solidity language are pushed out, it is common that the tutorial you have been following is outdated and you are sitting with broken code. When in doubt, google!

Here are some common community resources:

  1. Stack Exchange for tough questions
  2. Ethereum Magicians for discussions

Check the main developer page.

If you are looking to develop an app which requires reading the state of some part of the blockchain, knowledge of web3js and how metamask works would be helpful.

When I was editing the code for the project on voting, I noticed that the Event always on loading if metamask is enabled in browser. So i went to the place in the code where I check if there is a current provider for web3 and perform a few console logs to check the state of my accounts and voila! I found my problem: Web3.getaccounts returned an empty array. With help from stack overflow i picked the possible underlying problems (solvable with actions):

As you can see, some knowledge of how these things work is essential to pinpoint the location of the error (in this case I checked web3.currentProvider). And from there onwards stack overflow and github issues help to identify the deprecated features and other developer's workaround to the issue with the correct keywords.

Other interesting issues:

Barnabé Monnot, Robust Incentives Group, Ethereum Foundation

@barnabemonnot

What do we mean when we talk about incentives?

Game-theoretic view of the Bitcoin protocol

Transaction economics: Auctions and tolls

Hacking the games we play

Slides here

Jocelyn Chang,[email protected]

www.makerdao.com

1. What is DeFi?

2. Difference between traditional finance and decentralized finance

3. Unbundling of financial intermediary functions

4. DeFi Ecosystem and different DeFi tools

5. A few real world uses cases of MakerDAO

6. The total usage volume of DeFi

7. Benefits and concerns of DeFi

Slides here

For today, before you ask the workshop organisers, do a quick Google first or check Stackoverflow. We want you to try and debug by yourself first as part of the learning process. After 10 minutes, if you're still stuck, let us know. However we will be walking around throughout the day to ensure you're having a fruitful experience.

Optimising the participant experience

We will separate you into pairs and match according to your experience in programming. Today we will be adopting the pedagogy of pair programming so we can learn from each other.

Using Remix

Remix will be your main IDE for today. We use it because it's convenient to compile and test your contracts out quickly.

Ropsten

To gain some experience what it's like developing on Ethereum, we will use test ether from the Ropsten network. You can do this by using the Ropsten Ethereum Faucet or the Metamask Ropsten Ethereum Faucet..

Let's dive right into real smart contracts on the blockchain. Smart contracts are compiled before being migrated onto the blockchain. Thus you need to decompile them.

  1. Choose a smart contract from State of the dapps
  2. Decompile it with eveem by Tomas Kolinko
  3. Understand the decompiled code, formatting it properly might help!
  4. Discuss with your partner what this smart contract does
  5. Share with the class

We analysed a decompiled auction contract from OpenSea.

Simply by looking at the decompiled code, we noticed:

  1. Each auction was a struct
  2. How require works
  3. Some basic checks on deciding price upper and lower limits
  4. Assigning values to variables
  5. Event logging
def createAuction(address _tokenAddress, uint256 _tokenId, uint256 _startingPrice, uint256 _endingPrice, uint256 _duration, address _seller): # not payable
  require not uint8(stor0.field_160)
  require _startingPrice < 0xffffffffffffffffffffffffffffffff
  require _endingPrice < 0xffffffffffffffffffffffffffffffff
  require _duration <= 18446744073709551615
  require ext_code.size(_tokenAddress)
  call _tokenAddress.ownerOf(uint256 tokenId) with:
       gas gas_remaining - 710 wei
      args _tokenId
  require ext_call.success
  require ext_call.return_data[12 len 20] == caller
  require ext_code.size(_tokenAddress)
  call _tokenAddress.transferFrom(address from, address to, uint256 value) with:
       gas gas_remaining - 710 wei
      args caller, addr(this.address), _tokenId
  require ext_call.success
  require uint64(_duration) >= 60
  auction[addr(_tokenAddress)][_tokenId].field_0 = _tokenAddress
  auction[addr(_tokenAddress)][_tokenId].field_256 = _seller
  auction[addr(_tokenAddress)][_tokenId].field_512 = uint128(_startingPrice)
  auction[addr(_tokenAddress)][_tokenId].field_640 = uint128(_endingPrice)
  auction[addr(_tokenAddress)][_tokenId].field_768 = uint64(_duration)
  auction[addr(_tokenAddress)][_tokenId].field_832 = uint64(block.timestamp)
  log AuctionCreated(
       address contract=addr(_tokenAddress),
       uint256 tokenId=_tokenId,
       uint256 startingPrice=_startingPrice << 128,
       uint256 endingPrice=_endingPrice << 128,
      uint256 duration=uint64(_duration))

Today we will learn about Ethereum development through working with several smart contracts and dapps. Each segment is separated into:

  1. Deploy the smart contract and explore every function
  2. Discuss if the smart contract fulfills its intended objectives well, how it can do better in terms of gas use and security, and how one might induce unintended behaviour in the smart contract.
  3. Develop based on our earlier discussion, new functions or fixes for the smart contract
  4. Digest what you've just learned by searching for other real-life examples

Deploy

"Hello World" equivalent to familiarise users with smart contracts via remix GUI

  1. navigate to plugins to activate compilers and deployment
  2. paste code for Mortal/Greeter
  3. select proper compiler and option for "autocompile"
  4. switch environment from "Javascript VM" to "injected web3" to connect to metamask account which should be set on the Ropsten testnet
  5. deploy contract and check deployment on etherscan
  6. greet function, post the transaction id onto google docs and let people try the greet function with my contract
pragma solidity >=0.4.22 <0.6.0;

contract Mortal {
    /* Define variable owner of the type address */
    address owner;

    /* This constructor is executed at initialization and sets the owner of the contract */
    constructor() public { owner = msg.sender; }

    /* Function to recover the funds on the contract */
    function kill() public { if (msg.sender == owner) selfdestruct(msg.sender); }
}
/*inheritors*/
contract Greeter is Mortal {
    /* Define variable greeting of the type string */
    string greeting;

    /* This runs when the contract is executed */
    /* explain memory */
    /* public functions are functions that can be called by people while private functions are functions that can only be called by contracts */
    constructor(string memory _greeting) public {
        greeting = _greeting;
    }

    /* Main function */
    function greet() public view returns (string memory) {
        return greeting;
    }
}

Discuss

  1. What do you think of the deployment process?
  2. What happens if you want to change the greeting?

Deploy

You are able to send Ether to your agreement, set new terms, and withdraw Ether if an agreement has been violated.

pragma experimental ABIEncoderV2;

contract RoomieAgreement {
    //Events are to pass information when something happens on the blockchain to your front-end
    event NewAgreement (uint agreementId, string agreementTerms);
    event NewDispute (uint disputeId, string dispute, uint _claim, address claimant);
    //Variables must be declared
    uint agreementId = 0;
    uint minDepositVal = 1;

    //Solidity only support integers
    uint minDeposit = minDepositVal / 1000;
    uint totalDeposit = 0;
    uint disputeId = 0;
    //Structs let you create data types with multiple properties
    struct agreement {
        uint agreementId;
        string agreementTerms;
    }
    
    // Declare an array called agreements of type agreement (a struct) that is publicly viewable
    agreement[] public agreements;
    
    //hardcoded addresses of your roomies
    address[] public roomies = [**,**];
    
    // Mappings are like dictionaries, it's 1 to 1
    mapping (address => uint) public roomieDeposits;
    
    // This function checks that you submit the minimum deposit and includes it
    // The payable modifier lets you send money to the smart contract through this function
    function addDeposit() public payable {
        //require checks a condition. if it fails, it kills the current transaction and reverts
        require(msg.value > minDeposit);
        roomieDeposits[msg.sender] = msg.value;
        totalDeposit = totalDeposit+msg.value;
    }
    
    // This is actually an experimental function but let's you check the agreements
    function viewAgreements() public view returns (agreement[]) {
        return agreements;
    }
    
    //This lets you view the current total deposits
    function viewDeposits () public view returns (uint) {
        return totalDeposit;
    }
    // This function checks that you picked your right Id and adds your new agreement
    function createAgreement(string _agreementTerms, uint _roomieId) public {
        require(roomies[_roomieId-1] == msg.sender);
        agreementId++;
        agreements.push(agreement(agreementId,_agreementTerms));

        // Event is fired off when this function is complete
        emit NewAgreement(agreementId,_agreementTerms);
    }
    
    // This function lets you execute a claim and will send it to the claimant
    function createDispute(uint _dispute, uint _claim, uint _roomieId) public {
        address claimant = msg.sender;
        require(roomies[_roomieId-1] == msg.sender);
        require(_dispute <= agreementId);
        disputeId++;
        string dispute = agreements[_dispute-1].agreementTerms;
        //Event is fired off when this function is complete
        totalDeposit = totalDeposit-_claim;
        claimant.transfer(_claim);
        emit NewDispute(disputeId, dispute, _claim, claimant);
    }
}

Discuss

  1. Privacy - you have put your contract address on the blockchain, how about private voting?
  2. Fake claims - your roomie can essentially just claim all the money anytime
  3. Lack of adjudication - how do you know who was right or wrong in a dispute?
  4. Gas guzzling - are there more efficient ways to write this contract?
  5. Game theory - how do you incentivise someone to not abuse the claiming function?
  6. What is wrong with the Dispute function? How can we do it better?

Develop

Advanced: Implement a solution to any one of these problems we have discussed.

Simple passwords

Having a simple password on the contract. Note that the password should at least be hashed.

uint[] private passwords = [12345, 123456]; //should be hashed

function createAgreement(string _agreementTerms, uint _roomieId, uint _password) public {
        require(roomies[_roomieId-1] == msg.sender);
        require(_password == passwords[_roomieId-1]);
        agreementId++;
        agreements.push(agreement(agreementId,_agreementTerms));

        // Event is fired off when this function is complete
        emit NewAgreement(agreementId,_agreementTerms);
    }

Adding a username password pair

Before allowing the transaction, check if the password and username is correct. Again, this should be hashed. Also, the return function should be an emit instead.

bool has_Login = false;

function enterPassword(string _username, string _password) public returns (string memory){
        if (keccak256(abi.encodePacked((_username))) == keccak256(abi.encodePacked(("hello"))) && keccak256(abi.encodePacked((_password))) == keccak256(abi.encodePacked(("yes")))){
            has_Login = true;
            return "Welcome!"; // emit welcomeMessage(bool _has_Login);
        }
        else{

            return "Login Failed!"; // emit welcomeMessage(bool _has_Login);

        }
    }

function addDeposit() public payable {
        //require checks a condition. if it fails, it kills the current transaction and reverts
        require(msg.value > minDeposit);
        require(has_Login == true);
        roomieDeposits[msg.sender] = msg.value;
        totalDeposit = totalDeposit+msg.value;
    }

Intermediate: Create a function that adds another party as an adjudicator

bool disputeApproval = false;
    function approveDisputeClaim() public {
        require(msg.sender==roomies[2]);
        disputeApproval = true;
    }
    function createDisputeAdjudication(uint _dispute, uint _claim, uint _roomieId) {
        require(disputeApproval == true);
        address claimant = msg.sender;
        require(roomies[_roomieId-1] == msg.sender);
        require(_dispute <= agreementId);
        disputeId++;
        string dispute = agreements[_dispute-1].agreementTerms;
        //Event is fired off when this function is complete
        
        totalDeposit = totalDeposit-_claim;
        claimant.transfer(_claim);
        disputeApproval = false;
        emit NewDispute(disputeId, dispute, _claim, claimant);

    }

Beginner: Do some research on escrow contracts and share with the workshop

Digest

Deploy

This is a message board that can hold up to 10 messages and allows you to post messages.

You will get the chance to call other people's contracts once they have uploaded.

pragma solidity ^0.4.22;

//Contract is just like classes
contract MessageBoard {
    //Defining User structure. address is a variable type from solidity
    struct User {
        address _address;
        string name;
        bool isValue;
    }

    //Defining Message structure. You can see it have User type member
    struct Message {
        string text;
        User _user;
    }
    
    Message[10] public messageBoardLogs;
    
    //Declaring an event
    event MessageCreated (string text, address _address, string name, bool isValue  );

    //mapping is a key value store. We are specified type of key and value
    mapping (address => User) users;

    // public function, which is returning boolean
    function setUsername(string memory name) public returns (bool) {
        //require() is throwing error, if the input is not true
        require(userExists(msg.sender) == false, "userAddress has been previously registered");
        //msg.sender is the address, from where the signUp was called.
        users[msg.sender] = User(msg.sender, name, true);
        return true;
    }

    //8 bit unsigned integer (so no negative values).
    uint8 public messageCount = 0;
    
    //it is creating an event Message with text and user.
    function sendMessage (string text) public returns (bool) {
        //require() is throwing error, if the input is not true
        require(userExists(msg.sender) == true,"msg.sender is not added into users");
        messageCount++;
        emit MessageCreated(text, users[msg.sender]._address,users[msg.sender].name,true);
        messageBoardLogs[messageCount] = Message(text, users[msg.sender]);
        return true;
    }
    
    //We are using view keyword, to specify that it is not modified the state of contract. It will consume less resources. 
    function userExists(address userAddress) view public returns (bool) {
        return users[userAddress].isValue;
    }
    function readMessage(uint messageIndex) view public returns (string text){
        require(messageIndex<=messageCount,"message index out of range");
        return messageBoardLogs[messageIndex].text;
    }


}

Discuss

Develop

Advanced: Implement a solution to any one of these problems we have discussed.

Implement a check to prevent consecutive posting and payment

Ensure that any post will need to be paid for

    address prev;
    uint minVal = 1;
    uint minDeposit = minVal / 10000;

    function sendMessage (string text) public payable returns (bool) {
        //require() is throwing error, if the input is not true
        require(userExists(msg.sender) == true,"msg.sender is not added into users");
        require(msg.sender != prev, "You're already the previous sender! Wait a while.");
        require(msg.value >= minDeposit);
        messageCount++;
        latest++;
        latest = latest % maxLog;
        emit MessageCreated(text, users[msg.sender]._address,users[msg.sender].name,true);
        messageBoardLogs[latest] = Message(text, users[msg.sender]);
        prev = msg.sender;
        return true;
    }

Make message posters perform some form of proof-of-work

Store proofs that have already been done to prevent repeats. Message posters will have to hash their text and a nonce to form their own proof. Check that the proof has 4 leading 0s.

mapping (bytes32 => bool) proofs;
    //it is creating an event Message with text and user.
    // To control for spam, must hash your message with a nonce to obtain a proof with
    //    required amount of leading zeros.
    function sendMessage (string text, uint256 nonce, bytes32 proof) public returns (bool) {
        //require() is throwing error, if the input is not true
        require(proofs[proof] == false);
        require(keccak256(abi.encodePacked(text, nonce)) == proof);
        require(userExists(msg.sender) == true,"msg.sender is not added into users");
        require(proof[0] == 0);
        require(proof[1] == 0);
        require(proof[2] == 0);
        require(proof[3] == 0);
        proofs[proof] = true;
        messageCount++;
        emit MessageCreated(text, users[msg.sender]._address,users[msg.sender].name,true);
        messageBoardLogs[messageCount] = Message(text, users[msg.sender]);
        return true;
    }

Intermediate: devise a solution for the 10 message limit

    //messageBoardLogs can store up to 20 messages
    Message[21] messageBoardLogs;
    //state change when messageBoardLogs is full
    bool messageBoardLogsFull = false;
    //Declaring an event
    event MessageCreated (string text, address _address, string name, bool isValue  );
    //empty constructor will be generated at compile-time if code is not provided.
    //constructor() public {}
    //mapping is a key value store. We are specified type of key and value
    mapping (address => User) users;
    // public function, which is returning boolean
    function setUsername(string memory name) public returns (bool) {
        //require() is throwing error, if the input is not true
        require(userExists(msg.sender) == false, "userAddress has been previously registered");
        //msg.sender is the address, from where the signUp was called.
        users[msg.sender] = User(msg.sender, name, true);
        return true;
    }

    //8 bit unsigned integer (so no negative values).
    uint8 public messageCount = 0;
    //it is creating an event Message with text and user.
    function sendMessage (string memory text) public returns (bool) {
        //require() is throwing error, if the input is not true
        require(userExists(msg.sender) == true,"msg.sender is not added into users");
        //replaces message when threshold message number exceeded
        if(messageCount<=20){
            messageCount++;
        }else{
            messageBoardLogsFull = true;
            messageCount = 1;
        }
        emit MessageCreated(text, users[msg.sender]._address,users[msg.sender].name,true);
        messageBoardLogs[messageCount] = Message(text, users[msg.sender]);
        return true;
    }
  function userExists(address userAddress)  public view returns (bool) {
        return users[userAddress].isValue;
    }
    function readMessage(uint messageIndex)  public view returns (string memory text){
        if(messageBoardLogsFull == false){
            require(messageIndex<=messageCount,"message index out of range");
        }else{
            require(messageIndex<=21,"message index out of range");
        }
        return messageBoardLogs[messageIndex].text;
    }
    function readLatestMessage() public view returns(string memory text){
        require(messageCount>0,"there are no messages in messageBoardLogs");
        return messageBoardLogs[messageCount].text;
    }


}

Beginner: what are some ways that people have used blockchain to share messages?

Deploy

This contract simulates a simple voting process. There are 2 candidates you can vote for.

To view the frontend, download this file.

pragma solidity ^0.5.1;

contract Election {
    // Model a Candidate
    struct Candidate {
        uint id;
        string name;
        uint voteCount;
        // uint _candidateId;
    }

    // Store accounts that have voted
    mapping(address => bool) public voters;
    // Store Candidates
    // Fetch Candidate
    mapping(uint => Candidate) public candidates;
    // Store Candidates Count
    uint public candidatesCount;

    // voted event
    event votedEvent (
        uint indexed _candidateId
    );

    constructor () public {
        addCandidate("Candidate 1");
        addCandidate("Candidate 2");
    }

    function addCandidate (string memory _name) private {
        candidatesCount ++;
        candidates[candidatesCount] = Candidate(candidatesCount, _name, 0);
    }

    function vote (uint _candidateId) public {
        // require that they haven't voted before
        require(!voters[msg.sender], "if voter is not msg.sender");

        // update candidate vote Count
        candidates[_candidateId].voteCount ++;

        // trigger voted event
        emit votedEvent(_candidateId);
    }
    
}

Discuss

Develop

Advanced: Implement a solution to any one of these problems we have discussed.

Intermediate: Implement a way you can check that the candidate is valid and the voter votes only once.

    function vote (uint _candidateId) public {
        // require that they haven't voted before
        require(!voters[msg.sender], "if voter is not msg.sender");

        // require a valid candidate
        require(_candidateId > 0 && _candidateId <= candidatesCount,"if candidate not valid");

        // record that voter has voted
        voters[msg.sender] = true;

        // update candidate vote Count
        candidates[_candidateId].voteCount ++;

        // trigger voted event
        emit votedEvent(_candidateId);
    }

Beginner: what other aspects of the democratic process could be decentralised?

Digest

Deploy

You've deployed a local front-end connected to the decentralised attendance and question-answer dapp.

pragma solidity ^0.5.0;



contract Users {
    // data structure that stores a user
    
    bytes32 answer;
    
    struct User {
        string name;
        string status;
        address walletAddress;
        uint createdAt;
        uint updatedAt;
        uint points;
    }

    // it maps the user's wallet address with the user ID
    mapping (address => uint) public usersIds;

    // Array of User that holds the list of users and their details
    User[] public users;

    // event fired when an user is registered
    event newUserRegistered(uint id);

    // event fired when the user updates his status or name
    event userUpdateEvent(uint id);



    // Modifier: check if the caller of the smart contract is registered
    modifier checkSenderIsRegistered {
            require(isRegistered());
            _;
    }



    /**
     * Constructor function
     */
    constructor() public
    {
        // NOTE: the first user MUST be emtpy: if you are trying to access to an element
        // of the usersIds mapping that does not exist (like usersIds[0x12345]) you will
        // receive 0, that's why in the first position (with index 0) must be initialized
        addUser(address(0x0), "", "");

        // Some dummy data
        addUser(address(0x333333333333), "Leo Brown", "Available");
        addUser(address(0x111111111111), "John Doe", "Very happy");
        addUser(address(0x222222222222), "Mary Smith", "Not in the mood today");
    }
    
    function postAnswer(string memory _answer) public returns(bytes32){
        require(msg.sender==** || msg.sender==** || msg.sender==**);
        answer = keccak256(abi.encode(_answer));
    }


    /**
     * Function to register a new user.
     *
     * @param _userName                 The displaying name
     * @param _status        The status of the user
     */
    function registerUser(string memory _userName, string memory _status) public
    returns(uint)
    {
            return addUser(msg.sender, _userName, _status);
    }



    /**
     * Add a new user. This function must be private because an user
     * cannot insert another user on behalf of someone else.
     *
     * @param _wAddr                 Address wallet of the user
     * @param _userName                Displaying name of the user
     * @param _status            Status of the user
     */
    function addUser(address _wAddr, string memory  _userName, string memory _status) private
    returns(uint)
    {
        // checking if the user is already registered
        uint userId = usersIds[_wAddr];
        require (userId == 0);

        // associating the user wallet address with the new ID
        usersIds[_wAddr] = users.length;
        uint newUserId = users.length++;
        // storing the new user details
        users[newUserId] = User({
                name: _userName,
                status: _status,
                walletAddress: _wAddr,
                createdAt: now,
                updatedAt: now,
            points: 0
        });

        // emitting the event that a new user has been registered
        emit newUserRegistered(newUserId);

        return newUserId;
    }



    /**
     * Update the user profile of the caller of this method.
     * Note: the user can modify only his own profile.
     *
     * @param _newUserName        The new user's displaying name
     * @param _newStatus         The new user's status
     */
    function updateUser(string memory _newUserName, string memory _newStatus) checkSenderIsRegistered public
    returns(uint)
    {
            // An user can modify only his own profile.
            uint userId = usersIds[msg.sender];

            User storage user = users[userId];
        if (keccak256(abi.encode(_newStatus)) == answer){
            user.points += 10;
        }
            user.name = _newUserName;
            user.status = _newStatus;
            user.updatedAt = now;

            emit userUpdateEvent(userId);


            return userId;
    }



    /**
     * Get the user's profile information.
     *
     * @param _id         The ID of the user stored on the blockchain.
     */
    function getUserById(uint _id) public view
    returns(
            uint,
            string memory,
            string memory,
            address,
            uint,
            uint,
            uint
    ) {
            // checking if the ID is valid
            require( (_id > 0) || (_id <= users.length) );

            User memory i = users[_id];

            return (
                    _id,
                    i.name,
                    i.status,
                    i.walletAddress,
                    i.createdAt,
                    i.updatedAt,
                    i.points
            );
    }



    /**
     * Return the profile information of the caller.
     */
    function getOwnProfile() checkSenderIsRegistered public view
    returns(
            uint,
            string memory,
            string memory,
            address,
            uint,
            uint,
            uint
    ) {
            uint id = usersIds[msg.sender];

            return getUserById(id);
    }



    /**
     * Check if the user that is calling the smart contract is registered.
     */
    function isRegistered() public view returns (bool)
    {
            return (usersIds[msg.sender] > 0);
    }



    /**
     * Return the number of total registered users.
     */
    function totalUsers() public view returns (uint)
    {
        // NOTE: the total registered user is length-1 because the user with
        // index 0 is empty check the contructor: addUser(address(0x0), "", "");
        return users.length - 1;
    }

}

Discuss

Develop

This is our last dapp for the day, thus it is CAPSTONE TIME.

Fully design and implement a new feature for this dapp.

Process of creating a dapp (beginners)

The University ecosystem in Singapore, our blockchain research collective.

The blockchain industry in Singapore, OpenNodes.

Congratulations! You have reached the end of this workshop.