arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

39
Noname manuscript No. (will be inserted by the editor) HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types Yuki Nishida · Hiromasa Saito · Ran Chen · Akira Kawata · Jun Furuse · Kohei Suenaga · Atsushi Igarashi the date of receipt and acceptance should be inserted later Abstract A smart contract is a program executed on a blockchain, based on which many cryptocurrencies are implemented, and is being used for automating transactions. Due to the large amount of money that smart contracts deal with, there is a surging demand for a method that can statically and formally verify them. This article describes our type-based static verification tool HELMHOLTZ for Michelson, which is a statically typed stack-based language for writing smart contracts that are executed on the blockchain platform Tezos. HELMHOLTZ is designed on top of our extension of Michelson’s type system with refinement types. HELMHOLTZ takes a Michelson program annotated with a user-defined specification written in the form of a refinement type as input; it then typechecks the program against the specification based on the refinement type system, discharging the generated verification conditions with the SMT solver Z3. We briefly introduce our refinement type system for the core calculus Mini-Michelson of Michelson, which incorporates the characteristic features such as compound datatypes (e.g., lists and pairs), higher-order functions, and invocation of another contract. HELMHOLTZ successfully verifies several practical Michelson programs, including one that transfers money to an account and that checks a digital signature. Keywords Smart contract · Blockchain · Formal verification · Tools 1 Introduction A blockchain is a data structure to implement a distributed ledger in a trustless yet secure way. The idea of blockchains is initially devised for the Bitcoin cryptocur- Yuki Nishida (B) · Hiromasa Saito · Ran Chen · Akira Kawata · Kohei Suenaga · Atsushi Igarashi Kyoto University, Kyoto, Japan E-mail: {nishida,hsaito,aran,akira,ksuenaga,igarashi}@fos.kuis.kyoto-u.ac.jp Jun Furuse DaiLambda, Inc., Kyoto, Japan E-mail: [email protected] arXiv:2108.12971v2 [cs.PL] 10 Sep 2021

Transcript of arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

Page 1: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

Noname manuscript No.(will be inserted by the editor)

HELMHOLTZ: A Verifier for Tezos Smart Contracts Basedon Refinement Types

Yuki Nishida · Hiromasa Saito · Ran Chen ·Akira Kawata · Jun Furuse · Kohei Suenaga ·Atsushi Igarashi

the date of receipt and acceptance should be inserted later

Abstract A smart contract is a program executed on a blockchain, based on whichmany cryptocurrencies are implemented, and is being used for automating transactions.Due to the large amount of money that smart contracts deal with, there is a surgingdemand for a method that can statically and formally verify them.

This article describes our type-based static verification tool HELMHOLTZ forMichelson, which is a statically typed stack-based language for writing smart contractsthat are executed on the blockchain platform Tezos. HELMHOLTZ is designed on top ofour extension of Michelson’s type system with refinement types. HELMHOLTZ takes aMichelson program annotated with a user-defined specification written in the form ofa refinement type as input; it then typechecks the program against the specificationbased on the refinement type system, discharging the generated verification conditionswith the SMT solver Z3. We briefly introduce our refinement type system for thecore calculus Mini-Michelson of Michelson, which incorporates the characteristicfeatures such as compound datatypes (e.g., lists and pairs), higher-order functions, andinvocation of another contract. HELMHOLTZ successfully verifies several practicalMichelson programs, including one that transfers money to an account and that checksa digital signature.

Keywords Smart contract · Blockchain · Formal verification · Tools

1 Introduction

A blockchain is a data structure to implement a distributed ledger in a trustless yetsecure way. The idea of blockchains is initially devised for the Bitcoin cryptocur-

Yuki Nishida(B) · Hiromasa Saito · Ran Chen · Akira Kawata · Kohei Suenaga · Atsushi IgarashiKyoto University, Kyoto, JapanE-mail: {nishida,hsaito,aran,akira,ksuenaga,igarashi}@fos.kuis.kyoto-u.ac.jp

Jun FuruseDaiLambda, Inc., Kyoto, JapanE-mail: [email protected]

arX

iv:2

108.

1297

1v2

[cs

.PL

] 1

0 Se

p 20

21

Page 2: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

2 Yuki Nishida et al.

rency [12] platform. Many cryptocurrencies are implemented using blockchains, inwhich value equivalent to a significant amount of money is exchanged.

Recently, many cryptocurrency platforms allow programs to be executed on ablockchain. Such programs are called smart contracts [19] (or, simply contracts inthis article) since they work as a device to enable automated execution of a contract. Ingeneral, a smart contract is a program Pa associated with an account a on a blockchain.When the account a receives money from another account b with a parameter v, thecomputation defined in Pa is conducted, during which the state of the account a (e.g.,the balance of the account and values that are stored by the previous invocations of Pa)which is recorded on the blockchain may be updated. The contract Pa may executemoney transactions to another account (say c), which results in invocations of othercontracts (say Pc) during or after the computation; therefore, contract invocations maybe chained.

Although smart contracts’ original motivation was handling simple transactions(e.g., money transfer) among the accounts on a blockchain, recent contracts are beingused for more complicated purposes (e.g., establishing a fund involving multipleaccounts). Following this trend, the languages for writing smart contracts also evolvefrom those that allow a contract to execute relatively simple transactions (e.g., Scriptfor Bitcoin) to those that allow a program that is as complex as one written in standardprogramming languages (e.g., EVM for Ethereum and Michelson [13] for Tezos [4]).

Due to a large amount of money they deal with, verification of smart contracts isimperative. Static verification is especially needed since a smart contract cannot befixed once deployed on a blockchain. Attack on a vulnerable contract indeed happened.For example, the DAO attack, in which the vulnerability of a fundraising contract wasexploited, resulted in the loss of cryptocurrency equivalent to approximately 150MUSD [18].

In this article, we describe our type-based static verifier HELMHOLTZ1 for smartcontracts written in Michelson. The Michelson language is a statically and simplytyped stack-based language equipped with rich data types (e.g., lists, maps, andhigher-order functions) and primitives to manipulate them. Although several high-level languages that compile to Michelson are being developed, Michelson is mostwidely used to write a smart contract for Tezos as of writing.

A Michelson program expresses the above computation in a purely functional style,in which the Michelson program corresponding to Pa is defined as a function. Thefunction takes a pair of the parameter v and a value s that represents the current state ofthe account (called storage) and returns a pair of a list of operations and the updatedstorage s′. Here, an operation is a Michelson value that expresses the computation(e.g., transferring money to an account and invoking the contract associated with theaccount) that is to be conducted after the current computation (i.e., Pa) terminates.After the computation specified by Pa finishes with a pair of a storage value and anoperation list, a blockchain system invokes the computation specified in the operationlist. This purely functional style admits static verification methods for Michelsonprograms similar to those for standard functional languages.

1 Hermann von Helmholtz (1821–1894), a German physicist and physician, was a doctoral advisor ofAlbert A. Michelson (1852–1931), whom the Michelson language is apparently named after.

Page 3: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 3

As the theoretical foundation of HELMHOLTZ, we design a refinement type systemfor Michelson as an extension of the original simple type system. In contrast tostandard refinement types that refine the types of values, our type system refines thetype of stacks.

We show that our tool can verify several practical smart contracts. In addition tothe contracts we wrote ourselves, we apply our tool to the sample Michelson programsused in Mi-cho-coq [2], a formalization of Michelson in Coq proof assistant [21].These contracts consist of practical contracts such as one that checks a digital signatureand one that transfers money.

We note that HELMHOLTZ currently supports approximately 80% of the wholeinstructions of the Michelson language. Another limitation of the current HELMHOLTZis that it can verify only a single contract, although one often uses multiple contractsfor an application, in which a contract may call another by a money transfer operation,and their behavior as a whole is of interest. We are currently extending HELMHOLTZso that it can deal with more programs.

Our contribution is summarized as follows: (1) Definition of the core calculusMini-Michelson and its refinement type system; (2) Automated verification toolHELMHOLTZ for Michelson contracts implemented based on the type system of Mini-Michelson; the interface to the implementation can be found at https://www.fos.kuis.kyoto-u.ac.jp/trylang/Helmholtz; and (3) Evaluation of HELMHOLTZwith various Michelson contracts, including practical ones. A preliminary version ofthis article was presented at International Conference on Tools and Algorithms forthe Construction and Analysis of Systems (TACAS) in 2021. We have given detailedproofs of properties of Mini-Michelson and a more detailed description about theverifier implementation, in addition to revision of the text.

The rest of this article is organized as follows. Before introducing the technicaldetails, we present an overview of the verifier HELMHOLTZ in Section 2 using asimple example of a Michelson contract. Section 3 introduces the core calculus Mini-Michelson with its refinement type system and states soundness of the refinementtype system. (Detailed proofs are deferred to Appendix A.) We also discuss a fewextensions implemented in the verifier. Section 4 describes the verifier HELMHOLTZ,a case study, and experimental results. After discussing related work in Section 5, weconclude in Section 6.

2 Overview of HELMHOLTZ and Michelson

We give an overview of our tool HELMHOLTZ in this section before presenting itstechnical details. We also explain Michelson by example (Section 2.2) and user-writtenannotation added to a Michelson program for verification purposes (Section 2.3).

2.1 HELMHOLTZ

As input, HELMHOLTZ takes a Michelson program annotated with (1) its specificationexpressed in a refinement type and (2) additional user annotations such as loop

Page 4: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

4 Yuki Nishida et al.

1 parameter unit;2 storage unit;3 << ContractAnnot { (param , st) | True } ->4 { (ops , st ’) | amount = 0 && ops = [] ||5 amount <> 0 && (match contract_opt source with6 | Some c -> ops = [Transfer Unit amount c]7 | None -> False) }8 & { _ | False } >>9 code /* (param ,st) */

10 { CDR; /* st */11 NIL operation; /* [] . st */12 AMOUNT; /* amount . [] . st */13 PUSH mutez 0; /* 0 . amount . [] . st */14 IFCMPEQ15 {} /* [] . st (amount ≤ 0) */16 { /* [] . st (amount > 0) */17 SOURCE; /* src . [] . st */18 CONTRACT unit; /* Some (Contract src) . [] . st */19 ASSERT_SOME; /* (Contract src) . [] . st */20 AMOUNT; UNIT;21 /* Unit . amount . (Contract src) . [] . st */22 TRANSFER_TOKENS;23 /* (Transfer Unit amount (Contract src)) . [] . st */24 CONS25 /* [Transfer Unit amount (Contract src)] . st */26 };27 /* ops . st, where ops is the top element at the end of each branch, namely,28 [Transfer Unit amount (Contract src)] if amount> 0; or [] otherwise */29 PAIR } /* (ops , st) */

Fig. 1 boomerang.tz. The comment inside /* */ describes the stack at the program point.

invariants. It typechecks the annotated program against the specification using ourrefinement type system; the verification conditions generated during the typecheckingis discharged by the SMT solver Z3 [11]. If the code successfully typechecks, then theprogram is guaranteed to satisfy the specification.

HELMHOLTZ is implemented as a subcommand of tezos-client, the clientprogram of the Tezos blockchain. For example, to verify boomerang.tz in Figure 1,we run tezos-client refinement boomerang.tz. If the verification succeeds,the command outputs VERIFIED to the terminal screen (with a few log messages);otherwise, it outputs UNVERIFIED.

2.2 An Example Contract in Michelson

Figure 1 shows an example of a Michelson program called boomerang. A Michelsonprogram is associated with an account on the Tezos blockchain; the program is invokedby transferring money to this account. This artificial program in Figure 1, when it isinvoked, is supposed to transfer the received money back to the account that initiatedthe transaction.

Page 5: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 5

A Michelson program starts with type declarations of its parameter, whose valueis given by contract invocation, and storage, which is the state that the contractaccount stores. Lines 1–2 declare that the types of both are unit, the type inhabited bythe only value Unit. Lines 3–8 surrounded by << and >> are a user-written annotationused by HELMHOLTZ for verification; we will explain this annotation later. The codesection in Lines 10–29 is the body of this program.

Let us take a look at the code section of the program. In the following explana-tion of each instruction, we describe the state of the stack after each instruction ascomments; stack elements are delimited by ..

– Execution of a Michelson program starts with a stack with one value, which is apair (param, st) of a parameter param and a storage value storage.

– CDR pops the pair at the top of the stack and pushes the second value of the poppedpair; thus, after executing the instruction, the stack contains the single value st.

– NIL pushes the empty list [] to the stack; the instruction is accompanied by thetype operation of the list elements for typechecking purposes.

– AMOUNT pushes the nonnegative amount of the money sent to the account to whichthis program is associated.

– PUSH mutez 0 pushes the value 0. The type mutez represents a unit of moneyused in Tezos.

– IFCMPEQ b1 b2, if the state of the stack before executing the instruction is v1. v2 . tl, (1) pops v1 and v2 and (2) executes the then-branch b1 (resp., theelse-branch b2) if v2 = v1 (resp., v2 6= v1). In boomerang, this instruction doesnothing if amount = 0; otherwise, the instructions in the else-branch are executed.

– SOURCE at the beginning of the else-branch pushes the address src of the sourceaccount, which initiated the chain of contract invocations that the current contractbelongs to, resulting in the stack src . [] . st.

– CONTRACT T pops an address addr from the stack and typechecks whether thecontract associated with addr takes an argument of type T . If the typecheckingsucceeds, then Some (Contract addr) is pushed; otherwise, None is pushed.The constructor Contract creates an object that represents a typechecked contractat the given address. In Tezos, the source account is always a contract that takes thevalue Unit as a parameter; thus, Some (Contract src) will always be pushedonto the stack.

– ASSERT_SOME pops a value v from the stack and pushes v’ if v is Some v’;otherwise, it raises an exception.

– UNIT pushes the unit value Unit to the stack.– TRANSFER_TOKENS, if the stack is of the shape varg . vamt . vcontr . tl, popsvarg, vamt, and vcontr from the stack and pushes (Transfer varg vamtvcontr) onto tl. The value Transfer varg vamt vcontr is an operationobject expressing that money (of amount vamt) shall be sent to the account vcontrwith the argument varg after this program finishes without raising an exception.Therefore, the program associated with vcontr is invoked after this programfinishes. Otherwise, an operation object is an opaque tuple and no instruction canextract its elements.

Page 6: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

6 Yuki Nishida et al.

– CONS with the stack v1 . v2 . tl pops v1 and v2, and pushes a cons list v1::v2onto the stack. (We use the list notation in OCaml here.)

– After executing one of the branches associated with IFCMPEQ in this program,the shape of the stack should be ops . storage, where ops is [] if amount = 0or [Transfer varg vamt vcontr] if amount > 0. The instruction PAIR popsops and storage, and pushes (ops,storage).

