Lab 4 Details for MPCS 56605

Each lab will consist of a small problem and details of how to proceed. Each lab is intended to give every student hands-on experience with the core concepts and technologies covered during the course.  A student may concentrate, as a team member, on one technology over another for the final project, but labs are designed to give each and every student exposure to all the technologies that come into play.  You need to submit labs to the TAs for grading--see submission instructions below.  Generally, unless otherwise specified, you will have one week to complete each assigned lab.

See the syllabus for information on grading.  Turning in lab assignments on time is required, without exception, and all late deliveries will be penalized, regardless of cause.  Submit your assignments to the subversion repository according to the directions on the syllabus page.

You may write Problem 2 in any programming language of your choice (Solidity is required for Problem 1).  Our suggestion is now is not the time to learn a new programming language along with the concepts themselves.  So our suggestion is to use whatever programming language you  know best for building your own blockchain.

Lab 4   Due: 4:00 pm, Monday, July 17, 2023

Problem 1:  IDE Setup & First Ethereum Contract Deployment: 

BACKGROUND:

You are going to use Visual Studio Code to create and deploy your first smart contract.  Since this is your first contract, as well as continuing to set up your environment, we're going to go gently step-by-step with the instructions.  In subsequent labs, we'll expect you to know how to do all this and the instructions will be less detailed and more directive than the gentle step-by-step of this lab.

WHAT YOU NEED TO DO:

You are going to run an Ethereum blockchain (Ganache) and deploy your first smart contract onto it.

STEP 1:

You want to install the Juan Blanco Solidity extension into VS Code so you can enjoy syntax highlighting, ease of compilation, etc.  So open up Visual Studio Code and click on the Extensions icon on the icon bar:

STEP 2:

Once you have clicked on the extensions icon, type "Juan Blanco Solidity" into the Extensions: Marketplace search bar and then click on the blue "Install" button next to the Juan Blanco solidity extension (it may take a second or two to show up in the marketplace results):

 

STEP 3:

Once you've installed the Juan Blanco solidity extension (it can take a few minutes), you need to configure the correct solidity compiler version that we will be using in the class, which is "v0.5.16+commit.9c3226ce".  Click on the "settings" gear icon:



and then scroll down the long list of installation/configuration options for the solidity extension until you come to the external compiler configuration.  For the "Solidity:  Compile Using Remote Version", replace the "latest" default with the compiler version "
v0.5.16+commit.9c3226ce" (without the quotation marks) and press Enter:



With this, you can simply close the extensions settings windows.

STEP 4:
 
At the command line, let's create a new directory (you can of course choose where you want to store your source code) and cd into it:

$ mkdir -p ~/56605/src/LABS/Lab.4/MPCSHowdy

$ cd
~/56605/src/LABS/Lab.4/MPCSHowdy

STEP 5:

Now we want to run "truffle init" (you installed truffle in Lab 1) in our new ~/56605/src/LABS/Lab.4/MPCSHowdy directory:

$ truffle init

After running init, see what new files have appeared in your directory:

$ find .
.
./migrations
./migrations/1_initial_migration.js
./test
./test/.gitkeep
./contracts
./contracts/Migrations.sol
./truffle-config.js

Sweet! as they say.  You will be modifying truffle-config.js and the migrations and contracts in the remainder of this section of the lab.

STEP 6:

First, we need to set truffle up to run against Ganache, so first let's launch ganache from the Applications directory and let's create a new workspace for our first contract:

 

Now, let's create a new workspace called "MPCSHowdy".  Type in "MPCSHowdy" (without the quotation marks) into the Workspace Name field, and then click on "ADD PROJECT":



Once you've added the new project, you'll see your new workspace appear and a listing of all the new accounts:



Note that your new workspace is named "MPCSHOWDY" and your new RPC SERVER is effectively localhost (127.0.0.1) listening on port 7545.  This is important so remember it, as it leads us to our first coding in our new MS Code project.

