├── .github └── workflows │ └── workflow.yml ├── .gitignore ├── LICENSE ├── README.md ├── docs ├── Makefile ├── make.bat ├── source │ ├── conf.py │ ├── contact.rst │ ├── index.rst │ ├── interface.rst │ ├── intro.rst │ ├── nstatic │ │ └── imgs │ │ │ ├── callgraph.png │ │ │ ├── scilla-logo-bw.jpg │ │ │ ├── scilla-logo-color-transparent.png │ │ │ └── scilla-logo-color.jpg │ ├── scilla-by-example.rst │ ├── scilla-checker.rst │ ├── scilla-in-depth.rst │ ├── scilla-tips-and-tricks.rst │ ├── scilla-trial.rst │ ├── spelling_wordlist.txt │ └── stdlib.rst └── texsources │ └── gas-costs │ ├── Makefile │ ├── gas-doc.pdf │ └── gas-doc.tex └── requirements.txt /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | - name: Install OS packages 16 | run: sudo apt-get update && sudo apt-get install -yq python3 python3-pip libenchant-2-dev aspell-en 17 | - name: Install Python packages 18 | run: pip install -r requirements.txt 19 | - name: Run spellcheck 20 | run: pushd docs && make spell && popd 21 | - name: Build HTML 22 | run: pushd docs && make html && popd 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | docs/build 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Zilliqa Research Pvt. Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scilla Docs 2 | 3 | ![](https://github.com/Zilliqa/scilla-docs/workflows/build/badge.svg) 4 | 5 | Scilla short for Smart Contract Intermediate-Level LAnguage is a smart contract 6 | language being developed for Zilliqa. 7 | 8 | ## Contributing 9 | 10 | If you spot any issues or have any ideas on how we can improve the 11 | documentation, help us log an issue 12 | [here](https://github.com/Zilliqa/scilla-docs/issues). 13 | 14 | ## Installing dependencies 15 | 16 | To compile the documentation you need the following system packages: 17 | [Python3](https://www.python.org/downloads/) with the 18 | [pip](https://pypi.org/project/pip/) package and 19 | [libenchant](https://www.abisource.com/projects/enchant/) used to check 20 | spelling. 21 | 22 | On Debian-based systems you can install them with: 23 | ``` 24 | sudo apt-get install python3 python3-pip libenchant-2-dev 25 | ``` 26 | 27 | Then clone this repository and install the required Python packages from 28 | [`requirements.txt`](https://github.com/Zilliqa/scilla-docs/blob/master/requirements.txt): 29 | ``` 30 | pip3 -U install -r requirements.txt 31 | ``` 32 | 33 | ## Building the docs 34 | 35 | ### Spellchecking 36 | 37 | Run `make spell` from [docs](./docs/) folder. In case of any found spelling 38 | mistakes you will see an output like the following: 39 | ``` 40 | Spelling checker messages written to /path/to/scilla-docs/docs/build/spelling/output.txt 41 | WARNING: Found 1 misspelled words 42 | ``` 43 | Checkout the `output.txt` file and fix the typos. 44 | 45 | If you need to teach the spelling checker more words, add them to the 46 | [spelling_wordlist.txt](./docs/source/spelling_wordlist.txt) file. The format is 47 | really simple -- it's just one word per line the file. And the file is sorted in 48 | ascending order. 49 | 50 | ### Building HTML docs 51 | 52 | Run `make html` from [docs](./docs/) folder and make sure that the edits are 53 | rendered correctly on the HTML files. To do that point your browser at the 54 | locally built `/path/to/scilla-docs/docs/build/html/index.html` file and start 55 | checking from it. 56 | 57 | ### Before submitting a pull request 58 | 59 | Please preview your HTML files _before_ submitting a pull request. Try to 60 | [squash](https://blog.github.com/2016-04-01-squash-your-commits/) your commits 61 | before making the pull request. We know it's difficult, but it helps us keep our 62 | commit logs clean and makes the reviewers' lives easier. 63 | 64 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = -W 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = scilla-doc 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | spell: 16 | @$(SPHINXBUILD) -M spelling "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 17 | 18 | .PHONY: help Makefile 19 | 20 | # Catch-all target: route all unknown targets to Sphinx using the new 21 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 22 | %: Makefile 23 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 24 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=scilla-doc 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :spell 33 | %SPHINXBUILD% -M spelling %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :help 36 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 37 | 38 | :end 39 | popd 40 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Configuration file for the Sphinx documentation builder. 4 | # 5 | # This file does only contain a selection of the most common options. For a 6 | # full list see the documentation: 7 | # http://www.sphinx-doc.org/en/master/config 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | # If extensions (or modules to document with autodoc) are in another directory, 12 | # add these directories to sys.path here. If the directory is relative to the 13 | # documentation root, use os.path.abspath to make it absolute, like shown here. 14 | # 15 | # import os 16 | # import sys 17 | # sys.path.insert(0, os.path.abspath('.')) 18 | 19 | import sys, os 20 | 21 | # Check if we are building on readthedocs 22 | on_readthedocs = os.environ.get('READTHEDOCS', None) == 'True' 23 | 24 | # -- Project information ----------------------------------------------------- 25 | 26 | project = u'scilla-doc' 27 | copyright = u'2019, Zilliqa Research' 28 | author = u'Amrit Kumar' 29 | 30 | # The short X.Y version 31 | version = u'' 32 | # The full version, including alpha/beta/rc tags 33 | release = u'0.11.0' 34 | 35 | 36 | # -- General configuration --------------------------------------------------- 37 | 38 | # If your documentation needs a minimal Sphinx version, state it here. 39 | # 40 | # needs_sphinx = '1.0' 41 | 42 | # Add any Sphinx extension module names here, as strings. They can be 43 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 44 | # ones. 45 | extensions = [ 46 | 'sphinx.ext.autodoc', 47 | ] 48 | 49 | if not on_readthedocs: 50 | # readthedocs doesn't install dependencies 51 | extensions.append('sphinxcontrib.spelling') 52 | 53 | # Add any paths that contain templates here, relative to this directory. 54 | templates_path = ['ntemplates'] 55 | 56 | # The suffix(es) of source filenames. 57 | # You can specify multiple suffix as a list of string: 58 | # 59 | # source_suffix = ['.rst', '.md'] 60 | source_suffix = '.rst' 61 | 62 | # The master toctree document. 63 | master_doc = 'index' 64 | 65 | # The language for content autogenerated by Sphinx. Refer to documentation 66 | # for a list of supported languages. 67 | # 68 | # This is also used if you do content translation via gettext catalogs. 69 | # Usually you set "language" from the command line for these cases. 70 | language = 'en' 71 | 72 | # List of patterns, relative to source directory, that match files and 73 | # directories to ignore when looking for source files. 74 | # This pattern also affects html_static_path and html_extra_path . 75 | exclude_patterns = [] 76 | 77 | # The name of the Pygments (syntax highlighting) style to use. 78 | pygments_style = 'sphinx' 79 | 80 | 81 | # -- Options for HTML output ------------------------------------------------- 82 | 83 | # The theme to use for HTML and HTML Help pages. See the documentation for 84 | # a list of builtin themes. 85 | # 86 | html_theme = 'sphinx_rtd_theme' 87 | 88 | # Theme options are theme-specific and customize the look and feel of a theme 89 | # further. For a list of options available for each theme, see the 90 | # documentation. 91 | # 92 | # html_theme_options = {} 93 | 94 | # Add any paths that contain custom static files (such as style sheets) here, 95 | # relative to this directory. They are copied after the builtin static files, 96 | # so a file named "default.css" will overwrite the builtin "default.css". 97 | html_static_path = ['nstatic'] 98 | 99 | # Custom sidebar templates, must be a dictionary that maps document names 100 | # to template names. 101 | # 102 | # The default sidebars (for documents that don't match any pattern) are 103 | # defined by theme itself. Builtin themes are using these templates by 104 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', 105 | # 'searchbox.html']``. 106 | # 107 | # html_sidebars = {} 108 | 109 | 110 | # -- Options for HTMLHelp output --------------------------------------------- 111 | 112 | # Output file base name for HTML help builder. 113 | htmlhelp_basename = 'scilla-docdoc' 114 | 115 | 116 | # -- Options for LaTeX output ------------------------------------------------ 117 | 118 | latex_elements = { 119 | # The paper size ('letterpaper' or 'a4paper'). 120 | # 121 | # 'papersize': 'letterpaper', 122 | 123 | # The font size ('10pt', '11pt' or '12pt'). 124 | # 125 | # 'pointsize': '10pt', 126 | 127 | # Additional stuff for the LaTeX preamble. 128 | # 129 | # 'preamble': '', 130 | 131 | # Latex figure (float) alignment 132 | # 133 | # 'figure_align': 'htbp', 134 | } 135 | 136 | # Grouping the document tree into LaTeX files. List of tuples 137 | # (source start file, target name, title, 138 | # author, documentclass [howto, manual, or own class]). 139 | latex_documents = [ 140 | (master_doc, 'scilla-doc.tex', u'scilla-doc Documentation', 141 | u'Amrit Kumar', 'manual'), 142 | ] 143 | 144 | 145 | # -- Options for manual page output ------------------------------------------ 146 | 147 | # One entry per manual page. List of tuples 148 | # (source start file, name, description, authors, manual section). 149 | man_pages = [ 150 | (master_doc, 'scilla-doc', u'scilla-doc Documentation', 151 | [author], 1) 152 | ] 153 | 154 | 155 | # -- Options for Texinfo output ---------------------------------------------- 156 | 157 | # Grouping the document tree into Texinfo files. List of tuples 158 | # (source start file, target name, title, author, 159 | # dir menu entry, description, category) 160 | texinfo_documents = [ 161 | (master_doc, 'scilla-doc', u'scilla-doc Documentation', 162 | author, 'scilla-doc', 'One line description of project.', 163 | 'Miscellaneous'), 164 | ] 165 | 166 | 167 | # -- Extension configuration ------------------------------------------------- 168 | # 169 | spelling_lang='en_UK' 170 | -------------------------------------------------------------------------------- /docs/source/contact.rst: -------------------------------------------------------------------------------- 1 | Contact 2 | ========= 3 | 4 | Questions? Talk to us on `Discord `_. 5 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. scilla-doc documentation master file, created by 2 | sphinx-quickstart on Wed Jun 20 09:34:04 2018. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Scilla 7 | ====================================== 8 | 9 | .. image:: nstatic/imgs/scilla-logo-color-transparent.png 10 | :width: 100px 11 | :align: center 12 | :height: 100px 13 | 14 | 15 | 16 | `Scilla` (short for `Smart Contract Intermediate-Level LAnguage`) is 17 | an intermediate-level smart contract language being developed for the 18 | `Zilliqa `_ blockchain. Scilla is designed 19 | as a principled language with smart contract safety in mind. 20 | 21 | Scilla imposes a structure on smart contracts that will make applications less 22 | vulnerable to attacks by eliminating certain known vulnerabilities directly at 23 | the language-level. Furthermore, the principled structure of Scilla will make 24 | applications inherently more secure and amenable to formal verification. 25 | 26 | The language is being developed hand-in-hand with formalization of its 27 | semantics and its embedding into the `Coq proof assistant 28 | `_ — a state-of-the art tool for mechanized proofs about 29 | properties of programs. Coq is based on advanced dependently-typed theory and 30 | features a large set of mathematical libraries. It has been successfully 31 | applied previously to implement certified (i.e., fully mechanically 32 | verified) compilers, concurrent and distributed applications, including 33 | blockchains among others. 34 | 35 | `Zilliqa` --- the underlying blockchain platform on which Scilla contracts are 36 | run --- has been designed to be scalable. It employs the idea of sharding to 37 | validate transactions in parallel. Zilliqa has an intrinsic token named 38 | `Zilling` (ZIL for short) that are required to run smart contracts on Zilliqa. 39 | 40 | 41 | Development Status 42 | ****************** 43 | 44 | Scilla is under active research and development and hence parts of the 45 | specification described in this document are subject to change. Scilla 46 | currently comes with an interpreter binary that has been integrated into two 47 | Scilla-specific web-based IDEs. :ref:`trial-label` presents the features of the 48 | two IDEs. 49 | 50 | Resources 51 | ********* 52 | 53 | There are several resources to learn about Scilla and Zilliqa. Some of these 54 | are given below: 55 | 56 | 57 | **Scilla** 58 | + `Scilla Design Paper `_ 59 | 60 | + `Scilla Slides `_ 61 | 62 | + `Scilla Language Grammar 63 | `_ 64 | 65 | + `Scilla Design Story Piece by Piece: Part 1 (Why do we need a new 66 | language?) 67 | `_ 68 | 69 | **Zilliqa** 70 | + `The Zilliqa Design Story Piece by Piece: Part 1 (Network Sharding) `_ 71 | + `The Zilliqa Design Story Piece by Piece: Part 2 (Consensus Protocol) `_ 72 | + `The Zilliqa Design Story Piece by Piece: Part 3 (Making Consensus Efficient) `_ 73 | + `Technical Whitepaper `_ 74 | + `The Not-So-Short Zilliqa Technical FAQ `_ 75 | 76 | 77 | 78 | Contents 79 | ========= 80 | .. toctree:: 81 | :maxdepth: 3 82 | 83 | intro 84 | scilla-trial 85 | scilla-by-example 86 | scilla-in-depth 87 | stdlib 88 | scilla-tips-and-tricks 89 | scilla-checker 90 | interface 91 | contact 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /docs/source/interface.rst: -------------------------------------------------------------------------------- 1 | .. _interface-label: 2 | 3 | 4 | Interpreter Interface 5 | ===================== 6 | 7 | The Scilla interpreter provides a calling interface that enables users 8 | to invoke transitions with specified inputs and obtain 9 | outputs. Execution of a transition with supplied inputs will result in a 10 | set of outputs, and a change in the smart contract mutable state. 11 | 12 | .. _calling-interface: 13 | 14 | Calling Interface 15 | ################# 16 | 17 | A transition defined in a contract can be called either by the 18 | issuance of a transaction, or by message calls from another 19 | contract. The same calling interface will be used to call the contract 20 | via external transactions and inter-contract message calls. 21 | 22 | The inputs to the interpreter (``scilla-runner``) consists of four input JSON 23 | files as described below. Every invocation of the interpreter to execute a 24 | transition must be provided with these four JSON inputs: :: 25 | 26 | ./scilla-runner -init init.json -istate input_state.json -iblockchain input_blockchain.json -imessage input_message.json -o output.json -i input.scilla 27 | 28 | The interpreter executable can be run either to create a contract (denoted 29 | ``CreateContract``) or to invoke a transition in a contract (``InvokeContract``). 30 | Depending on which of these two, some of the arguments will be absent. 31 | The table below outlays the arguments that should be present in each of 32 | these two cases. A ``CreateContract`` is distinguished from an 33 | ``InvokeContract``, based on the presence of ``input_message.json`` and 34 | ``input_state.json``. If these arguments are absent, then the interpreter will 35 | evaluate it as a ``CreateContract``. Else, it will treat it as an ``InvokeContract``. 36 | Note that for ``CreateContract``, the interpreter only performs basic checks such as 37 | matching the contract’s immutable parameters with ``init.json`` and whether the 38 | contract definition is free of syntax errors. 39 | 40 | 41 | +---------------------------+-------------------------------+-----------------------------------------+ 42 | | | | Present | 43 | +===========================+===============================+====================+====================+ 44 | | Input | Description | ``CreateContract`` | ``InvokeContract`` | 45 | +---------------------------+-------------------------------+--------------------+--------------------+ 46 | | ``init.json`` | Immutable contract parameters | Yes | Yes | 47 | +---------------------------+-------------------------------+--------------------+--------------------+ 48 | | ``input_state.json`` | Mutable contract state | No | Yes | 49 | +---------------------------+-------------------------------+--------------------+--------------------+ 50 | | ``input_blockchain.json`` | Blockchain state | Yes | Yes | 51 | +---------------------------+-------------------------------+--------------------+--------------------+ 52 | | ``input_message.json`` | Transition and parameters | No | Yes | 53 | +---------------------------+-------------------------------+--------------------+--------------------+ 54 | | ``output.json`` | Output | Yes | Yes | 55 | +---------------------------+-------------------------------+--------------------+--------------------+ 56 | | ``input.scilla`` | Input contract | Yes | Yes | 57 | +---------------------------+-------------------------------+--------------------+--------------------+ 58 | 59 | In addition to the command line arguments provided above, the interpreter also expects a mandatory 60 | ``-gaslimit X`` argument (where ``X`` is a positive integer value). If the contract or library module 61 | imports other libraries (including the standard library), a `-libdir` option must be provided, with 62 | a list of directories (in the standard PATH format) as the argument, indicating directories to be 63 | searched for for finding the libraries. 64 | 65 | 66 | Initializing the Immutable State 67 | ################################ 68 | 69 | ``init.json`` defines the values of the immutable parameters of a contract. 70 | It does not change between invocations. The JSON is an array of 71 | objects, each of which contains the following fields: 72 | 73 | ========= ========================================== 74 | Field Description 75 | ========= ========================================== 76 | ``vname`` Name of the immutable contract parameter 77 | ``type`` Type of the immutable contract parameter 78 | ``value`` Value of the immutable contract parameter 79 | ========= ========================================== 80 | 81 | ``init.json`` must specify ``_scilla_version`` of type ``Uint32`` 82 | specifying a value that is the same as specified in the contract's 83 | source, and ``_library`` of type ``Bool`` specifying whether the 84 | deployed code file is a library. Additionally, the 85 | blockchain will provide two implicit contract parameters 86 | ``_this_address``, a ``ByStr20`` value denoting the address of the 87 | contract itself, and ``_creation_block``, a ``BNum`` value denoting 88 | the block in which the contract is / was created. While working with 89 | the offline interpreter, you may need to provide these values in the 90 | ``init.json`` yourself. 91 | 92 | Example 1 93 | ********** 94 | 95 | For the ``HelloWorld.scilla`` contract fragment given below, we have only one 96 | immutable parameter ``owner``. 97 | 98 | .. code-block:: ocaml 99 | 100 | contract HelloWorld 101 | (* Immutable parameters *) 102 | (owner: ByStr20) 103 | 104 | 105 | A sample ``init.json`` for this contract will look like the following: 106 | 107 | .. code-block:: json 108 | 109 | [ 110 | { 111 | "vname" : "_scilla_version", 112 | "type" : "Uint32", 113 | "value" : "0" 114 | }, 115 | { 116 | "vname" : "_library", 117 | "type" : "Bool", 118 | "value": { "constructor": "False", "argtypes": [], "arguments": [] } 119 | }, 120 | { 121 | "vname" : "owner", 122 | "type" : "ByStr20", 123 | "value" : "0x1234567890123456789012345678901234567890" 124 | }, 125 | { 126 | "vname" : "_this_address", 127 | "type" : "ByStr20", 128 | "value" : "0xabfeccdc9012345678901234567890f777567890" 129 | }, 130 | { 131 | "vname" : "_creation_block", 132 | "type" : "BNum", 133 | "value" : "1" 134 | } 135 | ] 136 | 137 | 138 | Example 2 139 | ********** 140 | 141 | For the ``Crowdfunding.scilla`` contract fragment given below, we have three 142 | immutable parameters ``owner``, ``max_block`` and ``goal``. 143 | 144 | 145 | .. code-block:: ocaml 146 | 147 | contract Crowdfunding 148 | (* Immutable parameters *) 149 | (owner : ByStr20, 150 | max_block : BNum, 151 | goal : UInt128) 152 | 153 | 154 | A sample ``init.json`` for this contract will look like the following: 155 | 156 | 157 | .. code-block:: json 158 | 159 | [ 160 | { 161 | "vname" : "_scilla_version", 162 | "type" : "Uint32", 163 | "value" : "0" 164 | }, 165 | { 166 | "vname" : "_library", 167 | "type" : "Bool", 168 | "value": { "constructor": "False", "argtypes": [], "arguments": [] } 169 | }, 170 | { 171 | "vname" : "owner", 172 | "type" : "ByStr20", 173 | "value" : "0x1234567890123456789012345678901234567890" 174 | }, 175 | { 176 | "vname" : "max_block", 177 | "type" : "BNum" , 178 | "value" : "199" 179 | }, 180 | { 181 | "vname" : "_this_address", 182 | "type" : "ByStr20", 183 | "value" : "0xabfeccdc9012345678901234567890f777567890" 184 | }, 185 | { 186 | "vname" : "goal", 187 | "type" : "Uint128", 188 | "value" : "500000000000000" 189 | }, 190 | { 191 | "vname" : "_creation_block", 192 | "type" : "BNum", 193 | "value" : "1" 194 | } 195 | ] 196 | 197 | Example 3: Using Address Types 198 | *********************************** 199 | 200 | Whenever a contract has an immutable parameter of an address type, the 201 | type ``ByStr20`` must be used in the to initialise the parameter. 202 | 203 | For the ``SimpleExchange`` we have a single the immutable parameter, 204 | which has an address type: 205 | 206 | .. code-block:: ocaml 207 | 208 | contract SimpleExchange 209 | ( 210 | initial_admin : ByStr20 with end 211 | ) 212 | 213 | The JSON entry for the ``initial_admin`` parameter must use the type 214 | ``ByStr20`` rather than the type ``ByStr20 with end``, so an example 215 | ``init.json`` for this contract could like the following: 216 | 217 | .. code-block:: json 218 | 219 | [ 220 | { 221 | "vname" : "_scilla_version", 222 | "type" : "Uint32", 223 | "value" : "0" 224 | }, 225 | { 226 | "vname" : "_library", 227 | "type" : "Bool", 228 | "value": { "constructor": "False", "argtypes": [], "arguments": [] } 229 | }, 230 | { 231 | "vname" : "_this_address", 232 | "type" : "ByStr20", 233 | "value" : "0xabfeccdc9012345678901234567890f777567890" 234 | }, 235 | { 236 | "vname" : "_creation_block", 237 | "type" : "BNum", 238 | "value" : "1" 239 | }, 240 | { 241 | "vname" : "initial_admin", 242 | "type" : "ByStr20", 243 | "value" : "0x1234567890123456789012345678901234567890" 244 | } 245 | ] 246 | 247 | 248 | 249 | 250 | 251 | Input Blockchain State 252 | ######################## 253 | 254 | ``input_blockchain.json`` feeds the current blockchain state to the 255 | interpreter. It is similar to ``init.json``, except that it is a fixed size 256 | array of objects, where each object has ``vname`` fields only from a 257 | predetermined set (which correspond to actual blockchain state variables). 258 | 259 | **Permitted JSON fields:** At the moment, the only blockchain value that is exposed to contracts is the current ``BLOCKNUMBER``. 260 | 261 | .. code-block:: json 262 | 263 | [ 264 | { 265 | "vname" : "BLOCKNUMBER", 266 | "type" : "BNum", 267 | "value" : "3265" 268 | } 269 | ] 270 | 271 | Input Message 272 | ############### 273 | 274 | ``input_message.json`` contains the information required to invoke a 275 | transition. The json is an array containing the following four objects: 276 | 277 | =========== =========================================== 278 | Field Description 279 | =========== =========================================== 280 | ``_tag`` Transition to be invoked 281 | ``_amount`` Number of QA to be transferred 282 | ``_sender`` Address of the invoker (in a chain call, this is the immediate caller) 283 | ``_origin`` Address from which the transaction originated 284 | ``params`` An array of parameter objects 285 | =========== =========================================== 286 | 287 | 288 | All the four fields are mandatory. ``params`` can be empty if the transition 289 | takes no parameters. 290 | 291 | The ``params`` array is encoded similar to how ``init.json`` is encoded, with 292 | each parameter specifying the (``vname``, ``type``, ``value``) that has to be 293 | passed to the transition that is being invoked. 294 | 295 | Example 1 296 | ********** 297 | For the following transition: 298 | 299 | .. code-block:: ocaml 300 | 301 | transition SayHello() 302 | 303 | an example ``input_message.json`` is given below: 304 | 305 | .. code-block:: json 306 | 307 | { 308 | "_tag" : "SayHello", 309 | "_amount" : "0", 310 | "_sender" : "0x1234567890123456789012345678901234567890", 311 | "_origin" : "0x1234567890123456789012345678901234567890", 312 | "params" : [] 313 | } 314 | 315 | Example 2 316 | ********** 317 | For the following transition: 318 | 319 | .. code-block:: ocaml 320 | 321 | transition TransferFrom (from : ByStr20, to : ByStr20, tokens : Uint128) 322 | 323 | an example ``input_message.json`` is given below: 324 | 325 | .. code-block:: json 326 | 327 | { 328 | "_tag" : "TransferFrom", 329 | "_amount" : "0", 330 | "_sender" : "0x64345678901234567890123456789012345678cd", 331 | "_origin" : "0x64345678901234567890123456789012345678cd", 332 | "params" : [ 333 | { 334 | "vname" : "from", 335 | "type" : "ByStr20", 336 | "value" : "0x1234567890123456789012345678901234567890" 337 | }, 338 | { 339 | "vname" : "to", 340 | "type" : "ByStr20", 341 | "value" : "0x78345678901234567890123456789012345678cd" 342 | }, 343 | { 344 | "vname" : "tokens", 345 | "type" : "Uint128", 346 | "value" : "500000000000000" 347 | } 348 | ] 349 | } 350 | 351 | Example 3: Using user-defined types 352 | *********************************** 353 | 354 | .. note:: 355 | 356 | Due to a bug in the Scilla implementation the information in this 357 | section is only valid from Scilla version 0.10.0 and 358 | forwards. Contracts written in Scilla versions prior to 0.10.0 and 359 | which exploit this bug will have to be rewritten and redeployed, as 360 | they will no longer work from version 0.10.0 and onwards. 361 | 362 | When passing a value of user-defined type through the interpreter interface, the json structure is identical to the one described in the previous example. However, in the interpreter interface all types must be fully qualified, which is defined as follows: 363 | 364 | - For a user-defined type ``T`` defined in a module deployed at address ``A``, the fully qualified name is ``A.T``. 365 | 366 | - For a user-defined constructor ``C`` defined in a module deployed at address ``A``, the fully qualified name is ``A.C``. 367 | 368 | .. note:: 369 | 370 | For the purposes of offline development the address of a module is 371 | defined as the module's filename, without file extension. That is, 372 | if a contract defines a type ``T`` with a constructor ``C`` in a 373 | file ``F.scilla``, then the fully qualified name of the type is 374 | ``F.T``, and the fully qualified name of the constructor is 375 | ``F.C``. 376 | 377 | As an example consider a contract that implements a simple board game. The contract might define a type ``Direction`` and a transition ``MoveAction`` as follows: 378 | 379 | .. code-block:: ocaml 380 | 381 | type Direction = 382 | | East 383 | | South 384 | | West 385 | | North 386 | 387 | ... 388 | 389 | transition MoveAction (dir : Direction, spaces : Uint32) 390 | ... 391 | 392 | Say that the contract has been deployed at address ``0x1234567890123456789012345678906784567890``. To invoke the transition with parameters ``East`` and ``2``, use the type name ``0x1234567890123456789012345678906784567890.Direction`` and the constructor name ``0x1234567890123456789012345678906784567890.East`` in the message json: 393 | 394 | .. code-block:: json 395 | 396 | { 397 | "_tag": "MoveAction", 398 | "_amount": "0", 399 | "_sender" : "0x64345678901234567890123456789012345678cd", 400 | "_origin" : "0x64345678901234567890123456789012345678cd", 401 | "params": [ 402 | { 403 | "vname" : "dir", 404 | "type" : "0x1234567890123456789012345678906784567890.Direction", 405 | "value" : 406 | { 407 | "constructor" : "0x1234567890123456789012345678906784567890.East", 408 | "argtypes" : [], 409 | "arguments" : [] 410 | } 411 | }, 412 | { 413 | "vname" : "spaces", 414 | "type" : "Uint32", 415 | "value" : "2" 416 | } 417 | ] 418 | } 419 | 420 | 421 | If a contract has immutable fields of user-defined types, then the fields must also be initialised using fully qualified names in the associated ``init.json``. 422 | 423 | Example 4: Using Address Types 424 | *********************************** 425 | 426 | When passing an address value the type ``ByStr20`` must be used. It is 427 | not possible to use address types (``ByStr20 with ... end``) in 428 | messages. 429 | 430 | This means that for the following transition 431 | 432 | .. code-block:: ocaml 433 | 434 | transition ListToken( 435 | token_code : String, 436 | new_token : ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end 437 | ) 438 | 439 | the ``input_message.json`` must use the type ``ByStr20`` for the 440 | ``new_token`` parameter, e.g., as follows: 441 | 442 | .. code-block:: json 443 | 444 | { 445 | "_tag" : "ListToken", 446 | "_amount" : "0", 447 | "_sender" : "0x64345678901234567890123456789012345678cd", 448 | "_origin" : "0x64345678901234567890123456789012345678cd", 449 | "params" : [ 450 | { 451 | "vname" : "token_code", 452 | "type" : "String", 453 | "value" : "XYZ" 454 | }, 455 | { 456 | "vname" : "new_token", 457 | "type" : "ByStr20", 458 | "value" : "0x78345678901234567890123456789012345678cd" 459 | } 460 | ] 461 | } 462 | 463 | 464 | Interpreter Output 465 | ##################### 466 | 467 | The interpreter will return a JSON object (``output.json``) with the following 468 | fields: 469 | 470 | ========================= ==================================================================== 471 | Field Description 472 | ========================= ==================================================================== 473 | ``scilla_major_version`` The major version of the Scilla language of this contract. 474 | ``gas_remaining`` The remaining gas after invoking or deploying a contract. 475 | ``_accepted`` Whether the incoming QA have been accepted (Either ``"true"`` or ``"false"``) 476 | ``message`` The message to be sent to another contract/non-contract account, if any. 477 | ``states`` An array of objects that form the new contract state 478 | ``events`` An array of events emitted by the transition and the procedures it invoked. 479 | ========================= ==================================================================== 480 | 481 | + ``message`` is a JSON object with a similar format to 482 | ``input_message.json``, except that it has a ``_recipient`` field 483 | instead of the ``_sender`` field. The fields in ``message`` are 484 | given below: 485 | 486 | =============== ======================================================= 487 | Field Description 488 | =============== ======================================================= 489 | ``_tag`` Transition to be invoked 490 | ``_amount`` Number of QA to be transferred 491 | ``_recipient`` Address of the recipient 492 | ``params`` An array of parameter objects to be passed 493 | =============== ======================================================= 494 | 495 | 496 | The ``params`` array is encoded similar to how ``init.json`` is encoded, with 497 | each parameter specifying the (``vname``, ``type``, ``value``) that has to be 498 | passed to the transition that is being invoked. 499 | 500 | + ``states`` is an array of objects that represents the mutable state of the 501 | contract. Each entry of the ``states`` array also specifies (``vname``, 502 | ``type``, ``value``). 503 | 504 | + ``events`` is an array of objects that represents the events emitted 505 | by the transition. The fields in each object in the ``events`` array 506 | are given below: 507 | 508 | =============== ======================================================= 509 | Field Description 510 | =============== ======================================================= 511 | ``_eventname`` The name of the event 512 | ``params`` An array of additional event fields 513 | =============== ======================================================= 514 | 515 | The ``params`` array is encoded similar to how ``init.json`` is 516 | encoded, with each parameter specifying the (``vname``, ``type``, 517 | ``value``) of each event field. 518 | 519 | Example 1 520 | ********* 521 | 522 | An example of the output generated by ``Crowdfunding.scilla`` is given 523 | below. The example also shows the format for maps in contract states. 524 | 525 | .. code-block:: json 526 | 527 | { 528 | "scilla_major_version": "0", 529 | "gas_remaining": "7365", 530 | "_accepted": "false", 531 | "message": { 532 | "_tag": "", 533 | "_amount": "100000000000000", 534 | "_recipient": "0x12345678901234567890123456789012345678ab", 535 | "params": [] 536 | }, 537 | "states": [ 538 | { "vname": "_balance", "type": "Uint128", "value": "300000000000000" }, 539 | { 540 | "vname": "backers", 541 | "type": "Map (ByStr20) (Uint128)", 542 | "value": [ 543 | { "key": "0x12345678901234567890123456789012345678cd", "val": "200000000000000" }, 544 | { "key": "0x123456789012345678901234567890123456abcd", "val": "100000000000000" } 545 | ] 546 | }, 547 | { 548 | "vname": "funded", 549 | "type": "Bool", 550 | "value": { "constructor": "False", "argtypes": [], "arguments": [] } 551 | } 552 | ], 553 | "events": [ 554 | { 555 | "_eventname": "ClaimBackSuccess", 556 | "params": [ 557 | { 558 | "vname": "caller", 559 | "type": "ByStr20", 560 | "value": "0x12345678901234567890123456789012345678ab" 561 | }, 562 | { "vname": "amount", "type": "Uint128", "value": "100000000000000" }, 563 | { "vname": "code", "type": "Int32", "value": "9" } 564 | ] 565 | } 566 | ] 567 | } 568 | 569 | 570 | Example 2 571 | ********* 572 | 573 | For values of an ADT type, the ``value`` field contains three subfields: 574 | 575 | - ``constructor``: The name of the constructor used to construct the value. 576 | 577 | - ``argtypes``: An array of type instantiations. For the ``List`` and 578 | ``Option`` types, this array will contain one type, indicating the 579 | type of the list elements or the optional value, respectively. For 580 | the ``Pair`` type, the array will contain two types, indicating the 581 | types of the two values in the pair. For all other ADTs, the array 582 | will be empty. 583 | 584 | - ``arguments``: The arguments to the constructor. 585 | 586 | The following example shows how values of the ``List`` and ``Option`` types are represented in the output json: 587 | 588 | .. code-block:: json 589 | 590 | { 591 | "scilla_major_version": "0", 592 | "gas_remaining": "7733", 593 | "_accepted": "false", 594 | "message": null, 595 | "states": [ 596 | { "vname": "_balance", "type": "Uint128", "value": "0" }, 597 | { 598 | "vname": "gpair", 599 | "type": "Pair (List (Int64)) (Option (Bool))", 600 | "value": { 601 | "constructor": "Pair", 602 | "argtypes": [ "List (Int64)", "Option (Bool)" ], 603 | "arguments": [ 604 | [], 605 | { "constructor": "None", "argtypes": [ "Bool" ], "arguments": [] } 606 | ] 607 | } 608 | }, 609 | { "vname": "llist", "type": "List (List (Int64))", "value": [] }, 610 | { "vname": "plist", "type": "List (Option (Int32))", "value": [] }, 611 | { 612 | "vname": "gnat", 613 | "type": "Nat", 614 | "value": { "constructor": "Zero", "argtypes": [], "arguments": [] } 615 | }, 616 | { 617 | "vname": "gmap", 618 | "type": "Map (ByStr20) (Pair (Int32) (Int32))", 619 | "value": [ 620 | { 621 | "key": "0x12345678901234567890123456789012345678ab", 622 | "val": { 623 | "constructor": "Pair", 624 | "argtypes": [ "Int32", "Int32" ], 625 | "arguments": [ "1", "2" ] 626 | } 627 | } 628 | ] 629 | } 630 | ], 631 | "events": [] 632 | } 633 | 634 | 635 | Input Mutable Contract State 636 | ############################ 637 | 638 | ``input_state.json`` contains the current value of mutable state variables. It 639 | has the same forms as the ``states`` field in ``output.json``. An example of 640 | ``input_state.json`` for ``Crowdfunding.scilla`` is given below. 641 | 642 | .. code-block:: json 643 | 644 | [ 645 | { 646 | "vname": "backers", 647 | "type": "Map (ByStr20) (Uint128)", 648 | "value": [ 649 | { 650 | "key": "0x12345678901234567890123456789012345678cd", 651 | "val": "200000000000000" 652 | }, 653 | { 654 | "key": "0x12345678901234567890123456789012345678ab", 655 | "val": "100000000000000" 656 | } 657 | ] 658 | }, 659 | { 660 | "vname": "funded", 661 | "type": "Bool", 662 | "value": { 663 | "constructor": "False", 664 | "argtypes": [], 665 | "arguments": [] 666 | } 667 | }, 668 | { 669 | "vname": "_balance", 670 | "type": "Uint128", 671 | "value": "300000000000000" 672 | } 673 | ] 674 | 675 | -------------------------------------------------------------------------------- /docs/source/intro.rst: -------------------------------------------------------------------------------- 1 | Scilla Design Principles 2 | ========================= 3 | 4 | `Smart contracts` provide a mechanism to express computations on a blockchain, 5 | i.e., a decentralized Byzantine-fault tolerant distributed ledger. With the 6 | advent of smart contracts, it has become possible to build what is referred to 7 | as `decentralized applications` or Dapps for short. These applications have 8 | their program and business logic coded in the form of a smart contract that can 9 | be run on a decentralized blockchain network. 10 | 11 | Running applications on a decentralized network eliminates the need of a 12 | trusted centralized party or a server typical of other applications. These 13 | features of smart contracts have become so popular today that they now drive 14 | real-world economies through applications such as crowdfunding, games, 15 | decentralized exchanges, payment processors among many others. 16 | 17 | 18 | However, experience over the last few years has shown that implemented 19 | operational semantics of smart contract languages admit rather subtle behaviour 20 | that diverge from the `intuitive understanding` of the language in the minds of 21 | contract developers. This divergence has led to some of the largest attacks on 22 | smart contracts, e.g., the attack on the DAO contract and Parity wallet among 23 | others. The problem becomes even more severe because smart contracts cannot 24 | directly be updated due to the immutable nature of blockchains. It is hence 25 | crucial to ensure that smart contracts that get deployed are safe to run. 26 | 27 | 28 | Formal methods such as verification and model checking have proven to be 29 | effective in improving the safety of software systems in other disciplines and 30 | hence it is natural to explore their applicability in improving the readability 31 | and safety of smart contracts. Moreover, with formal methods, it becomes 32 | possible to produce rigorous guarantees about the behavior of a contract. 33 | 34 | 35 | Applying formal verification tools with existing languages such as Solidity 36 | however is not an easy task because of the extreme expressivity typical of a 37 | Turing-complete language. Indeed, there is a trade-off between making a 38 | language simpler to understand and amenable to formal verification, and making 39 | it more expressive. For instance, Bitcoin's scripting language occupies the 40 | `simpler` end of the spectrum and does not handle stateful-objects. On the 41 | `expressive` side of the spectrum is a Turing-complete language such as 42 | Solidity. 43 | 44 | `Scilla` is a new (intermediate-level) smart contract language that has been 45 | designed to achieve both `expressivity` and `tractability` at the same time, 46 | while enabling formal reasoning about contract behavior by adopting certain 47 | fundamental design principles as described below: 48 | 49 | **Separation Between Computation and Communication** 50 | 51 | Contracts in Scilla are structured as communicating automata: every in-contract 52 | computation (e.g., changing its balance or computing a value of a function) is 53 | implemented as a standalone, atomic transition, i.e., without involving any 54 | other parties. Whenever such involvement is required (e.g., for transferring 55 | control to another party), a transition would end, with an explicit 56 | communication, by means of sending and receiving messages. The automata-based 57 | structure makes it possible to disentangle the contract-specific effects (i.e., 58 | transitions) from blockchain-wide interactions (i.e., sending/receiving funds 59 | and messages), thus providing a clean reasoning mechanism about contract 60 | composition and invariants. 61 | 62 | 63 | 64 | **Separation Between Effectful and Pure Computations** 65 | 66 | Any in-contract computation happening within a transition has to terminate, and 67 | have a predictable effect on the state of the contract and the execution. In 68 | order to achieve this, Scilla draws inspiration from functional programming 69 | with effects in distinguishing between pure expressions (e.g., expressions 70 | with primitive data types and maps), impure local state manipulations (i.e., 71 | reading/writing into contract fields), and blockchain reflection (e.g., reading 72 | current block number). By carefully designing semantics of interaction between 73 | pure and impure language aspects, Scilla ensures a number of foundational 74 | properties about contract transitions, such as progress and type preservation, 75 | while also making them amenable to interactive and/or automatic verification 76 | with standalone tools. 77 | 78 | **Separation Between Invocation and Continuation** 79 | 80 | Structuring contracts as communicating automata provides a computational model, 81 | which only allows `tail-calls`, i.e., every call to an external function (i.e., 82 | another contract) has to be done as the absolutely last instruction. 83 | 84 | -------------------------------------------------------------------------------- /docs/source/nstatic/imgs/callgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/scilla-docs/db915484d65e20d21771aec785d8c82cb2d07516/docs/source/nstatic/imgs/callgraph.png -------------------------------------------------------------------------------- /docs/source/nstatic/imgs/scilla-logo-bw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/scilla-docs/db915484d65e20d21771aec785d8c82cb2d07516/docs/source/nstatic/imgs/scilla-logo-bw.jpg -------------------------------------------------------------------------------- /docs/source/nstatic/imgs/scilla-logo-color-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/scilla-docs/db915484d65e20d21771aec785d8c82cb2d07516/docs/source/nstatic/imgs/scilla-logo-color-transparent.png -------------------------------------------------------------------------------- /docs/source/nstatic/imgs/scilla-logo-color.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/scilla-docs/db915484d65e20d21771aec785d8c82cb2d07516/docs/source/nstatic/imgs/scilla-logo-color.jpg -------------------------------------------------------------------------------- /docs/source/scilla-by-example.rst: -------------------------------------------------------------------------------- 1 | Scilla by Example 2 | ================== 3 | 4 | 5 | HelloWorld 6 | ################### 7 | 8 | We start off by writing a classical ``HelloWorld.scilla`` contract with the 9 | following specification: 10 | 11 | 12 | + It should have an `immutable contract parameter` ``owner`` to be initialized 13 | by the creator of the contract. The parameter is immutable in the sense that 14 | once initialized during contract deployment, its value cannot be changed. 15 | ``owner`` will be of type ``ByStr20`` (a hexadecimal Byte String representing 16 | a 20 byte address). 17 | 18 | + It should have a `mutable field` ``welcome_msg`` of type ``String`` 19 | initialized to ``""``. Mutability here refers to the possibility of modifying 20 | the value of a variable even after the contract has been deployed. 21 | 22 | + The ``owner`` and **only her** should be able to modify ``welcome_msg`` 23 | through an interface ``setHello``. The interface takes a ``msg`` (of type 24 | ``String``) as input and allows the ``owner`` to set the value of 25 | ``welcome_msg`` to ``msg``. 26 | 27 | + It should have an interface ``getHello`` that welcomes any caller with 28 | ``welcome_msg``. ``getHello`` will not take any input. 29 | 30 | 31 | Defining a Contract, its Immutable Parameters and Mutable Fields 32 | **************************************************************** 33 | 34 | A contract is declared using the ``contract`` keyword that starts the scope of 35 | the contract. The keyword is followed by the name of the contract which will be 36 | ``HelloWorld`` in our example. So, the following code fragment declares a 37 | ``HelloWorld`` contract. 38 | 39 | .. code-block:: ocaml 40 | 41 | contract HelloWorld 42 | 43 | 44 | .. note:: 45 | 46 | In the current implementation, a Scilla contract can only contain a single 47 | contract declaration and hence any code that follows the ``contract`` 48 | keyword is part of the contract declaration. In other words, there is no 49 | explicit keyword to declare the end of the contract definition. 50 | 51 | 52 | 53 | A contract declaration is followed by the declaration of its immutable 54 | parameters, the scope of which is defined by ``()``. Each immutable parameter is 55 | declared in the following way: ``vname: vtype``, where ``vname`` is the 56 | parameter name and ``vtype`` is the parameter type. Immutable parameters are 57 | separated by ``,``. As per the specification, the contract will have only one 58 | immutable parameter ``owner`` of type ``ByStr20`` and hence the following code 59 | fragment. 60 | 61 | 62 | .. code-block:: ocaml 63 | 64 | (owner: ByStr20) 65 | 66 | Mutable fields in a contract are declared through keyword ``field``. Each 67 | mutable field is declared in the following way: ``field vname : vtype = 68 | init_val``, where ``vname`` is the field name, ``vtype`` is its type and 69 | ``init_val`` is the value to which the field has to be initialized. The 70 | ``HelloWorld`` contract has one mutable field ``welcome_msg`` of type ``String`` 71 | initialized to ``""``. This yields the following code fragment: 72 | 73 | .. code-block:: ocaml 74 | 75 | field welcome_msg : String = "" 76 | 77 | 78 | At this stage, our ``HelloWorld.scilla`` contract will have the following form 79 | that includes the contract name, its immutable parameters and mutable fields: 80 | 81 | .. code-block:: ocaml 82 | 83 | contract HelloWorld 84 | (owner: ByStr20) 85 | 86 | field welcome_msg : String = "" 87 | 88 | 89 | 90 | 91 | Defining Interfaces `aka` Transitions 92 | *************************************** 93 | 94 | Interfaces like ``setHello`` are referred to as `transitions` in Scilla. 95 | Transitions are similar to `functions` or `methods` in other languages. There is 96 | an important difference, however, most languages allow their functions or 97 | methods to be "interrupted" by a thread running in parallel, but Scilla won't 98 | let a transition to be interrupted ensuring there is no so-called reentrancy 99 | issues. 100 | 101 | 102 | .. note:: 103 | 104 | The term `transition` comes from the underlying computation model in Scilla 105 | which follows a communicating automaton. A contract in Scilla is an 106 | automaton with some state. The state of an automaton can be changed using a 107 | transition that takes a previous state and an input and yields a new state. 108 | Check the `wikipedia entry `_ 109 | to read more about transition systems. 110 | 111 | A transition is declared using the keyword ``transition``. The end of a 112 | transition scope is declared using the keyword ``end``. The ``transition`` 113 | keyword is followed by the transition name, which is ``setHello`` for our 114 | example. Then follows the input parameters within ``()``. Each input parameter 115 | is separated by a ``,`` and is declared in the following format: ``vname : 116 | vtype``. According to the specification, ``setHello`` takes only one parameter 117 | of name ``msg`` of type ``String``. This yields the following code fragment: 118 | 119 | .. code-block:: ocaml 120 | 121 | transition setHello (msg : String) 122 | 123 | What follows the transition signature is the body of the transition. Code for 124 | the first transition ``setHello (msg : String)`` to set ``welcome_msg`` is 125 | given below: 126 | 127 | 128 | .. code-block:: ocaml 129 | :linenos: 130 | 131 | transition setHello (msg : String) 132 | is_owner = builtin eq owner _sender; 133 | match is_owner with 134 | | False => 135 | e = {_eventname : "setHello"; code : not_owner_code}; 136 | event e 137 | | True => 138 | welcome_msg := msg; 139 | e = {_eventname : "setHello"; code : set_hello_code}; 140 | event e 141 | end 142 | end 143 | 144 | At first, the caller of the transition is checked against the ``owner`` using 145 | the instruction ``builtin eq owner _sender`` in ``Line 2``. In order to compare 146 | two addresses, we are using the function ``eq`` defined as a ``builtin`` 147 | operator. The operator returns a Boolean value ``True`` or ``False``. 148 | 149 | 150 | .. note:: 151 | 152 | Scilla internally defines some variables that have special semantics. These 153 | special variables are often prefixed by ``_``. For instance, ``_sender`` in 154 | Scilla refers to the account address that called the current contract. 155 | 156 | Depending on the output of the comparison, the transition takes a different path 157 | declared using `pattern matching`, the syntax of which is given in the fragment 158 | below. 159 | 160 | .. code-block:: ocaml 161 | 162 | match expr with 163 | | pattern_1 => expr_1 164 | | pattern_2 => expr_2 165 | end 166 | 167 | The above code checks whether ``expr`` evaluates to a value that 168 | matches ``pattern_1`` or ``pattern_2``. If ``expr`` evaluates to a 169 | value matching ``pattern_1``, then the next expression to be evaluated 170 | will be ``expr_1``. Otherwise, if ``expr`` evaluates to a value 171 | matching ``pattern_2``, then the next expression to be evaluated will 172 | be ``expr_2``. 173 | 174 | Hence, the following code block implements an ``if-then-else`` instruction: 175 | 176 | .. code-block:: ocaml 177 | 178 | match expr with 179 | | True => expr_1 180 | | False => expr_2 181 | end 182 | 183 | 184 | The Caller is Not the Owner 185 | """"""""""""""""""""""""""""" 186 | 187 | In case the caller is different from ``owner``, the transition takes 188 | the ``False`` branch and the contract emits an event using the 189 | instruction ``event``. 190 | 191 | An event is a signal that gets stored on the blockchain for everyone 192 | to see. If a user uses a client application to invoke a transition on 193 | a contract, the client application can listen for events that the 194 | contract may emit, and alert the user. 195 | 196 | More concretely, the output event in this case is: 197 | 198 | .. code-block:: ocaml 199 | 200 | e = {_eventname : "setHello"; code : not_owner_code}; 201 | 202 | An event is comprised of a number of ``vname : value`` pairs delimited 203 | by ``;`` inside a pair of curly braces ``{}``. An event must contain 204 | the compulsory field ``_eventname``, and may contain other fields such 205 | as the ``code`` field in the example above. 206 | 207 | .. note:: 208 | 209 | In our example we have chosen to name the event after the 210 | transition that emits the event, but any name can be 211 | chosen. However, it is recommended that you name the events in a 212 | way that makes it easy to see which part of the code emitted the 213 | event. 214 | 215 | 216 | 217 | 218 | The Caller is the Owner 219 | """""""""""""""""""""""" 220 | 221 | In case the caller is ``owner``, the contract allows the caller to set the 222 | value of the mutable field ``welcome_msg`` to the input parameter ``msg``. 223 | This is done through the following instruction: 224 | 225 | 226 | .. code-block:: ocaml 227 | 228 | welcome_msg := msg; 229 | 230 | 231 | .. note:: 232 | 233 | Writing to a mutable field is done using the operator ``:=``. 234 | 235 | 236 | And as in the previous case, the contract then emits an event with 237 | the code ``set_hello_code``. 238 | 239 | 240 | Libraries 241 | *************** 242 | 243 | A Scilla contract may come with some helper libraries that declare 244 | purely functional components of a contract, i.e., components with no 245 | state manipulation. A library is declared in the preamble of a 246 | contract using the keyword ``library`` followed by the name of the 247 | library. In our current example a library declaration would look as 248 | follows: 249 | 250 | .. code-block:: ocaml 251 | 252 | library HelloWorld 253 | 254 | The library may include utility functions and program constants using 255 | the ``let ident = expr`` construct. In our example the library will 256 | only include the definition of error codes: 257 | 258 | .. code-block:: ocaml 259 | 260 | let not_owner_code = Uint32 1 261 | let set_hello_code = Uint32 2 262 | 263 | At this stage, our contract fragment will have the following form: 264 | 265 | .. code-block:: ocaml 266 | 267 | library HelloWorld 268 | 269 | let not_owner_code = Uint32 1 270 | let set_hello_code = Uint32 2 271 | 272 | 273 | contract HelloWorld 274 | (owner: ByStr20) 275 | 276 | field welcome_msg : String = "" 277 | 278 | transition setHello (msg : String) 279 | is_owner = builtin eq owner _sender; 280 | match is_owner with 281 | | False => 282 | e = {_eventname : "setHello"; code : not_owner_code}; 283 | event e 284 | | True => 285 | welcome_msg := msg; 286 | e = {_eventname : "setHello"; code : set_hello_code}; 287 | event e 288 | end 289 | end 290 | 291 | 292 | Adding Another Transition 293 | *************************** 294 | 295 | We may now add the second transition ``getHello()`` that allows client 296 | applications to know what the ``welcome_msg`` is. The declaration is 297 | similar to ``setHello (msg : String)`` except that ``getHello()`` does 298 | not take a parameter. 299 | 300 | .. code-block:: ocaml 301 | 302 | transition getHello () 303 | r <- welcome_msg; 304 | e = {_eventname: "getHello"; msg: r}; 305 | event e 306 | end 307 | 308 | .. note:: 309 | 310 | Reading from a local mutable field, i.e., a field defined in the current contract, is done using the operator ``<-``. 311 | 312 | In the ``getHello()`` transition, we will first read from a mutable field, and 313 | then we construct and emit the event. 314 | 315 | 316 | Scilla Version 317 | *************** 318 | 319 | Once a contract has been deployed on the network, it cannot be 320 | changed. It is therefore necessary to specify which version of Scilla 321 | the contract is written in, so as to ensure that the behaviour of the 322 | contract does not change even if changes are made to the Scilla 323 | specification. 324 | 325 | The Scilla version of the contract is declared using the keyword 326 | ``scilla_version``: 327 | 328 | .. code-block:: ocaml 329 | 330 | scilla_version 0 331 | 332 | The version declaration must appear before any library or contract 333 | code. 334 | 335 | 336 | Putting it All Together 337 | ************************* 338 | 339 | The complete contract that implements the desired specification is 340 | given below, where we have added comments using the ``(* *)`` 341 | construct: 342 | 343 | .. code-block:: ocaml 344 | 345 | (* HelloWorld contract *) 346 | 347 | (***************************************************) 348 | (* Scilla version *) 349 | (***************************************************) 350 | 351 | scilla_version 0 352 | 353 | (***************************************************) 354 | (* Associated library *) 355 | (***************************************************) 356 | library HelloWorld 357 | 358 | let not_owner_code = Uint32 1 359 | let set_hello_code = Uint32 2 360 | 361 | (***************************************************) 362 | (* The contract definition *) 363 | (***************************************************) 364 | 365 | contract HelloWorld 366 | (owner: ByStr20) 367 | 368 | field welcome_msg : String = "" 369 | 370 | transition setHello (msg : String) 371 | is_owner = builtin eq owner _sender; 372 | match is_owner with 373 | | False => 374 | e = {_eventname : "setHello"; code : not_owner_code}; 375 | event e 376 | | True => 377 | welcome_msg := msg; 378 | e = {_eventname : "setHello"; code : set_hello_code}; 379 | event e 380 | end 381 | end 382 | 383 | transition getHello () 384 | r <- welcome_msg; 385 | e = {_eventname: "getHello"; msg: r}; 386 | event e 387 | end 388 | 389 | 390 | 391 | A Second Example: Crowdfunding 392 | ################################# 393 | 394 | In this section, we present a slightly more involved contract that runs a 395 | crowdfunding campaign. In a crowdfunding campaign, a project owner wishes to 396 | raise funds through donations from the community. 397 | 398 | It is assumed that the owner (``owner``) wishes to run the campaign 399 | until a certain, predetermined block number is reached on the 400 | blockchain (``max_block``). The owner also wishes to raise a minimum 401 | amount of QA (``goal``) without which the project can not be 402 | started. The contract hence has three immutable parameters ``owner``, 403 | ``max_block`` and ``goal``. 404 | 405 | The immutable parameters are provided when the contract is deployed. At 406 | that point we wish to add a sanity check that the ``goal`` is a 407 | strictly positive amount. If the contract is accidentally initialised 408 | with a ``goal`` of 0, then the contract should not be deployed. 409 | 410 | The total amount that has been donated to the campaign so far is 411 | stored in a field ``_balance``. Any contract in Scilla has an implicit 412 | ``_balance`` field of type ``Uint128``, which is initialised to 0 when 413 | the contract is deployed, and which holds the amount of QA in the 414 | contract's account on the blockchain. 415 | 416 | The campaign is deemed successful if the owner can raise the goal in 417 | the stipulated time. In case the campaign is unsuccessful, the 418 | donations are returned to the project backers who contributed during 419 | the campaign. The backers are supposed to ask for refund explicitly. 420 | 421 | The contract maintains two mutable fields: 422 | 423 | - ``backers``: a field map from a contributor's address (a ``ByStr20`` value) 424 | to the amount contributed, represented with a ``Uint128`` value. 425 | Since there are no backers initially, this map is initialized to an 426 | ``Emp`` (empty) map. The map enables the contract to register a donor, 427 | prevent multiple donations and to refund back the money if the campaign 428 | does not succeed. 429 | 430 | - ``funded``: a Boolean flag initialized to ``False`` that indicates 431 | whether the owner has already transferred the funds after the end of 432 | the campaign. 433 | 434 | The contract contains three transitions: ``Donate ()`` that allows anyone to 435 | contribute to the crowdfunding campaign, ``GetFunds ()`` that allows **only the 436 | owner** to claim the donated amount and transfer it to ``owner`` and 437 | ``ClaimBack()`` that allows contributors to claim back their donations in case 438 | the campaign is not successful. 439 | 440 | Sanity check for contract parameters 441 | ********************************************* 442 | 443 | To ensure that the ``goal`` is a strictly positive amount, we use a 444 | `contract constraint`: 445 | 446 | .. code-block:: ocaml 447 | 448 | with 449 | let zero = Uint128 0 in 450 | builtin lt zero goal 451 | => 452 | 453 | The Boolean expression between ``with`` and ``=>`` above is evaluated during 454 | contract deployment and the contract only gets deployed if the result of 455 | evaluation is ``True``. This ensures that the contract cannot be deployed with a 456 | ``goal`` of 0 by mistake. 457 | 458 | 459 | Reading the Current Block Number 460 | ********************************** 461 | 462 | The deadline is given as a block number, so to check whether the 463 | deadline has passed, we must compare the deadline against the current 464 | block number. 465 | 466 | The current block number is read as follows: 467 | 468 | .. code-block:: ocaml 469 | 470 | blk <- & BLOCKNUMBER; 471 | 472 | Block numbers have a dedicated type ``BNum`` in Scilla, so as to not 473 | confuse them with regular unsigned integers. 474 | 475 | .. note:: 476 | 477 | Reading data from the blockchain is done using the operator 478 | ``<- &``. Blockchain data cannot be updated directly from the 479 | contract. 480 | 481 | 482 | Reading and Updating the Current Balance 483 | ****************************************** 484 | 485 | The target for the campaign is specified by the owner in the immutable 486 | parameter ``goal`` when the contract is deployed. To check whether the 487 | target have been met, we must compare the total amount raised to the 488 | target. 489 | 490 | The amount of QA raised is stored in the contract's account on the 491 | blockchain, and can be accessed through the implicitly declared 492 | ``_balance`` field as follows: 493 | 494 | .. code-block:: ocaml 495 | 496 | bal <- _balance; 497 | 498 | Money is represented as values of type ``Uint128``. 499 | 500 | .. note:: 501 | 502 | The ``_balance`` field is read using the operator ``<-`` just like any other 503 | contract field. However, the ``_balance`` field can only be updated by 504 | accepting money from incoming messages (using the instruction ``accept``), or 505 | by explicitly transferring money to other account (using the instruction 506 | ``send`` as explained below). 507 | 508 | 509 | 510 | Sending Messages 511 | ********************** 512 | 513 | In Scilla, there are two ways that transitions can transmit data. One 514 | way is through events, as covered in the previous example. The other 515 | is through the sending of messages using the instruction ``send``. 516 | 517 | ``send`` is used to send messages to other accounts, either in order 518 | to invoke transitions on another smart contract, or to transfer money 519 | to user accounts. On the other hand, events are dispatched signals 520 | that smart contracts can use to transmit data to client applications. 521 | 522 | To construct a message we use a similar syntax as when constructing 523 | events: 524 | 525 | .. code-block:: ocaml 526 | 527 | msg = {_tag : ""; _recipient : owner; _amount : bal; code : got_funds_code}; 528 | 529 | A message must contain the compulsory `message fields` ``_tag``, ``_recipient`` 530 | and ``_amount``. The ``_recipient`` message field is the blockchain address (of 531 | type ``ByStr20``) that the message is to be sent to, and the ``_amount`` message 532 | field is the number of QA to be transferred to that account. 533 | 534 | The value of the ``_tag`` message field is the name of the transition (of type 535 | ``String``) that is to be invoked on the contract deployed at ``_recipient`` 536 | address. If ``_recipient`` is the address of a user account then the value of 537 | ``_tag`` is ignored, hence for simplicity we put ``""`` here. 538 | 539 | .. note:: 540 | 541 | To make it possible to refund both contracts and user accounts (this is 542 | useful if a backer used a wallet contract to donate), use a standard 543 | transition name as per `ZRC-5 544 | `_, i.e. 545 | ``AddFunds``. 546 | 547 | In addition to the compulsory fields the message may contain other 548 | fields, such as ``code`` above. However, if the message recipient is a 549 | contract, the additional fields must have the same names and types as 550 | the parameters of the transition being invoked on the recipient 551 | contract. 552 | 553 | Sending a message is done using the ``send`` instruction, which takes 554 | a list of messages as a parameter. Since we will only ever send one 555 | message at a time in the crowdfunding contract, we define a library 556 | function ``one_msg`` to construct a list consisting of one message: 557 | 558 | .. code-block:: ocaml 559 | 560 | let one_msg = 561 | fun (msg : Message) => 562 | let nil_msg = Nil {Message} in 563 | Cons {Message} msg nil_msg 564 | 565 | 566 | To send out a message, we first construct the message, insert it into 567 | a list, and send it: 568 | 569 | .. code-block:: ocaml 570 | 571 | msg = {_tag : ""; _recipient : owner; _amount : bal; code : got_funds_code}; 572 | msgs = one_msg msg; 573 | send msgs 574 | 575 | 576 | Procedures 577 | ********************** 578 | 579 | The transitions of a Scilla contract often need to perform the same 580 | small sequence of instructions. In order to prevent code duplication a 581 | contract may define a number of `procedures`, which may be invoked 582 | from the contract's transitions. Procedures also help divide the 583 | contract code into separate, self-contained pieces which are easier to 584 | read and reason about individually. 585 | 586 | A procedure is declared using the keyword ``procedure``. The end of a 587 | procedure is declared using the keyword ``end``. The ``procedure`` 588 | keyword is followed by the transition name, then the input parameters 589 | within ``()``, and then the statements of the procedure. 590 | 591 | In our example the ``Donate`` transition will issue an event in three 592 | situations: An error event if the donation happens after the deadline, 593 | another error event if the backer has donated money previously, and a 594 | non-error event indicating a successful donation. Since much of the 595 | event issuing code is identical, we decide to define a procedure 596 | ``DonationEvent`` which is responsible for issuing the correct event: 597 | 598 | .. code-block:: ocaml 599 | 600 | procedure DonationEvent (failure : Bool, error_code : Int32) 601 | match failure with 602 | | False => 603 | e = {_eventname : "DonationSuccess"; donor : _sender; 604 | amount : _amount; code : accepted_code}; 605 | event e 606 | | True => 607 | e = {_eventname : "DonationFailure"; donor : _sender; 608 | amount : _amount; code : error_code}; 609 | event e 610 | end 611 | end 612 | 613 | The procedure takes two arguments: A ``Bool`` indicating whether the 614 | donation failed, and an error code indicating the type of failure if a 615 | failure did indeed occur. 616 | 617 | The procedure performs a ``match`` on the ``failure`` argument. If the 618 | donation did not fail, the error code is ignored, and a 619 | ``DonationSuccess`` event is issued. Otherwise, if the donation 620 | failed, then a ``DonationFailure`` event is issued with the error code 621 | that was passed as the second argument to the procedure. 622 | 623 | The following code shows how to invoke the ``DonationEvent`` 624 | procedure with the arguments ``True`` and ``0``: 625 | 626 | .. code-block:: ocaml 627 | 628 | c = True; 629 | err_code = Int32 0; 630 | DonationEvent c err_code; 631 | 632 | 633 | .. note:: 634 | 635 | The special parameters ``_sender``, ``_origin`` and ``_amount`` are available to 636 | a procedure even though the procedure is invoked by a transition 637 | rather than by an incoming message. It is not necessary to pass 638 | these special parameters as arguments to the procedure. 639 | 640 | .. note:: 641 | 642 | Procedures are similar to library functions in that they can be 643 | invoked from any transition (as long as the transition is defined 644 | after the procedure). However, procedures are different from 645 | library functions in that library functions cannot access the 646 | contract state, and procedures cannot return a value. 647 | 648 | Procedures are similar to transitions in that they can access and 649 | change the contract state, as well as read the incoming messages 650 | and send outgoing messages. However, procedures cannot be invoked 651 | from the blockchain layer. Only transitions may be invoked from 652 | outside the contract, so procedures can be viewed as private 653 | transitions. 654 | 655 | 656 | 657 | Putting it All Together 658 | ************************* 659 | 660 | The complete crowdfunding contract is given below. 661 | 662 | .. code-block:: ocaml 663 | 664 | 665 | (***************************************************) 666 | (* Scilla version *) 667 | (***************************************************) 668 | 669 | scilla_version 0 670 | 671 | (***************************************************) 672 | (* Associated library *) 673 | (***************************************************) 674 | import BoolUtils 675 | 676 | library Crowdfunding 677 | 678 | let one_msg = 679 | fun (msg : Message) => 680 | let nil_msg = Nil {Message} in 681 | Cons {Message} msg nil_msg 682 | 683 | let blk_leq = 684 | fun (blk1 : BNum) => 685 | fun (blk2 : BNum) => 686 | let bc1 = builtin blt blk1 blk2 in 687 | let bc2 = builtin eq blk1 blk2 in 688 | orb bc1 bc2 689 | 690 | let get_funds_allowed = 691 | fun (cur_block : BNum) => 692 | fun (max_block : BNum) => 693 | fun (balance : Uint128) => 694 | fun (goal : Uint128) => 695 | let in_time = blk_leq cur_block max_block in 696 | let deadline_passed = negb in_time in 697 | let target_not_reached = builtin lt balance goal in 698 | let target_reached = negb target_not_reached in 699 | andb deadline_passed target_reached 700 | 701 | let claimback_allowed = 702 | fun (balance : Uint128) => 703 | fun (goal : Uint128) => 704 | fun (already_funded : Bool) => 705 | let target_not_reached = builtin lt balance goal in 706 | let not_already_funded = negb already_funded in 707 | andb target_not_reached not_already_funded 708 | 709 | let accepted_code = Int32 1 710 | let missed_deadline_code = Int32 2 711 | let already_backed_code = Int32 3 712 | let not_owner_code = Int32 4 713 | let too_early_code = Int32 5 714 | let got_funds_code = Int32 6 715 | let cannot_get_funds = Int32 7 716 | let cannot_reclaim_code = Int32 8 717 | let reclaimed_code = Int32 9 718 | 719 | (***************************************************) 720 | (* The contract definition *) 721 | (***************************************************) 722 | contract Crowdfunding 723 | 724 | (* Parameters *) 725 | (owner : ByStr20, 726 | max_block : BNum, 727 | goal : Uint128) 728 | 729 | (* Contract constraint *) 730 | with 731 | let zero = Uint128 0 in 732 | builtin lt zero goal 733 | => 734 | 735 | (* Mutable fields *) 736 | field backers : Map ByStr20 Uint128 = Emp ByStr20 Uint128 737 | field funded : Bool = False 738 | 739 | procedure DonationEvent (failure : Bool, error_code : Int32) 740 | match failure with 741 | | False => 742 | e = {_eventname : "DonationSuccess"; donor : _sender; 743 | amount : _amount; code : accepted_code}; 744 | event e 745 | | True => 746 | e = {_eventname : "DonationFailure"; donor : _sender; 747 | amount : _amount; code : error_code}; 748 | event e 749 | end 750 | end 751 | 752 | procedure PerformDonate () 753 | c <- exists backers[_sender]; 754 | match c with 755 | | False => 756 | accept; 757 | backers[_sender] := _amount; 758 | DonationEvent c accepted_code 759 | | True => 760 | DonationEvent c already_backed_code 761 | end 762 | end 763 | 764 | transition Donate () 765 | blk <- & BLOCKNUMBER; 766 | in_time = blk_leq blk max_block; 767 | match in_time with 768 | | True => 769 | PerformDonate 770 | | False => 771 | t = True; 772 | DonationEvent t missed_deadline_code 773 | end 774 | end 775 | 776 | procedure GetFundsFailure (error_code : Int32) 777 | e = {_eventname : "GetFundsFailure"; caller : _sender; 778 | amount : _amount; code : error_code}; 779 | event e 780 | end 781 | 782 | procedure PerformGetFunds () 783 | bal <- _balance; 784 | tt = True; 785 | funded := tt; 786 | msg = {_tag : ""; _recipient : owner; _amount : bal; code : got_funds_code}; 787 | msgs = one_msg msg; 788 | send msgs 789 | end 790 | 791 | transition GetFunds () 792 | is_owner = builtin eq owner _sender; 793 | match is_owner with 794 | | False => 795 | GetFundsFailure not_owner_code 796 | | True => 797 | blk <- & BLOCKNUMBER; 798 | bal <- _balance; 799 | allowed = get_funds_allowed blk max_block bal goal; 800 | match allowed with 801 | | False => 802 | GetFundsFailure cannot_get_funds 803 | | True => 804 | PerformGetFunds 805 | end 806 | end 807 | end 808 | 809 | procedure ClaimBackFailure (error_code : Int32) 810 | e = {_eventname : "ClaimBackFailure"; caller : _sender; 811 | amount : _amount; code : error_code}; 812 | event e 813 | end 814 | 815 | procedure PerformClaimBack (amount : Uint128) 816 | delete backers[_sender]; 817 | msg = {_tag : ""; _recipient : _sender; _amount : amount; code : reclaimed_code}; 818 | msgs = one_msg msg; 819 | e = { _eventname : "ClaimBackSuccess"; caller : _sender; amount : amount; code : reclaimed_code}; 820 | event e; 821 | send msgs 822 | end 823 | 824 | transition ClaimBack () 825 | blk <- & BLOCKNUMBER; 826 | after_deadline = builtin blt max_block blk; 827 | match after_deadline with 828 | | False => 829 | ClaimBackFailure too_early_code 830 | | True => 831 | bal <- _balance; 832 | f <- funded; 833 | allowed = claimback_allowed bal goal f; 834 | match allowed with 835 | | False => 836 | ClaimBackFailure cannot_reclaim_code 837 | | True => 838 | res <- backers[_sender]; 839 | match res with 840 | | None => 841 | (* Sender has not donated *) 842 | ClaimBackFailure cannot_reclaim_code 843 | | Some v => 844 | PerformClaimBack v 845 | end 846 | end 847 | end 848 | end 849 | 850 | A Third Example: A Simple Token Exchange 851 | ######################################## 852 | 853 | As a third example we look at how contracts written in Scilla can 854 | interact by passing messages to each other, and by reading each 855 | other's states. As our example application we choose a simplified 856 | token exchange contracts in which users can place offers of swapping 857 | one type of fungible tokens for another type. 858 | 859 | Fungible Tokens 860 | **************** 861 | 862 | Recall that a fungible token is one which is indistinguishable from 863 | another token of the same type. For example, a US $1 bank note is 864 | indistinguishable from any other US $1 bank note (for the purposes of 865 | using the bank note to pay for goods, services, or other tokens, at 866 | least). 867 | 868 | The `Zilliqa Reference Contracts `_ 869 | library offers specifications and reference implementations of 870 | commonly used contract types, and the `ZRC2 871 | `_ standard 872 | specifies a standard for fungible tokens, which we will use for this 873 | example. We will not go into detail about how the token contract 874 | works, but only point out a few important aspects that will be needed 875 | in order to implement the token exchange. 876 | 877 | 878 | Exchange Specification 879 | *********************** 880 | 881 | We want our simple exchange to support the following functionality: 882 | 883 | + The exchange has a number of listed tokens that can be freely 884 | swapped with each other. Each listed token is identified by its 885 | token code (e.g., "USD" for US dollars). 886 | 887 | + The exchange should have an administrator at all times. The 888 | administrator is in charge of approving token contracts, and listing 889 | them on the exchange. The administrator may pass the administrator 890 | role on to someone else. 891 | 892 | + Any user can place an order on the exchange. To place an order, the 893 | user specifies which token he wants to sell and how many of them he 894 | is offering, and which token he wants to buy and how many he 895 | wants in return. The contract keeps track of every active 896 | (unmatched) order. 897 | 898 | + When a user attempts to place an order to sell some tokens, the 899 | exchange checks that the user actually has those tokens to sell. If 900 | he does, then the exchange claims those tokens and holds on to them 901 | until the order is matched. 902 | 903 | + Any user can match an active order on the exchange. To match an 904 | order, the user specifies which order to match. 905 | 906 | + When a user attempts to match an order, the exchange checks that the 907 | user actually has the tokens that the order placer wants to buy. If 908 | he does, then the exchange transfers the tokens that were claimed 909 | when the order was placed to the order matcher, and transfers the 910 | tokens that the order placer wants to buy from the order matcher to 911 | the order placer. After the tokens have been transferred the 912 | exchange deletes the fulfilled order. 913 | 914 | To keep the example brief our exchange will not support unlisting of 915 | tokens, cancellation of orders, orders with expiry time, prioritising 916 | orders so that the order matcher gets the best deal possible, partial 917 | matching of orders, securing the exchange against abuse, fees for 918 | trading on the exchange, etc.. We encourage the reader to implement 919 | additional features as a way to familiarise themselves even further 920 | with Scilla. 921 | 922 | 923 | The Administrator Role 924 | ******************************** 925 | 926 | The exchange must have an administrator at all times, including when 927 | it is first deployed. The administrator may change over time, so we 928 | define a mutable field ``admin`` to keep track of the current 929 | administrator, and initialise it to an ``initial_admin``, which is 930 | given as an immutable parameter: 931 | 932 | .. code-block:: ocaml 933 | 934 | contract SimpleExchange 935 | ( 936 | initial_admin : ByStr20 with end 937 | ) 938 | 939 | field admin : ByStr20 with end = initial_admin 940 | 941 | The type of the ``admin`` field is ``ByStr20 with end``, which is an 942 | `address type`. As in the earlier examples ``ByStr20`` is the type of 943 | byte strings of length 20, but we now add the additional requirement 944 | that when that byte string is interpreted as an address on the 945 | network, the address must be `in use`, and the contents at that 946 | address must satisfy whatever is between the ``with`` and ``end`` 947 | keywords. 948 | 949 | In this case there is nothing between ``with`` and ``end``, so we have 950 | no additional requirements. However, the address must be in use, 951 | either by a user or by another contract - otherwise Scilla will not 952 | accept it as having a legal address type. (We will go into more detail 953 | about address types when the exchange interacts with the listed token 954 | contracts.) 955 | 956 | Multiple transitions will need to check that the ``_sender`` is the 957 | current ``admin``, so let us define a procedure that checks that that 958 | is the case: 959 | 960 | .. code-block:: ocaml 961 | 962 | procedure CheckSenderIsAdmin() 963 | current_admin <- admin; 964 | is_admin = builtin eq _sender current_admin; 965 | match is_admin with 966 | | True => (* Nothing to do *) 967 | | False => 968 | (* Construct an exception object and throw it *) 969 | e = { _exception : "SenderIsNotAdmin" }; 970 | throw e 971 | end 972 | end 973 | 974 | If the ``_sender`` is the current administrator, then nothing happens, 975 | and whichever transition called this procedure can continue. If the 976 | ``_sender`` is someone else, however, the procedure throws an 977 | `exception` causing the current transaction to be aborted. 978 | 979 | We want the administrator to be able to pass on the administrator role 980 | to someone else, so we define our first transition ``SetAdmin`` as 981 | follows: 982 | 983 | .. code-block:: ocaml 984 | 985 | transition SetAdmin(new_admin : ByStr20 with end) 986 | (* Only the former admin may appoint a new admin *) 987 | CheckSenderIsAdmin; 988 | admin := new_admin 989 | end 990 | 991 | The transition applies the ``CheckSenderIsAdmin`` procedure, and if no 992 | exception is thrown then the sender is indeed the current 993 | administrator, and is thus allowed to pass on the administrator role 994 | on to someone else. The new admin must once again be an address that 995 | is in use. 996 | 997 | 998 | Intermezzo: Transferring Tokens On Behalf Of The Token Owner 999 | ************************************************************* 1000 | 1001 | Before we continue adding features to our exchange we must first look 1002 | at how token contracts transfer tokens between users. 1003 | 1004 | The ZRC2 token standard defines a field ``balances`` which keeps 1005 | track of how many tokens each user has: 1006 | 1007 | .. code-block:: ocaml 1008 | 1009 | field balances: Map ByStr20 Uint128 1010 | 1011 | However, this is not particularly useful for our exchange, because the 1012 | token contract won't allow the exchange to transfer tokens belonging 1013 | to someone other than the exchange itself. 1014 | 1015 | Instead, the ZRC2 standard defines a field ``allowances``, which a 1016 | user who owns tokens can use to allow another user partial access to 1017 | the owner's tokens: 1018 | 1019 | .. code-block:: ocaml 1020 | 1021 | field allowances: Map ByStr20 (Map ByStr20 Uint128) 1022 | 1023 | For instance, if Alice has given Bob an allowance of 100 tokens, then 1024 | the ``allowances`` map in token contract will contain the value 1025 | ``allowances[
][
] = 100``. This 1026 | allows Bob to spend 100 of Alice's tokens as if they were his 1027 | own. (Alice can of course withdraw the allowance, as long as Bob 1028 | hasn't yet spent the tokens). 1029 | 1030 | Before a user places an order, the user should provide the exchange 1031 | with an allowance of the token he wants to sell to cover the 1032 | order. The user can then place the order, and the exchange can check 1033 | that the allowance is sufficient. The exchange then transfers the 1034 | tokens to its own account for holding until the order is matched. 1035 | 1036 | Similarly, before a user matches an order, the user should provide the 1037 | exchange with an allowance of the token that the order placer wants to 1038 | buy. The user can then match the order, and the exchange can check 1039 | that the allowance is sufficient. The exchange then transfers those 1040 | tokens to the user who placed the order, and transfers to the matching 1041 | user the tokens that it transferred to itself when the order was 1042 | placed. 1043 | 1044 | In order to check the current allowance that a user has given to the 1045 | exchange, we will need to specify the ``allowances`` field in the 1046 | token address type. We do this as follows: 1047 | 1048 | .. code-block:: ocaml 1049 | 1050 | ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end 1051 | 1052 | As with the ``admin`` field we require that the address is in 1053 | use. Additionally, the requirements between ``with`` and ``end`` must 1054 | also be satisfied: 1055 | 1056 | + The keyword ``contract`` specifies that the address must be in use 1057 | by a contract, and not by a user. 1058 | 1059 | + The keyword ``field`` specifies that the contract in question must 1060 | contain a mutable field with the specified name and of the specified 1061 | type. 1062 | 1063 | 1064 | Listing a New Token 1065 | ******************** 1066 | 1067 | The exchange keeps track of its listed tokens, i.e., which tokens are 1068 | allowed to be traded on the exchange. We do this by defining a map 1069 | from the token code (a ``String``) to the address of the token. 1070 | 1071 | .. code-block:: ocaml 1072 | 1073 | field listed_tokens : 1074 | Map String (ByStr20 with contract 1075 | field allowances : Map ByStr20 (Map ByStr20 Uint128) 1076 | end) 1077 | = Emp String (ByStr20 with contract 1078 | field allowances : Map ByStr20 (Map ByStr20 Uint128) 1079 | end) 1080 | 1081 | Only the administrator is allowed to list new tokens, so we leverage 1082 | the ``CheckSenderIsAdmin`` procedure again here. 1083 | 1084 | Additionally, we only want to list tokens that have a different token 1085 | code from the previously listed tokens. For this purpose we define a 1086 | procedure ``CheckIsTokenUnlisted`` to check whether a token code is 1087 | defined as a key in the ``listed_tokens`` map. 1088 | : 1089 | 1090 | .. code-block:: ocaml 1091 | 1092 | library SimpleExchangeLib 1093 | 1094 | let false = False 1095 | 1096 | ... 1097 | 1098 | contract SimpleExchange (...) 1099 | 1100 | ... 1101 | 1102 | procedure ThrowListingStatusException( 1103 | token_code : String, 1104 | expected_status : Bool, 1105 | actual_status : Bool) 1106 | e = { _exception : "UnexpectedListingStatus"; 1107 | token_code: token_code; 1108 | expected : expected_status; 1109 | actual : actual_status }; 1110 | throw e 1111 | end 1112 | 1113 | procedure CheckIsTokenUnlisted( 1114 | token_code : String 1115 | ) 1116 | (* Is the token code listed? *) 1117 | token_code_is_listed <- exists listed_tokens[token_code]; 1118 | match token_code_is_listed with 1119 | | True => 1120 | (* Incorrect listing status *) 1121 | ThrowListingStatusException token_code false token_code_is_listed 1122 | | False => (* Nothing to do *) 1123 | end 1124 | end 1125 | 1126 | This time we define a helper procedure ``ThrowListingStatusException`` 1127 | which unconditionally throws an exception. This will be useful later 1128 | when we later write the transition for placing orders, because we will 1129 | need to check that the tokens involved in the order are listed. 1130 | 1131 | We also define the constant ``false`` in the contract's library. This 1132 | is due to the fact that Scilla requires all values to be named before 1133 | they are used in computations. Defining constants in library code 1134 | prevents us from cluttering the transition code with constant 1135 | definitions: 1136 | 1137 | .. code-block:: ocaml 1138 | 1139 | (* Incorrect listing status *) 1140 | false = False; (* We don't want to do it like this *) 1141 | ThrowListingStatusException token_code false token_code_is_listed 1142 | 1143 | With the helper procedures in place we are now ready to define the 1144 | ``ListToken`` transition as follows: 1145 | 1146 | .. code-block:: ocaml 1147 | 1148 | transition ListToken( 1149 | token_code : String, 1150 | new_token : ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end 1151 | ) 1152 | (* Only the admin may list new tokens. *) 1153 | CheckSenderIsAdmin; 1154 | (* Only new token codes are allowed. *) 1155 | CheckIsTokenUnlisted token_code; 1156 | (* Everything is ok. The token can be listed *) 1157 | listed_tokens[token_code] := new_token 1158 | end 1159 | 1160 | Placing an Order 1161 | ******************** 1162 | 1163 | To place an order a user must specify the token code and the amount of 1164 | the token he wants to sell, and the token code and amount he wants to 1165 | buy. We invoke the ``ThrowListingStatusException`` procedure if any of 1166 | the token codes are unlisted: 1167 | 1168 | .. code-block:: ocaml 1169 | 1170 | transition PlaceOrder( 1171 | token_code_sell : String, 1172 | sell_amount : Uint128, 1173 | token_code_buy: String, 1174 | buy_amount : Uint128 1175 | ) 1176 | (* Check that the tokens are listed *) 1177 | token_sell_opt <- listed_tokens[token_code_sell]; 1178 | token_buy_opt <- listed_tokens[token_code_buy]; 1179 | match token_sell_opt with 1180 | | Some token_sell => 1181 | match token_buy_opt with 1182 | | Some token_buy => 1183 | ... 1184 | | None => 1185 | (* Unlisted token *) 1186 | ThrowListingStatusException token_code_buy true false 1187 | end 1188 | | None => 1189 | (* Unlisted token *) 1190 | ThrowListingStatusException token_code_sell true false 1191 | end 1192 | end 1193 | 1194 | If both tokens are listed, we must first check that the user has 1195 | supplied a sufficient allowance to the exchange. We will need a 1196 | similar check when another user matches the order, so we define a 1197 | helper procedure ``CheckAllowance`` to perform the check: 1198 | 1199 | .. code-block:: ocaml 1200 | 1201 | procedure CheckAllowance( 1202 | token : ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end, 1203 | expected : Uint128 1204 | ) 1205 | ... 1206 | end 1207 | 1208 | To perform the check we will need to perform a `remote read` of the 1209 | ``allowances`` field in the token contract. We are interested in the 1210 | allowance given by the ``_sender`` to the exchange, whose address is 1211 | given by a special immutable field ``_this_address``, so we want to 1212 | remote read the value of ``allowances[_sender][_this_address]`` in the 1213 | token contract. 1214 | 1215 | Remote reads in Scilla are performed using the operator ``<- &``, and 1216 | we use ``.`` notation to specify the contract that we want to remote 1217 | read from. The entire statement for the remote read is therefore as 1218 | follows: 1219 | 1220 | .. code-block:: ocaml 1221 | 1222 | actual_opt <-& token.allowances[_sender][_this_address]; 1223 | 1224 | Just as when we perform a local read of a map, the result of reading 1225 | from a remote map is an optional value. If the result is ``Some v`` 1226 | for some ``v``, then the user has provided the exchange with an 1227 | allowance of ``v`` tokens, and if the result is ``None`` the user has 1228 | not supplied an allowance at all. We therefore need to pattern-match 1229 | the result to get the actual allowance: 1230 | 1231 | .. code-block:: ocaml 1232 | 1233 | (* Find actual allowance. Use 0 if None is given *) 1234 | actual = match actual_opt with 1235 | | Some x => x 1236 | | None => zero 1237 | end; 1238 | 1239 | Once again, we define the constant ``zero = Uint128 0`` in the 1240 | contract library for convenience. 1241 | 1242 | We can now compare the actual allowance to the allowance we are 1243 | expecting, and throw an exception if the actual allowance is 1244 | insufficient: 1245 | 1246 | .. code-block:: ocaml 1247 | 1248 | is_sufficient = uint128_le expected actual; 1249 | match is_sufficient with 1250 | | True => (* Nothing to do *) 1251 | | False => 1252 | ThrowInsufficientAllowanceException token expected actual 1253 | end 1254 | 1255 | The function ``uint128_le`` is a utility function which performs a 1256 | less-than-or-equal comparison on values of type ``Uint128``. The 1257 | function is defined in the ``IntUtils`` part of the standard library, 1258 | so in order to use the function we must import ``IntUtils`` into the 1259 | contract, which is done immediately after the ``scilla_version`` 1260 | preamble, and before the contract library definitions: 1261 | 1262 | .. code-block:: ocaml 1263 | 1264 | scilla_version 0 1265 | 1266 | import IntUtils 1267 | 1268 | library SimpleExchangeLib 1269 | ... 1270 | 1271 | 1272 | We also utilise a helper procedure 1273 | ``ThrowInsufficientAllowanceException`` to throw an exception if the 1274 | allowance is insufficient, so the ``CheckAllowance`` procedure ends up 1275 | looking as follows: 1276 | 1277 | .. code-block:: ocaml 1278 | 1279 | procedure ThrowInsufficientAllowanceException( 1280 | token : ByStr20, 1281 | expected : Uint128, 1282 | actual : Uint128) 1283 | e = { _exception : "InsufficientAllowance"; 1284 | token: token; 1285 | expected : expected; 1286 | actual : actual }; 1287 | throw e 1288 | end 1289 | 1290 | procedure CheckAllowance( 1291 | token : ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end, 1292 | expected : Uint128 1293 | ) 1294 | actual_opt <-& token.allowances[_sender][_this_address]; 1295 | (* Find actual allowance. Use 0 if None is given *) 1296 | actual = match actual_opt with 1297 | | Some x => x 1298 | | None => zero 1299 | end; 1300 | is_sufficient = uint128_le expected actual; 1301 | match is_sufficient with 1302 | | True => (* Nothing to do *) 1303 | | False => 1304 | ThrowInsufficientAllowanceException token expected actual 1305 | end 1306 | end 1307 | 1308 | transition PlaceOrder( 1309 | token_code_sell : String, 1310 | sell_amount : Uint128, 1311 | token_code_buy: String, 1312 | buy_amount : Uint128 1313 | ) 1314 | (* Check that the tokens are listed *) 1315 | token_sell_opt <- listed_tokens[token_code_sell]; 1316 | token_buy_opt <- listed_tokens[token_code_buy]; 1317 | match token_sell_opt with 1318 | | Some token_sell => 1319 | match token_buy_opt with 1320 | | Some token_buy => 1321 | (* Check that the placer has allowed sufficient funds to be accessed *) 1322 | CheckAllowance token_sell sell_amount; 1323 | ... 1324 | | None => 1325 | (* Unlisted token *) 1326 | ThrowListingStatusException token_code_buy true false 1327 | end 1328 | | None => 1329 | (* Unlisted token *) 1330 | ThrowListingStatusException token_code_sell true false 1331 | end 1332 | end 1333 | 1334 | If the user has given the exchange a sufficient allowance, the 1335 | exchange can send a message to the token contract to perform the 1336 | transfer of tokens from the allowance the exchange's own balance. The 1337 | transition we need to invoke on the token contract is called 1338 | ``TransferFrom``, as opposed to ``Transfer`` which transfers funds 1339 | from the sender's own token balance rather than from the sender's 1340 | allowance of someone else's balance. 1341 | 1342 | Since the message will look much like the messages that we need when 1343 | an order is matched, we generate the message using helper functions in 1344 | the contract library (we will also need a new constant ``true``): 1345 | 1346 | .. code-block:: ocaml 1347 | 1348 | library SimpleExchangeLib 1349 | 1350 | let true = True 1351 | 1352 | ... 1353 | 1354 | let one_msg : Message -> List Message = 1355 | fun (msg : Message) => 1356 | let mty = Nil { Message } in 1357 | Cons { Message } msg mty 1358 | 1359 | let mk_transfer_msg : Bool -> ByStr20 -> ByStr20 -> ByStr20 -> Uint128 -> Message = 1360 | fun (transfer_from : Bool) => 1361 | fun (token_address : ByStr20) => 1362 | fun (from : ByStr20) => 1363 | fun (to : ByStr20) => 1364 | fun (amount : Uint128) => 1365 | let tag = match transfer_from with 1366 | | True => "TransferFrom" 1367 | | False => "Transfer" 1368 | end 1369 | in 1370 | { _recipient : token_address; 1371 | _tag : tag; 1372 | _amount : Uint128 0; (* No Zil are transferred, only custom tokens *) 1373 | from : from; 1374 | to : to; 1375 | amount : amount } 1376 | 1377 | let mk_place_order_msg : ByStr20 -> ByStr20 -> ByStr20 -> Uint128 -> List Message = 1378 | fun (token_address : ByStr20) => 1379 | fun (from : ByStr20) => 1380 | fun (to : ByStr20) => 1381 | fun (amount : Uint128) => 1382 | (* Construct a TransferFrom messsage to transfer from seller's allowance to exhange *) 1383 | let msg = mk_transfer_msg true token_address from to amount in 1384 | (* Create a singleton list *) 1385 | one_msg msg 1386 | 1387 | contract SimpleExchange (...) 1388 | 1389 | ... 1390 | 1391 | transition PlaceOrder( 1392 | token_code_sell : String, 1393 | sell_amount : Uint128, 1394 | token_code_buy: String, 1395 | buy_amount : Uint128 1396 | ) 1397 | (* Check that the tokens are listed *) 1398 | token_sell_opt <- listed_tokens[token_code_sell]; 1399 | token_buy_opt <- listed_tokens[token_code_buy]; 1400 | match token_sell_opt with 1401 | | Some token_sell => 1402 | match token_buy_opt with 1403 | | Some token_buy => 1404 | (* Check that the placer has allowed sufficient funds to be accessed *) 1405 | CheckAllowance token_sell sell_amount; 1406 | (* Transfer the sell tokens to the exchange for holding. Construct a TransferFrom message to the token contract. *) 1407 | msg = mk_place_order_msg token_sell _sender _this_address sell_amount; 1408 | (* Send message when the transition completes. *) 1409 | send msg; 1410 | ... 1411 | | None => 1412 | (* Unlisted token *) 1413 | ThrowListingStatusException token_code_buy true false 1414 | end 1415 | | None => 1416 | (* Unlisted token *) 1417 | ThrowListingStatusException token_code_sell true false 1418 | end 1419 | end 1420 | 1421 | 1422 | Finally, we need to store the new order, so that users may match the 1423 | order in the future. For this we define a new type ``Order``, which 1424 | holds all the information needed when eventually the order is matched: 1425 | 1426 | .. code-block:: ocaml 1427 | 1428 | (* Order placer, sell token, sell amount, buy token, buy amount *) 1429 | type Order = 1430 | | Order of ByStr20 1431 | (ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end) 1432 | Uint128 1433 | (ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end) 1434 | Uint128 1435 | 1436 | A value of type ``Order`` is given by the type constructor ``Order``, 1437 | a token address and an amount of tokens to sell, and a token address 1438 | and an amount of tokens to buy. 1439 | 1440 | We now need a field containing a map from order numbers (of type 1441 | ``Uint128``) to ``Order``, which represents the currently active 1442 | orders. Additionally, we will need a way to generate a unique order 1443 | number, so we'll define a field which holds the next order number to 1444 | use: 1445 | 1446 | .. code-block:: ocaml 1447 | 1448 | field active_orders : Map Uint128 Order = Emp Uint128 Order 1449 | 1450 | field next_order_no : Uint128 = zero 1451 | 1452 | To add a new order we need to generate a new order number, store the 1453 | generated order number and the new order in the ``active_orders`` map, 1454 | and finally increment the ``next_order_no`` field (using the library 1455 | constant ``one = Uint128 1``) so that it is ready for the next order 1456 | to be placed. We will put that in a helper procedure ``AddOrder``, and 1457 | add a call to the procedure in the ``PlaceOrder`` transition: 1458 | 1459 | .. code-block:: ocaml 1460 | 1461 | procedure AddOrder( 1462 | order : Order 1463 | ) 1464 | (* Get the next order number *) 1465 | order_no <- next_order_no; 1466 | (* Add the order *) 1467 | active_orders[order_no] := order; 1468 | (* Update the next_order_no field *) 1469 | new_order_no = builtin add order_no one; 1470 | next_order_no := new_order_no 1471 | end 1472 | 1473 | transition PlaceOrder( 1474 | token_code_sell : String, 1475 | sell_amount : Uint128, 1476 | token_code_buy: String, 1477 | buy_amount : Uint128 1478 | ) 1479 | (* Check that the tokens are listed *) 1480 | token_sell_opt <- listed_tokens[token_code_sell]; 1481 | token_buy_opt <- listed_tokens[token_code_buy]; 1482 | match token_sell_opt with 1483 | | Some token_sell => 1484 | match token_buy_opt with 1485 | | Some token_buy => 1486 | (* Check that the placer has allowed sufficient funds to be accessed *) 1487 | CheckAllowance token_sell sell_amount; 1488 | (* Transfer the sell tokens to the exchange for holding. Construct a TransferFrom message to the token contract. *) 1489 | msg = mk_place_order_msg token_sell _sender _this_address sell_amount; 1490 | (* Send message when the transition completes. *) 1491 | send msg; 1492 | (* Create order and add to list of active orders *) 1493 | order = Order _sender token_sell sell_amount token_buy buy_amount; 1494 | AddOrder order 1495 | | None => 1496 | (* Unlisted token *) 1497 | ThrowListingStatusException token_code_buy true false 1498 | end 1499 | | None => 1500 | (* Unlisted token *) 1501 | ThrowListingStatusException token_code_sell true false 1502 | end 1503 | end 1504 | 1505 | ``PlaceOrder`` is now complete, but there is still one thing 1506 | missing. The `ZRC2` token standard specifies that when a 1507 | ``TransferFrom`` transition is executed, the token sends messages to 1508 | the recipient and the ``_sender`` (known as the `initiator`) notifying 1509 | them of the successful transfer. These notifications are known as 1510 | `callbacks`. Since our exchange executes a ``TransferFrom`` transition 1511 | on the sell token, and since the exchange is the recipient of those 1512 | tokens, we will need to specify transitions that can handle both 1513 | callbacks - if we don't, then the callbacks will not be recognised, 1514 | causing the entire ``PlaceOrder`` transaction to fail. 1515 | 1516 | Token contracts notify the recipients of token transfers because such 1517 | notifications add an extra safeguard against the risk of transferring 1518 | tokens to a contract that is unable to deal with token ownership. For 1519 | instance, if someone were to transfer tokens to the ``HelloWorld`` 1520 | contract in the first example in this section, then the tokens would 1521 | be locked forever because the ``HelloWorld`` contract is incapable of 1522 | doing anything with the tokens. 1523 | 1524 | Our exchange is only capable of dealing with tokens for which there is 1525 | an active order, but in principle there is nothing stopping a user 1526 | from transferring funds to the exchange without placing an order, so 1527 | we need to ensure that the exchange is only involved in token 1528 | transfers that it itself has initiated. We therefore define a 1529 | procedure ``CheckInitiator``, which throws an exception if the 1530 | exchange is involved in a token transfer that it itself did not 1531 | initiate, and invoke that procedure from all callback transitions: 1532 | 1533 | .. code-block:: ocaml 1534 | 1535 | procedure CheckInitiator( 1536 | initiator : ByStr20) 1537 | initiator_is_this = builtin eq initiator _this_address; 1538 | match initiator_is_this with 1539 | | True => (* Do nothing *) 1540 | | False => 1541 | e = { _exception : "UnexpecedTransfer"; 1542 | token_address : _sender; 1543 | initiator : initiator }; 1544 | throw e 1545 | end 1546 | end 1547 | 1548 | transition RecipientAcceptTransferFrom ( 1549 | initiator : ByStr20, 1550 | sender : ByStr20, 1551 | recipient : ByStr20, 1552 | amount : Uint128) 1553 | CheckInitiator initiator 1554 | end 1555 | 1556 | transition TransferFromSuccessCallBack ( 1557 | initiator : ByStr20, 1558 | sender : ByStr20, 1559 | recipient : ByStr20, 1560 | amount : Uint128) 1561 | CheckInitiator initiator 1562 | end 1563 | 1564 | 1565 | Matching an Order 1566 | ******************** 1567 | 1568 | For the ``MatchOrder`` transition we can leverage many of the helper 1569 | functions and procedures defined in the previous section. 1570 | 1571 | The user specifies an order he wishes to match. We then look up the 1572 | order number in the ``active_orders`` map, and throw an exception if 1573 | the order is not found: 1574 | 1575 | .. code-block:: ocaml 1576 | 1577 | transition MatchOrder( 1578 | order_id : Uint128) 1579 | order <- active_orders[order_id]; 1580 | match order with 1581 | | Some (Order order_placer sell_token sell_amount buy_token buy_amount) => 1582 | ... 1583 | | None => 1584 | e = { _exception : "UnknownOrder"; 1585 | order_id : order_id }; 1586 | throw e 1587 | end 1588 | end 1589 | 1590 | In order to match the order, the matcher has to provide sufficient 1591 | allowance of the buy token. This is checked by the ``CheckAllowance`` 1592 | procedure we defined earlier, so we simply reuse that procedure here: 1593 | 1594 | .. code-block:: ocaml 1595 | 1596 | transition MatchOrder( 1597 | order_id : Uint128) 1598 | order <- active_orders[order_id]; 1599 | match order with 1600 | | Some (Order order_placer sell_token sell_amount buy_token buy_amount) => 1601 | (* Check that the placer has allowed sufficient funds to be accessed *) 1602 | CheckAllowance buy_token buy_amount; 1603 | ... 1604 | | None => 1605 | e = { _exception : "UnknownOrder"; 1606 | order_id : order_id }; 1607 | throw e 1608 | end 1609 | end 1610 | 1611 | We now need to generate two transfer messages: One message is a 1612 | ``TransferFrom`` message on the buy token, transferring the matcher's 1613 | allowance to the user who placed the order, and the other message is a 1614 | ``Transfer`` message on the sell token, transferring the tokens held 1615 | by the exchange to the order matcher. Once again, we define helper 1616 | functions to generate the messages: 1617 | 1618 | .. code-block:: ocaml 1619 | 1620 | library SimpleExchangeLib 1621 | 1622 | ... 1623 | 1624 | let two_msgs : Message -> Message -> List Message = 1625 | fun (msg1 : Message) => 1626 | fun (msg2 : Message) => 1627 | let first = one_msg msg1 in 1628 | Cons { Message } msg2 first 1629 | 1630 | let mk_make_order_msgs : ByStr20 -> Uint128 -> ByStr20 -> Uint128 -> 1631 | ByStr20 -> ByStr20 -> ByStr20 -> List Message = 1632 | fun (token_sell_address : ByStr20) => 1633 | fun (sell_amount : Uint128) => 1634 | fun (token_buy_address : ByStr20) => 1635 | fun (buy_amount : Uint128) => 1636 | fun (this_address : ByStr20) => 1637 | fun (order_placer : ByStr20) => 1638 | fun (order_maker : ByStr20) => 1639 | (* Construct a Transfer messsage to transfer from exchange to maker *) 1640 | let sell_msg = mk_transfer_msg false token_sell_address this_address order_maker sell_amount in 1641 | (* Construct a TransferFrom messsage to transfer from maker to placer *) 1642 | let buy_msg = mk_transfer_msg true token_buy_address order_maker order_placer buy_amount in 1643 | (* Create a singleton list *) 1644 | two_msgs sell_msg buy_msg 1645 | 1646 | ... 1647 | 1648 | contract SimpleExchange (...) 1649 | 1650 | ... 1651 | 1652 | transition MatchOrder( 1653 | order_id : Uint128) 1654 | order <- active_orders[order_id]; 1655 | match order with 1656 | | Some (Order order_placer sell_token sell_amount buy_token buy_amount) => 1657 | (* Check that the placer has allowed sufficient funds to be accessed *) 1658 | CheckAllowance buy_token buy_amount; 1659 | (* Create the two transfer messages and send them *) 1660 | msgs = mk_make_order_msgs sell_token sell_amount buy_token buy_amount _this_address order_placer _sender; 1661 | send msgs; 1662 | ... 1663 | | None => 1664 | e = { _exception : "UnknownOrder"; 1665 | order_id : order_id }; 1666 | throw e 1667 | end 1668 | end 1669 | 1670 | Since the order has now been matched, it should no longer be listed as 1671 | an active order, so we delete the entry in ``active_orders``: 1672 | 1673 | .. code-block:: ocaml 1674 | 1675 | transition MatchOrder( 1676 | order_id : Uint128) 1677 | order <- active_orders[order_id]; 1678 | match order with 1679 | | Some (Order order_placer sell_token sell_amount buy_token buy_amount) => 1680 | (* Check that the placer has allowed sufficient funds to be accessed *) 1681 | CheckAllowance buy_token buy_amount; 1682 | (* Create the two transfer messages and send them *) 1683 | msgs = mk_make_order_msgs sell_token sell_amount buy_token buy_amount _this_address order_placer _sender; 1684 | send msgs; 1685 | (* Order has now been matched, so remove it *) 1686 | delete active_orders[order_id] 1687 | | None => 1688 | e = { _exception : "UnknownOrder"; 1689 | order_id : order_id }; 1690 | throw e 1691 | end 1692 | end 1693 | 1694 | This concludes the ``MatchOrder`` transition, but we need to define 1695 | one additional callback transition. When placing an order we executed 1696 | a ``TransferFrom`` transition, but now we also execute a ``Transfer`` 1697 | transition, which gives rise to a different callback: 1698 | 1699 | .. code-block:: ocaml 1700 | 1701 | transition TransferSuccessCallBack ( 1702 | initiator : ByStr20, 1703 | sender : ByStr20, 1704 | recipient : ByStr20, 1705 | amount : Uint128) 1706 | (* The exchange only accepts transfers that it itself has initiated. *) 1707 | CheckInitiator initiator 1708 | end 1709 | 1710 | Note that we do not need to specify a transition handling the receipt 1711 | of tokens from a ``Transfer`` transition, because the exchange never 1712 | executes a ``Transfer`` with itself as the recipient. By not defining 1713 | the callback transition at all, we also take care of the situation 1714 | where a user performs a ``Transfer`` with the exchange as the 1715 | recipient, because the recipient callback won't have a matching 1716 | transition on the exchange, causing the entire transfer transaction to 1717 | fail. 1718 | 1719 | Adding callbacks 1720 | ***************** 1721 | 1722 | Similar to how tokens execute callbacks whenever a transfer is 1723 | performed, we will want to issue callbacks to the users and contracts 1724 | that trade on our exchange. Callbacks should be issued in the following 1725 | cases: 1726 | 1727 | - When an order is placed: The address placing the order should receive a 1728 | callback. 1729 | - When an order is matched: The address that placed the order and the 1730 | address matching the order should each receive a callback. 1731 | 1732 | We choose to only issue callbacks whenever the addresses involved are 1733 | contracts. This is not strictly necessary, since a callback sent to a 1734 | user address does not cause a failure, but we do this to illustrate 1735 | the use of address type casts. 1736 | 1737 | We add the following helper functions to the contract library. These 1738 | simply construct the callback messages to the relevant addresses in 1739 | the cases mentioned above: 1740 | 1741 | .. code-block:: ocaml 1742 | 1743 | let mk_place_order_callback_msg : ByStr20 -> ByStr20 -> Uint128 -> ByStr20 -> Uint128 -> List Message = 1744 | fun (order_placer : ByStr20) => 1745 | fun (token_sell : ByStr20) => 1746 | fun (sell_amount : Uint128) => 1747 | fun (token_buy : ByStr20) => 1748 | fun (buy_amount : Uint128) => 1749 | let msg = { _recipient : order_placer; 1750 | _tag : "PlaceOrderSuccesful"; 1751 | _amount : Uint128 0; 1752 | selling_token : token_sell; 1753 | selling_amount : sell_amount; 1754 | buying_token : token_buy; 1755 | buying_amount : buy_amount } 1756 | in 1757 | one_msg msg 1758 | 1759 | let mk_order_matched_callback_msg = 1760 | fun (order_matcher : ByStr20) => 1761 | fun (token_sell : ByStr20) => 1762 | fun (sell_amount : Uint128) => 1763 | fun (token_buy : ByStr20) => 1764 | fun (buy_amount : Uint128) => 1765 | let msg = { _recipient : order_matcher; 1766 | _tag : "MatchOrderSuccesful"; 1767 | _amount : Uint128 0; 1768 | selling_token : token_sell; 1769 | selling_amount : sell_amount; 1770 | buying_token : token_buy; 1771 | buying_amount : buy_amount } 1772 | in 1773 | one_msg msg 1774 | 1775 | let mk_placed_order_matched_callback_msg = 1776 | fun (order_placer : ByStr20) => 1777 | fun (token_sell : ByStr20) => 1778 | fun (sell_amount : Uint128) => 1779 | fun (token_buy : ByStr20) => 1780 | fun (buy_amount : Uint128) => 1781 | let msg = { _recipient : order_placer; 1782 | _tag : "PlacedOrderMatched"; 1783 | _amount : Uint128 0; 1784 | selling_token : token_sell; 1785 | selling_amount : sell_amount; 1786 | buying_token : token_buy; 1787 | buying_amount : buy_amount } 1788 | in 1789 | one_msg msg 1790 | 1791 | 1792 | When an order is placed successfully we want to check if the placer is 1793 | a contract. The way to check this is by using an `address type cast`, 1794 | which is done as follows: 1795 | 1796 | .. code-block:: ocaml 1797 | 1798 | sender_as_contract_opt <-& _sender as ByStr20 with contract end; 1799 | 1800 | If ``_sender`` satisfies the address type ``ByStr20 with contract 1801 | end``, then ``sender_as_contract_opt`` will be bound to the value 1802 | ``Some v``, where ``v`` is a value that is equal to ``_sender``, but 1803 | which has the type ``ByStr20 with contract end``. If ``_sender`` does 1804 | not satisfy the type, then ``sender_as_contract_opt`` will be bound to 1805 | ``None``. 1806 | 1807 | Performing this type cast allows us to issue a callback in 1808 | ``PlaceOrder`` when ``_sender`` is a contract: 1809 | 1810 | .. code-block:: ocaml 1811 | 1812 | match sender_as_contract_opt with 1813 | | Some sender_as_contract => 1814 | callback_msg = mk_place_order_callback_msg _sender token_sell sell_amount token_buy buy_amount; 1815 | send callback_msg 1816 | | None => (* Do nothing *) 1817 | end 1818 | 1819 | We add a similar check to ``MatchOrder``, except that here we might 1820 | need to issue callbacks both to the ``_sender`` and the order placer: 1821 | 1822 | .. code-block:: ocaml 1823 | 1824 | match sender_as_contract_opt with 1825 | | Some sender_as_contract => 1826 | callback_msg = mk_order_matched_callback_msg _sender sell_token sell_amount buy_token buy_amount; 1827 | send callback_msg 1828 | | None => (* Do nothing *) 1829 | end; 1830 | placer_as_contract_opt <-& order_placer as ByStr20 with contract end; 1831 | match placer_as_contract_opt with 1832 | | Some placer_as_contract => 1833 | callback_msg = mk_placed_order_matched_callback_msg order_placer sell_token sell_amount buy_token buy_amount; 1834 | send callback_msg 1835 | | None => (* Do nothing *) 1836 | end 1837 | 1838 | 1839 | Putting it All Together 1840 | ************************* 1841 | 1842 | We now have everything in place to specify the entire contract: 1843 | 1844 | .. code-block:: ocaml 1845 | 1846 | scilla_version 0 1847 | 1848 | import IntUtils 1849 | 1850 | library SimpleExchangeLib 1851 | 1852 | (* Order placer, sell token, sell amount, buy token, buy amount *) 1853 | type Order = 1854 | | Order of ByStr20 1855 | (ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end) 1856 | Uint128 1857 | (ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end) 1858 | Uint128 1859 | 1860 | (* Helper values and functions *) 1861 | let true = True 1862 | let false = False 1863 | 1864 | let zero = Uint128 0 1865 | let one = Uint128 1 1866 | 1867 | let one_msg : Message -> List Message = 1868 | fun (msg : Message) => 1869 | let mty = Nil { Message } in 1870 | Cons { Message } msg mty 1871 | 1872 | let two_msgs : Message -> Message -> List Message = 1873 | fun (msg1 : Message) => 1874 | fun (msg2 : Message) => 1875 | let first = one_msg msg1 in 1876 | Cons { Message } msg2 first 1877 | 1878 | let mk_transfer_msg : Bool -> ByStr20 -> ByStr20 -> ByStr20 -> Uint128 -> Message = 1879 | fun (transfer_from : Bool) => 1880 | fun (token_address : ByStr20) => 1881 | fun (from : ByStr20) => 1882 | fun (to : ByStr20) => 1883 | fun (amount : Uint128) => 1884 | let tag = match transfer_from with 1885 | | True => "TransferFrom" 1886 | | False => "Transfer" 1887 | end 1888 | in 1889 | { _recipient : token_address; 1890 | _tag : tag; 1891 | _amount : Uint128 0; (* No Zil are transferred, only custom tokens *) 1892 | from : from; 1893 | to : to; 1894 | amount : amount } 1895 | 1896 | let mk_place_order_msg : ByStr20 -> ByStr20 -> ByStr20 -> Uint128 -> List Message = 1897 | fun (token_address : ByStr20) => 1898 | fun (from : ByStr20) => 1899 | fun (to : ByStr20) => 1900 | fun (amount : Uint128) => 1901 | (* Construct a TransferFrom messsage to transfer from seller's allowance to exhange *) 1902 | let msg = mk_transfer_msg true token_address from to amount in 1903 | (* Create a singleton list *) 1904 | one_msg msg 1905 | 1906 | let mk_make_order_msgs : ByStr20 -> Uint128 -> ByStr20 -> Uint128 -> 1907 | ByStr20 -> ByStr20 -> ByStr20 -> List Message = 1908 | fun (token_sell_address : ByStr20) => 1909 | fun (sell_amount : Uint128) => 1910 | fun (token_buy_address : ByStr20) => 1911 | fun (buy_amount : Uint128) => 1912 | fun (this_address : ByStr20) => 1913 | fun (order_placer : ByStr20) => 1914 | fun (order_maker : ByStr20) => 1915 | (* Construct a Transfer messsage to transfer from exchange to maker *) 1916 | let sell_msg = mk_transfer_msg false token_sell_address this_address order_maker sell_amount in 1917 | (* Construct a TransferFrom messsage to transfer from maker to placer *) 1918 | let buy_msg = mk_transfer_msg true token_buy_address order_maker order_placer buy_amount in 1919 | (* Create a singleton list *) 1920 | two_msgs sell_msg buy_msg 1921 | 1922 | (* Callback messages *) 1923 | let mk_place_order_callback_msg : ByStr20 -> ByStr20 -> Uint128 -> ByStr20 -> Uint128 -> List Message = 1924 | fun (order_placer : ByStr20) => 1925 | fun (token_sell : ByStr20) => 1926 | fun (sell_amount : Uint128) => 1927 | fun (token_buy : ByStr20) => 1928 | fun (buy_amount : Uint128) => 1929 | let msg = { _recipient : order_placer; 1930 | _tag : "PlaceOrderSuccesful"; 1931 | _amount : Uint128 0; 1932 | selling_token : token_sell; 1933 | selling_amount : sell_amount; 1934 | buying_token : token_buy; 1935 | buying_amount : buy_amount } 1936 | in 1937 | one_msg msg 1938 | 1939 | let mk_order_matched_callback_msg = 1940 | fun (order_matcher : ByStr20) => 1941 | fun (token_sell : ByStr20) => 1942 | fun (sell_amount : Uint128) => 1943 | fun (token_buy : ByStr20) => 1944 | fun (buy_amount : Uint128) => 1945 | let msg = { _recipient : order_matcher; 1946 | _tag : "MatchOrderSuccesful"; 1947 | _amount : Uint128 0; 1948 | selling_token : token_sell; 1949 | selling_amount : sell_amount; 1950 | buying_token : token_buy; 1951 | buying_amount : buy_amount } 1952 | in 1953 | one_msg msg 1954 | 1955 | let mk_placed_order_matched_callback_msg = 1956 | fun (order_placer : ByStr20) => 1957 | fun (token_sell : ByStr20) => 1958 | fun (sell_amount : Uint128) => 1959 | fun (token_buy : ByStr20) => 1960 | fun (buy_amount : Uint128) => 1961 | let msg = { _recipient : order_placer; 1962 | _tag : "PlacedOrderMatched"; 1963 | _amount : Uint128 0; 1964 | selling_token : token_sell; 1965 | selling_amount : sell_amount; 1966 | buying_token : token_buy; 1967 | buying_amount : buy_amount } 1968 | in 1969 | one_msg msg 1970 | 1971 | contract SimpleExchange 1972 | ( 1973 | initial_admin : ByStr20 with end 1974 | ) 1975 | 1976 | (* Active admin. *) 1977 | field admin : ByStr20 with end = initial_admin 1978 | 1979 | (* Tokens listed on the exchange. *) 1980 | field listed_tokens : 1981 | Map String (ByStr20 with contract 1982 | field allowances : Map ByStr20 (Map ByStr20 Uint128) 1983 | end) 1984 | = Emp String (ByStr20 with contract 1985 | field allowances : Map ByStr20 (Map ByStr20 Uint128) 1986 | end) 1987 | 1988 | (* Active orders, identified by the order number *) 1989 | field active_orders : Map Uint128 Order = Emp Uint128 Order 1990 | 1991 | (* The order number to use when the next order is placed *) 1992 | field next_order_no : Uint128 = zero 1993 | 1994 | procedure ThrowListingStatusException( 1995 | token_code : String, 1996 | expected_status : Bool, 1997 | actual_status : Bool) 1998 | e = { _exception : "UnexpectedListingStatus"; 1999 | token_code: token_code; 2000 | expected : expected_status; 2001 | actual : actual_status }; 2002 | throw e 2003 | end 2004 | 2005 | procedure ThrowInsufficientAllowanceException( 2006 | token : ByStr20, 2007 | expected : Uint128, 2008 | actual : Uint128) 2009 | e = { _exception : "InsufficientAllowance"; 2010 | token: token; 2011 | expected : expected; 2012 | actual : actual }; 2013 | throw e 2014 | end 2015 | 2016 | (* Check that _sender is the active admin. *) 2017 | (* If not, throw an error and abort the transaction *) 2018 | procedure CheckSenderIsAdmin() 2019 | current_admin <- admin; 2020 | is_admin = builtin eq _sender current_admin; 2021 | match is_admin with 2022 | | True => (* Nothing to do *) 2023 | | False => 2024 | (* Construct an exception object and throw it *) 2025 | e = { _exception : "SenderIsNotAdmin" }; 2026 | throw e 2027 | end 2028 | end 2029 | 2030 | (* Change the active admin *) 2031 | transition SetAdmin( 2032 | new_admin : ByStr20 with end 2033 | ) 2034 | (* Only the former admin may appoint a new admin *) 2035 | CheckSenderIsAdmin; 2036 | admin := new_admin 2037 | end 2038 | 2039 | (* Check that a given token code is not already listed. If it is, throw an error. *) 2040 | procedure CheckIsTokenUnlisted( 2041 | token_code : String 2042 | ) 2043 | (* Is the token code listed? *) 2044 | token_code_is_listed <- exists listed_tokens[token_code]; 2045 | match token_code_is_listed with 2046 | | True => 2047 | (* Incorrect listing status *) 2048 | ThrowListingStatusException token_code false token_code_is_listed 2049 | | False => (* Nothing to do *) 2050 | end 2051 | end 2052 | 2053 | (* List a new token on the exchange. Only the admin may list new tokens. *) 2054 | (* If a token code is already in use, raise an error *) 2055 | transition ListToken( 2056 | token_code : String, 2057 | new_token : ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end 2058 | ) 2059 | (* Only the admin may list new tokens. *) 2060 | CheckSenderIsAdmin; 2061 | (* Only new token codes are allowed. *) 2062 | CheckIsTokenUnlisted token_code; 2063 | (* Everything is ok. The token can be listed *) 2064 | listed_tokens[token_code] := new_token 2065 | end 2066 | 2067 | (* Check that the sender has allowed access to sufficient funds *) 2068 | procedure CheckAllowance( 2069 | token : ByStr20 with contract field allowances : Map ByStr20 (Map ByStr20 Uint128) end, 2070 | expected : Uint128 2071 | ) 2072 | actual_opt <-& token.allowances[_sender][_this_address]; 2073 | (* Find actual allowance. Use 0 if None is given *) 2074 | actual = match actual_opt with 2075 | | Some x => x 2076 | | None => zero 2077 | end; 2078 | is_sufficient = uint128_le expected actual; 2079 | match is_sufficient with 2080 | | True => (* Nothing to do *) 2081 | | False => 2082 | ThrowInsufficientAllowanceException token expected actual 2083 | end 2084 | end 2085 | 2086 | procedure AddOrder( 2087 | order : Order 2088 | ) 2089 | (* Get the next order number *) 2090 | order_no <- next_order_no; 2091 | (* Add the order *) 2092 | active_orders[order_no] := order; 2093 | (* Update the next_order_no field *) 2094 | new_order_no = builtin add order_no one; 2095 | next_order_no := new_order_no 2096 | end 2097 | 2098 | (* Place an order on the exchange *) 2099 | transition PlaceOrder( 2100 | token_code_sell : String, 2101 | sell_amount : Uint128, 2102 | token_code_buy: String, 2103 | buy_amount : Uint128 2104 | ) 2105 | (* Check that the tokens are listed *) 2106 | token_sell_opt <- listed_tokens[token_code_sell]; 2107 | token_buy_opt <- listed_tokens[token_code_buy]; 2108 | match token_sell_opt with 2109 | | Some token_sell => 2110 | match token_buy_opt with 2111 | | Some token_buy => 2112 | (* Check that the placer has allowed sufficient funds to be accessed *) 2113 | CheckAllowance token_sell sell_amount; 2114 | (* Transfer the sell tokens to the exchange for holding. Construct a TransferFrom message to the token contract. *) 2115 | msg = mk_place_order_msg token_sell _sender _this_address sell_amount; 2116 | (* Send message when the transition completes. *) 2117 | send msg; 2118 | (* Create order and add to list of active orders *) 2119 | order = Order _sender token_sell sell_amount token_buy buy_amount; 2120 | AddOrder order; 2121 | (* Do a callback if the placer is a contract *) 2122 | sender_as_contract_opt <-& _sender as ByStr20 with contract end; 2123 | match sender_as_contract_opt with 2124 | | Some sender_as_contract => 2125 | callback_msg = mk_place_order_callback_msg _sender token_sell sell_amount token_buy buy_amount; 2126 | send callback_msg 2127 | | None => (* Do nothing *) 2128 | end 2129 | | None => 2130 | (* Unlisted token *) 2131 | ThrowListingStatusException token_code_buy true false 2132 | end 2133 | | None => 2134 | (* Unlisted token *) 2135 | ThrowListingStatusException token_code_sell true false 2136 | end 2137 | end 2138 | 2139 | transition MatchOrder( 2140 | order_id : Uint128) 2141 | order <- active_orders[order_id]; 2142 | match order with 2143 | | Some (Order order_placer sell_token sell_amount buy_token buy_amount) => 2144 | (* Check that the placer has allowed sufficient funds to be accessed *) 2145 | CheckAllowance buy_token buy_amount; 2146 | (* Create the two transfer messages and send them *) 2147 | msgs = mk_make_order_msgs sell_token sell_amount buy_token buy_amount _this_address order_placer _sender; 2148 | send msgs; 2149 | (* Order has now been matched, so remove it *) 2150 | delete active_orders[order_id]; 2151 | (* Do callbacks if the matcher or the placer were contracts *) 2152 | sender_as_contract_opt <-& _sender as ByStr20 with contract end; 2153 | match sender_as_contract_opt with 2154 | | Some sender_as_contract => 2155 | callback_msg = mk_order_matched_callback_msg _sender sell_token sell_amount buy_token buy_amount; 2156 | send callback_msg 2157 | | None => (* Do nothing *) 2158 | end; 2159 | placer_as_contract_opt <-& order_placer as ByStr20 with contract end; 2160 | match placer_as_contract_opt with 2161 | | Some placer_as_contract => 2162 | callback_msg = mk_placed_order_matched_callback_msg order_placer sell_token sell_amount buy_token buy_amount; 2163 | send callback_msg 2164 | | None => (* Do nothing *) 2165 | end 2166 | | None => 2167 | e = { _exception : "UnknownOrder"; 2168 | order_id : order_id }; 2169 | throw e 2170 | end 2171 | end 2172 | 2173 | procedure CheckInitiator( 2174 | initiator : ByStr20) 2175 | initiator_is_this = builtin eq initiator _this_address; 2176 | match initiator_is_this with 2177 | | True => (* Do nothing *) 2178 | | False => 2179 | e = { _exception : "UnexpecedTransfer"; 2180 | token_address : _sender; 2181 | initiator : initiator }; 2182 | throw e 2183 | end 2184 | end 2185 | 2186 | transition RecipientAcceptTransferFrom ( 2187 | initiator : ByStr20, 2188 | sender : ByStr20, 2189 | recipient : ByStr20, 2190 | amount : Uint128) 2191 | (* The exchange only accepts transfers that it itself has initiated. *) 2192 | CheckInitiator initiator 2193 | end 2194 | 2195 | transition TransferFromSuccessCallBack ( 2196 | initiator : ByStr20, 2197 | sender : ByStr20, 2198 | recipient : ByStr20, 2199 | amount : Uint128) 2200 | (* The exchange only accepts transfers that it itself has initiated. *) 2201 | CheckInitiator initiator 2202 | end 2203 | 2204 | transition TransferSuccessCallBack ( 2205 | initiator : ByStr20, 2206 | sender : ByStr20, 2207 | recipient : ByStr20, 2208 | amount : Uint128) 2209 | (* The exchange only accepts transfers that it itself has initiated. *) 2210 | CheckInitiator initiator 2211 | end 2212 | 2213 | 2214 | As mentioned in the introduction we have kept the exchange simplistic 2215 | in order to keep the focus on Scilla features. 2216 | 2217 | To further familiarise themselves with Scilla we encourage the reader 2218 | to add additional features such as unlisting of tokens, cancellation 2219 | of orders, orders with expiry time, prioritising orders so that the 2220 | order matcher gets the best deal possible, partial matching of orders, 2221 | securing the exchange against abuse, fees for trading on the exchange, 2222 | etc.. 2223 | -------------------------------------------------------------------------------- /docs/source/scilla-checker.rst: -------------------------------------------------------------------------------- 1 | The Scilla checker 2 | ================== 3 | .. _scilla_checker: 4 | 5 | The Scilla checker (``scilla-checker``) works as a compiler frontend, 6 | parsing the contract and performing a number of static checks 7 | including typechecking. 8 | 9 | 10 | Phases of the Scilla checker 11 | ############################ 12 | .. _scilla_checker_phases: 13 | 14 | The Scilla checker operates in distinct phases, each of which can perform 15 | checks (and potentially reject contracts that do not pass the checks) and add 16 | annotations to each piece of syntax: 17 | 18 | + `Lexing and parsing` reads the contract code and builds an abstract 19 | syntax tree (AST). Each node in the tree is annotated with a 20 | location from the source file in the form of line and column 21 | numbers. 22 | 23 | + `ADT checking` checks various constraints on user-defined ADTs. 24 | 25 | + `Typechecking` checks that values in the contract are used in a way 26 | that is consistent with the type system. The typechecker also 27 | annotates each expression with its type. 28 | 29 | + `Pattern-checking` checks that each pattern-match in the contract is 30 | exhaustive (so that execution will not fail due to no match being 31 | found), and that each pattern can be reached (so that the programmer 32 | does not inadvertently introduce a pattern branch that can never be 33 | reached). 34 | 35 | + `Event-info` checks that messages and events in the contract contain all 36 | necessary fields. For events, if a contract emits two events with the same 37 | name (``_eventname``), then their structure (the fields and their types) must 38 | also be the same. 39 | 40 | + `Cashflow analysis` analyzes the usage of variables, fields and 41 | ADTs, and attempts to determine which fields are used to represent 42 | (native) blockchain money. No checks are performed, but expressions, 43 | variables, fields and ADTs are annotated with tags indicating their 44 | usage. 45 | 46 | + `Payment acceptance checking` checks contracts for payment acceptances. It 47 | raises a warning if a contract has no transitions which accept payment. Also, 48 | the check raises a warning if a transition has any code path with potentially 49 | multiple ``accept`` statements in it. This check does not raise an error since 50 | it is not possible via static analysis to know for sure in all cases whether 51 | multiple ``accept`` statements would be reached if present, since this can be 52 | dependent on conditions which are only known at run-time. The Scilla checker 53 | only performs this check if only ``-cf`` flag is specified on the command 54 | line, i.e. it is performed together with the cashflow analysis. For instance, 55 | fungible token contracts don't generally need ``accept`` statements, hence 56 | this check is not mandatory. 57 | 58 | + `Sanity-checking` performs a number of minor checks, e.g., that all 59 | parameters to a transition or a procedure have distinct names. 60 | 61 | Additionally, the Scilla checker provides an option that dumps the `call graph 62 | `_ in the ``.dot`` format which is 63 | useful for auditing contracts. 64 | 65 | 66 | Annotations 67 | *********** 68 | .. _scilla_checker_annotations: 69 | 70 | Each phase in the Scilla checker can add an annotation to each node in 71 | the abstract syntax tree. The type of an annotation is specified 72 | through instantiations of the module signature ``Rep``. ``Rep`` 73 | specifies the type ``rep``, which is the type of the annotation: 74 | 75 | .. code-block:: ocaml 76 | 77 | module type Rep = sig 78 | type rep 79 | ... 80 | end 81 | 82 | 83 | In addition to the type of the annotation, the instantiation of 84 | ``Rep`` can declare helper functions that allow subsequent phases to 85 | access the annotations of previous phases. Some of these functions are 86 | declared in the ``Rep`` signature, because they involve creating new 87 | abstract syntax nodes, which must be created with annotations from the 88 | parser onward: 89 | 90 | .. code-block:: ocaml 91 | 92 | module type Rep = sig 93 | ... 94 | 95 | val mk_id_address : string -> rep ident 96 | val mk_id_uint128 : string -> rep ident 97 | val mk_id_bnum : string -> rep ident 98 | val mk_id_string : string -> rep ident 99 | 100 | val rep_of_sexp : Sexp.t -> rep 101 | val sexp_of_rep : rep -> Sexp.t 102 | 103 | val parse_rep : string -> rep 104 | val get_rep_str: rep -> string 105 | end 106 | 107 | ``mk_id_`` creates an identifier with an appropriate type 108 | annotation if annotation is one of the phases that has been 109 | executed. If the typechecker has not yet been executed, the functions 110 | simply create an (untyped) identifier with a dummy location. 111 | 112 | ``rep_of_sexp`` and ``sexp_of_rep`` are used for pretty-printing. They 113 | are automatically generated if rep is defined with the ``[@@deriving 114 | sexp]`` directive. 115 | 116 | ``parse_rep`` and ``get_rep_str`` are used for caching of typechecked 117 | libraries, so that they do not need to be checked again if they 118 | haven't changed. These will likely be removed in future versions of 119 | the Scilla checker. 120 | 121 | As an example, consider the annotation module ``TypecheckerERep``: 122 | 123 | .. code-block:: ocaml 124 | 125 | module TypecheckerERep (R : Rep) = struct 126 | type rep = PlainTypes.t inferred_type * R.rep 127 | [@@deriving sexp] 128 | 129 | let get_loc r = match r with | (_, rr) -> R.get_loc rr 130 | 131 | let mk_id s t = 132 | match s with 133 | | Ident (n, r) -> Ident (n, (PlainTypes.mk_qualified_type t, r)) 134 | 135 | let mk_id_address s = mk_id (R.mk_id_address s) (bystrx_typ address_length) 136 | let mk_id_uint128 s = mk_id (R.mk_id_uint128 s) uint128_typ 137 | let mk_id_bnum s = mk_id (R.mk_id_bnum s) bnum_typ 138 | let mk_id_string s = mk_id (R.mk_id_string s) string_typ 139 | 140 | let mk_rep (r : R.rep) (t : PlainTypes.t inferred_type) = (t, r) 141 | 142 | let parse_rep s = (PlainTypes.mk_qualified_type uint128_typ, R.parse_rep s) 143 | let get_rep_str r = match r with | (_, rr) -> R.get_rep_str rr 144 | 145 | let get_type (r : rep) = fst r 146 | end 147 | 148 | The functor (parameterized structure) takes the annotation from the 149 | previous phase as the parameter ``R``. In the Scilla checker this 150 | previous phase is the parser, but any phase could be added in-between 151 | the two phases by specifying the phase in the top-level runner. 152 | 153 | The type ``rep`` specifies that the new annotation is a pair of a type 154 | and the previous annotation. 155 | 156 | The function ``get_loc`` merely serves as a proxy for the ``get_loc`` 157 | function of the previous phase. 158 | 159 | The function ``mk_id`` is a helper function for the ``mk_id_`` 160 | functions, which create an identifier with the appropriate type 161 | annotation. 162 | 163 | The ``mk_rep`` function is a helper function used by the typechecker. 164 | 165 | Prettyprinting does not output the types of AST nodes, so the 166 | functions ``parse_rep`` and ``get_rep_str`` ignore the type 167 | annotations. 168 | 169 | Finally, the function ``get_type`` provides access to type information 170 | for subsequent phases. This function is not mentioned in the ``Rep`` 171 | signature, since it is made available by the typechecker once type 172 | annotations have been added to the AST. 173 | 174 | 175 | Abstract syntax 176 | *************** 177 | .. _scilla_checker_syntax: 178 | 179 | The ``ScillaSyntax`` functor defines the AST node types. Each phase 180 | will instantiate the functor twice, once for the input syntax and once 181 | for the output syntax. These two syntax instantiations differ only in 182 | the type of annotations of each syntax node. If the phase produces no 183 | additional annotations, the two instantiations will be identical. 184 | 185 | The parameters ``SR`` and ``ER``, both of type ``Rep``, define the 186 | annotations for statements and expressions, respectively. 187 | 188 | .. code-block:: ocaml 189 | 190 | module ScillaSyntax (SR : Rep) (ER : Rep) = struct 191 | 192 | type expr_annot = expr * ER.rep 193 | and expr = ... 194 | 195 | type stmt_annot = stmt * SR.rep 196 | and stmt = ... 197 | end 198 | 199 | Initial annotation 200 | ****************** 201 | .. _scilla_checker_initial_annotation: 202 | 203 | The parser generates the initial annotation, which only contains 204 | information about where the syntax node is located in the source 205 | file. The function ``get_loc`` allows subsequent phases to access the 206 | location. 207 | 208 | The ``ParserRep`` structure is used for annotations both of statements 209 | and expressions. 210 | 211 | .. code-block:: ocaml 212 | 213 | module ParserRep = struct 214 | type rep = loc 215 | [@@deriving sexp] 216 | 217 | let get_loc l = l 218 | ... 219 | end 220 | 221 | Typical phase 222 | ************* 223 | .. _scilla_checker_typical_phase: 224 | 225 | Each phase that produces additional annotations will need to provide a 226 | new implementation of the ``Rep`` module type. The implementation 227 | should take the previous annotation type (as a structure implementing 228 | the ``Rep`` module type) as a parameter, so that the phase's 229 | annotations can be added to the annotations of the previous phases. 230 | 231 | The typechecker adds a type to each expression node in the AST, but 232 | doesn't add anything to statement node annotations. Consequently, the 233 | typechecker only defines an annotation type for expressions. 234 | 235 | In addition, the ``Rep`` implementation defines a function 236 | ``get_type``, so that subsequent phases can access the type in the 237 | annotation. 238 | 239 | .. code-block:: ocaml 240 | 241 | module TypecheckerERep (R : Rep) = struct 242 | type rep = PlainTypes.t inferred_type * R.rep 243 | [@@deriving sexp] 244 | 245 | let get_loc r = match r with | (_, rr) -> R.get_loc rr 246 | 247 | ... 248 | let get_type (r : rep) = fst r 249 | end 250 | 251 | The Scilla typechecker takes the statement and expression annotations 252 | of the previous phase, and then instantiates ``TypeCheckerERep`` 253 | (creating the new annotation type), ``ScillaSyntax`` (creating the 254 | abstract syntax type for the previous phase, which serves as input to 255 | the typechecker), and ``ScillaSyntax`` again (creating the abstract 256 | syntax type that the typechecker outputs). 257 | 258 | .. code-block:: ocaml 259 | 260 | module ScillaTypechecker 261 | (SR : Rep) 262 | (ER : Rep) = struct 263 | 264 | (* No annotation added to statements *) 265 | module STR = SR 266 | (* TypecheckerERep is the new annotation for expressions *) 267 | module ETR = TypecheckerERep (ER) 268 | 269 | (* Instantiate ScillaSyntax with source and target annotations *) 270 | module UntypedSyntax = ScillaSyntax (SR) (ER) 271 | module TypedSyntax = ScillaSyntax (STR) (ETR) 272 | 273 | (* Expose target syntax and annotations for subsequent phases *) 274 | include TypedSyntax 275 | include ETR 276 | 277 | (* Instantiate helper functors *) 278 | module TU = TypeUtilities (SR) (ER) 279 | module TBuiltins = ScillaBuiltIns (SR) (ER) 280 | module TypeEnv = TU.MakeTEnv(PlainTypes)(ER) 281 | module CU = ScillaContractUtil (SR) (ER) 282 | ... 283 | end 284 | 285 | Crucially, the typechecker module exposes the annotations and the 286 | syntax type that it generates, so that they can be made available to 287 | the next phase. 288 | 289 | The typechecker finally instantiates helper functors such as 290 | ``TypeUtilities`` and ``ScillaBuiltIns``. 291 | 292 | 293 | Cashflow Analysis 294 | ################# 295 | .. _scilla_checker_cashflow: 296 | 297 | The cashflow analysis phase analyzes the usage of a contract's 298 | variables, fields, and ADT constructor, and attempts to determine 299 | which fields and ADTs are used to represent (native) blockchain 300 | money. Each contract field is annotated with a tag indicating the 301 | field's usage. 302 | 303 | The resulting tags are an approximation based on the usage of the 304 | contract's fields, variables, and ADT constructors. The tags are not 305 | guaranteed to be accurate, but are intended as a tool to help the 306 | contract developer use her fields in the intended manner. 307 | 308 | 309 | Running the analysis 310 | ******************** 311 | 312 | The cashflow analysis is activated by running ``scilla-checker`` with 313 | the option ``-cf``. The analysis is not run by default, since it is 314 | only intended to be used during contract development. 315 | 316 | A contract is never rejected due to the result of the cashflow 317 | analysis. It is up to the contract developer to determine whether the 318 | cashflow tags are consistent with the intended use of each contract 319 | field. 320 | 321 | 322 | The Analysis in Detail 323 | ********************** 324 | 325 | The analysis works by continually analysing the transitions and 326 | procedures of the contract until no further information is gathered. 327 | 328 | The starting point for the analysis is the incoming message that 329 | invokes the contract's transition, the outgoing messages and events 330 | that may be sent by the contract, the contract's account balance, and 331 | any field being read from the blockchain such as the current 332 | blocknumber. 333 | 334 | Both incoming and outgoing messages contain a field ``_amount`` whose 335 | value is the amount of money being transferred between accounts by the 336 | message. Whenever the value of the ``_amount`` field of the incoming 337 | message is loaded into a local variable, that local variable is tagged 338 | as representing money. Similarly, a local variable used to initialise 339 | the ``_amount`` field of an outgoing message is also tagged as 340 | representing money. 341 | 342 | Conversely, the message fields ``_sender``, ``_origin``, ``_recipient``, and 343 | ``_tag``, the event field ``_eventname``, the exception field 344 | ``_exception``, and the blockchain field ``BLOCKNUMBER``, are known to 345 | not represent money, so any variable used to initialise those fields 346 | or to hold the value read from one of those fields is tagged as not 347 | representing money. 348 | 349 | Once some variables have been tagged, their usage implies how other 350 | variables can be tagged. For instance, if two variables tagged as 351 | money are added to each other, the result is also deemed to represent 352 | money. Conversely, if two variables tagged as non-money are added, the 353 | result is deemed to represent non-money. 354 | 355 | Tagging of contract fields happens when a local variable is used when 356 | loading or storing a contract field. In these cases, the field is 357 | deemed to have the same tag as the local variable. 358 | 359 | Tagging of custom ADTs is done when they are used for constructing 360 | values, and when they are used in pattern-matching. 361 | 362 | Once a transition or procedure has been analyzed, the local variables 363 | and their tags are saved and the analysis proceeds to the next 364 | transition or procedure while keeping the tags of the contract fields 365 | and ADTs. The analysis continues until all the transitions and 366 | procedures have been analysed without any existing tags having 367 | changed. 368 | 369 | 370 | Tags 371 | **** 372 | 373 | The analysis uses the following set of tags: 374 | 375 | - `No information`: No information has been gathered about the 376 | variable. This sometimes (but not always) indicates that the 377 | variable is not being used, indicating a potential bug. 378 | 379 | - `Money`: The variable represents money. 380 | 381 | - `Not money`: The variable represents something other than money. 382 | 383 | - `Map t` (where `t` is a tag): The variable represents a map or a function 384 | whose co-domain is tagged with `t`. Hence, when performing a lookup in the 385 | map, or when applying a function on the values stored in the map, the result 386 | is tagged with `t`. Keys of maps are assumed to always be `Not money`. Using 387 | a variable as a function parameter does not give rise to a tag. 388 | 389 | - `T t1 ... tn` (where `T` is an ADT, and `t1 ... tn` are tags): The 390 | variable represents a value of an ADT, such as `List` or 391 | `Option`. The tags `t1 ... tn` correspond to the tags of each type 392 | parameter of the ADT. (See the simple example_ further down.) 393 | 394 | - `Inconsistent`: The variable has been used to represent both money 395 | and not money. Inconsistent usage indicates a bug. 396 | 397 | Library and local functions are only partially supported, since no 398 | attempt is made to connect the tags of parameters to the tag of the 399 | result. Built-in functions are fully supported, however. 400 | 401 | .. _example: 402 | 403 | A simple example 404 | **************** 405 | Consider the following code snippet: 406 | 407 | .. code-block:: ocaml 408 | 409 | match p with 410 | | Nil => 411 | | Cons x xs => 412 | msg = { _amount : x ; ...} 413 | ... 414 | end 415 | 416 | ``x`` is used to initialise the ``_amount`` field of a message, so 417 | ``x`` gets tagged with `Money`. Since ``xs`` is the tail of a list of 418 | which ``x`` is the first element, ``xs`` must be a list of elements 419 | with the same tag as ``x``. ``xs`` therefore gets tagged with `List 420 | Money`, corresponding to the fact that the ``List 'A`` type has one 421 | type parameter. 422 | 423 | Similarly, ``p`` is matched against the patterns ``Nil`` and ``Cons x 424 | xs``. ``Nil`` is a list, but since the list is empty we don't know 425 | anything about the contents of the list, and so the ``Nil`` pattern 426 | corresponds to the tag `List (No information)`. ``Cons x xs`` is also 427 | a list, but this time we do know something about the contents, namely 428 | that the first element ``x`` is tagged with `Money`, and the tail of 429 | the list is tagged with `List Money`. Consequently, ``Cons x xs`` 430 | corresponds to `List Money`. 431 | 432 | Unifying the two tags `List (No information)` and `List Money` gives 433 | the tag `List Money`, so ``p`` gets tagged with `List Money`. 434 | 435 | 436 | ADT constructor tagging 437 | *********************** 438 | 439 | In addition to tagging fields and local variables, the cashflow 440 | analyser also tags constructors of custom ADTs. 441 | 442 | To see how this works, consider the following custom ADT: 443 | 444 | .. code-block:: ocaml 445 | 446 | type Transaction = 447 | | UserTransaction of ByStr20 Uint128 448 | | ContractTransaction of ByStr20 String Uint128 449 | 450 | A user transaction is a transaction where the recipient is a user 451 | account, so the ``UserTransaction`` constructor takes two arguments: 452 | An address of the recipient user account, and the amount to transfer. 453 | 454 | A contract transaction is a transaction where the recipient is another 455 | contract, so the ``ContractTransaction`` takes three arguments: An 456 | address of the recipient contract, the name of the transition to 457 | invoke on the recipient contract, and the amount to transfer. 458 | 459 | In terms of cashflow it is clear that the last argument of both 460 | constructors is used to represent an amount of money, whereas all other 461 | arguments are used to represent non-money. The cashflow analyser 462 | therefore attempts to tag the arguments of the two constructors with 463 | appropriate tags, using the principles described in the previous 464 | sections. 465 | 466 | 467 | A more elaborate example 468 | ************************ 469 | 470 | As an example, consider a crowdfunding contract written in Scilla. Such a 471 | contract may declare the following immutable parameters and mutable fields: 472 | 473 | .. code-block:: ocaml 474 | 475 | contract Crowdfunding 476 | 477 | (* Parameters *) 478 | (owner : ByStr20, 479 | max_block : BNum, 480 | goal : Uint128) 481 | 482 | (* Mutable fields *) 483 | field backers : Map ByStr20 Uint128 = ... 484 | field funded : Bool = ... 485 | 486 | The ``owner`` parameter represents the address of the person deploying 487 | the contract. The ``goal`` parameter is the amount of money the owner 488 | is trying to raise, and the ``max_block`` parameter represents the 489 | deadline by which the goal is to be met. 490 | 491 | The field ``backers`` is a map from the addresses of contributors to the amount 492 | of money contributed, and the field ``funded`` represents whether the goal has 493 | been reached. 494 | 495 | Since the field ``goal`` represents an amount of money, ``goal`` 496 | should be tagged as `Money` by the analysis. Similarly, the 497 | ``backers`` field is a map with a co-domain representing `Money`, so 498 | ``backers`` should be tagged with `Map Money`. 499 | 500 | Conversely, both ``owner``, ``max_block`` and ``funded`` represent 501 | something other than money, so they should all be tagged with `Not 502 | money`. 503 | 504 | The cashflow analysis will tag the parameters and fields according to 505 | how they are used in the contract's transitions and procedures, and if 506 | the resulting tags do not correspond to the expectation, then the 507 | contract likely contains a bug somewhere. 508 | 509 | Exploring the call graph 510 | ######################## 511 | .. _scilla_checker_call_graph: 512 | 513 | The call graph option allows the user to view the graphical 514 | representation of relationships between functions, procedures and 515 | transitions in a contract. 516 | 517 | Calling ``scilla-checker`` with the ``-dump-callgraph`` option 518 | generates a ``.dot`` file in the same directory as the contract 519 | file. Using the ``dot`` command line tool (part of the `Graphviz 520 | package `_) this file can be converted into a 521 | picture using the command ``dot -Tsvg filename.dot -o filename.svg``. 522 | 523 | It is also possible to dump the call graph to ``stdout`` using the option ``-dump-callgraph-stdout``. 524 | 525 | Example 526 | ******* 527 | 528 | Consider the following contract, which we assume to be located in the file ``callgraph.scilla``: 529 | 530 | .. code-block:: ocaml 531 | 532 | scilla_version 0 533 | 534 | library Callgraph 535 | 536 | let id = fun (a: Uint32) => a 537 | let id_alias = id 538 | 539 | let option_value = 540 | tfun 'A => 541 | fun (default: 'A) => 542 | fun (v: Option 'A) => 543 | match v with 544 | | Some v => v 545 | | None => default 546 | end 547 | let option_uint32 = @option_value Uint32 548 | 549 | contract Callgraph() 550 | 551 | procedure pr1(a: Uint32) 552 | accept 553 | end 554 | 555 | procedure pr2(a: Uint32) 556 | pr1 a 557 | end 558 | 559 | transition tr(a: Uint32) 560 | res = id_alias a; 561 | pr2 res 562 | end 563 | 564 | We now run the shell commands 565 | 566 | .. code-block:: bash 567 | 568 | $ scilla-checker -dump-callgraph -libdir path/to/stdlib -gaslimit 1000 callgraph.scilla 569 | $ dot -Tsvg callgraph.dot -o callgraph.svg 570 | 571 | This will generate the following graphic illustrating the call graph, 572 | located in the file ``callgraph.svg`` (note that comments have been 573 | added manually): 574 | 575 | .. image:: nstatic/imgs/callgraph.png 576 | 577 | Tools to work with the call graph 578 | ********************************* 579 | 580 | The suggested way to work with the call graph is the `Graphviz plugin 581 | `_ 582 | for `VSCode `_. It allows the user to 583 | interactively view the call graph in their editor. 584 | 585 | Another graphical viewer for the dot file is the crossplatform `dot.py 586 | `_ utility. 587 | -------------------------------------------------------------------------------- /docs/source/scilla-tips-and-tricks.rst: -------------------------------------------------------------------------------- 1 | .. _scilla_tips: 2 | 3 | Scilla Tips and Tricks 4 | ====================== 5 | 6 | .. _scilla_tips_performance: 7 | 8 | Performance 9 | ########### 10 | 11 | 12 | .. _field_map_size: 13 | 14 | Field map size 15 | ************** 16 | 17 | If your contract needs to know the size of a field map, i.e. a field 18 | variable that is a map then the obvious implementation that reads a 19 | field map into a variable and applies the ``size`` builtin (as in the 20 | following code snippet) can be very inefficient as it requires making 21 | a copy of the map in question. 22 | 23 | .. code-block:: ocaml 24 | 25 | field accounts : Map ByStr20 Uint128 = Emp ByStr20 Uint128 26 | 27 | transition Foo() 28 | ... 29 | accounts_copy <- accounts; 30 | accounts_size = builtin size accounts_copy; 31 | ... 32 | end 33 | 34 | In order to solve the issue one tracks the size information in a 35 | corresponding field variable as in the following code snippet. Notice 36 | that now instead of copying a map one just reads from 37 | ``accounts_size`` field variable. 38 | 39 | .. code-block:: ocaml 40 | 41 | let uint32_one = Uint32 1 42 | 43 | field accounts : Map ByStr20 Uint128 = Emp ByStr20 Uint128 44 | field accounts_size : Uint32 = 0 45 | 46 | transition Foo() 47 | ... 48 | num_of_accounts <- accounts_size; 49 | ... 50 | end 51 | 52 | Now, to make sure that the map and its size stay in sync, one needs to 53 | update the size of the map when using the builtin in-place statements 54 | like ``m[k] := v`` or ``delete m[k]`` or better yet define and use 55 | systematically procedures that do exactly that. 56 | 57 | Here is the definition of a procedure that updates a key/value pair in 58 | the ``accounts`` map and changes its size accordingly. 59 | 60 | .. code-block:: ocaml 61 | 62 | procedure insert_to_accounts (key : ByStr20, value : Uint128) 63 | already_exists <- exists accounts[key]; 64 | match already_exists with 65 | | True => 66 | (* do nothing as the size does not change *) 67 | | False => 68 | size <- accounts_size; 69 | new_size = builtin add size uint32_one; 70 | accounts_size := new_size 71 | end; 72 | accounts[key] := value 73 | end 74 | 75 | And this is the definition of a procedure that removes a key/value pair from 76 | the ``accounts`` map and changes its size accordingly. 77 | 78 | .. code-block:: ocaml 79 | 80 | procedure delete_from_accounts (key : ByStr20) 81 | already_exists <- exists accounts[key]; 82 | match already_exists with 83 | | False => 84 | (* do nothing as the map and its size do not change *) 85 | | True => 86 | size <- accounts_size; 87 | new_size = builtin sub size uint32_one; 88 | accounts_size := new_size 89 | end; 90 | delete accounts[key] 91 | end 92 | 93 | 94 | Money Idioms 95 | ############ 96 | .. _scilla_tips_money: 97 | 98 | Partially accepting funds 99 | ************************* 100 | 101 | Let's say you are working on a contract which lets people tips each other. 102 | Naturally, you'd like to avoid a situation when a person tips too much because 103 | of a typo. It would be nice to ask Scilla to accept incoming funds partially, 104 | but there is no ``accept `` builtin. You can either not accept at all or 105 | accept the funds fully. We can work around this restriction by fully accepting 106 | the incoming funds and then immediately refunding the tipper if the tip exceeds 107 | some cap. 108 | 109 | It turns out we can encapsulate this kind of behavior as a reusable procedure. 110 | 111 | .. code-block:: ocaml 112 | 113 | procedure accept_with_cap (cap : Uint128) 114 | sent_more_than_necessary = builtin lt cap _amount; 115 | match sent_more_than_necessary with 116 | | True => 117 | amount_to_refund = builtin sub _amount cap; 118 | accept; 119 | msg = { _tag : ""; _recipient: _sender; _amount: amount_to_refund }; 120 | msgs = one_msg msg; 121 | send msgs 122 | | False => 123 | accept 124 | end 125 | end 126 | 127 | Now, the ``accept_with_cap`` procedure can be used as follows. 128 | 129 | .. code-block:: ocaml 130 | 131 | 132 | 133 | contract Tips (tip_cap : Uint128) 134 | 135 | transition Tip (message_from_tipper : String) 136 | accept_with_cap tip_cap; 137 | e = { _eventname: "ThanksForTheTip" }; 138 | event e 139 | end 140 | 141 | .. _scilla_tips_safety: 142 | 143 | Safety 144 | ###### 145 | 146 | 147 | .. _transfer_contract_ownership: 148 | 149 | Transfer Contract Ownership 150 | *************************** 151 | 152 | If your contract has an owner (usually it means someone with admin powers like 153 | adding/removing user accounts or pausing/unpausing the contract) which can be 154 | changed at runtime then at first sight this can be formalized in the code as a 155 | field ``owner`` and a transition like ``ChangeOwner``: 156 | 157 | .. code-block:: ocaml 158 | 159 | contract Foo (initial_owner : ByStr20) 160 | 161 | field owner : ByStr20 = initial_owner 162 | 163 | transition ChangeOwner(new_owner : ByStr20) 164 | (* continue executing the transition if _sender is the owner, 165 | throw exception and abort otherwise *) 166 | isOwner; 167 | owner := new_owner 168 | end 169 | 170 | However, this might lead to a situation when the current owner gets locked out 171 | of the control over the contract. For instance, the current owner can 172 | potentially transfer contract ownership to a non-existing address due to a typo 173 | in the address parameter when calling the ``ChangeOwner`` transition. Here we 174 | provide a design pattern to circumvent this issue. 175 | 176 | One way to ensure the new owner is active is to do ownership transfer in two 177 | phases: 178 | 179 | - the current owner offers to transfer ownership to a new owner, note that at 180 | this point the current owner is still the contract owner; 181 | 182 | - the future new owner accepts the pending ownership transfer and becomes the 183 | current owner; 184 | 185 | - at any moment before the future new owner accepts the transfer the current 186 | owner can abort the ownership transfer. 187 | 188 | Here is a possible implementation of the pattern outlined above (the code for 189 | the ``isOwner`` procedure is not shown): 190 | 191 | .. code-block:: ocaml 192 | 193 | contract OwnershipTransfer (initial_owner : ByStr20) 194 | 195 | field owner : ByStr20 = initial_owner 196 | field pending_owner : Option ByStr20 = None {ByStr20} 197 | 198 | transition RequestOwnershipTransfer (new_owner : ByStr20) 199 | isOwner; 200 | po = Some {ByStr20} new_owner; 201 | pending_owner := po 202 | end 203 | 204 | transition ConfirmOwnershipTransfer () 205 | optional_po <- pending_owner; 206 | match optional_po with 207 | | Some pend_owner => 208 | caller_is_new_owner = builtin eq _sender pend_owner; 209 | match caller_is_new_owner with 210 | | True => 211 | (* transfer ownership *) 212 | owner := pend_owner; 213 | none = None {ByStr20}; 214 | pending_owner := none 215 | | False => (* the caller is not the new owner, do nothing *) 216 | end 217 | | None => (* ownership transfer is not in-progress, do nothing *) 218 | end 219 | end 220 | 221 | Ownership transfer for contracts with the above transition is supposed to happen 222 | as follows: 223 | 224 | - the current owner calls ``RequestOwnershipTransfer`` transition with the 225 | new owner address as its only explicit parameter; 226 | - the new owner calls ``ConfirmOwnershipTransfer``. 227 | 228 | Until the ownership transition is finalized, the current owner can abort it by 229 | calling ``RequestOwnershipTransfer`` with their own address. A (redundant) 230 | dedicated transition can be added to make it even more evident to the contract 231 | owners and users. 232 | -------------------------------------------------------------------------------- /docs/source/scilla-trial.rst: -------------------------------------------------------------------------------- 1 | .. _trial-label: 2 | 3 | Trying out Scilla 4 | ================= 5 | 6 | Scilla is under active development. You can try out Scilla in the online IDE. 7 | 8 | 9 | Savant IDE 10 | ************************ 11 | 12 | `Neo Savant IDE `_ is a web-based development 13 | environment that allows you to interact with the simulated testnet environment, the 14 | live developer testnet, and the live mainnet. It is optimized for use in Chrome Web Browser. 15 | Neo Savant IDE allows you to import accounts from external wallets like Ledger or keystore files. 16 | 17 | The IDE automatically request the faucets to disburse testnet $ZIL to you when the wallet is successfully imported. 18 | On the simulated testnet environment, you will receive 10,000 $ZIL. 19 | While on the developer testnet, you will receive 300 $ZIL. 20 | There are no faucets for the live mainnet. 21 | 22 | The Neo Savant IDE can act as a staging environment, before doing automated script testing with tools 23 | like `Isolated Server `_ and 24 | `Zilliqa-JS `_. 25 | To try out the Neo Savant IDE, users need to visit `Neo Savant IDE `_. 26 | 27 | 28 | Example Contracts 29 | ****************** 30 | 31 | Savant IDE comes with the following sample smart contracts written in Scilla: 32 | 33 | + **HelloWorld** : It is a simple contract that allows a specified account 34 | denoted ``owner`` to set a welcome message. Setting the welcome message is 35 | done via ``setHello (msg: String)``. The contract also provides an interface 36 | ``getHello()`` to allow any account to be returned with the welcome message 37 | when called. 38 | 39 | + **BookStore** : A demonstration of a CRUD app. Only ``owner`` of the contract can 40 | add ``members``. All ``members`` will have read/write access capability to 41 | create OR update books in the inventory with `book title`, `author`, and `bookID`. 42 | 43 | + **CrowdFunding** : Crowdfunding implements a Kickstarter-style campaign where users 44 | can donate funds to the contract using ``Donate()``. If the campaign is 45 | successful, i.e., enough money is raised within a given time period, the 46 | raised money can be sent to a predefined account ``owner`` via 47 | ``GetFunds()``. Else, if the campaign fails, then contributors can take back 48 | their donations via the transition ``ClaimBack()``. 49 | 50 | + **Auction** : A simple open auction contract where bidders can make their 51 | bid using ``Bid()``, and the highest and winning bid amount goes to a 52 | predefined account. Bidders who don't win can take back their bid using the 53 | transition ``Withdraw()``. The organizer of the auction can claim the highest 54 | bid by invoking the transition ``AuctionEnd()``. 55 | 56 | + **FungibleToken** : ZRC-2 Fungible token standard contract for creating 57 | fungible digital assets such as stablecoins, utility tokens, and loyalty points. 58 | 59 | + **NonFungible Token** : ZRC-1 Non-fungible token standard contract for creating 60 | unique digital assets such as digital collectibles, music records, arts, and domains. 61 | 62 | + **ZilGame** : A two-player game where the goal is to find the closest 63 | preimage of a given SHA256 digest (``puzzle``). More formally, given a 64 | digest `d`, and two values `x` and `y`, `x` is said to be a closer preimage 65 | than `y` of `d` if Distance(SHA-256(x), d) < Distance(SHA-256(y), d), for 66 | some `Distance` function. The game is played in two phases. In the first 67 | phase, players submit their hash, i.e., SHA-256(x) and SHA-256(y) using the 68 | transition ``Play(guess: ByStr32)``. Once the first player has submitted her 69 | hash, the second player has a bounded time to submit her hash. If the second 70 | player does not submit her hash within the stipulated time, then the first 71 | player may become the winner. In the second phase, players have to submit the 72 | corresponding values ``x`` or ``y`` using the transition 73 | ``ClaimReward(solution: Int128)``. The player submitting the closest 74 | preimage is declared the winner and wins a reward. The contract also 75 | provides a transition ``Withdraw ()`` to recover funds and send to a 76 | specified ``owner`` in case no player plays the game. 77 | 78 | + **SchnorrTest** : A sample contract to test the generation of a Schnorr 79 | public/private key pairs, signing of a ``msg`` with the private keys, 80 | and verification of the signature. 81 | 82 | + **ECDSATest** : A sample contract to test the generation of a ECDSA 83 | public/private keypairs, signing of a message with the private keys, 84 | and verification of the signature. 85 | -------------------------------------------------------------------------------- /docs/source/spelling_wordlist.txt: -------------------------------------------------------------------------------- 1 | Cashflow 2 | Coq 3 | Crypto 4 | Cryptographic 5 | Dapps 6 | Datatypes 7 | De 8 | Initialiser 9 | Keccak 10 | Kickstarter 11 | Namespaces 12 | Peano 13 | Preimage 14 | Prettyprinting 15 | SHA 16 | Schnorr 17 | Shogi 18 | bech 19 | blocknumber 20 | cashflow 21 | cryptographic 22 | datatype 23 | datatypes 24 | decrementing 25 | dereference 26 | discoverability 27 | endian 28 | executables 29 | facto 30 | frontend 31 | functors 32 | initialiser 33 | instantiation 34 | instantiations 35 | invariants 36 | invoker 37 | iteratively 38 | namespace 39 | namespaces 40 | preimage 41 | prettyprinting 42 | recursing 43 | satisfier 44 | scalable 45 | serialisable 46 | storable 47 | subfields 48 | subpatterns 49 | substring 50 | subvalues 51 | tokenised 52 | typechecked 53 | typechecker 54 | typechecking 55 | typechecks 56 | unassociated 57 | uninstantiated 58 | untyped 59 | Zilliqa 60 | Scilla 61 | substring 62 | substrings 63 | Uint 64 | Polynetwork 65 | deserializing 66 | keypairs 67 | mainnet 68 | testnet 69 | keystore 70 | stablecoins 71 | endianness 72 | runtime 73 | unpausing 74 | blockchain 75 | onwards 76 | pseudocode 77 | subtype 78 | typecheck 79 | subtyping 80 | explicit 81 | OCaml 82 | matcher 83 | unlisting 84 | reentrancy 85 | automata 86 | stateful 87 | Qa 88 | blockchains 89 | expressivity 90 | sharding 91 | Graphviz 92 | crossplatform 93 | -------------------------------------------------------------------------------- /docs/source/stdlib.rst: -------------------------------------------------------------------------------- 1 | The Scilla Standard Library 2 | =========================== 3 | .. _stdlib: 4 | 5 | The Scilla standard library contains five libraries: 6 | ``BoolUtils.scilla``, ``IntUtils.scilla``, ``ListUtils.scilla``, 7 | ``NatUtils.scilla`` and ``PairUtils.scilla``. As the names suggests 8 | these contracts implement utility operations for the ``Bool``, 9 | ``IntX``, ``List``, ``Nat`` and ``Pair`` types, respectively. 10 | 11 | To use functions from the standard library in a contract, the relevant 12 | library file must be imported using the ``import`` declaration. The 13 | following code snippet shows how to import the functions from the 14 | ``ListUtils`` and ``IntUtils`` libraries: 15 | 16 | .. code-block:: ocaml 17 | 18 | import ListUtils IntUtils 19 | 20 | The ``import`` declaration must occur immediately before the 21 | contract's own library declaration, e.g.: 22 | 23 | .. code-block:: ocaml 24 | 25 | import ListUtils IntUtils 26 | 27 | library WalletLib 28 | ... (* The declarations of the contract's own library values and functions *) 29 | 30 | contract Wallet ( ... ) 31 | ... (* The transitions and procedures of the contract *) 32 | 33 | 34 | Below, we present the functions defined in each of the library. 35 | 36 | BoolUtils 37 | ************ 38 | 39 | - ``andb : Bool -> Bool -> Bool``: Computes the logical AND of two ``Bool`` values. 40 | 41 | - ``orb : Bool -> Bool -> Bool``: Computes the logical OR of two ``Bool`` values. 42 | 43 | - ``negb : Bool -> Bool``: Computes the logical negation of a ``Bool`` value. 44 | 45 | - ``bool_to_string : Bool -> String``: Transforms a ``Bool`` value into a ``String`` 46 | value. ``True`` is transformed into ``"True"``, and ``False`` is 47 | transformed into ``"False"``. 48 | 49 | IntUtils 50 | ************ 51 | 52 | - ``intX_eq : IntX -> IntX -> Bool``: Equality operator specialised 53 | for each ``IntX`` type. 54 | 55 | .. code-block:: ocaml 56 | 57 | let int_list_eq = @list_eq Int64 in 58 | 59 | let one = Int64 1 in 60 | let two = Int64 2 in 61 | let ten = Int64 10 in 62 | let eleven = Int64 11 in 63 | 64 | let nil = Nil {Int64} in 65 | let l1 = Cons {Int64} eleven nil in 66 | let l2 = Cons {Int64} ten l1 in 67 | let l3 = Cons {Int64} two l2 in 68 | let l4 = Cons {Int64} one l3 in 69 | 70 | let f = int64_eq in 71 | (* See if [2,10,11] = [1,2,10,11] *) 72 | int_list_eq f l3 l4 73 | 74 | - ``uintX_eq : UintX -> UintX -> Bool``: Equality operator specialised 75 | for each ``UintX`` type. 76 | 77 | - ``intX_lt : IntX -> IntX -> Bool``: Less-than operator specialised 78 | for each ``IntX`` type. 79 | - ``uintX_lt : UintX -> UintX -> Bool``: Less-than operator specialised 80 | for each ``UintX`` type. 81 | 82 | - ``intX_neq : IntX -> IntX -> Bool``: Not-equal operator specialised 83 | for each ``IntX`` type. 84 | - ``uintX_neq : UintX -> UintX -> Bool``: Not-equal operator specialised 85 | for each ``UintX`` type. 86 | 87 | - ``intX_le : IntX -> IntX -> Bool``: Less-than-or-equal operator specialised 88 | for each ``IntX`` type. 89 | - ``uintX_le : UintX -> UintX -> Bool``: Less-than-or-equal operator specialised 90 | for each ``UintX`` type. 91 | 92 | - ``intX_gt : IntX -> IntX -> Bool``: Greater-than operator specialised 93 | for each ``IntX`` type. 94 | - ``uintX_gt : UintX -> UintX -> Bool``: Greater-than operator specialised 95 | for each ``UintX`` type. 96 | 97 | - ``intX_ge : IntX -> IntX -> Bool``: Greater-than-or-equal operator specialised 98 | for each ``IntX`` type. 99 | - ``uintX_ge : UintX -> UintX -> Bool``: Greater-than-or-equal operator specialised 100 | for each ``UintX`` type. 101 | 102 | 103 | ListUtils 104 | ************ 105 | 106 | - ``list_map : ('A -> 'B) -> List 'A -> : List 'B``. 107 | 108 | | Apply ``f : 'A -> 'B`` to every element of ``l : List 'A``, 109 | constructing a list (of type ``List 'B``) of the results. 110 | 111 | .. code-block:: ocaml 112 | 113 | (* Library *) 114 | let f = 115 | fun (a : Int32) => 116 | builtin sha256hash a 117 | 118 | (* Contract transition *) 119 | (* Assume input is the list [ 1 ; 2 ; 3 ] *) 120 | (* Apply f to all values in input *) 121 | hash_list_int32 = @list_map Int32 ByStr32; 122 | hashed_list = hash_list_int32 f input; 123 | (* hashed_list is now [ sha256hash 1 ; sha256hash 2 ; sha256hash 3 ] *) 124 | 125 | - ``list_filter : ('A -> Bool) -> List 'A -> List 'A``. 126 | 127 | | Filter out elements on the list based on the predicate 128 | ``f : 'A -> Bool``. If an element satisfies ``f``, it will be in the 129 | resultant list, otherwise it is removed. The order of the elements is 130 | preserved. 131 | 132 | .. code-block:: ocaml 133 | 134 | (*Library*) 135 | let f = 136 | fun (a : Int32) => 137 | let ten = Int32 10 in 138 | builtin lt a ten 139 | 140 | (* Contract transition *) 141 | (* Assume input is the list [ 1 ; 42 ; 2 ; 11 ; 12 ] *) 142 | less_ten_int32 = @list_filter Int32; 143 | less_ten_list = less_ten_int32 f l 144 | (* less_ten_list is now [ 1 ; 2 ]*) 145 | 146 | - ``list_head : (List 'A) -> (Option 'A)``. 147 | 148 | | Return the head element of a list ``l : List 'A`` as an optional 149 | value. If ``l`` is not empty with the first element ``h``, the 150 | result is ``Some h``. If ``l`` is empty, then the result is 151 | ``None``. 152 | 153 | - ``list_tail : (List 'A) -> (Option List 'A)``. 154 | 155 | | Return the tail of a list ``l : List 'A`` as an optional value. If 156 | ``l`` is a non-empty list of the form ``Cons h t``, then the 157 | result is ``Some t``. If ``l`` is empty, then the result is 158 | ``None``. 159 | 160 | - ``list_foldl_while : ('B -> 'A -> Option 'B) -> 'B -> List 'A -> 'B`` 161 | 162 | | Given a function ``f : 'B -> 'A -> Option 'B``, accumulator ``z : 'B`` 163 | and list ``ls : List 'A`` execute a left fold when our given function 164 | returns ``Some x : Option 'B`` using ``f z x : 'B`` or list is empty 165 | but in the case of ``None : Option 'B`` terminate early, returning ``z``. 166 | 167 | .. code-block:: ocaml 168 | 169 | (* assume zero = 0, one = 1, negb is in scope and ls = [10,12,9,7] 170 | given a max and list with elements a_0, a_1, ..., a_m 171 | find largest n s.t. sum of i from 0 to (n-1) a_i <= max *) 172 | let prefix_step = fun (len_limit : Pair Uint32 Uint32) => fun (x : Uint32) => 173 | match len_limit with 174 | | Pair len limit => let limit_lt_x = builtin lt limit x in 175 | let x_leq_limit = negb limit_lt_x in 176 | match x_leq_limit with 177 | | True => let len_succ = builtin add len one in let l_sub_x = builtin sub limit x in 178 | let res = Pair {Uint32 Uint32} len_succ l_sub_x in 179 | Some {(Pair Uint32 Uint32)} res 180 | | False => None {(Pair Uint32 Uint32)} 181 | end 182 | end in 183 | let fold_while = @list_foldl_while Uint32 (Pair Uint32 Uint32) in 184 | let max = Uint32 31 in 185 | let init = Pair {Uint32 Uint32} zero max in 186 | let prefix_length = fold_while prefix_step init ls in 187 | match prefix_length with 188 | | Pair length _ => length 189 | end 190 | 191 | 192 | - ``list_append : (List 'A -> List 'A -> List 'A)``. 193 | 194 | | Append the first list to the front of the second list, keeping the 195 | order of the elements in both lists. Note that ``list_append`` has 196 | linear time complexity in the length of the first argument list. 197 | 198 | - ``list_reverse : (List 'A -> List 'A)``. 199 | 200 | | Return the reverse of the input list. Note that ``list_reverse`` 201 | has linear time complexity in the length of the argument list. 202 | 203 | - ``list_flatten : (List List 'A) -> List 'A``. 204 | 205 | | Construct a list of all the elements in a list of lists. Each 206 | element (which has type ``List 'A``) of the input list (which has 207 | type ``List List 'A``) are all concatenated together, keeping the 208 | order of the input list. Note that ``list_flatten`` has linear 209 | time complexity in the total number of elements in all of the 210 | lists. 211 | 212 | - ``list_length : List 'A -> Uint32`` 213 | 214 | | Count the number of elements in a list. Note that ``list_length`` 215 | has linear time complexity in the number of elements in the list. 216 | 217 | - ``list_eq : ('A -> 'A -> Bool) -> List 'A -> List 'A -> Bool``. 218 | 219 | | Compare two lists element by element, using a predicate function 220 | ``f : 'A -> 'A -> Bool``. If ``f`` returns ``True`` for every pair 221 | of elements, then ``list_eq`` returns ``True``. If ``f`` returns 222 | ``False`` for at least one pair of elements, or if the lists have 223 | different lengths, then ``list_eq`` returns ``False``. 224 | 225 | - ``list_mem : ('A -> 'A -> Bool) -> 'A -> List 'A -> Bool``. 226 | 227 | | Checks whether an element ``a : 'A`` is an element in the list 228 | ``l : List'A``. ``f : 'A -> 'A -> Bool`` should be provided for 229 | equality comparison. 230 | 231 | .. code-block:: ocaml 232 | 233 | (* Library *) 234 | let f = 235 | fun (a : Int32) => 236 | fun (b : Int32) => 237 | builtin eq a b 238 | 239 | (* Contract transition *) 240 | (* Assume input is the list [ 1 ; 2 ; 3 ; 4 ] *) 241 | keynumber = Int32 5; 242 | list_mem_int32 = @list_mem Int32; 243 | check_result = list_mem_int32 f keynumber input; 244 | (* check_result is now False *) 245 | 246 | - ``list_forall : ('A -> Bool) -> List 'A -> Bool``. 247 | 248 | | Check whether all elements of list ``l : List 'A`` satisfy the 249 | predicate ``f : 'A -> Bool``. ``list_forall`` returns ``True`` if 250 | all elements satisfy ``f``, and ``False`` if at least one element 251 | does not satisfy ``f``. 252 | 253 | - ``list_exists : ('A -> Bool) -> List 'A -> Bool``. 254 | 255 | | Check whether at least one element of list ``l : List 'A`` 256 | satisfies the predicate ``f : 'A -> Bool``. ``list_exists`` 257 | returns ``True`` if at least one element satisfies ``f``, and 258 | ``False`` if none of the elements satisfy ``f``. 259 | 260 | - ``list_sort : ('A -> 'A -> Bool) -> List 'A -> List 'A``. 261 | 262 | | Sort the input list ``l : List 'A`` using insertion sort. The 263 | comparison function ``flt : 'A -> 'A -> Bool`` provided must 264 | return ``True`` if its first argument is less than its second 265 | argument. ``list_sort`` has quadratic time complexity. 266 | 267 | .. code-block:: ocaml 268 | 269 | let int_sort = @list_sort Uint64 in 270 | 271 | let flt = 272 | fun (a : Uint64) => 273 | fun (b : Uint64) => 274 | builtin lt a b 275 | 276 | let zero = Uint64 0 in 277 | let one = Uint64 1 in 278 | let two = Uint64 2 in 279 | let three = Uint64 3 in 280 | let four = Uint64 4 in 281 | 282 | (* l6 = [ 3 ; 2 ; 1 ; 2 ; 3 ; 4 ; 2 ] *) 283 | let l6 = 284 | let nil = Nil {Uint64} in 285 | let l0 = Cons {Uint64} two nil in 286 | let l1 = Cons {Uint64} four l0 in 287 | let l2 = Cons {Uint64} three l1 in 288 | let l3 = Cons {Uint64} two l2 in 289 | let l4 = Cons {Uint64} one l3 in 290 | let l5 = Cons {Uint64} two l4 in 291 | Cons {Uint64} three l5 292 | 293 | (* res1 = [ 1 ; 2 ; 2 ; 2 ; 3 ; 3 ; 4 ] *) 294 | let res1 = int_sort flt l6 295 | 296 | - ``list_find : ('A -> Bool) -> List 'A -> Option 'A``. 297 | 298 | | Return the first element in a list ``l : List 'A`` satisfying the 299 | predicate ``f : 'A -> Bool``. If at least one element in the list 300 | satisfies the predicate, and the first one of those elements is 301 | ``x``, then the result is ``Some x``. If no element satisfies the 302 | predicate, the result is ``None``. 303 | 304 | - ``list_zip : List 'A -> List 'B -> List (Pair 'A 'B)``. 305 | 306 | | Combine two lists element by element, resulting in a list of 307 | pairs. If the lists have different lengths, the trailing elements 308 | of the longest list are ignored. 309 | 310 | - ``list_zip_with : ('A -> 'B -> 'C) -> List 'A -> List 'B -> List 'C )``. 311 | 312 | | Combine two lists element by element using a combining function 313 | ``f : 'A -> 'B -> 'C``. The result of ``list_zip_with`` is a list 314 | of the results of applying ``f`` to the elements of the two 315 | lists. If the lists have different lengths, the trailing elements 316 | of the longest list are ignored. 317 | 318 | - ``list_unzip : List (Pair 'A 'B) -> Pair (List 'A) (List 'B)``. 319 | 320 | | Split a list of pairs into a pair of lists consisting of the 321 | elements of the pairs of the original list. 322 | 323 | - ``list_nth : Uint32 -> List 'A -> Option 'A``. 324 | 325 | | Return the element number ``n`` from a list. If the list has at 326 | least ``n`` elements, and the element number ``n`` is ``x``, 327 | ``list_nth`` returns ``Some x``. If the list has fewer than ``n`` 328 | elements, ``list_nth`` returns ``None``. 329 | 330 | NatUtils 331 | ************ 332 | 333 | - ``nat_prev : Nat -> Option Nat``: Return the Peano number one less 334 | than the current one. If the current number is ``Zero``, the result 335 | is ``None``. If the current number is ``Succ x``, then the result is 336 | ``Some x``. 337 | 338 | - ``nat_fold_while : ('T -> Nat -> Option 'T) -> 'T -> Nat -> 'T``: 339 | Takes arguments ``f : 'T -> Nat -> Option 'T``, ``z : `T`` and 340 | ``m : Nat``. This is ``nat_fold`` with early termination. Continues 341 | recursing so long as ``f`` returns ``Some y`` with new accumulator 342 | ``y``. Once ``f`` returns ``None``, the recursion terminates. 343 | 344 | - ``is_some_zero : Nat -> Bool``: Zero check for Peano numbers. 345 | 346 | - ``nat_eq : Nat -> Nat -> Bool``: Equality check specialised for the 347 | ``Nat`` type. 348 | 349 | - ``nat_to_int : Nat -> Uint32``: Convert a Peano number to its 350 | equivalent ``Uint32`` integer. 351 | 352 | - ``uintX_to_nat : UintX -> Nat``: Convert a ``UintX`` integer to its 353 | equivalent Peano number. The integer must be small enough to fit 354 | into a ``Uint32``. If it is not, then an overflow error will occur. 355 | 356 | - ``intX_to_nat : IntX -> Nat``: Convert an ``IntX`` integer to its 357 | equivalent Peano number. The integer must be non-negative, and must 358 | be small enough to fit into a ``Uint32``. If it is not, then an 359 | underflow or overflow error will occur. 360 | 361 | 362 | PairUtils 363 | ************ 364 | 365 | - ``fst : Pair 'A 'B -> 'A``: Extract the first element of a Pair. 366 | 367 | .. code-block:: ocaml 368 | 369 | let fst_strings = @fst String String in 370 | let nick_name = "toby" in 371 | let dog = "dog" in 372 | let tobias = Pair {String String} nick_name dog in 373 | fst_strings tobias 374 | 375 | - ``snd : Pair 'A 'B -> 'B``: Extract the second element of a Pair. 376 | 377 | Conversions 378 | *********** 379 | 380 | This library provides conversions b/w Scilla types, particularly 381 | between integers and byte strings. 382 | 383 | To enable specifying the encoding of integer arguments to these functions, 384 | a type is defined for endianness. 385 | 386 | .. code-block:: ocaml 387 | 388 | type IntegerEncoding = 389 | | LittleEndian 390 | | BigEndian 391 | 392 | The functions below, along with their primary result, also return ``next_pos : Uint32`` 393 | which indicates the position from which any further data extraction from the input 394 | ``ByStr`` value can proceed. This is useful when deserializing a byte stream. In other 395 | words, ``next_pos`` indicates where this function stopped reading bytes from the input 396 | byte string. 397 | 398 | - ``substr_safe : ByStr -> Uint32 -> Uint32 -> Option ByStr`` 399 | While Scilla provides a builtin to extract substrings 400 | of byte strings (``ByStr``), it is not exception safe. When provided incorrect 401 | arguments, it throws exceptions. This library function is provided as an 402 | exception safe function to extract, from a string ``s : ByStr``, a substring 403 | starting at position ``pos : Uint32`` and of length ``len : Uint32``. It 404 | returns ``Some ByStr`` on success and ``None`` on failure. 405 | 406 | - ``extract_uint32 : IntegerEncoding -> ByStr -> Uint32 -> Option (Pair Uint32 Uint32)`` 407 | Extracts a ``Uint32`` value from ``bs : ByStr``, starting at position ``pos : Uint32``. 408 | On success, ``Some extracted_uint32_value next_pos`` is returned. ``None`` otherwise. 409 | 410 | - ``extract_uint64 : IntegerEncoding -> ByStr -> Uint32 -> Option (Pair Uint64 Uint32)`` 411 | Extracts a ``Uint64`` value from ``bs : ByStr``, starting at position ``pos : Uint32``. 412 | On success, ``Some extracted_uint64_value next_pos`` is returned. ``None`` otherwise. 413 | 414 | - ``extract_uint128 : IntegerEncoding -> ByStr -> Uint32 -> Option (Pair Uint128 Uint32)`` 415 | Extracts a Uint128 value from ``bs : ByStr``, starting at position ``pos : Uint32``. 416 | On success, ``Some extracted_uint128_value next_pos`` is returned. ``None`` otherwise. 417 | 418 | - ``extract_uint256 : IntegerEncoding -> ByStr -> Uint32 -> Option (Pair Uint256 Uint32)`` 419 | Extracts a ``Uint256`` value from ``bs : ByStr``, starting at position ``pos : Uint32``. 420 | On success, ``Some extracted_uint256_value next_pos`` is returned. ``None`` otherwise. 421 | 422 | - ``extract_bystr1 : ByStr -> Uint32 -> Option (Pair ByStr1 Uint32)`` 423 | Extracts a ``ByStr1`` value from ``bs : ByStr``, starting at position ``pos : Uint32``. 424 | On success, ``Some extracted_bystr1_value next_pos`` is returned. ``None`` otherwise. 425 | 426 | - ``extract_bystr2 : ByStr -> Uint32 -> Option (Pair ByStr2 Uint32)`` 427 | Extracts a ``ByStr2`` value from ``bs : ByStr``, starting at position ``pos : Uint32``. 428 | On success, ``Some extracted_bystr2_value next_pos`` is returned. ``None`` otherwise. 429 | 430 | - ``extract_bystr20 : ByStr -> Uint32 -> Option (Pair ByStr20 Uint32)`` 431 | Extracts a ``ByStr2`` value from ``bs : ByStr``, starting at position ``pos : Uint32``. 432 | On success, ``Some extracted_bystr20_value next_pos`` is returned. ``None`` otherwise. 433 | 434 | - ``extract_bystr32 : ByStr -> Uint32 -> Option (Pair ByStr32 Uint32)`` 435 | Extracts a ``ByStr2`` value from ``bs : ByStr``, starting at position ``pos : Uint32``. 436 | On success, ``Some extracted_bystr32_value next_pos`` is returned. ``None`` otherwise. 437 | 438 | - ``append_uint32 : IntegerEncoding -> ByStr -> Uint32 -> ByStr`` 439 | Serialize a ``Uint32`` value (with the specified encoding) and append it to the provided 440 | ``ByStr`` and return the result ``ByStr``. 441 | 442 | - ``append_uint64 : IntegerEncoding -> ByStr -> Uint32 -> ByStr`` 443 | Serialize a ``Uint64`` value (with the specified encoding) and append it to the provided 444 | ``ByStr`` and return the result ``ByStr``. 445 | 446 | - ``append_uint128 : IntegerEncoding -> ByStr -> Uint32 -> ByStr`` 447 | Serialize a ``Uint128`` value (with the specified encoding) and append it to the provided 448 | ``ByStr`` and return the result ``ByStr``. 449 | 450 | - ``append_uint256 : IntegerEncoding -> ByStr -> Uint32 -> ByStr`` 451 | Serialize a ``Uint256`` value (with the specified encoding) and append it to the provided 452 | ``ByStr`` and return the result ``ByStr``. 453 | 454 | Polynetwork Support Library 455 | *************************** 456 | 457 | This library provides utility functions used in building the Zilliqa Polynetwork bridge. 458 | These functions are migrated from 459 | `Polynetwork's ethereum support `_, 460 | with the contract itself separately deployed. 461 | -------------------------------------------------------------------------------- /docs/texsources/gas-costs/Makefile: -------------------------------------------------------------------------------- 1 | all: gas-doc.pdf 2 | 3 | quick: Makefile *.tex *.bib *.cls 4 | pdflatex gas-doc 5 | 6 | gas-doc.pdf: Makefile *.tex *.bib *.cls 7 | pdflatex gas-doc 8 | 9 | clean: 10 | rm -f *.aux *.bbl *.blg *.log *.out *.rel gas-doc.pdf gas-doc.idx 11 | 12 | .PHONY: all clean *.tex *.bib *.cls 13 | -------------------------------------------------------------------------------- /docs/texsources/gas-costs/gas-doc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zilliqa/scilla-docs/db915484d65e20d21771aec785d8c82cb2d07516/docs/texsources/gas-costs/gas-doc.pdf -------------------------------------------------------------------------------- /docs/texsources/gas-costs/gas-doc.tex: -------------------------------------------------------------------------------- 1 | \documentclass[10pt]{article} 2 | \usepackage{geometry} 3 | \geometry{ 4 | a4paper, 5 | total={170mm,257mm}, 6 | %left=20mm, 7 | %top=30mm, 8 | } 9 | % Required to make the margins smaller to fit more content on each page 10 | \usepackage[linkcolor=blue]{hyperref} % Required to create hyperlinks to questions from elsewhere in the document 11 | \hypersetup{pdfborder={0 0 0}, colorlinks=true, urlcolor=blue} % Specify a color for hyperlinks 12 | %\usepackage{tocloft} % Required to give customize the table of contents to display questions 13 | \usepackage{microtype} % Slightly tweak font spacing for aesthetics 14 | \usepackage{palatino} % Use the Palatino font 15 | \usepackage{verbatim} 16 | \usepackage{makeidx} 17 | \usepackage{xcolor} 18 | \usepackage{amsmath,multirow} 19 | % Uncomment the line below to get rid of the trailing dots in the table of contents 20 | %\renewcommand{\cftdot}{} 21 | 22 | % Uncomment the two lines below to get rid of the numbers in the table of contents 23 | %\let\Contentsline\contentsline 24 | %\renewcommand\contentsline[3]{\Contentsline{#1}{#2}{}} 25 | \usepackage{marvosym} 26 | \usepackage{fontawesome} 27 | \usepackage{tipa} 28 | \usepackage{ltablex} 29 | \usepackage{caption, booktabs} 30 | \usepackage{ragged2e} 31 | 32 | \makeindex 33 | 34 | 35 | \begin{document} 36 | 37 | \title{\Huge{\textbf{Gas Accounting on \textsc{Zilliqa}}} \\ \small{[Draft version 38 | 0.1]} \\ \small{\textcolor{red}{Subject to changes}}} 39 | 40 | \author{ 41 | \href{https://www.zilliqa.com/team.html}{The \textsc{Zilliqa} Team} \\ 42 | \Mundus~\href{https://www.zilliqa.com}{\texttt{www.zilliqa.com}} 43 | \phantom{xx}\faQuestionCircle~\href{https://gitter.im/Zilliqa/SmartContract}{\texttt{gitter.im/zilliqa}} 44 | } 45 | 46 | 47 | \maketitle 48 | 49 | \section{Storage} 50 | 51 | Gas consumed for storage (presented in \autoref{tab:storage}) depends on the 52 | type of literal being stored. In the table below, $\ell$ denotes the literal 53 | and $litGas(\ell)$, the gas consumed for storing it. Note that for certain 54 | literal types such as \textbf{\texttt{\textcolor{teal}{Messsage}}}, 55 | \textbf{\texttt{\textcolor{teal}{Map}}} and 56 | \textbf{\texttt{\textcolor{teal}{ADT}}}, $litGas(\ell)$ is recursively 57 | computed. 58 | 59 | 60 | \begin{table}[!hbt] 61 | \caption{Gas for storage of literals. \label{tab:storage} } 62 | \centering 63 | \begin{tabular}{|p{3cm}|p{6.7cm}|p{6cm}|} 64 | \hline 65 | \textbf{Literal Type} & \textbf{$litGas(\ell)$} & \textbf{Remarks} \\ \hline 66 | \textbf{\texttt{\textcolor{teal}{String}}} & $$ 67 | \begin{cases} 68 | 20, & \text{if } len(\ell) \leq 20, \\ 69 | len(\ell), & \text{else } \\ 70 | \end{cases} $$ & $len(\cdot)$ computes the length of an input string, e.g., 71 | $len(\texttt{"Hello"})$ = 5. \\ \hline 72 | \textbf{\texttt{\textcolor{teal}{Int32}}}, 73 | \textbf{\texttt{\textcolor{teal}{Uint32}}} & $4$ 74 | & \multirow{4}{6cm} 75 | {Gas consumed is equal to the size of integer in bytes.} \\ \cline{1-2} 76 | \textbf{\texttt{\textcolor{teal}{Int64}}}, 77 | \textbf{\texttt{\textcolor{teal}{Uint64}}} & $8$ & \\ \cline{1-2} 78 | \textbf{\texttt{\textcolor{teal}{Int128}}}, 79 | \textbf{\texttt{\textcolor{teal}{Uint128}}} & $16$ &\\ \cline{1-2} 80 | \textbf{\texttt{\textcolor{teal}{Int256}}}, 81 | \textbf{\texttt{\textcolor{teal}{Uint256}}} & $32$ & \\ \hline 82 | \textbf{\texttt{\textcolor{teal}{BNum}}} & $64$ & Internally 83 | represented as an unsigned BigNum. \\ \hline 84 | \textbf{\texttt{\textcolor{teal}{ByStrX}}}, \textbf{\texttt{\textcolor{teal}{ByStr}}} & 85 | \multirow{2}{*}{$width(\ell)$} & $width(\cdot)$ returns 86 | the number of bytes needed to represent the hexadecimal input, e.g., 87 | $width(\texttt{0x1cd2}) = 2.$ \\ \hline 88 | \textbf{\texttt{\textcolor{teal}{Messsage}}} 89 | & For $\ell = \{s_1 : v_1; s_2 : v_2; 90 | \ldots, s_n : v_n\}$: 91 | $$ \begin{cases} 92 | 0, & \text{if } n = 0, \\ 93 | litGas(\{s_2 : v_2; \ldots, s_n : v_n\}) \\ 94 | + litGas(s_1) + litGas(v_1), & 95 | \text{else } \\ 96 | \end{cases} 97 | $$ & As a \textbf{\texttt{\textcolor{teal}{Messsage}}} literal is an associative array 98 | between a \textbf{\texttt{\textcolor{teal}{String}}} literal (denoted $s_i$) and a 99 | value literal (denoted $v_i$), $litGas$ 100 | is recursively computed by summing up over each $s_i : v_i$. \\ \hline 101 | \textbf{\texttt{\textcolor{teal}{Map}}} & 102 | For $\ell = \{(k_1 \rightarrow v_1), 103 | \ldots, (k_n \rightarrow v_n)\}$: 104 | $$ \begin{cases} 105 | 0, & \text{if } n = 0, \\ 106 | \sum_{(k \rightarrow v) \in \ell} {litGas(k) + litGas(v)}, & 107 | \text{else } \\ 108 | \end{cases} 109 | $$ & As a \textbf{\texttt{\textcolor{teal}{Map}}} literal maps a key literal ($k_i$) to a 110 | value literal ($v_i$), $litGas$ 111 | is recursively computed by summing up over each $k_i \rightarrow v_i$. \\ \hline 112 | \textbf{\texttt{\textcolor{teal}{ADT}}} (e.g., 113 | \textbf{\textcolor{magenta}{\texttt{Bool}}}, 114 | \textbf{\textcolor{magenta}{\texttt{Option}}}, 115 | \textbf{\textcolor{magenta}{\texttt{List}}}) & For $\ell = \texttt{cname}\; 116 | \{\texttt{types}\} \; t_1 t_2 \ldots t_n $: 117 | $$ \begin{cases} 118 | 1, & \text{if } n = 0, \\ 119 | \sum_{i} {litGas(t_i)}, & 120 | \text{else } \\ 121 | \end{cases} 122 | $$ 123 | 124 | & An \textbf{\texttt{\textcolor{teal}{ADT}}} literal takes a constructor name 125 | (\texttt{cname}), types of the arguments (\texttt{types}) and arguments 126 | denoted by $t_1, t_2, \ldots, t_n$. $litGas$ is recursively computed 127 | by summing up the gas required over each $t_i$. \\ \hline 128 | \end{tabular} 129 | \end{table} 130 | 131 | \section{Computation} 132 | 133 | In this section, we present the gas required to perform computations in a contract 134 | using \textsc{Scilla} \emph{expressions}. Expressions handle purely 135 | mathematical computations and do not have any side-effects. 136 | 137 | Gas consumed for a computation via an expression can be divided into two parts: the 138 | static part associated with employing a specific expression and the dynamic 139 | part that takes into account the cost that can only be estimated at run time. 140 | Some expressions do not entail any dynamic gas consumption. 141 | 142 | \subsection{Static Cost for Expressions} 143 | 144 | Every expression has a static gas associated with it. 145 | \autoref{tab:expressions} lists the gas for each expression supported in 146 | \textsc{Scilla}. In the table below, $e$ denotes the expression and 147 | $statExprCost(e)$ the static gas associated with using $e$. 148 | 149 | \begin{table}[!hbt] 150 | \caption{Static gas for expressions. \label{tab:expressions} } 151 | \centering 152 | \begin{tabular}{|l|c|p{10.0cm}|} 153 | \hline 154 | \textbf{Expression} & \textbf{$statExprGas(e)$} & \textbf{Remarks} \\ \hline 155 | \textbf{\texttt{\textcolor{teal}{Literal}}} & \multirow{9}{*}{1} & \\ 156 | \cline{1-1} \cline{3-3} 157 | \textbf{\texttt{\textcolor{teal}{Var}}} & & \\ \cline{1-1}\cline{3-3} 158 | 159 | \textbf{\texttt{\textcolor{teal}{Let}}} & & \\ \cline{1-1}\cline{3-3} 160 | 161 | \textbf{\texttt{\textcolor{teal}{Message}}} & & \\ \cline{1-1}\cline{3-3} 162 | \textbf{\texttt{\textcolor{teal}{Fun}}} & & A function declaration, e.g., \texttt{\textbf{\textcolor{violet}{fun}} (x : \textbf{\textcolor{orange}{T}}) => e}. \\ \cline{1-1} \cline{3-3} 163 | \textbf{\texttt{\textcolor{teal}{App}}} & & A function application, e.g., 164 | \texttt{\textbf{\textcolor{violet}{f}} }. \\ \cline{1-1}\cline{3-3} 165 | 166 | \textbf{\texttt{\textcolor{teal}{TFun}}} & & A type function of 167 | the form: \texttt{\textbf{\textcolor{violet}{tfun}} $\alpha$ => e}. \\ \cline{1-1}\cline{3-3} 168 | 169 | \textbf{\texttt{\textcolor{teal}{TApp}}} & & A type 170 | instantiation: \texttt{\textbf{\textcolor{violet}{@}}x 171 | \textbf{\textcolor{orange}{T}}}. \\ \cline{1-1}\cline{3-3} 172 | 173 | \textbf{\texttt{\textcolor{teal}{Constr}}} & & Constructors. \\ \hline 174 | \textbf{\texttt{\textcolor{teal}{MatchExpr}}} & $nbClauses(e)$ 175 | &$nbClauses(\cdot)$ returns the number of clauses in the pattern match. 176 | \\ \hline 177 | \textbf{\texttt{\textcolor{teal}{Fixpoint}}} & 1 & Rest of the gas is 178 | accounted during recursive evaluation. \\ \hline 179 | \textbf{\texttt{\textcolor{teal}{Builtin}}} & 0 & Purely dynamic gas 180 | accounting. For more details see~\autoref{tab:gas_builtins}. \\ \hline 181 | \end{tabular} 182 | \end{table} 183 | 184 | 185 | \subsection{Executing Statements} 186 | 187 | State changes and other operations that entail side-effects such as reading the 188 | current state of the blockchain or invoking message calls to other contracts 189 | are performed via \textsc{Scilla} \emph{statements}. \autoref{tab:statements} 190 | presents the gas consumed for statements. In the table, $l$ is the literal 191 | being handled and $stateGas(\ell)$ is the required gas. Note that the 192 | $litGas(\cdot)$ function used below comes from~\autoref{tab:storage}. 193 | 194 | \begin{table}[!hbt] 195 | \caption{Gas for statements. \label{tab:statements} } 196 | \centering 197 | \begin{tabular}{|l|p{8cm}|p{4cm}|} 198 | \hline 199 | \textbf{Statement $(l)$} & \textbf{$stateGas(l)$} & \textbf{Remarks} \\ \hline 200 | \textbf{\texttt{\textcolor{teal}{G\_Load}}} & $litGas(l)$ & \\ \hline 201 | \textbf{\texttt{\textcolor{teal}{G\_Store}}} & $\max\{litGas(l_{old}), 202 | litGas(l_{new})\} + litGas(l_{new}) - 203 | litGas(l_{old})$ & $\ell_{old}$ is the existing literal, while, $\ell_{new}$ is the 204 | new literal to be stored. Note that gas for the store statement can be 0. \\ \hline 205 | \textbf{\texttt{\textcolor{teal}{G\_Bind}}} & 1 & \\ \hline 206 | \textbf{\texttt{\textcolor{teal}{G\_MatchStmt}}} & $nbclauses(l)$ & \\ \hline 207 | \textbf{\texttt{\textcolor{teal}{G\_ReadFromBC}}} & 1 & Gas to read 208 | current blockchain values such as 209 | \textbf{\textcolor{magenta}{\texttt{BLOCKNUMBER}}} (previous block 210 | number). \\ \hline 211 | \textbf{\texttt{\textcolor{teal}{G\_AcceptPayment}}} & 1 & \\ \hline 212 | \textbf{\texttt{\textcolor{teal}{G\_SendMsgs}}} & For $t = [\{s_{1}^{1} : 213 | v_{1}^{1}; \ldots, s_n^1 : v_n^1\}, \{s_1^2 : v_1^2; \ldots, s_n^2 : 214 | v_n^2\}, \ldots, \{s_1^m : v_1^m; \ldots, s_n^2 : v_n^m\}] $: $$ 215 | \sum_{\ell \in t} litGas(l) $$ & 216 | \textbf{\texttt{\textcolor{teal}{G\_SendMsgs}}} takes a list of 217 | \textbf{\texttt{\textcolor{teal}{Messsage}}} as an input. Hence, gas 218 | required is the sum of $litGas(\cdot)$ for each individual 219 | \textbf{\texttt{\textcolor{teal}{Messsage}}} in the list. \\ \hline 220 | \textbf{\texttt{\textcolor{teal}{G\_CreateEvent}}} & $litGas(\ell)$ & \\ \hline 221 | \end{tabular} 222 | \end{table} 223 | 224 | 225 | \subsection{Builtins} 226 | 227 | In~\autoref{tab:gas_builtins}, we present the dynamic gas associated with 228 | \textbf{\texttt{\textcolor{teal}{Builtin}}} operators in \textsc{Scilla}. The 229 | gas consumed often depends on the operator and operand types, etc. In the table 230 | below, we group \textbf{\texttt{\textcolor{teal}{Builtin}}} in categories which 231 | are self-explanatory. 232 | 233 | 234 | \begin{tabularx}{\linewidth}{|l|p{4.1cm}|p{4cm}|p{4cm}|} 235 | \caption{Gas required for Builtin operations.} \label{tab:gas_builtins} \\ 236 | \hline 237 | \textbf{Category} & \textbf{Operation} & \textbf{$builtinGas$} & \textbf{Remarks} \\ \hline 238 | \hline 239 | \endfirsthead 240 | \hline 241 | \textbf{Category} & \textbf{Operation} & \textbf{$builtinGas$} & \textbf{Remarks} \\ \hline 242 | \hline 243 | \endhead 244 | %\midrule 245 | \hline 246 | \multicolumn{4}{r}{\footnotesize( To be continued)} 247 | \endfoot 248 | %\bottomrule 249 | \endlastfoot 250 | \multirow{3}{*}{\textbf{\texttt{\textcolor{teal}{String}}}} & 251 | \textbf{\texttt{\textcolor{magenta}{eq}}} & For \textbf{ 252 | \texttt{\textcolor{magenta}{eq}}} $s_1\, s_2$: $$ \min \{len(s_1), len(s_2)\} $$ 253 | & \multirow{14}{4cm}{$len(\cdot)$ computes the length of an input string, e.g., 254 | $len(\texttt{"Hello"})$ = 5.} \\ 255 | \cline{2-3} 256 | & \texttt{\textbf{\textcolor{magenta}{concat}}} & For \textbf{ 257 | \texttt{\textcolor{magenta}{concat}}} $s_1\, s_2$: $$ len(s_1) + len(s_2) $$ & \\ \cline{2-3} 258 | & \textbf{\texttt{\textcolor{magenta}{substr}}} & 259 | For \textbf{ 260 | \texttt{\textcolor{magenta}{substr}}} $s \, \, i_1 \, i_2$: $$ i_1 + 261 | \min \{len(s) - i_1, i_2\} $$ 262 | & \\ \hline \hline 263 | \multirow{5}{*}{\textbf{\texttt{\textcolor{teal}{Hashing}}}} & 264 | \textbf{\texttt{\textcolor{magenta}{eq}}} & For 265 | \textbf{\texttt{\textcolor{magenta}{eq}}} $d_1 \, d_2$: $$width(d_i) 266 | $$ & $width(\cdot)$ 267 | returns the size in number of bytes of the input. Note that the 268 | operator expects two inputs of the same size. \\ \cline{2-4} 269 | & \textbf{\texttt{\textcolor{magenta}{dist}}} & $32$ & 270 | \textbf{\texttt{\textcolor{magenta}{dist}}} computes the distance 271 | between two \textbf{\textcolor{orange}{\texttt{ByStr32}}} values. \\ \cline{2-4} 272 | & \textbf{\texttt{\textcolor{magenta}{ripemd160hash}}} & For 273 | \textbf{\texttt{\textcolor{magenta}{ripemd160hash}}} $x$: $$ 10 274 | \times \left\lceil 275 | \frac{size(x)}{64}\right \rceil $$ & \multirow{3}{4cm}{\justify{$size(\cdot)$ 276 | returns the size of the serialized input in bytes. Gas consumed is 277 | dependent on the input size and the block size of the hash function. 278 | Block sizes of \textbf{\texttt{\textcolor{magenta}{ripemd160hash}}}, 279 | \textbf{\texttt{\textcolor{magenta}{sha256hash}}} and 280 | \textbf{\texttt{\textcolor{magenta}{keccak256hash}}} are 64, 64 and 281 | 136 bytes respectively.}} \\ 282 | \cline{2-3} 283 | & \textbf{\texttt{\textcolor{magenta}{sha256hash}}} & For 284 | \textbf{\texttt{\textcolor{magenta}{sha256hash}}} $x$: $$15 \times \left\lceil 285 | \frac{size(x)}{64}\right \rceil $$ & \\ \cline{2-3} 286 | & \textbf{\texttt{\textcolor{magenta}{keccak256hash}}} & 287 | For 288 | \textbf{\texttt{\textcolor{magenta}{keccak256hash}}} $x$: $$ 15 289 | \times \left\lceil \frac{size(x)}{136}\right \rceil $$ 290 | & \\ \hline 291 | \hline 292 | \multirow{4}{*}{\textbf{\texttt{\textcolor{teal}{Signing}}}} & 293 | \textbf{\texttt{\textcolor{magenta}{schnorr\_gen\_key\_pair}}} & $ 20 $ & \\ 294 | \cline{2-4} 295 | & \textbf{\texttt{\textcolor{magenta}{schnorr\_sign}}} & For~\textbf{\texttt{\textcolor{magenta}{schnorr\_sign}}} $k_{priv} \, 296 | k_{pub} 297 | \, m:$ $$350 + 15 298 | \times \left\lceil 299 | \frac{66+size(m)}{64}\right \rceil $$ & Signing requires computing hash 300 | of the input message and other parameters (of size 66 bytes). The 301 | constant cost of 350 is for elliptic curve operations and other base 302 | field operations. \\ \cline{2-4} 303 | & \textbf{\texttt{\textcolor{magenta}{schnorr\_verify}}} & 304 | For~\textbf{\texttt{\textcolor{magenta}{schnorr\_verify}}} $ 305 | k_{pub} 306 | \, m \, sig:$ $$250 + 15 307 | \times \left\lceil 308 | \frac{66+size(m)}{64}\right \rceil $$ & Verification also requires computing hash 309 | of the input message and other parameters (of size 66 bytes). The 310 | constant cost of 250 is for elliptic curve operations and other base 311 | field operations. Verification is cheaper than signing. \\ 312 | \hline \hline 313 | \textbf{\texttt{\textcolor{teal}{ByStrX}}} & 314 | \textbf{\texttt{\textcolor{magenta}{to\_bystr}}} & $width(a)$ & 315 | \textbf{\texttt{\textcolor{magenta}{to\_bystr}}} is a conversion utility to 316 | convert from \textbf{\texttt{\textcolor{teal}{ByStrX}}} to 317 | \textbf{\texttt{\textcolor{teal}{ByStr}}}. $width(\cdot)$ 318 | returns 319 | the number of 320 | bytes represented 321 | by the input.\\ \hline 322 | \hline 323 | \multirow{2}{*}{\textbf{\texttt{\textcolor{teal}{Map}}}} & 324 | \textbf{\texttt{\textcolor{magenta}{contains}}}, 325 | \textbf{\texttt{\textcolor{magenta}{get}}} & $1$ & Requires constant 326 | time. \\ 327 | \cline{2-4} 328 | & \textbf{\texttt{\textcolor{magenta}{put}}}, 329 | \textbf{\texttt{\textcolor{magenta}{remove}}}, 330 | \textbf{\texttt{\textcolor{magenta}{to\_list}}}, 331 | \textbf{\texttt{\textcolor{magenta}{size}}} & $ 1 + 332 | lenOfMap$ & $lenOfMap$ is the number of keys in the map. \\ 333 | \hline \hline 334 | \multirow{4}{*}{\textbf{\texttt{\textcolor{teal}{Nat}}}} & 335 | \textbf{\texttt{\textcolor{magenta}{to\_nat}}} & For 336 | \textbf{\texttt{\textcolor{magenta}{to\_nat}}} $i$: $$i$$ & \\ \hline \hline 337 | \multirow{4}{*}{\textbf{\texttt{\textcolor{teal}{Int32}}}, 338 | \textbf{\texttt{\textcolor{teal}{Uint32}}}} & 339 | \textbf{\texttt{\textcolor{magenta}{mul}}}, 340 | \textbf{\texttt{\textcolor{magenta}{div}}}, 341 | \textbf{\texttt{\textcolor{magenta}{rem}}} & 342 | $ 20 $ & \\ \cline{2-4} 343 | & \textbf{\texttt{\textcolor{magenta}{eq}}}, 344 | \textbf{\texttt{\textcolor{magenta}{lt}}}, 345 | \textbf{\texttt{\textcolor{magenta}{add}}}, 346 | \textbf{\texttt{\textcolor{magenta}{sub}}}, 347 | \textbf{\texttt{\textcolor{magenta}{to\_int32}}}, 348 | \textbf{\texttt{\textcolor{magenta}{to\_uint32}}}, 349 | \textbf{\texttt{\textcolor{magenta}{to\_int64}}}, 350 | \textbf{\texttt{\textcolor{magenta}{to\_uint64}}}, 351 | \textbf{\texttt{\textcolor{magenta}{to\_int128}}}, 352 | \textbf{\texttt{\textcolor{magenta}{to\_uint128}}}, 353 | \textbf{\texttt{\textcolor{magenta}{to\_int256}}}, 354 | \textbf{\texttt{\textcolor{magenta}{to\_uint256}}}, 355 | \textbf{\texttt{\textcolor{magenta}{to\_nat}}} 356 | & $4$ & \\ \hline \hline 357 | \multirow{4}{*}{\textbf{\texttt{\textcolor{teal}{Int64}}}, 358 | \textbf{\texttt{\textcolor{teal}{Uint64}}}} & 359 | \textbf{\texttt{\textcolor{magenta}{mul}}}, 360 | \textbf{\texttt{\textcolor{magenta}{div}}}, 361 | \textbf{\texttt{\textcolor{magenta}{rem}}} & 362 | $ 20 $ & \\ \cline{2-4} 363 | & 364 | \textbf{\texttt{\textcolor{magenta}{eq}}}, 365 | \textbf{\texttt{\textcolor{magenta}{lt}}}, 366 | \textbf{\texttt{\textcolor{magenta}{add}}}, 367 | \textbf{\texttt{\textcolor{magenta}{sub}}}, 368 | \textbf{\texttt{\textcolor{magenta}{to\_int32}}}, 369 | \textbf{\texttt{\textcolor{magenta}{to\_uint32}}}, 370 | \textbf{\texttt{\textcolor{magenta}{to\_int64}}}, 371 | \textbf{\texttt{\textcolor{magenta}{to\_uint64}}}, 372 | \textbf{\texttt{\textcolor{magenta}{to\_int128}}}, 373 | \textbf{\texttt{\textcolor{magenta}{to\_uint128}}}, 374 | \textbf{\texttt{\textcolor{magenta}{to\_int256}}}, 375 | \textbf{\texttt{\textcolor{magenta}{to\_uint256}}}, 376 | \textbf{\texttt{\textcolor{magenta}{to\_nat}}} 377 | & $4$ & \\ \hline \hline 378 | \multirow{4}{*}{\textbf{\texttt{\textcolor{teal}{Int128}}}, 379 | \textbf{\texttt{\textcolor{teal}{Uint128}}}} & 380 | \textbf{\texttt{\textcolor{magenta}{mul}}}, 381 | \textbf{\texttt{\textcolor{magenta}{div}}}, 382 | \textbf{\texttt{\textcolor{magenta}{rem}}} & 383 | $ 40 $ & \\ \cline{2-4} 384 | & 385 | \textbf{\texttt{\textcolor{magenta}{eq}}}, 386 | \textbf{\texttt{\textcolor{magenta}{lt}}}, 387 | \textbf{\texttt{\textcolor{magenta}{add}}}, 388 | \textbf{\texttt{\textcolor{magenta}{sub}}}, 389 | \textbf{\texttt{\textcolor{magenta}{to\_int32}}}, 390 | \textbf{\texttt{\textcolor{magenta}{to\_uint32}}}, 391 | \textbf{\texttt{\textcolor{magenta}{to\_int64}}}, 392 | \textbf{\texttt{\textcolor{magenta}{to\_uint64}}}, 393 | \textbf{\texttt{\textcolor{magenta}{to\_int128}}}, 394 | \textbf{\texttt{\textcolor{magenta}{to\_uint128}}}, 395 | \textbf{\texttt{\textcolor{magenta}{to\_int256}}}, 396 | \textbf{\texttt{\textcolor{magenta}{to\_uint256}}}, 397 | \textbf{\texttt{\textcolor{magenta}{to\_nat}}} 398 | & $8$ & \\ \hline \hline 399 | \multirow{4}{*}{\textbf{\texttt{\textcolor{teal}{Int256}}}, 400 | \textbf{\texttt{\textcolor{teal}{Uint256}}}} & 401 | \textbf{\texttt{\textcolor{magenta}{mul}}}, 402 | \textbf{\texttt{\textcolor{magenta}{div}}}, 403 | \textbf{\texttt{\textcolor{magenta}{rem}}} & 404 | $ 80 $ & \\ \cline{2-4} 405 | & 406 | \textbf{\texttt{\textcolor{magenta}{eq}}}, 407 | \textbf{\texttt{\textcolor{magenta}{lt}}}, 408 | \textbf{\texttt{\textcolor{magenta}{add}}}, 409 | \textbf{\texttt{\textcolor{magenta}{sub}}}, 410 | \textbf{\texttt{\textcolor{magenta}{to\_int32}}}, 411 | \textbf{\texttt{\textcolor{magenta}{to\_uint32}}}, 412 | \textbf{\texttt{\textcolor{magenta}{to\_int64}}}, 413 | \textbf{\texttt{\textcolor{magenta}{to\_uint64}}}, 414 | \textbf{\texttt{\textcolor{magenta}{to\_int128}}}, 415 | \textbf{\texttt{\textcolor{magenta}{to\_uint128}}}, 416 | \textbf{\texttt{\textcolor{magenta}{to\_int256}}}, 417 | \textbf{\texttt{\textcolor{magenta}{to\_uint256}}}, 418 | \textbf{\texttt{\textcolor{magenta}{to\_nat}}} 419 | & $ 16 $ & \\ \hline \hline 420 | \textbf{\texttt{\textcolor{teal}{BNum}}} & 421 | \textbf{\texttt{\textcolor{magenta}{eq}}}, 422 | \textbf{\texttt{\textcolor{magenta}{blt}}}, 423 | \textbf{\texttt{\textcolor{magenta}{badd}}} & $32$ & \\ \hline 424 | \end{tabularx} 425 | 426 | \section{Contract Deployment} 427 | 428 | In addition to the above gas, there is an upfront cost for contract deployment and 429 | transition invocations. The goal of these costs is to prevent spam attacks. 430 | 431 | At the time of contract deployment, an end user will provide two input files: 432 | one containing the contract (a \texttt{.scilla} file) and the other containing 433 | the value of the immutable parameters (a JSON file). 434 | 435 | The upfront gas consumed for contract deployment comes in two parts: 436 | 437 | \begin{itemize} 438 | \item An amount of gas equal to the size (in bytes) of the 439 | \textsc{Scilla} file plus that of the JSON file. 440 | \item An amount of gas based on the work required to typecheck the 441 | contract. This amount is charged because the size of a 442 | \textsc{Scilla} type may be exponential in the size of the contract 443 | code. 444 | \end{itemize} 445 | 446 | The gas charge for typechecking is calculated for each use of the 447 | \textbf{\texttt{\textcolor{teal}{TApp}}} operator (\texttt{@}), which applies a 448 | type function \texttt{\textbf{\textcolor{violet}{tfun}} $\alpha$ => e} 449 | to a type $\tau$, and thus replaces all (free) occurrences of $\alpha$ 450 | with $\tau$ in the type of \texttt{e}. 451 | 452 | Each type a type function \texttt{$\alpha$ => e} is applied to a type 453 | $\tau$, the following (recursively computed) gas is charged: 454 | 455 | \begin{table}[!hbt] 456 | \caption{Gas for typechecking of expressions. \label{tab:typechecking} } 457 | \centering 458 | \begin{tabular}{|p{3.5cm}|p{6.7cm}|p{5.5cm}|} 459 | \hline 460 | \textbf{Type of e} & \textbf{$typGas(\tau)$} & \textbf{Remarks} \\ \hline 461 | Primitive type ( 462 | \textbf{\texttt{\textcolor{teal}{String}}}, 463 | \textbf{\texttt{\textcolor{teal}{IntX}}}, 464 | \textbf{\texttt{\textcolor{teal}{BNum}}}, 465 | \textbf{\texttt{\textcolor{teal}{ByStrX}}}, 466 | \textbf{\texttt{\textcolor{teal}{Messsage}}}, 467 | etc.), 468 | & \multirow{4}{5cm}{ $1$ } & 469 | \\ \hline 470 | \textbf{\texttt{\textcolor{teal}{ADT n $\tau_1$\dots $\tau_k$}}} 471 | & 472 | $ 1 + \sum_{i \in 1...k} typGas(\tau_{i}) $ & 473 | An abstract datatype such as \textbf{\textcolor{magenta}{\texttt{Pair}}} 474 | may be parameterised with other types $\tau_1$\dots $\tau_k$. 475 | Substitution needs to happen inside those types as well. 476 | \\ \hline 477 | \textbf{\texttt{\textcolor{teal}{MapType~$\tau_{key}~\tau_{val}$}}} 478 | & 479 | $ 1 + typGas(\tau_{key}) + typGas(\tau_{val}) $ 480 | & 481 | \\ \hline 482 | \textbf{\texttt{\textcolor{teal}{FunType~$\tau_{arg}~\tau_{body}$}}} 483 | & 484 | $ 1 + typGas(\tau_{arg}) + typGas(\tau_{body}) $ 485 | & 486 | \\ \hline 487 | \textbf{\texttt{\textcolor{teal}{PolyFun~$\alpha'~\tau_{body}$}}} 488 | & 489 | $ 1 + typGas(\tau_{body})$ 490 | & 491 | The typechecker ensures that $\alpha'$ is different from 492 | $\alpha$, so that no unwanted variable capture takes place. 493 | \\ \hline 494 | \textbf{\texttt{\textcolor{teal}{TypeVar~$\alpha'$}}} 495 | & 496 | $$ 497 | \begin{cases} 498 | |\tau|, & \text{if } \alpha=\alpha', \\ 499 | $1$, & \text{else } \\ 500 | \end{cases} 501 | $$ 502 | & 503 | $|\tau|$ is the size of the type being substituted, which is 504 | calculated using the same table, except that the size of a 505 | \textbf{\texttt{\textcolor{teal}{TypeVar}}} is always $1$. 506 | \\ \hline 507 | \end{tabular} 508 | \end{table} 509 | 510 | \section{Transition Invocation} 511 | When a transition is invoked on a deployed contract by an end user (as 512 | a part of a transaction), the user supplies a JSON file containing 513 | information on which transition needs to be invoked and the parameters 514 | to pass. Gas associated with such transition calls is equal to the 515 | size of this JSON file. 516 | 517 | If the invocation causes the contract to invoke another transition (by 518 | sending a message either to itself or to another contract), this 519 | causes additional gas to be charged based on the size of the JSON file 520 | of the new message, as well as for the execution of the next 521 | transition. All gas expended through such chain calls will be charged 522 | to the end user who invoked the first transition in the chain. 523 | 524 | 525 | %\section{Importing Standard Libraries} 526 | 527 | 528 | 529 | \end{document} 530 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx-rtd-theme 2 | sphinxcontrib-spelling 3 | Sphinx>=5.0,<6.0 4 | --------------------------------------------------------------------------------