A Michelson program is supposed to finish its execution with a singleton stack whoseunique element is a pair of (1) a list of operations to be executed after the currentexecution of the contract finishes and (2) the new value for the storage.

Michelson is a statically typed language. Each instruction is associated with atyping rule that specifies the shapes of stacks before and after it by a sequence ofsimple types such as int and int list. For example, CONS requires the type of topelement to be T and that of the second to be T list (for any T ); it ensures the topelement after it has type T list.

Other notable features of Michelson include first-class functions, hashing, instruc-tions related to cryptography such as signature verification, and manipulation of ablockchain using operations.

2.3 Specification

A user can specify the behavior of a program by a ContractAnnot annotation,which is a part of the augmented syntax of our verification tool. A ContractAnnotannotation gives a specification of a Michelson program by the following notationinspired by the refinement types: {(param,st) | pre} -> {(ops,st’) | post}& {exc | abpost} where pre, post, and abpost are predicates. This specificationreads as follows: if this program is invoked with a parameter param and storage stthat satisfies the property pre, then (1) if the execution of this program succeeds,then it returns a list of operations ops and new storage storage’ that satisfy theproperty post; (2) if this program raises an exception with value exc, then excsatisfies abpost. The specification language, which is ML-like, is expressive enoughto cover the specifications for practical contracts, including the ones we used in theexperiments in Section 4.5. In the predicates, one can use several keywords such asamount for the amount of the money sent to this program when it is invoked andsource for the source account’s address.

The ContractAnnot annotation in Figure 1 (Lines 3–8) formalizes this program’sspecification as follows. This program can take any parameter and storage (Line 3).Successful execution of this program results in a pair (ops,st’) that satisfies thecondition in Lines 4–7 that expresses (1) if amount = 0, then ops is empty, that is,no operation will be issued; (2) if amount> 0, then ops is a list of a single elementTransfer Unit amount c, where c is bound for Contract source2, which ex-presses transfer of money of the amount amount to the account at source with the unit

2 It is one axiom of our domain specific theory that contract_opt source always returnSome (Contract source).

Page 7: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 7

V ::= i | a | Transfer(V, i,a) | (V1,V2) | [] | V1 :: V2 | 〈IS〉T ::= int | address | operation | T1×T2 | T list | T1→ T2

IS ::= {} | {I; IS}I ::= IS | DIP IS | DROP | DUP | SWAP | PUSHT V | NOT | ADD | IF IS1 IS2 | LOOP IS |

PAIR | CAR | CDR | NILT | CONS | IF_CONS IS1 IS2 | ITER IS | LAMBDAT1 T2 IS |EXEC | TRANSFER_TOKENST

S ::= ‡ | V .S

T ::= ‡ | T . T

ϒ ::= ‡ | x:T .ϒ

Fig. 2 Syntax of Mini-Michelson.

argument.3 In the specification language, source and amount are keywords that standfor the source account and the amount of money sent to this program, respectively.The part & { _ | False } expresses that this program does not raise an exception.This specification correctly formalizes the intended behavior of this program.

3 Refinement Type System for Mini-Michelson

In this section, we formalize Mini-Michelson, a core subset of Michelson with itssyntax, operational semantics, and refinement type system. We omit many featuresfrom the full language in favor of conciseness but includes language constructs—suchas higher-order functions and iterations—that make verification difficult.

3.1 Syntax

Figure 2 shows the syntax of Mini-Michelson. Values, ranged over by V , consist ofintegers i; addresses a; operation objects Transfer(V, i,a) to invoke a contract at a bysending money of amount i and an argument V ; pairs (V1,V2) of values; the empty list[]; cons V1 :: V2; and code 〈IS〉 of first-class functions.4 Unlike Michelson, which hasprimitive Boolean literals True and False, we use integers as a substitute for Booleanvalues so that 0 means False and the others mean True. As we have mentioned, thereis no instruction to extract elements from an operation object but the elements can bereferenced in refinement types to state what kind of operation object is constructed bya smart contract. Simple types, ranged over by T , consist of base types (int, address,and operation, which are self-explanatory), pair types T1×T2, list types T list, andfunction types T1→ T2. Instruction sequences, ranged over by IS, are a sequence ofinstructions, ranged over by I, enclosed by curly braces. A Mini-Michelson programis an instruction sequence.

Instructions include those for operand stack manipulation (to DROP, DUPlicate,SWAP, and PUSH values); NOT and ADD for manipulating integers; PAIR, CAR, and CDR

3 As we mentioned in Section 1, HELMHOLTZ can currently verify the behavior of a single contract,although there will be an invocation of the contract associated with source after the termination ofboomerang. An operation is treated as an opaque data structure, from which one cannot extract values.

4 Closures are not needed because functions in Michelson can access only arguments.

Page 8: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

8 Yuki Nishida et al.

for pairs; NIL and CONS for constructing lists; LAMBDA for a first-class function; EXECfor calling a function; and TRANSFER_TOKENS to create an operation. Instructions forcontrol structures are IF and IF_CONS, which are for branching on integers (whetherthe stack top is True or not) and lists (whether the stack top is a cons or not), respec-tively, and LOOP and ITER, which are for iteration on integers and lists, respectively.LAMBDA pushes a function (described by its operand IS) onto the stack and EXEC calls afunction. Perhaps unfamiliar is DIP IS, which pops and saves the stack top somewhereelse, execute IS, and then push back the saved value.

We also use a few kinds of stacks in the following definitions: operand stacks,ranged over by S, type stacks, ranged over by T , and type binding stacks, ranged overby ϒ . The empty stack is denoted by ‡ and push is by .. We often omit the empty stackand write, for example, V1 .V2 for V1 .V2 .‡. Intuitively, T1 . .. .Tn and x1:T1 . .. .xn:Tn describe stacks V1 . .. .Vn where each value Vi is of type Ti. We will use variablesto name stack elements in the refinement type system.

We summarize main differences from Michelson proper:

– Michelson has the notion of type attributes, which classify types, according towhich generic operations such as PUSH can be applied. For example, values ofpushable types can be put on the stack by PUSH. Since type operation is notpushable, an instruction such as PUSHoperationTransfer(V, i,a) is not validin Michelson—all operations have to be created by designated instructions. Weignore type attributes for simplicity here, but the implementation of HELMHOLTZ,which calls the typechecker of Michelson, does not.

– As we saw in Section 2, an operation is created from an address in two steps via acontract value. Since we model only one kind of operations, i.e., Transfer(V, i,a),we simplify the process to let instruction TRANSFER_TOKENS directly creates anoperation from an address in one step. We also omit the typecheck of the contractassociated with an address.

– In Michelson, each execution of a smart contract is assigned a gas to control howlong the contract can run to prevent contracts from running too long.

– We do not formally model exceptions for simplicity and, thus, the refinement typesystem do not (have to) capture exceptional behavior. Our verifier, however, doeshandle exceptions; we will informally discuss how we extend the type system withexceptions in Section 3.6.

3.2 Operational Semantics

Figure 3 defines the operational semantics of Mini-Michelson. A judgment of theform S ` I ⇓ S′ (or S ` IS ⇓ S′, resp.) means that evaluating the instruction I (or theinstruction sequence IS, resp.) under the stack S results in the stack S′. Although thedefining rules are straightforward, we will make a few remarks about them.

The rule (E-DIP) means that DIP IS pops and saves the stack top somewhere else,execute IS, and then push back the saved value, as explained above. This instructionimplicitly gives Mini-Michelson (and Michelson) a secondary stack. (E-PUSH) meansthat PUSHT V does not check if the pushed value is well formed at run time: the checkis the job of the simple type system, discussed soon.

Page 9: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 9

Evaluation for Instruction Sequence S ` IS ⇓ S

(E-NOP)S ` {} ⇓ S

S ` I ⇓ S′ S′ ` IS ⇓ S′′(E-SEQ)

S ` {I; IS} ⇓ S′′

Evaluation for Instruction S ` I ⇓ S

S ` IS ⇓ S′(E-DIP)

V .S ` DIP IS ⇓V .S′(E-DROP)

V .S ` DROP ⇓ S(E-DUP)

V .S ` DUP ⇓V .V .S

(E-SWAP)V1 .V2 .S ` SWAP ⇓V2 .V1 .S

(E-PUSH)S ` PUSHT V ⇓V .S

(i 6= 0)(E-NOTT)

i.S ` NOT ⇓ 0.S(E-NOTF)

0.S ` NOT ⇓ 1.S(i1 + i2 = i3)

(E-ADD)i1 . i2 .S ` ADD ⇓ i3 .S

(E-PAIR)V1 .V2 .S ` PAIR ⇓ (V1,V2).S

(E-CAR)(V1,V2).S ` CAR ⇓V1 .S

(E-CDR)(V1,V2).S ` CDR ⇓V2 .S

(E-NIL)S ` NILT ⇓ [].S

(E-CONS)V1 .V2 .S ` CONS ⇓V1 :: V2 .S

(i 6= 0) S ` IS1 ⇓ S′(E-IFT)

i.S ` IF IS1 IS2 ⇓ S′

S ` IS2 ⇓ S′(E-IFF)

0.S ` IF IS1 IS2 ⇓ S′(i 6= 0) S ` IS ⇓ S′ S′ ` LOOP IS ⇓ S′′

(E-LOOPT)i.S ` LOOP IS ⇓ S′′

(E-LOOPF)0.S ` LOOP IS ⇓ S

V1 .V2 .S ` IS1 ⇓ S′(E-IFCONST)

V1 :: V2 .S ` IF_CONS IS1 IS2 ⇓ S′

S ` IS2 ⇓ S′(E-IFCONSF)

[].S ` IF_CONS IS1 IS2 ⇓ S′(E-ITERNIL)

[].S ` ITER IS ⇓ S

V1 .S ` IS ⇓ S′ V2 .S′ ` ITER IS ⇓ S′′(E-ITERCONS)

V1 :: V2 .S ` ITER IS ⇓ S′′(E-LAMBDA)

S ` LAMBDAT1 T2 IS ⇓ 〈IS〉.S

V .‡ ` IS ⇓V ′ .‡(E-EXEC)

V . 〈IS〉.S ` EXEC ⇓V ′ .S

(E-TRANSFERTOKENS)V . i.a.S ` TRANSFER_TOKENST ⇓ Transfer(V, i,a).S

Fig. 3 Operational Semantics of Mini-Michelson

The rules (E-IFT) and (E-IFF) define the behavior of the branching instructionIF IS1 IS2, which executes IS1 or IS2, depending on the top of the operand stack. Aswe have mentioned, nonzero integers mean True. Thus, (E-IFT) is used for the casein which IS1 is executed, and otherwise, (E-IFF) is used. There is another branchinginstruction IF_CONS IS1 IS2, which executes either instruction sequence depending

Page 10: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

10 Yuki Nishida et al.

on whether the list at the top of the stack is empty or not (cf. (E-IFCONST) and(E-IFCONSF)).

The rules (E-LOOPT) and (E-LOOPF) define the behavior of the looping in-struction LOOP IS. This instruction executes IS repeatedly until the top of the stackbecomes false. (E-LOOPT) means that, if the condition is True, IS is executed, andthen LOOP IS is executed again. (E-LOOPF) means that, if the condition is false, theloop is finished after dropping the stack top. A similar looping instruction is ITER IS,which iterates over a list (see (E-ITERNIL) and (E-ITERCONS)).

The rule (E-LAMBDA) means that LAMBDAT1 T2 IS pushes the instruction sequenceto the stack and (E-EXEC) means that EXEC pops the instruction sequence 〈IS〉 andthe stack top V , saves the rest of the stack S elsewhere, runs IS with V as the sole valuein the stack, pushes the result V ′ back to the restored stack S.

The rule (E-TRANSFERTOKENS) means that TRANSFER_TOKENST creates anoperation object and pushes onto the stack. (As we have discussed, we omit a run-timecheck to see if T is really the argument type of the contract that the address a stores.)

3.3 Simple Type System

Mini-Michelson (as well as Michelson) is equipped with a simple type system. Thetype judgment for instructions is written T ` I⇒ T ′, which means that instructionI transforms a stack of type T into another stack of type T ′. The type judgment forvalues is written V : T , which means that V is given simple type T . The typing rules,which are shown in Figure 4, are fairly straightforward. Note that these two judgmentforms depend on each other—see (RTV-FUN) and (T-PUSH).

3.4 Refinement Type System

Now we extend the simple type system to a refinement type system. In the refinementtype system, a simple stack type T1 . .. . Tn is augmented with a formula ϕ in anassertion language to describe the relationship among stack elements. More concretely,we introduce refinement stack types, ranged over by Φ , of the form {x1:T1 . ... . xn:Tn | ϕ(x1, ... ,xn)}, which denotes a stack V1 . .. .Vn such that Vn : T1, . . . , Vn : Tnand ϕ(V1, ... ,Vn) hold, and refine the type judgment form, accordingly. We startwith an assertion language, which is many-sorted first-order logic and proceed to therefinement type system.

3.4.1 Assertion Language

The assertion language is many-sorted first-order logic, where sorts are simple types.We show the syntax of terms, ranged over by t, and formulae, ranged over by ϕ ,in Figure 5. As usual, x is bound in ∃x:T.ϕ . Most of them are straightforward butthe formulae of the form call(t1, t2) = t3 deserves an explanation. It means that, ifinstruction sequence denoted by t1 is called with (a singleton stack that stores) a valuedenoted by t2 (and terminates), it yields the value denoted by t3. The term constructor

Page 11: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 11

Value Typing V : T

(RTV-INT)i : int

(RTV-ADDRESS)a : address

V : T (RTV-OPERATION)Transfer(V, i,a) : operation

V1 : T1 V2 : T2 (RTV-PAIR)(V1,V2) : T1×T2

(RTV-NIL)[] : T list

V1 : T V2 : T list(RTV-CONS)

V1 :: V2 : T list

T1 .‡ ` IS⇒ T2 .‡(RTV-FUN)

〈IS〉 : T1→ T2

Instruction Sequence Typing T ` IS⇒ T ′

(T-NOP)T ` {}⇒ T

T1 ` I⇒ T2 T2 ` IS⇒ T3 (T-SEQ)T1 ` {I; IS}⇒ T3

Instruction Typing T ` I⇒ T ′

T1 ` IS⇒ T2 (T-DIP)T . T1 ` DIP IS⇒ T . T2

(T-DROP)T . T ` DROP⇒ T

(T-DUP)T . T ` DUP⇒ T .T . T

(T-SWAP)T1 .T2 . T ` SWAP⇒ T2 .T1 . T

V : T (T-PUSH)T ` PUSHT V ⇒ T . T

