VerX Safety Verification of Smart Contracts
VerX Safety Verification of Smart Contracts
Abstract—We present V ER X, the first automated verifier able Investors can claim refunds only if the sum
to prove functional properties of Ethereum smart contracts. of deposits never exceeded 10, 000 ether
V ER X addresses an important problem as all real-world con-
tracts must satisfy custom functional specifications. Formalized requirement (§V)
Contract
V ER X is based on a careful combination of three techniques,
enabling it to automatically verify temporal properties of infinite- always(claimRefund()==>
state smart contracts: (i) reduction of temporal property verifi- ! once(sum(deposits)>= 10000))
cation to reachability checking, (ii) a new symbolic execution
engine for the Ethereum Virtual Machine that is precise and
efficient for a practical fragment of Ethereum contracts, and
Abstraction predicates:
(iii) delayed predicate abstraction which uses symbolic execution
during transactions and abstraction at transaction boundaries. Instrumented e == Escrow
contract sum(deposits)>= 10000
Our extensive experimental evaluation on 83 temporal prop-
erties and 12 real-world projects, including popular crowdsales add predicates
and libraries, demonstrates that V ER X is practically effective.
Symbolic execution + Delayed predicate abstraction (§VI-VII)
Keywords-smart contracts, temporal specification, automated
verification
V ER X
Verified Counter-example
I. I NTRODUCTION
Fig. 1: Usage and verification flow of V ER X. A requirement
Ensuring correctness of smart contracts, programs that run is formalized as a temporal safety property. Then, the contract
on top of blockchains, is a pressing security concern. Today, is instrumented with the property and relevant predicates for
billions worth of USD are controlled by smart contracts, and verification are automatically extracted. These are fed into
only in the past couple of years, millions of these have been V ER X, which either verifies the property, outputs a counter-
lost by exploiting subtle flaws in the logic of these programs. example, or indicates that additional predicates are needed.
The issue is exacerbated as the code becomes immutable
once placed on the blockchain and hence bugs found after
deployment cannot be fixed. formally verified before deployment. While this observation is
Current audit practice. To mitigate this problem, current not new, only a handful of smart contract projects (e.g., Mak-
audit practices of smart contracts involve checking whether the erDAO [7]) have been formally verified so far. Current veri-
code is safe against two kinds of vulnerabilities: (i) generic fication efforts are conducted using heavyweight interactive-
security errors such as reentrancy and overflows, typically theorem provers, such as Isabelle/HOL [9] and Coq [68].
achieved by running automated security tools (e.g., Secu- These require non-trivial manual effort and expertise, making
rify [65], Slither [4], and Mythril [50]), and (ii) deeper, custom the audit process expensive and time-consuming, resulting in
functional requirements, often done by manual, best-effort limited adoption by the developer and audit communities.
code inspection. An example of such a requirement is: “The Key challenges. To address this problem and enable devel-
sum of deposits never exceeds the contract’s balance.”. While opers and auditors to formally certify smart contracts without
substantial advances in creating security tools that discover requiring deep expertise in formal verification, one would ide-
generic errors were made over the last few years, there has ally create a verifier capable of automatically proving custom,
been little progress in automating the verification of deeper, functional properties. However, building such an automated
more challenging functional properties. This is particularly verifier is challenging for at least two reasons.
problematic because manually checking the satisfaction of First, smart contracts often interact with external contracts
such deeper requirements often involves non-trivial reasoning, via function calls. In turn, these external contracts may call
increasing the chance that critical bugs slip in production. back the smart contracts we want to verify in arbitrary ways
Goal: Formal guarantees for smart contracts. We believe (since their code is unknown). Automated verification in the
that smart contracts, similarly to any safety-critical system presence of such arbitrary and unboundedly many callbacks
(e.g., controllers deployed in cars and airplanes), must be from external contracts is challenging.
1662
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
R0 Claiming a refund by an investor decreases the escrow’s ϕR0 ≡ 2(claimRefund(address p) → Escrow.balance =
balance by the investor’s deposit. Escrow.balance• − Escrow.deposits• [p])
R1 The escrow’s balance must be at least the sum of investor ϕR1 ≡ 2(state 6= SUCCESS
deposits, unless the crowdsale is declared successful. → sum(deposits) ≤ Escrow.balance)
R2 The escrow never allows the beneficiary to withdraw the
investments and the investors to claim refunds. ϕR2 ≡ 2(¬(withdraw() ∧ claimRefund()))
R3 Investors cannot claim refunds after more than 10, 000 ϕR3 ≡ 2(claimRefund() → ¬(sum(deposits) ≥ goal))
ether is collected.
(a) Requirements (c) Formalized requirements as temporal safety properties
1 contract Crowdsale { 1 contract Escrow {
2 Escrow escrow; 2 address owner, beneficiary;
3 uint256 closeTime; 3 mapping(address => uint256) deposits;
4 uint256 raised = 0; 4 enum State {OPEN, SUCCESS, REFUND}
5 uint256 goal = 10000 * 10**18; 5 State state = OPEN;
6 6 constructor(address b) {
7 function constructor() { 7 owner = msg.sender;
8 escrow = new Escrow(0x1234); 8 beneficiary = b;
9 closeTime = now + 30 days; 9 }
10 } 10 modifier onlyOwner {
11 11 require(msg.sender == owner);
12 function invest() payable { 12 }
13 require(raised < goal); 13 function close() onlyOwner {state = SUCCESS;}
14 // fix: uncomment pre-condition below: 14 function refund() onlyOwner {state = REFUND;}
15 // require(now<=closeTime); 15 function deposit(address p) onlyOwner payable {
16 escrow.deposit.value(msg.value)(msg.sender); 16 deposits[p] = deposits[p] + msg.value;
17 raised += msg.value; 17 }
18 } 18 function withdraw() {
19 19 require(state == SUCCESS);
20 function close() { 20 beneficiary.transfer(this.balance);
21 require(now > closeTime || raised >= goal); 21 }
22 if (raised >= goal) { 22 function claimRefund(address p) {
23 escrow.close(); 23 require(state == REFUND);
24 } else { 24 uint256 amount = deposits[p];
25 escrow.refund(); 25 deposits[p] = 0;
26 } 26 p.call.value(amount)();
27 } 27 }
28 } 28 }
1663
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
Escrow.claimRefund(p) Property / System Mythril Oyente Manticore V ER X
p.fallback() Inductive, holds: ϕR1 FP FP TN TN
Violation: ϕR2 FN TP FN TP
Escrow.claimRefund(q)
Fig. 4: Running existing symbolic analyzers for verifying ϕR1
q.fallback()
and finding the violation of ϕR2 .
Execution time
Formalizing requirements. We present the formalization of
Fig. 3: Interaction between the escrow and external contracts the requirements for our example in Fig. 2c. The properties
p and q. Rectangles indicate the active function, dashed are interpreted over a sequence of blockchain states that
lines indicate functions waiting for external calls to return, appear in-between transactions, i.e., states that users observe
downward and upward arrows indicate call and return points. before and after transactions. We deliberately do not define
the requirements’ semantics over states that appear within
transactions, as such states capture low-level implementation
Specification challenge. Requirements typically implicitly details and transient computations which are not observable
assume that the bundle executes without callbacks. For by end-users. As mentioned, the EECF condition lifts the
example, requirement R0 stipulates that at the end of requirements to behaviors with callbacks: for any behavior,
claimRefund(p), the escrow’s balance is decreased by p’s we impose the given requirements on the equivalent behaviors
deposit. This requirement does not hold if we interpret it where the contract’s functions are invoked without callbacks.
directly over executions with callbacks. As illustrated in Property ϕR0 formalizes that for any state (captured with
Fig. 3, there could be multiple calls to the escrow before the always temporal quantifier, denoted by 2) reached af-
claimRefund(p) terminates, which may increase the escrow’s ter a successful execution of claimRefund(address p), the
balance (via calls to deposit(p)) as well as decrease it escrow’s balance (returned by Escrow.balance) equals the
(via calls to claimRefund(p)). One approach to address this previous escrow’s balance (denoted Escrow.balance• ) minus
discrepancy is to directly account for all possible callbacks in the deposit of investor p in the previous state. The property
the formal requirement. Unfortunately, this is complicated and refers to the p’s deposit in the previous state, as it is set to
error-prone because external contracts can behave arbitrarily: zero after executing claimRefund.
one would need to accommodate infinitely many additional Property ϕR1 formalizes that if the escrow is not in state
behaviors. Conceptually, this challenge is analogous to speci- SUCCESS (indicating a successful crowdsale), then the sum of
fying concurrent objects [38], [58]. deposits (returned by sum(deposits)) is less than or equal to
Effectively external callback free contracts. To address the the escrow’s balance (returned by Escrow.balance).
specification challenge, we mirror a well-established approach Properties ϕR2 and ϕR3 use the once quantifier (denoted
to specifying concurrent objects [41]. The idea is to let user by ) to refer to past states, where ϕ holds if ϕ holds at any
requirements implicitly assume no callbacks from external past state of the contract. For example, ϕR2 formalizes that at
contracts. We then lift those requirements to all possible no point in time the functions withdraw() and claimRefund()
behaviors in a generic way. We do that by imposing an have both been successfully executed by the contract. Property
extra condition that we call effective external callback freedom ϕR3 uses the idiom 2(ϕ → ¬ψ) to formalize that once ψ
(EECF), which generalizes [38] to our setting. This extra holds at a given state, then ϕ must never hold in the future of
condition stipulates that any behavior with external callbacks the contract.
is equivalent to a behavior without. Any two equivalent
C. Challenge 2: Unbounded Verification
behaviors are guaranteed to execute the same functions and
terminate in an identical state. For example, the behavior in Next, we discuss the challenges in verifying the properties
Fig. 3 is equivalent to executing claimRefund(p) and then of our example.
claimRefund(q) sequentially (without external callbacks). Precise symbolic execution for Ethereum. We start with
The EECF condition is satisfied by most real-world con- ϕR1 , a global invariant of the form 2ϕ with no nested tempo-
tracts. In practice, contracts defer calls to external contracts ral quantifiers. This property is inductive: it holds in the initial
to the end of transactions, as a generic defense against unex- blockchain state, and it is also preserved after processing any
pected state changes due to malicious callbacks in the middle transaction sent to the contracts at any blockchain state where
of transactions. Indeed, this best practice provides a sufficient ϕR1 holds. To establish this, one can encode all behaviors
condition to establish EECF. A contract that did not satisfy of the functions using symbolic execution, which is possible
EECF is the infamous DAO contract [1], which has a function due to the limited use of loops/recursion in most contracts,
similar to claimRefund with the exception that lines 25 and 26 as we discuss in Section VII. However, modeling Ethereum’s
are switched, meaning that p’s deposit is set to zero only after Virtual Machine (EVM) both precisely and efficiently has
the call is made. Indeed, this enables an adversary to drain the proved challenging. In fact, in Fig. 4, we show that 2 out of the
escrow’s balance (by reclaiming her deposit multiple times). 3 state-of-the-art symbolic execution engines for Ethereum fail
1664
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
{ϕ0 } {ϕ0 }
C = {f1 , .., fn } f1 · · · fn iteratively compute:
ϕ {ϕ0 } {ϕ0 } reach(Cϕ0 ) = α1 ∨..∨αt reach(Cϕ0 ) ⇒ ϕ0
reach(Cϕ0 )
instrument Cϕ0 , 2ϕ0 inductive check Cϕ0 , 2ϕ0 compute fixed point check property
counter-example
C with ϕ via SE via delayed PA on fixpoint
holds add predicates holds
verified extract predicates from C and ϕ verified
Fig. 5: Flow of V ER X: given a contract C and property ϕ, V ER X instruments C with ϕ and attempts an inductive check using
symbolic execution. If this fails, it performs delayed abstraction using predicates extracted from C and ϕ.
to establish our invariant (i.e., to verify property ϕR1 ); we note at transaction boundaries. This delay is needed to verify ϕR3
that Manticore [2] is precise, but inefficient for some examples, because the predicate sum(deposits) ≤ raised is violated
as we show in our evaluation (Section IX). We address this between Line 16 and Line 17 in the crowdsale contract. That
challenge by designing a new symbolic execution engine for is, standard predicate abstraction would fail to verify ϕR3 with
EVM (Section VIII-A) that is both precise and efficient for a these predicates, while delayed predicate abstraction succeeds.
practical fragment of Solidity (Section VII). Delayed predicate abstraction is explained in Section VI.
Discovering deeper violations. The following sequence of
transactions violates property ϕR2 : III. V ERIFICATION F LOW OF V ER X
1) call to close() at state now > closeTime and raised < We now describe the verification flow of V ER X (illustrated
goal, which changes the escrow’s state to REFUND; in Fig. 5) and discuss how it addresses key technical chal-
2) call to claimRefund(p) with any address p; lenges. The input to V ER X consists of one or more contracts
3) call to invest() with ether value msg.value ≥ goal; (we call these a bundle of contracts) together with a (temporal)
4) call to close(), now at state now > closeTime and safety property ϕ. For illustration purposes, we assume we are
raised ≥ goal, which changes the escrow’s state to given one contract C with functions {f1 , . . . , fn }.
SUCCESS; Verification by reduction. First, to verify a temporal prop-
5) call to withdraw(). erty of interest ϕ, V ER X processes C and ϕ so to pro-
This violation is due to a missing pre-condition in function duce an instrumented contract Cϕ0 and a new property 2ϕ0 ,
invest() (Line 15 in Fig. 2b) which would prevent calls after where 2 is the always quantifier and ϕ0 is an assertion
the crowdsale has ended. This shows that counter-examples over the global blockchain state. For example, it converts
in stateful contracts can be deep, going beyond the reach of ϕR3 ≡ 2(claimRefund() → ¬(sum(deposits) ≥ goal))
existing symbolic execution tools (with their default depth into 2(claimRefund() → ¬pψ ), where pψ tracks whether
limit of 2). In fact, in Fig. 4, we see that both Mythril and sum(deposits) ≥ goal has been satisfied in the current or the
Manticore (which unroll multiple transactions) fail to discover past states of the contract. This approach of reducing temporal
the violation with a 5 hours timeout limit and a depth limit property verification to a reachability check was successfully
set to 5. Oyente discovers the bug due to imprecise modeling used in prior work (e.g., [26], [28]). A key benefit is that it
of EVM and not due to supporting precise exploration up enables V ER X to leverage existing analysis techniques, such as
to depth 5 (as shown in the false positive for ϕR1 ). V ER X symbolic execution and predicate abstraction (described next)
correctly fails to verify this property and outputs exactly the to verify that 2ϕ0 holds on the instrumented contract Cϕ0 (and
sequence of transactions listed above as a possible violation. as a result that ϕ holds on the original contract C).
As we show in our evaluation, once the missing pre-condition Verification of inductive properties without abstraction.
is added, V ER X successfully verifies ϕR2 . As stated, the contract’s functions are run in an infinite loop:
Inferring invariants using abstraction. Property ϕR3 is 1 while true
non-inductive. To verify it, V ER X needs to infer an in- 2 (user, func, args) := // arbitrary
3 run func(args) as user
variant that holds over all reachable contract states. In this
example, the invariant is constructed out of the following Each iteration corresponds to the execution of a single transac-
atomic predicates: now > closeTime, raised < goal, and tion, constructed by any user who decides to run an arbitrary
sum(deposits) ≤ raised, where the first two predicates contract function with arbitrary arguments. Since this loop is
come from function pre-conditions. V ER X extracts these and infinite and the number of transactions that users may initiate
other predicates from the code and the property. To com- is unknown in advance, we cannot directly apply symbolic
pute the needed invariant, V ER X uses delayed abstraction execution (SE) to verify that ϕ0 holds on the instrumented
(explained shortly) where the abstraction step is applied only contract (as SE requires loops to be bounded). That is why
1665
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
we first attempt to verify ϕ0 inductively: assuming ϕ0 holds hExecutioni ::= hExternal txni∗
before fi is invoked (pre-condition) with symbolic arguments
hExternal txni ::= hInternal txni
and a symbolic user, we check whether ϕ0 holds after fi has
hInternal txni ::= hMessagei[hCommandi∗ ]
completed (post-condition). If the inductive check holds for
every function fi and if ϕ0 holds on the initial state then hMessagei ::= (Recipient, Sender, Value, Data, Gas)
we conclude that 2ϕ0 holds on Cϕ0 . If the above check hCommandi ::= Load | Store | Local | hInternal txni
fails, however, ϕ0 may still hold for Cϕ0 . Conceptually, this
means we need to strengthen ϕ0 while ensuring that it over- Fig. 6: An execution is a sequence of external transactions
approximates the reachable states of Cϕ0 . each nesting one or more internal transactions. Each internal
transaction starts with a message and proceeds in a sequence
Verification with delayed predicate abstraction. To over- of commands. Commands may load or store data from and to
approximate the reachable states of Cϕ0 , V ER X uses a com- the private storage, perform local computations (not affecting
bination of predicate abstraction and symbolic execution to the storage), and initiate nested internal transactions.
perform delayed predicate abstraction. The idea is to analyze
each transaction fully with symbolic execution and abstract
only the states where ϕ0 is meant to hold, namely, those Behavior , MState∗ MState , BState × Frame∗
appearing at transaction boundaries. The abstraction is done BState , Address → Program × Storage × Value
using the predicates automatically extracted from the contract Frame , Message × Program × Memory × Gas
C and the original property ϕ.
We iterate this procedure in a classic fixed point iteration Program , Code Recipient , Address Data , Word∗
loop. First, we initialize the set reach(Cϕ0 ) of abstract states Storage , ByteWord Sender , Address Gas , Word
reachable at transaction boundaries with the abstraction of the
Memory , ByteWord Value , Word Address , Word
concrete initial state. Then, we simply follow the outer while
loop and non-deterministically select a function fi to invoke;
Fig. 7: An EVM behavior is a sequence of machine states,
we execute fi symbolically (assuming symbolic arguments and
represented as pairs holding a blockchain state and a stack of
a symbolic user) and compute the precise symbolic state s at
frames, one per nested internal transaction.
the end of the transaction. Technically, s is a disjunction of
constraints p1 ∨· · ·∨pk , each pi capturing a possible path of the
function’s k paths. After computing s, V ER X abstracts each
pi ∈ s into an abstract state αi . This results in a set of new users interact with the system by submitting transactions. Con-
abstract states α1 , . . . , αm which are added to reach(Cϕ0 ). tract accounts (or simply contracts) are autonomous objects
Note that m ≤ k, as the symbolic states of two paths may that process transactions. Every contract is associated with a
become abstracted to the same abstract state. program that executes incoming transactions, and a private
Despite the infinite outer loop, V ER X is guaranteed to reach storage for persisting data across transactions.
a fixed point since the predicate abstraction domain is finite An execution of the Ethereum blockchain is a sequence of
(due to the finite number of abstract predicates). The over- external transactions, as defined in Fig. 6. Each transaction
approximation of the reachable states of Cϕ0 is given by the is initiated with a message that defines the addresses of the
disjunction α1 ∨ · · · ∨ αt of all abstract states in the fixed point recipient and sender accounts, a value of ether (possibly zero)
reach(Cϕ0 ). To verify the property, V ER X checks whether to be transferred from the sender to the recipient account,
the fixed point logically implies the desired property ϕ0 . If (possibly empty) data, and a gas value. If the recipient is
yes, we conclude that 2ϕ0 holds on Cϕ0 . Otherwise, V ER X a user account then the data is empty and the transaction
attempts to construct a counter-example. In doing so, it either results in transferring the ether value from the sender to the
succeeds and outputs the counter-example or fails and asks recipient. Otherwise, the recipient is a contract, and the data
the user to provide additional predicates. We note that one identifies a function of the contract’s program together with
could implement standard abstraction refinement techniques arguments passed to the function. Upon the reception of such
to automate predicate discovery [25]. a message, the contract’s program is executed by the Ethereum
Virtual Machine (EVM) [66], modifying the contract’s storage
accordingly. The execution of each command, such as a
IV. S YSTEM M ODEL storage write, is associated with a gas fee and the execution
In this section we provide background on the Ethereum aborts if the accumulated gas fees exceed the gas value.
blockchain and formalize its behavior. We then formalize the A contract’s program can create messages to start transac-
notion of bundle behaviors V ER X reasons about. tions on other contracts, which in turn can do the same. These
transactions behave like a standard procedure call, meaning
A. Ethereum Overview the caller waits for the callee to complete. The calls have
The Ethereum blockchain is a distributed storage that transactional semantics and are termed internal transactions.
supports two types of accounts: user accounts and contract Since procedure calls nest, this leads to a nested transactional
accounts. User accounts are the entry-points through which model: aborting the current internal transaction undoes its
1666
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
effects on the storage, including the effects of all nested (child) Definition 1. A C-entry transaction in an EVM behavior
internal transactions. The top-level transaction initiated by the is a transaction executed by a contract in C whose parent
user together with all nested transactions form an external transaction, if present, is executed by a contract not in C.
transaction. Finally, an execution of the Ethereum blockchain
is a sequence of such external transactions. For example, in the EVM behavior in Fig. 3 the transactions
To illustrate this model, consider that account User calls Escrow.claimRefund(p) and Escrow.claimRefund(q) are C-
function close() of the Crowdsale contract in Fig. 2, which entry transactions: the first one is an external transaction
in turn calls function close() of the Escrow contract. The that executes contract Escrow and does not have a parent
corresponding external transaction is given by transaction; the second one executes contract Escrow and its
parent transaction is executed by a contract p 6∈ C outside the
(Crowdsale, User, 0, close(), 200000)[ bundle C = {Escrow, Crowdsale}.
··· We call an EVM execution C-external callback-free if it
(Escrow, Crowdsale, 0, close(), 129275)[ · · · ] contains no C-entry transaction nested in another C-entry
··· transaction. For example, the behavior shown in Fig. 3 is not
] C-external callback-free because Escrow.claimRefund(q) is
Here, we omit all non-calls (indicated by · · · ). This transaction nested inside Escrow.claimRefund(p).
consists of two messages. The amount of ether in both Let msg(T ) be the message that initiated T , and let post(T )
messages is set to 0, indicating that no ether is transferred stand for the blockchain state at the point when T finishes.
when processing the transaction. The value 200 000 in the first
message is the amount of gas provided by User for processing Definition 2. The bundle behavior of C corresponding to a
the transaction. The gas in the second message (129 275) is C-external callback-free EVM execution is the sequence that
lower as reaching the call to the escrow costs 70 725 gas. records the pair
msg(T ), post(T )
B. Ethereum Behaviors
A behavior of the EVM is the sequence of machine states for every C-entry transaction T in the EVM execution.
the EVM goes through during execution. The EVM state of the
That is, for every transaction T , the behavior records the
EVM is formalized in [66], of which we provide a schematic
transaction message and the blockchain state at the end of T
description in Fig. 7. A machine state is a pair holding the
but restricted only to contracts in the bundle C.
blockchain state, and a stack of frames, one per active nested
internal transaction. Effective external callback freedom. The criterion that we
• The blockchain state collects all account data and is a impose on bundles requires that all EVM behaviors are in a
mapping from account addresses to account states. The certain sense equivalent to C-external callback-free executions.
figure shows only contract accounts, whose state consists We adapt the effective callback freedom criterion of Grossman
of the contract’s program, storage, and balance (values). et al. [38] to our setting where the internal callbacks within
• A frame contains: (1) the message used to activate the a C-entry transaction are thought of as executing sequen-
call, (2) the next program fragment to be executed (i.e., a tially. Both, our criteria and [38], are variants of conflict-
program counter), (3) the call’s local scratchpad memory, serializability [52]. For brevity, we do not state the actual
(4) the remaining gas available for execution. criterion but a necessary and sufficient condition. As standard,
we formalize the condition by first constructing a directed
The EVM operates on 32-byte words. All numeric values (e.g.
graph (digraph) that captures dependencies between operations
addresses) are represented as unsigned integer words. Contract
and then requiring that the resulting digraph is acyclic.
storage and frame memory are word-addressed.
C. Bundle Behaviors Definition 3. The C-serialization digraph of an EVM execution
has a vertex for every C-entry transaction and an edge (S, T )
The requirements of a bundle C of contracts constrain its for all operations s of S and t of T (S 6= T ) such that:
EVM behaviors. Given an EVM behavior, we obtain the
behavior of the bundle by extracting the sequence of pairs 1) s precedes t in the EVM execution, and
(T, b) where T is a transaction whose recipient is in the 2) at least one of s and t modifies part of the blockchain
bundle C and b is a blockchain state that captures the effects state that is read or modified by the other.
of T . As discussed in Section II-B, we follow the well- Intuitively, each edge (S, T ) indicates that the blockchain
established approach in the field of concurrency and extract state after T depends on S being executed earlier. C-entry
behaviors of C only from EVM behaviors without external transactions not involved in circular dependencies are not
callbacks (i.e., without concurrency). Thus, our specifications logically interrupted by callbacks outside the bundle.
do not directly constrain all possible EVM behaviors but only
those where transactions run isolated from external callbacks. Definition 4. A bundle C is effectively external callback free
The remaining EVM behaviors are controlled by imposing a if all EVM executions have acyclic C-serialization digraphs.
generic correctness criterion [38] on the bundle C.
1667
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
V. P ROPERTY S PECIFICATION L ANGUAGE Finally, we rewrite ϕ to the non-temporal formula:
We express properties of bundle behaviors using past linear ϕ ≡ claimRefund() → ¬pψ .
temporal logic, or Past LTL for short. We provide a brief
introduction below, for further details please refer to [48]. Property syntax. To allow auditors and developers to specify
properties, we extend Solidity’s syntax with temporal quanti-
Linear temporal logic. A formula in Past LTL is inductively
fiers. For example, we denote the operators
defined as either an atomic formula A, or a logical or temporal
• always 2 by always,
connective applied to one or more formulas ϕ, ψ:
• once by once, and
ϕ, ψ ::= A | ϕ ∨ ψ | ¬ϕ | ϕ | ϕSψ • previously by prev.
For convenience, we also provide aggregate functions over
A formula can be interpreted at any position in a bundle mappings, denoted by sum(deposits). The properties can
behavior. Atomic formulas directly refer to the message or further refer to the latest transaction’s data, including its
blockchain at the current position. For example, the formula signature and arguments. We use Solidity’s syntax for logical
withdraw() holds when the current transaction was a call to connectives and expressions (e.g., users write && for ∧) and in-
the withdraw() function. The formula from Fig. 2 troduce implication (==>) which is often used in properties.
sum(deposits) ≤ Escrow.balance
VI. D ELAYED A BSTRACTION FOR V ERIFICATION
holds when in the current state the sum of values of the
To verify a Past LTL specification of a bundle of contracts C
deposits mapping is less than or equal to the balance of the
we apply abstract interpretation over a symbolic domain.
Escrow contract. Boolean connectives are interpreted in the
We employ predicate abstraction [35] but without the usual
standard way. For the temporal connectives
conversion to boolean programs. Our approach is similar to
• previously ϕ evaluates ϕ in the previous state if such that of Flanagan and Qadeer [32] where two transformers are
exists and returns false otherwise; alternated: precise symbolic transformers to handle individual
• since ϕ S ψ looks for a nonfuture moment when ψ holds commands, and an imprecise transformer to ensure conver-
so that ϕ holds in all moments after, up to the present. gence. In contrast, classic abstraction applies an imprecise
The since connective can define other useful connectives transformer at every step. Hence, we call the precise/imprecise
like once (is/was true) and still (is/has always been true): approach delayed abstraction.
V ER X receives a bundle C of contracts (written in Solidity)
ψ , > S ψ ψ , ¬¬ψ and a Past LTL specification 2ϕ that should hold for all
Given a Past LTL formula ϕ, the 2ϕ formula uses the behaviors of the bundle. First, it converts the Past LTL
always connective 2 and states that ϕ must hold at all specification to an assertion (Section V). Thus, at this point,
positions of the bundle behavior. we assume that ϕ is an assertion. Then, V ER X compiles all
contracts in C to EVM bytecode [66], and then it performs
Conversion to non-temporal formulas. Past LTL formulas abstract interpretation on the bytecode. We compile to EVM
are sufficiently expressive to capture any safety property, yet because it is much simpler than Solidity.
they are equivalent to assertions, i.e., non-temporal formulas. To keep the discussion concise, we assume the bundle C is
This allows us to convert them to assertions and reuse standard described by the blockchain address of its contracts together
safety verification techniques. with two programs:
To convert a Past LTL formula ϕ to a non-temporal one, we 1) initC —a deterministic program that produces the initial
encode the semantics of past connectives into the behaviors of blockchain state in every behavior of the bundle;
the contract. That is, we introduce auxiliary variables that track 2) stepC —a program that non-deterministically executes a
the truth values of temporal subformulas in ϕ, a well-known transaction of C starting in a given bundle state.
method [54]. Given the property ϕ and the compiled code of C, V ER X
We demonstrate this method with an example. Consider the tries to verify the following program:
temporal formula ϕR3 = 2ϕ from Fig. 2:
initC ; assert ϕ; while true { stepC ; assert ϕ }
ϕ ≡ claimRefund() → ¬(sum(deposits) ≥ goal). Ignoring the assertions, this program produces all possible
behaviors of the given bundle of contracts C.
First, we select a subformula whose top-level connective is
This verification problem amounts to proving that ϕ holds
temporal. Only one such subformula is present in our example:
for all message-state pairs (m, s) appearing in behaviors
ψ ≡ (sum(deposits) ≥ goal). of C. Recall that the behaviors of a bundle were defined
in Section IV. This set of pairs is called the concrete semantics
Then, we introduce a fresh variable pψ that tracks ψ’s truth of C, which we denote with Sem(C). Thus, our goal is to
value, and update it according to the semantic rule of : verify:
p0ψ ≡ (sum(deposits) ≥ goal) ∨ pψ . Sem(C) |= ϕ. (1)
1668
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
1: Input
A. Abstract Interpretation
2: C A bundle of contracts.
As the concrete semantics can be infeasible to compute, 3: ϕ A non-temporal formula.
establishing (1) directly is not possible except for restricted 4: procedure V ERIFY(C, ϕ)
cases. Abstract interpretation [27] addresses this issue by A ← F IXPOINT(A 7→ init# #
5: C t stepC (A))
soundly approximating the concrete semantics with an abstract 6: for all a ∈ A do
semantics Sem# (C) for which the verification problem is 7: if a 6|= ϕ then
easier: 8: report a and exit.
Sem# (C) |= ϕ. (2) 9: end if
10: end for
Sound approximation means that the set γ(Sem# (C)) of all 11: report ϕ holds.
message-state pairs encoded by the abstract semantics extends 12: end procedure
the concrete semantics:
Fig. 8: Verification via a powerset domain.
Sem(C) ⊆ γ(Sem# (C)).
B. Delayed Abstraction
Then, because of soundness, proving (2) implies that (1) holds.
We now discuss the particular abstraction used in V ER X.
To compute the abstract semantics by abstract interpretation,
We apply what we call a delayed abstraction, an elegant idea
one mimics how the concrete semantics is computed as an
which first appeared in [32]. Delayed abstraction addresses
element of the powerset lattice C = P(S), where S is the set
imprecision in reasoning about invariants that are broken at
of all possible message-state pairs, including those that do not
intermediate points of the program. For smart contracts this
appear in the behaviors of the bundle C. We reinterpret the two
typically occurs when a transaction temporarily breaks its
programs that describe the bundle as concrete transformers:
invariant inside and then restores it at the end of its execution.
initC ∈ C stepC : C → C. To deal with imprecision, delayed abstraction simply delays
the abstraction step until the end of the transaction, but
The concrete semantics Sem(C) is simply the least fixed point employs a precise symbolic domain Φ to reason within the
of the monotone map transaction. Because fixed point computation in the precise
domain might not converge, at the end of the transaction, a
S 7→ initC ∪ stepC (S). switch is made to a less precise predicate abstraction domain
To compute Sem# (C) by abstract interpretation, we first A = P(PA) where PA ⊆ Φ. Thus, the abstract transformer
replace the concrete domain C with an abstract domain A, first makes a precise symbolic execution step, and then a
where the two are connected by the concretization function predicate abstraction step:
SE(f ) α
γ : A → C. A −−−−−−−→ P(Φ) −−−−−−−→ A.
We then define abstract transformers instead of concrete ones: To perform symbolic execution, the symbolic domain Φ
consists of first order formulas with message-state pairs (m, s)
init#
C ∈A step#
C : A → A. for models. That is, for every ψ ∈ Φ
To mimic the concrete computation, we equip the abstract γ(ψ) = {(m, s) : (m, s) |= ψ}.
domain A with a lattice structure, that is, a join operation t.
The only restriction to the formulas in Φ is that an SMT
This allows us to define the abstract semantics Sem# (C) as a
solver should be able to handle them. The SE(f ) step starts
fixed point of the map
with a set of formulas A ∈ A and for each formula ψ ∈ A,
A 7→ init# # it symbolically executes all feasible program paths in f ∈
C t stepC (A).
{initC , stepC }. The result of processing ψ is a set of formulas
As a result, we obtain a sound approximation provided the {ψ10 , . . . , ψn0 } (one per feasible program path) that give the
abstract transformers are sound. That is, for all A ∈ A: strongest postcondition of f with respect to ψ:
n
initC ⊆ γ(init#
C) stepC (γ(A)) ⊆ γ(step#
C (A)).
_
γ( ψi0 ) = f (γ(ψ)).
Fig. 8 summarizes the abstract interpretation approach. To i=1
model non-determinism more precisely, we use a powerset The result of processing all formulas in A, as described
domain A, that is, the elements A ∈ A are sets of elements of above, is an intermediate set S ∈ P(Φ) of formulas.
another domain. The concretization function then is: For delayed abstraction, we use a set PA of cubes [35]
[ which are conjunctions of literals over a fixed set of basic
γ(A) = γ(a). predicates P = {P1 , . . . , Pn } ⊆ Φ. The abstraction step
a∈A
α : Φ → PA
1669
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
projects each formula ψ 0 ∈ S onto PA: Type Restriction
^
α(ψ 0 ) = {L ∈ Λ : ψ 0 |= L}, Loops Loops are bounded or match the packed-calls pattern
Recursion No recursion allowed
where Λ is the set of all literals over the set P . The result of External calls Must satisfy effective external callback freedom
processing the set S results in a set of cubes that is an element Gas exceptions Gas exceptions propagate to the external transaction
in the domain A. Storage access No direct access to storage via assembly
An important part of predicate abstraction is choosing the Restricted No execution of external code
set P of basic predicates. If one does not find the right set P , behaviors No creation or destruction of contracts
the computed abstract element will be sound, but might be too
imprecise to prove the desired specification ϕ. Typically, one Fig. 9: Solidity fragment supported by V ER X
seeds P with all atomic subformulas of the specification. If
verification fails with this set, we increase P automatically by
extracting predicates that appear in the program. We define B. Calls to External Contracts
the types of predicates we extract from the program and The Ethereum blockchain permits a bundle contract c1 to
give examples where they bring sufficient precision to prove call an external contract d, and then contract d to call back
realistic properties in Section IX-C. If verification still fails, some bundle contract c2 :
we ask the user to provide additional predicates (in principle,
one could extend the system to perform standard abstraction c1 ∈ C → d 6∈ C → c2 ∈ C. (4)
refinement so to automatically discover predicates).
The external contract d can potentially make an unbounded
number of callbacks to the bundle (until all gas is depleted),
VII. P RACTICAL S OLIDITY F RAGMENT
making it infeasible to fully analyze with symbolic execution.
The effectiveness of delayed abstraction relies on a precise To address this challenge, we consider only contracts that
symbolic execution engine that scales to realistic contracts. satisfy the effective external callback freedom condition de-
Building such an engine for arbitrary Solidity contracts is, fined in Section IV-C. This condition implies that any behavior
however, challenging due to the Turing-completeness of the of the contracts with external callbacks is equivalent to a
language. In this section, we define a practical fragment of behavior without external callbacks. We defined the check used
Solidity that covers a wide range of real-world contracts yet to determine whether a smart contract satisfies this condition
enables precise and scalable symbolic execution. We summa- in Section VIII.
rize the restrictions in Fig. 9 and explain them in detail below.
A. Loops and Recursion C. Gas Exception Propagation
Symbolic execution fails to terminate when a program If an internal transaction consumes all gas allocated to it,
contains unbounded loops or recursion. In Ethereum, however, then it terminates with an “out of gas” exception, undoing all
long transaction executions are costly (since they consume changes to the shared storage. After that, execution resumes
more gas) and moreover cannot exceed the block’s gas limit, in the parent transaction. Since the amount of gas can be
which imposes a hard bound on the gas budget of transactions. arbitrary, the above behavior implies that most calls are a
Because of this, unbounded loops and recursion are considered potential branching point where the call can be omitted. These
anti-patterns. branching points explode the number of program paths to be
In our fragment, we consider Solidity contracts without symbolically executed.
recursion and where loops are bounded. Such contracts have We avoid this explosion by requiring contracts to propagate
finitely many paths which can be fully analyzed symbolically. all exceptions up the call stack, effectively reverting the current
To further extend our fragment, we also support the following bundle transaction. This means that the transaction will not
common loop pattern in Solidity: appear in the blockchain execution and can be ignored.
1 function pm(Arg[] args) {
2 for (i = 0; i < args.length; i++) { D. Storage Access
3 f(args[i]);
4 } As mentioned in Section VI, we do not analyze Solidity
5 } directly but the EVM bytecode obtained by compiling Solidity
We call this loop pattern packed-calls, as the loop is merely code. The challenge with EVM bytecode is that it is low-
used to group multiple independent calls to the function f level and important information is lost during compilation.
in a single transaction. For the purpose of verification, such The major challenge comes when reasoning about compound
loops can be eliminated without changing the semantics of the objects such as structures, arrays, and maps. The EVM lacks
bundle. That is, any transaction invoked with multiple elements primitives for such objects, and consequently, all arrays and
in args can be split into multiple transactions to pm: maps are spliced together into a single flat array of 2256 words.
Therefore, to reason about compound objects, we need to
pm([a1 , . . . , an ]) ' pm([a1 ]) . . . pm([an ]). (3) reason about the specific hash-based allocation scheme used
V ER X automatically detects and soundly bounds such loops. by the Solidity compiler.
1670
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
To illustrate the scheme, consider an array a that holds precisely as 256-bit vectors. EVM storage and memory are
objects of size n. The array gets a unique identifier id(a), encoded in the theory of arrays. Calls to external contracts are
and its elements are distributed according to1 : modeled with uninterpreted constants.
a[i] 7→ SHA-3(id(a)) + n ∗ i. Checking effective external callback freedom (EECF). We
check that all contracts in the bundle satisfy EECF through a
That is, the array is allocated at the address that equals the proxy pattern. The pattern forbids:
SHA-3 hash of its identifier and spans the subsequent words.
1) Writing to the bundle’s blockchain state after calls to
The allocation scheme must be valid in the sense that
external contracts. Example writes include transferring
distinct objects are allocated disjoint storage. This validity
ether or writing values to storage variables;
rests on two conditions, which are required to model in order
2) Reading the bundle’s blockchain state after calls to ex-
to analyze array and map accesses precisely:
ternal contracts with ≥ 5000 gas. Example reads include
1) There are no SHA-3 collisions to guarantee that distinct accessing a contract’s balance or storage variables.
objects and map elements start at distinct addresses.
The latter condition allows reads for calls to external contracts
2) SHA-3 hashes are sufficiently spread apart so that dis-
with < 5000 gas since such calls guarantee that the callee
tinct objects do not overlap.
cannot, due to the limited gas budget, modify the bundle’s
These two conditions are assumed to hold for any Solidity blockchain state. We note that such calls are common because
contract. They can, however, be violated if a contract directly Solidty’s statements send and transfer are frequently used
accesses storage using inlined assembly. To this end, we by developers to transfer ether to other contracts and these
restrict the use of inlined assembly in bundle contracts. provide 2300 gas to the callee.
E. Unsupported Instructions Checking exception propagation. Standard function invo-
Finally, in our fragment we ignore contracts that: cations in Solidity automatically propagate exceptions. Only
1) Execute external code in the context of the bundle low-level calls such as send and call do not propagate
contracts (instructions CALLCODE or DELEGATECALL). them. To check that the caller propagates exceptions, we
2) Destroy bundle contracts or create new ones (instruc- perform dataflow analysis to verify that the contract always
tions CREATE and SUICIDE). reverts the state when such a call fails.
Execution of external code is known to entail a security risk. Modeling object allocation. As mentioned, a unique aspect
For example, attackers can execute arbitrary code if they of Solidity/EVM is the hash-based allocation scheme used to
gain control over the destination address of a DELEGATECALL determine the location of an object in the storage by applying
instruction. Due to these security risks, these instructions are the SHA-3 hash function. The challenge here is that SHA-3
considered a bad practice and are excluded from new bytecode is a rather complex function and if we model it precisely with
proposals that aim to improve the security of the EVM [44]. SMT constraints, we would cripple the symbolic execution.
Creation and destruction of contracts can be supported but is To this end, we interpret SHA-3 as a different, much simpler
less common and we omit these behaviors for simplicity. function, that still ensures the two assumptions mentioned
above, namely no hash collisions and sufficiently spread-
out hashes. This approach is sound provided that the smart
VIII. T HE V ER X S YSTEM
contracts we verify always access memory through Solidity’s
We now present details on our V ER X system: the new primitives, that is, they never access raw memory directly. We
symbolic execution engine, the types of predicates extracted ensure this memory access by forbidding the use of inline
from contracts, and optimizations used to scale verification. assembly in the Solidity source.
A. Symbolic Execution Engine We encode the simpler function with the function symbols
V ER X uses a new symbolic execution engine for EVM, SHA-3n : {0, 1}n → {0, 1}256 ,
which avoids the pitfalls of existing engines, as detailed in
our evaluation (Section IX-D). Our symbolic execution engine one per input size n to a SHA-3 invocation in the bytecode.
extends standard techniques [19], [45] with the following We encode the expected behavior of the function with extra
unique features of Solidity: (i) object allocation in Solidity (de- SMT constraints. Importantly, the constraints do not specify
termined by hash functions), (ii) contract calls, and (iii) sound the function completely. That is, we assume little about the
approximation of gas mechanics. We also model sums of function, and the V ER X verification results hold for any
arrays and mappings, which although not part of Solidity are function that satisfies those weak assumptions.
needed for verifying relevant properties. (i) The first set of constraints ensures that no collisions
occur. No collisions in principle means that the union of all the
Symbolic state. We perform symbolic execution with
functions SHA-3n is one-to-one. This is, of course, impossible
Z3 [31]. Machine words in the symbolic state are encoded
since the domain of the function is larger than its codomain.
1 For simplicity, we assume that n = 256. The storage address is computed That is why we do not ensure injectivity on its whole domain.
using a more complex formula if n 6= 256; cf. [8]. Instead, we consider each symbolic execution separately and
1671
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
ensure injectivity only on the (possibly symbolic) arguments is a constant, for calls initiated by send and transfer which
that appear in the execution. To do that, let Xn be the always provide 2, 300 gas to the callee. Finally, our engine
arguments applied to SHA-3n . We add the constraints: considers any paths where glower > gbudget as infeasible, as
1) No collisions within each Xn : they are guaranteed to revert due to an out-of-gas exception.
^ Tracking sums. Another feature of our engine is to track the
SHA-3−1n (SHA-3n (x)) = x. (5)
sums of arrays and mappings with numeric values. Although
x∈Xn
these sums are not a feature of the EVM, they enable to specify
Here, SHA-3−1 n is a function symbol that represents a important properties (e.g., Fig. 2). The sum of an object a is
pseudo-inverse function of SHA-3n . kept in a designated variable sum(a) in the symbolic store.
2) No collisions between each Xm and Xn , for sufficiently For each write a0 = a[x 7→ y], the sum changes as:
many pairs m 6= n of distinct input sizes:
^ sum(a0 ) = sum(a) − a[x] + y.
Ln ≤ SHA-3n (x) ≤ Rn . (6) B. Extracting Predicates
x∈Xn
Delayed abstraction needs a sufficient set of predicates to
The numbers Li ≤ Ri divide the address space into successfully verify properties. Based on our experience in
disjoint intervals [Lm , Rm ] ∩ [Ln , Rn ] = ∅. verifying contracts (see Section IX), we identify five important
In our experiments, we set concrete bounds Li , Ri that are far kinds of predicates:
apart from each other, giving intervals of length 2100 . 1) Property: atomic formulas from the property. For exam-
We used the above constraints instead of the prosaic ple, ϕR1 (Fig. 2) has two atomic formulas: state 6=
SUCCESS and sum(deposits) ≤ Escrow.balance.
x 6= y =⇒ SHA-3m (x) 6= SHA-3n (y).
2) Points-to: encode pointers to contract instances. For our
because they are linearly many in the size of the union ∪i Xi motivating example, we extract predicates that capture
instead of quadratic. In our experience, this reduced the load that escrow points to the escrow instance, and its field
on the SMT solver significantly. owner points to the crowdsale instance.
(ii) The second set of constraints ensures that hashes are 3) Enum/Bool: encode values of boolean and enumerated
spread apart. For every set Xn we add the constraints fields. For our motivating example, we extract three
^ predicates to encode the value of variable state.
SHA-3n (x) ≡ 0 (mod S), (7) 4) Constant: encode values of constant variables. For our
x∈Xn
motivating example, we extract goal = 10000 × 1018 .
where S is sufficiently large (64 in our experiments). 5) Other: predicates defined by users.
Calls. When the engine reaches a call, it checks whether the V ER X automatically extracts all predicates, except those
address being called is concrete or symbolic. If it is concrete, classified as Constant and Other, by traversing the contract’s
the execution continues by transferring the given (possibly abstract syntax tree (AST) to detect contract fields of type
symbolic) value of ether and then checks whether the address boolean, enum, or contract, and defining predicates to capture
is in the bundle. If so, this is a contract account, and we model their values. For a boolean field b, V ER X adds the predicate
the call precisely, activating the contract’s program in a new b = True. For an enum field e, V ER X finds its size n and adds
frame. If the concrete account is external, then we treat it as the predicates e = 0, . . . , e = n − 1. For example, for variable
a user account, meaning no further action beyond a transfer is state from Fig. 2(d), V ER X adds the predicates state = 0,
taken (as mentioned, this treatment is sound when the bundle state = 1, and state = 2. For a contract field c of type
contracts satisfy effective external callback freedom). t, V ER X adds the predicate c = t. This predicate captures
In the case where the address being called is symbolic, the the type of c, and also serves as a points-to predicate: in our
execution first non-deterministically selects the address of a benchmarks, there is one instance per contract type, and thus if
bundle contract or treats the symbolic address as an external the predicate c = t holds, then V ER X infers the exact instance
user account. After that, the execution proceeds as before. of c. In the future, we plan to extend V ER X with points-to
analysis to improve precision for more complex cases.
Gas mechanics. The engine implements gas mechanics to
terminate symbolic execution along infeasible paths, where C. Abstraction of non-linear arithmetic
an out-of-gas exception is guaranteed. Concretely, the engine To further scale the analysis, V ER X supports abstraction of
tracks a lower-bound glower on the gas cost of each symbolic non-linear arithmetic. Non-linear arithmetic poses a challenge
path. We note that we cannot track the precise gas cost simply to SMT solvers, yet it is unnecessary to prove many real-
because the gas fees for some operations depend on their world properties. That is why V ER X has the option to abstract
arguments, and therefore the concrete executions that follow a non-linear computations (e.g., multiplication) by substituting
symbolic path may have different gas costs. The engine also them with uninterpreted functions. The user of the system can
tracks the gas budget gbudget specified in the message initiating disable or enable the abstraction whenever needed, speeding
the call. There are two common cases for the gas budget: (1) up the analysis for the majority of cases.
it is often unbounded, as it is specified by the user, and (2) it
1672
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
IX. E VALUATION OF V ER X Project Type # Contracts # Func. # Ifs LOC
1673
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
Type Properties Description Example
always(RefundEscrow.deposit(address)==>
User-based 13 Define which users (identified by ad-
(msg.sender == RefundEscrow._primary))
access control dress or attributes) can invoke certain
functions
always(ICOCrowdsale.withdrawTokens()==>
State-based 44 Define invariants and access control
(now > ICOCrowdsale.closingTime))
properties that must be enforced in certain states
always(!(once(Escrow.state == SUCCESS)
State machine 15 Define which state-transitions of the
&& once(Escrow.state == REFUND))
requirements contract are correct
always(MRVToken.totalSupply ==
Invariant over 8 Invariants expressed over aggregate
sum(MRVToken.balances))
aggregates values stored in mappings and arrays
always((MANAContinuousSale.started != true)==>
(Multi)contract 3 Invariants that span across one or more
MANAToken.owner == MANACrowdsale)
invariants contracts
TABLE II: Common types of properties encountered during audits and concrete examples taken from the benchmarks.
TABLE III: Experimental results of V ER X’s property verification using inductive reasoning and delayed abstraction.
TABLE IV: Comparison of V ER X’s symbolic execution engine with state-of-the-art symbolic tools.
Finally, 10 of the projects do not require any cus- C. Importance of Derived Predicates
tom predicates to verify all their properties. This indicates
Most of the properties in our benchmarks are non-inductive
that most properties can be verified with few extra pred-
and therefore require additional predicates to verify. We in-
icates which are extracted automatically. The remaining 3
spected the relevance of the predicates derived automatically
projects do require few predicates classified as Other. Exam-
by V ER X and report our findings below.
ple predicates are Crowdsale.raised < Crowdsale.goal and
sum(Escrow.deposits)== Crowdsale.raised. We note that Points-to predicates. Points-to predicates are essential to
such predicates often appear in function preconditions (defined prove that the bundle contracts are effectively external callback
in require statements), indicating that they can be easily free (EECF). Calls to bundle contracts are often followed by
extracted automatically. changes to storage and thus do not satisfy the first condition
imposed by V ER X on external calls (see Section VIII). For
example, consider function invest() of the crowdsale (Fig. 2)
1674
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
which calls the escrow contract. Without additional predicates, contrast, it is tailored to contracts and applies abstraction only
the variable escrow is abstracted away, meaning that it can at the end of transactions.
store the address of any (including an external) contract. Under
this abstraction, V ER X fails to verify the specification because Symbolic execution. Symbolic execution has been shown
the function writes to variable raised after the call. To avoid successful in finding property violations of programs [12],
this false alarm, V ER X automatically adds the points-to pred- [22], [45], [53], [63], [67]. To mitigate path explosion due to
icate: Crowdsale.escrow == Escrow and proves that variable loops and recursive calls, different heuristics have been sug-
escrow always points to the escrow (a bundle contract), and gested: favoring paths that may lead to uncovered code [22],
by doing so avoids aborting the verification process. constraining search depth [53], using fitness functions [67],
chopping functions [63], function and loop summaries to reuse
Boolean/enumerated predicates. For our overview example, repetitive computations [10], [33], [34], and using concrete
V ER X also adds the three predicates inputs to select symbolic paths [11], [56], [57], [60], [70].
{Escrow.state == X | X ∈ {OPEN, SUCCESS, REFUND}}. The authors of [69] use symbolic execution to prune paths
irrelevant when verifying regular properties, which is sound
These predicates are needed to capture the possible state under input independency assumptions. To reduce paths via
transitions of the escrow. Without them, V ER X analyzes spu- subsumption, the authors of [13] combine symbolic execution
rious traces which violate the properties. Concretely, V ER X with abstraction to analyze programs that manipulate arrays
fails to prove that the escrow cannot transition from a and lists. In the context of contracts, there are different
state where Escrow.state == REFUND holds to a state where symbolic engines for Ethereum [2], [24], [46], [47], [50],
Escrow.state == SUCCESS holds (and vice versa) and, in turn, [51]. Some of these support multiple transactions and employ
fails to verify property ϕR2 . heuristics to guide the path exploration. In contrast, our
symbolic engine is more precise, as shown in our evaluation,
D. V ER X’s Symbolic Execution Engine and handles hash collisions more efficiently.
Finally, we compare V ER X’s symbolic execution engine
to three other state-of-the-art engines: Oyente [47], Mythril Analysis of temporal properties. Many works have studied
v0.19.3 [50], and Manticore v0.1.9 [2]. We collected 41 tests analysis of temporal properties. In [55], the authors show
from the smart contract security project [5], where security an approach to test for violations to past LTL formulas.
experts maintain a collection of vulnerable smart contracts. In [16], the authors show a verification of LTL formulas
Our benchmark consists of two kinds of tests: assertions and for UML models. T2 [21] is a system to verify temporal
arithmetic, where the tests check for failed assertions and, properties over LLVM which supports linear integer arithmetic
respectively, over- and under-flows. programs. E-HSF [18] verifies existential CTL formulas by
Table IV shows the results. For each of the four systems, representing them as existentially quantified Horn-like clauses,
we report: detected (TP), missed (FN), falsely reported (FP), and using a counterexample-guided approach to solve the
and correctly unreported (TN) violations. In our tests, we used clauses. Typestates [61] enable one to express correct usage
a timeout (T/O) of one hour. rules of class operations or protocols. Some of our evaluated
Results indicate that V ER X outperforms other symbolic properties can be expressed as typestates.
execution tools in terms of precision. V ER X has no false Static analysis and verification of smart contracts. Declar-
negatives and only 1 false positive. The test for which V ER X ative static analysis tools [20], [36], [64], [65] use Datalog to
reports a FP is a synthetic example that checks if the remaining identify generic security vulnerabilities in Ethereum contracts.
gas decreases during execution, and V ER X over-approximates These tools focus on data-flow and gas-related vulnerabilities.
gas-mechanics, which is sound for verification. Slither [4] and SmartCheck [62] identify vulnerabilities on the
abstract-syntax tree of the contract’s source code. The analysis
X. R ELATED W ORK of Grossman et al. [38] targets vulnerabilities due to callbacks
in contracts. In contrast to our work, these approaches do
We now describe the works that are closely related to ours. not support the verification of temporal properties. In [43],
Predicate abstraction. Predicate abstraction [35] has been Hirai defines a formal model for EVM in the Lem language.
shown successful to verify safety properties. SLAM [14], [15] Formal EVM semantics have been defined also defined for
automatically constructs a boolean program from a C program F* [37], the K framework [42], and Isabelle/HOL [9]. These
to verify properties. BLAST [39], [40] increases precision approaches provide formal guarantees while being precise (no
by adding predicates derived from the proofs of infeasible false positives). Some of these frameworks are non-trivial
traces. SatAbs [17], MAGIC [23], Murphy [30], and Java to automate. In contrast, we target automated verification of
PathFinder [29] also build on predicate abstraction for verifica- temporal properties. Analysis of temporal properties of smart
tion and differ mostly in the properties and programs analyzed. contracts has also been considered in [59], where authors show
Other works show how to apply refinement during abstraction, how to manually encode such properties and verify them with
e.g., the authors of [49] describe refinement by partitioning Coq. In contrast, we show an automatic approach and provide
traces. Our work is inspired by the above approaches. In an end-to-end tool to analyze temporal safety properties.
1675
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
XI. C ONCLUSION [16] Luciano Baresi, Mohammad Mehdi Pourhashem Kallehbasti, and Matteo
Rossi. Efficient scalable verification of LTL specifications. In Inter-
We presented V ER X, the first verifier that can automatically national Conference on Software Engineering, ICSE, pages 711–721.
prove temporal safety properties of Ethereum smart contracts. ACM/IEEE, 2015.
[17] Gérard Basler, Alastair F. Donaldson, Alexander Kaiser, Daniel Kroen-
The verifier is based on a careful combination of three ideas: ing, Michael Tautschnig, and Thomas Wahl. satabs: A bit-precise verifier
reduction of temporal safety verification to reachability check- for C programs - (competition contribution). In Tools and Algorithms
ing, an efficient symbolic execution engine used to compute for the Construction and Analysis of Systems, TACAS, pages 552–555,
2012.
precise symbolic states within a transaction, and delayed [18] Tewodros A. Beyene, Corneliu Popeea, and Andrey Rybalchenko. Solv-
abstraction which approximates symbolic states at the end of ing existentially quantified horn clauses. In Computer Aided Verification,
transactions into abstract states. Delayed abstraction allows CAV, pages 869–882, 2013.
[19] Robert S Boyer, Bernard Elspas, and Karl N Levitt. Selecta formal
the verifier to reduce the potentially unbounded concrete state system for testing and debugging programs by symbolic execution. ACM
space of the contact into a finite, bounded representation, SigPlan Notices, pages 234–245, 1975.
while still maintaining precision (thanks to employing precise [20] Lexi Brent, Anton Jurisevic, Michael Kong, Eric Liu, François Gauthier,
Vincent Gramoli, Ralph Holz, and Bernhard Scholz. Vandal: A scalable
reasoning during transactions). We demonstrated that V ER X security analysis framework for smart contracts. CoRR, 2018.
is practical and automatically proves 83 properties over 12 [21] Marc Brockschmidt, Byron Cook, Samin Ishtiaq, Heidy Khlaaf, and Nir
real-world Ethereum projects. Based on these experiments, Piterman. T2: temporal property verification. In Tools and Algorithms
we believe V ER X is an effective system for verifying custom for the Construction and Analysis of Systems, TACAS, pages 387–393,
2016.
functional properties of smart contracts. [22] Cristian Cadar, Daniel Dunbar, and Dawson R. Engler. KLEE: unassisted
and automatic generation of high-coverage tests for complex systems
ACKNOWLEDGMENTS programs. In USENIX Symposium on Operating Systems Design and
Implementation, OSDI, pages 209–224, 2008.
The authors would like to thank Hubert Ritzdorf from [23] Sagar Chaki, Edmund M. Clarke, Alex Groce, Somesh Jha, and Helmut
ChainSecurity AG for the valuable feedback on the specifics of Veith. Modular verification of software components in C. In Inter-
national Conference on Software Engineering, ICSE, pages 385–395.
the Ethereum Virtual Machine. We also thank the anonymous ACM/IEEE, 2003.
reviewers for their constructive comments. [24] Jialiang Chang, Bo Gao, Hao Xiao, Jun Sun, and Zijiang Yang. scom-
pile: Critical path identification and analysis for smart contracts. CoRR,
R EFERENCES 2018.
[25] Edmund M. Clarke, Orna Grumberg, Somesh Jha, Yuan Lu, and Helmut
[1] The dao hack explained: Unfortunate take-off of smart contracts, 2018. Veith. Counterexample-guided abstraction refinement. In International
Available from: https://medium.com/@ogucluturk/. Conference Computer Aided Verification, CAV, pages 154–169, 2000.
[2] Manticore, 2018. Available from: https://github.com/trailofbits/ [26] Byron Cook, Eric Koskinen, and Moshe Vardi. Temporal property
manticore. verification as a program analysis task. In International Conference
[3] Openzeppelin library, 2018. Available from: https://github.com/ Computer Aided Verification, CAV. Springer Berlin Heidelberg, 2011.
OpenZeppelin/openzeppelin-solidity. [27] Patrick Cousot and Radhia Cousot. Abstract interpretation: a unified
[4] Slither, 2018. Available from: https://github.com/trailofbits/slither. lattice model for static analysis of programs by construction or approx-
[5] Smart contract security project, 2018. Available from: https://github. imation of fixpoints. In Proceedings of Symposium on Principles of
com/SmartContractSecurity/SWC-registry/. programming languages, POPL, pages 238–252. ACM, 1977.
[6] Solidity language documentation, 2018. Available from: https://solidity. [28] Andrei Marian Dan, Yuri Meshman, Martin Vechev, and Eran Yahav.
readthedocs.io. Predicate abstraction for relaxed memory models. In Francesco Logozzo
[7] Formal verification of multicollateral dai, 2019. Available from: https: and Manuel Fähndrich, editors, Static Analysis, pages 84–104, Berlin,
//github.com/dapphub/k-dss/. Heidelberg, 2013. Springer Berlin Heidelberg.
[8] Solidity docs: Access to external variables, functions and libraries, 2019. [29] Jakub Daniel, Pavel Parizek, and Corina S. Pasareanu. Predicate
Available from: https://solidity.readthedocs.io/en/v0.5.9/assembly.html# abstraction in java pathfinder. Software Engineering Notes, pages 1–
access-to-external-variables-functions-and-libraries. 5, 2014.
[9] Sidney Amani, Myriam Bégel, Maksym Bortin, and Mark Staples. [30] Satyaki Das, David L. Dill, and Seungjoon Park. Experience with
Towards verifying ethereum smart contract bytecode in isabelle/hol. In predicate abstraction. In Computer Aided Verification, CAV, pages 160–
International Conference on Certified Programs and Proofs, CPP. ACM, 171, 1999.
2018. [31] Leonardo De Moura and Nikolaj Bjørner. Z3: An efficient smt solver. In
[10] Saswat Anand, Patrice Godefroid, and Nikolai Tillmann. Demand-driven International Conference on Tools and Algorithms for the Construction
compositional symbolic execution. In Tools and Algorithms for the and Analysis of Systems, TACAS, pages 337–340, 2008.
Construction and Analysis of Systems, TACAS, pages 367–381, 2008. [32] Cormac Flanagan and Shaz Qadeer. Predicate abstraction for software
[11] Saswat Anand, Mayur Naik, Mary Jean Harrold, and Hongseok Yang. verification. In SIGPLAN Notices, pages 191–202. ACM, 2002.
Automated concolic testing of smartphone apps. In Symposium on the [33] Patrice Godefroid. Compositional dynamic test generation. In Sympo-
Foundations of Software Engineering, FSE, 2012. sium on Principles of Programming Languages, POPL, pages 47–54.
[12] Saswat Anand, Corina S. Pasareanu, and Willem Visser. JPF-SE: A ACM, 2007.
symbolic execution extension to java pathfinder. In Tools and Algorithms [34] Patrice Godefroid and Daniel Luchaup. Automatic partial loop sum-
for the Construction and Analysis of Systems, TACAS, pages 134–138, marization in dynamic test generation. In International Symposium on
2007. Software Testing and Analysis, ISSTA, pages 23–33, 2011.
[13] Saswat Anand, Corina S. Pasareanu, and Willem Visser. Symbolic [35] Susanne Graf and Hassen Saı̈di. Construction of abstract state graphs
execution with abstraction. International Journal on Software Tools for with PVS. In Computer Aided Verification, CAV, pages 72–83, 1997.
Technology Transfer, STTT, pages 53–67, 2009. [36] Neville Grech, Michael Kong, Anton Jurisevic, Lexi Brent, Bernhard
[14] Thomas Ball, Rupak Majumdar, Todd D. Millstein, and Sriram K. Ra- Scholz, and Yannis Smaragdakis. Madmax: Surviving out-of-gas con-
jamani. Automatic predicate abstraction of C programs. In Conference ditions in ethereum smart contracts. In Conference on Object-Oriented
on Programming Language Design and Implementation (PLDI), pages Programming, Systems, Languages, and Applications, OOPSLA. ACM,
203–213. ACM, 2001. 2018.
[15] Thomas Ball and Sriram K. Rajamani. The SLAM project: debugging [37] Ilya Grishchenko, Matteo Maffei, and Clara Schneidewind. A semantic
system software via static analysis. In Symposium on Principles of framework for the security analysis of ethereum smart contracts. In
Programming Languages, POPL, pages 1–3. ACM, 2002. Principles of Security and Trust, POST, pages 243–269, 2018.
1676
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.
[38] Shelly Grossman, Ittai Abraham, Guy Golan-Gueta, Yan Michalevsky, ethereum smart contracts. In International Workshop on Emerging
Noam Rinetzky, Mooly Sagiv, and Yoni Zohar. Online detection of Trends in Software Engineering for Blockchain, WETSEB, 2018.
effectively callback free objects with applications to smart contracts. [63] David Trabish, Andrea Mattavelli, Noam Rinetzky, and Cristian Cadar.
In Symposium on Principles of Programming Languages, POPL. ACM, Chopped symbolic execution. In Proceedings of the 40th International
2018. Conference on Software Engineering, ICSE, pages 350–360. ACM/IEEE,
[39] Thomas A. Henzinger, Ranjit Jhala, Rupak Majumdar, and Kenneth L. 2018.
McMillan. Abstractions from proofs. In Symposium on Principles of [64] Petar Tsankov. Security analysis of smart contracts in datalog. In
Programming Languages, POPL, pages 232–244. ACM, 2004. Leveraging Applications of Formal Methods, Verification and Validation.
[40] Thomas A. Henzinger, Ranjit Jhala, Rupak Majumdar, and Grégoire Springer International Publishing, 2018.
Sutre. Lazy abstraction. In Symposium on Principles of Programming [65] Petar Tsankov, Andrei Dan, Dana Drachsler-Cohen, Arthur Gervais,
Languages, POPL, pages 58–70. ACM, 2002. Florian Bünzli, and Martin Vechev. Securify: Practical security analysis
[41] Maurice P Herlihy and Jeannette M Wing. Linearizability: A correctness of smart contracts. In Conference on Computer and Communications
condition for concurrent objects. ACM Transactions on Programming Security, CCS, 2018.
Languages and Systems (TOPLAS), pages 463–492, 1990. [66] Gavin Wood. Ethereum: A secure decentralised generalised transaction
[42] E. Hildenbrandt, M. Saxena, N. Rodrigues, X. Zhu, P. Daian, D. Guth, ledger. Ethereum project yellow paper, 151:1–32, 2014.
B. Moore, D. Park, Y. Zhang, A. Stefanescu, and G. Rosu. Kevm: [67] Tao Xie, Nikolai Tillmann, Jonathan de Halleux, and Wolfram Schulte.
A complete formal semantics of the ethereum virtual machine. In Fitness-guided path exploration in dynamic symbolic execution. In
Computer Security Foundations Symposium (CSF), 2018. International Conference on Dependable Systems and Networks, DSN,
[43] Yoichi Hirai. Defining the ethereum virtual machine for interactive pages 359–368, 2009.
theorem provers. In Financial Cryptography and Data Security. Springer [68] Zheng Yang and Hang Lei. Formal process virtual machine for smart
International Publishing, 2017. contracts verification. CoRR, abs/1805.00808, 2018.
[44] Theodoros Kasampalis, Dwight Guth, Brandon Moore, Traian Ser- [69] Hengbiao Yu, Zhenbang Chen, Ji Wang, Zhendong Su, and Wei Dong.
banuta, Virgil Serbanuta, Daniele Filaretti, Grigore Rosu, and Ralph Symbolic verification of regular properties. In International Conference
Johnson. Iele: An intermediate-level blockchain language designed and on Software Engineering, ICSE, pages 871–881. IEEE/ACM, 2018.
implemented using formal semantics. Technical report, July 2018. [70] Yufeng Zhang, Zhenbang Chen, Ji Wang, Wei Dong, and Zhiming Liu.
[45] James C King. Symbolic execution and program testing. Communica- Regular property guided dynamic symbolic execution. In International
tions of the ACM, pages 385–394, 1976. Conference on Software Engineering, ICSE, pages 643–653. IEEE/ACM,
[46] Johannes Krupp and Christian Rossow. Teether: Gnawing at ethereum 2015.
to automatically exploit smart contracts. In USENIX Security, 2018.
[47] Loi Luu, Duc-Hiep Chu, Hrishi Olickel, Prateek Saxena, and Aquinas
Hobor. Making smart contracts smarter. In Conference on Computer
and Communications Security, CCS, 2016.
[48] Zohar Manna and Amir Pnueli. Temporal verification of reactive
systems: safety. Springer, 1995.
[49] Laurent Mauborgne and Xavier Rival. Trace partitioning in abstract
interpretation based static analyzers. In European Symposium on
Programming, ESOP, pages 5–20, 2005.
[50] Bernhard Mueller. Smashing ethereum smart contracts for fun and real
profit. HITB SECCONF Amsterdam, 2018.
[51] Ivica Nikolić, Aashish Kolluri, Ilya Sergey, Prateek Saxena, and Aquinas
Hobor. Finding the greedy, prodigal, and suicidal contracts at scale.
In Annual Computer Security Applications Conference, ACSAC, pages
653–663. ACM, 2018.
[52] Christos H. Papadimitriou. The serializability of concurrent database
updates. Journal of the ACM, pages 631–653, 1979.
[53] Corina S. Pasareanu and Neha Rungta. Symbolic pathfinder: symbolic
execution of java bytecode. In International Conference on Automated
Software Engineering, ASE, pages 179–180. ACM/IEEE, 2010.
[54] Grigore Roşu. On safety properties and their monitoring. Scientific
Annals of Computer Science, pages 327–365, 2012.
[55] Grigore Rosu, Feng Chen, and Thomas Ball. Synthesizing monitors
for safety properties: This time with calls and returns. In International
Workshop on Runtime Verification, RV, pages 51–68, 2008.
[56] Koushik Sen. Concolic testing. In International Conference on Auto-
mated Software Engineering ASE, pages 571–572. IEEE/ACM, 2007.
[57] Koushik Sen, Darko Marinov, and Gul Agha. CUTE: a concolic unit
testing engine for C. In International Symposium on Foundations of
Software Engineering, FSE, pages 263–272, 2005.
[58] Ilya Sergey and Aquinas Hobor. A concurrent perspective on smart
contracts. In International Conference on Financial Cryptography and
Data Security, pages 478–493. Springer, 2017.
[59] Ilya Sergey, Amrit Kumar, and Aquinas Hobor. Temporal properties
of smart contracts. In Leveraging Applications of Formal Methods,
Verification and Validation, pages 323–338, 2018.
[60] Nick Stephens, John Grosen, Christopher Salls, Andrew Dutcher, Ruoyu
Wang, Jacopo Corbetta, Yan Shoshitaishvili, Christopher Kruegel, and
Giovanni Vigna. Driller: Augmenting fuzzing through selective symbolic
execution. In Network and Distributed System Security Symposium,
NDSS, 2016.
[61] Robert E. Strom and Shaula Yemini. Typestate: A programming lan-
guage concept for enhancing software reliability. IEEE Trans. Software
Eng., pages 157–171, 1986.
[62] S. Tikhomirov, E. Voskresenskaya, I. Ivanitskiy, R. Takhaviev,
E. Marchenko, and Y. Alexandrov. Smartcheck: Static analysis of
1677
Authorized licensed use limited to: Inner Mongolia University of Technology. Downloaded on September 12,2023 at 03:09:37 UTC from IEEE Xplore. Restrictions apply.