Build a “Smart Blockchain” with Solidity, and amazed at how it prevents “Double-Spending”

Somayyeh Gholami
The Capital
20 min readFeb 18, 2021

--

This is how we define “Smart Blockchain”; Any blockchain that uses smart contracts to verify transactions is a “Smart Blockchain.” In several previous articles, we reviewed the benefits of “Smart Blockchain” and explained the details of this idea for future blockchain use. But the next question is whether we can deploy a smart contract on an active blockchain (like Ethereium); To verify transactions without the need for miners? The answer is yes. In this article, we decide to create a smart Ethereum contract; Which can prevent it from “Double-Spending” its own tokens.

In this article, we first explain the method of producing tokens with ERC20 standard and write a smart contract for producing these types of tokens. Then we will develop the same smart contract so that it can store the details of all accounts since the birth of the tokens. This means that in addition to transactions, the details of all accounts that have received these types of tokens at least once are stored. In this way, this smart contract over time can calculate the balance of all accounts (i.e., the general ledger for this type of token) using transaction specifications. But the important point is that this contract has been deployed on the decentralized network of Ethereum, and the state of smart contracts in this network will be updated only after the consensus of all miners and the construction of a new Ethereum block.

In general, delays in updating the state of the smart contracts can allow users to “Double-Spending.” But we all know that miners and block producers verify transactions (with spending very high power consumption) to create each new block on the Ethereum network, and will not allow anyone to “Double-Spending.” But we assume that, for example, a fraudster has been able to take advantage of the decentralization of the network and record an invalid transaction of our token in the Ethereum blockchain and, so to speak, spend his token twice. In this way, the new state of our contract will also be invalid. But we already add codes to our contract that even, in this case, our smart contract will automatically detect and delete invalid transactions in its new state. In other words, we will see that the balance of all general ledger accounts will always be correct, and there is no need to verify the Ethereum network miners.

Do not be surprised; Verification of all transactions is possible with just a few lines of code with a Solidity language, and we will explain all the necessary details for the first time in this article. We also simulate fraud in transactions, so that together we can test the performance of our smart contract. In fact, we will show once again that the task of verifying tokens and cryptocurrency transactions can be entrusted to smart contracts with confidence, and there is no need for all sorts of consensus mechanisms and the loss of hundreds of millions of dollars in electricity costs. The present article was written in February 2021 by Somayeh Gholami and Mehran Kazeminia in two versions in English and Persian.

Persian Article

Understanding “Smart Blockchain” by coding

Step 1 — Producing ERC20 tokens

First, we want to produce one million ERC20 tokens called “AKA.” To do this, we have written a smart contract according to the ERC20 standard. You can download all the codes of this article here. But note that our file name at this stage is SmartCreator101.sol. We will review the details of this contract together.

The most widely used smart Ethereum network contracts are ERC20 token contracts. Fortunately, good articles and libraries have been published in this field. There are even other standards such as ERC223, ERC621, ERC777, ERC827 to complete and develop the ERC20 standard. But to make it easier for readers to understand the details of contract development, we avoided importing standards and libraries. Meanwhile, the essential SafeMath library, mandatory functions, and ERC20 standard events are all included in the SmartCreator101.sol contract.

The basic assumptions of the SmartCreator101.sol contract are as follows:

We store the address of the person who deploys this smart contract on the network as owner. The owner must specify the name, symbol, and number of tokens when deploying the contract. These parameters will always remain constant. Also, from the very beginning, all the generated tokens are equal to the balance of the owner account. By doing this, Only the owner will decide to sell or transfer all the tokens. We set the decimals parameter to zero. That is, we assumed that a fraction of a token could not be transferred and that an integer number of tokens could always be spent.

The functions of the SmartCreator101.sol contract are as follows:

The totalSupply and balanceOf functions are of the view function. These types of functions only read values from the blockchain and return those values. For this reason, they do not even have the cost of gas. This means that if someone calls totalSupply, they will receive the total number of tokens, and if someone calls the balanceOf function with an address, will receive the balance of the same address. These two functions do not do any calculations, but in any case, they are mandatory functions of the ERC20 standard and must remain the same and without any changes in the contract. For example, if you want to change the balanceOf function and add the token name to the function output before the balance value; ERC20 wallets may not work properly.