(T-NOT)int. T ` NOT⇒ int. T

(T-ADD)int.int. T ` ADD⇒ int. T

(T-PAIR)T1 .T2 . T ` PAIR⇒ T1×T2 . T

(T-CAR)T1×T2 . T ` CAR⇒ T1 . T

(T-CDR)T1×T2 . T ` CDR⇒ T2 . T

(T-NIL)T ` NILT ⇒ T list. T

(T-CONS)T .T list. T ` CONS⇒ T list. T

T ` IS1⇒ T ′ T ` IS2⇒ T ′(T-IF)

int. T ` IF IS1 IS2⇒ T ′

T ` IS⇒ int. T (T-LOOP)int. T ` LOOP IS⇒ T

T .T list. T ` IS1⇒ T ′ T ` IS2⇒ T ′(T-IFCONS)

T list. T ` IF_CONS IS1 IS2⇒ T ′

T . T ` IS⇒ T (T-ITER)T list. T ` ITER IS⇒ T

T1 .‡ ` IS⇒ T2 .‡(T-LAMBDA)

T ` LAMBDAT1 T2 IS⇒ T1→ T2 . T

(T-EXEC)T1 .T1→ T2 . T ` EXEC⇒ T2 . T

(T-TRANSFERTOKENS)T .int.address. T ` TRANSFER_TOKENST ⇒ operation. T

Fig. 4 Simple typing

Page 12: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

12 Yuki Nishida et al.

t ::= x | V | Transfer(t1, t2, t3) | (t1, t2) | (t1 :: t2) | t1 + t2ϕ ::= > | t1 = t2 | call(t1, t2) = t3 | ¬ϕ | ϕ1 ∨ϕ2 | ∃x:T.ϕ

Γ ::= empty | Γ ,x:T

Fig. 5 Syntax of Assertion Language

Well-Sorted Terms Γ ` t : T

x:T ∈ Γ (WT-VAR)Γ ` x : T

V : T (WT-VAL)Γ `V : T

Γ ` t1 : int Γ ` t2 : int(WT-PLUS)

Γ ` t1 + t2 : int

Γ ` t1 : T Γ ` t2 : int Γ ` t3 : address(WT-TRANSACTION)

Γ ` Transfer(t1, t2, t3) : operation

Γ ` t1 : T1 Γ ` t2 : T2 (WT-PAIR)Γ ` (t1, t2) : T1×T2

Γ ` t1 : T Γ ` t2 : T list(WT-CONS)

Γ ` t1 :: t2 : T list

Well-Sorted Formulae Γ ` ϕ : ∗

(WF-TRUE)Γ ` > : ∗

Γ ` t1 : T Γ ` t2 : T(WF-EQUAL)

Γ ` t1 = t2 : ∗

Γ ` t1 : T1→ T2 Γ ` t2 : T1 Γ ` t3 : T2 (WF-CALL)Γ ` call(t1, t2) = t3 : ∗

Γ ` ϕ : ∗(WF-NOT)

Γ ` ¬ϕ : ∗

Γ ` ϕ1 : ∗ Γ ` ϕ2 : ∗(WF-OR)

Γ ` ϕ1 ∨ϕ2 : ∗Γ ,x:T ` ϕ : ∗

(WF-EXISTS)Γ ` ∃x:T.ϕ : ∗

Fig. 6 Well-Sorted Terms and Formulae.

Transfer(t1, t2, t3) allows us to refer to the elements in an operation object, whichis opaque. Conjunction ϕ1∧ϕ , implication ϕ1 =⇒ ϕ2, and universal quantification∀x:T.ϕ are defined as abbreviations as usual. We use several common abbreviationssuch as t1 6= t2 for ¬(t1 = t2), ∃x1:T1, .. ,xn:Tn.ϕ for ∃x1:T1. . . .∃xn:Tn.ϕ , etc. A typingenvironment, ranged over by Γ , is a sequence of type binding. We assume all variablesin Γ are distinct. We abuse a comma to concatenate typing environments, e.g., Γ1,Γ2.We also use a type binding stack ϒ as a typing environment, explicitly denoted by ϒ ,which is defined as ‡ = empty and x:T .ϒ = x:T,ϒ .

Well-sorted terms and formulae are defined by the judgments Γ ` t : T and Γ `ϕ : ∗, respectively. The former means that the term t is a well-sorted term of the sort Tunder the typing environment Γ and the latter that the formula ϕ is well-sorted underthe typing environment Γ , respectively. The derivation rules for each judgment, shownin Figure 6, are straightforward. Note that well-sortedness depends on the simple typesystem via (WT-VAL).

Let a value assignment σ be a mapping from variables to values. We write σ [x 7→V ] to denote the value assignment which maps x to V and otherwise is identical toσ . As we are interested in well-sorted formulae, we consider a value assignment thatrespects a typing environment, as follows.

Page 13: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 13

Definition 1. A value assignment σ is typed under a typing environment Γ , denotedby σ : Γ , iff σ(x) : T for every x:T ∈ Γ .

Now we define the semantics of the well-sorted terms and formulae in a standardmanner as follows.

Definition 2 (Semantics of terms). For a typed value assignment σ : Γ . The semantics[[t]]σ :Γ of term t under typed value assignment σ : Γ is defined as follows:

[[x]]σ :Γ = σ(x) [[V ]]σ :Γ =V

[[Transfer(t1, t2, t3)]]σ :Γ = Transfer([[t1]]σ :Γ , [[t2]]σ :Γ , [[t3]]σ :Γ )

[[(t1, t2)]]σ :Γ = ([[t1]]σ :Γ , [[t2]]σ :Γ )

[[t1 :: t2]]σ :Γ = [[t1]]σ :Γ :: [[t2]]σ :Γ

[[t1 + t2]]σ :Γ = [[t1]]σ :Γ +[[t2]]σ :Γ .

Definition 3 (Semantics of formulae). For a typed value assignment σ : Γ , a validwell-sorted formula ϕ under Γ is denoted by σ : Γ |= ϕ and defined as follows.

– σ : Γ |=>.– σ : Γ |= t1 = t2 iff [[t1]]σ :Γ = [[t2]]σ :Γ .– σ : Γ |= call(t1, t2) = t3 iff [[t1]]σ :Γ = 〈IS〉 and [[t2]]σ :Γ .‡ ` IS ⇓ [[t3]]σ :Γ .‡.– σ : Γ |= ¬ϕ iff σ : Γ 6|= ϕ .– σ : Γ |= ϕ1∨ϕ2 iff σ : Γ |= ϕ1 or σ : Γ |= ϕ2.– σ : Γ |= ∃x:T.ϕ iff σ [x 7→V ] : Γ ,x:T |= ϕ for some V .

We write Γ |= ϕ iff σ : Γ |= ϕ for any σ .

3.4.2 Typing Rules

