Smart contracts benefit from the immutability (of code) and decentralization (of computation) provided by blockchain. However, they inherit problems because of the same reasons. The immutability prevents developers from patching the contract code to fix the bugs that are previously undetected. Any undetected bug could result in significant disruption (e.g., a loss of digital assets). What makes it even worse is that the decentralized nature of blockchain would make it almost impossible (unless a hard fork is performed) to revert the damage once the transactions from the attackers are processed and packed in blocks.
The current best practice for smart contract protection is to do a thorough audit of the contract code before its deployment. The purpose is to make sure that the code is free of all of the known bugs. There have existed various tools, standards and professional auditing services that help you to achieve that goal. However, all of the measures are PASSIVE, that is, they can only deal with the known types of mistakes in code, but not the unknown. With smart contracts designed to do more and more complicated jobs, it is only a matter of time that a new type of vulnerability would emerge and be exploited.
VeChain Foundation has been collaborating with researchers from the South China Normal University to come up with a proactive strategy to protect smart contracts. It is designed to allow smart contracts to be capable of detecting and nullifying suspicious transactions and more importantly, to be capable of adapting to handle the unknowns. It is particularly powerful against a category of attacks that attempt to execute the contract code in a previously unexpected and damaging order. The famous attacks on DAO and Parity fall in this category.
The strategy differs from the current passive methodology in the following ways:
It allows us to build in the contract code our prior knowledge of what are the legitimate ways we can use the contract;
It automatically nullifies any transaction that executes the contract code in a way that contradicts our prior knowledge;
It builds in a mechanism that allows contract owners to PATCH their prior knowledge to deal with some of the situations that are previously unexpected.
The protection of a smart contract consists of two phases: the pre-deployment phase and post-deployment phrases. In the pre-deployment phase, the contract’s EVM bytecode is first turned into a directional graph of bytecode blocks, representing the corresponding
control flow. A further analysis is conducted to assign weights to the graph edges such that the sums of the edge weights along different paths allowed in the graph end up with different values.
Keep in mind that a path corresponds to a possible way for a transaction to execute the contract code. Having had each possible path to have a different sum, we would be able to distinguish paths corresponding to the legitimate use of the contract and those corresponding to potential attacks.
The next step is to modify the original contract bytecode to implement additional functionality that allows the contract to:
Assign and accumulate edge weights during the execution of its code triggered by a transaction;
Manage a set of safe paths as the prior knowledge;
Validate the current code execution against the prior knowledge and revert the execution if the corresponding path is not in the set; and
Log the necessary information for off-chain analysis.
The last step of the pre-deployment phase is to use the contract in a testing environment to collect a set of all the safe paths as the prior knowledge and save it inside the contract. Now the contract code is ready to be deployed to blockchain.
After its deployment, the smart contract is protected in the way that is illustrated by the above figure. An incoming transaction triggers the execution of the contract code following a particular path. The path is checked against the prior knowledge, that is, a set of paths deemed safe by the contact owner, and if it is not in the set, the execution will be reverted and its information logged for further auditing. The contract owner can later on decide whether the interception is a false alarm or not based on the logged information. If it turns out to be a false alarm, the owner can call the built-in function to add the new path to the safe-path set to avoid future blocking of the same type of transactions. The owner can also arrange to reimburse the cost to the transaction sender and re-execute the transaction.
Our proactive strategy for smart-contract protection certainly has its own limitations. It is designed to protect smart contracts against the attacks that exploit the previously undetected paths of executing contract code. Therefore, it cannot cope with the vulnerabilities caused by integer underflow/overflow or external data dependency. Furthermore, since we need to modify the original contract code to have extra functionality, it will increase the gas usage for contract deployment (? 200k gas more) and contract calls (? 30k gas more).
This article has described a proactive strategy for smart contract protection. The strategy is particularly powerful against the attacks that exploit the previously unexpected order of executing contract code. Applying the strategy allows smart contracts to be capable of self detecting and nullifying suspicious transactions, and more importantly, to be capable of adapting its prior knowledge to handle the unknowns.