Photo by on Miguel Sousa Unsplash Ever since a good unveiled me the whole new land of and smart contracts, I have been witnessing how such new world gets ready to replace most of the things we know. Regardless of hype’s and bluffs, , and that’s what matters when the noise is filtered out. old friend Ethereum a whole new paradigm is there opportunities are there Today we are going to build a from the ground up. It will use the blockchain, and . We will implement a version of the game so that people can play with any unknown peer from the net. It might sound trivial to do, but a good implementation is far from trivial. Distributed Application Ethereum IPFS React provably fair Tic-Tac-Toe If you are not familiar with the core concept of a , make sure to blockchain follow videos like this If you need an introduction to and , feel free to check out if you have doubts as we move on Ethereum Solidity some tutorial Next, find the way to tattoo the following words: The blockchain is wonderful paradigm, but once a thing is done, there is absolutely no way back. No rollback, no sudo, no delete, no reboot. Unless you leave a door open. The road is bumpy and the slope is steep, but life long learning is the key to all success. Welcome on board, I hope you have a nice journey! Picture by publicdomainphotos What makes Ðapps be a Ðapp? The Ðapp we will be developing will allow users to play games with untrusted people around the world and eventually bet some money in a provably fair game. But how does a dapp look like? The dapp’s are not controlled by any central authority (neither by us) Smart Contracts No backend. Data transactions are broadcasted to the blockchain and miners execute them on the involved Smart Contracts Smart contracts enforce a set of rules governing data and money that : nobody can alter them once deployed are carved in stone The code of contracts should be , and public auditable verifiable Transactions are always , and public auditable verifiable The web site should be entirely made of and rely on the blockchain as the only database: depending on dynamic content from a server could alter the web’s behaviour arbitrarily static files To enforce the integrity of static files, our app will use the protocol and there will be no sever to manage. IPFS The Smart Contracts If you follow my posts, you’ll know that I am a big fan of Test Driven Development. In the case of Smart Contracts, because unlike a traditional app, there is no chance for updates once a faulty transaction has been mined. extensive testing is absolutely critical When developing we will use the to test and code our logic. When the contracts are ready for deployment, we will switch to a manual approach to be in control and save a bit of . Truffle Framework gas Truffle Make sure that you have on your system and install Truffle: NodeJS [sudo] npm install -g truffle Create two folders called and , then create a contract template inside of : blockchain web blockchain $ cd blockchain$ truffle initDownloading...Unpacking...Setting up...Unbox successful. Sweet! Commands: Compile: truffle compileMigrate: truffle migrateTest contracts: truffle test If we run several things will happen: truffle test Truffle will compile all the smart contracts inside with contracts solc It will deploy them to an ephemeral local blockchain It will run any test operations from files test/*.sol Mocha will run any test assertions from files test/*.js With no specs yet, the test just completes with 0/0 passed tests. Ðapp use cases Before writing any specs, we need to define what our dapp is supposed to do. The main use case scenario should look like: John opens a game, bets 0.01 ether and appears in a list of users who are up to play Mary sees John on the list and accepts the game by betting 0.01 ether too John confirms and the game starts John and Mary make their moves, one after each other If one of them wins, he or she can withdraw 0.02 ether from the contract If the game ends in draw, both users can withdraw their respective money Contract edge cases The first approach is clear. However, let’s never forget that we are . Contracts need to account for the but also define what happens when things . about to write a contract expected scenarios go wrong After a bit of thinking, some important questions should pop up in our head: How do we determine who starts the game? If the creator of the game is always the first to play, nobody would be interested to join a game with an initial disadvantage. The decision needs to be , but how can we achieve randomness if Smart Contracts are designed to run 100% deterministic transactions? random Both users will have to submit a random number at the beginning. If both random numbers are equally even or odd, the game creator will start. If one is even and the other is odd, the guest will start. But then, can’t the second user track the opponent’s transaction, find the number and forge a number to be always first? In order to hide the creator’s random number until both are sent, we will follow a . The creator will send a hash of his number and reveal the number afterwards. commit-reveal scheme If the random number is rather small, wouldn’t it be easy to compute the first, say, 10,000 hashes? Instead of a direct hash, we will use a . The user will have to reveal the number and salt before the game can start. The salt will make the hash unaffordable to precalculate. salted hash What if the creator does not reveal the random number? After 10 minutes of inactivity, the second player will be set as the winner. Creators could retrieve both random numbers and refuse to reveal if they don’t like to start in the second place. The best alternative is the 50% chance of winning instead of the 100% chance of losing. What if the first user reveals a random number that does not match the salted hash? The opponent will be set as the winner. Cheating is not nice. Everything is automated, such a mistake would only happen on purpose. Why not hiding both random numbers instead of just the creator’s? That’s an opinionated decision. In one hand, hiding both numbers would need an additional reveal step, the global gas cost would increase and the user experience would be slower. At the moment, our bias opts for minimizing the commit-reveals. What if a user quits the game before there is a winner? If someone doesn’t make his/her move within 10 minutes, the opponent will be able to claim the whole amount. In such case, the game will become locked and no further move will ever be made. What if nobody accepts a game? The money on deposit will be withdrawable if nobody accepts our game after 10 minutes. If the deposit is not withdrawn, the game will be up indefinitely until someone accepts it. What if a user forgets to withdraw the money? Every user’s money will be available until he or she claims it. Contract operations Given the above inputs, we will need one contract featuring the following operations: Get a list of games that can be accepted. **getOpenGames**() Get the data of the game at the given Id. **getGame**(gameId) Create a game and list it on the open games list. If an amount of money is sent, the user accepting it will need to bet the same amount. The salted hash of a random number must be provided with the transaction. **createGame**(randomNumberHash, nick) Accept the game identified by and provide a random number to determine who will start first. **acceptGame**(gameId, randomNumber, nick) gameId Get the random number, check it with the original hash, compute who will start first and set the game as started. **confirmGame**(gameId, originalNumber, salt) It will mark the cell number with the player’s symbol. If there is a winner or no cells are left, the game will be set as ended. Otherwise, the turn will be set to the other user. **markPosition**(gameId, cell) Transfers the corresponding amount of money to the player, depending on the status and result of the game. **withdraw**(gameId) The following events will be available as well: GameCreated(gameId) GameAccepted(gameId) GameStarted(gameId) PositionMarked(gameId) GameEnded(gameId) Given the above, our contract file should look like the following bare bones: All of the above needs to be achieved in the most efficient, simple and cheap way. Sending a transaction to the blockchain costs gas, so we need operations to enforce the rules while being . as light as possible Reusing code In order to save costs, a good practise is to encapsulate common code into libraries. Libraries are just a stateless version of a contract that provides functionality. They can be , and at any time. All we have to do in a contract is and to its deployed address. deployed once be reused by several smart contracts import the library link Let’s create the file with the following starter code: contracts/LibString.sol // contracts/LibString.sol solidity ^0.4.24; pragma LibString { saltedHash(uint8 randomNumber, string salt) (string) {return "";}} library function public pure returns Next, let’s import the library. At the top of add the import: contracts/DipDappDoe.sol solidity ^0.4.24; pragma "./LibString.sol"; import // etc. Test Driven Development Unlike traditional contracts, the great advantage of smart contracts is that they can be specified and tested, like any other piece of software. Just imagine 100 lawyers simulating 5000 lawsuits in 50 seconds against a contract to check that not a single lawsuit would be lost TDD and extensive tests are a must for us. However, in this article we will rather focus on demonstrating the architecture, smart contracts and the full stack. For the sake of readability, the full specs can be found on the repository, below. Let’s create two spec files for the contract and two more for the library: , , and . test/TestDipDappDoe.sol test/dipDappDoe.js test/TestLibString.sol test/libString.js Solidity files will perform assertions , while JS files will test it . Let’s add some trivial assertions for the library: within the blockchain from outside And a few specs for the game contract, too: Nothing fancy, just some trivial checks by now. Before they can run, we need truffle to deploy our Solidity contracts to a local blockchain. Let’s create the file with the following instructions: migrations/2_deploy_contracts.js LibString = artifacts.require("./LibString.sol"); DipDappDoe = artifacts.require("./DipDappDoe.sol"); const const .exports = (deployer) {deployer.deploy(LibString);deployer.link(LibString, DipDappDoe);deployer.deploy(DipDappDoe);}; module function The code above will deploy the library, link the contract to it and deploy the game contract so we can test it. Ready, steady… test! $ truffle test As expected, we get compiler warnings and testing errors, because everything is yet to be done. So let’s write the code that satisfies the specifications. In the library, we could try to code the operation as follows: saltedHash(...) saltedHash(uint8 randomNumber, string salt) (bytes32) {bytes bNum = bytes(1);bNum[0] = byte(randomNumber); function public pure returns memory new keccak256(bytes(concat(string(bNum), salt)));} return The core hashing function is , but for it to work in future versions of Solidity, we need to combine the two parameters into a single variable. So we need a function as well. keccak256 concat By adding the helper function, the library should look like: Let’s see if our new code passes the test now: $ truffle test Here it is! Our library is working as expected and the smart contract is properly linking to it. Now comes the meticulous work of specifying every single operation, coding the full functionality and auditing the contracts. For the sake of readability, we will focus on the logic behind , and . The rest of specs and operations are available on . createGame confirmGame withdraw the Git repository Game creation When a game is created: We will need to emit events with the new game’s ID so that the client can retrieve the value The new game should have a status of (not yet started) 0 The user’s address should be set as the creator The user’s last transaction timestamp should be registered The board should be empty Player 2 should be empty, as well as the corresponding timestamp If any money was sent, it has to be registered The creator has to commit to a random number by providing a salted hash of it Javascript or Solidity specs? How do we translate the above into test specs? We can write assertions in Solidity that run on the blockchain, but the . set of capabilities is rather limited We can’t use multiple accounts to simulate transactions from different users We can easily run into “out of gas” errors, so longer test sequences may not be runnable together We can’t easily check that an error has been thrown Local variables are limited: excess of them will result in compiler errors Stack too deep Assertion failures are not as easy to locate, compared to JS+Mocha If our smart contract is to be used by other smart contracts, we definitely want to test as much as possible from within the contract. In the rest of cases, we would put the heavy work on the Javascript side. In Solidity: And now, the more exhaustive version in Javascript: A few differences between the two approaches: In Solidity, function invocations are immediate and returned values can be retrieved; invocations in Javascript are asynchronous, return values need to be -ed or be retrieved through events call In Solidity, numeric variables can be used directly, whereas in Javascript they need to be converted to/from BigNumber The assertion helpers of Mocha (JS) are flexible and expressive, while Solidity’s assertion libraries are more rigid, we usually need to cast values or write custom helpers So, how does our spec do now? Work is yet to be done. And by that we mean: Defining our data model Implementing the operation Data Model If you are familiar with C struct’s and types, you will feel at home with Solidity. A notable difference is the use of ’s, which allow to store data that can only be accessed through a typed key. Iterating over a mapping’s elements can be achieved by having the whole collection of keys. mapping Given the problem definition, we will build our logic on top of the following data model: Data model of the DipDappDoe contract The structure contains a set of properties that define the state of an individual game Game The collection of ’s is stored in the mapping (not iterable) Game gamesData The state of the board will be stored in the array. Multidimensional arrays are not viable in Solidity, so we will flatten the state and use a 9 position vector cells contains the list of id’s for games that can be accepted openGames gamesData stores how many games have ever been created nextGameIdx What happens when a game ends? Do we keep the data? Solidity allows to values that are no mappings by setting them to zero. However, it is a bit pointless because clearing data costs gas and anyone could reconstruct prior states by replaying earlier transactions. We want games to be public and verifiable, so we should not spend time and money on clearing data that will remain there anyway. delete What we will do is adding and removing indexes on the array, but every game’s data is permanent. openGames Implementation Based on the specs and the model, we can finally get our hands dirty. The minimal implementation that satisfies the specs could be as follows: CreateGame operation of the DipDappDoe contract See the beauty of Test Driven Development? Staying focused in one thing at the time Focus leads to simpler code Simpler code leads to a lesser chance of errors Specs are our roadmap Roadmaps lead to coding only what’s needed and forget about what’s not Sleeping well at night Lets check again: Ready to move to the next one! Game start For the rest of specs of the dapp, we will follow the same approach. If you want to check them out, feel free to check out the along with the . Solidity tests Javascript tests To keep the article readable, we will focus on the logic to satisfy those specs. What needs to happen for a game to start: The game must be created, accepted and not yet started The creator has to reveal his/her random number and the salt Only the creator can confirm a game Player 1 should start if the last bit of both random numbers is equal; player 2 should start otherwise The random number needs to match the given hash, otherwise the creator loses the game The status has to correspond to the player that can start playing The creator’s last timestamp has to be updated The new specs are now failing. Let’s code the contract so that the red errors turn into green checks: ConfirmGame operation of the DipDappDoe contract Comments on the implementation Note that unlike , the operation is not now. This means that transactions can’t accidentally send any ether to it. createGame payable Also note the use of . It is a clean way to abort a transaction as soon as an exception is found. require(...) Solidity provides several ways to handle strings, most notably , and . In a normal environment we would deal with helper methods in order to check and compare values, but in a smart contract, and it shouldn’t be the contract’s goal anyway. string bytes bytes32 dealing with strings is expensive However, the game creator needs to hide the number. We could salt it with another number, but the 256x256 hashes of two ’s are quite easy to precalculate. Hashing a (32 bytes) could consume more resources than the typical random string submitted by a user and protecting data with a string resembles a password more than just using a big number. uint8 uint256 Our hashing function returns a value and with it, equality can be checked in one assembler instruction (i.e. much cheaper to compare). bytes32 Finally, note that we also try to save some gas with bitwise operations. To check that the random numbers have equal parity we could use modulus operator and compare the results. a % 2 == b % 2 Dividing twice is more expensive than a bitwise operation. An XOR of the two numbers will end in 0 if their last bit is equal, and 1 if different. Masking the last bit of the result is the cheapest way: . (a ^ b) & 0x01 == 0 Withdrawal In the last operation that we will cover, the following needs to happen: The game must exist The user requesting the withdrawal must be among the game players If the game creator does not confirm the game on time, the opponent should be able to withdraw the full amount and end the game If a player of a started game does not mark any position on time, the opponent should be able to withdraw the full amount and end the game The game must have ended in any other case Only the winner of the game can withdraw 2x the initial bet Both players can withdraw their initial bet if the game has ended in draw Nothing should be transferred when the game has no money Withdrawal can only be requested once Comments on the specs In our Solidity specs, we need to send ether so that we can request a withdrawal later. How to do that within a contract? We need to add to the invocation: .value(...) uint32 gameIdx = gamesInstance.createGame.value(0.01 ether)(hash, "John"); The sender of such ether would be the and not a regular account like in our JS tests: . test contract addresses[0], addresses[1], ... How can we send ether from a contract that holds no ether? We can tell Truffle to provision some money, by declaring a specific public variable with an amount: initialBalance = 1 ; uint public ether Now we can invoke operations along with money right away. What happens with gas? In this case the contract will be sending 0.01 ether and an eventual withdrawal will transfer 0.01 or 0.02 ether… ? minus gas In the Javascript land, the winner’s balance would increase by 2x the initial bet : he/she is starting the transaction. However, on the Solidity test, the transaction will be started by the Truffle wrapper. A on his own, only users can. minus some gas contract can never start a transaction So the internal Truffle account will pay the gas and the test smart contract will be the sender/receiver of money, without assuming any gas cost. What about checking that the timeout has effect? There is no way to make a smart contract execution for a while, so this will need to be checked from JS/Mocha. But we don’t want our JS tests to wait for 15 minutes to simulate a user abandoning a game. pause We need to parameterize the timeout of the contract, but we don’t want to change code when testing or deploying to production: we could very easily forget. manually Contract parameterizing The best way would be by using the contract ’s parameters. If no timeout is provided, the default (10 minutes) is set and if a non-zero value is provided, it becomes the timeout. Let’s add the constructor: constructor public timeout; uint16 // ... (uint16 givenTimeout) { (givenTimeout != 0) {timeout = givenTimeout;} {timeout = 10 minutes;}} constructor public if else Now let’s update our deployer script so that our tests work with a 2 seconds timeout. migrations/2_deploy_contracts.js LibString = artifacts.require("./LibString.sol"); DipDappDoe = artifacts.require("./DipDappDoe.sol"); const const .exports = (deployer) {deployer.deploy(LibString);deployer.link(LibString, DipDappDoe);deployer.deploy(DipDappDoe**, 2**); }; module function // timeout (2 seconds) And let’s also make sure that our test suite runs on the assumption of the right timeout: timeout = gamesInstance.timeout.call();assert.equal(timeout.toNumber(), 2, "The base timeout to test should be set to 2"); const await Timestamp considerations Timestamps in Ethereum are not meant to be a precise measure of time There is no guarantee of the time at which the next block will be mined Different miners could have slightly divergent internal clocks The smallest time resolution available is around 10 seconds The EVM uses a timestamp based in seconds, while Javascript values work on a millisecond basis Feel free to check the full specs on the article’s . Meanwhile, here is our implementation according to them: GitHub repository Catching issues This is the most complex operation of the contract, hence the most expensive to run. It has taken a while to assert every possible situation, but thanks to TDD we catched a couple of issues that could have gone unnoticed. The first one is not an issue with the contract. In the Solidity tests we were hitting a mysterious , even if the code looked fine. Event messages did not help because the message was a generic . After a bit of binary search, it turned out that the issue happened when -ing money back to the test contract. revert revert transfer Personal accounts can receive ether at any time, but newer versions of Solidity expect contracts to have an explicit default in order to accept any funds. Even if the contract is intended for testing purposes, it remains a contract, as any other. function() payable {} The second issue was caught by the tests checking that games are listed as available when appropriate. We had forgotten to remove games from the available list when creators cancel them after the timeout. Wrap up Our smart contract is implemented, tested and ready. To get you an idea: out of our current code, 2 817 lines are for testing and only 324 implement the actual smart contracts (including directives and blank lines). 90% of our code is for testing; less than 10% of it will reach the blockchain when your contract could potentially hold hundreds or millions of dollars. Much testing is never too much Many buggy contracts have already burned and lost money, or have been hacked because someone didn’t pay enough attention or didn’t check twice. Nobody wants to be the one on that situation, ever. So please, write specs first, code things after and have them audited by a third party before it is too late. This concludes part #1 on our journey to build a distributed application with Solidity. We have described the behaviour of a provably fair game, have specified the expected outputs of its smart contracts and we have finally coded them. The code featured on the article can be found in the folder of the GitHub repo: blockchain _dip-dapp-doe - Distributed app featured in a Medium article_github.com ledfusion/dip-dapp-doe Stay tuned, because in the next part, we will explore how to deploy the contract to a public blockchain and we will build a static web site that interacts with it. If you found the article useful, please don’t hesitate to clap 👏, smile 😊, like 👍 and share with the same 💚 as I put on the article. Thank you! After you take a refreshing break, part 2 is available here: _Today we are going to deploy the contracts and throw some colours on the screen as we design and build a static web site to interact…_hackernoon.com Dip Dapp Doe — Anatomy of an Ethereum distributed fair game (part 2) Photo by on Miguel Sousa Unsplash