A simulator of Solidity-style smart contracts in the theorem prover Agda

This paper extends the previous paper [6] by implementing two blockchain simulators of Solidity-style smart contracts – a simple and a complex one, using the interactive theorem prover Agda. In the previous article [6], we built a simple and complex abstract model of Solidity-style smart contracts in Agda. These models had many features, such as calling different smart contracts, supporting the ability to call different smart contracts, and providing simple and complex instructions. Because of the use of coalgebras for representing smart contracts they supported loops and conditionals, using the support of those features for coalgebraic programs in Agda. The complex model supported gas costs and pure functions, similar to the Solidity language. In this paper, we implement and design interfaces which allow to interactively interact with users in the simple and complex models. This makes use of the fact that Agda is as well a dependently typed programming language. Therefore we can write interactive programs which are running in the same language in which we will in a future next step verify smart contracts, avoiding the translation of programs which could be a source of errors. The simple blockchain simulator we have created can call other contracts, transfer funds to specific contracts, and update contracts. The complex blockchain simulator has in addition features that can deal with more complex blockchain instructions, support gas costs, and evaluate and update pure functions.


INTRODUCTION
Blockchains are distributed digital ledgers that operate without a centralised authority.A blockchain is a form of database that is decentralised, reliable, and difficult to utilise for fraud.Once a transaction publishes in the blockchain network, no one can change it except by changing the ledger, e.g., by a 50% attack, which is unlikely in the case of Ethereum.
Blockchain technology is the foundation for Ethereum [13] and other cryptocurrencies, enabling peer-to-peer transactions through a distributed public record.The technology supports other applications as well, such as smart contracts [40].Thus, the technology offers countless possibilities.
Ethereum was first suggested by Vitalik Buterin [13] in 2013.Since its launch in 2015, the project has rapidly risen to become one of the most widely used blockchain platforms.Ethereum is used to build many decentralised applications (DApps).
Smart contracts in blockchain are programs that automatically run when specific criteria are met [26].Due to the immutability of blockchains, once a smart contract has been issued, it cannot be modified in any way.Therefore, before deploying smart contracts, it is necessary to verify their correctness and security to avoid possibly triggering substantial financial losses.
Many languages can be used to write Ethereum's smart contracts, such as Solidity [15] and Vyper [41], both high-level languages.Other cryptocurrencies have their own languages [9].
Formal verification is one way to detect weaknesses and vulnerabilities at an early stage in smart contracts.It is also a promising way to provide security guarantees by mathematically verifying designs that use various mathematical and logical methods [22].There are many approaches to formal verification, such as theorem proving [21] and modelling checking [33].In theorem proving, it is possible to verify the correctness of different types of systems, such as smart contracts [21].
In our approach, we use the interactive theorem prover Agda [3] to execute the blockchain simulator.One interesting aspect of Agda is that it can be used as a dependently typed programming language [34].Therefore, together with the fact that it is also a theorem prover, Agda allows the writing of verified programs, where the verification takes place in the same language in which the program is written.This prevents translation problems that might arise because the verified program differs from the implemented one.
However, what we do not do at this stage is to translate solidity programs automatically into Agda, this is done manually.
As a background for this paper we note as well that the main unit of the Ethereum currency is ether, where 1 ether = 10 18 wei.
The remaining parts of this paper are as follows: In Sect.2, we present related work.In Sect.3, we briefly introduce the proof assistant Agda and the library used to build our interface.In Sect.4, we implement and design two simulators of Solidity-style smart contracts.We end with a conclusion and future work in Sect. 5.
Git repository.This work was created and formalised using the proof assistant Agda.All of the Agda code shown in this paper was derived from the type-checked Agda code.The source code can be found in [37].The git repository also contains a pdf file [8] that explains how to translate Solidity code manually into Agda.There we show as well how to deal with arithmetic overflow -in the main paper for brevity we use unrestricted integers.