The type system is defined by subtyping and typing: a subtyping judgment is of theform Γ `Φ1 <: Φ2, which means stack type Φ1 is a subtype of Φ2 under Γ , and a typejudgment for instructions (resp. instruction sequences) is of the form Γ `Φ1 I Φ2 (resp.Γ ` Φ1 IS Φ2), which means that if I (resp. IS) is executed under a stack satisfyingΦ1, the resulting stack (if terminates) satisfies Φ2. We often call Φ1 pre-condition andΦ2 post-condition, following the terminology of Hoare logic. Note that the scopesof variables declared in Γ include Φ1 and Φ2 but those bound in Φ1 do not includeΦ2. To express the relationship between the initial and final stacks, we use the typeenvironment Γ . In writing down concrete specifications, it is sometimes convenientto allow the scopes of variables bound in Φ1 to include Φ2, but we find that it wouldclutter the presentation of typing rules.

In our type system, subtyping is defined semantically as follows.

Definition 4 (Subtyping relation). A refinement stack type {ϒ | ϕ1} is called subtypeof a refinement stack type {ϒ | ϕ2} under a typing environment Γ , denoted by Γ `{ϒ | ϕ1}<: {ϒ | ϕ2}, iff Γ ,ϒ |= ϕ1 =⇒ ϕ2.

We show the typing rules in Figure 7 and Figure 8. It is easy to observe that thetype binding stack parts in the pre- and post-conditions follows the simple type system.We will focus on predicate parts below.

Page 14: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

14 Yuki Nishida et al.

Refinement Instruction Sequence Typing Γ `Φ1 IS Φ2

(RT-NOP)Γ `Φ {}Φ

Γ `Φ I Φ ′ Γ `Φ ′ IS Φ ′′ (RT-SEQ)Γ `Φ {I; IS}Φ ′′

Refinement Instruction Sequence Typing Γ `Φ1 IS Φ2

Γ ,x:T ` {ϒ | ϕ} IS {ϒ ′ | ϕ ′}(RT-DIP)

Γ ` {x:T .ϒ | ϕ} DIP IS {x:T .ϒ ′ | ϕ ′}(RT-DROP)

Γ ` {x:T .ϒ | ϕ} DROP {ϒ | ∃x:T.ϕ}

(x′ /∈ dom(Γ , x:T .ϒ ))(RT-DUP)

Γ ` {x:T .ϒ | ϕ} DUP {x′:T . x:T .ϒ | ϕ ∧ x′ = x}

(RT-SWAP)Γ ` {x1:T1 . x2:T2 .ϒ | ϕ} SWAP {x2:T2 . x1:T1 .ϒ | ϕ}

(x /∈ dom(Γ ,ϒ )) V : T(RT-PUSH)

Γ ` {ϒ | ϕ} PUSHT V {x:T .ϒ | ϕ ∧ x =V}

(x′ /∈ dom(Γ , x:int.ϒ ))(RT-NOT)

Γ ` {x:int.ϒ | ϕ} NOT {x′:int.ϒ | ∃x:int.ϕ ∧ (x 6= 0∧ x′ = 0∨ x = 0∧ x′ = 1)}

(x3 /∈ dom(Γ , x1:int. x2:int.ϒ ))(RT-ADD)

Γ ` {x1:int. x2:int.ϒ | ϕ} ADD {x3:int.ϒ | ∃x1:int,x2:int.ϕ ∧ x1 + x2 = x3}

(x3 /∈ dom(Γ , x1:T1 . x2:T2 .ϒ ))(RT-PAIR)

Γ ` {x1:T1 . x2:T2 .ϒ | ϕ} PAIR {x3:T1×T2 .ϒ | ∃x1:T1,x2:T2.ϕ ∧ (x1,x2) = x3}

(x1 6= x2) (x1 /∈ dom(Γ , x:T1×T2 .ϒ )) (x2 /∈ dom(Γ , x:T1×T2 .ϒ ))(RT-CAR)

Γ ` {x:T1×T2 .ϒ | ϕ} CAR {x1:T1 .ϒ | ∃x:T1×T2,x2:T2.ϕ ∧ x = (x1,x2)}

(x1 6= x2) (x1 /∈ dom(Γ , x:T1×T2 .ϒ )) (x2 /∈ dom(Γ , x:T1×T2 .ϒ ))(RT-CDR)

Γ ` {x:T1×T2 .ϒ | ϕ} CDR {x2:T2 .ϒ | ∃x:T1×T2,x1:T1.ϕ ∧ x = (x1,x2)}

(x /∈ dom(Γ ,ϒ ))(RT-NIL)

Γ ` {ϒ | ϕ} NILT {x:T list.ϒ | ϕ ∧ x = []}

(x3 /∈ dom(Γ , x1:T . x2:T list.ϒ ))(RT-CONS)

Γ ` {x1:T . x2:T list.ϒ | ϕ} CONS {x3:T list.ϒ | ∃x1:T,x2:T list.ϕ ∧ x1 :: x2 = x3}

Fig. 7 Typing rules (I)

– (RT-DIP) means that DIP IS is well typed if the body IS is typed under the stacktype obtained by removing the top element. However, since a property ϕ forthe initial stack relies on the popped value x, we keep the binding in the typingenvironment.

– (RT-IF) means that the instruction is well typed if both branches have the samepost-condition; the pre-conditions of the branches are strengthened by the assump-

Page 15: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 15

Γ ` {ϒ | ∃x:int.ϕ ∧ x 6= 0} IS1 Φ Γ ` {ϒ | ∃x:int.ϕ ∧ x = 0} IS2 Φ(RT-IF)

Γ ` {x:int.ϒ | ϕ} IF IS1 IS2 Φ

Γ ` {ϒ | ∃x:int.ϕ ∧ x 6= 0} IS {x:int.ϒ | ϕ}(RT-LOOP)

Γ ` {x:int.ϒ | ϕ} LOOP IS {ϒ | ∃x:int.ϕ ∧ x = 0}

(x1 6= x2) (x1 /∈ dom(Γ , x:T list.ϒ )) (x2 /∈ dom(Γ , x:T list.ϒ ))Γ ` {x1:T . x2:T list.ϒ | ∃x:T list.ϕ ∧ x1 :: x2 = x} IS1 Φ

Γ ` {ϒ | ∃x:T list.ϕ ∧ x = []} IS2 Φ(RT-IFCONS)

Γ ` {x:T list.ϒ | ϕ} IF_CONS IS1 IS2 Φ

(x1 6= x2) (x1 /∈ dom(Γ , x:T list.ϒ )) (x2 /∈ dom(Γ , x:T list.ϒ ))Γ ,x2:T list ` {x1:T .ϒ | ∃x:T list.ϕ ∧ x1 :: x2 = x} IS {ϒ | ∃x:T list.ϕ ∧ x2 = x}

(RT-ITER)Γ ` {x:T list.ϒ | ϕ} ITER IS {ϒ | ∃x:T list.ϕ ∧ x = []}

(x /∈ dom(Γ ,ϒ )∪{y1,y′1,y2}) (y1 6= y2) y′1:T1,y1:T1 ` ϕ1 : ∗y′1:T1 ` {y1:T1 .‡ | y′1 = y1 ∧ϕ1} IS {y2:T2 .‡ | ϕ2}

(RT-LAMBDA)Γ ` {ϒ | ϕ} LAMBDAT1 T2 IS {x:T1 → T2 .ϒ | ϕ ∧ ∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ ϕ1 ∧call(x,y′1) = y2 =⇒ ϕ2}

(x3 /∈ dom(Γ , x1:T1 . x2:T1→ T2 .ϒ ))(RT-EXEC)

Γ ` {x1:T1 .x2:T1→ T2 .ϒ | ϕ} EXEC {x3:T2 .ϒ | ∃x1:T1,x2:T1→ T2.ϕ∧call(x2,x1) = x3}

(x4 /∈ dom(Γ , x1:T . x2:int. x3:address.ϒ ))(RT-TRANSFERTOKENS)

Γ ` {x1:T . x2:int . x3:address . ϒ | ϕ} TRANSFER_TOKENST {x4:operation.ϒ | ∃x1:T,x2:int,x3:address.ϕ ∧ x4 = Transfer(x1,x2,x3)}

Γ `Φ1 <: Φ ′1 Γ `Φ ′1 I Φ ′2 Γ `Φ ′2 <: Φ2(RT-SUB)

Γ `Φ1 I Φ2

Fig. 8 Typing rules (II)

tions that the top of the input stack is True (x 6= 0) and False (x = 0). The variablex is existentially quantified because the top element will be removed before theexecution of either branch.

– (RT-LOOP) is similar to the proof rule for while-loops in Hoare logic. The formulaϕ is a loop invariant. Since the body of LOOP is executed while the stack top isnonzero, the pre-condition for the body IS is strengthened by x 6= 0, whereas thepost-condition of LOOP IS is strengthened by x = 0.

– (RT-LAMBDA) is for the instruction to push a first-class function onto the operandstack. The premise of the rule means that the body IS takes a value (named y1) oftype T1 that satisfies ϕ1 and outputs a value (named y2) of type T2 that satisfiesϕ2 (if it terminates). The post-condition in the conclusion expresses, by usingcall, that the function x has the property above. The extra variable y′1 in the typeenvironment of the premise is an alias of y1; being a variable declared in the type

Page 16: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

16 Yuki Nishida et al.

Stack Typing S : T

(ST-BOTTOM)‡ : ‡

V : T S : T (ST-PUSH)V .S : T . T

Refinement Stack Typing σ : Γ |= S : Φ

σ : Γ |= ϕ(SEM-BOTTOM)

σ : Γ |= ‡ : {‡ | ϕ}σ [x 7→V ] : Γ ,x:T |= S : {ϒ | ϕ}

(SEM-PUSH)σ : Γ |=V .S : {x:T .ϒ | ϕ}

Fig. 9 Simple and refinement stack typing

environment y′1 can appear in both ϕ1 and ϕ25 and can describe the relationship

between the input and output of the function.– (RT-EXEC) just adds to the post-condition call(x2,x1) = x3, meaning the result

of a call to the function x2 with x1 as an argument yields x3. It may look simplerthan expected; the crux here is that ϕ is expected to imply ∀x1:T1,x3:T2.ϕ1 ∧call(x2,x1) = x3 =⇒ ϕ2, where ϕ1 and ϕ2 represent the pre- and post-conditions,respectively, of function x2. If x1 satisfies ϕ1, then we can derive that ϕ2 holds.

– (RT-SUB) is the rule for subsumption to strengthening the pre-condition andweakening the post-condition.

3.5 Properties

In this section, we show soundness of our type system. Informally, what we showis that, for a well-typed program, if we execute it under a stack which satisfies thepre-condition of the typing, then (if the evaluation halts) the resulting stack satisfiesthe post-condition of the typing. We only sketch proofs with important lemmas. Thedetailed proofs are found in Appendix A.

To state the soundness formally, we give additional definitions.

Definition 5 (Free variables). The set of free variables in ϕ is denoted by fvars(ϕ).

Definition 6 (Erasure). We define bΦc, which is the simple stack type obtained byerasing predicates from Φ , as follows.

b{‡ | ϕ}c= ‡ b{x:T .ϒ | ϕ}c= T . b{ϒ | ϕ}c

Definition 7 (Stack typing). Stack typing S : T and refinement stack typing σ : Γ |=S : Φ are defined by the rules in Figure 9.

Note that the definition of refinement stack typing follows the informal explanationof the refinement stack types in Section 3.4.

We start from soundness of simple type system as follows, that is, for a simplywell-typed instruction sequence T1 ` IS⇒ T2, if evaluation starts from correct stack S1,that is S1 : T1, and results in a stack S2; then S2 respects T2, that is S2 : T2. This lemma

5 The scope of a variable in a refinement stack type is its predicate part and so y1 cannot appear in thepost-condition.

Page 17: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 17

is not only just a desirable property but also one we use for proving the soundness ofthe refinement type system in the case EXEC.

Lemma 8 (Soundness of the Simple Type System). If T1 ` IS⇒ T2, S1 ` IS ⇓ S2, andS1 : T1, then S2 : T2.

Proof. Proved with a similar statement

If T1 ` IS⇒ T2, S1 ` IS ⇓ S2, and S1 : T1, then S2 : T2

for a single instruction by simultaneous induction on T1 ` IS⇒ T2 and T1 ` I⇒ T2.

We state the main theorem as follows.

Theorem 9 (Soundness of the Refinement Type System). If Γ `Φ1 IS Φ2, S1 ` IS ⇓ S2,and σ : Γ |= S1 : Φ1, then σ : Γ |= S2 : Φ2.

The proof is close to a proof of soundness of Hoare logic, with a few extracomplications due to the presence of first-class functions. One of the key lemmas isthe following one, which states that a value assignment can be represented by a logicalformula or a stack element:

Lemma 10. The following statements are equivalent:

(1) σ : Γ |= S : {ϒ | ∃x:T.ϕ ∧ x =V};(2) σ [x 7→V ] : Γ ,x:T |= S : {ϒ | ϕ}; and(3) σ : Γ |=V .S : {x:T .ϒ | ϕ}.

Then, we prove a few lemmas related to LOOP (Lemma 11), ITER (Lemma 12),predicate call (Lemmas 13 and 14), and subtyping (Lemma 15).

Lemma 11. Suppose IS satisfies that S1 ` IS⇓ S2 and σ : Γ |= S1 : {ϒ | ∃x:int.ϕ∧x 6=0} imply σ : Γ |= S2 : {x:int .ϒ | ϕ} for any S1 and S2. If S1 ` LOOP IS ⇓ S2 andσ : Γ |= S1 : {x:int.ϒ | ϕ}, then σ : Γ |= S2 : {ϒ | ∃x:int.ϕ ∧ x = 0}.

Proof. By induction on the derivation of S1 ` LOOP IS ⇓ S2.

Lemma 12. Suppose x1 /∈ fvars(ϕ), x2 /∈ fvars(ϕ), and that S′1 ` IS ⇓ S′2 and σ ′ : Γ ,x2:T list |= S′1 : {x1:T .ϒ | ∃x:T list.ϕ ∧ x1 :: x2 = x} imply σ ′ : Γ ,x2:T list |=S′2 : {ϒ | ∃x:T list.ϕ ∧ x2 = x} for any S′1, S′2, and σ ′. If S1 ` ITER IS ⇓ S2 andσ : Γ |= S1 : {x:T list.ϒ | ϕ}, then σ : Γ |= S2 : {ϒ | ∃x:T list.ϕ ∧ x = []}.

Proof. By induction on the derivation of S1 ` ITER IS ⇓ S2.

Lemma 13. If y1 6= y2, y′1:T1,y1:T1 ` ϕ1 : ∗, y′1:T1,y2:T2 ` ϕ2 : ∗, 〈IS〉 : T1→ T2, and

for any V1,V2,σ , if V1 .‡ ` IS ⇓V2 .‡ and

σ : y′1:T1 |=V1 .‡ : {y1:T1 .‡ | y′1 = y1∧ϕ1}then σ : y′1:T1 |=V2 .‡ : {y2:T2 .‡ | ϕ2},

then Γ |= ∀y′1:T1,y1:T1,y2:T2.y′1 = y1∧ϕ1∧call(〈IS〉,y′1) = y2 =⇒ ϕ2 for any Γ .

Proof. By the definition of the semantics of call.

Page 18: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

18 Yuki Nishida et al.

Lemma 14. If V1 . ‡ ` IS ⇓ V2 . ‡, V1 : T1, V2 : T2, and 〈IS〉 : T1 → T2, then Γ |=call(〈IS〉,V1) =V2 for any Γ .

Proof. By the definition of the semantics of call.

Lemma 15. If Γ `Φ1 <: Φ2 and σ : Γ |= S : Φ1, then σ : Γ |= S : Φ2.

Proof. Straightforward from Definition 4.

Proof of Theorem 9. It is proved together with a similar statement

If Γ `Φ1 I Φ2, S1 ` I ⇓ S2, and σ : Γ |= S1 : Φ1, then σ : Γ |= S2 : Φ2.

for a single instruction by simultaneous induction on Γ `Φ1 IS Φ2 and Γ `Φ1 I Φ2with case analysis on the last typing rule used. We show a few representative cases.

Case (RT-DIP): We have I = DIP IS and Φ1 = {x:T .ϒ | ϕ} and Φ2 = {x:T .ϒ ′ | ϕ ′}and Γ ,x:T ` {ϒ | ϕ} IS {ϒ ′ | ϕ ′} for some IS, x, T , ϒ , ϒ ′, ϕ , and ϕ ′. By (E-DIP),we have S1 =V .S′1 and S2 =V .S′2 and S′1 ` IS ⇓ S′2 for some V , S′1, and S′2. ByLemma 10, we have σ [x 7→V ] : Γ ,x:T |= S′1 : {ϒ | ϕ}. By applying IH, we haveσ [x 7→ V ] : Γ ,x:T |= S′2 : {ϒ ′ | ϕ ′} from which σ : Γ |= V . S′2 : {x:T .ϒ ′ | ϕ ′}follows.

Case (RT-LOOP): We have I = LOOP IS and Φ1 = {x:int.ϒ | ϕ} and Φ2 = {ϒ | ∃x:int.ϕ ∧ x = 0} and Γ ` {ϒ | ∃x:int.ϕ ∧ x 6= 0} IS {x:int.ϒ | ϕ} and S1 = i.Sfor some IS, x, ϒ , S, and ϕ . By IH, we have that, for any S′1,S

′2, if S′1 ` IS ⇓

S′2 and σ : Γ |= S′1 : {ϒ | ∃x:int.ϕ ∧ x 6= 0}, then σ : Γ |= S′2 : {x:int .ϒ | ϕ}.The goal easily follows from Lemma 11.

Case (RT-LAMBDA): We have I = LAMBDAT1 T2 IS and Φ1 = {ϒ | ϕ} and Φ2 = {x:T1 → T2 .ϒ | ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ϕ1 ∧ call(x,y′1) = y2 =⇒ ϕ2}and y′1:T1 ` {y1:T1 . ‡ | y′1 = y1 ∧ ϕ1} IS {y2:T2 . ‡ | ϕ2} and x /∈ dom(Γ ,ϒ )∪{y1,y′1,y2} and y1 6= y′1 and y′1:T1,y1:T1 ` ϕ1 : ∗ and for some IS, x, y1, y′1, y2, T1,T2, ϒ , ϕ , ϕ1, and ϕ2. By (E-LAMBDA), we also have S2 = 〈IS〉.S1. By IH, wehave that, for any V1,V2,σ , if V1.‡` IS⇓V2.‡ and σ : y′1:T1 |=V1.‡ : {y1:T1.‡ |y′1 = y1∧ϕ1}, then σ : y′1:T1 |=V2.‡ : {y2:T2.‡ |ϕ2}. By Lemma 13, Γ ,ϒ |= ∀y′1:T1,y1:T1,y2:T2.y′1 = y1∧ϕ1∧call(〈IS〉,y′1) = y2 =⇒ ϕ2. Then, it is easy to showσ : Γ |= S1 : {ϒ | ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1∧ϕ1∧call(〈IS〉,y′1) = y2 =⇒ϕ2}. Then, we have

σ : Γ |= S1 : {ϒ | ∃x:T1→ T2.(ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1∧ϕ1∧call(x,y′1) = y2 =⇒ ϕ2)∧ x = 〈IS〉}.

Therefore, by Lemma 10, we have

σ : Γ |= 〈IS〉.S1 : {x:T1→ T2 .ϒ | ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1∧ϕ1∧call(x,y′1) = y2 =⇒ ϕ2}

as required.

Page 19: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 19

Case (RT-EXEC): We have I = EXEC and, for some x1, x2, x3, T1, T2, ϒ , and ϕ ,Φ1 = {x1:T1 . x2:T1→ T2 .ϒ | ϕ} and Φ2 = {x3:T2 .ϒ | ∃x1:T1,x2:T1→ T2.ϕ ∧call(x2,x1) = x3} and x3 /∈ dom(Γ , x1:T1 . x2:T1→ T2 .ϒ ). By (E-EXEC), wehave S1 =V1.〈IS〉.S and S2 =V2.S and V1.‡` IS⇓V2.‡ for some V1, V2, IS, andS. By the assumption σ : Γ |= S1 : Φ1, we have σ : Γ |= S : {ϒ | ∃x2:T1→ T2.(∃x1:T1.ϕ∧x1 =V1)∧x2 = 〈IS〉}. By Lemma 14, we have Γ ,ϒ |= call(〈IS〉,V1) =V2.Therefore, we have σ : Γ |= S : {ϒ | (∃x2:T1 → T2.(∃x1:T1.ϕ ∧ x1 = V1)∧ x2 =〈IS〉)∧call(〈IS〉,V1) =V2}. Finally, we have σ : Γ |= S : {ϒ | ∃x3:T2.(∃x1:T1,x2:T1→ T2.ϕ ∧call(x2,x1) = x3)∧ x3 =V2} and, thus, σ : Γ |=V2 .S : {x3:T2 .ϒ |(∃x1:T1,x2:T1→ T2.ϕ ∧call(x2,x1) = x3)} as required.

Case (RT-SUB): We have Γ ` Φ1 <: Φ ′1 and Γ ` Φ ′2 <: Φ2 and Γ ` Φ ′1 I Φ ′2 forsome Φ ′1 and Φ ′2. By Lemma 15, we have σ : Γ |= S1 : Φ ′1. By IH, we haveσ : Γ |= S2 : Φ ′2. Then, the goal follows from Lemma 15.

3.6 Extension with Exceptions

The type system implemented in HELMHOLTZ is extended to handle instructionFAILWITH, which immediately aborts the execution, discarding all the stack elementsbut the top element. The type judgment form is extended to

Γ `Φ1 IS Φ2 & Φ3,

which means that, if IS is executed under a stack satisfying Φ1, then the resulting stacksatisfies Φ2 (if normally terminates), or Φ3 (if aborted by FAILWITH). The typing rulefor instruction FAILWITH, which raises an exception with the value at the stack top, isgiven as follows:

Γ ` {x:T .ϒ | ϕ} FAILWITH {ϒ | ⊥}& {err | ∃x:T,ϒ .ϕ ∧ x = err}.

The rule expresses that, if FAILWITH is executed under a non-empty stack that satisfiesϕ , then the program point just after the instruction is not reachable (hence, {ϒ | ⊥}).The refinement ∃x:T,ϒ .ϕ ∧ x = err for the exception case states that ϕ in the pre-condition with the top element x is equal to the raised value err; since x is not in thescope in the exception refinement, x is bound by an existential quantifier. Most of theother typing rules can be extended with the “&” part easily.

For (RT-LAMBDA) and (RT-EXEC), we first extend the the assertion languagewith a new predicate call_err(t1, t2) = t3 meaning the call of t1 with t2 aborts withthe value t3. (The semantics of call is unchanged.) Using the new predicate, (RT-LAMBDA) and (RT-EXEC) are modified as in Figure 10.

4 Tool Implementation

In this section, we present HELMHOLTZ, the verification tool based on the refinementtype system. We first discuss how Michelson code can be annotated. Then, we give anoverview of the verification algorithm, which reduces the verification problem to SMTsolving, and discuss how Michelson-specific features are encoded. Finally, we show acase study of contract verification and present verification experiments.

Page 20: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

20 Yuki Nishida et al.

(x /∈ dom(Γ ,ϒ )∪{y1,y′1,y2}) (y1 6= y2) y′1:T1,y1:T1 ` ϕ1 : ∗y′1:T1 ` {y1:T1 .‡ | y′1 = y1 ∧ϕ1} IS {y2:T2 .‡ | ϕ2}& {err | ϕ3}

Γ ` {ϒ | ϕ} LAMBDAT1 T2 IS {x:T1→ T2 .ϒ | ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ϕ1 =⇒ (call(x,y′1) =y2 =⇒ ϕ2)∧ (call_err(x,y′1) = err =⇒ ϕ3)}& {err | ⊥}

(x3 /∈ dom(Γ , x1:T1 . x2:T1→ T2 .ϒ ))

Γ ` {x1:T1 . x2:T1→ T2 .ϒ | ϕ} EXEC {x3:T2 .ϒ | ∃x1:T1,x2:T1→ T2.ϕ ∧call(x2,x1) = x3}& {err |∃x1:T1,x2:T1→ T2,ϒ .ϕ ∧call_err(x2,x1) = err}

Fig. 10 Modified Typing Rules for LAMBDA and EXEC.

4.1 Annotations

HELMHOLTZ supports several forms of annotations (surrounded by << and >> in thesource code), other than ContractAnnot explained in Section 2. As we have alreadymentioned, ML-like notations can be used to describe formulae, which have to bequantifier free (mainly because state-of-the-art SMT solvers do not handle quentifiersvery well). We explain several constructs for an annotation in the following.

Assert Φ and Assume Φ can appear before or after an instruction. The formerasserts that the stack at the annotated program location satisfies the type Φ ; theassertion is verified by HELMHOLTZ. If there is an annotation Assume Φ , HELM-HOLTZ assumes that the stack satisfies the type Φ at the annotated program location.A user can give a hint to HELMHOLTZ by using Assume Φ . The user has to make surethat it is correct; if an Assume annotation is incorrect, the verification result may alsobe incorrect.

An annotation LoopInv Φ may appear before a loop instruction (e.g., LOOP andITER). It asserts that Φ is a loop invariant of the loop instruction. In the currentimplementation, annotating a loop invariant using LoopInv Φ is mandatory for a loopinstruction. HELMHOLTZ checks that Φ is indeed a loop invariant and uses it to verifythe rest of the program.

In the current implementation, a LAMBDA instruction, which pushes a func-tion on the top of the stack, must be accompanied by a LambdaAnnot annotation.LambdaAnnot comes with a specification of the pushed function written in the sameway as ContractAnnot. Concretely, the specification of the form Φpre → Φpost &Φabpost(x1 : T1, . . . ,xn : Tn) specifies the precondition Φpre, the (normal) postconditionΦpost, and the (abnormal) postcondition Φabpost as refinement stack types. The binding(x1 : T1, . . . ,xn : Tn) introduces the ghost variables that can be used in the annotationsin the body of the annotated LAMBDA instruction;6; one can omit if it is empty.

The first contract in Figure 11, which pushes a function that takes a pair of integersand returns the sum of them, exemplifies LambdaAnnot. The annotated type of thefunction (Line 6) expresses that it returns 4 if it is fed with a pair (3,1). The ghostvariables a and b are used in the annotations Assume (Line 10) and Assert (Line 12)in the body to denote the first and the second arguments of the pair passed to thisfunction.

6 ContractAnnot also allows declarations of ghost variable used in the code section.

Page 21: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 21

1 parameter unit ;2 storage int;3 << ContractAnnot { _ | True } -> { _ | True }4 & { _ | False } >>5 code { DROP;6 << LambdaAnnot { p | p = (3, 1) } -> { x | x = 4 }7 & { _ | False }8 (a:int , b:int) >>9 LAMBDA (pair int int) int

10 { << Assume { p | p = (a, b) } >>11 UNPAIR; ADD12 << Assert { p | p = a + b } >>13 };14 PUSH int 1; PUSH int 3; PAIR; EXEC;15 << Assert { x | x = 4 } >>16 NIL operation; PAIR17 }

1 parameter (list int);2 storage int;3 << Measure len : list int -> int4 where [] = 0 | h :: t = (1 + len t) >>5 << ContractAnnot6 { (p, _) | True } -> { (_, ret) | len p = ret }7 & { _ | False } >>8 code { CAR; PUSH int 0; SWAP;9 << LoopInv { l : n | len l + n = len p } >>

10 ITER { DROP; PUSH int 1; ADD };11 NIL operation;12 PAIR13 }

Fig. 11 lambda.tz, which uses higher-order functions, and length.tz, which uses a measure functionin the contract annotation.

To describe a property for recursive data structures, HELMHOLTZ supports measurefunctions introduced by Kawaguchi et al. [9] and also supported in Liquid Haskell [23].A measure function is a (recursive) function over a recursive data structure that canbe used in assertions. The annotation Measure x : T1→ T2 where p1 = e1 | · · · | pn =en defines a measure function x over the type T1. The measure function x takes avalue of type T1, destructs it by the pattern matching, and returns a value of type T2.Metavariables p and e represent ML-like patterns and expressions. The second contractin Figure 11, which computes the length of the list passed as a parameter, exemplifiesthe usage of the Measure annotation. This contract defines a measure function lenthat takes a list of integers and returns its type; it is used in ContractAnnot andLoopInv.

Page 22: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

22 Yuki Nishida et al.

4.2 Overview of the Verification Algorithm

HELMHOLTZ takes an annotated Michelson program and conducts typecheckingbased on the refinement type system in Section 3.4. The typechecking procedure (1)computes the verification conditions (VCs) for the program to be well-typed and (2)discharges it using an SMT solver. The latter is standard: We decide the validity of thegenerated VCs using an SMT solver (Z3 in the current implementation.) We explainthe VC-generation step in detail.

For an annotated contract, HELMHOLTZ conducts forward reasoning startingfrom the precondition and generates VCs if necessary. During the forward reasoning,HELMHOLTZ keeps track of the Γ -and-ϒ part of the type judgment.

The typing rules are designed so that they enable the forward reasoning if aprogram is simply typed. For example, consider the rule (RT-ADD) in Figure 7. Thisrule can be read as a rule to compute a postcondition ∃x1:int,x2:int.ϕ∧x1+x2 = x3from a precondition ϕ if the first two elements in ϒ are x1 and x2. The other rules canalso be read as postcondition-generation rules in the same way.

There are three places where HELMHOLTZ generates a verification condition.

– At the end of the program: HELMHOLTZ generates a condition that ensures that thecomputed postcondition of the entire program implies the postcondition annotatedto the program.

– Before and after instruction LAMBDA: HELMHOLTZ generates conditions that en-sure that the pre- and post- conditions of the instruction LAMBDA is as annotated inLambdaAnnot.

– At a loop instruction: HELMHOLTZ generates verification conditions that ensurethe condition annotated by LoopInv is indeed a loop invariant of this instruction.

A VC generated by HELMHOLTZ at these places is of the form ∀~x :~T .ϕ1⇒ ϕ2, where~x :~T is a sequence of bindings.

To discharge each VC, as many verification condition discharging proceduresdo, HELMHOLTZ checks whether its negation, ∃~x :~T .ϕ1∧¬ϕ2, is satisfiable; if it isunsatisfiable, then the original VC is successfully discharged. We remark that ourtype system is designed so that ϕ1 and ϕ2 are quantifier free for a program that doesnot use LAMBDA and EXEC. Indeed, ϕ2 comes only from the annotations, which arequantifier-free. ϕ1 comes from the postcondition-computation procedure, which is ofthe form ∃~x′ : ~T ′.ϕ ′1 for quantifier-free ϕ ′1 for the instructions other than LAMBDA andEXEC; the formula ∃~x :~T .ϕ1∧¬ϕ2 is equivalent to ∃~x :~T ,~x′ : ~T ′.ϕ ′1∧¬ϕ2.

4.3 Encoding Micheslon-Specific Features

Since our assertion language includes several specific features that originates fromMichelson and our assertion language, HELMHOLTZ needs to encode them in dis-charging VCs so that Z3 can handle them. We explain how this encoding is conducted.

Page 23: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 23

4.3.1 Michelson-Specific Functions and Predicates

We use encode several Michelson-specific values using uninterpreted functions. Forexample, HELMHOLTZ assumes the following typing rule for instruction SHA256,which converts the top element to its SHA256 hash.

Γ ` {x : bytesBϒ | φ} SHA256 {y : bytesBϒ | ∃x.φ ∧ y = sha256(x)}In the post condition, we use an uninterpreted function sha256 to express the SHA256hash of x. In Z3, this uninterpreted function is declared as follows.1 (declare -fun sha256 (String) String)2 (assert (forall ((x String )) (= (str.len (sha256 x)) 32)))

The first line declares the signature of sha256. The second line is the axiom for sha256that the length of a hash is always 32.

Notice that, we cannot assert that a hash is equal to a specific constant since sha256is an uninterpreted function. Therefore, HELMHOLTZ cannot prove:

Γ ` {x : bytes | x = 0} SHA256 {y : bytesBϒ | ∃x.φ ∧ y =

“6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d”}.

4.3.2 Implementation of Measure Functions

A measure function is encoded as an uninterpreted function accompanied with axiomsthat specify the behavior of the function, which is defined by the Measure annotation.For example, for the following form of a measure function definition for lists:1 << Measure f : list T -> T’ where [] = e1 | h :: t = e2 >>

Theoretically, one could insert the following declarations and assertions when itgenerates an input to Z3 to encode this definition:

1 (declare-fun f ((List T)) T’)2 (assert (= (f []) e1))3 (assert (forall ((h T) (t (List T)))4 (= (f (cons h t)) e2)))

However, Z3 tends to timeout if we naively insert the above axioms to Z3 input whichcontains a universal quantifier in the encoded definition.

To address this problem, HELMHOLTZ rewrites VCs so that heuristically in-stantiated conditions on a measure function are available where necessary. Con-sider the above definition of f as an example. Suppose HELMHOLTZ obtains aVC of the form ∃~x :~T .ϕ1 ∧¬ϕ2 mentioned in Section 4.2 and ϕ1 and ϕ2 contains(cons eh,i et,i) for i ∈ {1, . . . ,N}. Then, HELMHOLTZ constructs a formula ϕmeas :=∧

i∈{1,...,N} (= (f (cons eh,i et,i)) e2) and rewrites the VC to ∃~x :~T .ϕmeas ∧ϕ1 ∧¬ϕ2.

We remark that, in LiquidHaskell, measure functions are treated as a part of thetype system [9]: the asserted axioms are systematically (instantiated and) embeddedinto the typing rules. In HELMHOLTZ, measure functions are treated as an ingredientthat is orthogonal to the type system; the type system is oblivious of measure functionsuntil its definition is inserted to Z3 input.

Page 24: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

24 Yuki Nishida et al.

4.3.3 Overloaded Functions

Due to the polymorphically-typed instructions in Michelson, our assertion languageincorporates polymorphic uninterpreted functions. For example, Michelson has aninstruction PACK, which pops a value (of any type) from the stack, serializes it to abinary representation, and pushes the serialized value. HELMHOLTZ typechecks thisinstruction based on the following rule.

Γ ` {x : T Bϒ | ϕ} PACK {y : bytesBϒ | ∃x : T.ϕ ∧ y = pack(x)}

The term pack(x) in the postcondition represents a serialized value created from x.Since x may be of any simple type T , pack must be polymorphic.

Having a polymorphic uninterpreted function in assertions is tricky because Z3does not support a polymorphic value. HELMHOLTZ encodes polymorphic uninter-preted functions to a monomorphic function whose name is generated by mangling thename of its instantiated parameter type. For example, the above pack(x) is encoded asa Z3 function pack!int(x) whose type is int→ bytes. Although there are infinitelymany types, the number of the encoded functions is finite since only finitely manytypes appear in a single contract.

4.3.4 Michelson-Specific Types

In encoding a VC as Z3 constraints, HELMHOLTZ maps types in Michelson intosorts in Z3, e.g., the Michelson type nat for nonnegative integers to the Z3 sortInt. A naive mapping from Michelson types to Z3 sorts is problematic; for example,∀x : nat.x≥ 0 in HELMHOLTZ is valid, but a naively encoded formula (forall ((xInt)) (>= x 0)) is invalid in Z3. This naive encoding ignores that a value of sortnat is nonnegative.

To address the problem, we adapt the method encoding a many-sorted logicformula into a single-sorted logic formula [3]. Concretely, we define a sort predicatePT (x) for each sort T , which characterizes the property of the sort T . For example,Pnat(x) := x≥ 0. We also define sort predicates for compound data types.

Using the sort predicates, we can encode a VC into a Z3 constraint as follows: ∀x :T.φ is encoded into ∀x : [[T ]].PT (x)⇒ φ and ∃x : T.φ is encoded into ∃x : [[T ]].PT (x)∧φ , where [[T ]] denotes the target sort of T (e.g., [[nat]] = Int). Furthermore, we alsoadd axioms about co-domain of uninterpreted functions as ∀~xi : ~Ti.PT ( f (~xi)) for thefunction f of ~Ti→ T .

4.4 Case Study: Contract with Signature Verification

Figure 12 presents the code of the contract checksig.tz, which verifies that asender indeed signed certain data using her private key. This contract uses instructionCHECK_SIGNATURE, which is supposed to be executed under a stack of the form key. sig . bytes . tl, where key is a public key, sig is a signature, and bytes is somedata. CHECK_SIGNATURE pops these three values from the stack and pushes Trueif sig is the valid signature for bytes with the private key corresponding to key.

Page 25: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 25

1 parameter (pair signature string );2 storage (pair address key);3 << ContractAnnot4 { ((sign , data), (addr , pubkey )) |5 match contract_opt addr with6 | Some (Contract <string > _) -> True | _ -> False } ->7 { (ops , new_store) | (addr , pubkey) = new_store &&8 sig pubkey sign (pack data) &&9 match contract_opt addr with

10 | Some c -> ops = [ TransferTokens data 1 c ]11 | None -> False }12 & { _ | not (sig pubkey sign (pack data)) } >>13 code { DUP; DUP; DUP;14 DIP { CAR; UNPAIR; DIP { PACK } }; CDDR;15 CHECK_SIGNATURE; ASSERT;1617 UNPAIR; CDR; SWAP; CAR;18 CONTRACT string; ASSERT_SOME; SWAP;19 PUSH mutez 1; SWAP;20 TRANSFER_TOKENS;2122 NIL operation; SWAP;23 CONS; DIP { CDR };24 PAIR25 }

Fig. 12 checksig.tz, which involves signature verification.

The instruction ASSERT after CHECK_SIGNATURE checks if the signature checking hassucceeded; it aborts the execution of the contract if the stack top is False; otherwise,it pops the stack top (True) and proceeds the next instruction.

The intended behavior of checksig.tz is as follows. It stores a pair of an addressaddr, which is the address of a contract that takes a string parameter, and a publickey pubkey in its storage. It takes a pair (sign,data) of type (pair signaturestring) as a parameter; here, signature is the primitive Michelson type for signa-tures. This contract terminates without exception if sign is created from the serialized(packed) representation of data and signed by the private key corresponding topubkey. In a normal termination, this contract transfers 1 mutez to the contract withaddress addr. If this signature verification fails, then an exception is raised.

This behavior is expressed as a specification in the ContractAnnot annotation inchecksig.tz as follows.

– The refinement of its pre-condition part expresses that the address stored inthe first element addr of the storage is an address of a contract that takes avalue of type string as a parameter. This is expressed by the pattern-matchingon contract_opt addr, which checks if there is an intended parameter typeof contract stored at the address addr and returns the contract (wrapped bySome) if there is. The intended parameter type is given by the pattern expres-sion Contract<string> _, which matches a contract that takes a string.

Page 26: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

26 Yuki Nishida et al.

– The refinement of the post-condition forces the following three conditions: (1)the store is not updated by this contract ((addr, pubkey) = new_store); (2)sign is the signature created from the packed bytes pack data of the string inthe second element of the parameter and signed by the private key correspondingto the second element pubkey of the store (sig pubkey sign (pack data));and (3) the operations ops returned by this contract is [ Transfer data 1 c], which represents an operation of transferring 1 mutez to the contract c, whichis bound to Contract addr, with the parameter data. The predicate sig and thefunction pack are primitives of the assertion language of HELMHOLTZ.

– The refinement in the exception part expresses that if an exception is raised, thenthe signature verification should have failed (not (sig pubkey sign (packdata))).

HELMHOLTZ successfully verifies checksig.tz without any additional annota-tion in the code section. If we change the instruction ASSERT in Line 15 to DROP tolet the contract drop the result of the signature verification (hence, an exception is notraised even if the signature verification fails), the verification fails as intended.

4.5 Experiments

We applied HELMHOLTZ to various contracts; Table 1 is an excerpt of the result, inwhich we show (1) the number of the instructions in each contract (column #instr.) and(2) time (ms) spent to verify each contract. The experiments are conducted on MacOSBig Sur 11.4 with Quad-Core Intel Core i7 (2.3 GHz), 32 GB RAM. We used Z3version 4.8.10. The contracts boomerang.tz, deposit.tz, manager.tz, vote.tz,and reservoir.tz are taken from the benchmark of Mi-cho-coq [2]. checksig.tz,discussed above, is derived from weather_insurance.tz of the official Tezos testsuite.7 vote_for_delegate.tz and xcat.tz are taken from the official test suite;xcat.tz is simplified from the original. tzip.tz is taken from Tezos Improvementproposals.8 triangular_num.tz is a simple test case that we made as an exampleof using LOOP. The source code of these contracts can be found at the Web interfaceof HELMHOLTZ. Each contract is supposed to work as follows.

– boomerang.tz: Transfers the received amount of money to the source account.– deposit.tz: Transfers money to the sender if the address of the sender is identical

to that is stored in the storage.– manager.tz: Calls the passed function if the address of the caller matches the

address stored in the storage.– vote.tz: Accepts a vote to a candidate if the voter transfers enough voting fee,

and stores the tally.– tzip.tz: One of the components implementing Tezos smart contracts API. We

verify one entrypoint of the contract.

7 https://gitlab.com/tezos/tezos/-/tree/ee2f75bb941522acbcf6d5065a9f3b2/tests_python/contracts/mini_scenarios

8 https://gitlab.com/tezos/tzip/-/blob/b73c7cd5df8e045bbf7ad9ac20a45fb3cb862c87/proposals/tzip-7/tzip-7.md

Page 27: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 27

Table 1 Benchmark result

Filename #instr. time (ms) Filename #instr. time (ms)boomerang.tz 17 435 checksig_unverified.tz 30 462deposit.tz 24 451 vote_for_delegate.tz 78 608manager.tz 24 449 xcat.tz 52 513vote.tz 24 450 reservoir.tz 45 482tzip.tz 537 15578 triangular_num.tz 16 517checksig.tz 32 468

– checksig.tz: The one explained in Section 4.4.– vote_for_delegate.tz: Delegates one’s ballot in voting by stakeholders, which

is one of the fundamental features of Tezos, to another using a primitive operationof Tezos.

– xcat.tz: Transfers all stored money to one of the two accounts specified before-hand if called with the correct password. The account that gets money is decidedbased on whether the contract is called before or after a deadline.

– reservoir.tz: Sends a certain amount of money to either a contract or anotherdepending on whether the contract is executed before or after the deadline.

– triangular_num.tz: Calculates the sum from 1 to n, which is the passed param-eter.

In the experiments, we verified that each contract indeed works according to theintention explained above. triangular_num.tz was the only contract that requireda manual annotation for verification in the code section; we needed to specify a loopinvariant in this contract.

Although the numbers of instructions in these contracts are not large, they captureessential features of smart contracts; every contract except triangular_num.tzexecutes transactions; deposit.tz and manager.tz check the identity of the caller;and checksig.tz conducts signature verification. The time spent on verification issmall.

5 Related Work

There are several publications on the formalization of programming languages forwriting smart contracts. Hirai [7] formalizes EVM, a low-level smart contract languageof Ethereum and its implementation, using Lem [14], a language to specify semanticdefinitions; definitions written in Lem can be compiled into definitions in Coq, HOL4,and Isabelle/HOL. Based on the generated definition, he verifies several propertiesof Ethereum smart contracts using Isabelle/HOL. Bernardo et al. [2] implementedMi-Cho-Coq, a formalization of the semantics of Michelson using the Coq proofassistant. They also verified several Michelson contracts. Compared to their approach,we aim to develop an automated verification tool for smart contracts. Park et al. [15]developed a formal verification tool for EVM by using the K-framework [17], whichcan be used to derive a symbolic model checker from a formally specified languagesemantics (in this case, formalized EVM semantics [6]), and successfully applied thederived model checker to a few EVM contracts. It would be interesting to formalize

Page 28: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

28 Yuki Nishida et al.

the semantics of Michelson in the K-framework to compare HELMHOLTZ with thederived model checker.

The DAO attack [18], mentioned in Section 1, is one of the notorious attacks on asmart contract. It exploits a vulnerability of a smart contract that is related to a callback.Grossman et al. [5] proposed a type-based technique to verify that execution of a smartcontract that may contain callbacks is equivalent to another execution without anycallback. This property, called effectively callback freedom, can be seen as one of thecriteria for execution of a smart contract not to be vulnerable to the DAO-like attack.Their type system focuses on verifying the ECF property of the execution of a smartcontract, whereas ours concerns the verification of generic functional properties of asmart contract.

Benton proposes a program logic for a minimal stack-based programming lan-guage [1]. His program logic can give an assertion to a stack as our stack refinementtypes do. However, his language does not support first-class functions nor instructionsfor dealing with smart contracts (e.g., signature verification).

Our type system is an extension of the Michelson type system with refinementtypes, which have been successfully applied to various programming languages [16,22,9,10,20,26,23,24,25]. DTAL [25] is a notable example of an application of refinementtypes to an assembly language, a low-level language like Michelson. A DTAL programdefines a computation using registers; we are not aware of refinement types for stack-based languages like Michelson.

We notice the resemblance between our type system and a program logic for PCFproposed by Honda and Yoshida [8], although the targets of verification are different.Their logic supports a judgment of the form A ` e :u B, where e is a PCF program,A is a pre-condition assertion, B is a post-condition assertion, and u represents thevalue that e evaluates to and can be used in B, which resembles our type judgmentin the formalization in Section 3. Their assertion language also incorporates a termexpression f • x, which expresses the value resulting from the application of f to x;this expression resembles the formula call(t1, t2) = t3 used in a refinement predicate.We have not noticed an automated verifier implemented based on their logic. Furthercomparison is interesting future work.

6 Conclusion

We described our automated verification tool HELMHOLTZ for the smart contractlanguage Michelson based on the refinement type system for Mini-Michelson. HELM-HOLTZ verifies whether a Michelson program follows a specification given in the formof a refinement type. We also demonstrated that HELMHOLTZ successfully verifiesvarious practical Michelson contracts.

Currently, HELMHOLTZ supports approximately 80% of the whole instructions ofthe Michelson language. The definition of a measure function is limited in the sensethat, for example, it can define only a function with one argument. We are currentlyextending HELMHOLTZ so that it can deal with more programs.

HELMHOLTZ currently verifies the behavior of a single contract, although ablockchain application often consists of multiple contracts in which contract calls are

Page 29: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 29

chained. To verify such an application as a whole, we plan to extend HELMHOLTZso that it can verify an inter-contract behavior compositionally by combining theverification results of each contract.

Acknowledgements This work has been partially supported by a research grant from Tezos Foundations.Igarashi’s first encounter with formal proofs was when he visited Prof. Hagiya’s group as an intern andplayed Boomborg PC.

References

1. Benton, N.: A Typed, Compositional Logic for a Stack-Based Abstract Machine. In: Proceedings ofAsian Symposium on Programming Languages and Systems (APLAS), pp. 364–380. Springer BerlinHeidelberg (2005). DOI 10.1007/11575467_24

2. Bernardo, B., Cauderlier, R., Hu, Z., Pesin, B., Tesson, J.: Mi-Cho-Coq, a framework for certifyingTezos smart contracts. In: Formal Methods. FM 2019 International Workshops - Porto, Portugal,October 7-11, 2019, Revised Selected Papers, Part I, Lecture Notes in Computer Science, vol. 12232,pp. 368–379. Springer (2019). DOI 10.1007/978-3-030-54994-7_28

3. Enderton, H.B.: A Mathematical Introduction to Logic. Academic Press (2001)4. Goodman, L.: Tezos — a self-amending crypto-ledger. white paper. https://tezos.com/static/

white_paper-2dc8c02267a8fb86bd67a108199441bf.pdf (2014). Retrieved Oct. 14, 2020.5. Grossman, S., Abraham, I., Golan-Gueta, G., Michalevsky, Y., Rinetzky, N., Sagiv, M., Zohar, Y.:

Online detection of effectively callback free objects with applications to smart contracts. Proc. ACMProgram. Lang. 2(POPL) (2017). DOI 10.1145/3158136

6. Hildenbrandt, E., Saxena, M., Rodrigues, N., Zhu, X., Daian, P., Guth, D., Moore, B., Park, D.,Zhang, Y., Stefanescu, A., Rosu, G.: KEVM: A Complete Formal Semantics of the Ethereum VirtualMachine. In: 2018 IEEE 31st Computer Security Foundations Symposium (CSF), pp. 204–217 (2018).DOI 10.1109/CSF.2018.00022

7. Hirai, Y.: Defining the Ethereum virtual machine for interactive theorem provers. In: FinancialCryptography and Data Security, pp. 520–535. Springer International Publishing (2017)

8. Honda, K., Yoshida, N.: A compositional logic for polymorphic higher-order functions. In: Proceedingsof the 6th International ACM SIGPLAN Conference on Principles and Practice of Declarative Program-ming, 24-26 August 2004, Verona, Italy, pp. 191–202. ACM (2004). DOI 10.1145/1013963.1013985

9. Kawaguchi, M., Rondon, P.M., Jhala, R.: Type-based data structure verification. In: Proceedings ofthe 2009 ACM SIGPLAN Conference on Programming Language Design and Implementation, PLDI2009, Dublin, Ireland, June 15-21, 2009, pp. 304–315. ACM (2009). DOI 10.1145/1542476.1542510

10. Kobayashi, N., Sato, R., Unno, H.: Predicate abstraction and CEGAR for higher-order model check-ing. In: Proceedings of the 32nd ACM SIGPLAN Conference on Programming Language De-sign and Implementation, PLDI 2011, San Jose, CA, USA, June 4-8, 2011, pp. 222–233 (2011).DOI 10.1145/1993498.1993525

11. de Moura, L.M., Bjørner, N.: Z3: an efficient SMT solver. In: Tools and Algorithms for the Constructionand Analysis of Systems, 14th International Conference, TACAS 2008, Held as Part of the JointEuropean Conferences on Theory and Practice of Software, ETAPS 2008, Budapest, Hungary, March29-April 6, 2008. Proceedings, pp. 337–340 (2008). DOI 10.1007/978-3-540-78800-3_24

12. Nakamoto, S.: Bitcoin: A peer-to-peer electronic cash system. https://bitcoin.org/bitcoin.pdf (2008). Retrieved Oct. 12, 2020.

13. Nomadic Labs: Michelson: the language of smart contracts in Tezos. https://tezos.gitlab.io/whitedoc/michelson.html. Retrieved Oct. 14, 2020.

14. Owens, S., Böhm, P., Zappa Nardelli, F., Sewell, P.: Lem: A lightweight tool for heavyweight semantics.In: Interactive Theorem Proving, pp. 363–369. Springer Berlin Heidelberg (2011)

15. Park, D., Zhang, Y., Saxena, M., Daian, P., Rosu, G.: A formal verification tool for Ethereum VMbytecode. In: Proceedings of the 2018 26th ACM Joint Meeting on European Software EngineeringConference and Symposium on the Foundations of Software Engineering, pp. 912–915. ACM (2018).DOI 10.1145/3236024.3264591

16. Rondon, P.M., Kawaguchi, M., Jhala, R.: Liquid types. In: Proceedings of the ACM SIGPLAN 2008Conference on Programming Language Design and Implementation, Tucson, AZ, USA, June 7-13,2008, pp. 159–169 (2008). DOI 10.1145/1375581.1375602

Page 30: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

30 Yuki Nishida et al.

17. Rosu, G., Serbanuta, T.F.: An overview of the K semantic framework. The Journal of Logic andAlgebraic Programming 79(6), 397–434 (2010). DOI 10.1016/j.jlap.2010.03.012

18. Siegel, D.: Understanding the DAO attack. CoinDesk (2016). URL https://www.coindesk.com/understanding-dao-hack-journalists. Retrieved Oct. 13, 2020.

19. Szabo, N.: Formalizing and securing relationships on public networks. First Monday 2(9) (1997).DOI 10.5210/fm.v2i9.548

20. Terauchi, T.: Dependent types from counterexamples. In: Proceedings of the 37th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL 2010, Madrid, Spain, January17-23, 2010, pp. 119–130 (2010). DOI 10.1145/1706299.1706315

21. The Coq development team: The coq proof assistant reference manual (2020). URL http://coq.inria.fr. Version 8.12.0

22. Unno, H., Kobayashi, N.: Dependent type inference with interpolants. In: Proceedings of the 11thInternational ACM SIGPLAN Conference on Principles and Practice of Declarative Programming,September 7-9, 2009, Coimbra, Portugal, pp. 277–288 (2009). DOI 10.1145/1599410.1599445

23. Vazou, N., Seidel, E.L., Jhala, R., Vytiniotis, D., Jones, S.L.P.: Refinement types for Haskell. In:Proceedings of the 19th ACM SIGPLAN international conference on Functional programming, Gothen-burg, Sweden, September 1-3, 2014, pp. 269–282. ACM (2014). DOI 10.1145/2628136.2628161

24. Xi, H.: Dependent ML an approach to practical programming with dependent types. J. Funct. Program.17(2), 215–286 (2007). DOI 10.1017/S0956796806006216

25. Xi, H., Harper, R.: A dependently typed assembly language. In: Proceedings of the Sixth ACMSIGPLAN International Conference on Functional Programming (ICFP ’01), Firenze (Florence), Italy,September 3-5, 2001, pp. 169–180. ACM (2001). DOI 10.1145/507635.507657

26. Zhu, H., Jagannathan, S.: Compositional and lightweight dependent type inference for ML. In:Verification, Model Checking, and Abstract Interpretation, 14th International Conference, VMCAI2013, Rome, Italy, January 20-22, 2013. Proceedings, pp. 295–314 (2013). DOI 10.1007/978-3-642-35873-9_19

A Proof of Soundness

This Appendix is only for reviewing. If this is accepted, we will remove it, upload a preprint version with theproof to arXiv. The article will refer to the preprint version.

We prove our main theorem (Theorem 16), whose first item is Theorem 9. The proof is close to a proofof soundness of Hoare logic, with a few extra complications due to the presence of first-class functions. Wefirst prove a few lemmas related to LOOP (Lemma 11), ITER (Lemma 12), predicate call (Lemmas 13 and14), and subtyping (Lemma 15). We often use Theorem 8 implicitly.

Lemma 11. Suppose IS satisfies

S1 ` IS ⇓ S2 and σ : Γ |= S1 : {ϒ | ∃x:int.ϕ ∧ x 6= 0} imply σ : Γ |= S2 : {x:int.ϒ | ϕ} (H.1)

for any S1 and S2. If

S1 ` LOOP IS ⇓ S2 (H.2)

σ : Γ |= S1 : {x:int.ϒ | ϕ} (H.3)

then σ : Γ |= S2 : {ϒ | ∃x:int.ϕ ∧ x = 0}.

Proof. By induction on the derivation of (H.2). We conduct the last rule that derives (H.2), which is either(E-LOOPT) or (E-LOOPF).

Case (E-LOOPT). We have

S1 = i.S (H.4)

i 6= 0 (H.5)

S ` IS ⇓ S′ (H.6)

S′ ` LOOP IS ⇓ S2. (H.7)

Page 31: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 31

From (H.4), (H.5), and (H.3), we have

σ : Γ |= S : {ϒ | ∃x:int.ϕ ∧ x 6= 0} (H.8)

From (H.1), we have σ : Γ |= S′ : {x:int.ϒ | ϕ}. Then, the goal from (H.7), (H.8), and IH.

Case (E-LOOPF). We have S1 = 0 . S2. The goal σ : Γ |= S2 : {ϒ | ∃x:int.ϕ ∧ x = 0} follows fromLemma 10 and (H.3).

Lemma 12. Suppose

x1 /∈ fvars(ϕ) (H.1)

x2 /∈ fvars(ϕ) (H.2)

Suppose also that

S′1 ` IS ⇓ S′2 and σ′ : Γ ,x2:T list |= S′1 : {x1:T .ϒ | ∃x:T list.ϕ ∧ x1 :: x2 = x} imply

σ′ : Γ ,x2:T list |= S′2 : {ϒ | ∃x:T list.ϕ ∧ x2 = x} (H.3)

for any S′1, S′2, and σ ′. If

S1 ` ITER IS ⇓ S2 (H.4)

σ : Γ |= S1 : {x:T list.ϒ | ϕ} (H.5)

then σ : Γ |= S2 : {ϒ | ∃x:T list.ϕ ∧ x = []}.

Proof. By induction on the derivation of (H.4). We conduct case analysis on the last rule that derives (H.4),which is either (E-ITERNIL) or (E-ITERCONS).Case (E-ITERNIL). We have

S1 = [].S2. (H.6)

The goal follows from Lemma 10 and (H.5).

Case (E-ITERCONS). We have

S1 =V1 :: V2 .S (H.7)

V1 .S ` IS ⇓ S′ (H.8)

V2 .S′ ` ITER IS ⇓ S2. (H.9)

Therefore, from (H.7), (H.5), and Lemma 10, we have

σ : Γ |= S : {ϒ | ∃x:T list.ϕ ∧ x =V1 :: V2}. (H.10)

Therefore, we have σ : Γ |= S : {ϒ | ∃x:T list,x1:T,x2:T list.ϕ ∧ x = x1 :: x2 ∧ x1 = V1 ∧ x2 = V2}and hence

σ [x2 7→V2] : Γ ,x2:T list |=V1 .S : {x1:T .ϒ | ∃x:T list.ϕ ∧ x = x1 :: x2} (H.11)

From (H.3) and (H.8), we have

σ [x2 7→V2] : Γ ,x2:T list |= S′ : {ϒ | ∃x:T list.ϕ ∧ x = x2}. (H.12)

From Lemma 10, we have

σ : Γ |=V2 .S′ : {x2:T list.ϒ | ∃x:T list.ϕ ∧ x = x2}. (H.13)

Knowing (H.2), we have

σ : Γ |=V2 .S′ : {x:T list.ϒ | ϕ} (H.14)

from (H.13). Now the goal follows from IH, (H.8), and (H.14).

Page 32: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

32 Yuki Nishida et al.

Lemma 13. If

y1 6= y2 (H.1)

y′1:T1,y1:T1 ` ϕ1 : ∗ (H.2)

y′1:T1,y2:T2 ` ϕ2 : ∗ (H.3)

〈IS〉 : T1→ T2 (H.4)For any V1,V2,σ ,

if V1 .‡ ` IS ⇓V2 .‡ and

σ : y′1:T1 |=V1 .‡ : {y1:T1 .‡ | y′1 = y1 ∧ϕ1}then σ : y′1:T1 |=V2 .‡ : {y2:T2 .‡ | ϕ2}

(H.5)

then Γ |= ∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ϕ1 ∧call(〈IS〉,y′1) = y2 =⇒ ϕ2 for any Γ .

Proof. By the definition of the semantics of call.

Lemma 14. If

V1 .‡ ` IS ⇓V2 .‡ (H.1)

V1 : T1 (H.2)

V2 : T2 (H.3)

〈IS〉 : T1→ T2 (H.4)

then Γ |= call(〈IS〉,V1) =V2 for any Γ .

Proof. By the definition of the semantics of call.

Lemma 15. If

Γ `Φ1 <: Φ2 (H.1)

σ : Γ |= S : Φ1 (H.2)

then σ : Γ |= S : Φ2.

Proof. Straightforward from Definition 4.

Theorem 16 (Soundness). The following two statements hold.

(1) If

Γ `Φ1 IS Φ2 (H.1)

S1 ` IS ⇓ S2 (H.2)

σ : Γ |= S1 : Φ1 (H.3)

then σ : Γ |= S2 : Φ2.(2) If

Γ `Φ1 I Φ2 (H.4)

S1 ` I ⇓ S2 (H.5)

σ : Γ |= S1 : Φ1 (H.6)

then σ : Γ |= S2 : Φ2.

Proof. The proof is done by mutual induction on the given derivation of (H.1) and (H.4).

Page 33: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 33

Case (RT-NOP). We have IS = {} and Φ1 = Φ2. The last rule that is used to derive (H.2) is (E-NOP).Therefore, we have S1 = S2, which is followed by (H.3).

Case (RT-SEQ). We have

IS = {I′; IS′} (H.7)

Γ `Φ1 I′ Φ ′ (H.8)

Γ `Φ′ IS′ Φ2 (H.9)

for some I′, IS′, and Φ ′. The last rule that derives (H.2) is (E-SEQ). Therefore, we have

S1 ` I′ ⇓ S′ (H.10)

S′ ` IS′ ⇓ S2 (H.11)

for some S′. Now we have

σ : Γ |= S′ : Φ′ (H.12)

by applying IH to (H.8), (H.10) and (H.3). Then, the goal follows by applying IH to (H.9), (H.11) and(H.12).

Case (RT-DIP). We have

I = DIP IS (H.13)

Φ1 = {x:T .ϒ | ϕ} (H.14)

Φ2 = {x:T .ϒ′ | ϕ ′} (H.15)

Γ ,x:T ` {ϒ | ϕ} IS {ϒ ′ | ϕ ′} (H.16)

for some IS, x, T , ϒ , ϒ ′, ϕ , and ϕ ′. The last rule that derives (H.5) is (E-DIP). Therefore, we have

S1 =V .S′1 (H.17)

S2 =V .S′2 (H.18)

S′1 ` IS ⇓ S′2 (H.19)

for some V , S′1, and S′2. By Lemma 10, we have

σ [x 7→V ] : Γ ,x:T |= S′1 : {ϒ | ϕ} (H.20)

from (H.6), (H.14), and (H.17). By applying IH to (H.16), (H.19), and (H.20), we have

σ [x 7→V ] : Γ ,x:T |= S′2 : {ϒ ′ | ϕ ′}. (H.21)

Therefore, σ : Γ |=V .S′2 : {x:T .ϒ ′ | ϕ ′} follows from (SEM-PUSH) and (H.21).

Case (RT-DROP). We have

I = DROP (H.22)

Φ1 = {x:T .ϒ | ϕ} (H.23)

Φ2 = {ϒ | ∃x:T.ϕ} (H.24)

for some x, T , ϒ , and ϕ . The last rule that derives (H.5) is (E-DROP). Therefore, we have

S1 =V .S2 (H.25)

for some V . From (H.6), (H.25), and Lemma 10, we have σ : Γ |= S2 : {ϒ | ∃x:T.ϕ ∧ x =V} and henceσ : Γ |= S2 : {ϒ | ∃x:T.ϕ} as required.

Page 34: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

34 Yuki Nishida et al.

Case (RT-DUP). We have

I = DUP (H.26)

Φ1 = {x:T .ϒ | ϕ} (H.27)

Φ2 = {x′:T . x:T .ϒ | ϕ ∧ x′ = x} (H.28)

x′ /∈ dom(Γ , x:T .ϒ ) (H.29)

for some x, x′, T , ϒ , and ϕ . The last rule that derives (H.5) is (E-DUP). Therefore, we have

S1 =V .S (H.30)

S2 =V .V .S (H.31)

for some V and S. By (H.6), we have σ [x 7→V ] : Γ ,x:T |= S : {ϒ | ϕ} and hence σ [x 7→V ][x′ 7→V ] : Γ ,x:T,x′:T |= S : {ϒ | ϕ}. Since σ [x 7→ V ][x′ 7→ V ] : Γ ,x:T,x′:T |= ϕ =⇒ (ϕ ∧ x = x′), we have σ [x 7→V ][x′ 7→ V ] : Γ ,x:T,x′:T |= S : {ϒ | ϕ ∧ x = x′}. Therefore, we have σ : Γ |= S2 : {ϒ | ϕ ∧ x = x′} asrequired.

Case (RT-SWAP). We have

I = SWAP (H.32)

Φ1 = {x:T . x′:T .ϒ | ϕ} (H.33)

Φ2 = {x′:T . x:T .ϒ | ϕ} (H.34)

for some x, x′, T , ϒ , and ϕ . The last rule that derives (H.5) is (E-SWAP). Therefore, we have

S1 =V .V ′ .S (H.35)

S2 =V ′ .V .S (H.36)

for some V , V ′, and S. By (H.6), we have σ [x 7→V ][x′ 7→V ′] : Γ ,x:T,x′:T |= S : {ϒ | ϕ}. Since x 6= x′,we have σ [x′ 7→V ′][x 7→V ] : Γ ,x′:T,x:T |= S : {ϒ | ϕ}. Therefore, we have σ : Γ |= S2 : {ϒ | ϕ}.

Case (RT-PUSH). We have

I = PUSHT V (H.37)

Φ1 = {ϒ | ϕ} (H.38)

Φ2 = {x:T .ϒ | ϕ ∧ x =V} (H.39)

V : T (H.40)

x /∈ dom(Γ ,ϒ ) (H.41)

for some x, T , ϒ , ϕ , and V . The last rule that derives (H.6) is (E-PUSH). Therefore, we have

S2 =V .S1. (H.42)

To show σ : Γ |= S2 : {x:T .ϒ | ϕ ∧x =V}, it suffices to show σ [x 7→V ] : Γ ,x:T |= S1 : {ϒ | ϕ ∧x =V}from (SEM-PUSH), which follows from σ : Γ |= S1 : {ϒ | ϕ}, (H.41), and σ [x 7→V ] : Γ ,x:T |= ϕ =⇒(ϕ ∧ x =V ).

Case (RT-NOT). We have

I = NOT (H.43)

Φ1 = {x:int.ϒ | ϕ} (H.44)

Φ2 = {x′:int.ϒ | ∃x:int.ϕ ∧ (x 6= 0∧ x′ = 0∨ x = 0∧ x′ = 1)} (H.45)

x′ /∈ dom(Γ , x:int.ϒ ) (H.46)

Page 35: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 35

for some x, ϒ , and ϕ . The last rule that derives (H.5) is either (E-NOTT) or (E-NOTF). We only showthe case of (E-NOTT); (E-NOTF) is similar. In this case, we have

S1 = i.S (H.47)

i 6= 0 (H.48)

S2 = 0.S. (H.49)

for some i and S. By (H.6), we have

σ [x 7→ i] : Γ ,x:T |= S : {ϒ | ϕ}. (H.50)

From Lemma 10, we have

σ : Γ |= S : {ϒ | ∃x:T.ϕ ∧ x = i}. (H.51)

From i 6= 0, we have σ : Γ |= S : {ϒ | ∃x:T.ϕ ∧ x 6= 0}. From (H.46), we have σ [x′ 7→ 0] : Γ ,x′:T |=S : {ϒ | ∃x:T.ϕ ∧ x 6= 0}. From σ [x′ 7→ 0] : Γ ,x′:T |= (∃x:T.ϕ ∧ x 6= 0) =⇒ (∃x:T.ϕ ∧ x 6= 0∧ x′ = 0),we have σ [x′ 7→ 0] : Γ ,x′:T |= S : {ϒ | ∃x:T.ϕ ∧ x 6= 0∧ x′ = 0}. Therefore, we have σ : Γ |= 0.S : {x′:T .ϒ | ∃x:T.ϕ ∧x 6= 0∧x′ = 0}, which is followed by σ : Γ |= 0.S : {x′:T .ϒ | ∃x:T.ϕ ∧ ((x 6= 0∧x′ =0)∨ (x = 0∧ x′ 6= 1))} as required.

Case (RT-ADD). We have We have

I = ADD (H.52)

Φ1 = {x1:int. x2:int.ϒ | ϕ} (H.53)

Φ2 = {x3:int.ϒ | ∃x1:int,x2:int.ϕ ∧ x1 + x2 = x3} (H.54)

x3 /∈ dom(Γ , x1:int. x2:int.ϒ ) (H.55)

for some x1, x2, x3, ϒ , and ϕ . The last rule that derives (H.5) is (E-ADD). Therefore,

S1 = i1 . i2 .S (H.56)

S2 = i3 .S (H.57)

i1 + i2 = i3 (H.58)

for some i1, i2, i3, and S. By (H.6), we have

σ [x1 7→ i1][x2 7→ i2] : Γ ,x1:int,x2:int |= S : {ϒ | ϕ}. (H.59)

From (H.55), we have

σ [x1 7→ i1][x2 7→ i2][x3 7→ i3] : Γ ,x1:int,x2:int,x3:int |= S : {ϒ | ϕ}. (H.60)

From σ [x1 7→ i1][x2 7→ i2][x3 7→ i3] : Γ ,x1:int,x2:int,x3:int |= ϕ =⇒ ϕ ∧ x1 + x2 = x3, we have

σ [x1 7→ i1][x2 7→ i2][x3 7→ i3] : Γ ,x1:int,x2:int,x3:int |= S : {ϒ | ϕ ∧ x1 + x2 = x3}. (H.61)

and therefore σ [x1 7→ i1][x2 7→ i2] : Γ ,x1:int,x2:int |= i3 . S : {x3:int .ϒ | ϕ ∧ x1 + x2 = x3}. ByLemma 10, we have σ : Γ |= i3 .S : {x3:int.ϒ | ∃x1:int,x2:int.ϕ ∧ x1 + x2 = x3} as required.

Case (RT-PAIR). Similar to the case for (RT-ADD).

Case (RT-CAR). We have

I = CAR (H.62)

Φ1 = {x:T1×T2 .ϒ | ϕ} (H.63)

Φ2 = {x1:T1 .ϒ | ∃x:T1×T2,x2:T2.ϕ ∧ x = (x1,x2)} (H.64)

x1 6= x2 (H.65)

x1 /∈ dom(Γ , x:T1×T2 .ϒ ) (H.66)

x2 /∈ dom(Γ , x:T1×T2 .ϒ ) (H.67)

Page 36: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

36 Yuki Nishida et al.

for some x, x1, x2, T1, T2, ϒ , and ϕ . The last rule that derives (H.5) is (E-CAR). Therefore,

S1 = (V1,V2).S (H.68)

S2 =V1 .S (H.69)

for some V1, V2, and S. By (H.6), we have

σ [x 7→ (V1,V2)] : Γ ,x:T1×T2 |= S : {ϒ | ϕ} (H.70)

and hence

σ : Γ |= S : {ϒ | ∃x:T1×T2.ϕ ∧ x = (V1,V2)}. (H.71)

σ : Γ |= S : {ϒ | ∃x:T1×T2.ϕ ∧ x = (V1,V2)} implies σ : Γ |= S : {ϒ | ∃x:T1×T2,x1:T1,x2:T2.ϕ ∧ x =(x1,x2)∧x1 =V1∧x2 =V2}. Therefore, σ : Γ |= S : {ϒ | ∃x1:T1.(∃x:T1×T2,x2:T2.ϕ)∧x1 =V1}, whichimplies σ : Γ |=V1 .S : {ϒ | ∃x:T1×T2,x2:T2.ϕ} as required.

Case (RT-CDR). Similar to the case for (RT-CAR).

Case (RT-NIL). Similar to the case for (RT-PUSH).

Case (RT-CONS). Similar to the case for (RT-PAIR).

Case (RT-IF). We have

I = IF IS1 IS2 (H.72)

Φ1 = {x:int.ϒ | ϕ} (H.73)

Γ ` {ϒ | ∃x:int.ϕ ∧ x 6= 0} IS1 Φ2 (H.74)

Γ ` {ϒ | ∃x:int.ϕ ∧ x = 0} IS2 Φ2 (H.75)

for some IS1, IS2, x, ϒ , and ϕ . The last rule that derives (H.5) is (E-IFT) or (E-IFF). We conduct caseanalysis.SCase (E-IFT). For some i and S,

i 6= 0 (H.76)

S1 = i.S (H.77)

S ` IS1 ⇓ S2. (H.78)

From (H.6), (H.73), (H.77), and Lemma 10, we have

σ : Γ |= S : {ϒ | ∃x:int.ϕ ∧ x = i}. (H.79)

From (H.76), we have

σ : Γ |= S : {ϒ | ∃x:int.ϕ ∧ x 6= 0}. (H.80)

Then, the goal follows From IH and (H.74).

SCase (E-IFF). Similar to the case for (E-IFT).

Case (RT-LOOP). We have

I = LOOP IS (H.81)

Φ1 = {x:int.ϒ | ϕ} (H.82)

Φ2 = {ϒ | ∃x:int.ϕ ∧ x = 0} (H.83)

Γ ` {ϒ | ∃x:int.ϕ ∧ x 6= 0} IS {x:int.ϒ | ϕ} (H.84)

S1 = i.S (H.85)

Page 37: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 37

for some IS, x, ϒ , S, and ϕ . By IH and (H.84), we have

For any S′1,S′2, if S′1 ` IS ⇓ S′2 and σ : Γ |= S′1 : {ϒ | ∃x:int.ϕ ∧ x 6= 0},

then σ : Γ |= S′2 : {x:int.ϒ | ϕ}. (H.86)

Then, the goal follows from Lemma 11, (H.5), (H.6), (H.81), (H.82) and (H.86).

Case (RT-IFCONS). Similar to the case for (RT-IF).

Case (RT-ITER). We have

I = ITER IS (H.87)

Φ1 = {x:T list.ϒ | ϕ} (H.88)

Φ2 = {ϒ | ∃x:T list.ϕ ∧ x = []} (H.89)

Γ ,x2:T list ` {x1:T .ϒ | ∃x:T list.ϕ ∧ x1 :: x2 = x} IS {ϒ | ∃x:T list.ϕ ∧ x2 = x} (H.90)

x1 6= x2 (H.91)

x1 /∈ dom(Γ , x:T list.ϒ ) (H.92)

x2 /∈ dom(Γ , x:T list.ϒ ) (H.93)

for some IS, x, x1, x2, T , ϒ , and ϕ . From IH and (H.90), we have

For any S′1,S′2,σ

′, if S′1 ` IS⇓ S′2 and σ′ : Γ ,x2:T list |= S′1 : {x1:T .ϒ | ∃x:T list.ϕ∧x1 :: x2 = x},

then σ′ : Γ ,x2:T list |= S′2 : {ϒ | ∃x:T list.ϕ ∧ x2 = x}. (H.94)

Then, the goal follows from Lemma 12. (H.93), and (H.94).

Case (RT-LAMBDA). Since the last rule that derive (H.5) is (E-LAMBDA), we have

I = LAMBDAT1 T2 IS (H.95)

Φ1 = {ϒ | ϕ} (H.96)

Φ2 = {x:T1→ T2 .ϒ | ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ϕ1 ∧call(x,y′1) = y2 =⇒ ϕ2} (H.97)

y′1:T1 ` {y1:T1 .‡ | y′1 = y1 ∧ϕ1} IS {y2:T2 .‡ | ϕ2} (H.98)

x /∈ dom(Γ ,ϒ )∪{y1,y′1,y2} (H.99)

y1 6= y′1 (H.100)

y′1:T1,y1:T1 ` ϕ1 : ∗ (H.101)

S2 = 〈IS〉.S1 (H.102)

for some IS, x, y1, y′1, y2, T1, T2, ϒ , ϕ , ϕ1, and ϕ2. From IH and (H.98), we have

For any V1,V2,σ , if V1 .‡ ` IS ⇓V2 .‡ and σ : y′1:T1 |=V1 .‡ : {y1:T1 .‡ | y′1 = y1 ∧ϕ1},then σ : y′1:T1 |=V2 .‡ : {y2:T2 .‡ | ϕ2}. (H.103)

By Lemma 13, (H.100), (H.101), (H.103), (H.91), and (H.92), we have

Γ ,ϒ |= ∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ϕ1 ∧call(〈IS〉,y′1) = y2 =⇒ ϕ2 (H.104)

and hence, from (H.6), (H.96), and (H.104), we have

σ : Γ |= S1 : {ϒ | ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ϕ1 ∧call(〈IS〉,y′1) = y2 =⇒ ϕ2}. (H.105)

By (H.99), and (H.105), we have

σ : Γ |= S1 : {ϒ | ∃x:T1→ T2.(ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ϕ1 ∧call(x,y′1) = y2 =⇒ ϕ2)∧ x = 〈IS〉}. (H.106)

Page 38: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

38 Yuki Nishida et al.

Therefore, from Lemma 10 and (H.106), we have

σ : Γ |= 〈IS〉.S1 : {x:T1→ T2 .ϒ | ϕ ∧∀y′1:T1,y1:T1,y2:T2.y′1 = y1 ∧ϕ1 ∧call(x,y′1) = y2 =⇒ ϕ2}

as required.

Case (RT-EXEC). We have

I = EXEC (H.107)

Φ1 = {x1:T1 . x2:T1→ T2 .ϒ | ϕ} (H.108)

Φ2 = {x3:T2 .ϒ | ∃x1:T1,x2:T1→ T2.ϕ ∧call(x2,x1) = x3} (H.109)

x3 /∈ dom(Γ , x1:T1 . x2:T1→ T2 .ϒ ) (H.110)

for some x1, x2, x3, T1, T2, ϒ , and ϕ . Since the last rule that derives (H.5) is (E-EXEC), we have

S1 =V1 . 〈IS〉.S (H.111)

S2 =V2 .S (H.112)

V1 .‡ ` IS ⇓V2 .‡ (H.113)

for some V1, V2, IS, and S. By (H.6), (H.108), and (H.111), we have

σ : Γ |= S : {ϒ | ∃x2:T1→ T2.(∃x1:T1.ϕ ∧ x1 =V1)∧ x2 = 〈IS〉}. (H.114)

By Lemma 14, (H.113), (H.105), and (H.51), we have

Γ ,ϒ |= call(〈IS〉,V1) =V2. (H.115)

Therefore, from (H.114), and (H.115), we have

σ : Γ |= S : {ϒ | (∃x2:T1→ T2.(∃x1:T1.ϕ ∧ x1 =V1)∧ x2 = 〈IS〉)∧call(〈IS〉,V1) =V2}. (H.116)

From (H.110) and (H.116), we have σ : Γ |= S : {ϒ | ∃x3:T2.(∃x1:T1,x2:T1 → T2.ϕ ∧ call(x2,x1) =x3)∧ x3 = V2} and hence σ : Γ |= V2 . S : {x3:T2 .ϒ | (∃x1:T1,x2:T1 → T2.ϕ ∧call(x2,x1) = x3)} asrequired.

Case (RT-TRANSFERTOKENS). We have

I = TRANSFER_TOKENST (H.117)

Φ1 = {x1:T . x2:int. x3:address.ϒ | ϕ} (H.118)

Φ2 = {x4:operation.ϒ | ∃x1:T,x2:int,x3:address.ϕ ∧ x4 = Transfer(x1,x2,x3)} (H.119)

x4 /∈ dom(Γ , x1:T . x2:int. x3:address.ϒ ) (H.120)

for some x1, x2, x3, x4, T , ϒ , and ϕ . The last rule that derives (H.5) is (E-TRANSFERTOKENS); therefore

S1 =V . i.a.S (H.121)

S2 = Transfer(V, i,a).S (H.122)

for some V , i, a, and S. By (H.6), (H.118), and (H.121), we have

σ : Γ |= S : {ϒ | ∃x3:address.(∃x2:int.(∃x1:T.ϕ ∧ x1 =V )∧ x2 = i)∧ x3 = a}. (H.123)

By (H.120) and (H.123), we have

σ : Γ |= S : {ϒ | ∃x4:operation.(∃x1:T,x2:int,x3:address.ϕ ∧x4 = Transfer(x1,x2,x3))∧ x4 = Transfer(V, i,a)}. (H.124)

Page 39: arXiv:2108.12971v1 [cs.CL] 30 Aug 2021

HELMHOLTZ: A Verifier for Tezos Smart Contracts Based on Refinement Types 39

Therefore, we have

σ : Γ |= Transfer(V, i,a).S : {x4:operation.ϒ | (∃x1:T,x2:int,x3:address.ϕ ∧x4 = Transfer(x1,x2,x3))}

as required.

Case (RT-SUB). We have

Γ `Φ1 <: Φ′1 (H.125)

Γ `Φ′2 <: Φ2 (H.126)

Γ `Φ′1 I Φ

′2 (H.127)

for some Φ ′1 and Φ ′2. By Lemma 15, (H.6), and (H.125), we have

σ : Γ |= S1 : Φ′1. (H.128)

By IH, (H.127), (H.5), and (H.128), we have

σ : Γ |= S2 : Φ′2. (H.129)

Then, the goal follows from Lemma 15.