Chris Pollett > Students >
Mayuri

    ( Print View)

    [Bio]

    [Blog]

    [C297 Proposal]

    [Bitcoin Slides-pdf]

    [Deliverable 1]

    [Deliverable 2]

    [Deliverable 3]

    [Deliverable 4]

    [CS 297 (PDF)]

    [CS 298 Proposal]

    [Deliverable 5]

    [Deliverable 6]

Deliverable 2

Setting up a local blockchain

Mayuri Shimpi (mayuripraveen.shimpi@sjsu.edu)

How to compile a Solidity contract?

Setting up a local blockchain environment is valuable for various reasons, especially during the development and testing phases of blockchain applications.

I used the Web3.js JavaScript library that provides an interface for interacting with the Ethereum blockchain. It enables developers to build applications that can interact with smart contracts, query blockchain data, and send transactions.

I compiled the _coinFlip.sol file so that I could test and deploy the smart contract.

The properties needed to use on the compiled file are the Application Binary Interface (ABI) or interface and the bytecode. The bytecode is what will go onto the blockchain to make the smart contract work and the interface will be the Javascript layer that acts like a human-friendly map of the bytecode.

For this, I first installed solc- A solidity compiler.

In the terminal at the root of my project, I typed:

npm install --save solc

Next, I created a file called compile.js at the root of the project.

const path = require("path"); const fs = require("fs"); const solc = require('solc'); const coinflip = path.resolve(__dirname, 'Contracts', '_coinFlip.sol'); const source = fs.readFileSync(coinflip, 'utf8'); console.log(solc.compile(source,1).contracts[':CoinFlip']); module.exports = solc.compile(source,1).contracts[':CoinFlip'];

We require two Node modules(path and fs) and our Solidity compiler (solc). The reason we cannot simply require our hello.sol file is because Node will try to execute files as Javascript and would throw an error with a .sol file.

The source variable is populated by the raw source code of "_coinFlip.sol" which is located in the "coinflip" variable. The UTF-8 string represents the encoding of the file.

Finally, I called compile() on the source code and specified how many contracts we will be compiling. In our case, it is one.

When I compile my code using the command node compile.js, this is the output:

The bytecode is the code that will be deployed onto the blockchain and executed in the Ethereum Virtual Machine (EVM). As you can see, it is not human-readable.

This brings us to our second important property, the interface. This is the ABI.

Let us take a closer look at the interface:

These are the Javascript methods that we will be able to use when testing and deploying our contract.

Next, I tested my smart contract using the Mocha Testing framework.

How to test a smart contract?

Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases.

In addition to Mocha, I used Ganache and Web3. Ganache provides us with a set of unlocked accounts we will use in our local testing environment. In this case, unlocked means that there is no need to use a public or private key to access them. Web3 provides libraries that allow access to a local or remote Ethereum node. For testing purposes, I connected to a local node, also provided by Ganache.

In the terminal and within my project, I typed:

npm install --save mocha ganache-cli web3@4.1.2

Next, I created a test folder, inside which I created a file called _coinFlip.test.js.

First, require the Mocha assertion library, Ganache, and Web3. Notice that we capitalize Web3. It is a constructor, and we will be using an instance of it in our code. We also require the interface and bytecode from the compile file.

const assert = require('assert'); const { Web3 } = require('web3'); const ganache = require('ganache'); const web3 = new Web3(ganache.provider()); const {interface, bytecode} = require('../compile')

Before Each block:

beforeEach(async () =>{ accounts = await web3.eth.getAccounts(); flip = await new web3.eth.Contract(JSON.parse(interface)) .deploy( {data: bytecode, arguments:[accounts[1], accounts[2]]}) .send({from: accounts[0], gas: 1000000}) });

Test Case 1:

describe("Coin Flip",() =>{ it("Deploys a contract", () =>{ assert.ok(flip.options.address); }); it('should add 1 ether to the contract balance', async function () { const initialBalance = await web3.eth.getBalance(flip.options.address); const amountToSend = web3.utils.toWei('1', 'ether'); await flip.methods.addEther().send({ from: accounts[0], value: amountToSend }); const newBalance = await web3.eth.getBalance(flip.options.address); assert.equal( newBalance - initialBalance, amountToSend, 'Balance was not updated correctly' ); });

Test Case 2:

it('should distribute the contract balance to player1 or player2 when distributeEther is called', async () => { const initialBalance = await web3.eth.getBalance(flip.options.address); const amountToSend = web3.utils.toWei('1', 'ether'); const player1BalanceBefore = await web3.eth.getBalance(accounts[1]); const player2BalanceBefore = await web3.eth.getBalance(accounts[2]); console.log("Player 1 balance before:", player1BalanceBefore); console.log("Player 2 balance before:", player2BalanceBefore) // Add 1 ETH to the contract balance await flip.methods.addEther().send({ from: accounts[0], value: amountToSend }); // Call distributeEther await flip.methods.distributeEther().send({ from: accounts[0] }); const player1BalanceAfter = await web3.eth.getBalance(accounts[1]); const player2BalanceAfter = await web3.eth.getBalance(accounts[2]); console.log("Player 1 balance after:", player1BalanceAfter); console.log("Player 2 balance after:", player2BalanceAfter) const newBalance = await web3.eth.getBalance(flip.options.address); assert.equal(newBalance, 0, 'Balance was not reset to 0'); }); });

Test case outputs:

View my code