RELATED WORK
In this section, we describe the work that have focused on verifying, formalizing, and analyzing smart contracts using theorem provers such as Agda [3], Coq [11,14], and Isabelle/HOL [24].Then, we provide research using model checking, tools, and a framework to analyze and verify smart contracts.Our earlier articles [5][6][7] analysed the relevant literature in more depth, this part only provides a brief update.Some efforts verify smart contracts using the theorem prover.Ayoade et al. [10] proposed and developed a framework for rewriting Ethereum bytecode without access to the source code.Their approach enables bytecode modifications to Ethereum without a high-level language's source code.They used the Coq theorem prover to implement and verify the Ethereum virtual machine code.Zheng et al. [42] developed Lolisa, an intermediate specification language for Ethereum smart contracts in Coq.Lolisa has a major subset of Ethereum's Solidity programming language in its formal syntax and semantics.Lolisa's formal syntax uses a stronger static type system than Solidity to improve type safety.Lolisa also incorporates general-purpose programming language capabilities and a substantial fraction of Solidity syntax components.Thus, translating Solidity programs into Lolisa is possible.Lolisa is naturally generalizable and can express various programming languages.Finally, Coq interprets Lolisa's syntax and semantics.Thus, Coq can execute and verify Lolisa's smart contracts symbolically.In [5,7], we have proved the correctness of Bitcoin script in Agda and developed a methodology to obtain a human-readable weakest precondition of Hoare logic.This helps to fill the validation gap between user needs and the formal specification of smart contracts.We have applied this methodology to two standard scripts, Pay to Public Key Hash (P2PKH) and Pay to Multisig (P2MS).Marmsoler et al. [27] propose an executable denotational semantics of Solidity in the Isabelle/HOL proof assistant.Their formal semantics creates the groundwork for an interactive program verification environment for the Solidity program and enables checking Solidity programs by symbolic execution.
Many types of research analyse and verify smart contracts using frameworks, model checking, and tools.Mavridou et al. [29] developed FSolidM, a framework for creating more secure contracts on ETH via a graphical interface for developing finite state machines that can immediately be converted into ETH smart contracts.Nam et al. [32] presented a novel formal verification approach using an alternating-time temporal logic (ATL) model to investigate blockchain smart contracts developed through solidity.They used MCMAS [25], an effective ATL model checker that verifies multiagent systems.They aimed at identifying subtle defects in real smart contracts.So et al. [38] presented a static analysis tool, VeriSmart, to ensure the arithmetic safety of Ethereum smart contracts.They focused on detecting arithmetic bugs, such as integer over/underflows and division-by-zeros, because smart contracts typically involve many arithmetic operations which are major sources for security vulnerabilities.

BACKGROUND 3.1 A brief introduction to the Agda theorem prover
Agda [12,39] is a language that implements Martin-Löf's type theory [28], with expanding records and modules.It serves as a functional programming language and proof assistant.
In the following we provide a concise overview of Agda; more comprehensive information on the proof assistant Agda is available in our previous papers [5][6][7].
Agda's user interface is based on Emacs, which has been beneficial for interactively developing and verifying proofs [12].When using Agda, programmers can write their code incrementally, allowing them to leave certain parts unfinished.With the help of Agda's type-checking tool, they can receive useful guidance on completing these sections step by step.
Agda is based on dependent types.( : ) →  is the type of functions which takes an element  :  and map it to an element of , where  may depend on .Agda supports hidden arguments with syntax { : } →  -in this case we can omit the application of the function to its argument, if it can be inferred uniquely by the compiler.If it cannot be inferred, one can provide the hidden argument explicitly, writing  {} for the application of  to hidden argument .Nondependent function types are instances of dependent types with no dependency, and we write  →  for the type of functions from  to .In Agda one writes ∀ →   ( : ) →  and ∀{ }→ for { : }→, if  can be inferred uniquely by Agda.Furthermore, _ denotes arguments which are not used, or can be inferred uniquely.
Apart from dependent function types, Agda supports inductive (data) types and record types, where the latter can be coinductive types.As an example of a data type, we define ErrorMsg, which will be used in the simple simulator later in this article.It can be used to describe a variety of error messages as follows: data ErrorMsg : Set where strErr : String → ErrorMsg numErr : N → ErrorMsg undefined : ErrorMsg We define three different error message constructors in the Er-rorMsg data type.The strErr constructor is used for error messages given by a string, numErr is used for error messages given by a natural number, and undefined is used for reporting the error message "undefined".We can define functions by using pattern matching on these constructors, e.g.errorMsg2Str : ErrorMsg → String errorMsg2Str (strErr s) = s errorMsg2Str (numErr n) = show n errorMsg2Str undefined = "undefined" The function errorMsg2Str converts an error into a readable string suitable for printing or displaying to users.
An example of a record type is as follows: record Record types can refer directly or indirectly via other types to themselves.If we add the word coinductive, then an element of it can be defined using copattern matching by using full recursion referring to itself, as long as in the chain from on element to itself there is at least one observation.This allows to define coalgebras in Agda.They are infinite structures which don't break normalisation in Agda (which means every term in Agda has finite normal formal), because in order to unfold a term one needs to apply one of the observations (fields) of the record type.For more details see [1].
Agda employs different levels of types, with the smallest level being called Set for historical reasons [23,30].In this article, we use apart from Set the next higher type level Set 1 .Set 1 encompasses all sets (via an explicit embedding), but as well Set itself and types formed from it such as Set → Set.