The next function is ‘transfer.’ The sender always calls this function and at the same time specifies the recipient’s address and the number of tokens sent. This function then performs all the necessary controls, and if it does not find a problem, it updates the transmitter and receiver balance, and so the transfer is completed. Since the sender calls this function, it has to pay for the gas himself. Also, because we have to model the transfer process in the same way in other functions, we decided to add another function called ‘_transfer’ internally. This way, only we write transfer controls and calculations at once. We also added another function called ‘add’ to control Overflow (when adding operations for uint256 variables). This is an internal and pure function. It means it does not read blockchain information and does not store anything in the blockchain.

There are three functions that have not yet been explored. The ‘approve’ function is used when someone wants to delegate someone else to spend a number of their own tokens. To do this, he must call this function and at the same time specify the address of the spender and the number of tokens (allowance). This function then stores the holder’s address, spender’s address, as well as the number of tokens in an embedded mapping. The name of the next function is ‘transferFrom’. This function is performed by a spender call, which must simultaneously specify the holder’s address, recipient’s address, and the number of tokens sent (less than or equal to allowance). Of course, this function will also perform all the necessary controls, and if there is no problem, this function will be executed and will update the sender’s balance (holder), recipient’s balance, and allowance value. Please note that the gas cost is paid by the holder to perform the ‘approve’ function. But to perform the ‘transferFrom’ function, the gas fee must be paid by the executor of a contract, the spender. In other words, although the spender’s balance is the same and does not change when running the ‘approve’ function, it reduced the cost from gas when performing the ‘transferFrom’ function. The last function is called ‘allowance’, which is a view function. Anyone can call this function without paying gas and read the remaining allowance from the blockchain. Obviously, when calling this function, you must specify the address of the holder and the address of the spender you want.

The events of the SmartCreator101.sol contract are as follows:

The name of the first event is ‘Transfer’. After each successful transaction, this event sends a message to the outside environment of the contract and the transaction details are stored in the blockchain. The parameters of this event are the sender’s address, the recipient’s address, and the number of tokens sent. The second event is ‘Approval’. Each time the ‘approve’ function is executed successfully, this event is also stored in the blockchain. The second event also has three parameters. The parameters of this event are holder address, spender address, and allowance value, respectively.

Testing SmartCreator101.sol contract

Now it’s time to test our contract. To do this, we use the Rinkeby and MetaMask test networks. After compiling the contract in Remix, we go to the deployment section. Then in the ENVIRONMENT box, we must select the Injected web3 option. Of course, we have several accounts in the Rinkeby test network. For example, with account number one, we deploy the contract and pass the following values to the contract:

name = “AKA Token” ________The name of the coins
symbol = “AKA” ____________Symbol of the coins
totalValue = 1000000 ______Total number of coins

In this way, the address of account number one will be the owner of this contract, and according to the contract codes, the owner’s balance will be registered equal to one million AKA tokens. Before we test each function, it is better to introduce Aka Token to MetaMask wallet so that we can see the balance of accounts in MetaMask at the same time. So we have to add AKA Token to MetaMask wallet. To do this, first from the Remix, we copy the address of the deployed contract, and in MetaMask, we paste this address on a special box. Also, type the token symbol “AKA” in the lower box. When Aka Token is added to the number one account, MetaMask displays the balance of this account as one million AKA Tokens.

Now we can easily test the performance of all the functions of the contract. The blue functions are view type and have no gas cost. But if we call the orange functions, we will be charged for the gas by the blockchain. Of course, for now, our contract has been deployed on the Rinkeby test network, and the gas fee will be paid with fake Ether. Below, you can see the pictures of some of the tests performed. You can also do more tests to make sure this contract works.

Step 2 — ERC20 contract development

As mentioned earlier, besides the transaction information, we want to store the records of all addresses, from the birth moment of tokens, that at least once have received an Aka Token. In this way, over time, we can calculate the balance of all accounts using transaction specifications. You will also find that verifying transactions and solving the problem of double-spending could be done easily by using the same stored information. So at this point, we need new Structs, mappings, and calculations. On the other hand, let’s remind you that the ERC20 standard must always be considered.

