├── .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 | 
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 |
--------------------------------------------------------------------------------