Software Architecture for Blockchain Applications
Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: THEend8_
COMP6452 Software Architecture for Blockchain Applications
1 Learning Outcomes
In this project, you will learn how to write a smart contract using Solidity and deploy it on the Ethereum
blockchain. After completing the project, you will be able to:
• develop a simple smart contract using Solidity
• create and fund your account on the Ethereum Testnet
• deploy your contract to the Ethereum Testnet
• test your smart contract by issuing transactions
• programmatically interact with your smart contract
2 Introduction
Smart contracts are user-defined code deployed on and executed by nodes in a blockchain. In addition to
executing a set of instructions, smart contracts can hold, manage, and transfer digitalised assets. They
are deployed to a blockchain as transaction data. Execution of a smart contract function is triggered using
a transaction issued by a user (or a system acting on behalf of the user) or another smart contract which
in turn triggered by a user-issued transaction. Inputs to a smart contract function are provided through
transactions and the current state of the blockchain. Due to the immutability, consistency, integrity, and
transparency properties of blockchains, smart contract code is immutable and deterministic, as well its
execution is trustworthy. While the “code is law” is synonymous with smart contracts, smart contracts
are neither smart nor have legal binding as per the contract law.
While Bitcoin [1] supports an elementary form of smart contracts, it was Ethereum [2] that demon-
strated the true power of smart contracts by developing a Turing complete language and a run-time
environment to code and execute smart contracts. Smart contracts in Ethereum are deployed and exe-
cuted as bytecode, i.e., binary code results from compiling code written in a high-level language. Bytecode
runs on the Ethereum Virtual Machine (EVM) on each blockchain node [3]. Solidity [4] is the most popu-
lar smart contract language for Ethereum. As Solidity is ultimately compiled into Ethereum bytecode, it
can also be used in other blockchain platforms that support the EVM such as Hyperledger Besu. Solidity
is a high-level, object-oriented language that is syntactically is similar to JavaScript. It is statically typed
and supports inheritance, libraries, and user-defined types.
Figure 1 shows the typical development cycle of a smart contract. Like any program, it starts with the
requirement analysis and modelling. State diagrams, Unified Modeling Language (UML), and Business
Process Model and Notation (BPMN) are typically used to model smart contracts. The smart contract
code is then developed using a suitable tool ranging from Notepad to sophisticated IDEs. Various libraries
and Software Development Kits (SDKs) may be used in the process to minimise potential errors and
enhance productivity. Depending on the smart contract language, code may also need to be compiled,
e.g., Solidity. As the smart contract code and the result of a transaction are immutable and transparent,
the code must be free of bugs. Because transactions trigger smart contracts, we need to pay fees to
execute smart contracts on a public blockchain. In Ethereum this fee is referred to as gas. Amount of
gas needs to execute a smart contract depends on several factors such as computational and memory
complexity of the code, volume of data it handles, and bandwidth requirements. Therefore, extensive
testing and optimisation of a smart contract are essential to keep the cost low. The extent that you can
test (e.g., unit testing), debug, and optimize your code depends on the chosen smart contract language
and available tools. While Ethereum has a rich set of tools, in this lab, we will explore only a small subset
1
of them. Most public blockchains also host a test/development network, referred to as the testnet, that
is identical in functionality to the production network. Further, they usually provide fast finality and do
not charge real transaction fees. It is highly recommended to test a smart contract on a testnet. Testnet
can also be used to estimate transaction fees you may need to pay in the production network. Once you
are confident that the code is ready to go to the production/public blockchain network, the next step is
to deploy the code using a transaction. Once the code is successfully deployed, you will get an address
(aka., identifier or handler) for future interactions with the smart contract. Finally, you can interact with
the smart contract by issuing transactions with the smart contract address as the recipient. The smart
contract will continue to remain active until its creator disables it or reaches a terminating state. Due
to the immutability of blockchains, smart contract code will remain in the blockchain even though it is
deactivated and cannot be executed.
Requirement
Analysis &
Modeling
State
Action
Development
Testing (Local
& Testnet)
Deployment Execution
Figure 1: Smart contract development cycle.
The project has two parts. In part one (Section 3 to 8), you will develop, test, and deploy a given
smart contract to the Ethereum testnet by following a set of steps. In part two (Section 9), you will
update the smart contract to fix some of its functional weaknesses, and then deploy it on to the testnet.
3 Developing a Smart Contract
In this project, we will write a smart contract and deploy it to the public Ethereum Ropsten testnet.
The motivation of our Decentralized Application (DApp) is to solve a million-Dollar question: Where to
have lunch?
Basic requirements for our DApp are as follows:
1. Only the contract creator can create the list of venues v
2. The contract creator also sets the list of friends f to vote for v
3. The contract stop accepting votes when a quorum is met (e.g., number of votes > f /2) and declares
the lunch venue
Following code shows a smart contract written in Solidity to decide the lunch venue based on votes.
The first line indicates that the source code is released as non-open-source code. Third line tells that the
code is written for Solidity and should not be used with a compiler earlier than version 0.8.0. Further, the
∧ symbol says that the code is not designed to work on future compiler versions, e.g., 0.9.0 or higher. It
could work on any version labelled as 0.8.xx. These constraints are indicated using the pragma keyword,
which is an instruction for compilers. As Solidity is rapidly evolving and smart contracts are immutable,
it is desirable to indicate even a specific version such that all participants of a contract have a clear
understanding of the behaviour of the smart contract. Between lines 8 and 16, we define two structures
to keep track of the list of friends and votes. We keep track of individual votes to avoid non-repudiation.
address is a special data type in Solidity that refers to a 160-bit address/account in Ethereum. In lines
18-19 and 26-27, we define several hash maps (aka., maps or hash tables) to keep track of the list of
venues, friends, votes, and results. Compared to some of the other languages, Solidity cannot tell us
how many keys are in a hash map or cannot directly iterate in a map. Thus, the number of entries
are tracked separately (lines 20-22). Also, a hash map cannot be defined dynamically. manager is used
to keep track of the creator of the smart contract. Selected lunch venue and voting state are stored in
variables votedVenue and voteOpen, respectively. Also, note the permissions of these variables. The
compiler will automatically generate getter functions for public variables.
In the constructor, we set the transaction/message sender (msg.sender) that deployed the smart
contract as the manager of the contract. addVenue and addFriend functions are used to create a list
2
of lunch venues and friends that can vote for a venue. These functions also return the number of lunch
venues and friends added to the blockchain. The memory keyword is used to hold temporary variables.
EVM provides two other areas to store data referred to as storage and stack. For example, all the
variables between lines 18 and 28 are in the storage. restricted is a function modifier, which is used to
create additional features or apply restrictions on a function. For example, restricted function (lines
106-109) indicates that only the manager can invoke this function. Therefore, function modifier can be
used to enforce access control. If the condition is satisfied, the function body is placed on the line beneath
;. Similarly, votingOpen (lines 112-115) is used to enforce that votes are accepted only when voteOpen
is true. doVote function is used for voting, as far as voting state is open and both the friend and venue
are valid (lines 65-66). It further returns a Boolean value to indicates whether voting was successful. In
lines 77, after the submission of each vote, we check whether the quorum is reached. If so, finalResults
function is called to chose the most voted lunch venue. This function uses a hash map to track the vote
count for each venue, and one with the highest number of votes is declared as the chosen lunch venue.
Voting is also marked as no longer open by setting voteOpen to false (line 102).