To do all this, one way is to individually deploy a smart block producer contract(BPSC) on the Ethereum network and use that contract in the existing contract. Of course, in this case, the deployment of contracts must be done in two stages. (First, for example, contract X must be deployed, and after specifying its address, this address can be registered in contract Y, and finally, contract Y must be deployed.) Also, in this method, we must observe other cases. But our main goal in this article is to make it easy for the readers to understand the details. So for the second step, we have prepared a contract called SmartCreator102.sol, to access this file, all the necessary items have been added to the text of the SmartCreator101.sol contract, and in fact, the functions of the first contract have been developed in the second contract.

The most important data of the new contract are stored in mapping accounts and struct trxes. Mapping accounts stores all customer information (balance, etc.). Even if an address receives an “Aka Token” for the first time and is not yet a member of this mapping, all the details of this account will be added to the mapping accounts immediately and this account will be a member forever. The ‘trxes’ struct stores transaction details. The functionality of the embedded mapping of ‘allowances’ in the new contract is exactly the same as the previous contract. In addition, some other parameters have been added to the new contract to make programming easier.

In the new contract; The first specification stored in the mapping accounts (client number zero) is the owner specification. And the first specification stored in the trxes struct (transaction number zero) is that the owner balance is equal to the total number of tokens. In addition, the other functions of the old and new contracts are very similar. But the ‘_transfer’ function is an exception and has changed a lot. Most of the tasks we expect from a new contract take place in this function. In the following, we will examine the functionality of this function in detail and take a closer look at its codes.

The Specifications of the ‘_transfer’ function and checking its functionality in the new contract

As you can see in the picture above; Almost the basic code of the ‘_transfer’ function has not changed. It means the function specifications and the basic requirements, are like the first contract. Only we added a new modifier called ‘isLocked’ to the specifications of this function. This modifier does not allow this function to call two different addresses at the same time.

In the image above, you can see the continuation of the _transfer function codes. These codes are the most important part of the new contract. It may seem a bit confusing, but do not worry because these codes do only a few simple things in order. We have divided the image above into several sections, and in the following, we will give a brief explanation for each of these sections.

In Section A; The balance of all accounts stored in the contract is added together and the result is stored in the ‘totalChecking’ variable. Bear in mind that the basis of calculations is always the state of the moment of the contract, which has been updated with the latest Ethereum network block.

Sections C, B, and D; are only activated when the value obtained for the ‘totalChecking’ variable does not equal ‘totalValue’ the actual number of contract tokens. In this case, we find that individuals have been able to take advantage of network decentralization and record one or more invalid transactions from our token in the Ethereum blockchain. In other words, fraudsters have managed to spend their tokens twice. Therefore, in order to correct the balance of all accounts in this contract, we must identify and delete invalid transactions.

In Section B; We zero the balance of each contract account. Of course, this may sound scary, but fortunately, there is no problem, and we still have information on all transactions.

In section C; We start with transaction number zero (related to the owner) and examine each transaction in order. Obviously, the balance between the sender and receiver accounts is updated only when a transaction is valid. The main condition for a transaction to be valid is that the sender’s balance at that moment is equal to or greater than the transaction amount. In this way, the codes in this section cause all invalid transactions that have been registered in our contract through the Ethereum network by deception or even mistakes of the miners to be ignored. This means that double-spending transactions can never be a problem in our calculations, and account balances are always corrected automatically. So there is practically no need to verify the miners. Of course, you can delete invalid transactions immediately, and even before deleting them, notify it outside the contract by Event, so that it can be registered on the Ethereum blockchain. But even if you do not delete, each time these transactions are detected and ignored. Note that these invalid transactions are already stored in the Ethereum blockchain, however.

The code in this section might seem a bit complicated, but actually, it’s simple functionality. If you look at the details of the code for a few minutes, you will notice that these complexities are created solely to access the data stored in the struct and mappings. Also, by paying more attention to codes, you will make sure that the mathematical logic of these few lines of code is much stronger than the logic of various consensus mechanisms, and the codes of this contract can be a much better alternative to various consensus mechanisms (for cryptocurrency transactions and token transactions). The main problem of decentralized networks has always been the problem of double-spending. But in this contract, this is mathematically impossible to happen. Even for double-checking the contract codes, we will have another ‘require’ in section E.

