Formal Specification and Verification of Smart Contracts for Azure Blockchain
Yuepeng Wang, Shuvendu K. Lahiri, Shuo Chen, Rong Pan, Isil Dillig, Cody Born, Immad Naseer
FFormal Specification and Verification of SmartContracts in Azure Blockchain
Yuepeng Wang
University of Texas at Austin [email protected]
Shuvendu K. Lahiri
Microsoft Research [email protected]
Shuo Chen
Microsoft Research [email protected]
Rong Pan
University of Texas at Austin [email protected]
Isil Dillig
University of Texas at Austin [email protected]
Cody Born
Microsoft Azure [email protected]
Immad Naseer
Microsoft Azure [email protected]
Abstract —Ensuring correctness of smart contracts isparamount to ensuring trust in blockchain-based systems. Thispaper studies the safety and security of smart contracts inthe
Azure Blockchain Workbench , an enterprise Blockchain-as-a-Service offering from Microsoft. As part of this study, weformalize semantic conformance of smart contracts against astate machine model with access-control policy and develop ahighly-automated formal verifier for Solidity that can produceproofs as well as counterexamples. We have applied our verifier V ERI S OL to analyze all contracts shipped with the AzureBlockchain Workbench, which includes application samples aswell as a governance contract for Proof of Authority (PoA).We have found previously unknown bugs in these publishedsmart contracts. After fixing these bugs, V ERI S OL was able tosuccessfully perform full verification for all of these contracts. I. I
NTRODUCTION
As a decentralized and distributed consensus protocol tomaintain and secure a shared ledger, the blockchain is seen asa disruptive technology with far-reaching impact on diverseareas. As a result, major cloud platform companies, includingMicrosoft, IBM, Amazon, SAP, and Oracle, are offeringBlockchain-as-a-Service (BaaS) solutions, primarily targetingenterprise scenarios, such as financial services, supply chains,escrow, and consortium governance. A recent study by Gartnerpredicts that the business value-add of the blockchain has thepotential to exceed $3.1 trillion by 2030 [16].Programs running on the blockchain are known as smartcontracts . The popular Ethereum blockchain provides a low-level stack-based bytecode language that executes on top ofthe Ethereum Virtual Machine (EVM). High-level languagessuch as Solidity and Serpent have been developed to enabletraditional application developers to author smart contracts.However, because blockchain transactions are immutable, bugsin smart contract code have devastating consequences, andvulnerabilities in smart contracts have resulted in severalhigh-profile exploits that undermine trust in the underlyingblockchain technology. For example, the infamous
TheDAO exploit [1] resulted in the loss of almost $60 million worth ofEther, and the
Parity Wallet bug caused 169 million USDworth of ether to be locked forever [4]. The only remedy forthese incidents was to hard-fork the blockchain and revert one of the forks back to the state before the incident. However,this remedy itself is devastating as it defeats the core valuesof blockchain, such as immutability, decentralized trust, andself-governance. This situation leaves no options for smartcontract programmers other than writing correct code to startwith.Motivated by the serious consequences of bugs in smartcontract code, recent work has studied many types of securitybugs such as reentrancy, integer underflow/overflow, and issuesrelated to delegatecalls on Ethereum. While these low-levelbugs have drawn much attention due to high-visibility incidentson public blockchains, we believe that the BaaS infrastructureand enterprise scenarios bring a set of interesting, yet lesswell-studied security problems.In this paper, we present our research on smart contractcorrectness in the context of Azure Blockchain, a BaaS solutionoffered by Microsoft [3]. Specifically, we focus on a cloudservice named
Azure Blockchain Workbench (or Workbenchfor short) [6], [7]. The Workbench allows an enterprisecustomer to easily build and deploy a smart contract applicationintegrating active directory, database, web UI, blob storage, etc.A customer implements the smart contract application (thatmeets the requirements specified in an application policy) anduploads it onto the Workbench. The code is then deployedto the underlying blockchain ledger to function as an end-to-end application. In addition to customer (application) smartcontracts, the Workbench system itself is comprised of smartcontracts that customize the underlying distributed blockchainconsensus protocols. Workbench ships one such smart contractfor the governance of the Ethereum blockchain that uses theProof-of-Authority (PoA) consensus protocol for validatingtransactions. Workbench relies on the correctness of the PoAgovernance contract to offer a trusted blockchain on Azure.Customer contracts in the Workbench architecture implementcomplex business logic, starting with a high-level finite-state-machine (FSM) workflow policy specified in a JSON file.Intuitively, the workflow describes (a) a set of categories ofusers called roles , (b) the different states of a contract, and(c) the set of enabled actions (or functions) at each staterestricted to each role. The high-level policy is useful to a r X i v : . [ c s . P L ] A p r esign contracts around state machine abstractions as wellas specify the required access-control for the actions. Whilethese state machines offer powerful abstraction patterns duringsmart contract design, it is non-trivial to decide whether agiven smart contract faithfully implements the intended FSM.In this paper, we define semantic conformance checking asthe problem of deciding whether a customer contract correctlyimplements the underlying workflow policy expressed as anFSM. Given a Workbench policy π that describes the workflowand a contract C , our approach first constructs a new contract C (cid:48) such that C semantically conforms to π if and only if C (cid:48) does not fail any assertions.In order to automatically check the correctness of theassertions in a smart contract (such as C (cid:48) or PoA governance),we develop a new verifier called V ERI S OL for smart contractswritten in Solidity. V ERI S OL is a general-purpose Solidityverifier and is not tied to Workbench. The verifier encodes thesemantics of Solidity programs into a low-level intermediateverification language Boogie and leverages the well-engineeredBoogie verification pipeline [14] for both verification andcounter-example generation. In particular, V ERI S OL takes ad-vantage of existing bounded model checking tool C ORRAL [24]for Boogie to generate witnesses to assertion violations, and itleverages practical verification condition generators for Boogieto automate correctness proofs. In addition, V
ERI S OL usesmonomial predicate abstraction [17], [22] to automaticallyinfer so-called contract invariants , which we have found to becrucial for automatic verification of semantic conformance.To evaluate the effectiveness and efficiency of V ERI S OL , wehave performed an experiment on all sample applicationsthat are shipped with the Workbench, as well as the PoAgovernance contract for the blockchain itself. V ERI S OL finds previously unknown defects in these published smart contracts,all of which have been confirmed as true bugs by the developers(many of them fixed at the time of writing). The experimentalresults also demonstrate the practicality of V ERI S OL in thatit can perform full verification of all the fixed contracts withmodest effort; most notably, V ERI S OL can automatically verify out of of the fixed versions of sample smart contractswithin . seconds on average. Contributions.
This paper makes the following contributions:1) We study the safety and security of smart contracts presentin Workbench, a BaaS offering.2) We formalize the Workbench application policy languageand define the semantic conformance checking problembetween a contract and a policy.3) We develop a new formal verifier V
ERI S OL for smartcontracts written in Solidity.4) We perform an evaluation of V ERI S OL on all the contractsshipped with Workbench. This includes all the applicationsamples as well as the highly-trusted PoA governancecontract.5) We report previously unknown bugs that have been con-firmed and several already fixed. Application Roles: Requestor (REQ) Responder (RES)
TF: SendResponseTF: SendRequest
Request Respond
AR: RESAIR: REQ
Legend
TF: Transition FunctionAIR: Allowed Instance RoleAR: Allowed Role
Fig. 1. Workflow policy diagram for HelloBlockchain application.
II. O
VERVIEW
In this section, we give an example of a Workbenchapplication policy for a sample contract and describe ourapproach for semantic conformance checking between thecontract and the policy.
A. Workbench Application Policy
Workbench requires every customer to provide a policy (or model ) representing the high-level workflow of the applicationin a JSON file . The policy consists of several attributes such asthe application name and description, a set of roles , as well asa set of workflows . For example, Figure 1 provides an informalpictorial representation of the policy for a simple applicationcalled HelloBlockchain . . The application consists of two global roles (see “Application Roles”), namely R EQUESTOR and R
ESPONDER . Informally, each role represents a set ofuser addresses and provides access control or permissions forvarious actions exposed by the application. We distinguish aglobal role from an instance role in that the latter applies to aspecific instance of the workflow. It is expected that the instanceroles are always a subset of the user addresses associated withthe global role.As illustrated in Figure 1, the simple
HelloBlockchain application consists of a single workflow with two states,namely
Request and
Respond . The data members (or fields)include instance role members (
Requestor and
Responder )that range over user addresses. The workflow consists of twoactions (or functions) in addition to the constructor function,
SendRequest and
SendResponse , both of which take a stringas argument.A transition in the workflow consists of a start state, anaction or function, an access control list, and a set of successorstates. Figure 1 describes two transitions, one from each of thetwo states. For example, the application can transition from
Request to Respond if a user belongs to the R
ESPONDER role https://docs.microsoft.com/en-us/azure/blockchain/workbench/configuration The example related details can be found on the associated web page: https://github.com/Azure-Samples/blockchain/tree/master/blockchain-workbench/application-and-smart-contract-samples/hello-blockchain A R) and invokes the action
SendResponse . An “ApplicationInstance Role” ( A IR) refers to an instance role data memberof the workflow that stores a member of a global role (suchas
Requestor ). For instance, the transition from
Respond to Request in Figure 1 uses an
AIR and is only allowed if theuser address matches the value stored in the instance datavariable
Requestor . pragma solidity ^0.4.20;contract HelloBlockchain {//Set of Statesenum StateType {Request , Respond}//List of propertiesStateType public State;address public Requestor;address public Responder;string public RequestMessage;string public ResponseMessage;// constructor functionfunction HelloBlockchain(string message) constructor_checker() public{ Requestor = msg.sender;RequestMessage = message;State = StateType.Request;}// call this function to send a requestfunction SendRequest(string requestMessage) SendRequest_checker() public{ RequestMessage = requestMessage;State = StateType.Request;}// call this function to send a responsefunction SendResponse(string responseMessage)
SendResponse_checker() public{ Responder = msg.sender;ResponseMessage = responseMessage;State = StateType.Respond;}
Fig. 2. Solidity contract for HelloBlockchain application.
B. Workbench Application Smart Contract
After specifying the application policy, a user provides asmart contract for the appropriate blockchain ledger to imple-ment the workflow. Currently, Workbench supports the popularlanguage Solidity for targeting applications on Ethereum.Figure 2 describes a Solidity smart contract that implementsthe
HelloBlockchain workflow in the
HelloBlockchain application. For the purpose of this sub-section, we start byignoring the portions of the code that are underlined . Thecontract declares the data members present in the configurationas state variables with suitable types. Each contract implement-ing a workflow defines an additional state variable
State totrack the current state of a workflow. The contract consists ofthe constructor function along with the two functions definedin the policy, with matching signatures. The functions set thestate variables and update the state variables appropriately toreflect the state transitions. The Workbench service allows a user to upload the policy, theSolidity code, and optionally add users and perform variousactions permitted by the configuration. Although the smartcontract drives the application, the policy is used to exposethe set of enabled actions at each state for a given user.Discrepancies between the policy and Solidity code can leadto unexpected state transitions that do not conform to the high-level policy. To ensure the correct functioning and security ofthe application, it is crucial to verify that the Solidity programsemantically conforms to the intended meaning of the policyconfiguration.
C. Semantic Conformance Verification
Given the application policy and a smart contract, wedefine the problem of semantic conformance between thetwo that ensures that the smart contract respects the policy(Section III-B). Moreover, we reduce the semantic conformanceverification problem to checking assertions on an instrumentedSolidity program. For the
HelloBlockchain application, theinstrumentation is provided by adding the underlined modifier invocations in Figure 2. A modifier is a Solidity languageconstruct that allows wrapping a function invocation with codethat executes before and after the execution. function nondet() returns (bool); //no definition// Checker modifiersmodifier constructor_checker(){ _;assert (nondet() /* global role REQUESTOR */==> State == StateType.Request);}modifier SendRequest_checker(){ StateType oldState = State;address oldRequestor = Requestor;_;assert ((msg.sender == oldRequestor &&oldState == StateType.Respond)==> State == StateType.Request);}modifier SendResponse_checker(){ StateType oldState = State;_;assert ((nondet() /* global role RESPONDER */ &&oldState == StateType.Request)==> State == StateType.Respond);}
Fig. 3. Modifier definitions for instrumented HelloBlockchain application.
Figure 3 shows the definition of the modifiers used toinstrument for conformance checking. Intuitively, we wrapthe constructor and functions with checks to ensure that theyimplement the FSM state transitions correctly. For example,if the FSM transitions from a state s to a state s upon theinvocation of function f by a user with access control ac , thenwe instrument the definition of f to ensure that any executionstarting in s with access control satisfying ac should transitionto s .Finally, given the instrumented Solidity program, we dis-charge the assertions statically using a new formal verifier forSolidity called V ERI S OL . The verifier can find counterexamplesin the form of a sequence of transactions involving calls to theconstructor and public methods) as well as automatically con-struct proofs of semantic conformance. Note that, even thoughthe simple HelloBlockchain example does not contain anyunbounded loops or recursion, verifying semantic conformancestill requires reasoning about executions that involve unboundednumbers of calls to the two public functions. We demonstratethat V
ERI S OL is able to find deep violations of the conformanceproperty for well-tested Workbench applications, as wellas automatically construct inductive proofs for most of theapplication samples shipped with Workbench.III. S EMANTIC C ONFORMANCE C HECKING FOR W ORKBENCH P OLICIES
In this section, we formalize the Workbench applicationpolicy that we informally introduced in Section II. Our formal-ization can be seen as a mathematical representation of theofficial Workbench application JSON schema documentation.
A. Formalization of Workbench Application Policies
The Workbench policy for an application allows the user todescribe (i) the data members and actions of an application,(ii) a high-level state-machine view of the application, and (iii)role-based access control for state transitions . The role-basedaccess control provides security for deploying smart contractsin an open and adversarial setting; the high-level state machinenaturally captures the essence of a workflow that progressesbetween a set of states based on some actions from the user.More formally, a
Workbench Application Policy is a pair ( R , W ) where R is a set of global roles used for accesscontrol, and W is a set of workflows defining a kind of finitestate machine. Specifically, a workflow is defined by a tuple (cid:104)S , s , R w , F , F , ac , γ (cid:105) where: • S is a finite set of states , and s ∈ S is an initial state • R w is a finite set of instance roles of the form ( id : t ) ,where id is an identifier and t is a role drawn from R • F ( id , . . . , id k ) is a set of actions (functions) , with F denoting an initial action (constructor) • ac ⊆ R is the initiator role for restricting users that cancreate an instance of the contract • γ ⊆ S × F × ( R w ∪ R ) × S is a set of transitions. Givena transition τ = ( s, f, ac, S ) , we write τ.s, τ.f, τ.ac, τ.S todenote s, f, ac , and S respectivelyIntuitively, S defines the different “states” that the contractcan be in, and γ describes which state can transition to whatother states by performing certain actions. The transitions areadditionally “guarded” by roles (which can be either global orinstance roles) that qualify which users are allowed to performthose actions. As mentioned earlier in Section II, each “role”corresponds to a set of users (i.e., addresses on the blockchain).The use of instance roles in the workbench policy allowsdifferent instances of the contract to authorize different usersto perform certain actions. B. Semantic Conformance
Given a contract C and a Workbench Application policy π , semantic conformance between C and π requires that thecontract C faithfully implements the policy specified by π . Inthis subsection, we first define some syntactic requirements onthe contract, and then formalize what we mean by semanticconformance between a contract and a policy. Syntactic conformance.
Given a client contract C and a policy π = ( R , W ) , our syntactic conformance requirement stipulatesthat the contract for each w ∈ W implements all the instancestate variables as well as definitions for each of the functions.Additionally, each contract function has a parameter called sender , which is a blockchain address that denotes the user orcontract invoking this function. Finally, each contract shouldcontain a state variable s w that ranges over S w , for each w ∈ W . Semantic conformance.
We formalize the semantic conformancerequirement for smart contracts using Floyd-Hoare triples ofthe form { φ } S { ψ } indicating that any execution of statement S starting in a state satisfying φ results in a state satisfying ψ (if the execution of S terminates). We can define semanticconformance between a contract C and a policy π as a set ofHoare triples, one for each pair ( m, s ) where m is a methodin the contract and s is a state in the Workbench policy. At ahigh-level, the idea is simple: we insist that, when a function isexecuted along a transition, the resulting state transition shouldbe in accordance with the Workbench policy.Given an application policy π = ( R , W ) and workflow w = (cid:104)S , s , R w , F , F , ac , γ (cid:105) ∈ W , we can formalize thishigh-level idea by using the following Hoare triples:1) Initiation . { sender ∈ ac } F ( v , . . . , v k ) { s w = s } The Hoare triple states that the creation of an instance ofthe workflow with the appropriate access control ac resultsin establishing the initial state.2) Consecution . Let τ = ( s , f , ac , S ) be a transition in γ . Then, for each such transition, semantic conformancerequires the following Hoare triple to be valid: { sender ∈ ac ∧ s w = s } f ( v , . . . , v k ) { s w ∈ S } Here, the precondition checks two facts: First, the sender must satisfy the access control, and, second, the startstate must be s . The post-condition asserts that theimplementation of method f in the contract results in astate that is valid according to policy π . C. Instrumentation for Semantic Conformance Checking
As mentioned in Section II, our approach checks semanticconformance of Solidity contracts by (a) instrumenting thecontract with assertions, and (b) using a verification tool tocheck that none of the assertions can fail. We explain ourinstrumentation strategy in this subsection and refer the readerto Section IV for a description of our verification tool chain.ur instrumentation methodology heavily relies on the modifier construct in Solidity. A modifier has syntax verysimilar to a function definition in Solidity with a name andlist of parameters and a body that can refer to parameters andglobals in scope. The general structure of a modifier definitionwithout any parameters is [2]: modifier Foo() {pre -stmt;_;post -stmt;} where pre-stmt and post-stmt are Solidity statements.When this modifier is applied to a function
Bar , function Bar(int x) Foo (){Bar -stmt;} the Solidity compiler transforms the body of Bar to execute pre-stmt (respectively, post-stmt ) before (respectively, af-ter)
Bar-stmt . This provides a convenient way to inject codeat multiple return sites from a procedure and also inject codebefore the execution of the constructor code (since a constructormay invoke other base class constructors implicitly).We now define a couple of helper predicates before describ-ing the actual checks. Let P ( ac ) be a predicate that encodesthe membership of sender in the set ac : P ( ac ) . = false , ac = {} msg . sender = q, ac = { q ∈ R w } nondet () ac = { r ∈ R} P ( ac ) ∨ P ( ac ) ac = ac ∪ ac Here nondet is a side-effect free Solidity function that returnsa non-deterministic Boolean value at each invocation. For thesake of static verification, one can declare a function withoutany definition. This allows us to model the membership check sender ∈ ac conservatively in the absence of global roles onthe blockchain.Next, we define a predicate for membership of a contractstate in a set of states S (cid:48) ⊆ S using α ( S (cid:48) ) as follows: α ( S (cid:48) ) . = false , S (cid:48) = {} s w = s, S (cid:48) = { s ∈ S} α ( S ) ∨ α ( S ) , S (cid:48) = S ∪ S We can now use these predicates to define the source codetransformations below:
Constructor.
We add the following modifier to constructors: modifier constructor_checker () {_;assert ( P ( ac ) ⇒ α ( { s } ) );} Here, the assertion ensures that the constructor sets up thecorrect initial state when executed by a user with access control ac . Other functions.
For a function g , let γ g . = { τ ∈ γ | τ =( s , g, ac , S ) } be the set of all transitions where g is invoked. call f (*);while (true) {if (*) call f (*);else if (*) call f (*);...else if (*) call f n (*);} Fig. 4. Harness for Solidity contracts modifier g_checker () {// copy old StateStateType oldState = s w ;// copy old instance role vars..._;assert (cid:86) τ ∈ γ g ( old ( P ( τ. ac ) ∧ α ( { τ. s } )) ⇒ α ( τ. S )) ;} Here, the instrumented code first copies the s w variableand all of the variables in R w into corresponding “old”copies. Next, the assertion checks that if the function isexecuted in a transition τ , then state transitions to one ofthe successor states in τ. S . The notation old ( e ) replacesany occurrences of a state variable (such as s w ) with the“old” copy that holds the value at the entry to the function.Figure 3 shows the modifier definitions for our running example HelloBlockchain described in Section II Although we showthe nondet () to highlight the issue of global roles, one cansafely replace nondet () with true since the function onlyappears negatively in any assertion. In fact, this observationallows us to add the simplified assertions for runtime checkingas well. Finally, since conjunction distributes over assertions,we can replace the single assertion with an assertion for eachtransition in the implementation.IV. F ORMAL V ERIFICATION USING V ERI S OL In this section, we present our formal verifier calledV
ERI S OL for checking the correctness of assertions in Soliditysmart contracts. Since our verifier is built on top of theBoogie tool chain, it can be used for both verification andcounterexample generation. A. General Methodology
Let C = { λ (cid:126)x .f , λ (cid:126)x .f , . . . , λ (cid:126)x n .f n } be a smartcontract annotated with assertions where: • λ (cid:126)x .f is the constructor • λ (cid:126)x i .f i for i ∈ [1 , n ] are public functionsOur verification methodology is based on finding a contractinvariant I satisfying the following Hoare triples: | = { true } f {I} (1) | = {I} f i {I} for all i ∈ [1 , n ] (2)Here, the first condition states the contract invariant isestablished by the constructor, and the second condition statesthat I is inductive — i.e., it is preserved by every publicfunction in C . Note that such a contract invariant suffices to eriSol BoogieTranslation InvariantGenerationBoundedModelChecking
Smart Contractwith Assertions Fully Veri fi edVeri fi ed Up ToK TransactionsCounterexample Fig. 5. Schematic workflow of V
ERI S OL . establish the validity of all assertions in the contract under any possible sequence of function invocations of the contract.To see why this is the case, consider a “harness” that invokesthe functions in C as in Figure 4. This harness first createsan instance of the contract by calling the constructor, andthen repeatedly and non-deterministically invokes one of thepublic functions of C . Observe that the Hoare triples (1) and(2) listed above essentially state that I is an inductive invariantof the loop in this harness; thus, the contract invariant I overapproximates the state of the contract under any sequenceof the contract’s function invocations. Furthermore, when thefunctions contain assertions, the Hoare triple {I} f i {I} canonly be proven if I is strong enough to imply the assertionconditions. Thus, the validity of the Hoare triples in (1) and(2) establishes correctness under all possible usage patterns ofthe contract. B. Overview
We now describe the design of our tool called V
ERI S OL forchecking safety of smart contracts. V ERI S OL is based on theproof methodology outlined in Section IV-A, and its workflowis illustrated in Figure 5. At a high-level, V ERI S OL takes asinput a Solidity contract C annotated with assertions and yieldsone of the following three outcomes: • Fully verified:
This means that the assertions in C areguaranteed not to fail under any usage scenario. • Refuted:
This indicates that C was able to find at least oneinput and invocation sequence of the contract functions underwhich one of the assertions is guaranteed to fail. • Partially verified:
When V
ERI S OL can neither verify norrefute contract correctness, it performs bounded verificationto establish that the contract is safe up to k transactions.This essentially corresponds to unrolling the ”harness” loopfrom Figure 4 k times and then verifying that the assertionsdo not fail in the unrolled version.V ERI S OL consists of three modules, namely (a) BoogieTranslation from a Solidity program, (b)
Invariant Generation to infer a contract invariant as well as loop invariants andprocedure summaries, and (c)
Bounded Model Checking toexplore assertion failures within all transactions up to a user-specified depth k . In the remaining subsections, we discusseach of these components in more detail. ct ∈ ContractNameset ∈ SolElemTypes ::= integer | string | addressst ∈ SolTypes ::= et | ct | et ⇒ st se ∈ SolExprs ::= c | x | op ( se , . . . , se ) | se [ se ] | msg.sender | x.length sst ∈ SolStmts ::= x := se | x [ se ] . . . [ se ] := y | sst ; sst | require ( se ) | assert ( se ) | if ( se ) { sst } else { sst }| while ( se ) do { sst } | x . push ( se ) | se := f ( (cid:126) se ) | se := se . f ( (cid:126) se ) | se := new C ( (cid:126) se ) | se := new t []( se ) | se := new ( t ⇒ t ⇒ . . . ⇒ t k ⇒ t )() Fig. 6. A subset of Solidity language. C denotes a contract name, t denotesan elementary Solidity type, and f denotes a function name. C. Solidity to Boogie Translation
In this subsection, we formally describe our translation ofSolidity source code to the Boogie intermediate verificationlanguage. We start with a brief description of Solidity andBoogie, and then discuss our translation.
Solidity Language.
Figure 6 shows a core subset of Soliditythat we use for our formalization. At a high level, Solidity isa typed object-oriented programming language with built-insupport for basic verification constructs, such as the require construct for expressing pre-conditions.Types in our core language include integers, strings, contracts,addresses, and mappings. We use the notation τ ⇒ τ todenote a mapping from a value of type τ to a value of type τ (where τ can be a nested map type). Since arrays can beviewed as a special form of mapping integer ⇒ τ , we do notintroduce a separate array type to simplify presentation.As standard, expressions in Solidity include constants, localvariables, state variables (i.e., fields in standard object-orientedlanguage terminology), unary/binary operators (denoted op ),and array/map lookup e [ e (cid:48) ] . Given an array x , the expression x.length yields the length of that array, and msg.sender yields the address of the contract or user that initiates thecurrent function invocation.Statements in our core Solidity language include assignments,conditional statements, loops, sequential composition, arrayinsertion ( push ), internal and external function calls, contractinstance creation, and dynamic allocations. The construct require is used to specify the precondition of a function, and assert checks that its input evaluates to true and terminatesexecution otherwise. Solidity differentiates between two typesof function calls: internal and external. An internal call se := f ( (cid:126) se ) invokes the function f and keeps msg.sender unchanged. An external call se := se . f ( (cid:126) se ) invokes function f in the contract instance pointed by se (which may include this ), and uses this as the msg.sender for the callee. bt ∈ BoogieElemTypes ::= int | Ref bt ∈ BoogieTypes ::= bbt | [ bbt ] bt e ∈ Exprs ::= c | x | op ( e , . . . , e ) | uf ( e , . . . , e ) | x [ e ] . . . [ e ] | ∀ i : bbt :: est ∈ Stmts ::= skip | havoc x | x := e | x [ e ] . . . [ e ] := e | assume e | assert e | call (cid:126) x := f ( e , . . . , e ) | st ; st | if ( e ) { st } else { st } | while ( e ) do { st } Fig. 7. A subset of Boogie language.
We omit several aspects of the language that are desugaredinto our core Solidity language. This includes fairly compre-hensive support for modifiers , libraries and structs . Boogie Language.
Since our goal is to translate Solidityto Boogie, we also give a brief overview of the Boogieintermediate verification language. As shown in Figure 7,types in Boogie include integers ( int ), references (
Ref ),and nested maps. Expressions (
Exprs ) consist of constants,variables, arithmetic and logical operators ( op ), uninterpretedfunctions ( uf ), map lookups, and quantified expressions.Standard statements ( Stmts ) in Boogie consist of skip, variableand array/map assignment, sequential composition, conditionalstatements, and loops. The havoc x statement assigns anarbitrary value of appropriate type to a variable x . A procedurecall ( call (cid:126) x := f ( e , . . . , e ) ) returns a vector of values thatcan be stored in local variables. The assert and assume statements behave as no-ops when their arguments evaluate totrue and terminate execution otherwise. An assertion failure isconsidered a failing execution, whereas an assumption failureblocks. From Solidity to Boogie types.
We define a function µ : SolTypes → BoogieTypes that translates a Solidity type to atype in Boogie as follows: µ ( st ) . = int , st ∈ { integer , string } Ref , st ∈ { address } ∪ ContractNames
Ref , st . = et ⇒ st Specifically, we translate Solidity integers and strings to Boogieintegers; addresses, contract names, and mappings to Boogiereferences. Note that we represent Solidity strings as integers inBoogie because Solidity only allows equality checks betweenstrings in the core language.
From Solidity to Boogie expressions.
We present our trans-lation from Solidity to Boogie expressions using judgmentsof the form (cid:96) e (cid:44) → χ in Figure 8, where e is a Solidityexpression and χ is the corresponding Boogie expression. WhileSolidity local variables and the expression msg.sender aremapped directly into Boogie local variables and parametersrespectively, state variables in Solidity are translated into arraylookups. Specifically, for each state variable x for contract C , we introduce a mapping x C from contract instances o x ∈ LocalVars (cid:96) x (cid:44) → x (Var1) v = msg_sender (cid:96) msg.sender (cid:44) → v (Sender) Type ( c ) (cid:54) = string (cid:96) c (cid:44) → c (Const1) x ∈ StateVars(C) (cid:96) x (cid:44) → x C [ this ] (Var2) Type ( c ) = string c (cid:48) = Hash ( c ) (cid:96) c (cid:44) → StrToInt ( c (cid:48) ) (Const2) (cid:96) x (cid:44) → χ (cid:96) x . length (cid:44) → Length [ χ ] (Len) (cid:96) e (cid:44) → χ (cid:96) e (cid:44) → χ Type ( e ) = t Type ( e [ e ]) = t (cid:96) e [ e ] (cid:44) → M µ ( t ) µ ( t ) [ χ ][ χ ] (Map) (cid:96) e i (cid:44) → χ i i = 1 , . . . , n (cid:96) op ( e , . . . , e n ) (cid:44) → op ( χ , . . . , χ n ) (Op) Fig. 8. Inference rules for encoding Solidity expressions to Boogie expressions.
Type ( e ) is a function that returns the static type of Solidity expression e . to the value stored in its state variable x . Thus, reads fromstate variable x are modeled as x C [ this ] in Boogie. Next, wetranslate string constants in Solidity to Boogie integers usingan uninterpreted function called StrToInt that is applied to ahash of the string . As mentioned earlier, this string-to-integertranslation does not cause imprecision because Solidity onlyallows equality checks between variables of type string.Similar to our handling of state variables, our Boogieencoding also introduces an array called Length to map eachSolidity array to its corresponding length. Thus, a Solidityexpression x . length is translated as Length [ χ ] where χ isthe Boogie encoding of x .The translation of array/map look-up is somewhat moreinvolved due to potential aliasing issues. First, the basic ideais that for each map of type t ⇒ t , we introduce a Boogiemap M τ (cid:48) τ where τ is the Boogie encoding of type t (i.e., τ = µ ( t ) ) and τ (cid:48) is the Boogie encoding of type t (i.e., τ (cid:48) = µ ( t ) ). Intuitively, M τ (cid:48) τ maps each array/map object toits contents, which are in turn represented as a map. Thus, wecan think of M τ (cid:48) τ as a two-dimensional mapping where the firstdimension is the address of the Solidity map and the seconddimension is the look-up key. For a nested map expression e of type t ⇒ t where t is a nested map/array, observe thatwe look up e in M Ref µ ( t ) since maps and arrays in Solidityare dynamically allocated. Intuitively, everything that can bedynamically allocated is represented with type Ref in ourencoding to allow for potential aliasing.
Example 1.
Suppose that contract C has a state variable x ofSolidity type mapping(int => int[]) , which corresponds tothe type int => (int => int) in our core Solidity language. We assume that the hash function is collision-free. In our implementation,we enforce this by keeping a mapping from each string constant to a counter. he expression x[0][1] will be translated into the Boogieexpression M intint [ e ][1] where e is M Refint [ x C [ this ] ][0] usingthe rules from Figure 8. From Solidity to Boogie statements.
Figure 9 presents thetranslation from Solidity to Boogie statements using judgmentsof the form (cid:96) s (cid:32) ω indicating that Solidity statement s is translated to Boogie statement(s) ω . Since most rules inFigure 9 are self-explanatory, we only explain our translationfor assignments, function calls, and dynamic allocations. Function calls.
Functions in Solidity have two implicit parame-ters, namely this for the receiver object and msg.sender forthe Blockchain address of the caller. Thus, when translatingSolidity calls to their corresponding Boogie version, weexplicitly pass these parameters in the Boogie version. However,recall that the value of the implicit msg.sender parametervaries depending on whether the call is external or internal. Forinternal calls, msg.sender remains unchanged, whereas forexternal calls, msg.sender becomes the current receiver object.For both types of calls, our translation introduces a conditionalstatement to deal with dynamic dispatch. Specifically, ourBoogie encoding introduces a map τ to store the dynamic typeof receiver objects at allocation sites, and the translation offunction calls invokes the correct version of the method basedon the content of τ for the receiver object. Dynamic allocation.
Dynamic memory allocations in Solidityare translated into Boogie code with the aid of a helperprocedure New (shown in Figure 10) which always returns afresh reference. As shown in Figure 10, the New procedure isimplemented using a global map
Alloc to indicate whethera reference is allocated or not. All three types of dynamicmemory allocation (contract, array, and map) use this helperNew procedure to generate Boogie code.In the case of contract creation (labeled NewCont inFigure 9), the Boogie code we generate updates the τ mapmentioned previously in addition to allocating new memory.Specifically, given the freshly allocated reference v returned byNew, we initialize τ [ v ] to be C and also call C ’s constructoras required by Solidity semantics.Next, let us consider the allocation of array objects describedin rule NewArr in Figure 9. Recall that our Boogie encodinguses a map called Length to keep track of the size ofevery array. Thus, in addition to allocating new memory, thetranslation of array allocation also updates the
Length arrayand initializes all elements in the array to be zero (or null).Finally, the rule NewMap shows how to translate map alloca-tions in Solidity to Boogie using an auxiliary Boogie procedurecalled MapInit (shown in Figure 10) for map initialization.Given an n -dimensional map, the MapInit procedure iterativelyallocates lower dimensional maps and ensures that valuesstored in the map do not alias each other as well as anyother previously allocated reference. Example 2.
The Solidity code x := new (int => int => int)() is translated into the following Boogie code: call v := New(); assume Length[v] = 0; assume ∀ i :: Length[ M Refint [v][i]] = 0; assume ∀ i :: ¬ Alloc[ M Refint [v][i]]; call NewUnbounded(); assume ∀ i :: Alloc[ M Refint [v][i]]; assume ∀ i,j :: i=j ∨ M Refint [v][i] (cid:54) = M Refint [v][j]; assume ∀ i,j :: M intint [ M Refint [v][i]][j] = 0; x C [this] := v; First of all, we allocate a fresh reference v and initialize thelength of v and every inner map v [ i ] to zero (lines 1 - 2).Second, we allocate fresh references for every inner map v [ i ] (lines 3 - 5), and ensure the references of inner maps v [ i ] and v [ j ] do not alias if i (cid:54) = j (line 6). Finally, we initializeevery element v [ i ][ j ] to zero and assign reference v to the statevariable x (lines 7 - 8).D. Invariant Generation As mentioned earlier, translating Solidity code into Boogie al-lows V
ERI S OL to leverage the existing ecosystem around Boo-gie, including efficient verification condition generation [25].However, in order to completely automate verification (evenfor loop and recursion-free contracts), we still need to infera suitable contract invariant as discussed in Section IV-B.Specifically, recall that the contract invariant must satisfy thefollowing two properties:1) | = { true } f {I} | = {I} f i {I} for all i ∈ [1 , n ] V ERI S OL uses monomial predicate abstraction ( [17], [22],[23]) to automatically infer contract invariants satisfying theabove properties. Specifically, the contract invariant inferencealgorithm conjectures the conjunction of all candidate predi-cates as an inductive invariant and progressively weakens itbased on failure to prove a candidate predicate inductive. Thisalgorithm converges fairly fast even on large examples butrelies on starting with a superset of necessary predicates. Inthe current implementation of V ERI S OL , we obtain candidateinvariants by instantiating the predicate template e (cid:46)(cid:47) e where (cid:46)(cid:47) is either equality or disequality. Here, expressions e , e can be instantiated with variables corresponding to roles andstates in the Workbench policy as well as constants. We havefound these candidate predicates to be sufficiently general forautomatically verifying semantic conformance of Workbenchcontracts; however, additional predicates may be required forother types of contracts. E. Bounded Model Checking
If V
ERI S OL fails to verify contract correctness usingmonomial predicate abstraction, it employs an assertion-directed bounded verifier, namely C ORRAL [24], to lookfor a transaction sequence leading to an assertion violation.C
ORRAL analyzes the harness in Figure 4 by unrolling theloop k times and uses a combination of abstraction refinement techniques (including lazy inlining of nested procedures) tolook for counterexamples in a scalable manner. Thus, whenV ERI S OL fails to verify the property, it either successfully findsa counterexample or verifies the lack of any counterexamplewith k transactions. e (cid:44) → χ (cid:96) e (cid:44) → χ (cid:96) e := e (cid:32) χ := χ (Asgn) (cid:96) e (cid:44) → χ (cid:96) require ( e ) (cid:32) assume χ (Req) (cid:96) e (cid:44) → χ (cid:96) assert ( e ) (cid:32) assert χ (Asrt) (cid:96) e (cid:44) → χ (cid:96) s (cid:32) ω (cid:96) s (cid:32) ω (cid:96) if ( e ) { s } else { s } (cid:32) if ( χ ) { ω } else { ω } (Cond) (cid:96) e (cid:44) → χ (cid:96) s (cid:32) ω (cid:96) while ( e ) do { s } (cid:32) while ( χ ) do { ω } (Loop) (cid:96) e r (cid:44) → χ r (cid:96) e i (cid:44) → χ i i = 0 , . . . , n fresh v C j < : Type ( this ) j = 1 , . . . , mω ≡ if ( τ [ this ] = C ) { call v := f C ( this , χ , . . . , χ n , msg_sender ); χ r := v } else if . . . else if ( τ [ this ] = C m ) { call v := f C m ( this , χ , . . . , χ n , msg_sender ); χ r := v }(cid:96) e r := f ( e , . . . , e n ) (cid:32) ω (IntCall) (cid:96) e r (cid:44) → χ r (cid:96) e i (cid:44) → χ i i = 0 , . . . , n fresh v C j < : Type ( e ) j = 1 , . . . , mω ≡ if ( τ [ χ ] = C ) { call v := f C ( χ , χ , . . . , χ n , this ); χ r := v } else if . . . else if ( τ [ χ ] = C m ) { call v := f C m ( χ , χ , . . . , χ n , this ); χ r := v }(cid:96) e r := e .f ( e , . . . , e n ) (cid:32) ω (ExtCall) (cid:96) e r (cid:44) → χ r (cid:96) e i (cid:44) → χ i i = 1 , . . . , n fresh vω ≡ call v := New (); assume τ [ v ] = C ; call f C ( v, χ , . . . , χ n , this ); χ r := v (cid:96) e r := new C ( e , . . . , e n ) (cid:32) ω (NewCont) (cid:96) x [ x . Length++ ] := e (cid:32) ω (cid:96) x . push ( e ) (cid:32) ω (Push) (cid:96) e r (cid:44) → χ r (cid:96) e (cid:44) → χ fresh v (cid:96) v [ i ] (cid:44) → χ i ω ≡ call v := New ();
Length [ v ] := χ ; assume ∀ i :: χ i = 0; χ r := v (cid:96) e r := new t []( e ) (cid:32) ω (NewArr) (cid:96) s (cid:32) ω (cid:96) s (cid:32) ω (cid:96) s ; s (cid:32) ω ; ω (Seq) (cid:96) e r (cid:44) → χ r fresh v (cid:96) v [ i ] . . . [ i n ] (cid:44) → χω ≡ call v := New (); call
MapInit ( v, n ); assume ∀ i , . . . , i n :: χ = 0; χ r := v (cid:96) e r := new ( t ⇒ . . . ⇒ t n ⇒ t )() (cid:32) ω (NewMap) Fig. 9. Inference rules for encoding Solidity statements to Boogie statements.
Type ( e ) is a function that returns the static type of Solidity expression e . Symbol f C denotes the function f in contract C , and f C denotes the constructor of contract C . The < : relation represents the sub-typing relationship. τ is a globalBoogie map that maps receiver objects to their dynamic types. Types for universally quantified Boogie variables are omitted for brevity. var Alloc: [Ref]bool;procedure New() returns (ret: Ref) {havoc ret; assume (!Alloc[ret]);Alloc[ret] := true;}procedure NewUnbounded() {var oldAlloc: [Ref]bool;oldAlloc := Alloc; havoc Alloc;assume ∀ i ::oldAlloc[i] ==> Alloc[i];}procedure MapInit(v: Ref, n: int) {var j: int; j := 1; Length[v] := 0;while (j < n) {assume ∀ i , . . . , i j ::Length[ χ ( v, i , . . . , i j ) ]=0;assume ∀ i , . . . , i j :: ¬ Alloc[ χ ( v, i , . . . , i j ) ];call NewUnbounded();assume ∀ i , . . . , i j ::Alloc[ χ ( v, i , . . . , i j ) ];assume ∀ i , . . . , i j , i (cid:48) j :: ( i j = i (cid:48) j ) ∨ χ ( v, i , . . . , i j ) (cid:54) = χ ( v, i , . . . , i (cid:48) j ) ;j := j + 1; }} Fig. 10. Auxiliary Boogie procedures. The term χ ( v, i , . . . , i j ) denotes thetranslation result of Solidity expression v [ i ] . . . [ i j ] . Types for universallyquantified Boogie variables are omitted for brevity. V. E
VALUATION
We evaluate the effectiveness and efficiency of V
ERI S OL by performing two sets of experiments on smart contractsshipped with Workbench: (i) semantic conformance checkingfor Workbench samples, and (ii) checking safety and security properties for the PoA governance contract. The goal of ourevaluation is to answer the following research questions: RQ1
How does V
ERI S OL perform when checking semanticconformance of Workbench application policies? RQ2
How applicable is V
ERI S OL on smart contracts withcomplex data structures (such as PoA)? Experimental Setup.
Due to limited resources, we set ourtimeout as one hour for every benchmark. All experimentsare conducted on a machine with Intel Xeon(R) E5-1620 v3CPU and 32GB of physical memory, running the Ubuntu 14.04operating system.
A. Semantic Conformance for Workbench Application Policies
Benchmarks.
We have collected all sample smart contracts thatare shipped with Workbench and their corresponding applica-tion policies on the Github repository of Azure Blockchain [5].These smart contracts and their policies depict various workflowscenarios that are representative in real-world enterprise usecases. The smart contracts exercise various features of Soliditysuch as arrays, nested contract creation, external calls, enumtypes, and mutual-recursion. For each smart contract C and itsapplication policy π , we perform program instrumentation as ABLE IE
XPERIMENTAL R ESULTS OF S EMANTIC C ONFORMANCE AGAINST W ORKBENCH A PPLICATION P OLICIES . Name Description Orig Inst Init Status Status after Fix Time (s)SLOC SLOC
AssetTransfer
Selling high-value assets 192 444 Refuted Fully Verified 2.1
BasicProvenance
Keeping record of ownership 43 95 Fully Verified Fully Verified 1.5
BazaarItemListing
Multiple workflow scenario for selling items 98 175 Refuted Fully Verified 2.3
DefectCompCounter
Product counting using arrays for manufacturers 31 68 Fully Verified Fully Verified 1.3
DigitalLocker
Sharing digitally locked files 129 260 Refuted Fully Verified 1.7
FreqFlyerRewards
Calculating frequent flyer rewards using arrays 47 90 Fully Verified Fully Verified 1.3
HelloBlockchain
Request and response (Figure 1) 32 78 Fully Verified Fully Verified 1.3
PingPongGame
Multiple workflow for two-player games 74 136 Refuted Fully Verified (manual) 2.1
RefrigTransport
Provenance scenario with IoT monitoring 118 187 Fully Verified Fully Verified 2.2
RoomThermostat
Thermostat installation and use 42 99 Fully Verified Fully Verified 1.3
SimpleMarketplace
Owner and buyer transactions 62 118 Fully Verified Fully Verified 1.4
Average - 79 159 - - 1.7 explained in Section III-C to obtain contract C (cid:48) . Note that noassertion failure of C (cid:48) is equivalent to the semantic conformancebetween C and π , so we include such instrumented smartcontracts in our benchmark set. Main Results.
Table I summarizes the results of our firstexperimental evaluation. Here, the “Name” column gives thename of the contract, and the “Description” column describesthe contract’s usage scenario. The next two columns givethe number of lines of Solidity code before and after theinstrumentation described in Section III-C. The last threecolumns present the main verification results: In particular,“Init Status” shows the result of applying V
ERI S OL on theoriginal smart contract, and “Status after Fix” presents theresult of V ERI S OL after we manually fix the bug (if any).The fix may require changing either the policy or the contract,depending on the contract author’s feedback. Finally, “Time”shows the running time of V ERI S OL in seconds when appliedto the fixed contracts.Our experimental results demonstrate that V ERI S OL isuseful for checking semantic conformance between Workbenchcontracts and the policies they are supposed to implement.In particular, V ERI S OL finds bugs in of well-testedcontracts and precisely pinpoints the trace leading to theviolation. Our results also demonstrate that V ERI S OL caneffectively automate semantic conformance proofs, as it cansuccessfully verify all the contracts after fixing the originalbug. Moreover, for out of the contracts with theexception of PingPongGame , the invariant inference techniquessufficed to make the proofs completely push-button. Ourcandidate templates for contract invariant did not suffice for the
PingPongGame contract mainly due to the presence of mutuallyrecursive functions between two contracts. This required us tomanually provide a function summary for the mutually recursiveprocedures that states an invariant over the state variable s w of the sender contract represented by the msg.sender (e.g. s w [ msg.sender ] = s ∨ s w [ msg.sender ] = s where s i arestates of the sender contract). This illustrates that we canachieve the power of the underlying sound Boogie modularverification to perform non-trivial proofs with modest manual function Accept() public{ if (msg.sender != InstanceBuyer &&msg.sender != InstanceOwner) {revert();}...if (msg.sender == InstanceBuyer) {...}else {// msg.sender has to be InstanceOwner// from the revert earlierif (State == StateType.NotionalAcceptance) {State = StateType.SellerAccepted;}else if (State == StateType.BuyerAccepted) {// NON-CONFORMANCE: JSON transitions// to StateType.SellerAcceptedState = StateType.Accepted;}}...} Fig. 11. Buggy function
Accept of AssetTransfer . overhead. We are currently working on extending the templatesfor contract invariant inference to richer templates for inferringpostconditions for recursive procedures. Bug Analysis.
We report and analyze the five bugs thatV
ERI S OL found in the Azure Blockchain Workbench samplecontracts. These bugs can be categorized into two classes: (i)incorrect state transition, and (ii) incorrect initial state. Webriefly discuss these two classes of bugs. Incorrect state transition.
This class of bugs arises whenthe implementation of a function in the contract violates thestate transition stated by the policy. V
ERI S OL has found suchnon-conformance in the AssetTransfer and
PingPongGame contracts. For instance, let us consider
AssetTransfer as aconcrete example. In this contract, actions are guarded by themembership of msg.sender within one of the roles or instancerole variables (see Figure 11). V ERI S OL found the transition https://github.com/Azure-Samples/blockchain/tree/master/blockchain-workbench/application-and-smart-contract-samples/asset-transfer rom state BuyerAccepted to state
Accepted in the
Accept function had no matching transitions in the policy. Specif-ically, the policy allows a transition from
BuyerAccepted to SellerAccepted when invoking the function
Accept and msg.sender equals the instance role variable
InstanceOwner .However, the implementation of function
Accept transitionsto the state
Accepted instead of
SellerAccepted . Fromthe perspective of the bounded verifier, this is a fairly deepbug, as it requires at least 6 transactions to reach the state
BuyerAccepted from the initial state.
Incorrect initial state.
This class of bugs arises when the initialstate of a smart contract is not established as instructed by thecorresponding policy. We have found such non-conformancein
DigitalLocker and
BazaarItemListing . For instance,the policy of
DigitalLocker requires the initial state of thesmart contract to be
Requested , but the implementation endsup incorrectly setting the initial state to
DocumentReview . Inthe
BazaarItemListing benchmark, the developer fails toset the initial state of the contract despite the policy requiringit to be set to
ItemAvailable . B. Security Properties of PoA Governance Contract
In this section, we discuss our experience applying V
ERI S OL to PoA governance contracts. We first give some backgroundon PoA and then discuss experimental results. Background on PoA governance contracts.
In additionto application samples, Workbench also ships a core smartcontract that constitutes an integral part of the Workbenchsystem stack. PoA is an alternative to the popular Proof-of-Work (PoW) consensus protocol for permissioned blockchains,which consist of a set of nodes running the protocol andvalidating transactions to be appended to a block that will becommitted on the ledger [9].
Validators belong to differentorganizations, where each organization is represented by an administrator . The protocol for admin addition, removal, voting,and validator set management is implemented as the PoAgovernance contract. It implements the Parity Ethereum’s
ValidatorSet contract interface and is distributed on the AzureBlockchain github [8]. The smart contract consists of fivecomponent contracts (
ValidatorSet , SimpleValidatorSet , AdminValidatorSet , Admin , AdminSet ) totaling around 600lines of Solidity code. The correctness of the PoA governancecontract underpins the trust on Workbench as well the rest ofAzure Blockchain offering.The smart contract uses several features that make it achallenging benchmark for Solidity smart contract reasoning.We outline some of them here: • The contracts use multiple levels of inheritance since thetop-level contract
AdminValidatorSet derives from thecontract
SimpleValidatorSet which in turn derives from
ValidatorSet interface. • It uses sophisticated access control using Solidity modifiers torestrict which users and contracts can alter states of differentcontracts. • The contracts maintain deeply nested mappings and arraysto store the set of validators for different admins. • The contracts use nested loops and procedures to iterateover the arrays and use arithmetic operations to reason aboutmajority voting.
Properties.
We examined three key properties of the PoAcontract:
P1: At least one admin : The network starts out with a singleadmin at bootstrapping, but the set of admins shouldnever become empty. If this property is violated, theentire network will enter into a frozen state where anysubsequent transaction will revert.
P2: Correctness of AdminSet : The
AdminSet is a contractthat exposes a set interface to perform constant timeoperations such as lookup. Since Solidity does notpermit enumerating all the keys in a mapping, the setis implemented as a combination of an array of members addressList and a Boolean mapping inSet to mapthe members to true. The property checks the couplingbetween these two data structures — (i) addressList has no repeated elements, (ii) inSet [ a ] is true iff thereis an index j such that addressList [ j ] == a . P3: Element removal : Deleting an element from an arrayis a commonly used operation for PoA contracts. PoAcorrectness relies on invoking this procedure only for anelement that is a member of the array.
Bugs found.
To check the three correctness properties (P1),(P2), (P3) described above, we first annotated the PoAgovernance contracts with appropriate assertions and thenanalyzed them using V
ERI S OL . In addition to uncovering apreviously known violation of the “at least one admin” property,V ERI S OL identified a few other bugs that have been confirmedand fixed by the developers. In particular, V ERI S OL found abug that results in the violation of property (P3): When anadmin issues a transaction to remove a validator x from itslist of validators, a call to event InitiateChange will beemitted after removing x (using deleteArrayElement ). Topersist the change, another call to finalizeChange is needed.However, the implementation actually allows two consecutivecalls InitiateChange without a call to finalizeChange .As a result, this bug can result in the PoA contract to fail toremove validators that are initiated to be removed.In addition to the manually-added assertions that check thethree afore-mentioned properties, the PoA governance contractscontain additional assertions that were added by the originaldevelopers. Interestingly, V
ERI S OL also found violations ofthese original assertions. However, these assertion failureswere due to developers mistakenly using assert instead of require . Although both require and assert failures revertan execution, Solidity recommends using assert only forviolations of internal invariants that are not expected to failat runtime. V ERI S OL found five such instances of assertionmisuse. nbounded verification. Unlike the semantic conformancechecking problem for client contracts, verifying properties (P1),(P2), (P3) of the PoA contracts requires non-trivial quantifiedinvariants and reasoning about deeply nested arrays. Thus, weattempted semi-automated verification of the PoA contracts bymanually coming up with contract/loop invariants and methodpre- and post-conditions. In addition, inductive proof of some ofthe properties also requires introducing ghost variables that arenot present in the original code. Fully automated verification ofthese properties in PoA governance contracts is an ambitious,yet exciting area for future work.VI. R
ELATED W ORK
In this section, we discuss prior work on ensuring thesafety and security of smart contracts. Existing techniques forsmart contract security can be roughly categorized into variouscategories, including static approaches for finding vulnerablepatterns, formal verification techniques, and runtime checking.In addition, there has been work on formalizing the semanticsof EVM in a formal language such as the K Framework [20].Finally, there are several works that discuss a survey andtaxonomy of vulnerabilities in smart contracts [13], [26], [28].
Static analysis.
The set of static analysis tools are based ona choice of data-flow analysis or symbolic execution to findvariants of known vulnerable patterns. Such patterns include theuse of reentrancy, transaction ordering dependencies, sendingether to unconstrained addresses that may lead to lost ether, useof block time-stamps, mishandled exceptions, calling suicide on an unconstrained address, etc. Tools based on symbolicexecution include Oyente [26], MAIAN [28], Manticore [10],and Mythril++ [11]. On the other hand, several data-flowbased tools also exist such as Securify [29] and Slither [12].Finally, the MadMax tool [18] performs static analysis tofind vulnerabilities related to out-of-gas exceptions. Thesetools neither check semantic conformance nor verify assertions.Instead, they mostly find instances of known vulnerable patternsand do not provide any soundness or completeness guarantees.On the other hand, V
ERI S OL does not reason about gasconsumption since it analyzes Solidity code, and it also needsthe vulnerabilities to be expressed as formal specifications. Formal verification.
F* [15] and Zeus [21] use formal verifi-cation for checking correctness of smart contracts. These ap-proaches translate Solidity to the formal verification languagesof F* and LLVM respectively and then apply F*-based verifiersand constrained horn clause solvers to check the correctnessof the translated program. Although the F* based approachis fairly expressive, the tool only covers a small subset ofSolidity without loops and requires substantial user guidanceto discharge proofs of user-specified assertions. The designof Zeus shares similarities with V
ERI S OL in that it translatesSolidity to an intermediate language and uses SMT basedsolvers to discharge the verification problem. However, thereare several differences in the capabilities of the two works.First, one of the key contributions of this paper is the semanticconformance checking problem for smart contracts, which Zeus does not address. Second, unlike our formal treatmentof the translation to Boogie, Zeus only provides an informaldescription of the translation to LLVM and does not define thememory model in the presence of nested arrays and mappings.Unfortunately, we were unable to obtain a copy of Zeus totry on our examples, making it difficult for us to perform anexperimental comparison for discharging assertions in Soliditycode. Other approaches.
In addition to static analyzers and formalverification tools, there are also other approaches that enforcesafe reentrancy patterns at runtime by borrowing ideas fromlinearizability [19]. Another work that is related to this paperis FSolidM [27], which provides an approach to specify smartcontracts using a finite state machine with actions written inSolidity. Although there is a similarity in their state machinemodel with our Workbench policies, they do not consideraccess control, and the actions do not have nested procedurecalls or loops. Finally, the FSolidM tool does not provide anystatic or dynamic verification support.VII. C
ONCLUSION
In this work, we described one of the first uses of automatedformal verification for smart contracts in an industrial setting.We provided formal semantics to the Workbench applicationconfiguration, and performed automatic program instrumenta-tion to enforce such specifications. We described a new formalverification tool V
ERI S OL using the Boogie tool chain, andillustrated its application towards non-trivial smart contractverification and bug-finding. For the immediate future, we areworking on adding more features of the Solidity language thatare used in common enterprise workflows and exploring moresophisticated inference for inferring more complex contractinvariants. R EFERENCES[1] Explaining the dao exploit for beginners in solid-ity. https://medium.com/\spacefactor\@m{}MyPaoG/explaining-the-dao-exploit-for-beginners-in-solidity-80ee84f0d470,2016.[2] Solidity: Function modifiers. https://solidity.readthedocs.io/en/v0.4.24/structure-of-a-contract.html
Principles of Security andTrust - 6th International Conference, POST , Uppsala, Sweden, April22-29, 2017, Proceedings , pages 164–186, 2017.[14] Michael Barnett, Bor-Yuh Evan Chang, Robert DeLine, Bart Jacobs, andK. Rustan M. Leino. Boogie: A modular reusable verifier for object-oriented programs. In
Formal Methods for Components and Objects, 4thInternational Symposium, FMCO 2005, Amsterdam, The Netherlands,November 1-4, 2005, Revised Lectures , pages 364–387, 2005.[15] Karthikeyan Bhargavan, Antoine Delignat-Lavaud, Cédric Fournet, AnithaGollamudi, Georges Gonthier, Nadim Kobeissi, Natalia Kulatova, AseemRastogi, Thomas Sibut-Pinote, Nikhil Swamy, and Santiago ZanellaBéguelin. Formal verification of smart contracts: Short paper. In
Proceedings of the 2016 ACM Workshop on Programming Languagesand Analysis for Security, PLAS@CCS 2016, Vienna, Austria, October24, 2016
International Symposium of Formal MethodsEurope , pages 500–517. Springer, 2001.[18] Neville Grech, Michael Kong, Anton Jurisevic, Lexi Brent, BernhardScholz, and Yannis Smaragdakis. Madmax: surviving out-of-gasconditions in ethereum smart contracts.
PACMPL , 2(OOPSLA):116:1–116:27, 2018.[19] Shelly Grossman, Ittai Abraham, Guy Golan-Gueta, Yan Michalevsky,Noam Rinetzky, Mooly Sagiv, and Yoni Zohar. Online detection ofeffectively callback free objects with applications to smart contracts.
PACMPL , 2(POPL):48:1–48:28, 2018.[20] Everett Hildenbrandt, Manasvi Saxena, Nishant Rodrigues, Xiaoran Zhu,Philip Daian, Dwight Guth, Brandon M. Moore, Daejun Park, Yi Zhang,Andrei Stefanescu, and Grigore Rosu. KEVM: A complete formalsemantics of the ethereum virtual machine. In , pages 204–217, 2018.[21] Sukrit Kalra, Seep Goel, Mohan Dhawan, and Subodh Sharma. ZEUS:analyzing safety of smart contracts. In , 2018.[22] Shuvendu Lahiri and Shaz Qadeer. Complexity and algorithms formonomial and clausal predicate abstraction. In
International Conferenceon Automated Deduction (CADE ’09) . Springer Verlag, March 2009.[23] Shuvendu K. Lahiri, Shaz Qadeer, Juan P. Galeotti, Jan W. Voung, andThomas Wies. Intra-module inference. In
Computer Aided Verification,21st International Conference, CAV 2009, Grenoble, France, June 26 -July 2, 2009. Proceedings , pages 493–508, 2009.[24] Akash Lal, Shaz Qadeer, and Shuvendu K. Lahiri. A solver forreachability modulo theories. In
Computer Aided Verification - 24thInternational Conference, CAV 2012, Berkeley, CA, USA, July 7-13, 2012Proceedings , volume 7358, pages 427–443. Springer, 2012.[25] K. Rustan M. Leino. Efficient weakest preconditions.
Inf. Process. Lett. ,93(6):281–288, 2005.[26] Loi Luu, Duc-Hiep Chu, Hrishi Olickel, Prateek Saxena, and AquinasHobor. Making smart contracts smarter. In
Proceedings of the 2016ACM SIGSAC Conference on Computer and Communications Security,Vienna, Austria, October 24-28, 2016 , pages 254–269, 2016.[27] Anastasia Mavridou and Aron Laszka. Tool demonstration: Fsolidm fordesigning secure ethereum smart contracts. In
Principles of Securityand Trust - 7th International Conference, POST 2018, Held as Part ofthe European Joint Conferences on Theory and Practice of Software,ETAPS 2018, Thessaloniki, Greece, April 14-20, 2018, Proceedings ,pages 270–277, 2018.[28] Ivica Nikolic, Aashish Kolluri, Ilya Sergey, Prateek Saxena, and AquinasHobor. Finding the greedy, prodigal, and suicidal contracts at scale.In
Proceedings of the 34th Annual Computer Security ApplicationsConference, ACSAC 2018, San Juan, PR, USA, December 03-07, 2018 ,pages 653–663, 2018.[29] Petar Tsankov, Andrei Marian Dan, Dana Drachsler-Cohen, ArthurGervais, Florian Bünzli, and Martin T. Vechev. Securify: Practicalsecurity analysis of smart contracts. In