Feature/mkdocs with versioning (#333)
* docs: restructuring & introduction of mkdocs with versioning provided by mike * docs: ad repositories section to sophia examples * docs: refactoring and consistent naming of æternity * docs: hint for new file destination * docs: revert capital letter * docs: accept proposed changes * docs: fix anchors in stdlib
This commit is contained in:
parent
3029bf31cb
commit
262452fb70
BIN
.docssite/docs/favicon.png
Normal file
BIN
.docssite/docs/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
7
.docssite/hook.py
Normal file
7
.docssite/hook.py
Normal file
@ -0,0 +1,7 @@
|
||||
import glob
|
||||
import shutil
|
||||
|
||||
def pre_build(**kwargs):
|
||||
for file in glob.glob('../docs/*.md'):
|
||||
shutil.copy(file, 'docs')
|
||||
shutil.copy('../CHANGELOG.md', 'docs')
|
55
.docssite/mkdocs.yml
Normal file
55
.docssite/mkdocs.yml
Normal file
@ -0,0 +1,55 @@
|
||||
site_name: æternity Sophia Language
|
||||
plugins:
|
||||
- search
|
||||
- mkdocs-simple-hooks:
|
||||
hooks:
|
||||
on_pre_build: 'hook:pre_build'
|
||||
repo_url: 'https://github.com/aeternity/aesophia'
|
||||
edit_uri: ''
|
||||
|
||||
extra:
|
||||
version:
|
||||
provider: mike
|
||||
|
||||
theme:
|
||||
favicon: favicon.png
|
||||
name: material
|
||||
custom_dir: overrides
|
||||
language: en
|
||||
palette:
|
||||
- scheme: default
|
||||
primary: pink
|
||||
accent: pink
|
||||
toggle:
|
||||
icon: material/weather-night
|
||||
name: Switch to dark mode
|
||||
- scheme: slate
|
||||
primary: pink
|
||||
accent: pink
|
||||
toggle:
|
||||
icon: material/weather-sunny
|
||||
name: Switch to light mode
|
||||
features:
|
||||
- content.tabs.link
|
||||
- search.highlight
|
||||
- search.share
|
||||
- search.suggest
|
||||
|
||||
# Don't include MkDocs' JavaScript
|
||||
include_search_page: false
|
||||
search_index_only: true
|
||||
|
||||
markdown_extensions:
|
||||
- admonition
|
||||
- pymdownx.highlight
|
||||
- pymdownx.superfences
|
||||
- toc:
|
||||
toc_depth: 3
|
||||
|
||||
nav:
|
||||
- Introduction: index.md
|
||||
- Syntax: sophia_syntax.md
|
||||
- Features: sophia_features.md
|
||||
- Standard library: sophia_stdlib.md
|
||||
- Contract examples: sophia_examples.md
|
||||
- Changelog: CHANGELOG.md
|
8
.docssite/overrides/main.html
Normal file
8
.docssite/overrides/main.html
Normal file
@ -0,0 +1,8 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block outdated %}
|
||||
You're not viewing the latest version.
|
||||
<a href="{{ '../' ~ base_url }}">
|
||||
<strong>Click here to go to latest.</strong>
|
||||
</a>
|
||||
{% endblock %}
|
25
.github/workflows/docs-develop.yml
vendored
Normal file
25
.github/workflows/docs-develop.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: Publish development docs
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip3
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/requirements.txt') }}
|
||||
- run: pip3 install -r .github/workflows/requirements.txt
|
||||
- run: git config --global user.email "github-action@users.noreply.github.com"
|
||||
- run: git config --global user.name "GitHub Action"
|
||||
- run: |
|
||||
cd .docssite
|
||||
mike deploy --push master
|
26
.github/workflows/docs-release.yml
vendored
Normal file
26
.github/workflows/docs-release.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
name: Publish release docs
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.cache/pip3
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('.github/workflows/requirements.txt') }}
|
||||
- run: pip3 install -r .github/workflows/requirements.txt
|
||||
- run: git config --global user.email "github-action@users.noreply.github.com"
|
||||
- run: git config --global user.name "GitHub Action"
|
||||
- run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
|
||||
- run: |
|
||||
cd .docssite
|
||||
mike deploy --push --update-aliases $RELEASE_VERSION latest
|
4
.github/workflows/requirements.txt
vendored
Normal file
4
.github/workflows/requirements.txt
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
mkdocs==1.2.1
|
||||
mkdocs-simple-hooks==0.1.3
|
||||
mkdocs-material==7.1.9
|
||||
mike==1.0.1
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -22,3 +22,5 @@ aesophia
|
||||
.qcci
|
||||
current_counterexample.eqc
|
||||
test/contracts/test.aes
|
||||
__pycache__
|
||||
.docssite/docs/*.md
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
ISC License
|
||||
|
||||
Copyright (c) 2017, aeternity developers
|
||||
Copyright (c) 2017, æternity developers
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
16
README.md
16
README.md
@ -5,13 +5,17 @@ This is the __sophia__ compiler for the æternity system which compiles contract
|
||||
The compiler is currently being used three places
|
||||
- [The command line compiler](https://github.com/aeternity/aesophia_cli)
|
||||
- [The HTTP compiler](https://github.com/aeternity/aesophia_http)
|
||||
- In [Aeternity node](https://github.com/aeternity/aeternity) tests
|
||||
- In [æternity node](https://github.com/aeternity/aeternity) tests
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Smart Contracts on aeternity Blockchain](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md).
|
||||
* [Sophia Documentation](docs/sophia.md).
|
||||
* [Sophia Standard Library](docs/sophia_stdlib.md).
|
||||
* [Introduction](docs/index.md)
|
||||
* [Syntax](docs/sophia_syntax.md)
|
||||
* [Features](docs/sophia_features.md)
|
||||
* [Standard library](docs/sophia_stdlib.md)
|
||||
* [Contract examples](docs/sophia_examples.md)
|
||||
|
||||
Additionally you can check out the [contracts section](https://github.com/aeternity/protocol/blob/master/contracts/contracts.md) of the æternity blockchain specification.
|
||||
|
||||
## Versioning
|
||||
|
||||
@ -26,5 +30,5 @@ Versioning should follow the [semantic versioning](https://semver.org/spec/v2.0.
|
||||
|
||||
The basic modules for interfacing the compiler:
|
||||
|
||||
* [aeso_compiler: the Sophia compiler](./docs/aeso_compiler.md)
|
||||
* [aeso_aci: the ACI interface](./docs/aeso_aci.md)
|
||||
* [aeso_compiler: the Sophia compiler](docs/aeso_compiler.md)
|
||||
* [aeso_aci: the ACI interface](docs/aeso_aci.md)
|
||||
|
12
docs/index.md
Normal file
12
docs/index.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Introduction
|
||||
Sophia is a functional language designed for smart contract development. It is strongly typed and has
|
||||
restricted mutable state.
|
||||
|
||||
Sophia is customized for smart contracts, which can be published
|
||||
to a blockchain. Thus some features of conventional
|
||||
languages, such as floating point arithmetic, are not present in Sophia, and
|
||||
some [æternity blockchain](https://aeternity.com) specific primitives, constructions and types have been added.
|
||||
|
||||
!!! Note
|
||||
- For rapid prototyping of smart contracts check out [AEstudio](https://studio.aepps.com/)!
|
||||
- For playing around and diving deeper into the language itself check out the [REPL](https://repl.aeternity.io/)!
|
1196
docs/sophia.md
1196
docs/sophia.md
File diff suppressed because it is too large
Load Diff
73
docs/sophia_examples.md
Normal file
73
docs/sophia_examples.md
Normal file
@ -0,0 +1,73 @@
|
||||
# Contract examples
|
||||
|
||||
## Crowdfunding
|
||||
```sophia
|
||||
/*
|
||||
* A simple crowd-funding example
|
||||
*/
|
||||
contract FundMe =
|
||||
|
||||
record spend_args = { recipient : address,
|
||||
amount : int }
|
||||
|
||||
record state = { contributions : map(address, int),
|
||||
total : int,
|
||||
beneficiary : address,
|
||||
deadline : int,
|
||||
goal : int }
|
||||
|
||||
stateful function spend(args : spend_args) =
|
||||
Chain.spend(args.recipient, args.amount)
|
||||
|
||||
entrypoint init(beneficiary, deadline, goal) : state =
|
||||
{ contributions = {},
|
||||
beneficiary = beneficiary,
|
||||
deadline = deadline,
|
||||
total = 0,
|
||||
goal = goal }
|
||||
|
||||
function is_contributor(addr) =
|
||||
Map.member(addr, state.contributions)
|
||||
|
||||
stateful entrypoint contribute() =
|
||||
if(Chain.block_height >= state.deadline)
|
||||
spend({ recipient = Call.caller, amount = Call.value }) // Refund money
|
||||
false
|
||||
else
|
||||
let amount =
|
||||
switch(Map.lookup(Call.caller, state.contributions))
|
||||
None => Call.value
|
||||
Some(n) => n + Call.value
|
||||
put(state{ contributions[Call.caller] = amount,
|
||||
total @ tot = tot + Call.value })
|
||||
true
|
||||
|
||||
stateful entrypoint withdraw() =
|
||||
if(Chain.block_height < state.deadline)
|
||||
abort("Cannot withdraw before deadline")
|
||||
if(Call.caller == state.beneficiary)
|
||||
withdraw_beneficiary()
|
||||
elif(is_contributor(Call.caller))
|
||||
withdraw_contributor()
|
||||
else
|
||||
abort("Not a contributor or beneficiary")
|
||||
|
||||
stateful function withdraw_beneficiary() =
|
||||
require(state.total >= state.goal, "Project was not funded")
|
||||
spend({recipient = state.beneficiary,
|
||||
amount = Contract.balance })
|
||||
|
||||
stateful function withdraw_contributor() =
|
||||
if(state.total >= state.goal)
|
||||
abort("Project was funded")
|
||||
let to = Call.caller
|
||||
spend({recipient = to,
|
||||
amount = state.contributions[to]})
|
||||
put(state{ contributions @ c = Map.delete(to, c) })
|
||||
```
|
||||
|
||||
## Repositories
|
||||
This is a list with repositories that include smart contracts written in Sophia:
|
||||
|
||||
- [aepp-sophia-examples](https://github.com/aeternity/aepp-sophia-examples)
|
||||
- A repository that contains lots of different examples. The functionality of these examples is - to some extent - also covered by tests written in JavaScript.
|
783
docs/sophia_features.md
Normal file
783
docs/sophia_features.md
Normal file
@ -0,0 +1,783 @@
|
||||
# Features
|
||||
## Contracts
|
||||
|
||||
The main unit of code in Sophia is the *contract*.
|
||||
|
||||
- A contract implementation, or simply a contract, is the code for a
|
||||
smart contract and consists of a list of types, entrypoints and local
|
||||
functions. Only the entrypoints can be called from outside the contract.
|
||||
- A contract instance is an entity living on the block chain (or in a state
|
||||
channel). Each instance has an address that can be used to call its
|
||||
entrypoints, either from another contract or in a call transaction.
|
||||
- A contract may define a type `state` encapsulating its local
|
||||
state. When creating a new contract the `init` entrypoint is executed and the
|
||||
state is initialized to its return value.
|
||||
|
||||
The language offers some primitive functions to interact with the blockchain and contracts.
|
||||
Please refer to the [Chain](sophia_stdlib.md#chain), [Contract](sophia_stdlib.md#contract)
|
||||
and the [Call](sophia_stdlib.md#call) namespaces in the documentation.
|
||||
|
||||
### Calling other contracts
|
||||
|
||||
To call a function in another contract you need the address to an instance of
|
||||
the contract. The type of the address must be a contract type, which consists
|
||||
of a number of type definitions and entrypoint declarations. For instance,
|
||||
|
||||
```sophia
|
||||
// A contract type
|
||||
contract interface VotingType =
|
||||
entrypoint vote : string => unit
|
||||
```
|
||||
|
||||
Now given contract address of type `VotingType` you can call the `vote`
|
||||
entrypoint of that contract:
|
||||
|
||||
```sophia
|
||||
contract VoteTwice =
|
||||
entrypoint voteTwice(v : VotingType, alt : string) =
|
||||
v.vote(alt)
|
||||
v.vote(alt)
|
||||
```
|
||||
|
||||
Contract calls take two optional named arguments `gas : int` and `value : int`
|
||||
that lets you set a gas limit and provide tokens to a contract call. If omitted
|
||||
the defaults are no gas limit and no tokens. Suppose there is a fee for voting:
|
||||
|
||||
```sophia
|
||||
entrypoint voteTwice(v : VotingType, fee : int, alt : string) =
|
||||
v.vote(value = fee, alt)
|
||||
v.vote(value = fee, alt)
|
||||
```
|
||||
|
||||
Named arguments can be given in any order.
|
||||
|
||||
Note that reentrant calls are not permitted. In other words, when calling
|
||||
another contract it cannot call you back (directly or indirectly).
|
||||
|
||||
To construct a value of a contract type you can give a contract address literal
|
||||
(for instance `ct_2gPXZnZdKU716QBUFKaT4VdBZituK93KLvHJB3n4EnbrHHw4Ay`), or
|
||||
convert an account address to a contract address using `Address.to_contract`.
|
||||
Note that if the contract does not exist, or it doesn't have the entrypoint, or
|
||||
the type of the entrypoint does not match the stated contract type, the call
|
||||
fails.
|
||||
|
||||
To recover the underlying `address` of a contract instance there is a field
|
||||
`address : address`. For instance, to send tokens to the voting contract (given that it is payable)
|
||||
without calling it you can write
|
||||
|
||||
```sophia
|
||||
entrypoint pay(v : VotingType, amount : int) =
|
||||
Chain.spend(v.address, amount)
|
||||
```
|
||||
|
||||
### Protected contract calls
|
||||
|
||||
If a contract call fails for any reason (for instance, the remote contract
|
||||
crashes or runs out of gas, or the entrypoint doesn't exist or has the wrong
|
||||
type) the parent call also fails. To make it possible to recover from failures,
|
||||
contract calls takes a named argument `protected : bool` (default `false`).
|
||||
|
||||
The protected argument must be a literal boolean, and when set to `true`
|
||||
changes the type of the contract call, wrapping the result in an `option` type.
|
||||
If the call fails the result is `None`, otherwise it's `Some(r)` where `r` is
|
||||
the return value of the call.
|
||||
|
||||
```sophia
|
||||
contract interface VotingType =
|
||||
entrypoint : vote : string => unit
|
||||
|
||||
contract Voter =
|
||||
entrypoint tryVote(v : VotingType, alt : string) =
|
||||
switch(v.vote(alt, protected = true) : option(unit))
|
||||
None => "Voting failed"
|
||||
Some(_) => "Voting successful"
|
||||
```
|
||||
|
||||
Any gas that was consumed by the contract call before the failure stays
|
||||
consumed, which means that in order to protect against the remote contract
|
||||
running out of gas it is necessary to set a gas limit using the `gas` argument.
|
||||
However, note that errors that would normally consume all the gas in the
|
||||
transaction still only uses up the gas spent running the contract.
|
||||
|
||||
|
||||
### Contract factories and child contracts
|
||||
|
||||
Since the version 6.0.0 Sophia supports deploying contracts by other
|
||||
contracts. This can be done in two ways:
|
||||
|
||||
- Contract cloning via [`Chain.clone`](sophia_stdlib.md#clone)
|
||||
- Direct deploy via [`Chain.create`](sophia_stdlib.md#create)
|
||||
|
||||
These functions take variable number of arguments that must match the created
|
||||
contract's `init` function. Beside that they take some additional named
|
||||
arguments – please refer to their documentation for the details.
|
||||
|
||||
While `Chain.clone` requires only a `contract interface` and a living instance
|
||||
of a given contract on the chain, `Chain.create` needs a full definition of a
|
||||
to-create contract defined by the standard `contract` syntax, for example
|
||||
|
||||
```sophia
|
||||
contract IntHolder =
|
||||
type state = int
|
||||
entrypoint init(x) = x
|
||||
entrypoint get() = state
|
||||
|
||||
main contract IntHolderFactory =
|
||||
stateful entrypoint new(x : int) : IntHolder =
|
||||
let ih = Chain.create(x) : IntHolder
|
||||
ih
|
||||
```
|
||||
|
||||
In case of a presence of child contracts (`IntHolder` in this case), the main
|
||||
contract must be pointed out with the `main` keyword as shown in the example.
|
||||
|
||||
|
||||
## Mutable state
|
||||
|
||||
Sophia does not have arbitrary mutable state, but only a limited form of state
|
||||
associated with each contract instance.
|
||||
|
||||
- Each contract defines a type `state` encapsulating its mutable state.
|
||||
The type `state` defaults to the `unit`.
|
||||
- The initial state of a contract is computed by the contract's `init`
|
||||
function. The `init` function is *pure* and returns the initial state as its
|
||||
return value.
|
||||
If the type `state` is `unit`, the `init` function defaults to returning the value `()`.
|
||||
At contract creation time, the `init` function is executed and
|
||||
its result is stored as the contract state.
|
||||
- The value of the state is accessible from inside the contract
|
||||
through an implicitly bound variable `state`.
|
||||
- State updates are performed by calling a function `put : state => unit`.
|
||||
- Aside from the `put` function (and similar functions for transactions
|
||||
and events), the language is purely functional.
|
||||
- Functions modifying the state need to be annotated with the `stateful` keyword (see below).
|
||||
|
||||
To make it convenient to update parts of a deeply nested state Sophia
|
||||
provides special syntax for map/record updates.
|
||||
|
||||
### Stateful functions
|
||||
|
||||
Top-level functions and entrypoints must be annotated with the
|
||||
`stateful` keyword to be allowed to affect the state of the running contract.
|
||||
For instance,
|
||||
|
||||
```sophia
|
||||
stateful entrypoint set_state(s : state) =
|
||||
put(s)
|
||||
```
|
||||
|
||||
Without the `stateful` annotation the compiler does not allow the call to
|
||||
`put`. A `stateful` annotation is required to
|
||||
|
||||
* Use a stateful primitive function. These are
|
||||
- `put`
|
||||
- `Chain.spend`
|
||||
- `Oracle.register`
|
||||
- `Oracle.query`
|
||||
- `Oracle.respond`
|
||||
- `Oracle.extend`
|
||||
- `AENS.preclaim`
|
||||
- `AENS.claim`
|
||||
- `AENS.transfer`
|
||||
- `AENS.revoke`
|
||||
- `AENS.update`
|
||||
* Call a `stateful` function in the current contract
|
||||
* Call another contract with a non-zero `value` argument.
|
||||
|
||||
A `stateful` annotation *is not* required to
|
||||
|
||||
* Read the contract state.
|
||||
* Issue an event using the `event` function.
|
||||
* Call another contract with `value = 0`, even if the called function is stateful.
|
||||
|
||||
## Payable
|
||||
|
||||
### Payable contracts
|
||||
|
||||
A concrete contract is by default *not* payable. Any attempt at spending to such
|
||||
a contract (either a `Chain.spend` or a normal spend transaction) will fail. If a
|
||||
contract shall be able to receive funds in this way it has to be declared `payable`:
|
||||
|
||||
```sophia
|
||||
// A payable contract
|
||||
payable contract ExampleContract =
|
||||
stateful entrypoint do_stuff() = ...
|
||||
```
|
||||
|
||||
If in doubt, it is possible to check if an address is payable using
|
||||
`Address.is_payable(addr)`.
|
||||
|
||||
### Payable entrypoints
|
||||
|
||||
A contract entrypoint is by default *not* payable. Any call to such a function
|
||||
(either a [Remote call](#calling-other-contracts) or a contract call transaction)
|
||||
that has a non-zero `value` will fail. Contract entrypoints that should be called
|
||||
with a non-zero value should be declared `payable`.
|
||||
|
||||
```sophia
|
||||
payable stateful entrypoint buy(to : address) =
|
||||
if(Call.value > 42)
|
||||
transfer_item(to)
|
||||
else
|
||||
abort("Value too low")
|
||||
```
|
||||
|
||||
Note: In the æternity VM (AEVM) contracts and entrypoints were by default
|
||||
payable until the Lima release.
|
||||
|
||||
## Namespaces
|
||||
|
||||
Code can be split into libraries using the `namespace` construct. Namespaces
|
||||
can appear at the top-level and can contain type and function definitions, but
|
||||
not entrypoints. Outside the namespace you can refer to the (non-private) names
|
||||
by qualifying them with the namespace (`Namespace.name`).
|
||||
For example,
|
||||
|
||||
```sophia
|
||||
namespace Library =
|
||||
type number = int
|
||||
function inc(x : number) : number = x + 1
|
||||
|
||||
contract MyContract =
|
||||
entrypoint plus2(x) : Library.number =
|
||||
Library.inc(Library.inc(x))
|
||||
```
|
||||
|
||||
Functions in namespaces have access to the same environment (including the
|
||||
`Chain`, `Call`, and `Contract`, builtin namespaces) as function in a contract,
|
||||
with the exception of `state`, `put` and `Chain.event` since these are
|
||||
dependent on the specific state and event types of the contract.
|
||||
|
||||
## Splitting code over multiple files
|
||||
|
||||
Code from another file can be included in a contract using an `include`
|
||||
statement. These must appear at the top-level (outside the main contract). The
|
||||
included file can contain one or more namespaces and abstract contracts. For
|
||||
example, if the file `library.aes` contains
|
||||
|
||||
```sophia
|
||||
namespace Library =
|
||||
function inc(x) = x + 1
|
||||
```
|
||||
|
||||
you can use it from another file using an `include`:
|
||||
|
||||
```sophia
|
||||
include "library.aes"
|
||||
contract MyContract =
|
||||
entrypoint plus2(x) = Library.inc(Library.inc(x))
|
||||
```
|
||||
|
||||
This behaves as if the contents of `library.aes` was textually inserted into
|
||||
the file, except that error messages will refer to the original source
|
||||
locations. The language will try to include each file at most one time automatically,
|
||||
so even cyclic includes should be working without any special tinkering.
|
||||
|
||||
## Standard library
|
||||
|
||||
Sophia offers [standard library](sophia_stdlib.md) which exposes some
|
||||
primitive operations and some higher level utilities. The builtin
|
||||
namespaces like `Chain`, `Contract`, `Map`
|
||||
are included by default and are supported internally by the compiler.
|
||||
Others like `List`, `Frac`, `Option` need to be manually included using the
|
||||
`include` directive. For example
|
||||
```sophia
|
||||
include "List.aes"
|
||||
include "Pair.aes"
|
||||
-- Map is already there!
|
||||
|
||||
namespace C =
|
||||
entrypoint keys(m : map('a, 'b)) : list('a) =
|
||||
List.map(Pair.fst, (Map.to_list(m)))
|
||||
```
|
||||
|
||||
## Types
|
||||
Sophia has the following types:
|
||||
|
||||
| Type | Description | Example |
|
||||
|----------------------|---------------------------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| int | A 2-complement integer | ```-1``` |
|
||||
| address | æternity address, 32 bytes | ```Call.origin``` |
|
||||
| bool | A Boolean | ```true``` |
|
||||
| bits | A bit field | ```Bits.none``` |
|
||||
| bytes(n) | A byte array with `n` bytes | ```#fedcba9876543210``` |
|
||||
| string | An array of bytes | ```"Foo"``` |
|
||||
| list | A homogeneous immutable singly linked list. | ```[1, 2, 3]``` |
|
||||
| ('a, 'b) => 'c | A function. Parentheses can be skipped if there is only one argument | ```(x : int, y : int) => x + y``` |
|
||||
| tuple | An ordered heterogeneous array | ```(42, "Foo", true)``` |
|
||||
| record | An immutable key value store with fixed key names and typed values | ``` record balance = { owner: address, value: int } ``` |
|
||||
| map | An immutable key value store with dynamic mapping of keys of one type to values of one type | ```type accounts = map(string, address)``` |
|
||||
| option('a) | An optional value either None or Some('a) | ```Some(42)``` |
|
||||
| state | A user defined type holding the contract state | ```record state = { owner: address, magic_key: bytes(4) }``` |
|
||||
| event | An append only list of blockchain events (or log entries) | ```datatype event = EventX(indexed int, string)``` |
|
||||
| hash | A 32-byte hash - equivalent to `bytes(32)` | |
|
||||
| signature | A signature - equivalent to `bytes(64)` | |
|
||||
| Chain.ttl | Time-to-live (fixed height or relative to current block) | ```FixedTTL(1050)``` ```RelativeTTL(50)``` |
|
||||
| oracle('a, 'b) | And oracle answering questions of type 'a with answers of type 'b | ```Oracle.register(acct, qfee, ttl)``` |
|
||||
| oracle_query('a, 'b) | A specific oracle query | ```Oracle.query(o, q, qfee, qttl, rttl)``` |
|
||||
| contract | A user defined, typed, contract address | ```function call_remote(r : RemoteContract) = r.fun()``` |
|
||||
|
||||
## Literals
|
||||
| Type | Constant/Literal example(s) |
|
||||
| ---------- | ------------------------------- |
|
||||
| int | `-1`, `2425`, `4598275923475723498573485768` |
|
||||
| address | `ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt` |
|
||||
| bool | `true`, `false` |
|
||||
| bits | `Bits.none`, `Bits.all` |
|
||||
| bytes(8) | `#fedcba9876543210` |
|
||||
| string | `"This is a string"` |
|
||||
| list | `[1, 2, 3]`, `[(true, 24), (false, 19), (false, -42)]` |
|
||||
| tuple | `(42, "Foo", true)` |
|
||||
| record | `{ owner = Call.origin, value = 100000000 }` |
|
||||
| map | `{["foo"] = 19, ["bar"] = 42}`, `{}` |
|
||||
| option(int) | `Some(42)`, `None` |
|
||||
| state | `state{ owner = Call.origin, magic_key = #a298105f }` |
|
||||
| event | `EventX(0, "Hello")` |
|
||||
| hash | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||
| signature | `#000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f` |
|
||||
| Chain.ttl | `FixedTTL(1050)`, `RelativeTTL(50)` |
|
||||
| oracle('a, 'b) | `ok_2YNyxd6TRJPNrTcEDCe9ra59SVUdp9FR9qWC5msKZWYD9bP9z5` |
|
||||
| oracle_query('a, 'b) | `oq_2oRvyowJuJnEkxy58Ckkw77XfWJrmRgmGaLzhdqb67SKEL1gPY` |
|
||||
| contract | `ct_Ez6MyeTMm17YnTnDdHTSrzMEBKmy7Uz2sXu347bTDPgVH2ifJ` |
|
||||
|
||||
## Arithmetic
|
||||
|
||||
Sophia integers (`int`) are represented by 256-bit (AEVM) or arbitrary-sized (FATE) signed words and supports the following
|
||||
arithmetic operations:
|
||||
- addition (`x + y`)
|
||||
- subtraction (`x - y`)
|
||||
- multiplication (`x * y`)
|
||||
- division (`x / y`), truncated towards zero
|
||||
- remainder (`x mod y`), satisfying `y * (x / y) + x mod y == x` for non-zero `y`
|
||||
- exponentiation (`x ^ y`)
|
||||
|
||||
All operations are *safe* with respect to overflow and underflow. On AEVM they behave as the corresponding
|
||||
operations on arbitrary-size integers and fail with `arithmetic_error` if the
|
||||
result cannot be represented by a 256-bit signed word. For example, `2 ^ 255`
|
||||
fails rather than wrapping around to -2²⁵⁵.
|
||||
|
||||
The division and modulo operations also throw an arithmetic error if the
|
||||
second argument is zero.
|
||||
|
||||
## Bit fields
|
||||
|
||||
Sophia integers do not support bit arithmetic. Instead there is a separate
|
||||
type `bits`. See the standard library [documentation](sophia_stdlib.md#bits).
|
||||
|
||||
On the AEVM a bit field is represented by a 256-bit word and reading or writing
|
||||
a bit outside the 0..255 range fails with an `arithmetic_error`. On FATE a bit
|
||||
field can be of arbitrary size (but it is still represented by the
|
||||
corresponding integer, so setting very high bits can be expensive).
|
||||
|
||||
## Type aliases
|
||||
|
||||
Type aliases can be introduced with the `type` keyword and can be
|
||||
parameterized. For instance
|
||||
|
||||
```sophia
|
||||
type number = int
|
||||
type string_map('a) = map(string, 'a)
|
||||
```
|
||||
|
||||
A type alias and its definition can be used interchangeably. Sophia does not support
|
||||
higher-kinded types, meaning that following type alias is invalid: `type wrap('f, 'a) = 'f('a)`
|
||||
|
||||
## Algebraic data types
|
||||
|
||||
Sophia supports algebraic data types (variant types) and pattern matching. Data
|
||||
types are declared by giving a list of constructors with
|
||||
their respective arguments. For instance,
|
||||
|
||||
```sophia
|
||||
datatype one_or_both('a, 'b) = Left('a) | Right('b) | Both('a, 'b)
|
||||
```
|
||||
|
||||
Elements of data types can be pattern matched against, using the `switch` construct:
|
||||
|
||||
```sophia
|
||||
function get_left(x : one_or_both('a, 'b)) : option('a) =
|
||||
switch(x)
|
||||
Left(x) => Some(x)
|
||||
Right(_) => None
|
||||
Both(x, _) => Some(x)
|
||||
```
|
||||
|
||||
or directly in the left-hand side:
|
||||
```sophia
|
||||
function
|
||||
get_left : one_or_both('a, 'b) => option('a)
|
||||
get_left(Left(x)) = Some(x)
|
||||
get_left(Right(_)) = None
|
||||
get_left(Both(x, _)) = Some(x)
|
||||
```
|
||||
|
||||
*NOTE: Data types cannot currently be recursive.*
|
||||
|
||||
## Lists
|
||||
|
||||
A Sophia list is a dynamically sized, homogenous, immutable, singly
|
||||
linked list. A list is constructed with the syntax `[1, 2, 3]`. The
|
||||
elements of a list can be any of datatype but they must have the same
|
||||
type. The type of lists with elements of type `'e` is written
|
||||
`list('e)`. For example we can have the following lists:
|
||||
|
||||
```sophia
|
||||
[1, 33, 2, 666] : list(int)
|
||||
[(1, "aaa"), (10, "jjj"), (666, "the beast")] : list(int * string)
|
||||
[{[1] = "aaa", [10] = "jjj"}, {[5] = "eee", [666] = "the beast"}] : list(map(int, string))
|
||||
```
|
||||
|
||||
New elements can be prepended to the front of a list with the `::`
|
||||
operator. So `42 :: [1, 2, 3]` returns the list `[42, 1, 2, 3]`. The
|
||||
concatenation operator `++` appends its second argument to its first
|
||||
and returns the resulting list. So concatenating two lists
|
||||
`[1, 22, 33] ++ [10, 18, 55]` returns the list `[1, 22, 33, 10, 18, 55]`.
|
||||
|
||||
Sophia supports list comprehensions known from languages like Python, Haskell or Erlang.
|
||||
Example syntax:
|
||||
```sophia
|
||||
[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
|
||||
// yields [12,13,14,20,21,22,30,31,32]
|
||||
```
|
||||
|
||||
Lists can be constructed using the range syntax using special `..` operator:
|
||||
```sophia
|
||||
[1..4] == [1,2,3,4]
|
||||
```
|
||||
The ranges are always ascending and have step equal to 1.
|
||||
|
||||
|
||||
Please refer to the [standard library](sophia_stdlib.md#list) for the predefined functionalities.
|
||||
|
||||
## Maps and records
|
||||
|
||||
A Sophia record type is given by a fixed set of fields with associated,
|
||||
possibly different, types. For instance
|
||||
```sophia
|
||||
record account = { name : string,
|
||||
balance : int,
|
||||
history : list(transaction) }
|
||||
```
|
||||
|
||||
Maps, on the other hand, can contain an arbitrary number of key-value bindings,
|
||||
but of a fixed type. The type of maps with keys of type `'k` and values of type
|
||||
`'v` is written `map('k, 'v)`. The key type can be any type that does not
|
||||
contain a map or a function type.
|
||||
|
||||
Please refer to the [standard library](sophia_stdlib.md#map) for the predefined functionalities.
|
||||
|
||||
### Constructing maps and records
|
||||
|
||||
A value of record type is constructed by giving a value for each of the fields.
|
||||
For the example above,
|
||||
```sophia
|
||||
function new_account(name) =
|
||||
{name = name, balance = 0, history = []}
|
||||
```
|
||||
Maps are constructed similarly, with keys enclosed in square brackets
|
||||
```sophia
|
||||
function example_map() : map(string, int) =
|
||||
{["key1"] = 1, ["key2"] = 2}
|
||||
```
|
||||
The empty map is written `{}`.
|
||||
|
||||
### Accessing values
|
||||
|
||||
Record fields access is written `r.f` and map lookup `m[k]`. For instance,
|
||||
```sophia
|
||||
function get_balance(a : address, accounts : map(address, account)) =
|
||||
accounts[a].balance
|
||||
```
|
||||
Looking up a non-existing key in a map results in contract execution failing. A
|
||||
default value to return for non-existing keys can be provided using the syntax
|
||||
`m[k = default]`. See also `Map.member` and `Map.lookup` below.
|
||||
|
||||
### Updating a value
|
||||
|
||||
Record field updates are written `r{f = v}`. This creates a new record value
|
||||
which is the same as `r`, but with the value of the field `f` replaced by `v`.
|
||||
Similarly, `m{[k] = v}` constructs a map with the same values as `m` except
|
||||
that `k` maps to `v`. It makes no difference if `m` has a mapping for `k` or
|
||||
not.
|
||||
|
||||
It is possible to give a name to the old value of a field or mapping in an
|
||||
update: instead of `acc{ balance = acc.balance + 100 }` it is possible to write
|
||||
`acc{ balance @ b = b + 100 }`, binding `b` to `acc.balance`. When giving a
|
||||
name to a map value (`m{ [k] @ x = v }`), the corresponding key must be present
|
||||
in the map or execution fails, but a default value can be provided:
|
||||
`m{ [k = default] @ x = v }`. In this case `x` is bound to `default` if
|
||||
`k` is not in the map.
|
||||
|
||||
Updates can be nested:
|
||||
```sophia
|
||||
function clear_history(a : address, accounts : map(address, account)) : map(address, account) =
|
||||
accounts{ [a].history = [] }
|
||||
```
|
||||
This is equivalent to `accounts{ [a] @ acc = acc{ history = [] } }` and thus
|
||||
requires `a` to be present in the accounts map. To have `clear_history` create
|
||||
an account if `a` is not in the map you can write (given a function `empty_account`):
|
||||
```sophia
|
||||
accounts{ [a = empty_account()].history = [] }
|
||||
```
|
||||
|
||||
### Map implementation
|
||||
|
||||
Internally in the VM maps are implemented as hash maps and support fast lookup
|
||||
and update. Large maps can be stored in the contract state and the size of the
|
||||
map does not contribute to the gas costs of a contract call reading or updating
|
||||
it.
|
||||
|
||||
## Strings
|
||||
|
||||
There is a builtin type `string`, which can be seen as an array of bytes.
|
||||
Strings can be compared for equality (`==`, `!=`), used as keys in maps and
|
||||
records, and used in builtin functions `String.length`, `String.concat` and
|
||||
the hash functions described below.
|
||||
|
||||
Please refer to the `String` [library documentation](sophia_stdlib.md#string).
|
||||
|
||||
## Chars
|
||||
|
||||
There is a builtin type `char` (the underlying representation being an integer),
|
||||
mainly used to manipulate strings via `String.to_list`/`String.from_list`.
|
||||
|
||||
Characters can also be introduced as character literals (`'x', '+', ...).
|
||||
|
||||
Please refer to the `Char` [library documentation](sophia_stdlib.md#char).
|
||||
|
||||
## Byte arrays
|
||||
|
||||
Byte arrays are fixed size arrays of 8-bit integers. They are described in hexadecimal system,
|
||||
for example the literal `#cafe` creates a two-element array of bytes `ca` (202) and `fe` (254)
|
||||
and thus is a value of type `bytes(2)`.
|
||||
|
||||
Please refer to the `Bytes` [library documentation](sophia_stdlib.md#bytes).
|
||||
|
||||
## Cryptographic builtins
|
||||
|
||||
Libraries [Crypto](sophia_stdlib.md#crypto) and [String](sophia_stdlib.md#string) provide functions to
|
||||
hash objects, verify signatures etc. The `hash` is a type alias for `bytes(32)`.
|
||||
|
||||
## Authorization interface
|
||||
|
||||
When a Generalized account is authorized, the authorization function needs
|
||||
access to the transaction and the transaction hash for the wrapped transaction. (A `GAMetaTx`
|
||||
wrapping a transaction.) The transaction and the transaction hash is available in the primitive
|
||||
`Auth.tx` and `Auth.tx_hash` respectively, they are *only* available during authentication if invoked by a
|
||||
normal contract call they return `None`.
|
||||
|
||||
## Oracle interface
|
||||
You can attach an oracle to the current contract and you can interact with oracles
|
||||
through the Oracle interface.
|
||||
|
||||
For a full description of how Oracle works see
|
||||
[Oracles](https://github.com/aeternity/protocol/blob/master/oracles/oracles.md#oracles).
|
||||
For a functionality documentation refer to the [standard library](sophia_stdlib.md#oracle).
|
||||
|
||||
### Example
|
||||
|
||||
Example for an oracle answering questions of type `string` with answers of type `int`:
|
||||
```sophia
|
||||
contract Oracles =
|
||||
|
||||
stateful entrypoint registerOracle(acct : address,
|
||||
sign : signature, // Signed network id + oracle address + contract address
|
||||
qfee : int,
|
||||
ttl : Chain.ttl) : oracle(string, int) =
|
||||
Oracle.register(acct, signature = sign, qfee, ttl)
|
||||
|
||||
entrypoint queryFee(o : oracle(string, int)) : int =
|
||||
Oracle.query_fee(o)
|
||||
|
||||
payable stateful entrypoint createQuery(o : oracle_query(string, int),
|
||||
q : string,
|
||||
qfee : int,
|
||||
qttl : Chain.ttl,
|
||||
rttl : int) : oracle_query(string, int) =
|
||||
require(qfee =< Call.value, "insufficient value for qfee")
|
||||
Oracle.query(o, q, qfee, qttl, RelativeTTL(rttl))
|
||||
|
||||
stateful entrypoint extendOracle(o : oracle(string, int),
|
||||
ttl : Chain.ttl) : unit =
|
||||
Oracle.extend(o, ttl)
|
||||
|
||||
stateful entrypoint signExtendOracle(o : oracle(string, int),
|
||||
sign : signature, // Signed network id + oracle address + contract address
|
||||
ttl : Chain.ttl) : unit =
|
||||
Oracle.extend(o, signature = sign, ttl)
|
||||
|
||||
stateful entrypoint respond(o : oracle(string, int),
|
||||
q : oracle_query(string, int),
|
||||
sign : signature, // Signed network id + oracle query id + contract address
|
||||
r : int) =
|
||||
Oracle.respond(o, q, signature = sign, r)
|
||||
|
||||
entrypoint getQuestion(o : oracle(string, int),
|
||||
q : oracle_query(string, int)) : string =
|
||||
Oracle.get_question(o, q)
|
||||
|
||||
entrypoint hasAnswer(o : oracle(string, int),
|
||||
q : oracle_query(string, int)) =
|
||||
switch(Oracle.get_answer(o, q))
|
||||
None => false
|
||||
Some(_) => true
|
||||
|
||||
entrypoint getAnswer(o : oracle(string, int),
|
||||
q : oracle_query(string, int)) : option(int) =
|
||||
Oracle.get_answer(o, q)
|
||||
```
|
||||
|
||||
### Sanity checks
|
||||
|
||||
When an Oracle literal is passed to a contract, no deep checks are performed.
|
||||
For extra safety [Oracle.check](sophia_stdlib.md#check) and [Oracle.check_query](sophia_stdlib.md#check_query)
|
||||
functions are provided.
|
||||
|
||||
## AENS interface
|
||||
|
||||
Contracts can interact with the
|
||||
[æternity naming system](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
||||
For this purpose the [AENS](sophia_stdlib.md#aens) library was exposed.
|
||||
|
||||
### Example
|
||||
|
||||
In this example we assume that the name `name` already exists, and is owned by
|
||||
an account with address `addr`. In order to allow a contract `ct` to handle
|
||||
`name` the account holder needs to create a
|
||||
[signature](#delegation-signature) `sig` of `addr | name.hash | ct.address`.
|
||||
|
||||
Armed with this information we can for example write a function that extends
|
||||
the name if it expires within 1000 blocks:
|
||||
```sophia
|
||||
stateful entrypoint extend_if_necessary(addr : address, name : string, sig : signature) =
|
||||
switch(AENS.lookup(name))
|
||||
None => ()
|
||||
Some(AENS.Name(_, FixedTTL(expiry), _)) =>
|
||||
if(Chain.block_height + 1000 > expiry)
|
||||
AENS.update(addr, name, Some(RelativeTTL(50000)), None, None, signature = sig)
|
||||
```
|
||||
|
||||
And we can write functions that adds and removes keys from the pointers of the
|
||||
name:
|
||||
```sophia
|
||||
stateful entrypoint add_key(addr : address, name : string, key : string,
|
||||
pt : AENS.pointee, sig : signature) =
|
||||
switch(AENS.lookup(name))
|
||||
None => ()
|
||||
Some(AENS.Name(_, _, ptrs)) =>
|
||||
AENS.update(addr, name, None, None, Some(ptrs{[key] = pt}), signature = sig)
|
||||
|
||||
stateful entrypoint delete_key(addr : address, name : string,
|
||||
key : string, sig : signature) =
|
||||
switch(AENS.lookup(name))
|
||||
None => ()
|
||||
Some(AENS.Name(_, _, ptrs)) =>
|
||||
let ptrs = Map.delete(key, ptrs)
|
||||
AENS.update(addr, name, None, None, Some(ptrs), signature = sig)
|
||||
```
|
||||
|
||||
*Note:* From the Iris hardfork more strict rules apply for AENS pointers, when
|
||||
a Sophia contract lookup or update (bad) legacy pointers, the bad keys are
|
||||
automatically removed so they will not appear in the pointers map.
|
||||
|
||||
## Events
|
||||
|
||||
Sophia contracts log structured messages to an event log in the resulting
|
||||
blockchain transaction. The event log is quite similar to [Events in
|
||||
Solidity](https://solidity.readthedocs.io/en/v0.4.24/contracts.html#events).
|
||||
Events are further discussed in the [protocol](https://github.com/aeternity/protocol/blob/master/contracts/events.md).
|
||||
|
||||
|
||||
To use events a contract must declare a datatype `event`, and events are then
|
||||
logged using the `Chain.event` function:
|
||||
|
||||
```sophia
|
||||
datatype event
|
||||
= Event1(int, int, string)
|
||||
| Event2(string, address)
|
||||
|
||||
Chain.event(e : event) : unit
|
||||
```
|
||||
|
||||
The event can have 0-3 *indexed* fields, and an optional *payload* field. A
|
||||
field is indexed if it fits in a 32-byte word, i.e.
|
||||
- `bool`
|
||||
- `int`
|
||||
- `bits`
|
||||
- `address`
|
||||
- `oracle(_, _)`
|
||||
- `oracle_query(_, _)`
|
||||
- contract types
|
||||
- `bytes(n)` for `n` ≤ 32, in particular `hash`
|
||||
|
||||
The payload field must be either a string or a byte array of more than 32 bytes.
|
||||
The fields can appear in any order.
|
||||
|
||||
*NOTE:* Indexing is not part of the core æternity node.
|
||||
|
||||
Events are emitted by using the `Chain.event` function. The following function
|
||||
will emit one Event of each kind in the example.
|
||||
|
||||
```sophia
|
||||
entrypoint emit_events() : () =
|
||||
Chain.event(Event1(42, 34, "foo"))
|
||||
Chain.event(Event2("This is not indexed", Contract.address))
|
||||
```
|
||||
|
||||
### Argument order
|
||||
|
||||
It is only possible to have one (1) `string` parameter in the event, but it can
|
||||
be placed in any position (and its value will end up in the `data` field), i.e.
|
||||
```sophia
|
||||
AnotherEvent(string, indexed address)
|
||||
|
||||
...
|
||||
|
||||
Chain.event(AnotherEvent("This is not indexed", Contract.address))
|
||||
```
|
||||
would yield exactly the same result in the example above!
|
||||
|
||||
## Compiler pragmas
|
||||
|
||||
To enforce that a contract is only compiled with specific versions of the
|
||||
Sophia compiler, you can give one or more `@compiler` pragmas at the
|
||||
top-level (typically at the beginning) of a file. For instance, to enforce that
|
||||
a contract is compiled with version 4.3 of the compiler you write
|
||||
|
||||
```sophia
|
||||
@compiler >= 4.3
|
||||
@compiler < 4.4
|
||||
```
|
||||
|
||||
Valid operators in compiler pragmas are `<`, `=<`, `==`, `>=`, and `>`. Version
|
||||
numbers are given as a sequence of non-negative integers separated by dots.
|
||||
Trailing zeros are ignored, so `4.0.0 == 4`. If a constraint is violated an
|
||||
error is reported and compilation fails.
|
||||
|
||||
## Exceptions
|
||||
|
||||
Contracts can fail with an (uncatchable) exception using the built-in function
|
||||
|
||||
```sophia
|
||||
abort(reason : string) : 'a
|
||||
```
|
||||
|
||||
Calling abort causes the top-level call transaction to return an error result
|
||||
containing the `reason` string. Only the gas used up to and including the abort
|
||||
call is charged. This is different from termination due to a crash which
|
||||
consumes all available gas.
|
||||
|
||||
For convenience the following function is also built-in:
|
||||
|
||||
```sophia
|
||||
function require(b : bool, err : string) =
|
||||
if(!b) abort(err)
|
||||
```
|
||||
|
||||
## Delegation signature
|
||||
|
||||
Some chain operations (`Oracle.<operation>` and `AENS.<operation>`) have an
|
||||
optional delegation signature. This is typically used when a user/accounts
|
||||
would like to allow a contract to act on it's behalf. The exact data to be
|
||||
signed varies for the different operations, but in all cases you should prepend
|
||||
the signature data with the `network_id` (`ae_mainnet` for the æternity mainnet, etc.).
|
@ -12,40 +12,40 @@ in the scope and do not need any actions to be used, while the others require so
|
||||
|
||||
The out-of-the-box namespaces are:
|
||||
|
||||
- [Bits](#Bits)
|
||||
- [Bytes](#Bytes)
|
||||
- [Char](#Char)
|
||||
- [Int](#Int)
|
||||
- [Map](#Map)
|
||||
- [Address](#Address)
|
||||
- [Crypto](#Crypto)
|
||||
- [Auth](#Auth)
|
||||
- [Oracle](#Oracle)
|
||||
- [AENS](#AENS)
|
||||
- [Contract](#Contract)
|
||||
- [Call](#Call)
|
||||
- [Chain](#Chain)
|
||||
- [Bits](#bits)
|
||||
- [Bytes](#bytes)
|
||||
- [Char](#char)
|
||||
- [Int](#int)
|
||||
- [Map](#map)
|
||||
- [Address](#address)
|
||||
- [Crypto](#crypto)
|
||||
- [Auth](#auth)
|
||||
- [Oracle](#oracle)
|
||||
- [AENS](#aens)
|
||||
- [Contract](#contract)
|
||||
- [Call](#call)
|
||||
- [Chain](#chain)
|
||||
|
||||
The following ones need to be included as regular files with `.aes` suffix, for example
|
||||
```
|
||||
include "List.aes"
|
||||
```
|
||||
|
||||
- [List](#List)
|
||||
- [Option](#Option)
|
||||
- [String](#String)
|
||||
- [Func](#Func)
|
||||
- [Pair](#Pair)
|
||||
- [Triple](#Triple)
|
||||
- [BLS12_381](#BLS12_381)
|
||||
- [Frac](#Frac)
|
||||
- [List](#list)
|
||||
- [Option](#option)
|
||||
- [String](#string)
|
||||
- [Func](#func)
|
||||
- [Pair](#pair)
|
||||
- [Triple](#triple)
|
||||
- [BLS12_381](bls12_381)
|
||||
- [Frac](#frac)
|
||||
- [Set](#set-stdlib)
|
||||
|
||||
# Builtin namespaces
|
||||
## Builtin namespaces
|
||||
|
||||
They are available without any explicit includes.
|
||||
|
||||
## Bits
|
||||
### Bits
|
||||
|
||||
#### none
|
||||
```
|
||||
@ -119,7 +119,7 @@ Bits.difference(a : bits, b : bits) : bits
|
||||
Each bit is true if and only if it was 1 in `a` and 0 in `b`
|
||||
|
||||
|
||||
## Bytes
|
||||
### Bytes
|
||||
|
||||
#### to_int
|
||||
```
|
||||
@ -153,7 +153,7 @@ Bytes.split(a : bytes(m + n)) : bytes(m) * bytes(n)
|
||||
Splits a byte array at given index
|
||||
|
||||
|
||||
## Char
|
||||
### Char
|
||||
|
||||
#### to_int
|
||||
```
|
||||
@ -172,7 +172,7 @@ Char.from_int(i : int) : option(char)
|
||||
Opposite of [to_int](#to_int). Returns `None` if the integer doesn't correspond to a single (normalized) codepoint.
|
||||
|
||||
|
||||
## Int
|
||||
### Int
|
||||
|
||||
#### to_str
|
||||
```
|
||||
@ -182,7 +182,7 @@ Int.to_str : int => string
|
||||
Casts integer to string using decimal representation
|
||||
|
||||
|
||||
## Map
|
||||
### Map
|
||||
|
||||
#### lookup
|
||||
`Map.lookup(k : 'k, m : map('k, 'v)) : option('v)`
|
||||
@ -229,7 +229,7 @@ Turns a list of pairs of form `(key, value)` into a map
|
||||
|
||||
|
||||
|
||||
## Address
|
||||
### Address
|
||||
|
||||
#### to_str
|
||||
```
|
||||
@ -271,7 +271,7 @@ Address.to_contract(a : address) : C
|
||||
Cast address to contract type C (where `C` is a contract)
|
||||
|
||||
|
||||
## Crypto
|
||||
### Crypto
|
||||
|
||||
#### sha3
|
||||
```
|
||||
@ -328,7 +328,7 @@ Crypto.verify_sig_secp256k1(msg : hash, pubkey : bytes(64), sig : bytes(64)) : b
|
||||
<!-- TODO -->
|
||||
|
||||
|
||||
## Auth
|
||||
### Auth
|
||||
|
||||
|
||||
#### tx
|
||||
@ -368,7 +368,7 @@ Auth.tx_hash : option(hash)
|
||||
|
||||
Gets the transaction hash during authentication.
|
||||
|
||||
## Oracle
|
||||
### Oracle
|
||||
|
||||
#### register
|
||||
```
|
||||
@ -380,7 +380,7 @@ Registers new oracle answering questions of type `'a` with answers of type `'b`.
|
||||
* The `acct` is the address of the oracle to register (can be the same as the contract).
|
||||
* `signature` is a signature proving that the contract is allowed to register the account -
|
||||
the `network id` + `account address` + `contract address` (concatenated as byte arrays) is
|
||||
[signed](./sophia.md#delegation-signature) with the
|
||||
[signed](./sophia_features.md#delegation-signature) with the
|
||||
private key of the account, proving you have the private key of the oracle to be. If the
|
||||
address is the same as the contract `sign` is ignored and can be left out entirely.
|
||||
* The `qfee` is the minimum query fee to be paid by a user when asking a question of the oracle.
|
||||
@ -413,7 +413,7 @@ Responds to the question `q` on `o`.
|
||||
Unless the contract address is the same as the oracle address the `signature`
|
||||
(which is an optional, named argument)
|
||||
needs to be provided. Proving that we have the private key of the oracle by
|
||||
[signing](./sophia.md#delegation-signature)
|
||||
[signing](./sophia_features.md#delegation-signature)
|
||||
the `network id` + `oracle query id` + `contract address`
|
||||
|
||||
|
||||
@ -481,25 +481,25 @@ Oracle.check_query(o : oracle('a, 'b), q : oracle_query('a, 'b)) : bool
|
||||
It returns `true` iff the oracle query exist and has the expected type.
|
||||
|
||||
|
||||
## AENS
|
||||
### AENS
|
||||
|
||||
The following functionality is available for interacting with the Aeternity
|
||||
Naming System (AENS).
|
||||
The following functionality is available for interacting with the æternity
|
||||
naming system (AENS).
|
||||
If `owner` is equal to `Contract.address` the signature `signature` is
|
||||
ignored, and can be left out since it is a named argument. Otherwise we need
|
||||
a signature to prove that we are allowed to do AENS operations on behalf of
|
||||
`owner`. The [signature is tied to a network id](https://github.com/aeternity/protocol/blob/iris/consensus/consensus.md#transaction-signature),
|
||||
i.e. the signature material should be prefixed by the network id.
|
||||
|
||||
### Types
|
||||
#### Types
|
||||
|
||||
#### name
|
||||
##### name
|
||||
```
|
||||
datatype name = Name(address, Chain.ttl, map(string, AENS.pointee))
|
||||
```
|
||||
|
||||
|
||||
#### pointee
|
||||
##### pointee
|
||||
|
||||
```
|
||||
datatype pointee = AccountPt(address) | OraclePt(address)
|
||||
@ -507,9 +507,9 @@ datatype pointee = AccountPt(address) | OraclePt(address)
|
||||
```
|
||||
|
||||
|
||||
### Functions
|
||||
#### Functions
|
||||
|
||||
#### resolve
|
||||
##### resolve
|
||||
```
|
||||
AENS.resolve(name : string, key : string) : option('a)
|
||||
```
|
||||
@ -520,7 +520,7 @@ associated with this name (for instance `"account_pubkey"`). The return type
|
||||
type checked against this type at run time.
|
||||
|
||||
|
||||
#### lookup
|
||||
##### lookup
|
||||
```
|
||||
AENS.lookup(name : string) : option(AENS.name)
|
||||
```
|
||||
@ -534,53 +534,53 @@ let Some(Name(owner, FixedTTL(expiry), ptrs)) = AENS.lookup("example.chain")
|
||||
```
|
||||
|
||||
|
||||
#### preclaim
|
||||
##### preclaim
|
||||
```
|
||||
AENS.preclaim(owner : address, commitment_hash : hash, <signature : signature>) : unit
|
||||
```
|
||||
|
||||
The [signature](./sophia.md#delegation-signature) should be over
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `Contract.address` (concatenated as byte arrays).
|
||||
|
||||
|
||||
#### claim
|
||||
##### claim
|
||||
```
|
||||
AENS.claim(owner : address, name : string, salt : int, name_fee : int, <signature : signature>) : unit
|
||||
```
|
||||
|
||||
The [signature](./sophia.md#delegation-signature) should be over
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
|
||||
|
||||
#### transfer
|
||||
##### transfer
|
||||
```
|
||||
AENS.transfer(owner : address, new_owner : address, name : string, <signature : signature>) : unit
|
||||
```
|
||||
|
||||
Transfers name to the new owner.
|
||||
|
||||
The [signature](./sophia.md#delegation-signature) should be over
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
|
||||
|
||||
#### revoke
|
||||
##### revoke
|
||||
```
|
||||
AENS.revoke(owner : address, name : string, <signature : signature>) : unit
|
||||
```
|
||||
|
||||
Revokes the name to extend the ownership time.
|
||||
|
||||
The [signature](./sophia.md#delegation-signature) should be over
|
||||
The [signature](./sophia_features.md#delegation-signature) should be over
|
||||
`network id` + `owner address` + `name_hash` + `Contract.address`
|
||||
(concatenated as byte arrays)
|
||||
using the private key of the `owner` account for signing.
|
||||
|
||||
|
||||
#### update
|
||||
##### update
|
||||
```
|
||||
AENS.update(owner : address, name : string, expiry : option(Chain.ttl), client_ttl : option(int),
|
||||
new_ptrs : map(string, AENS.pointee), <signature : signature>) : unit
|
||||
@ -591,7 +591,7 @@ will not be updated, for example if `None` is passed as `expiry` the expiry
|
||||
block of the name is not changed.
|
||||
|
||||
|
||||
## Contract
|
||||
### Contract
|
||||
|
||||
Values related to the current contract
|
||||
|
||||
@ -619,7 +619,7 @@ Contract.balance : int
|
||||
Amount of coins in the contract account
|
||||
|
||||
|
||||
## Call
|
||||
### Call
|
||||
|
||||
Values related to the call to the current contract
|
||||
|
||||
@ -670,13 +670,13 @@ Call.gas_left() : int
|
||||
The amount of gas left for the current call.
|
||||
|
||||
|
||||
## Chain
|
||||
### Chain
|
||||
|
||||
Values and functions related to the chain itself and other entities that live on it.
|
||||
|
||||
### Types
|
||||
#### Types
|
||||
|
||||
#### tx
|
||||
##### tx
|
||||
```
|
||||
record tx = { paying_for : option(Chain.paying_for_tx)
|
||||
, ga_metas : list(Chain.ga_meta_tx)
|
||||
@ -686,18 +686,18 @@ record tx = { paying_for : option(Chain.paying_for_tx)
|
||||
, tx : Chain.base_tx }
|
||||
```
|
||||
|
||||
#### ga_meta_tx
|
||||
##### ga_meta_tx
|
||||
```
|
||||
datatype ga_meta_tx = GAMetaTx(address, int)
|
||||
```
|
||||
|
||||
#### paying_for_tx
|
||||
##### paying_for_tx
|
||||
```
|
||||
datatype paying_for_tx = PayingForTx(address, int)
|
||||
```
|
||||
|
||||
|
||||
#### base_tx
|
||||
##### base_tx
|
||||
```
|
||||
datatype base_tx = SpendTx(address, int, string)
|
||||
| OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx
|
||||
@ -711,9 +711,9 @@ datatype base_tx = SpendTx(address, int, string)
|
||||
```
|
||||
|
||||
|
||||
### Functions
|
||||
#### Functions
|
||||
|
||||
#### balance
|
||||
##### balance
|
||||
```
|
||||
Chain.balance(a : address) : int
|
||||
```
|
||||
@ -721,7 +721,7 @@ Chain.balance(a : address) : int
|
||||
The balance of account `a`.
|
||||
|
||||
|
||||
#### block_hash
|
||||
##### block_hash
|
||||
```
|
||||
Chain.block_hash(h : int) : option(bytes(32))
|
||||
```
|
||||
@ -734,7 +734,7 @@ allowed height. From FATE VM version 2 (IRIS) it will return the block hash of
|
||||
the current generation.
|
||||
|
||||
|
||||
#### block_height
|
||||
##### block_height
|
||||
```
|
||||
Chain.block_height : int"
|
||||
```
|
||||
@ -742,7 +742,7 @@ Chain.block_height : int"
|
||||
The height of the current block (i.e. the block in which the current call will be included).
|
||||
|
||||
|
||||
#### coinbase
|
||||
##### coinbase
|
||||
```
|
||||
Chain.coinbase : address
|
||||
```
|
||||
@ -750,7 +750,7 @@ Chain.coinbase : address
|
||||
The address of the account that mined the current block.
|
||||
|
||||
|
||||
#### timestamp
|
||||
##### timestamp
|
||||
```
|
||||
Chain.timestamp : int
|
||||
```
|
||||
@ -758,7 +758,7 @@ Chain.timestamp : int
|
||||
The timestamp of the current block.
|
||||
|
||||
|
||||
#### difficulty
|
||||
##### difficulty
|
||||
```
|
||||
Chain.difficulty : int
|
||||
```
|
||||
@ -766,7 +766,7 @@ Chain.difficulty : int
|
||||
The difficulty of the current block.
|
||||
|
||||
|
||||
#### gas
|
||||
##### gas
|
||||
```
|
||||
Chain.gas_limit : int
|
||||
```
|
||||
@ -774,7 +774,7 @@ Chain.gas_limit : int
|
||||
The gas limit of the current block.
|
||||
|
||||
|
||||
#### bytecode_hash
|
||||
##### bytecode_hash
|
||||
```
|
||||
Chain.bytecode_hash : 'c => option(hash)
|
||||
```
|
||||
@ -785,7 +785,7 @@ instantiated with a contract. The charged gas increases linearly to
|
||||
the size of the serialized bytecode of the deployed contract.
|
||||
|
||||
|
||||
#### create
|
||||
##### create
|
||||
```
|
||||
Chain.create(value : int, ...) => 'c
|
||||
```
|
||||
@ -837,7 +837,7 @@ main contract Market =
|
||||
The typechecker must be certain about the created contract's type, so it is
|
||||
worth writing it explicitly as shown in the example.
|
||||
|
||||
#### clone
|
||||
##### clone
|
||||
```
|
||||
Chain.clone : ( ref : 'c, gas : int, value : int, protected : bool, ...
|
||||
) => if(protected) option('c) else 'c
|
||||
@ -895,18 +895,18 @@ implementation of the `init` function does not actually return `state`, but
|
||||
calls `put` instead. Moreover, FATE prevents even handcrafted calls to `init`.
|
||||
|
||||
|
||||
#### event
|
||||
##### event
|
||||
```
|
||||
Chain.event(e : event) : unit
|
||||
```
|
||||
Emits the event. To use this function one needs to define the `event` type as a `datatype` in the contract.
|
||||
|
||||
|
||||
# Includable namespaces
|
||||
## Includable namespaces
|
||||
|
||||
These need to be explicitly included (with `.aes` suffix)
|
||||
|
||||
## List
|
||||
### List
|
||||
|
||||
This module contains common operations on lists like constructing, querying, traversing etc.
|
||||
|
||||
@ -1253,7 +1253,7 @@ List.enumerate(l : list('a)) : list(int * 'a)
|
||||
Equivalent to [zip](#zip) with `[0..length(l)]`, but slightly faster.
|
||||
|
||||
|
||||
## Option
|
||||
### Option
|
||||
|
||||
Common operations on `option` types and lists of `option`s.
|
||||
|
||||
@ -1438,7 +1438,7 @@ Option.choose_first(l : list(option('a))) : option('a)
|
||||
Same as [choose](#choose), but chooses from a list insted of two arguments.
|
||||
|
||||
|
||||
## String
|
||||
### String
|
||||
|
||||
Operations on the `string` type. A `string` is a UTF-8 encoded byte array.
|
||||
|
||||
@ -1554,7 +1554,7 @@ blake2b(s : string) : hash
|
||||
Computes the Blake2B hash of the string.
|
||||
|
||||
|
||||
## Func
|
||||
### Func
|
||||
|
||||
Functional combinators.
|
||||
|
||||
@ -1684,7 +1684,7 @@ Func.untuplify3(f : 'a * 'b * 'c => 'd) : ('a, 'b, 'c) => 'd
|
||||
Opposite to [tuplify](#tuplify).
|
||||
|
||||
|
||||
## Pair
|
||||
### Pair
|
||||
|
||||
Common operations on 2-tuples.
|
||||
|
||||
@ -1736,7 +1736,7 @@ Pair.swap(t : ('a * 'b)) : ('b * 'a)
|
||||
Swaps elements.
|
||||
|
||||
|
||||
## Triple
|
||||
### Triple
|
||||
|
||||
#### fst
|
||||
```
|
||||
@ -1817,234 +1817,234 @@ Triple.rotl(t : ('a * 'b * 'c)) : ('b * 'c * 'a)
|
||||
|
||||
Cyclic rotation of the elements to the left.
|
||||
|
||||
## BLS12\_381
|
||||
### BLS12\_381
|
||||
|
||||
### Types
|
||||
#### Types
|
||||
|
||||
#### fp
|
||||
##### fp
|
||||
|
||||
Built-in (Montgomery) integer representation 32 bytes
|
||||
|
||||
|
||||
#### fr
|
||||
##### fr
|
||||
|
||||
Built-in (Montgomery) integer representation 48 bytes
|
||||
|
||||
|
||||
#### fp2
|
||||
##### fp2
|
||||
```
|
||||
record fp2 = { x1 : fp, x2 : fp }`
|
||||
```
|
||||
|
||||
#### g1
|
||||
##### g1
|
||||
```
|
||||
record g1 = { x : fp, y : fp, z : fp }
|
||||
```
|
||||
|
||||
|
||||
#### g2
|
||||
##### g2
|
||||
```
|
||||
record g2 = { x : fp2, y : fp2, z : fp2 }
|
||||
```
|
||||
|
||||
|
||||
#### gt
|
||||
##### gt
|
||||
```
|
||||
record gt = { x1 : fp, x2 : fp, x3 : fp, x4 : fp, x5 : fp, x6 : fp, x7 : fp, x8 : fp, x9 : fp, x10 : fp, x11 : fp, x12 : fp }
|
||||
```
|
||||
|
||||
### Functions
|
||||
#### Functions
|
||||
|
||||
#### pairing\_check
|
||||
##### pairing\_check
|
||||
```
|
||||
BLS12_381.pairing_check(xs : list(g1), ys : list(g2)) : bool
|
||||
```
|
||||
|
||||
Pairing check of a list of points, `xs` and `ys` should be of equal length.
|
||||
|
||||
#### int_to_fr
|
||||
##### int_to_fr
|
||||
```
|
||||
BLS12_381.int_to_fr(x : int) : fr
|
||||
```
|
||||
|
||||
Convert an integer to an `fr` - a 32 bytes internal (Montgomery) integer representation.
|
||||
|
||||
#### int_to_fp
|
||||
##### int_to_fp
|
||||
```
|
||||
BLS12_381.int_to_fp(x : int) : fp
|
||||
```
|
||||
|
||||
Convert an integer to an `fp` - a 48 bytes internal (Montgomery) integer representation.
|
||||
|
||||
#### fr_to_int
|
||||
##### fr_to_int
|
||||
```
|
||||
BLS12_381.fr_to_int(x : fr) : int
|
||||
```
|
||||
|
||||
Convert a `fr` value into an integer.
|
||||
|
||||
#### fp_to_int
|
||||
##### fp_to_int
|
||||
```
|
||||
BLS12_381.fp_to_int(x : fp) : int
|
||||
```
|
||||
|
||||
Convert a `fp` value into an integer.
|
||||
|
||||
#### mk_g1
|
||||
##### mk_g1
|
||||
```
|
||||
BLS12_381.mk_g1(x : int, y : int, z : int) : g1
|
||||
```
|
||||
|
||||
Construct a `g1` point from three integers.
|
||||
|
||||
#### mk_g2
|
||||
##### mk_g2
|
||||
```
|
||||
BLS12_381.mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2
|
||||
```
|
||||
|
||||
Construct a `g2` point from six integers.
|
||||
|
||||
#### g1_neg
|
||||
##### g1_neg
|
||||
```
|
||||
BLS12_381.g1_neg(p : g1) : g1
|
||||
```
|
||||
|
||||
Negate a `g1` value.
|
||||
|
||||
#### g1_norm
|
||||
##### g1_norm
|
||||
```
|
||||
BLS12_381.g1_norm(p : g1) : g1
|
||||
```
|
||||
|
||||
Normalize a `g1` value.
|
||||
|
||||
#### g1_valid
|
||||
##### g1_valid
|
||||
```
|
||||
BLS12_381.g1_valid(p : g1) : bool
|
||||
```
|
||||
|
||||
Check that a `g1` value is a group member.
|
||||
|
||||
#### g1_is_zero
|
||||
##### g1_is_zero
|
||||
```
|
||||
BLS12_381.g1_is_zero(p : g1) : bool
|
||||
```
|
||||
|
||||
Check if a `g1` value corresponds to the zero value of the group.
|
||||
|
||||
#### g1_add
|
||||
##### g1_add
|
||||
```
|
||||
BLS12_381.g1_add(p : g1, q : g1) : g1
|
||||
```
|
||||
|
||||
Add two `g1` values.
|
||||
|
||||
#### g1_mul
|
||||
##### g1_mul
|
||||
```
|
||||
BLS12_381.g1_mul(k : fr, p : g1) : g1
|
||||
```
|
||||
|
||||
Scalar multiplication for `g1`.
|
||||
|
||||
#### g2_neg
|
||||
##### g2_neg
|
||||
```
|
||||
BLS12_381.g2_neg(p : g2) : g2
|
||||
```
|
||||
|
||||
Negate a `g2` value.
|
||||
|
||||
#### g2_norm
|
||||
##### g2_norm
|
||||
```
|
||||
BLS12_381.g2_norm(p : g2) : g2
|
||||
```
|
||||
|
||||
Normalize a `g2` value.
|
||||
|
||||
#### g2_valid
|
||||
##### g2_valid
|
||||
```
|
||||
BLS12_381.g2_valid(p : g2) : bool
|
||||
```
|
||||
|
||||
Check that a `g2` value is a group member.
|
||||
|
||||
#### g2_is_zero
|
||||
##### g2_is_zero
|
||||
```
|
||||
BLS12_381.g2_is_zero(p : g2) : bool
|
||||
```
|
||||
|
||||
Check if a `g2` value corresponds to the zero value of the group.
|
||||
|
||||
#### g2_add
|
||||
##### g2_add
|
||||
```
|
||||
BLS12_381.g2_add(p : g2, q : g2) : g2
|
||||
```
|
||||
|
||||
Add two `g2` values.
|
||||
|
||||
#### g2_mul
|
||||
##### g2_mul
|
||||
```
|
||||
BLS12_381.g2_mul(k : fr, p : g2) : g2
|
||||
```
|
||||
|
||||
Scalar multiplication for `g2`.
|
||||
|
||||
#### gt_inv
|
||||
##### gt_inv
|
||||
```
|
||||
BLS12_381.gt_inv(p : gt) : gt
|
||||
```
|
||||
|
||||
Invert a `gt` value.
|
||||
|
||||
#### gt_add
|
||||
##### gt_add
|
||||
```
|
||||
BLS12_381.gt_add(p : gt, q : gt) : gt
|
||||
```
|
||||
|
||||
Add two `gt` values.
|
||||
|
||||
#### gt_mul
|
||||
##### gt_mul
|
||||
```
|
||||
BLS12_381.gt_mul(p : gt, q : gt) : gt
|
||||
```
|
||||
|
||||
Multiply two `gt` values.
|
||||
|
||||
#### gt_pow
|
||||
##### gt_pow
|
||||
```
|
||||
BLS12_381.gt_pow(p : gt, k : fr) : gt
|
||||
```
|
||||
|
||||
Calculate exponentiation `p ^ k`.
|
||||
|
||||
#### gt_is_one
|
||||
##### gt_is_one
|
||||
```
|
||||
BLS12_381.gt_is_one(p : gt) : bool
|
||||
```
|
||||
|
||||
Compare a `gt` value to the unit value of the Gt group.
|
||||
|
||||
#### pairing
|
||||
##### pairing
|
||||
```
|
||||
BLS12_381.pairing(p : g1, q : g2) : gt
|
||||
```
|
||||
|
||||
Compute the pairing of a `g1` value and a `g2` value.
|
||||
|
||||
#### miller_loop
|
||||
##### miller_loop
|
||||
```
|
||||
BLS12_381.miller_loop(p : g1, q : g2) : gt
|
||||
```
|
||||
|
||||
Do the Miller loop stage of pairing for `g1` and `g2`.
|
||||
|
||||
#### final_exp
|
||||
##### final_exp
|
||||
```
|
||||
BLS12_381.final_exp(p : gt) : gt
|
||||
```
|
||||
|
||||
Perform the final exponentiation step of pairing for a `gt` value.
|
||||
|
||||
## Frac
|
||||
### Frac
|
||||
|
||||
This namespace provides operations on rational numbers. A rational number is represented
|
||||
as a fraction of two integers which are stored internally in the `frac` datatype.
|
||||
@ -2068,9 +2068,9 @@ language provides checkers to prevent unintended usage of them. Therefore the ty
|
||||
**will** allow that and the results of such comparison will be unspecified.
|
||||
You should use [lt](#lt), [geq](#geq), [eq](#eq) etc instead.
|
||||
|
||||
### Types
|
||||
#### Types
|
||||
|
||||
#### frac
|
||||
##### frac
|
||||
```
|
||||
datatype frac = Pos(int, int) | Zero | Neg(int, int)
|
||||
```
|
||||
@ -2079,194 +2079,194 @@ Internal representation of fractional numbers. First integer encodes the numerat
|
||||
both must be always positive, as the sign is being handled by the choice of the constructor.
|
||||
|
||||
|
||||
### Functions
|
||||
#### Functions
|
||||
|
||||
#### make_frac
|
||||
##### make_frac
|
||||
`Frac.make_frac(n : int, d : int) : frac`
|
||||
|
||||
Creates a fraction out of numerator and denominator. Automatically normalizes, so
|
||||
`make_frac(2, 4)` and `make_frac(1, 2)` will yield same results.
|
||||
|
||||
|
||||
#### num
|
||||
##### num
|
||||
`Frac.num(f : frac) : int`
|
||||
|
||||
Returns the numerator of a fraction.
|
||||
|
||||
|
||||
#### den
|
||||
##### den
|
||||
`Frac.den(f : frac) : int`
|
||||
|
||||
Returns the denominator of a fraction.
|
||||
|
||||
|
||||
#### to_pair
|
||||
##### to_pair
|
||||
`Frac.to_pair(f : frac) : int * int`
|
||||
|
||||
Turns a fraction into a pair of numerator and denominator.
|
||||
|
||||
|
||||
#### sign
|
||||
##### sign
|
||||
`Frac.sign(f : frac) : int`
|
||||
|
||||
Returns the signum of a fraction, -1, 0, 1 if negative, zero, positive respectively.
|
||||
|
||||
|
||||
#### to_str
|
||||
##### to_str
|
||||
`Frac.to_str(f : frac) : string`
|
||||
|
||||
Conversion to string. Does not display division by 1 or denominator if equals zero.
|
||||
|
||||
|
||||
#### simplify
|
||||
##### simplify
|
||||
`Frac.simplify(f : frac) : frac`
|
||||
|
||||
Reduces fraction to normal form if for some reason it is not in it.
|
||||
|
||||
|
||||
#### eq
|
||||
##### eq
|
||||
`Frac.eq(a : frac, b : frac) : bool`
|
||||
|
||||
Checks if `a` is equal to `b`.
|
||||
|
||||
|
||||
#### neq
|
||||
##### neq
|
||||
`Frac.neq(a : frac, b : frac) : bool`
|
||||
|
||||
Checks if `a` is not equal to `b`.
|
||||
|
||||
|
||||
#### geq
|
||||
##### geq
|
||||
`Frac.geq(a : frac, b : frac) : bool`
|
||||
|
||||
Checks if `a` is greater or equal to `b`.
|
||||
|
||||
|
||||
#### leq
|
||||
##### leq
|
||||
`Frac.leq(a : frac, b : frac) : bool`
|
||||
|
||||
Checks if `a` is lesser or equal to `b`.
|
||||
|
||||
|
||||
#### gt
|
||||
##### gt
|
||||
`Frac.gt(a : frac, b : frac) : bool`
|
||||
|
||||
Checks if `a` is greater than `b`.
|
||||
|
||||
|
||||
#### lt
|
||||
##### lt
|
||||
`Frac.lt(a : frac, b : frac) : bool`
|
||||
|
||||
Checks if `a` is lesser than `b`.
|
||||
|
||||
|
||||
#### min
|
||||
##### min
|
||||
`Frac.min(a : frac, b : frac) : frac`
|
||||
|
||||
Chooses lesser of the two fractions.
|
||||
|
||||
|
||||
#### max
|
||||
##### max
|
||||
`Frac.max(a : frac, b : frac) : frac`
|
||||
|
||||
Chooses greater of the two fractions.
|
||||
|
||||
|
||||
#### abs
|
||||
##### abs
|
||||
`Frac.abs(f : frac) : frac`
|
||||
|
||||
Absolute value.
|
||||
|
||||
|
||||
#### from_int
|
||||
##### from_int
|
||||
`Frac.from_int(n : int) : frac`
|
||||
|
||||
From integer conversion. Effectively `make_frac(n, 1)`.
|
||||
|
||||
|
||||
#### floor
|
||||
##### floor
|
||||
`Frac.floor(f : frac) : int`
|
||||
|
||||
Rounds a fraction to the nearest lesser or equal integer.
|
||||
|
||||
|
||||
#### ceil
|
||||
##### ceil
|
||||
`Frac.ceil(f : frac) : int`
|
||||
|
||||
Rounds a fraction to the nearest greater or equal integer.
|
||||
|
||||
|
||||
#### round_to_zero
|
||||
##### round_to_zero
|
||||
`Frac.round_to_zero(f : frac) : int`
|
||||
|
||||
Rounds a fraction towards zero.
|
||||
Effectively `ceil` if lesser than zero and `floor` if greater.
|
||||
|
||||
|
||||
#### round_from_zero
|
||||
##### round_from_zero
|
||||
`Frac.round_from_zero(f : frac) : int`
|
||||
|
||||
Rounds a fraction from zero.
|
||||
Effectively `ceil` if greater than zero and `floor` if lesser.
|
||||
|
||||
|
||||
#### round
|
||||
##### round
|
||||
`Frac.round(f : frac) : int`
|
||||
|
||||
Rounds a fraction to a nearest integer. If two integers are in the same distance it
|
||||
will choose the even one.
|
||||
|
||||
|
||||
#### add
|
||||
##### add
|
||||
`Frac.add(a : frac, b : frac) : frac`
|
||||
|
||||
Sum of the fractions.
|
||||
|
||||
|
||||
#### neg
|
||||
##### neg
|
||||
`Frac.neg(a : frac) : frac`
|
||||
|
||||
Negation of the fraction.
|
||||
|
||||
|
||||
#### sub
|
||||
##### sub
|
||||
`Frac.sub(a : frac, b : frac) : frac`
|
||||
|
||||
Subtraction of two fractions.
|
||||
|
||||
|
||||
#### inv
|
||||
##### inv
|
||||
`Frac.inv(a : frac) : frac`
|
||||
|
||||
Inverts a fraction. Throws error if `a` is zero.
|
||||
|
||||
|
||||
#### mul
|
||||
##### mul
|
||||
`Frac.mul(a : frac, b : frac) : frac`
|
||||
|
||||
Multiplication of two fractions.
|
||||
|
||||
|
||||
#### div
|
||||
##### div
|
||||
`Frac.div(a : frac, b : frac) : frac`
|
||||
|
||||
Division of two fractions.
|
||||
|
||||
|
||||
#### int_exp
|
||||
##### int_exp
|
||||
`Frac.int_exp(b : frac, e : int) : frac`
|
||||
|
||||
Takes `b` to the power of `e`. The exponent can be a negative value.
|
||||
|
||||
|
||||
#### optimize
|
||||
##### optimize
|
||||
`Frac.optimize(f : frac, loss : frac) : frac`
|
||||
|
||||
Shrink the internal size of a fraction as much as possible by approximating it to the
|
||||
point where the error would exceed the `loss` value.
|
||||
|
||||
|
||||
#### is_sane
|
||||
##### is_sane
|
||||
`Frac.is_sane(f : frac) : bool`
|
||||
|
||||
For debugging. If it ever returns false in a code that doesn't call `frac` constructors or
|
||||
|
263
docs/sophia_syntax.md
Normal file
263
docs/sophia_syntax.md
Normal file
@ -0,0 +1,263 @@
|
||||
# Syntax
|
||||
|
||||
## Lexical syntax
|
||||
|
||||
### Comments
|
||||
|
||||
Single line comments start with `//` and block comments are enclosed in `/*`
|
||||
and `*/` and can be nested.
|
||||
|
||||
### Keywords
|
||||
|
||||
```
|
||||
contract elif else entrypoint false function if import include let mod namespace
|
||||
private payable stateful switch true type record datatype main interface
|
||||
```
|
||||
|
||||
### Tokens
|
||||
|
||||
- `Id = [a-z_][A-Za-z0-9_']*` identifiers start with a lower case letter.
|
||||
- `Con = [A-Z][A-Za-z0-9_']*` constructors start with an upper case letter.
|
||||
- `QId = (Con\.)+Id` qualified identifiers (e.g. `Map.member`)
|
||||
- `QCon = (Con\.)+Con` qualified constructor
|
||||
- `TVar = 'Id` type variable (e.g `'a`, `'b`)
|
||||
- `Int = [0-9]+(_[0-9]+)*|0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*` integer literal with optional `_` separators
|
||||
- `Bytes = #[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*` byte array literal with optional `_` separators
|
||||
- `String` string literal enclosed in `"` with escape character `\`
|
||||
- `Char` character literal enclosed in `'` with escape character `\`
|
||||
- `AccountAddress` base58-encoded 32 byte account pubkey with `ak_` prefix
|
||||
- `ContractAddress` base58-encoded 32 byte contract address with `ct_` prefix
|
||||
- `OracleAddress` base58-encoded 32 byte oracle address with `ok_` prefix
|
||||
- `OracleQueryId` base58-encoded 32 byte oracle query id with `oq_` prefix
|
||||
|
||||
Valid string escape codes are
|
||||
|
||||
| Escape | ASCII | |
|
||||
|---------------|-------------|---|
|
||||
| `\b` | 8 | |
|
||||
| `\t` | 9 | |
|
||||
| `\n` | 10 | |
|
||||
| `\v` | 11 | |
|
||||
| `\f` | 12 | |
|
||||
| `\r` | 13 | |
|
||||
| `\e` | 27 | |
|
||||
| `\xHexDigits` | *HexDigits* | |
|
||||
|
||||
|
||||
See the [identifier encoding scheme](https://github.com/aeternity/protocol/blob/master/node/api/api_encoding.md) for the
|
||||
details on the base58 literals.
|
||||
|
||||
## Layout blocks
|
||||
|
||||
Sophia uses Python-style layout rules to group declarations and statements. A
|
||||
layout block with more than one element must start on a separate line and be
|
||||
indented more than the currently enclosing layout block. Blocks with a single
|
||||
element can be written on the same line as the previous token.
|
||||
|
||||
Each element of the block must share the same indentation and no part of an
|
||||
element may be indented less than the indentation of the block. For instance
|
||||
|
||||
```sophia
|
||||
contract Layout =
|
||||
function foo() = 0 // no layout
|
||||
function bar() = // layout block starts on next line
|
||||
let x = foo() // indented more than 2 spaces
|
||||
x
|
||||
+ 1 // the '+' is indented more than the 'x'
|
||||
```
|
||||
|
||||
## Notation
|
||||
|
||||
In describing the syntax below, we use the following conventions:
|
||||
|
||||
- Upper-case identifiers denote non-terminals (like `Expr`) or terminals with
|
||||
some associated value (like `Id`).
|
||||
- Keywords and symbols are enclosed in single quotes: `'let'` or `'='`.
|
||||
- Choices are separated by vertical bars: `|`.
|
||||
- Optional elements are enclosed in `[` square brackets `]`.
|
||||
- `(` Parentheses `)` are used for grouping.
|
||||
- Zero or more repetitions are denoted by a postfix `*`, and one or more
|
||||
repetitions by a `+`.
|
||||
- `Block(X)` denotes a layout block of `X`s.
|
||||
- `Sep(X, S)` is short for `[X (S X)*]`, i.e. a possibly empty sequence of `X`s
|
||||
separated by `S`s.
|
||||
- `Sep1(X, S)` is short for `X (S X)*`, i.e. same as `Sep`, but must not be empty.
|
||||
|
||||
|
||||
## Declarations
|
||||
|
||||
A Sophia file consists of a sequence of *declarations* in a layout block.
|
||||
|
||||
```c
|
||||
File ::= Block(TopDecl)
|
||||
|
||||
TopDecl ::= ['payable'] 'contract' Con '=' Block(Decl)
|
||||
| 'namespace' Con '=' Block(Decl)
|
||||
| '@compiler' PragmaOp Version
|
||||
| 'include' String
|
||||
|
||||
Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
|
||||
| 'record' Id ['(' TVar* ')'] '=' RecordType
|
||||
| 'datatype' Id ['(' TVar* ')'] '=' DataType
|
||||
| (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
|
||||
|
||||
FunDecl ::= Id ':' Type // Type signature
|
||||
| Id Args [':' Type] '=' Block(Stmt) // Definition
|
||||
|
||||
PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
|
||||
Version ::= Sep1(Int, '.')
|
||||
|
||||
EModifier ::= 'payable' | 'stateful'
|
||||
FModifier ::= 'stateful' | 'private'
|
||||
|
||||
Args ::= '(' Sep(Pattern, ',') ')'
|
||||
```
|
||||
|
||||
Contract declarations must appear at the top-level.
|
||||
|
||||
For example,
|
||||
```sophia
|
||||
contract Test =
|
||||
type t = int
|
||||
entrypoint add (x : t, y : t) = x + y
|
||||
```
|
||||
|
||||
There are three forms of type declarations: type aliases (declared with the
|
||||
`type` keyword), record type definitions (`record`) and data type definitions
|
||||
(`datatype`):
|
||||
|
||||
```c
|
||||
TypeAlias ::= Type
|
||||
RecordType ::= '{' Sep(FieldType, ',') '}'
|
||||
DataType ::= Sep1(ConDecl, '|')
|
||||
|
||||
FieldType ::= Id ':' Type
|
||||
ConDecl ::= Con ['(' Sep1(Type, ',') ')']
|
||||
```
|
||||
|
||||
For example,
|
||||
```sophia
|
||||
record point('a) = {x : 'a, y : 'a}
|
||||
datatype shape('a) = Circle(point('a), 'a) | Rect(point('a), point('a))
|
||||
type int_shape = shape(int)
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
```c
|
||||
Type ::= Domain '=>' Type // Function type
|
||||
| Type '(' Sep(Type, ',') ')' // Type application
|
||||
| '(' Type ')' // Parens
|
||||
| 'unit' | Sep(Type, '*') // Tuples
|
||||
| Id | QId | TVar
|
||||
|
||||
Domain ::= Type // Single argument
|
||||
| '(' Sep(Type, ',') ')' // Multiple arguments
|
||||
```
|
||||
|
||||
The function type arrow associates to the right.
|
||||
|
||||
Example,
|
||||
```sophia
|
||||
'a => list('a) => (int * list('a))
|
||||
```
|
||||
|
||||
## Statements
|
||||
|
||||
Function bodies are blocks of *statements*, where a statement is one of the following
|
||||
|
||||
```c
|
||||
Stmt ::= 'switch' '(' Expr ')' Block(Case)
|
||||
| 'if' '(' Expr ')' Block(Stmt)
|
||||
| 'elif' '(' Expr ')' Block(Stmt)
|
||||
| 'else' Block(Stmt)
|
||||
| 'let' LetDef
|
||||
| Expr
|
||||
|
||||
LetDef ::= Id Args [':' Type] '=' Block(Stmt) // Function definition
|
||||
| Pattern '=' Block(Stmt) // Value definition
|
||||
|
||||
Case ::= Pattern '=>' Block(Stmt)
|
||||
Pattern ::= Expr
|
||||
```
|
||||
|
||||
`if` statements can be followed by zero or more `elif` statements and an optional final `else` statement. For example,
|
||||
|
||||
```sophia
|
||||
let x : int = 4
|
||||
switch(f(x))
|
||||
None => 0
|
||||
Some(y) =>
|
||||
if(y > 10)
|
||||
"too big"
|
||||
elif(y < 3)
|
||||
"too small"
|
||||
else
|
||||
"just right"
|
||||
```
|
||||
|
||||
## Expressions
|
||||
|
||||
```c
|
||||
Expr ::= '(' LamArgs ')' '=>' Block(Stmt) // Anonymous function (x) => x + 1
|
||||
| 'if' '(' Expr ')' Expr 'else' Expr // If expression if(x < y) y else x
|
||||
| Expr ':' Type // Type annotation 5 : int
|
||||
| Expr BinOp Expr // Binary operator x + y
|
||||
| UnOp Expr // Unary operator ! b
|
||||
| Expr '(' Sep(Expr, ',') ')' // Application f(x, y)
|
||||
| Expr '.' Id // Projection state.x
|
||||
| Expr '[' Expr ']' // Map lookup map[key]
|
||||
| Expr '{' Sep(FieldUpdate, ',') '}' // Record or map update r{ fld[key].x = y }
|
||||
| '[' Sep(Expr, ',') ']' // List [1, 2, 3]
|
||||
| '[' Expr '|' Sep(Generator, ',') ']'
|
||||
// List comprehension [k | x <- [1], if (f(x)), let k = x+1]
|
||||
| '[' Expr '..' Expr ']' // List range [1..n]
|
||||
| '{' Sep(FieldUpdate, ',') '}' // Record or map value {x = 0, y = 1}, {[key] = val}
|
||||
| '(' Expr ')' // Parens (1 + 2) * 3
|
||||
| Id | Con | QId | QCon // Identifiers x, None, Map.member, AELib.Token
|
||||
| Int | Bytes | String | Char // Literals 123, 0xff, #00abc123, "foo", '%'
|
||||
| AccountAddress | ContractAddress // Chain identifiers
|
||||
| OracleAddress | OracleQueryId // Chain identifiers
|
||||
|
||||
Generator ::= Pattern '<-' Expr // Generator
|
||||
| 'if' '(' Expr ')' // Guard
|
||||
| LetDef // Definition
|
||||
|
||||
LamArgs ::= '(' Sep(LamArg, ',') ')'
|
||||
LamArg ::= Id [':' Type]
|
||||
|
||||
FieldUpdate ::= Path '=' Expr
|
||||
Path ::= Id // Record field
|
||||
| '[' Expr ']' // Map key
|
||||
| Path '.' Id // Nested record field
|
||||
| Path '[' Expr ']' // Nested map key
|
||||
|
||||
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
||||
UnOp ::= '-' | '!'
|
||||
```
|
||||
|
||||
## Operators types
|
||||
|
||||
| Operators | Type
|
||||
| --- | ---
|
||||
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
||||
| `!` `&&` `\|\|` | logical operators
|
||||
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
||||
| `::` `++` | list operators
|
||||
|
||||
## Operator precendences
|
||||
|
||||
In order of highest to lowest precedence.
|
||||
|
||||
| Operators | Associativity
|
||||
| --- | ---
|
||||
| `!` | right
|
||||
| `^` | left
|
||||
| `*` `/` `mod` | left
|
||||
| `-` (unary) | right
|
||||
| `+` `-` | left
|
||||
| `::` `++` | right
|
||||
| `<` `>` `=<` `>=` `==` `!=` | none
|
||||
| `&&` | right
|
||||
| `\|\|` | right
|
Loading…
x
Reference in New Issue
Block a user