In Section D; The balance of all accounts (modified in Section C) are added together, and the result is stored in the same ‘totalChecking’ variable. In other words, the value calculated in section A for the ‘totalChecking’ variable is modified.

In Section E; There is only one ‘require’. This ‘require’ once again controls the equality of the value obtained for the ‘totalChecking’ variable with the actual number of contract tokens (totalValue). Please note that this condition will definitely be met. Because the variable ‘totalChecking’ was calculated once in section A, and if it was not equal to ‘totalValue’, it was modified in sections B, C, and D, and now this condition certainly has met.

In the following, we will give a brief explanation for the last three parts of the ’_transfer’ function (above picture). These three sections are only executed when all the necessary controls have been run. Of course, as you saw in the previous sections, even in the presence of invalid transactions, the balance of all accounts has been carefully adjusted from the beginning.

In section F; If the recipient receives the tokens “AKA token” for the first time, all the recipient details will be stored in the mapping accounts. The ‘NewAccountWasOpened’ event is also executed immediately.

In section G; First, the number of tokens in this transfer means ‘amount’ is deducted from the sender’s balance, and then exactly the amount is added to the recipient’s balance. As you know, it is always recommended to update the sender’s balance first and then update the receiver’s balance.

Section H; The final part of the function is ‘_transfer’. In this section, all new transaction details are stored in the trxes struct. Also, the ‘Transfer’ event will be executed immediately.

Testing SmartCreator102.sol contract

You can simply test a new contract just like the initial one. But please note that normally transactions are executed only if they are valid. In other words, you simply can not observe the performance and test this contract in dealing with an invalid transaction. So what to do?

Our suggestion is to make small changes to the code before deploying the contract so that we can artificially simulate the occurrence of double-spending. To do this, we have created the SmartCreator102x.sol file, and in the following, we will explain the changes made to this file. Bear in mind that this file is for testing only, and the main file at this stage is the SmartCreator102.sol file. Also, in order for the tokens of this stage to be different from the tokens of the first stage, we have named the symbol of the tokens of the second stage AKAx.

For example, suppose that the total number of second-stage tokens is one million “AKAx” tokens and the ‘owner’ of the contract is account number one. In this way, after deploying the contract, the balance of account number one will be equal to one million. In the first transaction, we send five hundred thousand tokens from the first account to the second account. In the second transaction, another five hundred thousand tokens will be sent from the first account to the third account. Now we assume that in the third transaction there is fraud or error and the third account succeeds in spending all five hundred thousand tokens twice. This means, for example, that the third account can send one million tokens to the fourth account. Therefore, after the third transaction, the account balances will be as follows:

Account balance number one = 0
Account balance number two = 500000
Account balance number three = 0
Account balance number four = 1000000

But you will see that this mistake does not continue and our contract automatically detects the invalid transaction during the fourth transaction and ignores it. It will also correct the balance of all accounts at the same time. That is, if, for example, in the fourth transaction, one hundred thousand tokens are sent from account number two to account number one, in this case, you will be surprised and the balance of accounts will be corrected as follows:

Account balance number one = 100000
Account balance number two = 400000
Account balance number three = 0
Account balance number four = 500000

Of course, there are several ways to create this type of fraud (or mistake) for the contract. For example, in the SmartCreator102x.sol file, in order for transaction number three to be able to spend twice, we have just added the following code to the SmartCreator102.sol file:

For testing the contract in the second stage; We deployed the SmartCreator102x.sol file on the Rinkeby test network according to the specifications mentioned earlier. Then, according to the above description, we did four transactions. If you look at the pictures below, you will see that the test results are completely in line with our predictions. Of course, you can also do more tests to make sure this contract works.

In this way, over time, we will always have an independent and reliable general ledger for our tokens, and there is no need to verify by miners. Of course, as you can see, in this contract, the verification of the transaction was done before executing each transaction. But this scheduling, in other contracts, can be adjusted on another basis (for example, every few seconds). Meanwhile, In the third step, we provide several functions to access account and transaction information. Note, however, that this agreement is deployed on the Ethereum blockchain, and transaction information is stored and accessed as an ‘event’ in the Ethereum blockchain. You can see this information by using the tips and pictures below.