Interface Library
The representation of interactive programs as the IO monad [31] in dependent type theory was developed by the second author and Peter Hancock in a sequence of articles [16][17][18][19][20], see as well [1,Sect. 4].All the Agda code in this section is taken from [2, Sect.4] (with minor modifications).Interaction between a program with, for example, an operating system dealing with IO can be created as a series of commands (elements of Command) issued by the program to the operating system.For each of these commands the operating system returns a response (an element of Response).The type Response will depend on the command being issued.As shown in Figure 1, the interactive program gives a question to the world using a command, and the world answers with some response.Then the next command is issued depending on that response etc.In our interactive program interactions consist of input and output of strings.We use the interface ConsoleCommand to deal with console interface, which has two commands getLine and putStrLn.The command getLine : ConsoleCommand has no argument, and reads a user input line.The response returned by the system is the String typed in by the user, therefore we define ConsoleResponse getLine = String.The command putStrLn command has one argument of type String, namely the string to be printed, so we define putStrLn : String → ConsoleCommand.
The response is just the information that the string has been printed (we assume this command always succeeds so there is no error message), so the information is the void information given by the one element type Unit, and we define putStrLn s = Unit.
The complete definition is as follows (Source: [2]): The console interface consoleI is the interface consisting of Con-soleCommand and ConsoleResponse (Source: [2]): We define the set of interactive programs generically for any IOInterface.We will abstract from it, which is written in Agda by using the lines (Source: [2]) This line unpacks as well the interface into its two commands: the set of commands (the set ) and the response set  of the abstracted interface  .
We now define the type IO of interactive programs mutually recursively as a coinductive record IO together with the data type IO'.This definition is coinductive, since interactive program in principal are allowed to run an infinite non-terminating sequence of interactions.In accordance with Moggi's IO monad [31], interactive programs may as well terminate, returning an element of type .We use here sized types, which allow to define elements of coalgebras in a more generic way which without sized types would be rejected in this form by Agda's termination checker, even though they are productive.See [1,Sect. 6] for a detailed explanation of sized types.As a first approximation the user might ignore all arguments referring to the type Size in the following (most elements of type Size will be inferred automatically by Agda when writing Agda code).One could view sized types as a form of gas, where a program of size  is allowed to be unfolded at most  times.
IO has a field (or observation) force, which returns an element of type IO'.It has as well for convenience a lazy constructor delay which turns an element  of IO' into an element of IO' A, namely an element, which when we apply .force to it returns .Elements of IO' are either terminating programs return' , returning an element of type .Or they are of the form exec' c p, which means they execute command  : , and continue, if a response  :   is returned, executing program   .
The full definition is as follows (Source: [2]): record IO (i : Size) (A : Set) : Set where coinductive constructor delay field force : {j : Size< i} → IO' j A data IO' (i : Size) (A : Set) : Set where exec' : Note that elements of IO are not directly of the form (return' ) or (exec'  ) -instead we need to apply observation .force to it in order to unfold it into one these two choices.Otherwise an element of IO representing a infinite sequence of interactions would reduce to an infinite term, whereas Agda requires each correctly typed term to reduce to a finite normal form.In order to unfold an IO' once, we need to pay the price of applying once .force to it, breaking a possibly infinite reduction sequence.
We define the monad operation bind [31] for the IO monad in order to combine programs as follows (Source: [2]): The program  ≫=  first executes program .If it terminates, returning  : , then it continues executing  .If that program terminates the overall program terminates as well, returning the response returned when executing  .

A SIMULATOR OF SOLIDITY-STYLE SMART CONTRACTS IN AGDA 4.1 Simulator of the simple model
In the previous work [6], we developed a simple model of Soliditytype smart contracts.This model had as commands transferc for transferring money from one contract to another, callc for calling a function (in object-orientation terminology a method) of another contract, updatec for updating one of the functions of the contract, we are making the call from.In addition we had commands for looking up the address of the current contract and the balance (wei) of any contract.
These commands formed the set of commands (CCommands) of an interactive program.We defined for each command the set of possible responses CResponse returned in response to issuing this command.Since CResponse depends on CCommands, it's type is that of a function from CCommands to the type of sets Set.
Smart contracts are given as elements of a coinductive type Contract.They possibly infinitely often issue a command (element of CCommands) and depending on the response (CResponse) obtained when executing it execute the next command.They are similar to the interactive programs described in Sect.3.2, however the commands are executed on the ledger instead of asking the user for a response via an operating system.The resulting responses are obtained from the ledger, and the ledger changes as a result of the execution.The execution forms an object-model of Ethereum, similar to the object models developed by the second author with coauthors in [1,35].Smart contracts are given as coinductive interactive programs, executing this sequence of commands, with the commands computed depending on the response to the previous command.In Agda they are represented as a coinductive record type SmartContract (in [6] we called it SmartContractExec, but thought this shorter name improves readability of this paper).SmartContract is defined similar to the type of interactive programs, but it is running on the ledger instead of an operating system.
Contracts (Contract) are records consisting of the balance (amount of wei) given as a natural number and a function from function names to SmartContract.A ledger (Ledger) is a function from addresses (natural numbers) to Contract.
In this section we build, based on our previous work [6], a simulator of the simple model of Solidity-style smart contracts.The simple simulator supports the above mentioned operations, i.e. calling functions from other contracts, updating functions, transferring funds, and obtaining the money balance in other smart contract.However, at this level, the simple simulator does not include an explicit cost of gas -that will be included in the complex model.Without gas, execution of smart contracts may not terminate.This is reflected by the fact that in the file Ledger-Simple-Model.agda in the git repository [37], two auxiliary functions used in the evaluation of smart contracts are under the pragma {-# NON_TERMINATING #-} Agda requires that the programs terminate in order to be consistent as a theorem prover, and uses a termination checker to check for termination.Using this pragma, we can break the termination checker.Because of these nontermination problems, the simulator, which makes use of the evaluation function, is not guaranteed to terminate (an example would be a contract calling itself with the same argument it is called).This problem will be repaired in the complex model, where we add an explicit gas limit.We could as well restrict in the simple model the number of recursive calls to a certain number, having the effect of a simple form of gas limit.
Arguments of functions and the results returned will be serialised messages, which allows the encoding of complex data structures as byte arrays.In order to abstract from the process of serialisation, we use a data type Msg representing serialised messages: The simplest elements of it are of the form nat , representing the serialised number .All inputs of functions will be elements of Msg and outputs will be elements of MsgOrError (which includes in addition to Msg an error element).In Solidity, numbers are restricted integers or restricted positive integers.Integers can be, for instance, represented as pairs consisting of a Boolean value representing the sign and number, whereas Booleans can be represented as the natural numbers 0 and 1.When sending or receiving elements of these data types, one needs in Agda to check that the numbers are in the range given by the data type and, if they are not, deal with it according to what is done in Solidity, which means for the current version 0.8.21, to raise an exception.
In the simple model, variables are represented as constant functions, which returns the value of the variable.In order to change a variable, we update the function representing it.
We illustrate the simulation interface by referring to the following example testLedger.It will have only one defined contract at address 1 which has a variable "counter", and a function to increment this variable by 1.In order to demonstrate other features, we have as well a function which transfers 10 wei to contract 0.
The definition of testLedger is as follows: In testLedger, we define a contract at address 1.We set the balance (field amount) to 40.We have 3 functions: The first function "counter" represents a variable.This variable is initialised with the value nat 0.
The second function is "increment", which will increment the variable of "counter" by 1.In order to be independent of the address, on which it is deployed, it will look up the current address (which returns 1).Then it will lookup the value of the variable by applying the function "counter" to an arbitrary value (we choose nat 0).The function might return any serialised message, but it should, if correctly used, return a serialised number.So we make a case distinction on the serialised message: if it is of the form (nat n), we update "counter" to the constant function returning (nat (suc n)).Otherwise we return an error message, since "counter" returned not a serialised number.The final function is "transfer", which will transfer 10 wei to Address 0. All other contracts are initialised to have a balance of 0, with all functions being undefined, i.e. returning an error message ("Undefined").In the same way, all other functions (given by other strings) of contract 1 apart from the three functions mentioned before return the same error message.We use here the fact that in Agda patterns are evaluated in sequence.The first matching pattern will be used to determine the result, and any future pattern after a matching pattern will be ignored.So the line testLedger ow .amount=0 applies to all arguments  (for otherwise) which haven't been covered by a previous pattern, in this case all natural numbers except for 1.
Next, we develop our interface menu (mainBody) of the simple simulator Solidity-style smart contract, which has four options a user can select from to interact with the ledger, as shown in Figure 2.These options are as follows: "Option 1", execute a function of a contract; in case of our example testLedger, we can look up the value of "counter", by executing it, increment that variable by 1, or execute the transfer given; "Option 2" allows changing the calling address from which other contracts are called (the initial value used is 0); "Option 3" looks up the balance of any contract; and "Option 4" terminates the program.then executeLedgercheckamount ledger callAddr else (if str == "3" then executeLedgerChangeCallingAddress ledger callAddr else (if str == "4" then WriteString "The program will be terminated" else WriteStringWithCont "Please enter 1,2,3 or 4"  _ → mainBody ledger callAddr)))) We define mainBody mutually recursively with auxiliary functions for the different options.In case of "Option 1" these are executeLedgerStep2 -executeLedgerStep5.Function executeLedger asks a user to enter the calling address, i.e. the contract of which we want to execute a function.Then executeLedgerStep2 will check whether the result was a number.If yes, it asks for the function name to be executed (given as a string) After that, executeLedger-Step2 will call executeLedgerStep3 to ask the user to enter the argument of the function name as a natural number (we currently only support arguments of functions which are serialised natural numbers, in a future version, we will allow arbitrary serialised messages as inputs).Then, executeLedgerStep4 will check whether the user entered indeed a number, and if yes, return the result of evaluating the function applied to the message using executeLedgerStep5 and return to the start menu.Here the result returned will be the number returned if it was a number, a message indicating it was a list, if the result was a list, and otherwise, the error message.Note that in case of an error the ledger will return to its initial state except for gas used in the failed execution being deducted.
When converting a user input to a natural number, we get an element of Maybe N having elements (just ) for a successful converted natural number and nothing, if the string was not a natural number.Our code makes therefore a case distinction on whether the result of that conversion was nothing or (just ).
For example, as shown in Figure 3, we selected "Option 1", and execute at address 1 function "counter" with argument 1.The result is nat 0 (returning the content of the variable counter).In case of "Option 2", the program will ask for the address to look up the balance for, print out the result and return to the starting menu.
For example, as shown in Figure 4, when selecting "Option 2" and entering the calling Address 1, the result will be the available money, 40 wei, in Address 1, and it will return to the main interface.For "Option 3", which is defined by function executeLedger-ChangeCallingAddress, the system asks for the new calling address, and once obtained executes the same code as for "Option 1".
For instance, as shown in Figure 5, when selected, "Option 3" will ask to enter the new calling address; in our case, we enter the new calling address 1, the function "increment", and the function's argument as 0. The result will be (nat 1), and the operation increments the variable "counter" to 1.The main function serves as the entry point when executing the Agda program.It is in charge of starting the program and executing its main logic.In this scenario, the main function applies mainBody to the testLedger and starts with calling Address set to 0. This creates an interactive program.run translates it into a native IO program.Agda's compiler MAlonzo [4] will then create an interactive program.The compiled executable will execute the interactive program as described above.

