[ PizzaCoin the Series #2 ] Workflow Design for PizzaCoin Voting System
Welcome to the 2nd part of PizzaCoin the Series. Before deep diving into the code, we would like to walk you through an overview of workflow design for PizzaCoin contract. As mentioned in the previous article, one of the biggest challenges when developing Ethereum smart contract is a way to handling ‘Out-of-Gas’ error while deploying the contract to the blockchain network, due to some block gas limits on Ethereum blockchain. The prototype of PizzaCoin contract also confronted with these limitations since our contract requires several functional subsystems such as staff management, team and player management, and voting management subsystems. To avoid block gas limit problems, we decided to develop PizzaCoin contract using contract factory and proxy library patterns together with other advanced techniques (more on this will be described later on).
Terms used in this article
PizzaCoin – the mother contract of PizzaCoinStaff, PizzaCoinPlayer and PizzaCoinTeam contracts.
PizzaCoinStaff – one of the three PizzaCoin’s children contracts responsible for managing staff-related tasks such as registering staffs, revoking staffs, providing staff information, and managing token balance as well as voting action for the staff.
PizzaCoinPlayer – one of the three PizzaCoin’s children contracts responsible for managing player-related tasks such as registering players, revoking players, providing player information, and managing token balance as well as voting action for a player.
PizzaCoinTeam – one of the three PizzaCoin’s children contracts responsible for managing team-related tasks such as creating teams, registering a player to a specific team, revoking teams, revoking a specific player from a particular team, handling team voting, and providing team information as well as voting results.
PizzaCoinStaffDeployer – a contract factory library for deploying PizzaCoinStaff contract.
PizzaCoinPlayerDeployer – a contract factory library for deploying PizzaCoinPlayer contract.
PizzaCoinTeamDeployer – a contract factory library for deploying PizzaCoinTeam contract.
PizzaCoinCodeLib – one of the two proxy libraries used by PizzaCoin contract.
PizzaCoinCodeLib2 – one of the two proxy libraries used by PizzaCoin contract.
Project Deployer – a user who deploys PizzaCoin contract which is considered as one of staffs.
Workflow design for PizzaCoin contract
PizzaCoin contract consists of eight dependencies including three contracts: PizzaCoinStaff, PizzaCoinPlayer and PizzaCoinTeam, and five libraries: PizzaCoinStaffDeployer, PizzaCoinPlayerDeployer, PizzaCoinTeamDeployer, PizzaCoinCodeLib and PizzaCoinCodeLib2.
PizzaCoin contract acts as a mother contract of all dependencies. In more detail, the contract has three special children contracts, namely PizzaCoinStaff, PizzaCoinPlayer and PizzaCoinTeam contracts which would be deployed by the three deployer libraries named PizzaCoinStaffDeployer, PizzaCoinPlayerDeployer and PizzaCoinTeamDeployer respectively. Furthermore, PizzaCoin contract also has another two proxy libraries named PizzaCoinCodeLib and PizzaCoinCodeLib2 which would be used as libraries for migrating source code of PizzaCoin mother contract.
There are two stages when deploying PizzaCoin contract onto the blockchain. In the first stage, PizzaCoin contract’s dependencies including PizzaCoinStaffDeployer, PizzaCoinPlayerDeployer, PizzaCoinTeamDeployer, PizzaCoinCodeLib and PizzaCoinCodeLib2 libraries have to be deployed onto the blockchain one by one as separate transactions. The previously deployed libraries' addresses would then be linked and injected as dependency instances in order to deploy PizzaCoin mother contract to the ethereum network as illustrated in Figure 2.
In the second stage, the previously deployed PizzaCoin mother contract must get initialized by a project deployer (the staff who previously deployed PizzaCoin contract). The project deployer initiates three transactions (steps 1.1, 2.1 and 3.1) in order to deploy PizzaCoin children contracts–including PizzaCoinStaff, PizzaCoinPlayer and PizzaCoinTeam contracts–as shown in Figure 3. At this point, we employed a contract factory pattern using the deployer libraries, i.e. PizzaCoinStaffDeployer, PizzaCoinPlayerDeployer and PizzaCoinTeamDeployer, to deploy each corresponding child contract (steps 1.2 - 1.3, 2.2 - 2.3 and 3.2 - 3.3). The resulting children contracts' addresses would then be returned to store on PizzaCoin contract (steps 1.4, 2.4 and 3.4). This way makes PizzaCoin contract know where its children contracts are located on the ethereum blockchain.
On the prototype of PizzaCoin contract, we faced ‘Out-of-Gas’ error when deploying the contract because the contract contains too many function definitions. The solution to avoiding such the error we have used on a production version is to migrate almost all the logical source code of each function on PizzaCoin contract to store on proxy libraries named PizzaCoinCodeLib and PizzaCoinCodeLib2 instead as depicted in Figure 4.
PizzaCoin contract is considered as a contract coordinator or a reverse proxy contract for PizzaCoinStaff, PizzaCoinPlayer and PizzaCoinTeam contracts. When a user needs to interact with any contract function, a user just makes a call to PizzaCoin contract right away. For example, a user wants to join some specific team, he/she can achieve this by invoking registerPlayer function on PizzaCoin contract. The contract would then interact with its children contracts in order to do register the calling user as a player to the specified team.
In more technical detail when a user makes a call to PizzaCoin.registerPlayer() function on PizzaCoin contract, the function will instead forward the request to the delegated function named PizzaCoinCodeLib. registerPlayer() on the proxy library PizzaCoinCodeLib in order to process the requesting transaction on behalf of PizzaCoin contract. Next, the delegated function will hand over the process to the real worker function named PizzaCoinPlayer. registerPlayer() which is on PizzaCoinPlayer child contract. With these code migration techniques, we can significantly reduce gas consumption when deploying the PizzaCoin mother contract.
State transition on PizzaCoin contract
There are five states representing the status of PizzaCoin contract including Initial, Registration, Registration Locked, Voting and Voting Finished. Each state defines a different working context to the contract and it is changable by the staff privilege only. The contract state is unidirectional as illustrated in Figure 5. This means that if the state has been changed from one to another, we cannot change it back to any previous state.
Initial is the first state that is automatically set during PizzaCoin contract getting deployed. The contract state can be changed from Initial to Registration if and only if all the three children contracts (i.e., PizzaCoinStaff, PizzaCoinPlayer and PizzaCoinTeam contracts) have been created by the project deployer (the staff who deployed PizzaCoin contract). The project deployer can create the children contracts by invoking the following functions on PizzaCoin contract in no particular order: createStaffContract, createPlayerContract and createTeamContract (steps 1.1, 2.1 and 3.1 in Figure 3).
Once PizzaCoin contract’s state is changed to Registration, the contract is opened for registration. During this state, the staff can register a selected user as the new staff. A player can create a team and/or join to an existing team. Furthermore, the staff is allowed to revoke some player from a specific team or even revoke a whole team if necessary. PizzaCoin contract would be closed for registration once the state is changed to Registration Locked. Later, the staff can enable voting by changing the contract state to Voting. The vote would be opened until the contract state is moved to Voting Finished. In this state, PizzaCoin contract would determine the winning team automatically.
Let’s say the staff changes PizzaCoin contract’s state from Registration Locked to Voting. Figure 6 illustrates how PizzaCoin contract interacts with its children contracts. What happens is that as soon as the staff executes PizzaCoin.startVoting() function on PizzaCoin contract (step 1), the function would call to the delegated function PizzaCoinCodeLib2. signalChildrenContracts. ToStartVoting() on PizzaCoinCodeLib2 library (step 2). Later, the delegated function would order all the three children contracts to change their state to Voting by respectively executing PizzaCoinStaff.startVoting(), PizzaCoinPlayer.startVoting() and PizzaCoinTeam.startVoting() (steps 3.1 - 3.3).
Let’s summarize. In this article, you have learned how PizzaCoin contract and its dependencies were designed. Our design mainly focuses on an approach to avoiding ‘Out-of-Gas’ error while deploying PizzaCoin contract onto the ethereum blockchain. We adopted several advanced techniques such as contract factory pattern, proxy library pattern, contracts linking and so on.
In the next article, you will learn the implementation of PizzaCoin children contracts in more technical detail. Have a good day :)
PizzaCoin the series consists of 6 articles as follows.