You can use the geth (goethereum) client console tool to view these accounts from the command line.  To do so, simply attach geth to your running ganache application (on port 7545):

$ geth attach http://127.0.0.1:7545
WARN [05-29|06:47:42.487] Enabling deprecated personal namespace
Welcome to the Geth JavaScript console!

instance: Ganache/v7.7.3/EthereumJS TestRPC/v7.7.3/ethereum-js
coinbase: 0x0000000000000000000000000000000000000000
at block: 5 (Sun May 28 2023 15:46:15 GMT-0500 (CDT))
 modules: eth:1.0 evm:1.0 net:1.0 personal:1.0 rpc:1.0 web3:1.0

To exit, press ctrl-d or type exit
(reverse-i-search)`balance': ^C
> eth.accounts
["0x6e285a3dc04f9dad74876e4b4b3078cb4c8c80a1", "0x4426eacd7eda03d7d9a9a0f614e3f8b2131940ce", "0x6fbd263a62c11e644eaf41c3ecb071239c851c5a", "0x682c44118a1a8bc0d10594969b93945b10e9728d", "0x4c94cd92e82e51c0b1e365296dcdf1cf50b1450f", "0x78616d8b500d057991e0b35008a02847e72e11b8", "0xf4ac0cf38d1299d130c7786d51a10e5442dcb63d", "0xf9d28f831ae011d26b8c71510c4a23728e98f6ef", "0xf053ae36277cf5f70adb93499ebbca844c885c1e", "0xd7088abaf3d7ab7d753519a33a1dfa2a6108ba37"]
> web3.fromWei(eth.getBalance(eth.accounts[0]))
100
> web3.fromWei(eth.getBalance("0x4426eacd7eda03d7d9a9a0f614e3f8b2131940ce"))
100
>
eth.getBalance("0x4426eacd7eda03d7d9a9a0f614e3f8b2131940ce")
100000000000000000000

This will give you a taste of how you can use geth's eth and web3 interfaces to interact with ethereum blocks, transactions, accounts, etc.  Notice eth.accounts will print out an array of all your accounts (these accounts match the accounts in your ganache application).  You can then interact with individual accounts either by entering the account hex address or by simply referencing the appropriate account index from the array.

STEP 7:

Switch back into MS Code and from the main menu choose "Open" on the main screen and navigate down to your MPCSHowdy truffle directory "~/56605/src/LABS/Lab.4/MPCSHowdy" and open it:



Once you've opened the project, your VS Code screen should look like this:


  
STEP 8:

At this point, you can expand the various directories you see, exploring what's under contracts, migrations, and test.  Some default stuff which we will soon edit, but right now, we want to focus on the truffle-config.js file.  Go ahead and click on it to open it. Scroll down to the Networks section, and uncomment the lines for development, and change the port from the default of 8545 (default port for ethereum chains like geth) to our ganache default port, which is 7545 (remember above we saw that our ganache app was listening on "HTTP://127.0.0.1:7545"):

...
     development: {
      host: "127.0.0.1",     // Localhost (default: none)
      port: 7545,            // Standard Ethereum port (default: none)
      network_id: "*",       // Any network (default: none)
     },
...

Also, in the same truffle-config.js file, scroll down a little further to the compilers section, and change the solc compiler from version "0.8.16" (or whatever you have defaulted) to our working version of "0.5.16":

...
  compilers: {
    solc: {
      version: "0.5.16",
...

Once you've made these few changes, save the file (Cmd-S).  You can now close the truffle-config.js file.  Now we're ready to do some solidity coding.
 
STEP 9:

First, let's create our new HelloPeeps.sol solidity file.  Right click in the Explorer on the contracts directory and choose "New File".
  Type in "HelloPeeps.sol" and press return.  You'll see that  you have a new file under contracts, called HelloPeeps.sol.  You'll also notice that it's quite empty in the editor window.  Well, that's not going to last long.  Type in the following code (type it, don't just copy and paste it) and then save the file:

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;

/** @title HelloPeeps
smart contract. */
contract HelloPeeps {
   
    // the greeting variable
    string public greeting;
   
    //constructor for the contract HelloPeeps
    constructor() public {
       greeting = 'Hello MPCS 56605 Peeps!';
    }

    /** @dev say Hello MPCS 56605 Peeps!
      * @return greeting--the greeting to be returned
      */   
    function hello() public view returns (string memory)  {
        return greeting;
    }
   
    /** @dev sets the greeting.
      * @param _greeting The new greeting to be set
      */
    function setGreeting(string memory _greeting) public {
        greeting = _greeting;
    }
}

Now, lt's talk through this line by line.  The first line defines the license as MIT which gets rid of an annoying compiler warning.  The second line is a pragma statement that specifies that our code requires a solidity compiler greater than or equal to version 0.5.0.  We're fine, because we just set our compiler to 0.5.16 which will work just well.

The next lines that are inside comments /** ... */ that start with @ are documentation indicators called NatSpecs (similar to javadoc notations for those familiar with these).  "@title" specifies the title of a contract (or library).  "@dev" specifies a developer's comment and can include basically any advice the developer wishes to provide.  "@return" specifies the return value of the function, and "@param" provides the description of function parameters.  Using NatSpecs is simply good Solidity programming practice and you should get used to providing them in all your code. 

The first line of the HelloPeeps contract declares a string variable called greeting which is declared as public.  Come to think of it, that's a real bad idea, so let's go ahead and change that to "string private greeting" right now.  The next function is a constructor, which is thankfully public, which is used to initialize variables among other things, and in our case, it initializes the greeting to the string "Hello MPCS 56605 Peeps!".  The constructor is called at the initialization of the contract, and if you don't provide one, one will be automatically created in the background for you.

The next section defines a new function called hello() that returns the greeting string (we talk about memory modifiers for datatypes in class).  Whenever hello() function is called, it returns the greeting string. 

The final function provided is a "setter" that allows anyone to set the greeting.  It takes in a string parameter referenced (commonly with a leading underscore) as _greeting.  This function can be used to change the greeting to a new user-provided greeting going forward.
 
STEP 10:


Ok, now, we've got to add a migration for our new contract.  In the Explorer, right click on "migrations" and create a new migrations file called "2_contracts_migration.js".  In this file, type (don't paste):

var HelloPeeps = artifacts.require("HelloPeeps");
module.exports = function(deployer) {
    deployer.deploy(HelloPeeps);
};

and save the file.  This file will be used by truffle when we deploy our new contract onto our local ganache blockchain.  Deployment is the way that we migrate our contracts onto an ethereum blockchain.  We state that we require an artifact (contract) called HelloPeeps (actually, the contract's bytecode) and we're going to store it in a variable of the same name.  Then we define a new function for the deployment, and we simply deploy our new contract onto the blockchain.


STEP 11:

Ok, folks, it's now show time.  We now need to
merely compile our contracts to bytecode, then deploy the contracts onto ganache, and then execute the contract functions.  This is (if you've been following along and not making typos ;-) the fun part.  At this point, your Explorer window should look like this:



Now, in VS Code, click on View in the main menu, and select "Terminal".  When you do this, it will open up a new pane at the bottom of your VS Code window that has several tabs in it, one for problems, one for output, one for debug console, and one for terminal.  You'll be in the terminal and you'll see your normal terminal prompt in the window.  Mine looks like this:





STEP 12DEPLOYMENT:

Click at the prompt in your terminal window pane, and cd into your code directory ("
~/56605/src/LABS/Lab.4/MPCSHowdy" or wherever you're working), and then type:

$ truffle deploy --reset

You will see (hopefully) something like the following output:

Compiling your contracts...
===========================
Compiling your contracts...
===========================
> Compiling ./contracts/HelloPeeps.sol
> Compiling ./contracts/Migrations.sol
> Artifacts written to /Users/mark/56605/src/LABS/Lab.4/MPCSHowdy/build/contracts
> Compiled successfully using:
   - solc: 0.5.16+commit.9c3226ce.Emscripten.clang


Starting migrations...
======================
> Network name:    'development'
> Network id:      5777
> Block gas limit: 6721975 (0x6691b7)


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0x30f71e55598bacabaa19bd49c24fcb24b1e4c3250a4f9171ac812a86d4b782fb
   > Blocks: 0            Seconds: 0
   > contract address:    0x94435AE013172522e2eD648414a235D0f1fdf27a
   > block number:        1
   > block timestamp:     1685306007
   > account:             0x6E285a3Dc04f9daD74876E4B4b3078cb4C8c80A1
   > balance:             99.999347804875
   > gas used:            193243 (0x2f2db)
   > gas price:           3.375 gwei
   > value sent:          0 ETH
   > total cost:          0.000652195125 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:      0.000652195125 ETH


2_contracts_migration.js
========================

   Deploying 'HelloPeeps'
   ----------------------
   > transaction hash:    0x2f8a7ff467edda9e339b892f84e11336142bf312c8ebac10a419a637080ddc6f
   > Blocks: 0            Seconds: 0
   > contract address:    0x4865b237AcC91326dD014D59657abC229722C3Ec
   > block number:        3
   > block timestamp:     1685306008
   > account:             0x6E285a3Dc04f9daD74876E4B4b3078cb4C8c80A1
   > balance:             99.998409027084406434
   > gas used:            248408 (0x3ca58)
   > gas price:           3.176737487 gwei
   > value sent:          0 ETH
   > total cost:          0.000789127005670696 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:     0.000789127005670696 ETH

Summary
=======
> Total deployments:   2
> Final cost:          0.001441322130670696 ETH


[mark@cheiron]~/UofC/56605/src/LABS/Lab.4/MPCSHowdy
$


If you see this type of output, you're in good shape.  If you take a look at your ganache Transactions window, you'll see something like the following:



Here you see that we successfully created the contract and we called it twice (your window may look different depending on mistakes and re-runs, etc.).  Speaking of which, click on Blocks in your Ganache window and take a look at your blocks created, and then click on Contracts to see your contracts.  You'll see that you don't have any truffle projects linked (it's not done automatically):



Well, let's link our VS code project.  Click on LINK TRUFFLE PROJECTS,  click on Add Project at the bottom and navigate to your VS code directory and click on the truffle_config.js file and click on OK, then click on SAVE AND RESTART:



Once you've linked your VS Code project to your Ganache Workspace, you'll see this under Contracts (or something like this):



Here are your contracts, your Migrations.sol contract and your HelloPeeps.sol contract, both deployed.

Now that we've compiled and migrated our contracts, we can finally execute our code.
  At the prompt, type:

$ truffle console

You will enter the truffle development console and will see the dev prompt:

truffle(development)>

Now let's execute our code (what you type is in bold blue):

truffle(development)> HelloPeeps.deployed().then(function(instance) {helloPeeps = instance;})
undefined
truffle(development)> helloPeeps.hello()
'Hello MPCS 56605 Peeps!'

If, upon calling the hello() function on your helloPeeps contract variable, you see the output "Hello MPCS 56605 Peeps!", congratulate yourself.  You've just deployed and run your first ethereum contract.  It's Miller Time.

STEP 13:

Now, with such tremendous success under out belts, let's call our second function, setGreeting().  Call set greeting with your name (change "Mark" below to your name):

truffle(development)> helloPeeps.setGreeting("Mark")
{
  tx: '0x7c3b03fd715eee82e08c3ef53c864fc5500a048ab5734f4e14e9f14e68526656',
  receipt: {
    transactionHash: '0x7c3b03fd715eee82e08c3ef53c864fc5500a048ab5734f4e14e9f14e68526656',
    transactionIndex: 0,
    blockHash: '0xc6402355c591f0187d0a50f5aceeca40850fa8acb1b4dbf9dd243c528db8c021',
    blockNumber: 35,
    from: '0x68a658da2120b1191e8e26a002abb2fa88543ac5',
    to: '0x72ad0e1211c5051d0a1ac9240bd9141a71bcf4be',
    gasUsed: 29083,
    cumulativeGasUsed: 29083,
    contractAddress: null,
    logs: [],
    status: true,
    logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    rawLogs: []
  },
  logs: []
}

Now, let's call our hello() function again:

truffle(development)> helloPeeps.hello()
'Mark'



This works just fine.  Notice that calling setGreeting() caused a new transaction to happen and used some gas, 29083 to be exact.  We will cover in class what all this means, like transaction and block hashes, from and to accounts, bloom filters, etc.

Congratulations, you've now built your first Ethereum contract.  Now it's time to do a little more work on your own blockchain.

Problem 2:  Blockchain laboratory (continued): 

BACKGROUND:

Now it is time to begin to build your own blockchain.  From scratch.  Needless to say, our intention here is not to replicate Bitcoin Core.  That might be a bit much.  Over the remaining labs, you will build a simple blockchain that implements a simple cryptocurrency that can be mined and distributed across nodes.  You will generally find the References section below helpful in addition to the required and recommended reading. 

WHAT YOU NEED TO DO:

STEP 1:

You are to produce the following UML description in code using a programming language of your choice.  For those using non- or quasi-object-oriented-languages, use some alternate form of a class, whatever makes the most sense, for instance, a structure or other such data type that makes the most sense to you.  For those of you using an object-oriented-capable language (such as python, ruby, C++, Java, C#, etc.), you are to use classes.

The UML for the blockchain you are to build is this:



When we specify types in the above, these are indicators.  What that means is that you are to come as close as you can to that datatype in the language that you are using.  For those using strongly-typed languages (C++, Java, etc.), this will be trivial.  For those using late-binding languages (python, ruby, etc.), just do your best.  You will not be downgraded because a data is not of the correct "type".  In the above, if you see a default type specified (such as "int" for 0xD9B4BEF9 for the MagicNumber in the Block class), you are expected to implement that default at the time of class instantiation.  For further elucidation as to the meanings of the above, see the bitcoinwiki pages in the References section below.

Specifics:

The Header's hashPrevBlock is a double-SHA256 hash of the previous block's header (serialized/stringified as necessary and concatenated).  You do not need to worry about big-endian or little-endian issues with your blockchain.  Simply use whatever comes most natural to you.  We are not looking for a precise algorithm to do this.  Just do it and be consistent for your own sake.  The minimum set of specific fields you are to concatenate (and hash) from the header are:  TimeStamp + hashMerkleRoot + Bits + Nonce + previousHash.  These fields will represent a Block's Blockhash.  If you find that your implementation would benefit from hashing additional information, such as an index or count, etc., that is entirely up to you.  You are free to add to the above UML model as your implementation evolves.  You do not need to ask permission to add attributes (data fields) or methods or functions.  The Block's Blockhash is a hash of the current block's header.  What this means is that in our simplified case, a block holds a hash of it's own Header information, and it's header holds a hash of the previous block's hash, and so on.

For the Transactions ListOfIntputs, any list object or list-like data structure will be fine, including a map or a dictionary; this is all up to you.  The actual inputs and outputs can be a string for the moment (i.e., making the ListOfIntputs a list of strings).  The Transaction's TransactionHash is a hash of the concatenated (serialized/stringified) fields of VersionNumber, InCounter, ListOfInputs, OutCounter, and ListOfOutputs.  We will leave it to you to decide some means of serializing/stringifying your ListOfInputs and ListOfOutputs (which again, can be lists of simple strings).  You may wish to implement the Block's Transactions list as a map or dictionary, where each key is the actual Transaction's TransactionHash. 

For the hashMerkleRoot in the Header, you are to create a Merkle Tree and store its Merkle Root hash in this field.  You may actually store the transactions internally in the Block as a Merkle Tree (is in Block's Transactions list), what data structure you actually use to construct your tree is entirely up to you.  We've simply designated it as a "list".  That is not meant to be prescriptive.  You may re-use your Merkle Tree work from Lab 4 here.  Even if it's not a list.

The character and form and parameters of constructors, methods, etc., are entirely up to you.  You will notice that we have only specified data elements in the above UML model.  You are granted carte blanche to design and implement your blockchain as you see fit, as long as these core requirements are met.

Finally, you will notice that we have not specified a particular class called a Blockchain!  This is intentional.  You may design your Blockchain class or structure as you see fit, as long as it stores blocks (in memory only) that are cryptographically linked and the blocks themselves contain Transactions as specified above (Transaction inputs and outputs do not need to be linked or associated at this time).  For example, you may implement your blockchain as a list or other data structure, such a stack or a queue, in memory.  There are no requirements that your blockchain be persisted on disk.

If you have no immediate need for a given element (for instance, a Block's Nonce or Bits values), you can simply leave them with a default value for the time being.  Again, default values should be set at the time of object instantiation.

You are free to implement the printing of block data and transaction data (via printBlock() and printTransaction()) as you see fit.  This may imply a less anonymous blockchain where plain data is printed (i.e., from input strings and output strings) or you may encrypt these strings and print out the hashes (thus creating a more anonymous blockchain).  This again, is up to you.  You might find it simply easier to leave the data in plaintext in this first version to make debugging a little easier.

Other than the above requirements, there are a few other general requirements:

Function 1.  There must be some means of asking your Blockchain for a given block by block height and by block hash.  It is up to you how to implement this.
Function 2.  There must be some means of searching the Blockchain for a given Transaction by TransactionHash, which should return the Transaction being searched.  It is up to you how to implement this.

Everything else should be self-explanatory, including the meaning of the indicator "SHA256Doublehash".  You should know what that means by now. 

Most importantly:  Anything not specifically spelled out as a requirement above may be interpreted and implemented as you wish.  You can think of this as creative license.

STEP 2:

You are to write a test case (can be a simple main function or an actual test harness from a framework) in which you manually (read:  hard-code) instantiate 10 new transactions, where each transaction has one input and one output (can be anything you wish at the moment).  You will create a new Blockchain and by doing so you will create your genesis blockWhen instantiating the Blockchain, you are to create a Genesis Block which will have the hashPrevBlock hash set to all 0's.  You can set the genesis block's Transaction Input and Output strings to be anything you wish.  You may set the genesis block's Transaction Output string to be anything you wish.  Basically understand that the genesis block and its transactions are hard-coded.

You will manually add the first 5 transactions to a new block that you instantiate at height number 1.  Block 0 will be your genesis block.  You are to create a third block which will be at height number 2, and you will add the remaining 5 transactions to that third block.    After building your blockchain,  you are to use Function 1 above to find and return a block by height and then print that block.  You are then to use Function 2 to find and return a transaction from the blockchain (searching by transaction id hash) and print out that transaction.  Again, you may implement these two functions any way you see fit.

Submit all code and related files (if any) as specified below.

Finally, and importantly, there are many implementations of "simple block chains" around the internet.  Feel free to look at any of them.  But remember, your submission had best not closely resemble any of them in such a way that it would indicate direct copying.

References:

You may find the following references helpful (in addition to the links from previous labs):

Learning Bitcoin from the Command Line
The Bitcoinwiki Page on Transactions
The Bitcoinwiki Page on Raw Transactions
The Bitcoinwiki Page on Blocks

Submitting:

Each assignment will have an invitation to create a Github repository. You will use the lab[1-7]-YOUR_GITHUB_USERID to submit your homework.