Simulator of the complex model
In the previous work [6], we developed the complex model.One main feature is that it has in addition to normal functions pure functions.Pure functions are given as functions of type Msg → MsgOrError Pure functions don't interact with other functions or make updates, and directly compute from its input either the result or an error.Pure functions can be updated from the contract they belong to.Standard functions get a new command updatec which allows to update a pure function by referring to its previous definition.This is useful to represent maps in Solidity, which are finite functions from input to output.We represent maps as pure functions.We can update them for one argument to a new value, by checking whether the argument is equal to the updated argument (in which case we return the updated result) or not (in which case we return the result of the previous version of this function).
Another addition of the complex model is the use of gas cost.Since we cannot control the cost of execution of functions in Agda from Agda, we require that the user states the cost for computing the various operations explicitly as part of all commands of normal functions.Note that the main purpose of the model is to verify smart contracts.Whether a contract is correct depends on making realistic choices for the gas cost.
The main change to accommodate the gas cost is in the following definition of the commands for the complex model: data SmartContract (A : Set) : Set where return : The constructor return for terminating a SmartContract has an extra argument of type N which determines the cost for computing the result -the value returned could be computed by a very time consuming computation, and the argument states that cost.The constructor exec, which creates an program which executes one command and depending on the result returned executes a continuing SmartContract has an extra argument of type CResponse c → N which determines the cost of computing the continuation.The gas cost of each instruction will be at least one (technically we increment any gas cost stated by one), and therefore termination is guaranteed.The code actually passes Agda's termination checker, overcoming the obstacle of nontermination of the simple model, which compromises consistency of the theorem prover.
In the complex model SmartContract has new commands data CCommands : Set where callPure : Furthermore, we add an explicit error command raiseException with an explicit cost and error message.Using the implementation of the complex model in our previous work [6], we expand the simple simulator into the complex one, adding more complex options for the user: to evaluate pure functions, and to change and check the gas limit.
In order to demonstrate our interface, we develop a simple voting example (testLedger).The current example has only one candidate.We leave it to the user to enhance this example to a more advanced one involving multiple candidates (by making the counter and vote functions depend on a candidate number).
The definition of testLedger is as follows: testLedger 1 .amount= 100 testLedger 1 .purefunction"checkVoter" msg = theMsg (nat 0) testLedger 1 .purefunction"counter" msg = theMsg (nat 0) testLedger 1 .purefunctionCost"checkVoter" msg = 1 testLedger 1 .fun"addVoter" msg = exec (updatec "checkVoter" addr → exec (callPure addr "checkVoter" (nat addr)) ( _ → 1)  check → voteAux addr check msg testLedger 0 .amount= 100 testLedger 3 .amount= 100 testLedger ow .amount= 0 testLedger ow .funow' ow" = error (strErr "Undefined") 〈 ow » ow • ow' [ ow" ]〉 testLedger ow .purefunctionow' ow" = err (strErr "Undefined") testLedger ow .purefunctionCostow' ow" = 1 In our example testLedger at address 1, we have three fields, as follows: • Amount (amount) (i.e.balance of the contract in wei) is 100; • We define two pure functions (purefunction) as follows: -"checkVoter", which determines for its argument whether the argument (which is assumed to be (nat ) for an address ) represents a voter who is allowed to vote; Booleans are represented as (nat 0) for false and (nat (suc )) for true."checkVoter" initially always returns (nat 0) for false, i.e. nobody is allowed to vote.-"counter" is a variable counting the number of votes for the only candidate.It is initialised with the value of 0. • We define pure function cost (purefunctionCost) to calculate the pure function for each process.• Three functions (fun) are added as follows: -"addVoter" updates the pure function"checkVoter" to allow the address represented by its argument to vote.It makes use of the following function, which determines the new value "checkVoter" by checking whether the argument was updated or not, and if not referring to the old version of "checkVoter": -"deleteVoter" deletes a voter.The implementation is almost the same as "addVoter", except for that it sets "checkVoter" to 0 for false, in case the argument is the voter to be deleted.-"vote" does the following: It looks up the calling address first, then evaluates the ("checkVoter") function applied to it to check whether the caller is allowed to vote or not.Then it invokes voteAux to make a case distinction on whether the result was true, false, or a message not representing a number.If the voter is permitted to vote, the counter (pure function ("counter")) is increased by one, and the voter is deleted from "checkVoter" to prevent double voting.Otherwise, an error will be returned.The full definition of voteAux is as follows: voteAux : Address → MsgOrError → (candidate : Msg) → SmartContract Msg voteAux addr (theMsg (nat zero)) candidate = error (strErr "The voter is not allowed to vote") 〈 0 » 0 • "Voter is not allowed to vote" [ nat 0 ]〉 voteAux addr (theMsg (nat (suc n))) candidate = exec (updatec "checkVoter" (deleteVoterAux (nat addr))  _ _ _ → 1)( _ → 1) ( x → (incrementAux1 (theMsg candidate))) voteAux addr (theMsg ow) candidate = error (strErr "The message is not a number") 〈 0 » 0 • "Voter is not allowed to vote" [ nat 0 ]〉 voteAux addr (err x) candidate = error (strErr " Undefined ") 〈 0 » 0 • "The message is undefined" [ nat 0 ]〉 For other addresses, and for other normal and pure functions for contract 0, we will return an error ("Undefined") that includes the debug information, such as the last call address, current address, last function name call, and the last function argument.We define our main menu of the complex simulation interface mainBody, as shown in Figure 6.We have created three additional options ("Option 4", "Option 5", and "Option 6") to complement the existing ones in the simple simulator.These new options aid in verifying the voting example and show the gas consumption at each stage.Below are explanations for all seven options: • "Option 1", "Option 2", and "Option 3", which are functions similarly to the simple simulator.However, these options have been redefined to incorporate gas cost and pure function.
• "Option 4" may be utilized to update the gas limit used when calling smart contracts.
• "Option 5" may be used to verify the amount of gas left before or after each operation.• "Option 6", which we use to evaluate pure functions.In Solidity, pure functions do not call other functions.When called externally, these functions do not incur any gas cost.However, gas costs will be required if they are called from an internal function.
• "Option 7", which terminates the simulator.The state of the system will be given by an element  : StateIO defined below.The mainBody function depends on that state variable stIO.
We develop the StateIO, a record type that defines the current state of computation.It comprises three fields: • ledger is the current ledger on which the calculation will be executed.
• initialAddr is the initial address used to initialise the calculation; in our case, we initialised it to 0, but it can be changed by using "Option 3".• gas is the quantity of gas left for use in the calculation.As an example, the line of code below establishes the element of StateIO that has the ledger as our voting example (testLedger), 0 as initial address, and a gas amount of 20 wei: 〈 testLedger ledger, 0 initialAddr, 20 gas〉 As we mentioned before, "Option 1", "Option 2", and "Option 3" have comparable functions and structures as the simple simulator, with the inclusion of gas cost.For instance, as shown in Figure 7, when selecting "Option 3", entering a new calling address 1 instead of the previous address 0, it will start to execute the contract function "Option 1" by entering the "addVoter" function and the argument of the function 1.The result will be that the initial address is 1, the call address is 1, the argument of the function name is (nat 1), the remaining gas is 16 wei, and the value returned is (theMsg (nat 1)).
In addition, we have created the executeLedger-updateGas function along with its corresponding auxiliary function (executeLedgerStep-updateGasAux) mutually recursively.These functions allow for the implementation of "Option 4", which enables updating the gas limit.Upon execution of executeLedgerStep-updateGasAux, the user will be prompted to input a new value for the gas amount.If the input is successful, executeLedgerStep-updateGasAux will be called, and the function will return both the new and old gas limit values.For example, as shown in Figure 8, when selecting "Option 4", then entering the new gas limit 30.The result is that the gas limit has been updated successfully, the new gas limit is 30 wei, and the old value is 20 wei.For "Option 5", we have developed a mutually recursive function called executeLedger-checkGas.This function ensures that the gas limit is verified after updating to the new value, as illustrated in Figure 9.The definition of executeLedger-checkGas is as follows: Moreover, we develop mutually recursively executeLedger-purefunction together with its auxiliary functions (executeLedger-purefunction, executeLedger-purefunction0, executeLedger-purefunction1, executeLedger-purefunStep1-2, executeLedger-purefunStep1-3, and executeLedger-purefunStep1-4), in order to implement "Option 6".As an example, after using "Option 1" to add 1 as a voter, we proceed to select "Option 6", by entering calling address 1, called address 1, and the pure function "checkVoter" along with its argument 1.The result is that the initial address is 1, the called address is 1, and the pure function returns theMsg (nat 1), signifying that it is true, as shown in Figure 10.The main function has one single argument, and it will run the mainBody, which includes an argument with a tuple of three values -the ledger, the initial address, and the gas limit.The mainBody function will use our ledger (testLedger), start from the initial address 0, and have the gas limit of 20 wei.
In the git repository [37], we demonstrate our complex simulator by an example.The example shows that, if we change first the calling address to address 1, and then try to vote, the vote is rejected, because voter 1 has not been added yet.If we then add voter 1, and vote (with calling address 1), the vote succeeds, and the counter is incremented by 1.If contract 1 votes again, it is rejected, and we see that the number of votes stays at 1.

CONCLUSION AND FUTURE WORK
This paper presents two blockchain simulators of Solidity-style smart contracts in the theorem prover Agda.The first is the simple simulator, which has simple instructions for transferring money to specific addresses and executing and updating smart 'contracts.The second is the complex simulator, which has more features and complex instructions, supports gas costs, uses a pure function similar to the Solidity language, and displays better error messages than the simple simulator.The simulator is written in the interactive theorem prover Agda, in the same language in which we plan to carry out the verification.Therefore, there is no explicit translation needed from the simulated program to the verified program, avoiding translation errors.
In future work, we will verify contracts which can be run in the simple and complex blockchain simulators by using weakest preconditions [5,7].In our previous articles [5,7].we already implemented, proved, and verified Bitcoin's smart contracts in the Agda proof assistant using weakest preconditions.We plan to expand the work in those previous articles in order to verify Solidity-style smart contracts.

Figure 3 :
Figure 3: Execute a function of a contract (option 1).

Figure 4 :
Figure 4: Look up the balance of a contract (option 2).

The
constructor for StateIO requires three parameters, which are the values that are to be used for each of the three fields.The definition of StateIO is as follows: record StateIO : Set where constructor 〈_ledger,_initialAddr,_gas〉 field ledger : Ledger initialAddr : Address gas : N

Figure 7 :
Figure 7: Change the calling address in the complex blockchain simulator (option 3).

Figure 8 :
Figure 8: Update the gas limit in the complex blockchain simulator (option 4).

Figure 9 :
Figure 9: Check the gas limit in the complex blockchain simulator (option 5).

Figure 10 :
Figure 10: Evaluate a pure function in the complex simulator at (option 6).