Step 3 — Adding new features and complete the contract

To complete the contract, we add five new functions to the second phase contract. The name of our contract in the third phase is SmartCreator103.sol. Note that the information of all transactions, the balance of all accounts, and other important information are stored in this contract. Therefore, three of the new functions are of the ‘view’ type, and their function is simply to return the information stored in the contract. In addition, two other functions provide the possibility of selling tokens based on Ether. Read a brief description of the performance of these five new functions.

As you have already seen; Addresses, that were sent AKA tokens at least once, are recognized as members, and their full account information (balance, etc.) is stored and updated forever. Now, if one of the members calls myAccountInformation function, he can receive his account balance, the time of his membership, the time of the last sending of the token by himself, etc., without paying for gas.

But the systemInformation function can be called any Atrium network address. This function returns general information about the status of tokens. That is, in order; Returns the name and symbol of the tokens, the total number of tokens, the selling price of the token based on ether (Wei), the inventory of the contract address based on the ether, the total number of members and even the total number of transactions performed.

Any Ethereum network address can also call the lastBlock function. This function returns the information of the last transaction performed.

The buyTokens function is the payable type. Any address can perform this function and buy some aka tokens by paying ether. Of course, as you know from the beginning all Aka tokens are in the owner account and the contract address does not have a token. This function, after the necessary controls for this transaction, first receives the ether from the customer and deposits it to the contract address, and then transfers the tokens from the owner’s address to the customer’s address.

But payToOwner function can only be executed by the owner. If with the sale of aka tokens, some ether accumulates in the contract address, the owner can perform this function. By performing this function, all ethers collected in the contract address are transferred to the owner’s address.

Testing SmartCreator103.sol contract

Just like the previous steps, you can deploy the SmartCreator103.sol file on the Rinkeby test network and test these five new functions. Only when testing the buyTokens function, note that in the codes of this contract, by default, the price of each aka token is equal to 1e12 wei. On the other hand, in this contract, the value of decimals is zero, and no fraction is defined for aka tokens. So the amount paid based on wei must be a multiple of 1e12. Otherwise, the buyTokens function will not run.

Summary and conclusion:

You can download all the article files here and do more tests.

The SmartCreator101.sol file is a smart contract that generates tokens according to the ERC20 standard.

The SmartCreator102.sol file is another smart contract that, in addition to generating ERC20 tokens, stores all transaction details and member account details. In addition, this contract can verify the transaction of its own tokens and does not allow anyone to spend twice. In other words, this contract does not require the services of Ethereium network miners to verify transactions. The mathematical logic of the codes of this agreement is much stronger than the logic of various consensus mechanisms, and the general ledger of this intelligent agreement is quite reliable.

The SmartCreator102x.sol file is the same as the previous contract, except that in the third transaction, it sends the transaction amount twice to the recipient. This was done simply to simulate double-spending. Of course, we saw that immediately in the fourth transaction, the contract was able to identify the error or fraud and change the balance of all accounts to the correct state.

The SmartCreator103.sol file is the final version of this smart contract. This contract has five more functions than the second stage contract, and with these functions, new features have been added to the contract.

The last word

Ethereum Smart Contracts have many limitations and are designed initially for specific applications. However, we were able to deploy a smart contract on this network that could detect invalid transactions. Of course, we hope that future blockchains will be planned from the beginning to use BPSC (Block Producer Smart Contracts) and that these types of smart contracts can be a very cheap alternative to current methods. We have already considered the “Smart Blockchain” proposal for future blockchains in another article.

Unfortunately, currently, hundreds of millions of dollars per month in electricity cost for miners and blockchain builders, and a large portion of that staggering cost is related to controlling the double spending of cryptocurrencies and tokens using consensus mechanisms. On the other hand, the environmental problems of the current methods cannot be ignored. For all these reasons, we think that “smart blockchain” can be an important step for the sustainable development of blockchain technology. If you are interested in the topic, you can also read our previous articles.

Somayyeh Gholami
Mehran Kazeminia

--

--

Somayyeh Gholami
The Capital

Master of Applied Computer science (Specializing in BioMedical/Health) at VUB university