In this subsection, we deeply study common security problems in smart contracts and summarize five types of main security issues. Meanwhile, we propose a specific formal verification method for each security issue.
4.1.1. Integer Overflow
Integer overflow is the most common security issue in smart contracts. This security vulnerability is usually inadvertently introduced by programmers. It may cause some features of the contract to fail. In the most severe case, it may lead to hacker attacks and economic losses. For example, BEC, SMT, and EDU have been attacked by hackers due to integer overflow security vulnerabilities, which results in the value of the tokens becoming zero.
We formally verify the integer overflow issue in smart contracts by building a safe math library in Coq. Because the parameter types in smart contracts are unsigned integer types, we can represent unsigned integers directly through natural numbers. In order to ensure the reliability of the entire construction process, we did not use the conversion functions of nat and bool provided in Coq. Instead, we build functions nat_blt, nat_ble, and nat_neq from scratch. Furthermore, safe arithmetic and safe mapping are defined step by step.
First we define a function
nat_ble in
Figure 3 (lines 1–5) according to the function
nat_blt in
Figure 2. Since the function
nat_blt constructs the relation “<” on boolean, it is obvious that the function
nat_ble defines the relation “≤” on boolean. As shown in
Figure 3 (lines 7–13), on the basis of the definition of
nat in Coq, we construct a conversion function that converts the equality relationship of natural numbers to boolean values. We use the recursive definition method in the same way that function
nat_ble is defined. The function
nat_beq can judge whether two natural numbers are equal. If two natural numbers are equal, the output is true. Otherwise, the output is false.
Figure 4 declares some basic data types for defining safe computational functions of smart contracts. All integer types are modeled as
nat in Coq formalization. The type
uint256 is a 256-bit unsigned integer type. We normally define integer variables in contract as
uint256 type. The
uint8 type is used to define decimals. Both the
time type and the
address type are defined as
nat. We define
av in the line 6 and
aav in the line 7 to represent the Solidity “mapping“ type in Coq. The type
av represents the mapping (
address ⇒
uint256) and
aav represents the mapping (
address⇒ (
address⇒
uint256)). The mapping about address and value occurs more frequently in most smart contracts. We can assign them to other types in the specific contract.
maxuint256 is the maximum of the type uint256 and
Error is the return value when an exception occurs. The command
Parameter declares a global variable. In order to avoid interference with normal parameters of type
uint256, we set the value of
Error to be greater than
maxuint256 in line 11.
Next, as shown in
Figure 5, we define safe arithmetic, which consists of some basic operators. These operators, which can prevent overflow, include addition, subtraction, multiplication, and division. The function
safe_plus implements the safe addition of two uint256-type variables. By the judging condition in line 2,
Figure 5, it ensures that the sum of two numbers must be greater than or equal to each addend. The function
safe_minus implements the safe subtraction. Since the return value needs to be a positive number, the number
b must be less than or equal to the number
a (line 6,
Figure 5). We define the safe multiplication in lines 8–10. According to the division and the function
nat_beq, it can determine whether there is an overflow. The safe division is defined as the function
safe_div. We ensure that the dividend is not zero and prevent overflow by the condition (line 13,
Figure 5), where
mode is a modulo function.
The functions defined above are all for variables of
uint256 type. In smart contracts, we usually do arithmetic on mapping types. Therefore, we construct the safe arithmetic of mapping in
Figure 6. In
Figure 4 (lines 6–8), we define two mapping types
av and
aav that are common in smart contracts. Therefore, we build separate safe arithmetic functions for these two types. First, a function
dadd_beq (lines 1–4,
Figure 6) for judging two variables of type
(address * address) is constructed according to the function
nat_beq. It is to determine whether two variables are equal. Then we define two functions
map_rep and
map_repd in
Figure 6 (lines 6–9). Based on these two functions, we can replace a value in the mapping or add a value to the mapping. Taking function
map_rep as an example, we look for addresses equal to the address
(a:address) in the domain of mapping
(m:av) (line 7,
Figure 6). The values of those addresses are assigned to the value
(b:uint256). The
$\lambda $-abstraction meets our requirements of the above development and it is implemented through function
fun in Coq. As shown in lines 11–12, we define functions that map values to zero. Finally we define the safe arithmetic on the mapping in lines 14-25.
av_inc and
aav_inc are addition on mapping.
av_dec and
aav_dec are subtraction on mapping. The multiplication on mapping is defined as
av_mul and
aav_mul. The division on mapping is defined as
av_div and
aav_div.
As shown in
Figure 7, notations can be introduced to ease the reading and writing of specifications. This also allows us to stay close to the way mathematicians would write. Moreover, we can also define precedence levels and associativity rules of notations in Coq. So far, we have completed the construction of the safe math library in Coq. The safe arithmetic and safe mapping will be used when defining the function specification of smart contracts. By verifying the function specification, we can determine whether there is an integer overflow issue in the smart contract.
4.1.2. Function Specification Issue
The second type of security issue is the function specification issue of smart contracts. Nowadays, there is no uniform function specification for the implementation of many smart contracts. Smart contracts are interactive multi-person collaboration programs. If the function in the contract is not standardized, misunderstanding of the contract behavior will arise, which will lead to a large number of security problems. We present a formal verification method for the function specification issue in smart contracts. As shown in the following figure, we define an abstract structure of function specification in Coq by analyzing the construction of functions in smart contracts.
First, we declare the specific state of the contract in
Figure 8 (lines 1–2). The command
Record defines a record type
State in Coq. Its constructor has the default name
Build_State. The identifiers
st1,
st2, and
st3 are parameters of the record. These parameters include token name, total supply, owner address, token balance, etc., which may be different types. We use these parameters to describe the state of the contract. Then we define a abstract event model
Event in
Figure 8 (lines 4–9). In line 5, we define a creator event
Creator, which is usually called in an initialization function. The parameter
Sa is the address of function sender and
cr1,
cr2, and
cr3 represent the initial parameters. We define events for specific functions as
Fun1,
Fun2, and
Fun3. The number of input parameters for these events depends on the specific function. The event
Throw indicates that an exception is thrown and the parameter
Ca is the contract address. Finally, as shown in
Figure 8 (lines 12–15), we define the abstract structure of function specification
Specification. The specification of a function is generally composed of four parts. In the first part, the
require represents requirements via the command ‘require’ calls in Solidity. These requirements are formalized as the type
Prop. The
transfer implements the state transition on the basis of the behavior of functions in the second part. The safe arithmetic we defined in
Figure 5 will have important applications in it. The
event records events generated via event calls in the third part. We implement it through the definition
list provided in the Coq library. In the fourth part, the identifier
return define the return value of the function. The type
returns will change depending on the type of return value. All parameters in these abstract models can be changed according to the specific smart contract.
In the verification of a specific contract, we define the contract specification based on the above abstract model. Then we describe the Solidity code. Finally, on the basis of the mathematics theorem proof, we verify whether the Solidity code of the contract satisfies the contract specification.
4.1.3. Invariant Issue
There are some invariant quantities in the process of contract creation, function calls, and environment changes. The third kind of issue is about these invariant quantities. When we build a smart contract, we always hope that some parameters are not changed with the external environment and function calls. For example, the total amount of tokens needs to remain the same in most contracts.
As shown in
Figure 9, we present the abstract model of contract status, message calls, and current environment based on the Coq proof assistant. The type
Contract (lines 1–2) records the status of the current contract, including the contract address
adr and the state
st of each variable in the contract. These variables may include the token name, total supply, owner address, token balance, and so on. Then we define the abstract message call model
Message in lines 4–8. The parameter
data is the complete call data, including call information for each function. We define the input parameter
mdata based on functions in a specific contract. The parameter
(gas:uint256) represents the current remaining gas in the contract. The “gas” is the name of a special unit used by Ethereum. It measures how much work action or a series of actions needs to perform. We can inquire about the address of the message sender through
(sender:address). The
(value:uint256) represents the amount of money attached to the message. Finally, in lines 10–15, we define the environment type
Environment of the contract. The contract environment consists of some information about the current block, including block hash, miner’s address, difficulty, gas limit, and time stamp. In the specific contract, we can increase or decrease the parameters according to the actual situation.
After defining the abstract model of contract status, the specific contract state can directly defined by the record type
state in
Figure 8. Moreover, as shown in
Figure 10, we define a structural model
mdata of the data in message calls. We can adjust it according to the specific contract. The message data of creator function is declared as
md_creator in line 2 of
Figure 10. The parameters
cr1,
cr2 and
cr3 represent the initial parameters. The constructors
md_fun1,
md_fun2, and
md_fun3 are message data for specific functions (lines 3–4,
Figure 10). The number of input parameters for these events depends on the specific function. According to the specific function of the smart contract, then we construct an evaluation step that includes any of the possible invocations in the contract. When the environment is changed, we construct an evaluation step for the environment as well. Finally, the invariants are determined according to the specific requirements of the contract. We prove in the form of theorems that function calls and environmental changes do not change the invariants.
4.1.4. Authority Control Issue
The fourth is the issue of the authority control of smart contracts. Generally, an administrator (or owner) is set up in the smart contract. Administrators generally have super privileges, such as burning tokens, freezing accounts, closing transfers, and so on. The security risks of such contracts are relatively significant. Once the administrator’s private key is stolen, it can easily cause huge losses.
As shown in
Figure 11, we construct a formal verification method for the authority control issue. We verify the issue according to the mathematical theorem
Owner_Prop. The input parameter
msg is the message call of the function. The
spec is the specification of the function. The parameters
C and
C’ are specific contract model. The
E is the event list of the contract. In lines 2–3, we get the requirements, event, and state transition of the function
spec. Furthermore, we specify that the message sender is the owner of the contract
C in line 3. Then we can prove the specific property about the authority of the owner. The first
Prop in line 4 is the property about the variable account. The second
Prop is the specific property about authority control. The specific
Prop of different contracts are different.
4.1.5. Behavior of the Specific Function
The fifth kind of problem is about the behavior of the specific function in the smart contract. There may be various functions in the smart contract, each of which has a different purpose. The behavior of different functions can have different effects. In general, the function mainly operates on the following aspects: total supply, the balance of an account, contract status, and contract authority. Next, we consider the specific properties of functions. We will not discuss the permission change caused by functions because of the existence of the fourth security issue of smart contracts.
As shown in
Figure 12, we define an abstract model
Fun_Prop for verification of the function behavior. The input parameters
C and
C’ are different contract model. The
E is the event list of the contract. The parameter
spec represents a concrete function specification model. Line 2 defines the requirements of the function. The event and state transition of the function are obtained from lines 2–3. Then we can define the specific property of the function according to the above information. According to different functions, we can present different formal theorem descriptions.