A closer look at the Handshake dApp.
This is the next part of the Handshake Protocol series. Let’s take an in-depth look at our first ever dApp, Handshake.
Handshake has been putting lots of promises on the blockchain ever since our launch last week. Currently running on the Rinkeby Testnet, it gives users the opportunity to test the dApp before we migrate to the main net.
Hello! I’m Trong, lead developer for Handshake. 👋 This article is a follow up to The Handshake Protocol and will explain how a Handshake is implemented on the Ethereum network using Solidity. We will also demonstrate the use of the Truffle Framework for easier development. The 2 main contracts that Handshake uses are the BasicHandshake and the PayableHandshake.
First we will look at Ethereum smart contracts and how they are implemented on the blockchain.
- Compile the source code (.sol file) to retrieve the bytecode and the ABI of the contract. Since the contract needs to be run on all machines in the Ethereum network, the source code is compiled into binary bytecode for efficient storage. The other part, ABI, is the public interface of the contract. For example in the contract below, when a client wants to run the function shake, they need to provide the 2 parameters (hid and offchain) with the correct type. The ABI stores this information for the client.
Deploy the contract: This is when you share your contract to the Ethereum network. Every subsequent interaction with your contract will be verified by every single node on the network. Since all the nodes need to store your contract, you must pay a fee (called gas in Ethereum) to use this process.
Execute transaction: When you interact with the contract to retrieve/store or execute logic on the public blockchain. This step also requires gas to run.
These steps can be executed by using the Solidity compiler (solc) and web3.js module. However, this approach is not practical as we would need to manually copy and store all the information in order to pass through to the next step. In this article, we use the Truffle framework to help us with the above steps. Truffle provides commands to do the above process in a simplified manner.
Through Handshake we demonstrate the development process and abilities of smart contracts through a simple yet powerful concept. Handshake is a sleek, easy and cryptographically secure way to sign agreements and put promises on the blockchain. Traditional signatures are cumbersome, slow and prone to forgery and loss. eSignatures are also just as vulnerable to fraud. A lack of regulation means the security of your signature is often only as good as someone else’s word. These problems are perfectly suitable for blockchain technology to solve: we store the agreement on the blockchain and the user signs with their own digital, cryptographically-secure signature.
First, we will show you the contract itself implemented in Solidity and explain the important details. You don’t have to be familiar with Solidity to understand the core logic since the language itself is clear (in terms of syntax). For demonstration purposes, the handshake is kept simple: it only stores addresses of the involved parties and doesn’t handle payments.
The BasicHandshake contract consists of a list of the Handshake (which is a struct with two address: the initiator of the handshake and the acceptor). In blockchain technology, the address is unique and is usually the hash of the public key. To prove that you are the owner of that public key, you need to provide the corresponding private key. You should be the only person with access to your private key, ensuring that there is no way for a malicious party to impersonate you . By utilising the blockchain, we solve the problem of forgery that occurs in traditional pen signatures by using cryptography.
The first function creates a new handshake by the initiator and saves to the list of handshakes controlled by this contract. The function is marked as public which means everyone can call it (including other contracts on the Ethereum network). Here we introduce 2 concepts that are in Ethereum smart contracts:
- Msg.sender: a special keyword storing the address of the one who calls this function. When you call a function of a contract, you need to provide your identity (your public address) and prove that you own it by signing with your private key. With msg.sender, you can be sure that the one calling this function really owns the public address.
- Event: to interact with the outside world (e.g., a backend database of the dApp), we emit events with the result we want them to receive. Here, we emit __init event with the id of the new handshake. Multiple function calls can happen around the same time, therefore the backend needs to provide a unique key to be emitted along with the id.
After a handshake is created, the other party can accept the handshake and publicly record that action by calling the shake function. If the initiator hasn’t specified a unique acceptor in the initialization process, we set the acceptor as the one calling shake. Otherwise, we check if the one calling shake is the same as the one specified by the initiator. Here, the keyword required receives a boolean argument and reverts the call if it’s false. By reverting, all works made by this function, call will be undone and the function terminates after the require statement. You can see how a function call to a smart contract is often called a transaction (since the whole function needs to succeed for the process to take effect, just like a transaction in a database).
With only 30 lines of code, we materialized the concept of handshake and put it to use with all the advantages of blockchain technology: secure, immutable and always available.
Compile, deploy and interact with the contract
Truffle requires you to set up the project (with all the code and config) before you can take advantage of its commands. We provided the full project in our github repository so you can start testing it immediately.
In the repo, you can see a few directories:
- Build: all the compiled contracts (bytecode, ABI and other relevant information are stored here as a JSON file for each contract).
- Contracts: the solidity source code of the contract.
- Migrations: special configuration file for Truffle for the deploying process.
- Test: contains unit tests for the smart contracts (we use nodes for writing the unit tests).
Before using Truffle to compile and deploy a handshake contract, we need to connect to the Ethereum network. Instead of the main net (which requires real ETH to run contracts) or the test net (which takes a bit of time for the miners to process transactions), we will run a whole Ethereum network locally (with only one node: our own computer). You can run this private network using Ganache.
Download Ganache client, run it and config the network to be running locally on port 8545 (you can run on a different port, but make sure to use that the port is in truffle.js config file).
Restart Ganache to use the new config. There are 4 tabs in the Ganache UI:
- Accounts: Ganache conveniently provides 10 address with 100 ETH each for you to test your contracts. You can click on the key to the right of each row to get the private key of the corresponding address.
- Blocks: the blocks are in the main chain of the private network. As we haven’t run anything yet, there’s only one block: the genesis block (with height 0).
- Transactions: A list of transactions that are processed. The list should be empty since no transactions were sent to the blockchain.
- Logs: low level logging information.
With Ganache still open, we can start running Truffle’s commands. The default configuration connects truffle to the private network provided by Ganache.
First, start up the truffle console:
To compile the contracts, run the compile commands with — all flags (to recompile the contracts no matter if it’s modified or not). The artifacts (the JSON files contain bytecode, ABI, …) will be written in the build folder. You can take a look at these files to see what else Truffle also stores.
The next step is deploying the contract to the blockchain. This process is also called migration when using Truffle’s terminology. Take a look at the migration folder, you can see the 2 files implementing the logic to deploy the contracts. Instead of building the transaction to deploy the contract from scratch, you can write a small script to get all the needed information (stored in the artifacts from the previous step) and deploy to the network with 1 function call.
Running the migration command with — reset flag (to redeploy regardless of if the source code has changed or not), Truffle scans the migration folder and runs all the files sequentially. Note: the special contract called Migrations.
This contract is created by Truffle to keep track of the deployed contracts and enables you to skip them if the source code hasn’t changed. Each contract deployment is a special transaction (or function call) that returns the new contract address.
If you look at the transactions tab in the Ganache GUI, there are 5 transactions (sorted from newest to oldest). Three of them are contract creation transactions that correspond to the 3 contracts: BasicHandshake, PayableHandshake and Migration. The additional 2 contract calls are Truffle’s internal call to update the id of the contract that has been deployed. Click on each transaction to see the detail such as the amount of gas being used, msg.sender, etc…
Now we can instantiate the object to interact with the deployed contract. Calling a contract’s function with this object will create a transaction along with all the arguments we provide.
Let’s create a new handshake with the deployed contract. Here, we call the init function with 2 parameters: the acceptor address and a string represents the backend unique key (which is a dummy since we don’t need it right now). In the Truffle console, you can access the public address of the 10 accounts provided by Ganache by accessing web3.eth.accounts[i]. You can the compare it to the list of accounts in Ganache GUI to see that they indeed match. The result is that the transaction info with the event __init is emitted. To specify which account will sign the transaction (and therefore set msg.sender to that public address), we provide an additional parameter which contains a key from the sender and its value is the sender’s address.
Note: since this is a private network, the transaction is confirmed and included in a new block immediately. In a public network, after making the call, the result consists of the transaction hash only. We need to monitor the network with the hash until the event __init emitted.
The emitted event contains the id of the new handshake. Let’s try to ‘hack’ the contract by calling the function shake using a different account from the one specified when calling init function. As you can see, an exception occurred saying that the transaction has been reverted.
Now let’s shake with the correct account as sender. In the figure below, the call returns a transaction with event __shake emitted which means our handshake was successful. But all we changed is the account that was used; surely an attacker can also try to change the sender’s address to try to hack our contract? Well, when we run the Truffle console with the private network, all 10 accounts created initially are unlocked. When providing the public address as the caller of the function, we also need to unlock that address by providing the corresponding private key. If you keep your private key safe, there’s no way for the attacker to pretend to be you and forge a handshake that you’ve never made.
Congratulations, you just successfully made a cryptographic handshake on your own private blockchain.
The simple handshake contract doesn’t store anything beside the address of the involved parties. But what if you want to store the content of the agreements between them (which might be a simple note or a full-fledged document)? We can modify the contract to store the note as a string or the content of the pdf document in binary. However, there is a better way: instead of storing the documents on the Ethereum blockchain itself which would require quite a lot of gas to do, we can store it on a public decentralized data storage platform. IPFS and Swarm are perfect candidates for this process. The handshake contract then stores the link to the document which has been shortened (making it cheaper to store) and it still retains the strength of decentralization.
Handshake is a simple yet powerful decentralized application. For more advanced use cases that involve payments between parties, take a look at our PayableHandshakes on our github. We have open sourced the handshake contracts for you so that you can test them yourself. Visit our github and start exploring.
Ask us lots of questions and please give us feedback on telegram: https://t.me/ninjadotorg.
Coming up next: how we used the Handshake Protocol to build the Handshake app — a code walkthrough.
Till next time!