Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1975ccf804 | |||
| 4f68729631 | |||
| 10c845d3cf | |||
| 393d7710c1 | |||
| 37e5a92b2e | |||
| cb9c9df103 | |||
| c09313a92c | |||
| 75b2d6981f | |||
| 78d94786b6 | |||
| 216f7f8a25 | |||
| 254172e3a3 | |||
| eadb4e8c83 | |||
| e2af89287d | |||
| 3996b6a711 | |||
| e8b32a6875 | |||
| cca7bdff49 | |||
| 1d9f59fec3 | |||
| d82b42518e | |||
| 00a3a51d0d | |||
| 6858329faa | |||
| c2a3e333c7 | |||
| 4787830861 | |||
| a0111066e7 | |||
| 2311d19602 | |||
| 3b2ce63fa7 | |||
| 8b4a1aaf0d | |||
| c6e7db2381 | |||
| b8002029cf | |||
| 1a14602f36 | |||
| 2a78189f31 | |||
| 4504fb8dcf | |||
| 8798e0b2c9 | |||
| 1dfc349065 | |||
| 1266d9ea99 | |||
| bbb049cb2e | |||
| 787551b8bc | |||
| ac673602b9 | |||
| 079b3a45c9 | |||
| c0d9759e60 | |||
| b7b242bc66 | |||
| 05b87fe200 | |||
| cc07e3a638 | |||
| ea5850cf93 | |||
| d2dcb9e249 | |||
| adb3cf5406 | |||
| ad78f440d9 | |||
| 42cd47d1b3 | |||
| 93d2086ddf | |||
| 9487b79f42 | |||
| e64ac9396a | |||
| 4a812b6f3b | |||
| fe2d93ea8a | |||
| ecbc15db1b | |||
| d0caee24d9 | |||
| 57eb77f2f8 | |||
| 53ed60b498 | |||
| e49738c90c | |||
| a38a365181 | |||
| 0dddac3d86 | |||
| 3da694e798 | |||
| e98edd4eef | |||
| 2bad76314f | |||
| b9acf24dca | |||
| 6682b24156 | |||
| b31be6227d | |||
| bbc8555331 | |||
| 13bc821211 | |||
| 34c10e1518 | |||
| bb79e7dd89 | |||
| c3426f0e65 | |||
| db01e237c1 | |||
| 760d2841d1 | |||
| 43013ec920 | |||
| d821de6381 | |||
| 282f743925 | |||
| cf1072140e | |||
| 75797686ad | |||
| ed9384c2af | |||
| 1c24a700dc | |||
| f2e9fbcc51 | |||
| 2d49426fe0 | |||
| f5df2c1a5f | |||
| 04445e4dee | |||
| eec70f03a5 | |||
| c2c8e297ae | |||
| 5c5d3c60ef | |||
| 2a3274ba25 | |||
| 13b7bde44b | |||
| baf527b5fa | |||
| 422baa5b65 | |||
| 126e04ae42 |
@@ -8,6 +8,15 @@ executors:
|
|||||||
working_directory: ~/aesophia
|
working_directory: ~/aesophia
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
verify_rebar_lock:
|
||||||
|
executor: aebuilder
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Ensure lock file is up-to-date
|
||||||
|
command: |
|
||||||
|
./rebar3 upgrade
|
||||||
|
git diff --quiet -- rebar.lock || (echo "rebar.lock is not up-to-date" && exit 1)
|
||||||
build:
|
build:
|
||||||
executor: aebuilder
|
executor: aebuilder
|
||||||
steps:
|
steps:
|
||||||
@@ -35,3 +44,10 @@ jobs:
|
|||||||
- _build/default/rebar3_20.3.8_plt
|
- _build/default/rebar3_20.3.8_plt
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: _build/test/logs
|
path: _build/test/logs
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
build_test:
|
||||||
|
jobs:
|
||||||
|
- build
|
||||||
|
- verify_rebar_lock
|
||||||
|
|||||||
@@ -21,3 +21,4 @@ rebar3.crashdump
|
|||||||
aesophia
|
aesophia
|
||||||
.qcci
|
.qcci
|
||||||
current_counterexample.eqc
|
current_counterexample.eqc
|
||||||
|
test/contracts/test.aes
|
||||||
|
|||||||
+96
-1
@@ -9,6 +9,98 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Changed
|
### Changed
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
## [6.0.1] 2021-06-24
|
||||||
|
### Changed
|
||||||
|
- Fixed a bug in calldata encoding for contracts containing multiple contracts
|
||||||
|
- Fixed a missing `include` in the `Frac` standard library
|
||||||
|
|
||||||
|
## [6.0.0] 2021-05-26
|
||||||
|
### Added
|
||||||
|
- Child contracts
|
||||||
|
- `Chain.clone`
|
||||||
|
- `Chain.create`
|
||||||
|
- `Chain.bytecode_hash`
|
||||||
|
- Minor support for variadic functions
|
||||||
|
- `void` type that represents an empty type
|
||||||
|
- `Call.fee` builtin
|
||||||
|
### Changed
|
||||||
|
- Contract interfaces must be now invocated by `contract interface` keywords
|
||||||
|
- `main` keyword to indicate the main contract in case there are child contracts around
|
||||||
|
- `List.sum` and `List.product` no longer use `List.foldl`
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
## [5.0.0] 2021-04-30
|
||||||
|
### Added
|
||||||
|
- A new and improved [`String` standard library](https://github.com/aeternity/aesophia/blob/master/docs/sophia_stdlib.md#string)
|
||||||
|
has been added. Use it by `include "String.aes"`. It includes functions for
|
||||||
|
turning strings into lists of characters for detailed manipulation. For
|
||||||
|
example:
|
||||||
|
```
|
||||||
|
include "String.aes"
|
||||||
|
contract C =
|
||||||
|
entrypoint filter_all_a(s: string) : string =
|
||||||
|
String.from_list(List.filter((c : char) => c != 'a', String.to_list(s)))
|
||||||
|
```
|
||||||
|
will return a list with all `a`'s removed.
|
||||||
|
|
||||||
|
There are also convenience functions `split`, `concat`, `to_upper`,
|
||||||
|
`to_lower`, etc.
|
||||||
|
|
||||||
|
All String functions in FATEv2 operate on unicode code points.
|
||||||
|
- Operations for pairing-based cryptography has been added the operations
|
||||||
|
are in the standard library [BLS12_381](https://github.com/aeternity/aesophia/blob/master/docs/sophia_stdlib.md#bls12_381).
|
||||||
|
With these operations it is possible to do Zero Knowledge-proofs, etc.
|
||||||
|
The operations are for the BLS12-381 curve (as the name suggests).
|
||||||
|
- Calls to functions in other contracts (i.e. _remote calls_) can now be
|
||||||
|
[`protected`](https://github.com/aeternity/aesophia/blob/master/docs/sophia.md#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`).
|
||||||
|
|
||||||
|
If `protected = true` the result of the contract call is wrapped in an
|
||||||
|
`option`, and `Some(value)` indicates a succesful execution and `None`
|
||||||
|
indicates that the contract call failed. Note: any gas consumed until
|
||||||
|
the failure is still charged, but all side effects in the remote
|
||||||
|
contract are rolled back on failure.
|
||||||
|
- A new chain operation [`AENS.update`](https://github.com/aeternity/aesophia/blob/master/docs/sophia.md#aens-interface)
|
||||||
|
is supported.
|
||||||
|
- New chain exploring operations `AENS.lookup` and `Oracle.expiry` to
|
||||||
|
look up an AENS record and the expiry of an Oracle respectively, are added.
|
||||||
|
- Transaction introspection (`Auth.tx`) has been added. When a Generalized
|
||||||
|
account is authorized, the authorization function needs access to the
|
||||||
|
transaction (and the transaction hash) for the wrapped transaction. The
|
||||||
|
transaction and the transaction hash is available `Auth.tx`, it is only
|
||||||
|
available during authentication if invoked by a normal contract call
|
||||||
|
it returns `None`. Example:
|
||||||
|
```
|
||||||
|
switch(Auth.tx)
|
||||||
|
None => abort("Not in Auth context")
|
||||||
|
Some(tx0) =>
|
||||||
|
switch(tx0.tx)
|
||||||
|
Chain.SpendTx(_, amount, _) => amount > 400
|
||||||
|
Chain.ContractCallTx(_, _) => true
|
||||||
|
_ => false
|
||||||
|
```
|
||||||
|
- A debug mode is a added to the compiler. Right now its only use is to
|
||||||
|
turn off hermetization.
|
||||||
|
### Changed
|
||||||
|
- The function `Chain.block_hash(height)` is now (in FATEv2) defined for
|
||||||
|
the current height - this used to be an error.
|
||||||
|
- Standard library: Sort is optimized to do `mergesort` and a `contains`
|
||||||
|
function is added.
|
||||||
|
- Improved type errors and explicit errors for some syntax errors (empty code
|
||||||
|
blocks, etc.).
|
||||||
|
- Compiler optimization: The ACI is generated alongside bytecode. This means
|
||||||
|
that multiple compiler passes can be avoided.
|
||||||
|
- Compiler optimization: Improved parsing (less stack used when transpiled).
|
||||||
|
- A bug where constraints were handled out of order fixed.
|
||||||
|
- Fixed calldata decoding for singleton records.
|
||||||
|
- Improved the documentation w.r.t. signatures, especially stressing the fact that
|
||||||
|
the network ID is a part of what is signed.
|
||||||
|
### Removed
|
||||||
|
|
||||||
## [4.3.0]
|
## [4.3.0]
|
||||||
### Added
|
### Added
|
||||||
- Added documentation (moved from `protocol`)
|
- Added documentation (moved from `protocol`)
|
||||||
@@ -211,7 +303,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Simplify calldata creation - instead of passing a compiled contract, simply
|
- Simplify calldata creation - instead of passing a compiled contract, simply
|
||||||
pass a (stubbed) contract string.
|
pass a (stubbed) contract string.
|
||||||
|
|
||||||
[Unreleased]: https://github.com/aeternity/aesophia/compare/v4.3.0...HEAD
|
[Unreleased]: https://github.com/aeternity/aesophia/compare/v6.0.1...HEAD
|
||||||
|
[6.0.1]: https://github.com/aeternity/aesophia/compare/v6.0.0...v6.0.1
|
||||||
|
[6.0.0]: https://github.com/aeternity/aesophia/compare/v5.0.0...v6.0.0
|
||||||
|
[5.0.0]: https://github.com/aeternity/aesophia/compare/v4.3.0...v5.0.0
|
||||||
[4.3.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.3.0
|
[4.3.0]: https://github.com/aeternity/aesophia/compare/v4.2.0...v4.3.0
|
||||||
[4.2.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.2.0
|
[4.2.0]: https://github.com/aeternity/aesophia/compare/v4.1.0...v4.2.0
|
||||||
[4.1.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v4.1.0
|
[4.1.0]: https://github.com/aeternity/aesophia/compare/v4.0.0...v4.1.0
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ The compiler is currently being used three places
|
|||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
`aesophia` has a version that is only loosely connected to the version of the
|
Versioning should follow the [semantic versioning](https://semver.org/spec/v2.0.0) guidelines. Id est, given a version number MAJOR.MINOR.PATCH, increment the:
|
||||||
Aeternity node - in principle they will share the major version but not
|
|
||||||
minor/patch version. The `aesophia` compiler version MUST be bumped whenever
|
- MAJOR version when you make incompatible API changes
|
||||||
there is a change in how byte code is generated, but it MAY also be bumped upon
|
- MINOR version when you add functionality in a backwards compatible manner
|
||||||
API changes etc.
|
- PATCH version when you make backwards compatible bug fixes
|
||||||
|
|
||||||
|
|
||||||
## Interface Modules
|
## Interface Modules
|
||||||
|
|
||||||
|
|||||||
+173
-49
@@ -1,10 +1,23 @@
|
|||||||
<!-- IMPORTANT: REMEMBER TO UPDATE THE TABLE OF CONTENTS AFTER YOUR EDIT -->
|
<!-- IMPORTANT: REMEMBER TO UPDATE THE TABLE OF CONTENTS AFTER YOUR EDIT -->
|
||||||
|
|
||||||
|
# The Sophia Language
|
||||||
|
An Æternity BlockChain Language
|
||||||
|
|
||||||
|
The Sophia is a language in the ML family. It is strongly typed and has
|
||||||
|
restricted mutable state.
|
||||||
|
|
||||||
|
Sophia is customized for smart contracts, which can be published
|
||||||
|
to a blockchain (the Æternity BlockChain). Thus some features of conventional
|
||||||
|
languages, such as floating point arithmetic, are not present in Sophia, and
|
||||||
|
some blockchain specific primitives, constructions and types have been added.
|
||||||
|
|
||||||
**Table of Contents**
|
**Table of Contents**
|
||||||
|
|
||||||
- [Language Features](#language-features)
|
- [Language Features](#language-features)
|
||||||
- [Contracts](#contracts)
|
- [Contracts](#contracts)
|
||||||
- [Calling other contracts](#calling-other-contracts)
|
- [Calling other contracts](#calling-other-contracts)
|
||||||
|
- [Protected contract calls](#protected-contract-calls)
|
||||||
|
- [Contract factories and child contracts](#contract-factories-and-child-contracts)
|
||||||
- [Mutable state](#mutable-state)
|
- [Mutable state](#mutable-state)
|
||||||
- [Stateful functions](#stateful-functions)
|
- [Stateful functions](#stateful-functions)
|
||||||
- [Payable](#payable)
|
- [Payable](#payable)
|
||||||
@@ -26,6 +39,7 @@
|
|||||||
- [Updating a value](#updating-a-value)
|
- [Updating a value](#updating-a-value)
|
||||||
- [Map implementation](#map-implementation)
|
- [Map implementation](#map-implementation)
|
||||||
- [Strings](#strings)
|
- [Strings](#strings)
|
||||||
|
- [Chars](#chars)
|
||||||
- [Byte arrays](#byte-arrays)
|
- [Byte arrays](#byte-arrays)
|
||||||
- [Cryptographic builins](#cryptographic-builins)
|
- [Cryptographic builins](#cryptographic-builins)
|
||||||
- [AEVM note](#aevm-note)
|
- [AEVM note](#aevm-note)
|
||||||
@@ -34,6 +48,7 @@
|
|||||||
- [Example](#example)
|
- [Example](#example)
|
||||||
- [Sanity checks](#sanity-checks)
|
- [Sanity checks](#sanity-checks)
|
||||||
- [AENS interface](#aens-interface)
|
- [AENS interface](#aens-interface)
|
||||||
|
- [Example](#example)
|
||||||
- [Events](#events)
|
- [Events](#events)
|
||||||
- [Argument order](#argument-order)
|
- [Argument order](#argument-order)
|
||||||
- [Compiler pragmas](#compiler-pragmas)
|
- [Compiler pragmas](#compiler-pragmas)
|
||||||
@@ -46,24 +61,14 @@
|
|||||||
- [Layout blocks](#layout-blocks)
|
- [Layout blocks](#layout-blocks)
|
||||||
- [Notation](#notation)
|
- [Notation](#notation)
|
||||||
- [Declarations](#declarations)
|
- [Declarations](#declarations)
|
||||||
- [Types](#types-1)
|
- [Types](#types)
|
||||||
- [Statements](#statements)
|
- [Statements](#statements)
|
||||||
- [Expressions](#expressions)
|
- [Expressions](#expressions)
|
||||||
- [Operators types](#operators-types)
|
- [Operators types](#operators-types)
|
||||||
- [Operator precendences](#operator-precendences)
|
- [Operator precendences](#operator-precendences)
|
||||||
- [Examples](#examples)
|
- [Examples](#examples)
|
||||||
|
- [Delegation signature](#delegation-signature)
|
||||||
|
|
||||||
|
|
||||||
## The Sophia Language
|
|
||||||
An Æternity BlockChain Language
|
|
||||||
|
|
||||||
The Sophia is a language in the ML family. It is strongly typed and has
|
|
||||||
restricted mutable state.
|
|
||||||
|
|
||||||
Sophia is customized for smart contracts, which can be published
|
|
||||||
to a blockchain (the Æternity BlockChain). Thus some features of conventional
|
|
||||||
languages, such as floating point arithmetic, are not present in Sophia, and
|
|
||||||
some blockchain specific primitives, constructions and types have been added.
|
|
||||||
## Language Features
|
## Language Features
|
||||||
### Contracts
|
### Contracts
|
||||||
|
|
||||||
@@ -89,16 +94,16 @@ 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
|
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,
|
of a number of type definitions and entrypoint declarations. For instance,
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
// A contract type
|
// A contract type
|
||||||
contract VotingType =
|
contract interface VotingType =
|
||||||
entrypoint vote : string => unit
|
entrypoint vote : string => unit
|
||||||
```
|
```
|
||||||
|
|
||||||
Now given contract address of type `VotingType` you can call the `vote`
|
Now given contract address of type `VotingType` you can call the `vote`
|
||||||
entrypoint of that contract:
|
entrypoint of that contract:
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
contract VoteTwice =
|
contract VoteTwice =
|
||||||
entrypoint voteTwice(v : VotingType, alt : string) =
|
entrypoint voteTwice(v : VotingType, alt : string) =
|
||||||
v.vote(alt)
|
v.vote(alt)
|
||||||
@@ -109,7 +114,7 @@ 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
|
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:
|
the defaults are no gas limit and no tokens. Suppose there is a fee for voting:
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
entrypoint voteTwice(v : VotingType, fee : int, alt : string) =
|
entrypoint voteTwice(v : VotingType, fee : int, alt : string) =
|
||||||
v.vote(value = fee, alt)
|
v.vote(value = fee, alt)
|
||||||
v.vote(value = fee, alt)
|
v.vote(value = fee, alt)
|
||||||
@@ -131,15 +136,77 @@ 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)
|
`address : address`. For instance, to send tokens to the voting contract (given that it is payable)
|
||||||
without calling it you can write
|
without calling it you can write
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
entrypoint pay(v : VotingType, amount : int) =
|
entrypoint pay(v : VotingType, amount : int) =
|
||||||
Chain.spend(v.address, amount)
|
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
|
||||||
|
|
||||||
|
```
|
||||||
|
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
|
### Mutable state
|
||||||
|
|
||||||
Sophia does not have arbitrary mutable state, but only a limited form of
|
Sophia does not have arbitrary mutable state, but only a limited form of state
|
||||||
state associated with each contract instance.
|
associated with each contract instance.
|
||||||
|
|
||||||
- Each contract defines a type `state` encapsulating its mutable state.
|
- Each contract defines a type `state` encapsulating its mutable state.
|
||||||
The type `state` defaults to the `unit`.
|
The type `state` defaults to the `unit`.
|
||||||
@@ -165,7 +232,7 @@ Top-level functions and entrypoints must be annotated with the
|
|||||||
`stateful` keyword to be allowed to affect the state of the running contract.
|
`stateful` keyword to be allowed to affect the state of the running contract.
|
||||||
For instance,
|
For instance,
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
stateful entrypoint set_state(s : state) =
|
stateful entrypoint set_state(s : state) =
|
||||||
put(s)
|
put(s)
|
||||||
```
|
```
|
||||||
@@ -184,6 +251,7 @@ Without the `stateful` annotation the compiler does not allow the call to
|
|||||||
- `AENS.claim`
|
- `AENS.claim`
|
||||||
- `AENS.transfer`
|
- `AENS.transfer`
|
||||||
- `AENS.revoke`
|
- `AENS.revoke`
|
||||||
|
- `AENS.update`
|
||||||
* Call a `stateful` function in the current contract
|
* Call a `stateful` function in the current contract
|
||||||
* Call another contract with a non-zero `value` argument.
|
* Call another contract with a non-zero `value` argument.
|
||||||
|
|
||||||
@@ -201,7 +269,7 @@ 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
|
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`:
|
contract shall be able to receive funds in this way it has to be declared `payable`:
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
// A payable contract
|
// A payable contract
|
||||||
payable contract ExampleContract =
|
payable contract ExampleContract =
|
||||||
stateful entrypoint do_stuff() = ...
|
stateful entrypoint do_stuff() = ...
|
||||||
@@ -217,7 +285,7 @@ A contract entrypoint is by default *not* payable. Any call to such a function
|
|||||||
that has a non-zero `value` will fail. Contract entrypoints that should be called
|
that has a non-zero `value` will fail. Contract entrypoints that should be called
|
||||||
with a non-zero value should be declared `payable`.
|
with a non-zero value should be declared `payable`.
|
||||||
|
|
||||||
```javascript
|
```sophia
|
||||||
payable stateful entrypoint buy(to : address) =
|
payable stateful entrypoint buy(to : address) =
|
||||||
if(Call.value > 42)
|
if(Call.value > 42)
|
||||||
transfer_item(to)
|
transfer_item(to)
|
||||||
@@ -378,7 +446,7 @@ corresponding integer, so setting very high bits can be expensive).
|
|||||||
Type aliases can be introduced with the `type` keyword and can be
|
Type aliases can be introduced with the `type` keyword and can be
|
||||||
parameterized. For instance
|
parameterized. For instance
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
type number = int
|
type number = int
|
||||||
type string_map('a) = map(string, 'a)
|
type string_map('a) = map(string, 'a)
|
||||||
```
|
```
|
||||||
@@ -398,7 +466,7 @@ 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:
|
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) =
|
function get_left(x : one_or_both('a, 'b)) : option('a) =
|
||||||
switch(x)
|
switch(x)
|
||||||
Left(x) => Some(x)
|
Left(x) => Some(x)
|
||||||
@@ -407,7 +475,7 @@ function get_left(x : one_or_both('a, 'b)) : option('a) =
|
|||||||
```
|
```
|
||||||
|
|
||||||
or directly in the left-hand side:
|
or directly in the left-hand side:
|
||||||
```
|
```sophia
|
||||||
function
|
function
|
||||||
get_left : one_or_both('a, 'b) => option('a)
|
get_left : one_or_both('a, 'b) => option('a)
|
||||||
get_left(Left(x)) = Some(x)
|
get_left(Left(x)) = Some(x)
|
||||||
@@ -425,7 +493,7 @@ 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
|
type. The type of lists with elements of type `'e` is written
|
||||||
`list('e)`. For example we can have the following lists:
|
`list('e)`. For example we can have the following lists:
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
[1, 33, 2, 666] : list(int)
|
[1, 33, 2, 666] : list(int)
|
||||||
[(1, "aaa"), (10, "jjj"), (666, "the beast")] : list(int * string)
|
[(1, "aaa"), (10, "jjj"), (666, "the beast")] : list(int * string)
|
||||||
[{[1] = "aaa", [10] = "jjj"}, {[5] = "eee", [666] = "the beast"}] : list(map(int, string))
|
[{[1] = "aaa", [10] = "jjj"}, {[5] = "eee", [666] = "the beast"}] : list(map(int, string))
|
||||||
@@ -439,13 +507,13 @@ and returns the resulting list. So concatenating two lists
|
|||||||
|
|
||||||
Sophia supports list comprehensions known from languages like Python, Haskell or Erlang.
|
Sophia supports list comprehensions known from languages like Python, Haskell or Erlang.
|
||||||
Example syntax:
|
Example syntax:
|
||||||
```
|
```sophia
|
||||||
[x + y | x <- [1,2,3,4,5], let k = x*x, if (k > 5), y <- [k, k+1, k+2]]
|
[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]
|
// yields [12,13,14,20,21,22,30,31,32]
|
||||||
```
|
```
|
||||||
|
|
||||||
Lists can be constructed using the range syntax using special `..` operator:
|
Lists can be constructed using the range syntax using special `..` operator:
|
||||||
```
|
```sophia
|
||||||
[1..4] == [1,2,3,4]
|
[1..4] == [1,2,3,4]
|
||||||
```
|
```
|
||||||
The ranges are always ascending and have step equal to 1.
|
The ranges are always ascending and have step equal to 1.
|
||||||
@@ -457,7 +525,7 @@ Please refer to the [standard library](sophia_stdlib.md#List) for the predefined
|
|||||||
|
|
||||||
A Sophia record type is given by a fixed set of fields with associated,
|
A Sophia record type is given by a fixed set of fields with associated,
|
||||||
possibly different, types. For instance
|
possibly different, types. For instance
|
||||||
```
|
```sophia
|
||||||
record account = { name : string,
|
record account = { name : string,
|
||||||
balance : int,
|
balance : int,
|
||||||
history : list(transaction) }
|
history : list(transaction) }
|
||||||
@@ -474,12 +542,12 @@ Please refer to the [standard library](sophia_stdlib.md#Map) for the predefined
|
|||||||
|
|
||||||
A value of record type is constructed by giving a value for each of the fields.
|
A value of record type is constructed by giving a value for each of the fields.
|
||||||
For the example above,
|
For the example above,
|
||||||
```
|
```sophia
|
||||||
function new_account(name) =
|
function new_account(name) =
|
||||||
{name = name, balance = 0, history = []}
|
{name = name, balance = 0, history = []}
|
||||||
```
|
```
|
||||||
Maps are constructed similarly, with keys enclosed in square brackets
|
Maps are constructed similarly, with keys enclosed in square brackets
|
||||||
```
|
```sophia
|
||||||
function example_map() : map(string, int) =
|
function example_map() : map(string, int) =
|
||||||
{["key1"] = 1, ["key2"] = 2}
|
{["key1"] = 1, ["key2"] = 2}
|
||||||
```
|
```
|
||||||
@@ -488,7 +556,7 @@ The empty map is written `{}`.
|
|||||||
#### Accessing values
|
#### Accessing values
|
||||||
|
|
||||||
Record fields access is written `r.f` and map lookup `m[k]`. For instance,
|
Record fields access is written `r.f` and map lookup `m[k]`. For instance,
|
||||||
```
|
```sophia
|
||||||
function get_balance(a : address, accounts : map(address, account)) =
|
function get_balance(a : address, accounts : map(address, account)) =
|
||||||
accounts[a].balance
|
accounts[a].balance
|
||||||
```
|
```
|
||||||
@@ -513,14 +581,14 @@ in the map or execution fails, but a default value can be provided:
|
|||||||
`k` is not in the map.
|
`k` is not in the map.
|
||||||
|
|
||||||
Updates can be nested:
|
Updates can be nested:
|
||||||
```
|
```sophia
|
||||||
function clear_history(a : address, accounts : map(address, account)) : map(address, account) =
|
function clear_history(a : address, accounts : map(address, account)) : map(address, account) =
|
||||||
accounts{ [a].history = [] }
|
accounts{ [a].history = [] }
|
||||||
```
|
```
|
||||||
This is equivalent to `accounts{ [a] @ acc = acc{ history = [] } }` and thus
|
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
|
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`):
|
an account if `a` is not in the map you can write (given a function `empty_account`):
|
||||||
```
|
```sophia
|
||||||
accounts{ [a = empty_account()].history = [] }
|
accounts{ [a = empty_account()].history = [] }
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -538,7 +606,16 @@ Strings can be compared for equality (`==`, `!=`), used as keys in maps and
|
|||||||
records, and used in builtin functions `String.length`, `String.concat` and
|
records, and used in builtin functions `String.length`, `String.concat` and
|
||||||
the hash functions described below.
|
the hash functions described below.
|
||||||
|
|
||||||
Please refer to the `Map` [library documentation](sophia_stdlib.md#String).
|
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
|
||||||
|
|
||||||
@@ -565,11 +642,10 @@ string`, `String.sha3(s)` and `Crypto.sha3(s)` will give different results on AE
|
|||||||
### Authorization interface
|
### Authorization interface
|
||||||
|
|
||||||
When a Generalized account is authorized, the authorization function needs
|
When a Generalized account is authorized, the authorization function needs
|
||||||
access to the transaction hash for the wrapped transaction. (A `GAMetaTx`
|
access to the transaction and the transaction hash for the wrapped transaction. (A `GAMetaTx`
|
||||||
wrapping a transaction.) The transaction hash is available in the primitive
|
wrapping a transaction.) The transaction and the transaction hash is available in the primitive
|
||||||
`Auth.tx_hash`, it is *only* available during authentication if invoked by a
|
`Auth.tx` and `Auth.tx_hash` respectively, they are *only* available during authentication if invoked by a
|
||||||
normal contract call it returns `None`.
|
normal contract call they return `None`.
|
||||||
|
|
||||||
|
|
||||||
### Oracle interface
|
### Oracle interface
|
||||||
You can attach an oracle to the current contract and you can interact with oracles
|
You can attach an oracle to the current contract and you can interact with oracles
|
||||||
@@ -583,7 +659,7 @@ For a functionality documentation refer to the [standard library](sophia_stdlib.
|
|||||||
#### Example
|
#### Example
|
||||||
|
|
||||||
Example for an oracle answering questions of type `string` with answers of type `int`:
|
Example for an oracle answering questions of type `string` with answers of type `int`:
|
||||||
```
|
```sophia
|
||||||
contract Oracles =
|
contract Oracles =
|
||||||
|
|
||||||
stateful entrypoint registerOracle(acct : address,
|
stateful entrypoint registerOracle(acct : address,
|
||||||
@@ -645,6 +721,46 @@ Contracts can interact with the
|
|||||||
[Aeternity Naming System](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
[Aeternity Naming System](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
||||||
For this purpose the [AENS](sophia_stdlib.md#AENS) library was exposed.
|
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
|
### Events
|
||||||
|
|
||||||
@@ -684,17 +800,17 @@ The fields can appear in any order.
|
|||||||
Events are emitted by using the `Chain.event` function. The following function
|
Events are emitted by using the `Chain.event` function. The following function
|
||||||
will emit one Event of each kind in the example.
|
will emit one Event of each kind in the example.
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
entrypoint emit_events() : () =
|
entrypoint emit_events() : () =
|
||||||
Chain.event(TheFirstEvent(42))
|
Chain.event(Event1(42, 34, "foo"))
|
||||||
Chain.event(AnotherEvent(Contract.address, "This is not indexed"))
|
Chain.event(Event2("This is not indexed", Contract.address))
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Argument order
|
#### Argument order
|
||||||
|
|
||||||
It is only possible to have one (1) `string` parameter in the event, but it can
|
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.
|
be placed in any position (and its value will end up in the `data` field), i.e.
|
||||||
```
|
```sophia
|
||||||
AnotherEvent(string, indexed address)
|
AnotherEvent(string, indexed address)
|
||||||
|
|
||||||
...
|
...
|
||||||
@@ -753,7 +869,7 @@ and `*/` and can be nested.
|
|||||||
|
|
||||||
```
|
```
|
||||||
contract elif else entrypoint false function if import include let mod namespace
|
contract elif else entrypoint false function if import include let mod namespace
|
||||||
private payable stateful switch true type record datatype
|
private payable stateful switch true type record datatype main interface
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Tokens
|
#### Tokens
|
||||||
@@ -857,7 +973,7 @@ Args ::= '(' Sep(Pattern, ',') ')'
|
|||||||
Contract declarations must appear at the top-level.
|
Contract declarations must appear at the top-level.
|
||||||
|
|
||||||
For example,
|
For example,
|
||||||
```
|
```sophia
|
||||||
contract Test =
|
contract Test =
|
||||||
type t = int
|
type t = int
|
||||||
entrypoint add (x : t, y : t) = x + y
|
entrypoint add (x : t, y : t) = x + y
|
||||||
@@ -1005,7 +1121,7 @@ In order of highest to lowest precedence.
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
```
|
```sophia
|
||||||
/*
|
/*
|
||||||
* A simple crowd-funding example
|
* A simple crowd-funding example
|
||||||
*/
|
*/
|
||||||
@@ -1069,3 +1185,11 @@ contract FundMe =
|
|||||||
amount = state.contributions[to]})
|
amount = state.contributions[to]})
|
||||||
put(state{ contributions @ c = Map.delete(to, c) })
|
put(state{ contributions @ c = Map.delete(to, c) })
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 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 Aeternity mainnet, etc.).
|
||||||
|
|||||||
+669
-263
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
|||||||
|
namespace BLS12_381 =
|
||||||
|
type fr = MCL_BLS12_381.fr
|
||||||
|
type fp = MCL_BLS12_381.fp
|
||||||
|
record fp2 = { x1 : fp, x2 : fp }
|
||||||
|
record g1 = { x : fp, y : fp, z : fp }
|
||||||
|
record g2 = { x : fp2, y : fp2, z : fp2 }
|
||||||
|
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 }
|
||||||
|
|
||||||
|
function pairing_check(xs : list(g1), ys : list(g2)) =
|
||||||
|
switch((xs, ys))
|
||||||
|
([], []) => true
|
||||||
|
(x :: xs, y :: ys) => pairing_check_(pairing(x, y), xs, ys)
|
||||||
|
|
||||||
|
function pairing_check_(acc : gt, xs : list(g1), ys : list(g2)) =
|
||||||
|
switch((xs, ys))
|
||||||
|
([], []) => gt_is_one(acc)
|
||||||
|
(x :: xs, y :: ys) =>
|
||||||
|
pairing_check_(gt_mul(acc, pairing(x, y)), xs, ys)
|
||||||
|
|
||||||
|
function int_to_fr(x : int) = MCL_BLS12_381.int_to_fr(x)
|
||||||
|
function int_to_fp(x : int) = MCL_BLS12_381.int_to_fp(x)
|
||||||
|
function fr_to_int(x : fr) = MCL_BLS12_381.fr_to_int(x)
|
||||||
|
function fp_to_int(x : fp) = MCL_BLS12_381.fp_to_int(x)
|
||||||
|
|
||||||
|
function mk_g1(x : int, y : int, z : int) : g1 =
|
||||||
|
{ x = int_to_fp(x), y = int_to_fp(y), z = int_to_fp(z) }
|
||||||
|
|
||||||
|
function mk_g2(x1 : int, x2 : int, y1 : int, y2 : int, z1 : int, z2 : int) : g2 =
|
||||||
|
{ x = {x1 = int_to_fp(x1), x2 = int_to_fp(x2)},
|
||||||
|
y = {x1 = int_to_fp(y1), x2 = int_to_fp(y2)},
|
||||||
|
z = {x1 = int_to_fp(z1), x2 = int_to_fp(z2)} }
|
||||||
|
|
||||||
|
function pack_g1(t) = switch(t)
|
||||||
|
(x, y, z) => {x = x, y = y, z = z} : g1
|
||||||
|
function pack_g2(t) = switch(t)
|
||||||
|
((x1, x2), (y1, y2), (z1, z2)) =>
|
||||||
|
{x = {x1 = x1, x2 = x2}, y = {x1 = y1, x2 = y2}, z = {x1 = z1, x2 = z2}} : g2
|
||||||
|
function pack_gt(t) = switch(t)
|
||||||
|
(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12) =>
|
||||||
|
{x1 = x1, x2 = x2, x3 = x3, x4 = x4, x5 = x5, x6 = x6,
|
||||||
|
x7 = x7, x8 = x8, x9 = x9, x10 = x10, x11 = x11, x12 = x12} : gt
|
||||||
|
|
||||||
|
function g1_neg(p : g1) = pack_g1(MCL_BLS12_381.g1_neg((p.x, p.y, p.z)))
|
||||||
|
function g1_norm(p : g1) = pack_g1(MCL_BLS12_381.g1_norm((p.x, p.y, p.z)))
|
||||||
|
function g1_valid(p : g1) = MCL_BLS12_381.g1_valid((p.x, p.y, p.z))
|
||||||
|
function g1_is_zero(p : g1) = MCL_BLS12_381.g1_is_zero((p.x, p.y, p.z))
|
||||||
|
function g1_add(p : g1, q : g1) = pack_g1(MCL_BLS12_381.g1_add((p.x, p.y, p.z), (q.x, q.y, q.z)))
|
||||||
|
function g1_mul(k : fr, p : g1) = pack_g1(MCL_BLS12_381.g1_mul(k, (p.x, p.y, p.z)))
|
||||||
|
|
||||||
|
function g2_neg(p : g2) = pack_g2(MCL_BLS12_381.g2_neg(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
|
||||||
|
function g2_norm(p : g2) = pack_g2(MCL_BLS12_381.g2_norm(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
|
||||||
|
function g2_valid(p : g2) = MCL_BLS12_381.g2_valid(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))
|
||||||
|
function g2_is_zero(p : g2) = MCL_BLS12_381.g2_is_zero(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)))
|
||||||
|
function g2_add(p : g2, q : g2) = pack_g2(MCL_BLS12_381.g2_add(((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2)),
|
||||||
|
((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
|
||||||
|
function g2_mul(k : fr, p : g2) = pack_g2(MCL_BLS12_381.g2_mul(k, ((p.x.x1, p.x.x2), (p.y.x1, p.y.x2), (p.z.x1, p.z.x2))))
|
||||||
|
|
||||||
|
function gt_inv(p : gt) = pack_gt(MCL_BLS12_381.gt_inv((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12)))
|
||||||
|
function gt_add(p : gt, q : gt) = pack_gt(MCL_BLS12_381.gt_add((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12),
|
||||||
|
(q.x1, q.x2, q.x3, q.x4, q.x5, q.x6, q.x7, q.x8, q.x9, q.x10, q.x11, q.x12)))
|
||||||
|
function gt_mul(p : gt, q : gt) = pack_gt(MCL_BLS12_381.gt_mul((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12),
|
||||||
|
(q.x1, q.x2, q.x3, q.x4, q.x5, q.x6, q.x7, q.x8, q.x9, q.x10, q.x11, q.x12)))
|
||||||
|
function gt_pow(p : gt, k : fr) = pack_gt(MCL_BLS12_381.gt_pow((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12), k))
|
||||||
|
function gt_is_one(p : gt) = MCL_BLS12_381.gt_is_one((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12))
|
||||||
|
function pairing(p : g1, q : g2) = pack_gt(MCL_BLS12_381.pairing((p.x, p.y, p.z), ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
|
||||||
|
function miller_loop(p : g1, q : g2) = pack_gt(MCL_BLS12_381.miller_loop((p.x, p.y, p.z), ((q.x.x1, q.x.x2), (q.y.x1, q.y.x2), (q.z.x1, q.z.x2))))
|
||||||
|
function final_exp(p : gt) = pack_gt(MCL_BLS12_381.final_exp((p.x1, p.x2, p.x3, p.x4, p.x5, p.x6, p.x7, p.x8, p.x9, p.x10, p.x11, p.x12)))
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
include "String.aes"
|
||||||
|
|
||||||
namespace Frac =
|
namespace Frac =
|
||||||
|
|
||||||
private function gcd(a : int, b : int) =
|
private function gcd(a : int, b : int) =
|
||||||
|
|||||||
+155
-91
@@ -19,6 +19,16 @@ namespace List =
|
|||||||
[x] => Some(x)
|
[x] => Some(x)
|
||||||
_::t => last(t)
|
_::t => last(t)
|
||||||
|
|
||||||
|
function drop_last(l : list('a)) : option(list('a)) = switch(l)
|
||||||
|
[] => None
|
||||||
|
_ => Some(drop_last_unsafe(l))
|
||||||
|
|
||||||
|
function drop_last_unsafe(l : list('a)) : list('a) = switch(l)
|
||||||
|
[_] => []
|
||||||
|
h::t => h::drop_last_unsafe(t)
|
||||||
|
[] => abort("drop_last_unsafe: list empty")
|
||||||
|
|
||||||
|
|
||||||
function contains(e : 'a, l : list('a)) = switch(l)
|
function contains(e : 'a, l : list('a)) = switch(l)
|
||||||
[] => false
|
[] => false
|
||||||
h::t => h == e || contains(e, t)
|
h::t => h == e || contains(e, t)
|
||||||
@@ -32,14 +42,15 @@ namespace List =
|
|||||||
|
|
||||||
/** Returns list of all indices of elements from `l` that fulfill the predicate `p`.
|
/** Returns list of all indices of elements from `l` that fulfill the predicate `p`.
|
||||||
*/
|
*/
|
||||||
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, [])
|
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0)
|
||||||
private function find_indices_( p : 'a => bool
|
private function find_indices_( p : 'a => bool
|
||||||
, l : list('a)
|
, l : list('a)
|
||||||
, n : int
|
, n : int
|
||||||
, acc : list(int)
|
|
||||||
) : list(int) = switch(l)
|
) : list(int) = switch(l)
|
||||||
[] => reverse(acc)
|
[] => []
|
||||||
h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc)
|
h::t =>
|
||||||
|
let rest = find_indices_(p, t, n+1)
|
||||||
|
if(p(h)) n::rest else rest
|
||||||
|
|
||||||
function nth(n : int, l : list('a)) : option('a) =
|
function nth(n : int, l : list('a)) : option('a) =
|
||||||
switch(l)
|
switch(l)
|
||||||
@@ -68,44 +79,44 @@ namespace List =
|
|||||||
* `a` and `b` jumping by given `step`. Includes `a` and takes
|
* `a` and `b` jumping by given `step`. Includes `a` and takes
|
||||||
* `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0.
|
* `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0.
|
||||||
*/
|
*/
|
||||||
function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, [])
|
function from_to_step(a : int, b : int, s : int) : list(int) =
|
||||||
private function from_to_step_(a, b, s, acc) =
|
from_to_step_(a, b - (b-a) mod s, s, [])
|
||||||
if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc)
|
private function from_to_step_(a : int, b : int, s : int, acc : list(int)) : list(int) =
|
||||||
|
if(b < a) acc
|
||||||
|
else from_to_step_(a, b - s, s, b::acc)
|
||||||
|
|
||||||
|
|
||||||
/** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow
|
/** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow
|
||||||
*/
|
*/
|
||||||
function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
|
function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, [])
|
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l)
|
||||||
private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
|
private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
switch(l)
|
switch(l)
|
||||||
[] => abort("replace_at overflow")
|
[] => abort("replace_at overflow")
|
||||||
h::t => if (n == 0) reverse(e::acc) ++ t
|
h::t => if (n == 0) e::t
|
||||||
else replace_at_(n-1, e, t, h::acc)
|
else h::replace_at_(n-1, e, t)
|
||||||
|
|
||||||
/** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow
|
/** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow
|
||||||
*/
|
*/
|
||||||
function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
|
function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, [])
|
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l)
|
||||||
private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
|
private function insert_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if (n == 0) reverse(e::acc) ++ l
|
if (n == 0) e::l
|
||||||
else switch(l)
|
else switch(l)
|
||||||
[] => abort("insert_at overflow")
|
[] => abort("insert_at overflow")
|
||||||
h::t => insert_at_(n-1, e, t, h::acc)
|
h::t => h::insert_at_(n-1, e, t)
|
||||||
|
|
||||||
/** Assuming that cmp represents `<` comparison, inserts `x` before
|
/** Assuming that cmp represents `<` comparison, inserts `x` before
|
||||||
* the first element in the list `l` which is greater than it
|
* the first element in the list `l` which is greater than it
|
||||||
*/
|
*/
|
||||||
function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
|
function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
|
||||||
insert_by_(cmp, x, l, [])
|
|
||||||
private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) =
|
|
||||||
switch(l)
|
switch(l)
|
||||||
[] => reverse(x::acc)
|
[] => [x]
|
||||||
h::t =>
|
h::t =>
|
||||||
if(cmp(x, h)) // x < h
|
if(cmp(x, h)) // x < h
|
||||||
reverse(acc) ++ (x::l)
|
x::l
|
||||||
else
|
else
|
||||||
insert_by_(cmp, x, t, h::acc)
|
h::insert_by(cmp, x, t)
|
||||||
|
|
||||||
|
|
||||||
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
|
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
|
||||||
@@ -123,49 +134,52 @@ namespace List =
|
|||||||
f(e)
|
f(e)
|
||||||
foreach(l', f)
|
foreach(l', f)
|
||||||
|
|
||||||
function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l)
|
function reverse(l : list('a)) : list('a) = reverse_(l, [])
|
||||||
|
private function reverse_(l : list('a), acc : list('a)) : list('a) = switch(l)
|
||||||
|
[] => acc
|
||||||
|
h::t => reverse_(t, h::acc)
|
||||||
|
|
||||||
function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, [])
|
function map(f : 'a => 'b, l : list('a)) : list('b) = switch(l)
|
||||||
private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
h::t => f(h)::map(f, t)
|
||||||
h::t => map_(f, t, f(h)::acc)
|
|
||||||
|
|
||||||
/** Effectively composition of `map` and `flatten`
|
/** Effectively composition of `map` and `flatten`
|
||||||
*/
|
*/
|
||||||
function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
|
function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
|
||||||
ListInternal.flat_map(f, l)
|
ListInternal.flat_map(f, l)
|
||||||
|
|
||||||
function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, [])
|
function filter(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||||
private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
h::t =>
|
||||||
h::t => filter_(p, t, if(p(h)) h::acc else acc)
|
let rest = filter(p, t)
|
||||||
|
if(p(h)) h::rest else rest
|
||||||
|
|
||||||
/** Take `n` first elements
|
/** Take up to `n` first elements
|
||||||
*/
|
*/
|
||||||
function take(n : int, l : list('a)) : list('a) =
|
function take(n : int, l : list('a)) : list('a) =
|
||||||
if(n < 0) abort("Take negative number of elements") else take_(n, l, [])
|
if(n < 0) abort("Take negative number of elements") else take_(n, l)
|
||||||
private function take_(n : int, l : list('a), acc : list('a)) : list('a) =
|
private function take_(n : int, l : list('a)) : list('a) =
|
||||||
if(n == 0) reverse(acc)
|
if(n == 0) []
|
||||||
else switch(l)
|
|
||||||
[] => reverse(acc)
|
|
||||||
h::t => take_(n-1, t, h::acc)
|
|
||||||
|
|
||||||
/** Drop `n` first elements
|
|
||||||
*/
|
|
||||||
function drop(n : int, l : list('a)) : list('a) =
|
|
||||||
if(n < 0) abort("Drop negative number of elements")
|
|
||||||
elif (n == 0) l
|
|
||||||
else switch(l)
|
else switch(l)
|
||||||
[] => []
|
[] => []
|
||||||
h::t => drop(n-1, t)
|
h::t => h::take_(n-1, t)
|
||||||
|
|
||||||
|
/** Drop up to `n` first elements
|
||||||
|
*/
|
||||||
|
function drop(n : int, l : list('a)) : list('a) =
|
||||||
|
if(n < 0) abort("Drop negative number of elements") else drop_(n, l)
|
||||||
|
private function drop_(n : int, l : list('a)) : list('a) =
|
||||||
|
if (n == 0) l
|
||||||
|
else switch(l)
|
||||||
|
[] => []
|
||||||
|
h::t => drop_(n-1, t)
|
||||||
|
|
||||||
/** Get the longest prefix of a list in which every element
|
/** Get the longest prefix of a list in which every element
|
||||||
* matches predicate `p`
|
* matches predicate `p`
|
||||||
*/
|
*/
|
||||||
function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, [])
|
function take_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||||
private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
h::t => if(p(h)) h::take_while(p, t) else []
|
||||||
h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc)
|
|
||||||
|
|
||||||
/** Drop elements from `l` until `p` holds
|
/** Drop elements from `l` until `p` holds
|
||||||
*/
|
*/
|
||||||
@@ -176,18 +190,15 @@ namespace List =
|
|||||||
/** Splits list into two lists of elements that respectively
|
/** Splits list into two lists of elements that respectively
|
||||||
* match and don't match predicate `p`
|
* match and don't match predicate `p`
|
||||||
*/
|
*/
|
||||||
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], [])
|
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = switch(l)
|
||||||
private function partition_( p : 'a => bool
|
[] => ([], [])
|
||||||
, l : list('a)
|
h::t =>
|
||||||
, acc_t : list('a)
|
let (l, r) = partition(p, t)
|
||||||
, acc_f : list('a)
|
if(p(h)) (h::l, r) else (l, h::r)
|
||||||
) : (list('a) * list('a)) = switch(l)
|
|
||||||
[] => (reverse(acc_t), reverse(acc_f))
|
|
||||||
h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f)
|
|
||||||
|
|
||||||
/** Flattens list of lists into a single list
|
function flatten(l : list(list('a))) : list('a) = switch(l)
|
||||||
*/
|
[] => []
|
||||||
function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll)
|
h::t => h ++ flatten(t)
|
||||||
|
|
||||||
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
||||||
[] => true
|
[] => true
|
||||||
@@ -197,55 +208,108 @@ namespace List =
|
|||||||
[] => false
|
[] => false
|
||||||
h::t => if(p(h)) true else any(p, t)
|
h::t => if(p(h)) true else any(p, t)
|
||||||
|
|
||||||
function sum(l : list(int)) : int = foldl ((a, b) => a + b, 0, l)
|
function sum(l : list(int)) : int = switch(l)
|
||||||
|
[] => 0
|
||||||
function product(l : list(int)) : int = foldl((a, b) => a * b, 1, l)
|
h::t => h + sum(t)
|
||||||
|
|
||||||
|
function product(l : list(int)) : int = switch(l)
|
||||||
|
[] => 1
|
||||||
|
h::t => h * sum(t)
|
||||||
|
|
||||||
/** Zips two list by applying bimapping function on respective elements.
|
/** Zips two list by applying bimapping function on respective elements.
|
||||||
* Drops longer tail.
|
* Drops the tail of the longer list.
|
||||||
*/
|
*/
|
||||||
function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, [])
|
private function zip_with( f : ('a, 'b) => 'c
|
||||||
private function zip_with_( f : ('a, 'b) => 'c
|
|
||||||
, l1 : list('a)
|
, l1 : list('a)
|
||||||
, l2 : list('b)
|
, l2 : list('b)
|
||||||
, acc : list('c)
|
|
||||||
) : list('c) = switch ((l1, l2))
|
) : list('c) = switch ((l1, l2))
|
||||||
(h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc)
|
(h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2)
|
||||||
_ => reverse(acc)
|
_ => []
|
||||||
|
|
||||||
/** Zips two lists into list of pairs. Drops longer tail.
|
/** Zips two lists into list of pairs.
|
||||||
|
* Drops the tail of the longer list.
|
||||||
*/
|
*/
|
||||||
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
|
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
|
||||||
|
|
||||||
function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], [])
|
function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l)
|
||||||
private function unzip_( l : list('a * 'b)
|
[] => ([], [])
|
||||||
, acc_l : list('a)
|
(h1, h2)::t =>
|
||||||
, acc_r : list('b)
|
let (t1, t2) = unzip(t)
|
||||||
) : (list('a) * list('b)) = switch(l)
|
(h1::t1, h2::t2)
|
||||||
[] => (reverse(acc_l), reverse(acc_r))
|
|
||||||
(left, right)::t => unzip_(t, left::acc_l, right::acc_r)
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Improve?
|
/** Merges two sorted lists using `lt` comparator
|
||||||
function sort(lesser_cmp : ('a, 'a) => bool, l : list('a)) : list('a) = switch(l)
|
*/
|
||||||
[] => []
|
function
|
||||||
h::t => switch (partition((x) => lesser_cmp(x, h), t))
|
merge : (('a, 'a) => bool, list('a), list('a)) => list('a)
|
||||||
(lesser, bigger) => sort(lesser_cmp, lesser) ++ h::sort(lesser_cmp, bigger)
|
merge(lt, x::xs, y::ys) =
|
||||||
|
if(lt(x, y)) x::merge(lt, xs, y::ys)
|
||||||
|
else y::merge(lt, x::xs, ys)
|
||||||
|
merge(_, [], ys) = ys
|
||||||
|
merge(_, xs, []) = xs
|
||||||
|
|
||||||
|
|
||||||
|
/** Mergesort inspired by
|
||||||
|
* https://hackage.haskell.org/package/base-4.14.1.0/docs/src/Data.OldList.html#sort
|
||||||
|
*/
|
||||||
|
function
|
||||||
|
sort : (('a, 'a) => bool, list('a)) => list('a)
|
||||||
|
sort(_, []) = []
|
||||||
|
sort(lt, l) =
|
||||||
|
merge_all(lt, monotonic_subs(lt, l))
|
||||||
|
|
||||||
|
/** Splits list into compound increasing sublists
|
||||||
|
*/
|
||||||
|
private function
|
||||||
|
monotonic_subs : (('a, 'a) => bool, list('a)) => list(list('a))
|
||||||
|
monotonic_subs(lt, x::y::rest) =
|
||||||
|
if(lt(y, x)) desc(lt, y, [x], rest)
|
||||||
|
else asc(lt, y, [x], rest)
|
||||||
|
monotonic_subs(_, l) = [l]
|
||||||
|
|
||||||
|
/** Extracts the longest descending prefix and proceeds with monotonic split
|
||||||
|
*/
|
||||||
|
private function
|
||||||
|
desc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||||||
|
desc(lt, x, acc, h::t) =
|
||||||
|
if(lt(x, h)) (x::acc) :: monotonic_subs(lt, h::t)
|
||||||
|
else desc(lt, h, x::acc, t)
|
||||||
|
desc(_, x, acc, []) = [x::acc]
|
||||||
|
|
||||||
|
/** Extracts the longest ascending prefix and proceeds with monotonic split
|
||||||
|
*/
|
||||||
|
private function
|
||||||
|
asc : (('a, 'a) => bool, 'a, list('a), list('a)) => list(list('a))
|
||||||
|
asc(lt, x, acc, h::t) =
|
||||||
|
if(lt(h, x)) List.reverse(x::acc) :: monotonic_subs(lt, h::t)
|
||||||
|
else asc(lt, h, x::acc, t)
|
||||||
|
asc(_, x, acc, []) = [List.reverse(x::acc)]
|
||||||
|
|
||||||
|
/** Merges list of sorted lists
|
||||||
|
*/
|
||||||
|
private function
|
||||||
|
merge_all : (('a, 'a) => bool, list(list('a))) => list('a)
|
||||||
|
merge_all(_, [part]) = part
|
||||||
|
merge_all(lt, parts) = merge_all(lt, merge_pairs(lt, parts))
|
||||||
|
|
||||||
|
/** Single round of `merge_all` – pairs of lists in a list of list
|
||||||
|
*/
|
||||||
|
private function
|
||||||
|
merge_pairs : (('a, 'a) => bool, list(list('a))) => list(list('a))
|
||||||
|
merge_pairs(lt, x::y::rest) = merge(lt, x, y) :: merge_pairs(lt, rest)
|
||||||
|
merge_pairs(_, l) = l
|
||||||
|
|
||||||
|
|
||||||
/** Puts `delim` between every two members of the list
|
/** Puts `delim` between every two members of the list
|
||||||
*/
|
*/
|
||||||
function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, [])
|
function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l)
|
||||||
private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
[e] => [e]
|
||||||
[e] => reverse(e::acc)
|
h::t => h::delim::intersperse(delim, t)
|
||||||
h::t => intersperse_(delim, t, delim::h::acc)
|
|
||||||
|
|
||||||
|
|
||||||
/** Effectively a zip with an infinite sequence of natural numbers
|
/** Effectively a zip with an infinite sequence of natural numbers
|
||||||
*/
|
*/
|
||||||
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, [])
|
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0)
|
||||||
private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l)
|
private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l)
|
||||||
[] => reverse(acc)
|
[] => []
|
||||||
h::t => enumerate_(t, n + 1, (n, h)::acc)
|
h::t => (n, h)::enumerate_(t, n + 1)
|
||||||
|
|
||||||
+11
-11
@@ -69,20 +69,21 @@ namespace Option =
|
|||||||
/** Turns list of options into a list of elements that are under `Some`s.
|
/** Turns list of options into a list of elements that are under `Some`s.
|
||||||
* Safe.
|
* Safe.
|
||||||
*/
|
*/
|
||||||
function filter_options(l : list(option('a))) : list('a) = filter_options_(l, [])
|
function filter_options(l : list(option('a))) : list('a) = switch(l)
|
||||||
private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => List.reverse(acc)
|
None::t => filter_options(t)
|
||||||
None::t => filter_options_(t, acc)
|
Some(x)::t => x::filter_options(t)
|
||||||
Some(x)::t => filter_options_(t, x::acc)
|
|
||||||
|
|
||||||
/** Just like `filter_options` but requires all elements to be `Some` and returns
|
/** Just like `filter_options` but requires all elements to be `Some` and returns
|
||||||
* None if any of them is not
|
* None if any of them is not
|
||||||
*/
|
*/
|
||||||
function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, [])
|
function seq_options(l : list (option('a))) : option (list('a)) = switch(l)
|
||||||
private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l)
|
[] => Some([])
|
||||||
[] => Some(List.reverse(acc))
|
None::_ => None
|
||||||
None::t => None
|
Some(x)::t => switch(seq_options(t))
|
||||||
Some(x)::t => seq_options_(t, x::acc)
|
None => None
|
||||||
|
Some(st) => Some(x::st)
|
||||||
|
|
||||||
|
|
||||||
/** Choose `Some` out of two if possible
|
/** Choose `Some` out of two if possible
|
||||||
*/
|
*/
|
||||||
@@ -95,4 +96,3 @@ namespace Option =
|
|||||||
[] => None
|
[] => None
|
||||||
None::t => choose_first(t)
|
None::t => choose_first(t)
|
||||||
Some(x)::_ => Some(x)
|
Some(x)::_ => Some(x)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
include "List.aes"
|
||||||
|
namespace String =
|
||||||
|
// Computes the SHA3/Keccak hash of the string
|
||||||
|
function sha3(s : string) : hash = StringInternal.sha3(s)
|
||||||
|
// Computes the SHA256 hash of the string.
|
||||||
|
function sha256(s : string) : hash = StringInternal.sha256(s)
|
||||||
|
// Computes the Blake2B hash of the string.
|
||||||
|
function blake2b(s : string) : hash = StringInternal.blake2b(s)
|
||||||
|
|
||||||
|
// The length of a string - equivalent to List.lenght(to_list(s))
|
||||||
|
function length(s : string) : int = StringInternal.length(s)
|
||||||
|
// Concatenates `s1` and `s2`.
|
||||||
|
function concat(s1 : string, s2 : string) : string = StringInternal.concat(s1, s2)
|
||||||
|
// Concatenates a list of strings.
|
||||||
|
function
|
||||||
|
concats : (list(string)) => string
|
||||||
|
concats([]) = ""
|
||||||
|
concats(s :: ss) = List.foldl(StringInternal.concat, s, ss)
|
||||||
|
|
||||||
|
// Converts a `string` to a list of `char` - the code points are normalized, but
|
||||||
|
// composite characters are possibly converted to multiple `char`s.
|
||||||
|
function from_list(cs : list(char)) : string = StringInternal.from_list(cs)
|
||||||
|
// Converts a list of characters into a normalized UTF-8 string.
|
||||||
|
function to_list(s : string) : list(char) = StringInternal.to_list(s)
|
||||||
|
|
||||||
|
// Converts a string to lowercase.
|
||||||
|
function to_lower(s : string) = StringInternal.to_lower(s)
|
||||||
|
// Converts a string to uppercase.
|
||||||
|
function to_upper(s : string) = StringInternal.to_upper(s)
|
||||||
|
|
||||||
|
// Splits a string at (zero-based) index `ix`.
|
||||||
|
function split(i : int, s : string) : string * string =
|
||||||
|
let cs = StringInternal.to_list(s)
|
||||||
|
(StringInternal.from_list(List.take(i, cs)), StringInternal.from_list(List.drop(i, cs)))
|
||||||
|
|
||||||
|
// Returns the character/codepoint at (zero-based) index `ix`.
|
||||||
|
function at(ix : int, s : string) =
|
||||||
|
switch(List.drop(ix, StringInternal.to_list(s)))
|
||||||
|
[] => None
|
||||||
|
x :: _ => Some(x)
|
||||||
|
|
||||||
|
// Searches for `pat` in `str`, returning `Some(ix)` if `pat` is a substring
|
||||||
|
// of `str` starting at position `ix`, otherwise returns `None`.
|
||||||
|
function contains(str : string, substr : string) : option(int) =
|
||||||
|
if(substr == "") Some(0)
|
||||||
|
else
|
||||||
|
contains_(0, StringInternal.to_list(str), StringInternal.to_list(substr))
|
||||||
|
|
||||||
|
// Splits `s` into tokens, `pat` is the divider of tokens.
|
||||||
|
function tokens(s : string, pat : string) =
|
||||||
|
require(pat != "", "String.tokens: empty pattern")
|
||||||
|
tokens_(StringInternal.to_list(pat), StringInternal.to_list(s), [])
|
||||||
|
|
||||||
|
// Converts a decimal ("123", "-253") or a hexadecimal ("0xa2f", "-0xBBB") string
|
||||||
|
// into an integer. If the string doesn't contain a valid number `None` is returned.
|
||||||
|
function to_int(s : string) : option(int) =
|
||||||
|
let s = StringInternal.to_list(s)
|
||||||
|
switch(is_prefix(['-'], s))
|
||||||
|
None => to_int_pos(s)
|
||||||
|
Some(s) => switch(to_int_pos(s))
|
||||||
|
None => None
|
||||||
|
Some(x) => Some(-x)
|
||||||
|
|
||||||
|
// Private helper functions below
|
||||||
|
private function to_int_pos(s : list(char)) =
|
||||||
|
switch(is_prefix(['0', 'x'], s))
|
||||||
|
None =>
|
||||||
|
to_int_(s, ch_to_int_10, 0, 10)
|
||||||
|
Some(s) =>
|
||||||
|
to_int_(s, ch_to_int_16, 0, 16)
|
||||||
|
|
||||||
|
private function
|
||||||
|
tokens_(_, [], acc) = [StringInternal.from_list(List.reverse(acc))]
|
||||||
|
tokens_(pat, str, acc) =
|
||||||
|
switch(is_prefix(pat, str))
|
||||||
|
Some(str') =>
|
||||||
|
StringInternal.from_list(List.reverse(acc)) :: tokens_(pat, str', [])
|
||||||
|
None =>
|
||||||
|
let c :: cs = str
|
||||||
|
tokens_(pat, cs, c :: acc)
|
||||||
|
|
||||||
|
private function
|
||||||
|
contains_(_, [], _) = None
|
||||||
|
contains_(ix, str, substr) =
|
||||||
|
switch(is_prefix(substr, str))
|
||||||
|
None =>
|
||||||
|
let _ :: str = str
|
||||||
|
contains_(ix + 1, str, substr)
|
||||||
|
Some(_) =>
|
||||||
|
Some(ix)
|
||||||
|
|
||||||
|
private function
|
||||||
|
is_prefix([], ys) = Some(ys)
|
||||||
|
is_prefix(_, []) = None
|
||||||
|
is_prefix(x :: xs, y :: ys) =
|
||||||
|
if(x == y) is_prefix(xs, ys)
|
||||||
|
else None
|
||||||
|
|
||||||
|
private function
|
||||||
|
to_int_([], _, x, _) = Some(x)
|
||||||
|
to_int_(i :: is, value, x, b) =
|
||||||
|
switch(value(i))
|
||||||
|
None => None
|
||||||
|
Some(i) => to_int_(is, value, x * b + i, b)
|
||||||
|
|
||||||
|
private function ch_to_int_10(c) =
|
||||||
|
let c = Char.to_int(c)
|
||||||
|
if(c >= 48 && c =< 57) Some(c - 48)
|
||||||
|
else None
|
||||||
|
|
||||||
|
private function ch_to_int_16(c) =
|
||||||
|
let c = Char.to_int(c)
|
||||||
|
if(c >= 48 && c =< 57) Some(c - 48)
|
||||||
|
elif(c >= 65 && c =< 70) Some(c - 55)
|
||||||
|
elif(c >= 97 && c =< 102) Some(c - 87)
|
||||||
|
else None
|
||||||
|
|
||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"4f4d6d3"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"05dfd7f"}}}
|
||||||
, {getopt, "1.0.1"}
|
, {getopt, "1.0.1"}
|
||||||
, {eblake2, "1.0.0"}
|
, {eblake2, "1.0.0"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
{base_plt_apps, [erts, kernel, stdlib, crypto, mnesia]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{relx, [{release, {aesophia, "4.3.0"},
|
{relx, [{release, {aesophia, "6.0.1"},
|
||||||
[aesophia, aebytecode, getopt]},
|
[aesophia, aebytecode, getopt]},
|
||||||
|
|
||||||
{dev_mode, true},
|
{dev_mode, true},
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{"1.1.0",
|
{"1.1.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}},
|
{ref,"05dfd7ffc7fb1e07ecc0b1e516da571f56d7dc8f"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
|
|||||||
+13
-7
@@ -21,6 +21,8 @@
|
|||||||
, json_encode_expr/1
|
, json_encode_expr/1
|
||||||
, json_encode_type/1]).
|
, json_encode_type/1]).
|
||||||
|
|
||||||
|
-include("aeso_utils.hrl").
|
||||||
|
|
||||||
-type aci_type() :: json | string.
|
-type aci_type() :: json | string.
|
||||||
-type json() :: jsx:json_term().
|
-type json() :: jsx:json_term().
|
||||||
-type json_text() :: binary().
|
-type json_text() :: binary().
|
||||||
@@ -68,9 +70,7 @@ do_contract_interface(Type, Contract, Options) when is_binary(Contract) ->
|
|||||||
do_contract_interface(Type, ContractString, Options) ->
|
do_contract_interface(Type, ContractString, Options) ->
|
||||||
try
|
try
|
||||||
Ast = aeso_compiler:parse(ContractString, Options),
|
Ast = aeso_compiler:parse(ContractString, Options),
|
||||||
%% io:format("~p\n", [Ast]),
|
|
||||||
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]),
|
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]),
|
||||||
%% io:format("~p\n", [TypedAst]),
|
|
||||||
from_typed_ast(Type, TypedAst)
|
from_typed_ast(Type, TypedAst)
|
||||||
catch
|
catch
|
||||||
throw:{error, Errors} -> {error, Errors}
|
throw:{error, Errors} -> {error, Errors}
|
||||||
@@ -83,7 +83,7 @@ from_typed_ast(Type, TypedAst) ->
|
|||||||
string -> do_render_aci_json(JArray)
|
string -> do_render_aci_json(JArray)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|
encode_contract(Contract = {Head, _, {con, _, Name}, _}) when ?IS_CONTRACT_HEAD(Head) ->
|
||||||
C0 = #{name => encode_name(Name)},
|
C0 = #{name => encode_name(Name)},
|
||||||
|
|
||||||
Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ],
|
Tdefs0 = [ encode_typedef(T) || T <- sort_decls(contract_types(Contract)) ],
|
||||||
@@ -107,7 +107,7 @@ encode_contract(Contract = {contract, _, {con, _, Name}, _}) ->
|
|||||||
|| F <- sort_decls(contract_funcs(Contract)),
|
|| F <- sort_decls(contract_funcs(Contract)),
|
||||||
is_entrypoint(F) ],
|
is_entrypoint(F) ],
|
||||||
|
|
||||||
#{contract => C3#{functions => Fdefs, payable => is_payable(Contract)}};
|
#{contract => C3#{kind => Head, functions => Fdefs, payable => is_payable(Contract)}};
|
||||||
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
encode_contract(Namespace = {namespace, _, {con, _, Name}, _}) ->
|
||||||
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
Tdefs = [ encode_typedef(T) || T <- sort_decls(contract_types(Namespace)) ],
|
||||||
#{namespace => #{name => encode_name(Name),
|
#{namespace => #{name => encode_name(Name),
|
||||||
@@ -232,13 +232,19 @@ do_render_aci_json(Json) ->
|
|||||||
{ok, list_to_binary(string:join(DecodedContracts, "\n"))}.
|
{ok, list_to_binary(string:join(DecodedContracts, "\n"))}.
|
||||||
|
|
||||||
decode_contract(#{contract := #{name := Name,
|
decode_contract(#{contract := #{name := Name,
|
||||||
|
kind := Kind,
|
||||||
payable := Payable,
|
payable := Payable,
|
||||||
type_defs := Ts0,
|
type_defs := Ts0,
|
||||||
functions := Fs} = C}) ->
|
functions := Fs} = C}) ->
|
||||||
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
MkTDef = fun(N, T) -> #{name => N, vars => [], typedef => T} end,
|
||||||
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
Ts = [ MkTDef(<<"state">>, maps:get(state, C)) || maps:is_key(state, C) ] ++
|
||||||
[ MkTDef(<<"event">>, maps:get(event, C)) || maps:is_key(event, C) ] ++ Ts0,
|
[ MkTDef(<<"event">>, maps:get(event, C)) || maps:is_key(event, C) ] ++ Ts0,
|
||||||
[payable(Payable), "contract ", io_lib:format("~s", [Name])," =\n",
|
[payable(Payable), case Kind of
|
||||||
|
contract_main -> "main contract ";
|
||||||
|
contract_child -> "contract ";
|
||||||
|
contract_interface -> "contract interface "
|
||||||
|
end,
|
||||||
|
io_lib:format("~s", [Name])," =\n",
|
||||||
decode_tdefs(Ts), decode_funcs(Fs)];
|
decode_tdefs(Ts), decode_funcs(Fs)];
|
||||||
decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] ->
|
decode_contract(#{namespace := #{name := Name, type_defs := Ts}}) when Ts /= [] ->
|
||||||
["namespace ", io_lib:format("~s", [Name])," =\n",
|
["namespace ", io_lib:format("~s", [Name])," =\n",
|
||||||
@@ -332,10 +338,10 @@ payable(false) -> "".
|
|||||||
|
|
||||||
%% #contract{Ann, Con, [Declarations]}.
|
%% #contract{Ann, Con, [Declarations]}.
|
||||||
|
|
||||||
contract_funcs({C, _, _, Decls}) when C == contract; C == namespace ->
|
contract_funcs({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace ->
|
||||||
[ D || D <- Decls, is_fun(D)].
|
[ D || D <- Decls, is_fun(D)].
|
||||||
|
|
||||||
contract_types({C, _, _, Decls}) when C == contract; C == namespace ->
|
contract_types({C, _, _, Decls}) when ?IS_CONTRACT_HEAD(C); C == namespace ->
|
||||||
[ D || D <- Decls, is_type(D) ].
|
[ D || D <- Decls, is_type(D) ].
|
||||||
|
|
||||||
is_fun({letfun, _, _, _, _, _}) -> true;
|
is_fun({letfun, _, _, _, _, _}) -> true;
|
||||||
|
|||||||
+504
-78
File diff suppressed because it is too large
Load Diff
+177
-52
@@ -12,6 +12,8 @@
|
|||||||
-export([ast_to_fcode/2, format_fexpr/1]).
|
-export([ast_to_fcode/2, format_fexpr/1]).
|
||||||
-export_type([fcode/0, fexpr/0, fun_def/0]).
|
-export_type([fcode/0, fexpr/0, fun_def/0]).
|
||||||
|
|
||||||
|
-include("aeso_utils.hrl").
|
||||||
|
|
||||||
%% -- Type definitions -------------------------------------------------------
|
%% -- Type definitions -------------------------------------------------------
|
||||||
|
|
||||||
-type option() :: term().
|
-type option() :: term().
|
||||||
@@ -36,7 +38,14 @@
|
|||||||
bits_intersection | bits_union | bits_difference |
|
bits_intersection | bits_union | bits_difference |
|
||||||
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
||||||
crypto_sha3 | crypto_sha256 | crypto_blake2b |
|
crypto_sha3 | crypto_sha256 | crypto_blake2b |
|
||||||
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1.
|
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1 |
|
||||||
|
mcl_bls12_381_g1_neg | mcl_bls12_381_g1_norm | mcl_bls12_381_g1_valid |
|
||||||
|
mcl_bls12_381_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul |
|
||||||
|
mcl_bls12_381_g2_neg | mcl_bls12_381_g2_norm | mcl_bls12_381_g2_valid |
|
||||||
|
mcl_bls12_381_g2_is_zero | mcl_bls12_381_g2_add | mcl_bls12_381_g2_mul |
|
||||||
|
mcl_bls12_381_gt_inv | mcl_bls12_381_gt_add | mcl_bls12_381_gt_mul | mcl_bls12_381_gt_pow |
|
||||||
|
mcl_bls12_381_gt_is_one | mcl_bls12_381_pairing | mcl_bls12_381_miller_loop | mcl_bls12_381_final_exp |
|
||||||
|
mcl_bls12_381_int_to_fr | mcl_bls12_381_int_to_fp | mcl_bls12_381_fr_to_int | mcl_bls12_381_fp_to_int.
|
||||||
|
|
||||||
-type flit() :: {int, integer()}
|
-type flit() :: {int, integer()}
|
||||||
| {string, binary()}
|
| {string, binary()}
|
||||||
@@ -46,6 +55,7 @@
|
|||||||
| {oracle_pubkey, binary()}
|
| {oracle_pubkey, binary()}
|
||||||
| {oracle_query_id, binary()}
|
| {oracle_query_id, binary()}
|
||||||
| {bool, false | true}
|
| {bool, false | true}
|
||||||
|
| {contract_code, string()} %% for CREATE, by name
|
||||||
| {typerep, ftype()}.
|
| {typerep, ftype()}.
|
||||||
|
|
||||||
-type fexpr() :: {lit, flit()}
|
-type fexpr() :: {lit, flit()}
|
||||||
@@ -129,9 +139,10 @@
|
|||||||
-type type_env() :: #{ sophia_name() => type_def() }.
|
-type type_env() :: #{ sophia_name() => type_def() }.
|
||||||
-type fun_env() :: #{ sophia_name() => {fun_name(), non_neg_integer()} }.
|
-type fun_env() :: #{ sophia_name() => {fun_name(), non_neg_integer()} }.
|
||||||
-type con_env() :: #{ sophia_name() => con_tag() }.
|
-type con_env() :: #{ sophia_name() => con_tag() }.
|
||||||
-type builtins() :: #{ sophia_name() => {builtin(), non_neg_integer() | none} }.
|
-type child_con_env() :: #{sophia_name() => fcode()}.
|
||||||
|
-type builtins() :: #{ sophia_name() => {builtin(), non_neg_integer() | none | variable} }.
|
||||||
|
|
||||||
-type context() :: {main_contract, string()}
|
-type context() :: {contract_def, string()}
|
||||||
| {namespace, string()}
|
| {namespace, string()}
|
||||||
| {abstract_contract, string()}.
|
| {abstract_contract, string()}.
|
||||||
|
|
||||||
@@ -140,13 +151,15 @@
|
|||||||
-type env() :: #{ type_env := type_env(),
|
-type env() :: #{ type_env := type_env(),
|
||||||
fun_env := fun_env(),
|
fun_env := fun_env(),
|
||||||
con_env := con_env(),
|
con_env := con_env(),
|
||||||
|
child_con_env := child_con_env(),
|
||||||
event_type => aeso_syntax:typedef(),
|
event_type => aeso_syntax:typedef(),
|
||||||
builtins := builtins(),
|
builtins := builtins(),
|
||||||
options := [option()],
|
options := [option()],
|
||||||
state_layout => state_layout(),
|
state_layout => state_layout(),
|
||||||
context => context(),
|
context => context(),
|
||||||
vars => [var_name()],
|
vars => [var_name()],
|
||||||
functions := #{ fun_name() => fun_def() } }.
|
functions := #{ fun_name() => fun_def() }
|
||||||
|
}.
|
||||||
|
|
||||||
-define(HASH_BYTES, 32).
|
-define(HASH_BYTES, 32).
|
||||||
|
|
||||||
@@ -154,33 +167,74 @@
|
|||||||
|
|
||||||
%% Main entrypoint. Takes typed syntax produced by aeso_ast_infer_types:infer/1,2
|
%% Main entrypoint. Takes typed syntax produced by aeso_ast_infer_types:infer/1,2
|
||||||
%% and produces Fate intermediate code.
|
%% and produces Fate intermediate code.
|
||||||
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> fcode().
|
-spec ast_to_fcode(aeso_syntax:ast(), [option()]) -> {env(), fcode()}.
|
||||||
ast_to_fcode(Code, Options) ->
|
ast_to_fcode(Code, Options) ->
|
||||||
Verbose = lists:member(pp_fcode, Options),
|
|
||||||
init_fresh_names(),
|
init_fresh_names(),
|
||||||
FCode1 = to_fcode(init_env(Options), Code),
|
{Env1, FCode1} = to_fcode(init_env(Options), Code),
|
||||||
|
FCode2 = optimize(FCode1, Options),
|
||||||
|
Env2 = Env1#{ child_con_env :=
|
||||||
|
maps:map(
|
||||||
|
fun (_, FC) -> optimize(FC, Options) end,
|
||||||
|
maps:get(child_con_env, Env1)
|
||||||
|
)},
|
||||||
|
clear_fresh_names(),
|
||||||
|
{Env2, FCode2}.
|
||||||
|
|
||||||
|
optimize(FCode1, Options) ->
|
||||||
|
Verbose = lists:member(pp_fcode, Options),
|
||||||
[io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose],
|
[io:format("-- Before lambda lifting --\n~s\n\n", [format_fcode(FCode1)]) || Verbose],
|
||||||
FCode2 = optimize_fcode(FCode1),
|
FCode2 = optimize_fcode(FCode1),
|
||||||
[ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ],
|
[ io:format("-- After optimization --\n~s\n\n", [format_fcode(FCode2)]) || Verbose, FCode2 /= FCode1 ],
|
||||||
FCode3 = lambda_lift(FCode2),
|
FCode3 = lambda_lift(FCode2),
|
||||||
[ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ],
|
[ io:format("-- After lambda lifting --\n~s\n\n", [format_fcode(FCode3)]) || Verbose, FCode3 /= FCode2 ],
|
||||||
clear_fresh_names(),
|
|
||||||
FCode3.
|
FCode3.
|
||||||
|
|
||||||
%% -- Environment ------------------------------------------------------------
|
%% -- Environment ------------------------------------------------------------
|
||||||
|
|
||||||
-spec init_env([option()]) -> env().
|
-spec init_env([option()]) -> env().
|
||||||
init_env(Options) ->
|
init_env(Options) ->
|
||||||
|
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
|
||||||
#{ type_env => init_type_env(),
|
#{ type_env => init_type_env(),
|
||||||
fun_env => #{},
|
fun_env => #{},
|
||||||
builtins => builtins(),
|
builtins => builtins(),
|
||||||
|
child_con_env => #{},
|
||||||
con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] },
|
con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] },
|
||||||
["Some"] => #con_tag{ tag = 1, arities = [0, 1] },
|
["Some"] => #con_tag{ tag = 1, arities = [0, 1] },
|
||||||
["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] },
|
["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] },
|
||||||
["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] }
|
["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] },
|
||||||
|
["AENS", "AccountPt"] => #con_tag{ tag = 0, arities = [1, 1, 1, 1] },
|
||||||
|
["AENS", "OraclePt"] => #con_tag{ tag = 1, arities = [1, 1, 1, 1] },
|
||||||
|
["AENS", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1] },
|
||||||
|
["AENS", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1] },
|
||||||
|
["AENS", "Name"] => #con_tag{ tag = 0, arities = [3] },
|
||||||
|
["Chain", "GAMetaTx"] => #con_tag{ tag = 0, arities = [2] },
|
||||||
|
["Chain", "PayingForTx"] => #con_tag{ tag = 0, arities = [2] },
|
||||||
|
["Chain", "SpendTx"] => #con_tag{ tag = 0, arities = ChainTxArities },
|
||||||
|
["Chain", "OracleRegisterTx"] => #con_tag{ tag = 1, arities = ChainTxArities },
|
||||||
|
["Chain", "OracleQueryTx"] => #con_tag{ tag = 2, arities = ChainTxArities },
|
||||||
|
["Chain", "OracleResponseTx"] => #con_tag{ tag = 3, arities = ChainTxArities },
|
||||||
|
["Chain", "OracleExtendTx"] => #con_tag{ tag = 4, arities = ChainTxArities },
|
||||||
|
["Chain", "NamePreclaimTx"] => #con_tag{ tag = 5, arities = ChainTxArities },
|
||||||
|
["Chain", "NameClaimTx"] => #con_tag{ tag = 6, arities = ChainTxArities },
|
||||||
|
["Chain", "NameUpdateTx"] => #con_tag{ tag = 7, arities = ChainTxArities },
|
||||||
|
["Chain", "NameRevokeTx"] => #con_tag{ tag = 8, arities = ChainTxArities },
|
||||||
|
["Chain", "NameTransferTx"] => #con_tag{ tag = 9, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelCreateTx"] => #con_tag{ tag = 10, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelDepositTx"] => #con_tag{ tag = 11, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelWithdrawTx"] => #con_tag{ tag = 12, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelForceProgressTx"] => #con_tag{ tag = 13, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelCloseMutualTx"] => #con_tag{ tag = 14, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelCloseSoloTx"] => #con_tag{ tag = 15, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelSlashTx"] => #con_tag{ tag = 16, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelSettleTx"] => #con_tag{ tag = 17, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelSnapshotSoloTx"] => #con_tag{ tag = 18, arities = ChainTxArities },
|
||||||
|
["Chain", "ContractCreateTx"] => #con_tag{ tag = 19, arities = ChainTxArities },
|
||||||
|
["Chain", "ContractCallTx"] => #con_tag{ tag = 20, arities = ChainTxArities },
|
||||||
|
["Chain", "GAAttachTx"] => #con_tag{ tag = 21, arities = ChainTxArities }
|
||||||
},
|
},
|
||||||
options => Options,
|
options => Options,
|
||||||
functions => #{} }.
|
functions => #{}
|
||||||
|
}.
|
||||||
|
|
||||||
-spec builtins() -> builtins().
|
-spec builtins() -> builtins().
|
||||||
builtins() ->
|
builtins() ->
|
||||||
@@ -190,22 +244,29 @@ builtins() ->
|
|||||||
Scopes = [{[], [{"abort", 1}, {"require", 2}]},
|
Scopes = [{[], [{"abort", 1}, {"require", 2}]},
|
||||||
{["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none},
|
{["Chain"], [{"spend", 2}, {"balance", 1}, {"block_hash", 1}, {"coinbase", none},
|
||||||
{"timestamp", none}, {"block_height", none}, {"difficulty", none},
|
{"timestamp", none}, {"block_height", none}, {"difficulty", none},
|
||||||
{"gas_limit", none}]},
|
{"gas_limit", none}, {"bytecode_hash", 1}, {"create", variable}, {"clone", variable}]},
|
||||||
{["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]},
|
{["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]},
|
||||||
{["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none},
|
{["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none}, {"fee", none},
|
||||||
{"gas_left", 0}]},
|
{"gas_left", 0}]},
|
||||||
{["Oracle"], [{"register", 4}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
|
{["Oracle"], [{"register", 4}, {"expiry", 1}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
|
||||||
{"respond", 4}, {"extend", 3}, {"get_answer", 2},
|
{"respond", 4}, {"extend", 3}, {"get_answer", 2},
|
||||||
{"check", 1}, {"check_query", 2}]},
|
{"check", 1}, {"check_query", 2}]},
|
||||||
{["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
|
{["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
|
||||||
{"revoke", 3}]},
|
{"revoke", 3}, {"update", 6}, {"lookup", 1}]},
|
||||||
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
|
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
|
||||||
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
||||||
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
||||||
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
||||||
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
||||||
{["Auth"], [{"tx_hash", none}]},
|
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
||||||
{["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
{"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2},
|
||||||
|
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
|
||||||
|
{"pairing", 2}, {"miller_loop", 2}, {"final_exp", 1},
|
||||||
|
{"int_to_fr", 1}, {"int_to_fp", 1}, {"fr_to_int", 1}, {"fp_to_int", 1}]},
|
||||||
|
{["StringInternal"], [{"length", 1}, {"concat", 2}, {"to_list", 1}, {"from_list", 1},
|
||||||
|
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}, {"to_lower", 1}, {"to_upper", 1}]},
|
||||||
|
{["Char"], [{"to_int", 1}, {"from_int", 1}]},
|
||||||
|
{["Auth"], [{"tx_hash", none}, {"tx", none}]},
|
||||||
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
||||||
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
||||||
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
||||||
@@ -224,6 +285,11 @@ state_layout(Env) -> maps:get(state_layout, Env, {reg, 1}).
|
|||||||
|
|
||||||
-spec init_type_env() -> type_env().
|
-spec init_type_env() -> type_env().
|
||||||
init_type_env() ->
|
init_type_env() ->
|
||||||
|
BaseTx = {variant, [[address, integer, string], [], [], [], [], [], [string],
|
||||||
|
[hash], [hash], [address, hash], [address],
|
||||||
|
[address, integer], [address, integer], [address],
|
||||||
|
[address], [address], [address], [address], [address],
|
||||||
|
[integer], [address, integer], []]},
|
||||||
#{ ["int"] => ?type(integer),
|
#{ ["int"] => ?type(integer),
|
||||||
["bool"] => ?type(boolean),
|
["bool"] => ?type(boolean),
|
||||||
["bits"] => ?type(bits),
|
["bits"] => ?type(bits),
|
||||||
@@ -233,11 +299,18 @@ init_type_env() ->
|
|||||||
["hash"] => ?type(hash),
|
["hash"] => ?type(hash),
|
||||||
["signature"] => ?type(signature),
|
["signature"] => ?type(signature),
|
||||||
["oracle"] => ?type(Q, R, {oracle, Q, R}),
|
["oracle"] => ?type(Q, R, {oracle, Q, R}),
|
||||||
["oracle_query"] => ?type(_, _, oracle_query),
|
["oracle_query"] => ?type(_, _, oracle_query), %% TODO: not in Fate
|
||||||
["list"] => ?type(T, {list, T}),
|
["list"] => ?type(T, {list, T}),
|
||||||
["map"] => ?type(K, V, {map, K, V}),
|
["map"] => ?type(K, V, {map, K, V}),
|
||||||
["option"] => ?type(T, {variant, [[], [T]]}),
|
["option"] => ?type(T, {variant, [[], [T]]}),
|
||||||
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]})
|
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}),
|
||||||
|
["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}),
|
||||||
|
["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}),
|
||||||
|
["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}),
|
||||||
|
["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}),
|
||||||
|
["Chain", "base_tx"] => ?type(BaseTx),
|
||||||
|
["MCL_BLS12_381", "fr"] => ?type({bytes, 32}),
|
||||||
|
["MCL_BLS12_381", "fp"] => ?type({bytes, 48})
|
||||||
}.
|
}.
|
||||||
|
|
||||||
is_no_code(Env) ->
|
is_no_code(Env) ->
|
||||||
@@ -251,31 +324,44 @@ get_option(Opt, Env, Default) ->
|
|||||||
|
|
||||||
%% -- Compilation ------------------------------------------------------------
|
%% -- Compilation ------------------------------------------------------------
|
||||||
|
|
||||||
-spec to_fcode(env(), aeso_syntax:ast()) -> fcode().
|
-spec to_fcode(env(), aeso_syntax:ast()) -> {env(), fcode()}.
|
||||||
to_fcode(Env, [{contract, Attrs, MainCon = {con, _, Main}, Decls}]) ->
|
to_fcode(Env, [{Contract, Attrs, Con = {con, _, Name}, Decls}|Rest])
|
||||||
|
when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
|
case Contract =:= contract_interface of
|
||||||
|
false ->
|
||||||
#{ builtins := Builtins } = Env,
|
#{ builtins := Builtins } = Env,
|
||||||
MainEnv = Env#{ context => {main_contract, Main},
|
ConEnv = Env#{ context => {contract_def, Name},
|
||||||
builtins => Builtins#{[Main, "state"] => {get_state, none},
|
builtins => Builtins#{[Name, "state"] => {get_state, none},
|
||||||
[Main, "put"] => {set_state, 1},
|
[Name, "put"] => {set_state, 1},
|
||||||
[Main, "Chain", "event"] => {chain_event, 1}} },
|
[Name, "Chain", "event"] => {chain_event, 1}} },
|
||||||
|
#{ functions := PrevFuns } = ConEnv,
|
||||||
#{ functions := Funs } = Env1 =
|
#{ functions := Funs } = Env1 =
|
||||||
decls_to_fcode(MainEnv, Decls),
|
decls_to_fcode(ConEnv, Decls),
|
||||||
StateType = lookup_type(Env1, [Main, "state"], [], {tuple, []}),
|
StateType = lookup_type(Env1, [Name, "state"], [], {tuple, []}),
|
||||||
EventType = lookup_type(Env1, [Main, "event"], [], none),
|
EventType = lookup_type(Env1, [Name, "event"], [], none),
|
||||||
StateLayout = state_layout(Env1),
|
StateLayout = state_layout(Env1),
|
||||||
Payable = proplists:get_value(payable, Attrs, false),
|
Payable = proplists:get_value(payable, Attrs, false),
|
||||||
#{ contract_name => Main,
|
ConFcode = #{ contract_name => Name,
|
||||||
state_type => StateType,
|
state_type => StateType,
|
||||||
state_layout => StateLayout,
|
state_layout => StateLayout,
|
||||||
event_type => EventType,
|
event_type => EventType,
|
||||||
payable => Payable,
|
payable => Payable,
|
||||||
functions => add_init_function(Env1, MainCon, StateType,
|
functions => add_init_function(
|
||||||
add_event_function(Env1, EventType, Funs)) };
|
Env1, Con, StateType,
|
||||||
to_fcode(_Env, [NotContract]) ->
|
add_event_function(Env1, EventType, Funs)) },
|
||||||
fcode_error({last_declaration_must_be_contract, NotContract});
|
case Contract of
|
||||||
to_fcode(Env, [{contract, _, {con, _, Con}, Decls} | Code]) ->
|
contract_main -> [] = Rest, {Env1, ConFcode};
|
||||||
Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Con} }, Decls),
|
contract_child ->
|
||||||
to_fcode(Env1, Code);
|
Env2 = add_child_con(Env1, Name, ConFcode),
|
||||||
|
Env3 = Env2#{ functions := PrevFuns },
|
||||||
|
to_fcode(Env3, Rest)
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
Env1 = decls_to_fcode(Env#{ context => {abstract_contract, Name} }, Decls),
|
||||||
|
to_fcode(Env1, Rest)
|
||||||
|
end;
|
||||||
|
to_fcode(_Env, [NotMain = {NotMainHead, _ ,_ , _}]) when NotMainHead =/= contract_def ->
|
||||||
|
fcode_error({last_declaration_must_be_contract_def, NotMain});
|
||||||
to_fcode(Env, [{namespace, _, {con, _, Con}, Decls} | Code]) ->
|
to_fcode(Env, [{namespace, _, {con, _, Con}, Decls} | Code]) ->
|
||||||
Env1 = decls_to_fcode(Env#{ context => {namespace, Con} }, Decls),
|
Env1 = decls_to_fcode(Env#{ context => {namespace, Con} }, Decls),
|
||||||
to_fcode(Env1, Code).
|
to_fcode(Env1, Code).
|
||||||
@@ -285,13 +371,11 @@ decls_to_fcode(Env, Decls) ->
|
|||||||
%% First compute mapping from Sophia names to fun_names and add it to the
|
%% First compute mapping from Sophia names to fun_names and add it to the
|
||||||
%% environment.
|
%% environment.
|
||||||
Env1 = add_fun_env(Env, Decls),
|
Env1 = add_fun_env(Env, Decls),
|
||||||
lists:foldl(fun(D, E) ->
|
lists:foldl(fun(D, E) -> decl_to_fcode(E, D)
|
||||||
R = decl_to_fcode(E, D),
|
|
||||||
R
|
|
||||||
end, Env1, Decls).
|
end, Env1, Decls).
|
||||||
|
|
||||||
-spec decl_to_fcode(env(), aeso_syntax:decl()) -> env().
|
-spec decl_to_fcode(env(), aeso_syntax:decl()) -> env().
|
||||||
decl_to_fcode(Env = #{context := {main_contract, _}}, {fun_decl, _, Id, _}) ->
|
decl_to_fcode(Env = #{context := {contract_def, _}}, {fun_decl, _, Id, _}) ->
|
||||||
case is_no_code(Env) of
|
case is_no_code(Env) of
|
||||||
false -> fcode_error({missing_definition, Id});
|
false -> fcode_error({missing_definition, Id});
|
||||||
true -> Env
|
true -> Env
|
||||||
@@ -354,7 +438,7 @@ typedef_to_fcode(Env, Id = {id, _, Name}, Xs, Def) ->
|
|||||||
Env3 = compute_state_layout(Env2, Name, FDef),
|
Env3 = compute_state_layout(Env2, Name, FDef),
|
||||||
bind_type(Env3, Q, FDef).
|
bind_type(Env3, Q, FDef).
|
||||||
|
|
||||||
compute_state_layout(Env = #{ context := {main_contract, _} }, "state", Type) ->
|
compute_state_layout(Env = #{ context := {contract_def, _} }, "state", Type) ->
|
||||||
NoLayout = get_option(no_flatten_state, Env),
|
NoLayout = get_option(no_flatten_state, Env),
|
||||||
Layout =
|
Layout =
|
||||||
case Type([]) of
|
case Type([]) of
|
||||||
@@ -380,7 +464,7 @@ compute_state_layout(R, [H | T]) ->
|
|||||||
compute_state_layout(R, _) ->
|
compute_state_layout(R, _) ->
|
||||||
{R + 1, {reg, R}}.
|
{R + 1, {reg, R}}.
|
||||||
|
|
||||||
check_state_and_event_types(#{ context := {main_contract, _} }, Id, [_ | _]) ->
|
check_state_and_event_types(#{ context := {contract_def, _} }, Id, [_ | _]) ->
|
||||||
case Id of
|
case Id of
|
||||||
{id, _, "state"} -> fcode_error({parameterized_state, Id});
|
{id, _, "state"} -> fcode_error({parameterized_state, Id});
|
||||||
{id, _, "event"} -> fcode_error({parameterized_event, Id});
|
{id, _, "event"} -> fcode_error({parameterized_event, Id});
|
||||||
@@ -405,12 +489,18 @@ type_to_fcode(Env, Sub, {record_t, Fields}) ->
|
|||||||
type_to_fcode(Env, Sub, {tuple_t, [], lists:map(FieldType, Fields)});
|
type_to_fcode(Env, Sub, {tuple_t, [], lists:map(FieldType, Fields)});
|
||||||
type_to_fcode(_Env, _Sub, {bytes_t, _, N}) ->
|
type_to_fcode(_Env, _Sub, {bytes_t, _, N}) ->
|
||||||
{bytes, N};
|
{bytes, N};
|
||||||
|
type_to_fcode(_Env, _Sub, {tvar, Ann, "void"}) ->
|
||||||
|
fcode_error({found_void, Ann});
|
||||||
type_to_fcode(_Env, Sub, {tvar, _, X}) ->
|
type_to_fcode(_Env, Sub, {tvar, _, X}) ->
|
||||||
maps:get(X, Sub, {tvar, X});
|
maps:get(X, Sub, {tvar, X});
|
||||||
|
type_to_fcode(_Env, _Sub, {fun_t, Ann, _, var_args, _}) ->
|
||||||
|
fcode_error({var_args_not_set, {id, Ann, "a very suspicious function"}});
|
||||||
type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) ->
|
type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) ->
|
||||||
FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named],
|
FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named],
|
||||||
FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args],
|
FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args],
|
||||||
{function, FNamed ++ FArgs, type_to_fcode(Env, Sub, Res)};
|
{function, FNamed ++ FArgs, type_to_fcode(Env, Sub, Res)};
|
||||||
|
type_to_fcode(Env, Sub, {if_t, _, _, _, Else}) ->
|
||||||
|
type_to_fcode(Env, Sub, Else); %% Hacky: this is only for remote calls, in which case we want the unprotected type
|
||||||
type_to_fcode(_Env, _Sub, Type) ->
|
type_to_fcode(_Env, _Sub, Type) ->
|
||||||
error({todo, Type}).
|
error({todo, Type}).
|
||||||
|
|
||||||
@@ -620,14 +710,31 @@ expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
%% Function calls
|
%% Function calls
|
||||||
expr_to_fcode(Env, _Type, {app, _, Fun = {typed, _, _, {fun_t, _, NamedArgsT, _, _}}, Args}) ->
|
expr_to_fcode(Env, Type, {app, _, Fun = {typed, _, FunE, {fun_t, _, NamedArgsT, ArgsT, _}}, Args}) ->
|
||||||
Args1 = get_named_args(NamedArgsT, Args),
|
Args1 = get_named_args(NamedArgsT, Args),
|
||||||
FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1],
|
FArgs = [expr_to_fcode(Env, Arg) || Arg <- Args1],
|
||||||
case expr_to_fcode(Env, Fun) of
|
case expr_to_fcode(Env, Fun) of
|
||||||
{builtin_u, B, _Ar, TypeArgs} -> builtin_to_fcode(state_layout(Env), B, FArgs ++ TypeArgs);
|
{builtin_u, B, _Ar, TypeArgs} -> builtin_to_fcode(state_layout(Env), B, FArgs ++ TypeArgs);
|
||||||
|
{builtin_u, chain_clone, _Ar} ->
|
||||||
|
case ArgsT of
|
||||||
|
var_args -> fcode_error({var_args_not_set, FunE});
|
||||||
|
_ ->
|
||||||
|
%% Here we little cheat on the typechecker, but this inconsistency
|
||||||
|
%% is to be solved in `aeso_fcode_to_fate:type_to_scode/1`
|
||||||
|
FInitArgsT = aeb_fate_data:make_typerep([type_to_fcode(Env, T) || T <- ArgsT]),
|
||||||
|
builtin_to_fcode(state_layout(Env), chain_clone, [{lit, FInitArgsT}|FArgs])
|
||||||
|
end;
|
||||||
|
{builtin_u, chain_create, _Ar} ->
|
||||||
|
case {ArgsT, Type} of
|
||||||
|
{var_args, _} -> fcode_error({var_args_not_set, FunE});
|
||||||
|
{_, {con, _, Contract}} ->
|
||||||
|
FInitArgsT = aeb_fate_data:make_typerep([type_to_fcode(Env, T) || T <- ArgsT]),
|
||||||
|
builtin_to_fcode(state_layout(Env), chain_create, [{lit, {contract_code, Contract}}, {lit, FInitArgsT}|FArgs]);
|
||||||
|
{_, _} -> fcode_error({not_a_contract_type, Type})
|
||||||
|
end;
|
||||||
{builtin_u, B, _Ar} -> builtin_to_fcode(state_layout(Env), B, FArgs);
|
{builtin_u, B, _Ar} -> builtin_to_fcode(state_layout(Env), B, FArgs);
|
||||||
{def_u, F, _Ar} -> {def, F, FArgs};
|
{def_u, F, _Ar} -> {def, F, FArgs};
|
||||||
{remote_u, ArgsT, RetT, Ct, RFun} -> {remote, ArgsT, RetT, Ct, RFun, FArgs};
|
{remote_u, RArgsT, RRetT, Ct, RFun} -> {remote, RArgsT, RRetT, Ct, RFun, FArgs};
|
||||||
FFun ->
|
FFun ->
|
||||||
%% FFun is a closure, with first component the function name and
|
%% FFun is a closure, with first component the function name and
|
||||||
%% second component the environment
|
%% second component the environment
|
||||||
@@ -985,12 +1092,21 @@ stmts_to_fcode(Env, [Expr | Stmts]) ->
|
|||||||
|
|
||||||
op_builtins() ->
|
op_builtins() ->
|
||||||
[map_from_list, map_to_list, map_delete, map_member, map_size,
|
[map_from_list, map_to_list, map_delete, map_member, map_size,
|
||||||
string_length, string_concat, string_sha3, string_sha256, string_blake2b,
|
stringinternal_length, stringinternal_concat, stringinternal_to_list, stringinternal_from_list,
|
||||||
|
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
||||||
|
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
|
||||||
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
||||||
bits_difference, int_to_str, address_to_str, crypto_verify_sig,
|
bits_difference, int_to_str, address_to_str, crypto_verify_sig,
|
||||||
address_to_contract,
|
address_to_contract,
|
||||||
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
|
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
|
||||||
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1
|
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
|
||||||
|
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
|
||||||
|
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
|
||||||
|
mcl_bls12_381_g2_neg, mcl_bls12_381_g2_norm, mcl_bls12_381_g2_valid,
|
||||||
|
mcl_bls12_381_g2_is_zero, mcl_bls12_381_g2_add, mcl_bls12_381_g2_mul,
|
||||||
|
mcl_bls12_381_gt_inv, mcl_bls12_381_gt_add, mcl_bls12_381_gt_mul, mcl_bls12_381_gt_pow,
|
||||||
|
mcl_bls12_381_gt_is_one, mcl_bls12_381_pairing, mcl_bls12_381_miller_loop, mcl_bls12_381_final_exp,
|
||||||
|
mcl_bls12_381_int_to_fr, mcl_bls12_381_int_to_fp, mcl_bls12_381_fr_to_int, mcl_bls12_381_fp_to_int
|
||||||
].
|
].
|
||||||
|
|
||||||
set_state({reg, R}, Val) ->
|
set_state({reg, R}, Val) ->
|
||||||
@@ -1151,8 +1267,8 @@ lambda_lift_expr(Layout, UExpr) when element(1, UExpr) == def_u; element(1, UExp
|
|||||||
lambda_lift_expr(Layout, {remote_u, ArgsT, RetT, Ct, F}) ->
|
lambda_lift_expr(Layout, {remote_u, ArgsT, RetT, Ct, F}) ->
|
||||||
FVs = free_vars(Ct),
|
FVs = free_vars(Ct),
|
||||||
Ct1 = lambda_lift_expr(Layout, Ct),
|
Ct1 = lambda_lift_expr(Layout, Ct),
|
||||||
GasAndValueArgs = 2,
|
NamedArgCount = 3,
|
||||||
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + GasAndValueArgs) ],
|
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + NamedArgCount) ],
|
||||||
Args = [{var, X} || X <- Xs],
|
Args = [{var, X} || X <- Xs],
|
||||||
make_closure(FVs, Xs, {remote, ArgsT, RetT, Ct1, F, Args});
|
make_closure(FVs, Xs, {remote, ArgsT, RetT, Ct1, F, Args});
|
||||||
lambda_lift_expr(Layout, Expr) ->
|
lambda_lift_expr(Layout, Expr) ->
|
||||||
@@ -1547,6 +1663,10 @@ bind_constructors(Env = #{ con_env := ConEnv }, NewCons) ->
|
|||||||
|
|
||||||
%% -- Names --
|
%% -- Names --
|
||||||
|
|
||||||
|
-spec add_child_con(env(), sophia_name(), fcode()) -> env().
|
||||||
|
add_child_con(Env = #{child_con_env := CEnv}, Name, Fcode) ->
|
||||||
|
Env#{ child_con_env := CEnv#{Name => Fcode} }.
|
||||||
|
|
||||||
-spec add_fun_env(env(), [aeso_syntax:decl()]) -> env().
|
-spec add_fun_env(env(), [aeso_syntax:decl()]) -> env().
|
||||||
add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no functions from abstract contracts
|
add_fun_env(Env = #{ context := {abstract_contract, _} }, _) -> Env; %% no functions from abstract contracts
|
||||||
add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->
|
add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->
|
||||||
@@ -1561,7 +1681,7 @@ add_fun_env(Env = #{ fun_env := FunEnv }, Decls) ->
|
|||||||
make_fun_name(#{ context := Context }, Ann, Name) ->
|
make_fun_name(#{ context := Context }, Ann, Name) ->
|
||||||
Entrypoint = proplists:get_value(entrypoint, Ann, false),
|
Entrypoint = proplists:get_value(entrypoint, Ann, false),
|
||||||
case Context of
|
case Context of
|
||||||
{main_contract, Main} ->
|
{contract_def, Main} ->
|
||||||
if Entrypoint -> {entrypoint, list_to_binary(Name)};
|
if Entrypoint -> {entrypoint, list_to_binary(Name)};
|
||||||
true -> {local_fun, [Main, Name]}
|
true -> {local_fun, [Main, Name]}
|
||||||
end;
|
end;
|
||||||
@@ -1573,7 +1693,7 @@ make_fun_name(#{ context := Context }, Ann, Name) ->
|
|||||||
current_namespace(#{ context := Cxt }) ->
|
current_namespace(#{ context := Cxt }) ->
|
||||||
case Cxt of
|
case Cxt of
|
||||||
{abstract_contract, Con} -> Con;
|
{abstract_contract, Con} -> Con;
|
||||||
{main_contract, Con} -> Con;
|
{contract_def, Con} -> Con;
|
||||||
{namespace, NS} -> NS
|
{namespace, NS} -> NS
|
||||||
end.
|
end.
|
||||||
|
|
||||||
@@ -1920,8 +2040,11 @@ internal_error(Error) ->
|
|||||||
%% -- Pretty printing --------------------------------------------------------
|
%% -- Pretty printing --------------------------------------------------------
|
||||||
|
|
||||||
format_fcode(#{ functions := Funs }) ->
|
format_fcode(#{ functions := Funs }) ->
|
||||||
prettypr:format(pp_above(
|
prettypr:format(format_funs(Funs)).
|
||||||
[ pp_fun(Name, Def) || {Name, Def} <- maps:to_list(Funs) ])).
|
|
||||||
|
format_funs(Funs) ->
|
||||||
|
pp_above(
|
||||||
|
[ pp_fun(Name, Def) || {Name, Def} <- maps:to_list(Funs) ]).
|
||||||
|
|
||||||
format_fexpr(E) ->
|
format_fexpr(E) ->
|
||||||
prettypr:format(pp_fexpr(E)).
|
prettypr:format(pp_fexpr(E)).
|
||||||
@@ -2038,7 +2161,9 @@ pp_fexpr({set_state, R, A}) ->
|
|||||||
pp_call(pp_text("set_state"), [{lit, {int, R}}, A]);
|
pp_call(pp_text("set_state"), [{lit, {int, R}}, A]);
|
||||||
pp_fexpr({get_state, R}) ->
|
pp_fexpr({get_state, R}) ->
|
||||||
pp_call(pp_text("get_state"), [{lit, {int, R}}]);
|
pp_call(pp_text("get_state"), [{lit, {int, R}}]);
|
||||||
pp_fexpr({switch, Split}) -> pp_split(Split).
|
pp_fexpr({switch, Split}) -> pp_split(Split);
|
||||||
|
pp_fexpr({contract_code, Contract}) ->
|
||||||
|
pp_beside(pp_text("contract "), pp_text(Contract)).
|
||||||
|
|
||||||
pp_call(Fun, Args) ->
|
pp_call(Fun, Args) ->
|
||||||
pp_beside(Fun, pp_fexpr({tuple, Args})).
|
pp_beside(Fun, pp_fexpr({tuple, Args})).
|
||||||
|
|||||||
@@ -14,12 +14,13 @@
|
|||||||
|
|
||||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||||
-include("aeso_icode.hrl").
|
-include("aeso_icode.hrl").
|
||||||
|
-include("aeso_utils.hrl").
|
||||||
|
|
||||||
-spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode().
|
-spec convert_typed(aeso_syntax:ast(), list()) -> aeso_icode:icode().
|
||||||
convert_typed(TypedTree, Options) ->
|
convert_typed(TypedTree, Options) ->
|
||||||
{Payable, Name} =
|
{Payable, Name} =
|
||||||
case lists:last(TypedTree) of
|
case lists:last(TypedTree) of
|
||||||
{contract, Attrs, {con, _, Con}, _} ->
|
{Contr, Attrs, {con, _, Con}, _} when ?IS_CONTRACT_HEAD(Contr) ->
|
||||||
{proplists:get_value(payable, Attrs, false), Con};
|
{proplists:get_value(payable, Attrs, false), Con};
|
||||||
Decl ->
|
Decl ->
|
||||||
gen_error({last_declaration_must_be_contract, Decl})
|
gen_error({last_declaration_must_be_contract, Decl})
|
||||||
@@ -29,7 +30,8 @@ convert_typed(TypedTree, Options) ->
|
|||||||
Icode = code(TypedTree, NewIcode, Options),
|
Icode = code(TypedTree, NewIcode, Options),
|
||||||
deadcode_elimination(Icode).
|
deadcode_elimination(Icode).
|
||||||
|
|
||||||
code([{contract, _Attribs, Con, Code}|Rest], Icode, Options) ->
|
code([{Contract, _Attribs, Con, Code}|Rest], Icode, Options)
|
||||||
|
when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)),
|
NewIcode = contract_to_icode(Code, aeso_icode:set_namespace(Con, Icode)),
|
||||||
code(Rest, NewIcode, Options);
|
code(Rest, NewIcode, Options);
|
||||||
code([{namespace, _Ann, Name, Code}|Rest], Icode, Options) ->
|
code([{namespace, _Ann, Name, Code}|Rest], Icode, Options) ->
|
||||||
@@ -469,6 +471,7 @@ is_builtin_fun({qid, _, ["AENS", "preclaim"]}, _Icode) ->
|
|||||||
is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["AENS", "revoke"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["AENS", "revoke"]}, _Icode) -> true;
|
||||||
|
is_builtin_fun({qid, _, ["AENS", "update"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["Map", "lookup"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["Map", "lookup"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["Map", "member"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["Map", "member"]}, _Icode) -> true;
|
||||||
@@ -624,6 +627,12 @@ builtin_code(_, {qid, _, ["AENS", "revoke"]}, Args, _, _, Icode) ->
|
|||||||
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
|
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
|
||||||
[word, word, sign_t()], {tuple, []});
|
[word, word, sign_t()], {tuple, []});
|
||||||
|
|
||||||
|
builtin_code(_, {qid, _, ["AENS", "update"]}, Args, _, _, Icode) ->
|
||||||
|
{Sign, [Addr, Name, TTL, ClientTTL, Pointers]} = get_signature_arg(Args),
|
||||||
|
prim_call(?PRIM_CALL_AENS_UPDATE, #integer{value = 0},
|
||||||
|
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(TTL, Icode), ast_body(ClientTTL, Icode), ast_body(Pointers, Icode), ast_body(Sign, Icode)],
|
||||||
|
[word, string, word, word, word, sign_t()], {tuple, []});
|
||||||
|
|
||||||
%% -- Maps
|
%% -- Maps
|
||||||
%% -- lookup functions
|
%% -- lookup functions
|
||||||
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
|
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
|
||||||
@@ -927,11 +936,16 @@ ast_typerep1({variant_t, Cons}, Icode) ->
|
|||||||
{variant, [ begin
|
{variant, [ begin
|
||||||
{constr_t, _, _, Args} = Con,
|
{constr_t, _, _, Args} = Con,
|
||||||
[ ast_typerep1(Arg, Icode) || Arg <- Args ]
|
[ ast_typerep1(Arg, Icode) || Arg <- Args ]
|
||||||
end || Con <- Cons ]}.
|
end || Con <- Cons ]};
|
||||||
|
ast_typerep1({if_t, _, _, _, Else}, Icode) ->
|
||||||
|
ast_typerep1(Else, Icode). %% protected remote calls are not in AEVM
|
||||||
|
|
||||||
ttl_t(Icode) ->
|
ttl_t(Icode) ->
|
||||||
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
||||||
|
|
||||||
|
%% pointee_t(Icode) ->
|
||||||
|
%% ast_typerep({qid, [], ["AENS", "pointee"]}, Icode).
|
||||||
|
|
||||||
sign_t() -> bytes_t(64).
|
sign_t() -> bytes_t(64).
|
||||||
bytes_t(Len) when Len =< 32 -> word;
|
bytes_t(Len) when Len =< 32 -> word;
|
||||||
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
|
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
|
||||||
|
|||||||
@@ -10,9 +10,9 @@
|
|||||||
|
|
||||||
-export([format/1, pos/1]).
|
-export([format/1, pos/1]).
|
||||||
|
|
||||||
format({last_declaration_must_be_contract, Decl = {namespace, _, {con, _, C}, _}}) ->
|
format({last_declaration_must_be_main_contract, Decl = {Kind, _, {con, _, C}, _}}) ->
|
||||||
Msg = io_lib:format("Expected a contract as the last declaration instead of the namespace '~s'\n",
|
Msg = io_lib:format("Expected a main contract as the last declaration instead of the ~p '~s'\n",
|
||||||
[C]),
|
[Kind, C]),
|
||||||
mk_err(pos(Decl), Msg);
|
mk_err(pos(Decl), Msg);
|
||||||
format({missing_init_function, Con}) ->
|
format({missing_init_function, Con}) ->
|
||||||
Msg = io_lib:format("Missing init function for the contract '~s'.\n", [pp_expr(Con)]),
|
Msg = io_lib:format("Missing init function for the contract '~s'.\n", [pp_expr(Con)]),
|
||||||
@@ -87,6 +87,12 @@ format({higher_order_state, {type_def, Ann, _, _, State}}) ->
|
|||||||
Msg = io_lib:format("Invalid state type\n~s\n", [pp_type(2, State)]),
|
Msg = io_lib:format("Invalid state type\n~s\n", [pp_type(2, State)]),
|
||||||
Cxt = "The state cannot contain functions in the AEVM. Use FATE if you need this.\n",
|
Cxt = "The state cannot contain functions in the AEVM. Use FATE if you need this.\n",
|
||||||
mk_err(pos(Ann), Msg, Cxt);
|
mk_err(pos(Ann), Msg, Cxt);
|
||||||
|
format({var_args_not_set, Expr}) ->
|
||||||
|
mk_err( pos(Expr), "Could not deduce type of variable arguments list"
|
||||||
|
, "When compiling " ++ pp_expr(Expr)
|
||||||
|
);
|
||||||
|
format({found_void, Ann}) ->
|
||||||
|
mk_err(pos(Ann), "Found a void-typed value.", "`void` is a restricted, uninhabited type. Did you mean `unit`?");
|
||||||
|
|
||||||
format(Err) ->
|
format(Err) ->
|
||||||
mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])).
|
mk_err(aeso_errors:pos(0, 0), io_lib:format("Unknown error: ~p\n", [Err])).
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
-include_lib("aebytecode/include/aeb_opcodes.hrl").
|
||||||
-include("aeso_icode.hrl").
|
-include("aeso_icode.hrl").
|
||||||
|
-include("aeso_utils.hrl").
|
||||||
|
|
||||||
|
|
||||||
-type option() :: pp_sophia_code
|
-type option() :: pp_sophia_code
|
||||||
@@ -137,8 +138,9 @@ from_string1(aevm, ContractString, Options) ->
|
|||||||
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)};
|
{ok, maybe_generate_aci(Res, FoldedTypedAst, Options)};
|
||||||
from_string1(fate, ContractString, Options) ->
|
from_string1(fate, ContractString, Options) ->
|
||||||
#{ fcode := FCode
|
#{ fcode := FCode
|
||||||
|
, fcode_env := #{child_con_env := ChildContracts}
|
||||||
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
|
, folded_typed_ast := FoldedTypedAst } = string_to_code(ContractString, Options),
|
||||||
FateCode = aeso_fcode_to_fate:compile(FCode, Options),
|
FateCode = aeso_fcode_to_fate:compile(ChildContracts, FCode, Options),
|
||||||
pp_assembler(fate, FateCode, Options),
|
pp_assembler(fate, FateCode, Options),
|
||||||
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
{ok, Version} = version(),
|
{ok, Version} = version(),
|
||||||
@@ -178,8 +180,9 @@ string_to_code(ContractString, Options) ->
|
|||||||
, type_env => TypeEnv
|
, type_env => TypeEnv
|
||||||
, ast => Ast };
|
, ast => Ast };
|
||||||
fate ->
|
fate ->
|
||||||
Fcode = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, Options),
|
{Env, Fcode} = aeso_ast_to_fcode:ast_to_fcode(UnfoldedTypedAst, [{original_src, ContractString}|Options]),
|
||||||
#{ fcode => Fcode
|
#{ fcode => Fcode
|
||||||
|
, fcode_env => Env
|
||||||
, unfolded_typed_ast => UnfoldedTypedAst
|
, unfolded_typed_ast => UnfoldedTypedAst
|
||||||
, folded_typed_ast => FoldedTypedAst
|
, folded_typed_ast => FoldedTypedAst
|
||||||
, type_env => TypeEnv
|
, type_env => TypeEnv
|
||||||
@@ -241,8 +244,9 @@ check_call1(ContractString0, FunName, Args, Options) ->
|
|||||||
fate ->
|
fate ->
|
||||||
%% First check the contract without the __call function
|
%% First check the contract without the __call function
|
||||||
#{ fcode := OrgFcode
|
#{ fcode := OrgFcode
|
||||||
|
, fcode_env := #{child_con_env := ChildContracts}
|
||||||
, ast := Ast } = string_to_code(ContractString0, Options),
|
, ast := Ast } = string_to_code(ContractString0, Options),
|
||||||
FateCode = aeso_fcode_to_fate:compile(OrgFcode, []),
|
FateCode = aeso_fcode_to_fate:compile(ChildContracts, OrgFcode, []),
|
||||||
%% collect all hashes and compute the first name without hash collision to
|
%% collect all hashes and compute the first name without hash collision to
|
||||||
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
SymbolHashes = maps:keys(aeb_fate_code:symbols(FateCode)),
|
||||||
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
CallName = first_none_match(?CALL_NAME, SymbolHashes,
|
||||||
@@ -468,7 +472,7 @@ error_missing_call_function() ->
|
|||||||
Msg = "Internal error: missing '__call'-function",
|
Msg = "Internal error: missing '__call'-function",
|
||||||
aeso_errors:throw(aeso_errors:new(internal_error, Msg)).
|
aeso_errors:throw(aeso_errors:new(internal_error, Msg)).
|
||||||
|
|
||||||
get_call_type([{contract, _, _, Defs}]) ->
|
get_call_type([{Contract, _, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
case [ {lists:last(QFunName), FunType}
|
case [ {lists:last(QFunName), FunType}
|
||||||
|| {letfun, _, {id, _, ?CALL_NAME}, [], _Ret,
|
|| {letfun, _, {id, _, ?CALL_NAME}, [], _Ret,
|
||||||
{typed, _,
|
{typed, _,
|
||||||
@@ -482,7 +486,7 @@ get_call_type([_ | Contracts]) ->
|
|||||||
get_call_type(Contracts).
|
get_call_type(Contracts).
|
||||||
|
|
||||||
-dialyzer({nowarn_function, get_decode_type/2}).
|
-dialyzer({nowarn_function, get_decode_type/2}).
|
||||||
get_decode_type(FunName, [{contract, Ann, _, Defs}]) ->
|
get_decode_type(FunName, [{Contract, Ann, _, Defs}]) when ?IS_CONTRACT_HEAD(Contract) ->
|
||||||
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
|
GetType = fun({letfun, _, {id, _, Name}, Args, Ret, _}) when Name == FunName -> [{Args, Ret}];
|
||||||
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
|
({fun_decl, _, {id, _, Name}, {fun_t, _, _, Args, Ret}}) when Name == FunName -> [{Args, Ret}];
|
||||||
(_) -> [] end,
|
(_) -> [] end,
|
||||||
|
|||||||
+187
-50
@@ -9,7 +9,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_fcode_to_fate).
|
-module(aeso_fcode_to_fate).
|
||||||
|
|
||||||
-export([compile/2, term_to_fate/1]).
|
-export([compile/2, compile/3, term_to_fate/1, term_to_fate/2]).
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-export([optimize_fun/4, to_basic_blocks/1]).
|
-export([optimize_fun/4, to_basic_blocks/1]).
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
-define(s(N), {store, N}).
|
-define(s(N), {store, N}).
|
||||||
-define(void, {var, 9999}).
|
-define(void, {var, 9999}).
|
||||||
|
|
||||||
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true }).
|
-record(env, { contract, vars = [], locals = [], current_function, tailpos = true, child_contracts = #{}, options = []}).
|
||||||
|
|
||||||
%% -- Debugging --------------------------------------------------------------
|
%% -- Debugging --------------------------------------------------------------
|
||||||
|
|
||||||
@@ -70,9 +70,11 @@ code_error(Err) ->
|
|||||||
|
|
||||||
%% @doc Main entry point.
|
%% @doc Main entry point.
|
||||||
compile(FCode, Options) ->
|
compile(FCode, Options) ->
|
||||||
|
compile(#{}, FCode, Options).
|
||||||
|
compile(ChildContracts, FCode, Options) ->
|
||||||
#{ contract_name := ContractName,
|
#{ contract_name := ContractName,
|
||||||
functions := Functions } = FCode,
|
functions := Functions } = FCode,
|
||||||
SFuns = functions_to_scode(ContractName, Functions, Options),
|
SFuns = functions_to_scode(ChildContracts, ContractName, Functions, Options),
|
||||||
SFuns1 = optimize_scode(SFuns, Options),
|
SFuns1 = optimize_scode(SFuns, Options),
|
||||||
FateCode = to_basic_blocks(SFuns1),
|
FateCode = to_basic_blocks(SFuns1),
|
||||||
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
|
?debug(compile, Options, "~s\n", [aeb_fate_asm:pp(FateCode)]),
|
||||||
@@ -85,19 +87,20 @@ make_function_name(event) -> <<"Chain.event">>;
|
|||||||
make_function_name({entrypoint, Name}) -> Name;
|
make_function_name({entrypoint, Name}) -> Name;
|
||||||
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
|
make_function_name({local_fun, Xs}) -> list_to_binary("." ++ string:join(Xs, ".")).
|
||||||
|
|
||||||
functions_to_scode(ContractName, Functions, Options) ->
|
functions_to_scode(ChildContracts, ContractName, Functions, Options) ->
|
||||||
FunNames = maps:keys(Functions),
|
FunNames = maps:keys(Functions),
|
||||||
maps:from_list(
|
maps:from_list(
|
||||||
[ {make_function_name(Name), function_to_scode(ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
|
[ {make_function_name(Name), function_to_scode(ChildContracts, ContractName, FunNames, Name, Attrs, Args, Body, Type, Options)}
|
||||||
|| {Name, #{args := Args,
|
|| {Name, #{args := Args,
|
||||||
body := Body,
|
body := Body,
|
||||||
attrs := Attrs,
|
attrs := Attrs,
|
||||||
return := Type}} <- maps:to_list(Functions)]).
|
return := Type}} <- maps:to_list(Functions)]).
|
||||||
|
|
||||||
function_to_scode(ContractName, Functions, Name, Attrs0, Args, Body, ResType, _Options) ->
|
function_to_scode(ChildContracts, ContractName, Functions, Name, Attrs0, Args, Body, ResType, Options) ->
|
||||||
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
{ArgTypes, ResType1} = typesig_to_scode(Args, ResType),
|
||||||
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
|
Attrs = Attrs0 -- [stateful], %% Only track private and payable from here.
|
||||||
SCode = to_scode(init_env(ContractName, Functions, Name, Args), Body),
|
Env = init_env(ChildContracts, ContractName, Functions, Name, Args, Options),
|
||||||
|
SCode = to_scode(Env, Body),
|
||||||
{Attrs, {ArgTypes, ResType1}, SCode}.
|
{Attrs, {ArgTypes, ResType1}, SCode}.
|
||||||
|
|
||||||
-define(tvars, '$tvars').
|
-define(tvars, '$tvars').
|
||||||
@@ -133,7 +136,9 @@ type_to_scode({tvar, X}) ->
|
|||||||
put(?tvars, {I + 1, Vars#{ X => I }}),
|
put(?tvars, {I + 1, Vars#{ X => I }}),
|
||||||
{tvar, I};
|
{tvar, I};
|
||||||
J -> {tvar, J}
|
J -> {tvar, J}
|
||||||
end.
|
end;
|
||||||
|
type_to_scode(L) when is_list(L) -> {tuple, types_to_scode(L)}.
|
||||||
|
|
||||||
|
|
||||||
types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
|
types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
|
||||||
|
|
||||||
@@ -142,11 +147,13 @@ types_to_scode(Ts) -> lists:map(fun type_to_scode/1, Ts).
|
|||||||
|
|
||||||
%% -- Environment functions --
|
%% -- Environment functions --
|
||||||
|
|
||||||
init_env(ContractName, FunNames, Name, Args) ->
|
init_env(ChildContracts, ContractName, FunNames, Name, Args, Options) ->
|
||||||
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
#env{ vars = [ {X, {arg, I}} || {I, {X, _}} <- with_ixs(Args) ],
|
||||||
contract = ContractName,
|
contract = ContractName,
|
||||||
|
child_contracts = ChildContracts,
|
||||||
locals = FunNames,
|
locals = FunNames,
|
||||||
current_function = Name,
|
current_function = Name,
|
||||||
|
options = Options,
|
||||||
tailpos = true }.
|
tailpos = true }.
|
||||||
|
|
||||||
next_var(#env{ vars = Vars }) ->
|
next_var(#env{ vars = Vars }) ->
|
||||||
@@ -169,7 +176,7 @@ lookup_var(#env{vars = Vars}, X) ->
|
|||||||
|
|
||||||
%% -- The compiler --
|
%% -- The compiler --
|
||||||
|
|
||||||
lit_to_fate(L) ->
|
lit_to_fate(Env, L) ->
|
||||||
case L of
|
case L of
|
||||||
{int, N} -> aeb_fate_data:make_integer(N);
|
{int, N} -> aeb_fate_data:make_integer(N);
|
||||||
{string, S} -> aeb_fate_data:make_string(S);
|
{string, S} -> aeb_fate_data:make_string(S);
|
||||||
@@ -179,63 +186,80 @@ lit_to_fate(L) ->
|
|||||||
{contract_pubkey, K} -> aeb_fate_data:make_contract(K);
|
{contract_pubkey, K} -> aeb_fate_data:make_contract(K);
|
||||||
{oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
|
{oracle_pubkey, K} -> aeb_fate_data:make_oracle(K);
|
||||||
{oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H);
|
{oracle_query_id, H} -> aeb_fate_data:make_oracle_query(H);
|
||||||
|
{contract_code, C} ->
|
||||||
|
Options = Env#env.options,
|
||||||
|
FCode = maps:get(C, Env#env.child_contracts),
|
||||||
|
FateCode = compile(Env#env.child_contracts, FCode, Options),
|
||||||
|
ByteCode = aeb_fate_code:serialize(FateCode, []),
|
||||||
|
{ok, Version} = aeso_compiler:version(),
|
||||||
|
OriginalSourceCode = proplists:get_value(original_src, Options, ""),
|
||||||
|
Code = #{byte_code => ByteCode,
|
||||||
|
compiler_version => Version,
|
||||||
|
source_hash => crypto:hash(sha256, OriginalSourceCode ++ [0] ++ C),
|
||||||
|
type_info => [],
|
||||||
|
abi_version => aeb_fate_abi:abi_version(),
|
||||||
|
payable => maps:get(payable, FCode)
|
||||||
|
},
|
||||||
|
Serialized = aeser_contract_code:serialize(Code),
|
||||||
|
aeb_fate_data:make_contract_bytearray(Serialized);
|
||||||
{typerep, T} -> aeb_fate_data:make_typerep(type_to_scode(T))
|
{typerep, T} -> aeb_fate_data:make_typerep(type_to_scode(T))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
term_to_fate(E) -> term_to_fate(#{}, E).
|
term_to_fate(E) -> term_to_fate(#env{}, #{}, E).
|
||||||
|
term_to_fate(GlobEnv, E) -> term_to_fate(GlobEnv, #{}, E).
|
||||||
|
|
||||||
term_to_fate(_Env, {lit, L}) ->
|
term_to_fate(GlobEnv, _Env, {lit, L}) ->
|
||||||
lit_to_fate(L);
|
lit_to_fate(GlobEnv, L);
|
||||||
%% negative literals are parsed as 0 - N
|
%% negative literals are parsed as 0 - N
|
||||||
term_to_fate(_Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
|
term_to_fate(_GlobEnv, _Env, {op, '-', [{lit, {int, 0}}, {lit, {int, N}}]}) ->
|
||||||
aeb_fate_data:make_integer(-N);
|
aeb_fate_data:make_integer(-N);
|
||||||
term_to_fate(_Env, nil) ->
|
term_to_fate(_GlobEnv, _Env, nil) ->
|
||||||
aeb_fate_data:make_list([]);
|
aeb_fate_data:make_list([]);
|
||||||
term_to_fate(Env, {op, '::', [Hd, Tl]}) ->
|
term_to_fate(GlobEnv, Env, {op, '::', [Hd, Tl]}) ->
|
||||||
%% The Tl will translate into a list, because FATE lists are just lists
|
%% The Tl will translate into a list, because FATE lists are just lists
|
||||||
[term_to_fate(Env, Hd) | term_to_fate(Env, Tl)];
|
[term_to_fate(GlobEnv, Env, Hd) | term_to_fate(GlobEnv, Env, Tl)];
|
||||||
term_to_fate(Env, {tuple, As}) ->
|
term_to_fate(GlobEnv, Env, {tuple, As}) ->
|
||||||
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(Env, A) || A<-As]));
|
aeb_fate_data:make_tuple(list_to_tuple([ term_to_fate(GlobEnv, Env, A) || A<-As]));
|
||||||
term_to_fate(Env, {con, Ar, I, As}) ->
|
term_to_fate(GlobEnv, Env, {con, Ar, I, As}) ->
|
||||||
FateAs = [ term_to_fate(Env, A) || A <- As ],
|
FateAs = [ term_to_fate(GlobEnv, Env, A) || A <- As ],
|
||||||
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
|
aeb_fate_data:make_variant(Ar, I, list_to_tuple(FateAs));
|
||||||
term_to_fate(_Env, {builtin, bits_all, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, bits_all, []}) ->
|
||||||
aeb_fate_data:make_bits(-1);
|
aeb_fate_data:make_bits(-1);
|
||||||
term_to_fate(_Env, {builtin, bits_none, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, bits_none, []}) ->
|
||||||
aeb_fate_data:make_bits(0);
|
aeb_fate_data:make_bits(0);
|
||||||
term_to_fate(_Env, {op, bits_set, [B, I]}) ->
|
term_to_fate(GlobEnv, _Env, {op, bits_set, [B, I]}) ->
|
||||||
{bits, N} = term_to_fate(B),
|
{bits, N} = term_to_fate(GlobEnv, B),
|
||||||
J = term_to_fate(I),
|
J = term_to_fate(GlobEnv, I),
|
||||||
{bits, N bor (1 bsl J)};
|
{bits, N bor (1 bsl J)};
|
||||||
term_to_fate(_Env, {op, bits_clear, [B, I]}) ->
|
term_to_fate(GlobEnv, _Env, {op, bits_clear, [B, I]}) ->
|
||||||
{bits, N} = term_to_fate(B),
|
{bits, N} = term_to_fate(GlobEnv, B),
|
||||||
J = term_to_fate(I),
|
J = term_to_fate(GlobEnv, I),
|
||||||
{bits, N band bnot (1 bsl J)};
|
{bits, N band bnot (1 bsl J)};
|
||||||
term_to_fate(Env, {'let', X, E, Body}) ->
|
term_to_fate(GlobEnv, Env, {'let', X, E, Body}) ->
|
||||||
Env1 = Env#{ X => term_to_fate(Env, E) },
|
Env1 = Env#{ X => term_to_fate(GlobEnv, Env, E) },
|
||||||
term_to_fate(Env1, Body);
|
term_to_fate(GlobEnv, Env1, Body);
|
||||||
term_to_fate(Env, {var, X}) ->
|
term_to_fate(_GlobEnv, Env, {var, X}) ->
|
||||||
case maps:get(X, Env, undefined) of
|
case maps:get(X, Env, undefined) of
|
||||||
undefined -> throw(not_a_fate_value);
|
undefined -> throw(not_a_fate_value);
|
||||||
V -> V
|
V -> V
|
||||||
end;
|
end;
|
||||||
term_to_fate(_Env, {builtin, map_empty, []}) ->
|
term_to_fate(_GlobEnv, _Env, {builtin, map_empty, []}) ->
|
||||||
aeb_fate_data:make_map(#{});
|
aeb_fate_data:make_map(#{});
|
||||||
term_to_fate(Env, {op, map_set, [M, K, V]}) ->
|
term_to_fate(GlobEnv, Env, {op, map_set, [M, K, V]}) ->
|
||||||
Map = term_to_fate(Env, M),
|
Map = term_to_fate(GlobEnv, Env, M),
|
||||||
Map#{term_to_fate(Env, K) => term_to_fate(Env, V)};
|
Map#{term_to_fate(GlobEnv, Env, K) => term_to_fate(GlobEnv, Env, V)};
|
||||||
term_to_fate(_Env, _) ->
|
term_to_fate(_GlobEnv, _Env, _) ->
|
||||||
throw(not_a_fate_value).
|
throw(not_a_fate_value).
|
||||||
|
|
||||||
to_scode(Env, T) ->
|
to_scode(Env, T) ->
|
||||||
try term_to_fate(T) of
|
try term_to_fate(Env, T) of
|
||||||
V -> [push(?i(V))]
|
V -> [push(?i(V))]
|
||||||
catch throw:not_a_fate_value ->
|
catch throw:not_a_fate_value ->
|
||||||
to_scode1(Env, T)
|
to_scode1(Env, T)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
to_scode1(_Env, {lit, L}) ->
|
to_scode1(Env, {lit, L}) ->
|
||||||
[push(?i(lit_to_fate(L)))];
|
[push(?i(lit_to_fate(Env, L)))];
|
||||||
|
|
||||||
to_scode1(_Env, nil) ->
|
to_scode1(_Env, nil) ->
|
||||||
[aeb_fate_ops:nil(?a)];
|
[aeb_fate_ops:nil(?a)];
|
||||||
@@ -302,11 +326,13 @@ to_scode1(Env, {funcall, Fun, Args}) ->
|
|||||||
to_scode1(Env, {builtin, B, Args}) ->
|
to_scode1(Env, {builtin, B, Args}) ->
|
||||||
builtin_to_scode(Env, B, Args);
|
builtin_to_scode(Env, B, Args);
|
||||||
|
|
||||||
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value | Args]}) ->
|
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
|
||||||
Lbl = make_function_id(Fun),
|
Lbl = make_function_id(Fun),
|
||||||
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
||||||
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
||||||
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
||||||
|
case Protected of
|
||||||
|
{lit, {bool, false}} ->
|
||||||
case Gas of
|
case Gas of
|
||||||
{builtin, call_gas_left, _} ->
|
{builtin, call_gas_left, _} ->
|
||||||
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
||||||
@@ -315,6 +341,13 @@ to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value | Args]}) ->
|
|||||||
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
||||||
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
||||||
end;
|
end;
|
||||||
|
{lit, {bool, true}} ->
|
||||||
|
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
|
||||||
|
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
|
||||||
|
_ ->
|
||||||
|
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
|
||||||
|
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
|
||||||
|
end;
|
||||||
|
|
||||||
to_scode1(_Env, {get_state, Reg}) ->
|
to_scode1(_Env, {get_state, Reg}) ->
|
||||||
[push(?s(Reg))];
|
[push(?s(Reg))];
|
||||||
@@ -494,10 +527,14 @@ builtin_to_scode(_Env, call_value, []) ->
|
|||||||
[aeb_fate_ops:call_value(?a)];
|
[aeb_fate_ops:call_value(?a)];
|
||||||
builtin_to_scode(_Env, call_gas_price, []) ->
|
builtin_to_scode(_Env, call_gas_price, []) ->
|
||||||
[aeb_fate_ops:gasprice(?a)];
|
[aeb_fate_ops:gasprice(?a)];
|
||||||
|
builtin_to_scode(_Env, call_fee, []) ->
|
||||||
|
[aeb_fate_ops:fee(?a)];
|
||||||
builtin_to_scode(_Env, call_gas_left, []) ->
|
builtin_to_scode(_Env, call_gas_left, []) ->
|
||||||
[aeb_fate_ops:gas(?a)];
|
[aeb_fate_ops:gas(?a)];
|
||||||
builtin_to_scode(Env, oracle_register, [_Sign,_Account,_QFee,_TTL,_QType,_RType] = Args) ->
|
builtin_to_scode(Env, oracle_register, [_Sign,_Account,_QFee,_TTL,_QType,_RType] = Args) ->
|
||||||
call_to_scode(Env, aeb_fate_ops:oracle_register(?a, ?a, ?a, ?a, ?a, ?a, ?a), Args);
|
call_to_scode(Env, aeb_fate_ops:oracle_register(?a, ?a, ?a, ?a, ?a, ?a, ?a), Args);
|
||||||
|
builtin_to_scode(Env, oracle_expiry, [_Oracle] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:oracle_expiry(?a, ?a), Args);
|
||||||
builtin_to_scode(Env, oracle_query_fee, [_Oracle] = Args) ->
|
builtin_to_scode(Env, oracle_query_fee, [_Oracle] = Args) ->
|
||||||
call_to_scode(Env, aeb_fate_ops:oracle_query_fee(?a, ?a), Args);
|
call_to_scode(Env, aeb_fate_ops:oracle_query_fee(?a, ?a), Args);
|
||||||
builtin_to_scode(Env, oracle_query, [_Oracle, _Question, _QFee, _QTTL, _RTTL, _QType, _RType] = Args) ->
|
builtin_to_scode(Env, oracle_query, [_Oracle, _Question, _QFee, _QTTL, _RTTL, _QType, _RType] = Args) ->
|
||||||
@@ -536,8 +573,35 @@ builtin_to_scode(Env, aens_transfer, [_Sign, _From, _To, _Name] = Args) ->
|
|||||||
builtin_to_scode(Env, aens_revoke, [_Sign, _Account, _Name] = Args) ->
|
builtin_to_scode(Env, aens_revoke, [_Sign, _Account, _Name] = Args) ->
|
||||||
call_to_scode(Env, [aeb_fate_ops:aens_revoke(?a, ?a, ?a),
|
call_to_scode(Env, [aeb_fate_ops:aens_revoke(?a, ?a, ?a),
|
||||||
tuple(0)], Args);
|
tuple(0)], Args);
|
||||||
|
builtin_to_scode(Env, aens_update, [_Sign, _Account, _NameString, _TTL, _ClientTTL, _Pointers] = Args) ->
|
||||||
|
call_to_scode(Env, [aeb_fate_ops:aens_update(?a, ?a, ?a, ?a, ?a, ?a),
|
||||||
|
tuple(0)], Args);
|
||||||
|
builtin_to_scode(Env, aens_lookup, [_Name] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:aens_lookup(?a, ?a), Args);
|
||||||
builtin_to_scode(_Env, auth_tx_hash, []) ->
|
builtin_to_scode(_Env, auth_tx_hash, []) ->
|
||||||
[aeb_fate_ops:auth_tx_hash(?a)].
|
[aeb_fate_ops:auth_tx_hash(?a)];
|
||||||
|
builtin_to_scode(_Env, auth_tx, []) ->
|
||||||
|
[aeb_fate_ops:auth_tx(?a)];
|
||||||
|
builtin_to_scode(Env, chain_bytecode_hash, [_Addr] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:bytecode_hash(?a, ?a), Args);
|
||||||
|
builtin_to_scode(Env, chain_clone,
|
||||||
|
[InitArgsT, GasCap, Value, Prot, Contract | InitArgs]) ->
|
||||||
|
case GasCap of
|
||||||
|
{builtin, call_gas_left, _} ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:clone(?a, ?a, ?a, ?a),
|
||||||
|
[Contract, InitArgsT, Value, Prot | InitArgs]
|
||||||
|
);
|
||||||
|
_ ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:clone_g(?a, ?a, ?a, ?a, ?a),
|
||||||
|
[Contract, InitArgsT, Value, GasCap, Prot | InitArgs]
|
||||||
|
)
|
||||||
|
end;
|
||||||
|
|
||||||
|
builtin_to_scode(Env, chain_create,
|
||||||
|
[ Code, InitArgsT, Value | InitArgs]) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:create(?a, ?a, ?a),
|
||||||
|
[Code, InitArgsT, Value | InitArgs]
|
||||||
|
).
|
||||||
|
|
||||||
%% -- Operators --
|
%% -- Operators --
|
||||||
|
|
||||||
@@ -564,8 +628,14 @@ op_to_scode(map_to_list) -> aeb_fate_ops:map_to_list(?a, ?a);
|
|||||||
op_to_scode(map_delete) -> aeb_fate_ops:map_delete(?a, ?a, ?a);
|
op_to_scode(map_delete) -> aeb_fate_ops:map_delete(?a, ?a, ?a);
|
||||||
op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
|
op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
|
||||||
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
|
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
|
||||||
op_to_scode(string_length) -> aeb_fate_ops:str_length(?a, ?a);
|
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
|
||||||
op_to_scode(string_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
|
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
|
||||||
|
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
|
||||||
|
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
|
||||||
|
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
|
||||||
|
op_to_scode(stringinternal_to_upper) -> aeb_fate_ops:str_to_upper(?a, ?a);
|
||||||
|
op_to_scode(char_to_int) -> aeb_fate_ops:char_to_int(?a, ?a);
|
||||||
|
op_to_scode(char_from_int) -> aeb_fate_ops:char_from_int(?a, ?a);
|
||||||
op_to_scode(bits_set) -> aeb_fate_ops:bits_set(?a, ?a, ?a);
|
op_to_scode(bits_set) -> aeb_fate_ops:bits_set(?a, ?a, ?a);
|
||||||
op_to_scode(bits_clear) -> aeb_fate_ops:bits_clear(?a, ?a, ?a);
|
op_to_scode(bits_clear) -> aeb_fate_ops:bits_clear(?a, ?a, ?a);
|
||||||
op_to_scode(bits_test) -> aeb_fate_ops:bits_test(?a, ?a, ?a);
|
op_to_scode(bits_test) -> aeb_fate_ops:bits_test(?a, ?a, ?a);
|
||||||
@@ -584,9 +654,33 @@ op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a,
|
|||||||
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
op_to_scode(string_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(string_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a).
|
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_neg) -> aeb_fate_ops:bls12_381_g1_neg(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_norm) -> aeb_fate_ops:bls12_381_g1_norm(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_valid) -> aeb_fate_ops:bls12_381_g1_valid(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_is_zero) -> aeb_fate_ops:bls12_381_g1_is_zero(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_add) -> aeb_fate_ops:bls12_381_g1_add(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_mul) -> aeb_fate_ops:bls12_381_g1_mul(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_neg) -> aeb_fate_ops:bls12_381_g2_neg(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_norm) -> aeb_fate_ops:bls12_381_g2_norm(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_valid) -> aeb_fate_ops:bls12_381_g2_valid(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_is_zero) -> aeb_fate_ops:bls12_381_g2_is_zero(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_add) -> aeb_fate_ops:bls12_381_g2_add(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_mul) -> aeb_fate_ops:bls12_381_g2_mul(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_inv) -> aeb_fate_ops:bls12_381_gt_inv(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_add) -> aeb_fate_ops:bls12_381_gt_add(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_mul) -> aeb_fate_ops:bls12_381_gt_mul(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_pow) -> aeb_fate_ops:bls12_381_gt_pow(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_is_one) -> aeb_fate_ops:bls12_381_gt_is_one(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_pairing) -> aeb_fate_ops:bls12_381_pairing(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_miller_loop) -> aeb_fate_ops:bls12_381_miller_loop(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_final_exp) -> aeb_fate_ops:bls12_381_final_exp(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_int_to_fr) -> aeb_fate_ops:bls12_381_int_to_fr(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_int_to_fp) -> aeb_fate_ops:bls12_381_int_to_fp(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_fr_to_int) -> aeb_fate_ops:bls12_381_fr_to_int(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_fp_to_int) -> aeb_fate_ops:bls12_381_fp_to_int(?a, ?a).
|
||||||
|
|
||||||
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
|
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
|
||||||
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
||||||
@@ -734,6 +828,7 @@ attributes(I) ->
|
|||||||
{'CALL', A} -> Impure(?a, [A]);
|
{'CALL', A} -> Impure(?a, [A]);
|
||||||
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
||||||
{'CALL_GR', A, _, B, C, D, E} -> Impure(?a, [A, B, C, D, E]);
|
{'CALL_GR', A, _, B, C, D, E} -> Impure(?a, [A, B, C, D, E]);
|
||||||
|
{'CALL_PGR', A, _, B, C, D, E, F} -> Impure(?a, [A, B, C, D, E, F]);
|
||||||
{'CALL_T', A} -> Impure(pc, [A]);
|
{'CALL_T', A} -> Impure(pc, [A]);
|
||||||
{'CALL_VALUE', A} -> Pure(A, []);
|
{'CALL_VALUE', A} -> Pure(A, []);
|
||||||
{'JUMP', _} -> Impure(pc, []);
|
{'JUMP', _} -> Impure(pc, []);
|
||||||
@@ -815,6 +910,7 @@ attributes(I) ->
|
|||||||
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
||||||
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
||||||
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
||||||
|
{'AUTH_TX', A} -> Pure(A, []);
|
||||||
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
||||||
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
|
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
|
||||||
@@ -831,6 +927,7 @@ attributes(I) ->
|
|||||||
{'ORIGIN', A} -> Pure(A, []);
|
{'ORIGIN', A} -> Pure(A, []);
|
||||||
{'CALLER', A} -> Pure(A, []);
|
{'CALLER', A} -> Pure(A, []);
|
||||||
{'GASPRICE', A} -> Pure(A, []);
|
{'GASPRICE', A} -> Pure(A, []);
|
||||||
|
{'FEE', A} -> Pure(A, []);
|
||||||
{'BLOCKHASH', A, B} -> Pure(A, [B]);
|
{'BLOCKHASH', A, B} -> Pure(A, [B]);
|
||||||
{'BENEFICIARY', A} -> Pure(A, []);
|
{'BENEFICIARY', A} -> Pure(A, []);
|
||||||
{'TIMESTAMP', A} -> Pure(A, []);
|
{'TIMESTAMP', A} -> Pure(A, []);
|
||||||
@@ -853,12 +950,48 @@ attributes(I) ->
|
|||||||
{'ORACLE_GET_ANSWER', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
|
{'ORACLE_GET_ANSWER', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
|
||||||
{'ORACLE_GET_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]);
|
{'ORACLE_GET_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]);
|
||||||
{'ORACLE_QUERY_FEE', A, B} -> Pure(A, [B]);
|
{'ORACLE_QUERY_FEE', A, B} -> Pure(A, [B]);
|
||||||
{'AENS_RESOLVE', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'ORACLE_EXPIRY', A, B} -> Impure(A, [B]);
|
||||||
|
{'AENS_RESOLVE', A, B, C, D} -> Impure(A, [B, C, D]);
|
||||||
{'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]);
|
{'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]);
|
||||||
{'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]);
|
{'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]);
|
||||||
'AENS_UPDATE' -> Impure(none, []);%% TODO
|
{'AENS_UPDATE', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]);
|
||||||
{'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]);
|
{'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]);
|
||||||
{'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]);
|
{'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]);
|
||||||
|
{'AENS_LOOKUP', A, B} -> Impure(A, [B]);
|
||||||
|
{'BLS12_381_G1_NEG', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G1_NORM', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G1_VALID', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G1_IS_ZERO', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G1_ADD', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_G1_MUL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_G2_NEG', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G2_NORM', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G2_VALID', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G2_IS_ZERO', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G2_ADD', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_G2_MUL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_GT_INV', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_GT_ADD', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_GT_MUL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_GT_POW', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_GT_IS_ONE', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_PAIRING', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_MILLER_LOOP', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_FINAL_EXP', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_INT_TO_FR', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_INT_TO_FP', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_FR_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_FP_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
|
{'STR_TO_LIST', A, B} -> Pure(A, [B]);
|
||||||
|
{'STR_FROM_LIST', A, B} -> Pure(A, [B]);
|
||||||
|
{'STR_TO_UPPER', A, B} -> Pure(A, [B]);
|
||||||
|
{'STR_TO_LOWER', A, B} -> Pure(A, [B]);
|
||||||
|
{'CHAR_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
|
{'CHAR_FROM_INT', A, B} -> Pure(A, [B]);
|
||||||
|
{'CREATE', A, B, C} -> Impure(?a, [A, B, C]);
|
||||||
|
{'CLONE', A, B, C, D} -> Impure(?a, [A, B, C, D]);
|
||||||
|
{'CLONE_G', A, B, C, D, E} -> Impure(?a, [A, B, C, D, E]);
|
||||||
|
{'BYTECODE_HASH', A, B} -> Impure(A, [B]);
|
||||||
{'ABORT', A} -> Impure(pc, A);
|
{'ABORT', A} -> Impure(pc, A);
|
||||||
{'EXIT', A} -> Impure(pc, A);
|
{'EXIT', A} -> Impure(pc, A);
|
||||||
'NOP' -> Pure(none, [])
|
'NOP' -> Pure(none, [])
|
||||||
@@ -1611,6 +1744,10 @@ split_calls(Ref, [], Acc, Blocks) ->
|
|||||||
split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
|
split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
|
||||||
element(1, I) == 'CALL_R';
|
element(1, I) == 'CALL_R';
|
||||||
element(1, I) == 'CALL_GR';
|
element(1, I) == 'CALL_GR';
|
||||||
|
element(1, I) == 'CALL_PGR';
|
||||||
|
element(1, I) == 'CREATE';
|
||||||
|
element(1, I) == 'CLONE';
|
||||||
|
element(1, I) == 'CLONE_G';
|
||||||
element(1, I) == 'jumpif' ->
|
element(1, I) == 'jumpif' ->
|
||||||
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
|
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
|
||||||
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
|
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
|
||||||
|
|||||||
+6
-2
@@ -88,13 +88,18 @@ builtin_types() ->
|
|||||||
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
||||||
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
||||||
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
||||||
|
, ["AENS", "pointee"] => fun([]) -> {variant, [[word], [word], [word]]} end
|
||||||
}.
|
}.
|
||||||
|
|
||||||
builtin_constructors() ->
|
builtin_constructors() ->
|
||||||
#{ ["RelativeTTL"] => 0
|
#{ ["RelativeTTL"] => 0
|
||||||
, ["FixedTTL"] => 1
|
, ["FixedTTL"] => 1
|
||||||
, ["None"] => 0
|
, ["None"] => 0
|
||||||
, ["Some"] => 1 }.
|
, ["Some"] => 1
|
||||||
|
, ["AccountPointee"] => 0
|
||||||
|
, ["OraclePointee"] => 1
|
||||||
|
, ["ContractPointee"] => 2
|
||||||
|
}.
|
||||||
|
|
||||||
map_typerep(K, V) ->
|
map_typerep(K, V) ->
|
||||||
{map, K, V}.
|
{map, K, V}.
|
||||||
@@ -146,4 +151,3 @@ get_constructor_tag(Name, #{constructors := Constructors}) ->
|
|||||||
undefined -> error({undefined_constructor, Name});
|
undefined -> error({undefined_constructor, Name});
|
||||||
Tag -> Tag
|
Tag -> Tag
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|||||||
+15
-7
@@ -74,25 +74,31 @@
|
|||||||
%% first argument. I.e. no backtracking to the second argument if the first
|
%% first argument. I.e. no backtracking to the second argument if the first
|
||||||
%% fails.
|
%% fails.
|
||||||
|
|
||||||
|
trampoline({bounce, Cont}) when is_function(Cont, 0) ->
|
||||||
|
trampoline(Cont());
|
||||||
|
trampoline(Res) ->
|
||||||
|
Res.
|
||||||
|
-define(BOUNCE(X), {bounce, fun() -> X end}).
|
||||||
|
|
||||||
%% Apply a parser to its continuation. This compiles a parser to its low-level representation.
|
%% Apply a parser to its continuation. This compiles a parser to its low-level representation.
|
||||||
-spec apply_p(parser(A), fun((A) -> parser1(B))) -> parser1(B).
|
-spec apply_p(parser(A), fun((A) -> parser1(B))) -> parser1(B).
|
||||||
apply_p(?lazy(F), K) -> apply_p(F(), K);
|
apply_p(?lazy(F), K) -> apply_p(F(), K);
|
||||||
apply_p(?fail(Err), _) -> {fail, Err};
|
apply_p(?fail(Err), _) -> {fail, Err};
|
||||||
apply_p(?choice([P | Ps]), K) -> lists:foldl(fun(Q, R) -> choice1(apply_p(Q, K), R) end,
|
apply_p(?choice([P | Ps]), K) -> lists:foldl(fun(Q, R) -> choice1(trampoline(apply_p(Q, K)), R) end,
|
||||||
apply_p(P, K), Ps);
|
trampoline(apply_p(P, K)), Ps);
|
||||||
apply_p(?bind(P, F), K) -> apply_p(P, fun(X) -> apply_p(F(X), K) end);
|
apply_p(?bind(P, F), K) -> apply_p(P, fun(X) -> apply_p(F(X), K) end);
|
||||||
apply_p(?right(P, Q), K) -> apply_p(P, fun(_) -> apply_p(Q, K) end);
|
apply_p(?right(P, Q), K) -> apply_p(P, fun(_) -> apply_p(Q, K) end);
|
||||||
apply_p(?left(P, Q), K) -> apply_p(P, fun(X) -> apply_p(Q, fun(_) -> K(X) end) end);
|
apply_p(?left(P, Q), K) -> apply_p(P, fun(X) -> apply_p(Q, fun(_) -> K(X) end) end);
|
||||||
apply_p(?map(F, P), K) -> apply_p(P, fun(X) -> K(F(X)) end);
|
apply_p(?map(F, P), K) -> apply_p(P, fun(X) -> K(F(X)) end);
|
||||||
apply_p(?layout, K) -> {layout, K, {fail, {expected, layout_block}}};
|
apply_p(?layout, K) -> {layout, K, {fail, {expected, layout_block}}};
|
||||||
apply_p(?tok(Atom), K) -> {tok_bind, #{Atom => K}};
|
apply_p(?tok(Atom), K) -> {tok_bind, #{Atom => K}};
|
||||||
apply_p(?return(X), K) -> K(X);
|
apply_p(?return(X), K) -> ?BOUNCE(K(X));
|
||||||
apply_p([P | Q], K) -> apply_p(P, fun(H) -> apply_p(Q, fun(T) -> K([H | T]) end) end);
|
apply_p([P | Q], K) -> apply_p(P, fun(H) -> apply_p(Q, fun(T) -> K([H | T]) end) end);
|
||||||
apply_p(T, K) when is_tuple(T) -> apply_p(tuple_to_list(T), fun(Xs) -> K(list_to_tuple(Xs)) end);
|
apply_p(T, K) when is_tuple(T) -> apply_p(tuple_to_list(T), fun(Xs) -> K(list_to_tuple(Xs)) end);
|
||||||
apply_p(M, K) when is_map(M) ->
|
apply_p(M, K) when is_map(M) ->
|
||||||
{Keys, Ps} = lists:unzip(maps:to_list(M)),
|
{Keys, Ps} = lists:unzip(maps:to_list(M)),
|
||||||
apply_p(Ps, fun(Vals) -> K(maps:from_list(lists:zip(Keys, Vals))) end);
|
apply_p(Ps, fun(Vals) -> K(maps:from_list(lists:zip(Keys, Vals))) end);
|
||||||
apply_p(X, K) -> K(X).
|
apply_p(X, K) -> ?BOUNCE(K(X)).
|
||||||
|
|
||||||
%% -- Primitive combinators --------------------------------------------------
|
%% -- Primitive combinators --------------------------------------------------
|
||||||
|
|
||||||
@@ -160,7 +166,7 @@ layout() -> ?layout.
|
|||||||
%% @doc Parse a sequence of tokens using a parser. Fails if the parse is ambiguous.
|
%% @doc Parse a sequence of tokens using a parser. Fails if the parse is ambiguous.
|
||||||
-spec parse(parser(A), tokens()) -> {ok, A} | {error, term()}.
|
-spec parse(parser(A), tokens()) -> {ok, A} | {error, term()}.
|
||||||
parse(P, S) ->
|
parse(P, S) ->
|
||||||
case parse1(apply_p(P, fun(X) -> {return_plus, X, {fail, no_error}} end), S) of
|
case parse1(trampoline(apply_p(P, fun(X) -> {return_plus, X, {fail, no_error}} end)), S) of
|
||||||
{[], {Pos, Err}} -> {error, {add_current_file(Pos), parse_error, flatten_error(Err)}};
|
{[], {Pos, Err}} -> {error, {add_current_file(Pos), parse_error, flatten_error(Err)}};
|
||||||
{[A], _} -> {ok, A};
|
{[A], _} -> {ok, A};
|
||||||
{As, _} -> {error, {{1, 1}, ambiguous_parse, As}}
|
{As, _} -> {error, {{1, 1}, ambiguous_parse, As}}
|
||||||
@@ -241,7 +247,7 @@ col(T) when is_tuple(T) -> element(2, pos(T)).
|
|||||||
|
|
||||||
%% If both parsers want the next token we grab it and merge the continuations.
|
%% If both parsers want the next token we grab it and merge the continuations.
|
||||||
choice1({tok_bind, Map1}, {tok_bind, Map2}) ->
|
choice1({tok_bind, Map1}, {tok_bind, Map2}) ->
|
||||||
{tok_bind, merge_with(fun(F, G) -> fun(T) -> choice1(F(T), G(T)) end end, Map1, Map2)};
|
{tok_bind, merge_with(fun(F, G) -> fun(T) -> choice1(trampoline(F(T)), trampoline(G(T))) end end, Map1, Map2)};
|
||||||
|
|
||||||
%% If both parsers fail we combine the error messages. If only one fails we discard it.
|
%% If both parsers fail we combine the error messages. If only one fails we discard it.
|
||||||
choice1({fail, E1}, {fail, E2}) -> {fail, add_error(E1, E2)};
|
choice1({fail, E1}, {fail, E2}) -> {fail, add_error(E1, E2)};
|
||||||
@@ -255,7 +261,7 @@ choice1(P, {return_plus, X, Q}) -> {return_plus, X, choice1(P, Q)};
|
|||||||
%% If both sides want a layout block we combine them. If only one side wants a layout block we
|
%% If both sides want a layout block we combine them. If only one side wants a layout block we
|
||||||
%% will commit to a layout block is there is one.
|
%% will commit to a layout block is there is one.
|
||||||
choice1({layout, F, P}, {layout, G, Q}) ->
|
choice1({layout, F, P}, {layout, G, Q}) ->
|
||||||
{layout, fun(N) -> choice1(F(N), G(N)) end, choice1(P, Q)};
|
{layout, fun(N) -> choice1(trampoline(F(N)), trampoline(G(N))) end, choice1(P, Q)};
|
||||||
choice1({layout, F, P}, Q) -> {layout, F, choice1(P, Q)};
|
choice1({layout, F, P}, Q) -> {layout, F, choice1(P, Q)};
|
||||||
choice1(P, {layout, G, Q}) -> {layout, G, choice1(P, Q)}.
|
choice1(P, {layout, G, Q}) -> {layout, G, choice1(P, Q)}.
|
||||||
|
|
||||||
@@ -278,6 +284,8 @@ parse1(P, S) ->
|
|||||||
%% The main work horse. Returns a list of possible parses and an error message in case parsing
|
%% The main work horse. Returns a list of possible parses and an error message in case parsing
|
||||||
%% fails.
|
%% fails.
|
||||||
-spec parse1(parser1(A), #ts{}, [A], term()) -> {[A], error()}.
|
-spec parse1(parser1(A), #ts{}, [A], term()) -> {[A], error()}.
|
||||||
|
parse1({bounce, F}, Ts, Acc, Err) ->
|
||||||
|
parse1(F(), Ts, Acc, Err);
|
||||||
parse1({tok_bind, Map}, Ts, Acc, Err) ->
|
parse1({tok_bind, Map}, Ts, Acc, Err) ->
|
||||||
case next_token(Ts) of
|
case next_token(Ts) of
|
||||||
{T, Ts1} ->
|
{T, Ts1} ->
|
||||||
|
|||||||
+14
-2
@@ -93,8 +93,20 @@ decl() ->
|
|||||||
?LAZY_P(
|
?LAZY_P(
|
||||||
choice(
|
choice(
|
||||||
%% Contract declaration
|
%% Contract declaration
|
||||||
[ ?RULE(keyword(contract), con(), tok('='), maybe_block(decl()), {contract, _1, _2, _4})
|
[ ?RULE(token(main), keyword(contract),
|
||||||
, ?RULE(token(payable), keyword(contract), con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract, _2, _3, _5}))
|
con(), tok('='), maybe_block(decl()), {contract_main, _2, _3, _5})
|
||||||
|
, ?RULE(keyword(contract),
|
||||||
|
con(), tok('='), maybe_block(decl()), {contract_child, _1, _2, _4})
|
||||||
|
, ?RULE(keyword(contract), token(interface),
|
||||||
|
con(), tok('='), maybe_block(decl()), {contract_interface, _1, _3, _5})
|
||||||
|
, ?RULE(token(payable), token(main), keyword(contract),
|
||||||
|
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_main, _3, _4, _6}))
|
||||||
|
, ?RULE(token(payable), keyword(contract),
|
||||||
|
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_child, _2, _3, _5}))
|
||||||
|
, ?RULE(token(payable), keyword(contract), token(interface),
|
||||||
|
con(), tok('='), maybe_block(decl()), add_modifiers([_1], {contract_interface, _2, _4, _6}))
|
||||||
|
|
||||||
|
|
||||||
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
|
, ?RULE(keyword(namespace), con(), tok('='), maybe_block(decl()), {namespace, _1, _2, _4})
|
||||||
, ?RULE(keyword(include), str(), {include, get_ann(_1), _2})
|
, ?RULE(keyword(include), str(), {include, get_ann(_1), _2})
|
||||||
, pragma()
|
, pragma()
|
||||||
|
|||||||
+21
-11
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
-export_type([options/0]).
|
-export_type([options/0]).
|
||||||
|
|
||||||
|
-include("aeso_utils.hrl").
|
||||||
|
|
||||||
-type doc() :: prettypr:document().
|
-type doc() :: prettypr:document().
|
||||||
-type options() :: [{indent, non_neg_integer()} | show_generated].
|
-type options() :: [{indent, non_neg_integer()} | show_generated].
|
||||||
|
|
||||||
@@ -131,6 +133,10 @@ typed(A, Type) ->
|
|||||||
false -> follow(hsep(A, text(":")), type(Type))
|
false -> follow(hsep(A, text(":")), type(Type))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
contract_head(contract_main) -> text("main contract");
|
||||||
|
contract_head(contract_child) -> text("contract");
|
||||||
|
contract_head(contract_interface) -> text("contract interface").
|
||||||
|
|
||||||
%% -- Exports ----------------------------------------------------------------
|
%% -- Exports ----------------------------------------------------------------
|
||||||
|
|
||||||
-spec decls([aeso_syntax:decl()], options()) -> doc().
|
-spec decls([aeso_syntax:decl()], options()) -> doc().
|
||||||
@@ -145,11 +151,11 @@ decl(D, Options) ->
|
|||||||
with_options(Options, fun() -> decl(D) end).
|
with_options(Options, fun() -> decl(D) end).
|
||||||
|
|
||||||
-spec decl(aeso_syntax:decl()) -> doc().
|
-spec decl(aeso_syntax:decl()) -> doc().
|
||||||
decl({contract, Attrs, C, Ds}) ->
|
decl({Con, Attrs, C, Ds}) when ?IS_CONTRACT_HEAD(Con) ->
|
||||||
Mod = fun({Mod, true}) when Mod == payable ->
|
Mod = fun({Mod, true}) when Mod == payable ->
|
||||||
text(atom_to_list(Mod));
|
text(atom_to_list(Mod));
|
||||||
(_) -> empty() end,
|
(_) -> empty() end,
|
||||||
block(follow( hsep(lists:map(Mod, Attrs) ++ [text("contract")])
|
block(follow( hsep(lists:map(Mod, Attrs) ++ [contract_head(Con)])
|
||||||
, hsep(name(C), text("="))), decls(Ds));
|
, hsep(name(C), text("="))), decls(Ds));
|
||||||
decl({namespace, _, C, Ds}) ->
|
decl({namespace, _, C, Ds}) ->
|
||||||
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
block(follow(text("namespace"), hsep(name(C), text("="))), decls(Ds));
|
||||||
@@ -264,6 +270,8 @@ type({args_t, _, Args}) ->
|
|||||||
type({bytes_t, _, any}) -> text("bytes(_)");
|
type({bytes_t, _, any}) -> text("bytes(_)");
|
||||||
type({bytes_t, _, Len}) ->
|
type({bytes_t, _, Len}) ->
|
||||||
text(lists:concat(["bytes(", Len, ")"]));
|
text(lists:concat(["bytes(", Len, ")"]));
|
||||||
|
type({if_t, _, Id, Then, Else}) ->
|
||||||
|
beside(text("if"), args_type([Id, Then, Else]));
|
||||||
type({named_arg_t, _, Name, Type, _Default}) ->
|
type({named_arg_t, _, Name, Type, _Default}) ->
|
||||||
%% Drop the default value
|
%% Drop the default value
|
||||||
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
||||||
@@ -290,12 +298,9 @@ tuple_type(Factors) ->
|
|||||||
, text(")")
|
, text(")")
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-spec arg_expr(aeso_syntax:arg_expr()) -> doc().
|
-spec expr_p(integer(), aeso_syntax:arg_expr()) -> doc().
|
||||||
arg_expr({named_arg, _, Name, E}) ->
|
expr_p(P, {named_arg, _, Name, E}) ->
|
||||||
follow(hsep(expr(Name), text("=")), expr(E));
|
paren(P > 100, follow(hsep(expr(Name), text("=")), expr(E)));
|
||||||
arg_expr(E) -> expr(E).
|
|
||||||
|
|
||||||
-spec expr_p(integer(), aeso_syntax:expr()) -> doc().
|
|
||||||
expr_p(P, {lam, _, Args, E}) ->
|
expr_p(P, {lam, _, Args, E}) ->
|
||||||
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
||||||
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
||||||
@@ -377,8 +382,13 @@ expr_p(_, {char, _, C}) ->
|
|||||||
case C of
|
case C of
|
||||||
$' -> text("'\\''");
|
$' -> text("'\\''");
|
||||||
$" -> text("'\"'");
|
$" -> text("'\"'");
|
||||||
_ -> S = lists:flatten(io_lib:format("~p", [[C]])),
|
_ when C < 16#80 ->
|
||||||
text("'" ++ tl(lists:droplast(S)) ++ "'")
|
S = lists:flatten(io_lib:format("~p", [[C]])),
|
||||||
|
text("'" ++ tl(lists:droplast(S)) ++ "'");
|
||||||
|
_ ->
|
||||||
|
S = lists:flatten(
|
||||||
|
io_lib:format("'~ts'", [list_to_binary(aeso_scan:utf8_encode([C]))])),
|
||||||
|
text(S)
|
||||||
end;
|
end;
|
||||||
%% -- Names
|
%% -- Names
|
||||||
expr_p(_, E = {id, _, _}) -> name(E);
|
expr_p(_, E = {id, _, _}) -> name(E);
|
||||||
@@ -450,7 +460,7 @@ prefix(P, Op, A) ->
|
|||||||
app(P, F, Args) ->
|
app(P, F, Args) ->
|
||||||
paren(P > 900,
|
paren(P > 900,
|
||||||
beside(expr_p(900, F),
|
beside(expr_p(900, F),
|
||||||
tuple(lists:map(fun arg_expr/1, Args)))).
|
tuple(lists:map(fun expr/1, Args)))).
|
||||||
|
|
||||||
field({field, _, LV, E}) ->
|
field({field, _, LV, E}) ->
|
||||||
follow(hsep(lvalue(LV), text("=")), expr(E));
|
follow(hsep(lvalue(LV), text("=")), expr(E));
|
||||||
|
|||||||
+35
-27
@@ -7,7 +7,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_scan).
|
-module(aeso_scan).
|
||||||
|
|
||||||
-export([scan/1]).
|
-export([scan/1, utf8_encode/1]).
|
||||||
|
|
||||||
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
||||||
override/2, push/2, pop/1]).
|
override/2, push/2, pop/1]).
|
||||||
@@ -28,7 +28,13 @@ lexer() ->
|
|||||||
QID = ["(", CON, "\\.)+", ID],
|
QID = ["(", CON, "\\.)+", ID],
|
||||||
QCON = ["(", CON, "\\.)+", CON],
|
QCON = ["(", CON, "\\.)+", CON],
|
||||||
OP = "[=!<>+\\-*/:&|?~@^]+",
|
OP = "[=!<>+\\-*/:&|?~@^]+",
|
||||||
CHAR = "'([^'\\\\]|(\\\\.))'",
|
%% Five cases for a character
|
||||||
|
%% * 1 7-bit ascii, not \ or '
|
||||||
|
%% * 2-4 8-bit values (UTF8)
|
||||||
|
%% * \ followed by a known modifier [aernrtv]
|
||||||
|
%% * \xhh
|
||||||
|
%% * \x{hhh...}
|
||||||
|
CHAR = "'(([\\x00-\\x26\\x28-\\x5b\\x5d-\\x7f])|([\\x00-\\xff][\\x80-\\xff]{1,3})|(\\\\[befnrtv'\\\\])|(\\\\x[0-9a-fA-F]{2,2})|(\\\\x\\{[0-9a-fA-F]*\\}))'",
|
||||||
STRING = "\"([^\"\\\\]|(\\\\.))*\"",
|
STRING = "\"([^\"\\\\]|(\\\\.))*\"",
|
||||||
|
|
||||||
CommentStart = {"/\\*", push(comment, skip())},
|
CommentStart = {"/\\*", push(comment, skip())},
|
||||||
@@ -38,7 +44,9 @@ lexer() ->
|
|||||||
, {"[^/*]+|[/*]", skip()} ],
|
, {"[^/*]+|[/*]", skip()} ],
|
||||||
|
|
||||||
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||||
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace"],
|
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
||||||
|
"interface", "main"
|
||||||
|
],
|
||||||
KW = string:join(Keywords, "|"),
|
KW = string:join(Keywords, "|"),
|
||||||
|
|
||||||
Rules =
|
Rules =
|
||||||
@@ -77,34 +85,34 @@ scan(String) ->
|
|||||||
%% -- Helpers ----------------------------------------------------------------
|
%% -- Helpers ----------------------------------------------------------------
|
||||||
|
|
||||||
parse_string([$" | Chars]) ->
|
parse_string([$" | Chars]) ->
|
||||||
unescape(Chars).
|
unicode:characters_to_nfc_binary(unescape(Chars)).
|
||||||
|
|
||||||
parse_char([$', $\\, Code, $']) ->
|
parse_char([$' | Chars]) ->
|
||||||
case Code of
|
case unicode:characters_to_nfc_list(unescape($', Chars, [])) of
|
||||||
$' -> $';
|
[Char] -> Char;
|
||||||
$\\ -> $\\;
|
_Bad -> {error, "Bad character literal: '" ++ Chars}
|
||||||
$b -> $\b;
|
end.
|
||||||
$e -> $\e;
|
|
||||||
$f -> $\f;
|
|
||||||
$n -> $\n;
|
|
||||||
$r -> $\r;
|
|
||||||
$t -> $\t;
|
|
||||||
$v -> $\v;
|
|
||||||
_ -> {error, "Bad control sequence: \\" ++ [Code]}
|
|
||||||
end;
|
|
||||||
parse_char([$', C, $']) -> C.
|
|
||||||
|
|
||||||
unescape(Str) -> unescape(Str, []).
|
utf8_encode(Cs) ->
|
||||||
|
binary_to_list(unicode:characters_to_binary(Cs)).
|
||||||
|
|
||||||
unescape([$"], Acc) ->
|
unescape(Str) -> unescape($", Str, []).
|
||||||
|
|
||||||
|
unescape(Delim, [Delim], Acc) ->
|
||||||
list_to_binary(lists:reverse(Acc));
|
list_to_binary(lists:reverse(Acc));
|
||||||
unescape([$\\, $x, D1, D2 | Chars ], Acc) ->
|
unescape(Delim, [$\\, $x, ${ | Chars ], Acc) ->
|
||||||
|
{Ds, [_ | Cs]} = lists:splitwith(fun($}) -> false ; (_) -> true end, Chars),
|
||||||
|
C = list_to_integer(Ds, 16),
|
||||||
|
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
|
||||||
|
unescape(Delim, Cs, [Utf8Cs | Acc]);
|
||||||
|
unescape(Delim, [$\\, $x, D1, D2 | Chars ], Acc) ->
|
||||||
C = list_to_integer([D1, D2], 16),
|
C = list_to_integer([D1, D2], 16),
|
||||||
unescape(Chars, [C | Acc]);
|
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
|
||||||
unescape([$\\, Code | Chars], Acc) ->
|
unescape(Delim, Chars, [Utf8Cs | Acc]);
|
||||||
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
|
unescape(Delim, [$\\, Code | Chars], Acc) ->
|
||||||
|
Ok = fun(C) -> unescape(Delim, Chars, [C | Acc]) end,
|
||||||
case Code of
|
case Code of
|
||||||
$" -> Ok($");
|
Delim -> Ok(Delim);
|
||||||
$\\ -> Ok($\\);
|
$\\ -> Ok($\\);
|
||||||
$b -> Ok($\b);
|
$b -> Ok($\b);
|
||||||
$e -> Ok($\e);
|
$e -> Ok($\e);
|
||||||
@@ -115,8 +123,8 @@ unescape([$\\, Code | Chars], Acc) ->
|
|||||||
$v -> Ok($\v);
|
$v -> Ok($\v);
|
||||||
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
||||||
end;
|
end;
|
||||||
unescape([C | Chars], Acc) ->
|
unescape(Delim, [C | Chars], Acc) ->
|
||||||
unescape(Chars, [C | Acc]).
|
unescape(Delim, Chars, [C | Acc]).
|
||||||
|
|
||||||
strip_underscores(S) ->
|
strip_underscores(S) ->
|
||||||
lists:filter(fun(C) -> C /= $_ end, S).
|
lists:filter(fun(C) -> C /= $_ end, S).
|
||||||
|
|||||||
+5
-2
@@ -25,7 +25,8 @@
|
|||||||
-type ann_origin() :: system | user.
|
-type ann_origin() :: system | user.
|
||||||
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
-type ann_format() :: '?:' | hex | infix | prefix | elif.
|
||||||
|
|
||||||
-type ann() :: [{line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()} | stateful | private].
|
-type ann() :: [ {line, ann_line()} | {col, ann_col()} | {format, ann_format()} | {origin, ann_origin()}
|
||||||
|
| stateful | private | payable | main | interface].
|
||||||
|
|
||||||
-type name() :: string().
|
-type name() :: string().
|
||||||
-type id() :: {id, ann(), name()}.
|
-type id() :: {id, ann(), name()}.
|
||||||
@@ -34,7 +35,9 @@
|
|||||||
-type qcon() :: {qcon, ann(), [name()]}.
|
-type qcon() :: {qcon, ann(), [name()]}.
|
||||||
-type tvar() :: {tvar, ann(), name()}.
|
-type tvar() :: {tvar, ann(), name()}.
|
||||||
|
|
||||||
-type decl() :: {contract, ann(), con(), [decl()]}
|
-type decl() :: {contract_main, ann(), con(), [decl()]}
|
||||||
|
| {contract_child, ann(), con(), [decl()]}
|
||||||
|
| {contract_interface, ann(), con(), [decl()]}
|
||||||
| {namespace, ann(), con(), [decl()]}
|
| {namespace, ann(), con(), [decl()]}
|
||||||
| {pragma, ann(), pragma()}
|
| {pragma, ann(), pragma()}
|
||||||
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
|
| {type_decl, ann(), id(), [tvar()]} % Only for error msgs
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-define(IS_CONTRACT_HEAD(X),
|
||||||
|
(X =:= contract_main orelse
|
||||||
|
X =:= contract_interface orelse
|
||||||
|
X =:= contract_child
|
||||||
|
)
|
||||||
|
).
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{application, aesophia,
|
{application, aesophia,
|
||||||
[{description, "Contract Language for aeternity"},
|
[{description, "Compiler for Aeternity Sophia language"},
|
||||||
{vsn, "4.3.0"},
|
{vsn, "6.0.1"},
|
||||||
{registered, []},
|
{registered, []},
|
||||||
{applications,
|
{applications,
|
||||||
[kernel,
|
[kernel,
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ parameterized_contract(ExtraCode, FunName, Types) ->
|
|||||||
lists:flatten(
|
lists:flatten(
|
||||||
["contract Remote =\n"
|
["contract Remote =\n"
|
||||||
" entrypoint bla : () => unit\n\n"
|
" entrypoint bla : () => unit\n\n"
|
||||||
"contract Dummy =\n",
|
"main contract Dummy =\n",
|
||||||
ExtraCode, "\n",
|
ExtraCode, "\n",
|
||||||
" type an_alias('a) = string * 'a\n"
|
" type an_alias('a) = string * 'a\n"
|
||||||
" record r = {x : an_alias(int), y : variant}\n"
|
" record r = {x : an_alias(int), y : variant}\n"
|
||||||
|
|||||||
+19
-10
@@ -23,6 +23,7 @@ test_cases(1) ->
|
|||||||
#{name => <<"C">>,
|
#{name => <<"C">>,
|
||||||
type_defs => [],
|
type_defs => [],
|
||||||
payable => true,
|
payable => true,
|
||||||
|
kind => contract_main,
|
||||||
functions =>
|
functions =>
|
||||||
[#{name => <<"a">>,
|
[#{name => <<"a">>,
|
||||||
arguments =>
|
arguments =>
|
||||||
@@ -31,16 +32,17 @@ test_cases(1) ->
|
|||||||
returns => <<"int">>,
|
returns => <<"int">>,
|
||||||
stateful => true,
|
stateful => true,
|
||||||
payable => true}]}},
|
payable => true}]}},
|
||||||
DecACI = <<"payable contract C =\n"
|
DecACI = <<"payable main contract C =\n"
|
||||||
" payable entrypoint a : (int) => int\n">>,
|
" payable entrypoint a : (int) => int\n">>,
|
||||||
{Contract,MapACI,DecACI};
|
{Contract,MapACI,DecACI};
|
||||||
|
|
||||||
test_cases(2) ->
|
test_cases(2) ->
|
||||||
Contract = <<"contract C =\n"
|
Contract = <<"main contract C =\n"
|
||||||
" type allan = int\n"
|
" type allan = int\n"
|
||||||
" entrypoint a(i : allan) = i+1\n">>,
|
" entrypoint a(i : allan) = i+1\n">>,
|
||||||
MapACI = #{contract =>
|
MapACI = #{contract =>
|
||||||
#{name => <<"C">>, payable => false,
|
#{name => <<"C">>, payable => false,
|
||||||
|
kind => contract_main,
|
||||||
type_defs =>
|
type_defs =>
|
||||||
[#{name => <<"allan">>,
|
[#{name => <<"allan">>,
|
||||||
typedef => <<"int">>,
|
typedef => <<"int">>,
|
||||||
@@ -53,12 +55,12 @@ test_cases(2) ->
|
|||||||
returns => <<"int">>,
|
returns => <<"int">>,
|
||||||
stateful => false,
|
stateful => false,
|
||||||
payable => false}]}},
|
payable => false}]}},
|
||||||
DecACI = <<"contract C =\n"
|
DecACI = <<"main contract C =\n"
|
||||||
" type allan = int\n"
|
" type allan = int\n"
|
||||||
" entrypoint a : (C.allan) => int\n">>,
|
" entrypoint a : (C.allan) => int\n">>,
|
||||||
{Contract,MapACI,DecACI};
|
{Contract,MapACI,DecACI};
|
||||||
test_cases(3) ->
|
test_cases(3) ->
|
||||||
Contract = <<"contract C =\n"
|
Contract = <<"main contract C =\n"
|
||||||
" type state = unit\n"
|
" type state = unit\n"
|
||||||
" datatype event = SingleEventDefined\n"
|
" datatype event = SingleEventDefined\n"
|
||||||
" datatype bert('a) = Bin('a)\n"
|
" datatype bert('a) = Bin('a)\n"
|
||||||
@@ -71,7 +73,7 @@ test_cases(3) ->
|
|||||||
#{<<"C.bert">> => [<<"string">>]}}],
|
#{<<"C.bert">> => [<<"string">>]}}],
|
||||||
name => <<"a">>,returns => <<"int">>,
|
name => <<"a">>,returns => <<"int">>,
|
||||||
stateful => false, payable => false}],
|
stateful => false, payable => false}],
|
||||||
name => <<"C">>, payable => false,
|
name => <<"C">>, payable => false, kind => contract_main,
|
||||||
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
event => #{variant => [#{<<"SingleEventDefined">> => []}]},
|
||||||
state => <<"unit">>,
|
state => <<"unit">>,
|
||||||
type_defs =>
|
type_defs =>
|
||||||
@@ -80,7 +82,7 @@ test_cases(3) ->
|
|||||||
#{variant =>
|
#{variant =>
|
||||||
[#{<<"Bin">> => [<<"'a">>]}]},
|
[#{<<"Bin">> => [<<"'a">>]}]},
|
||||||
vars => [#{name => <<"'a">>}]}]}},
|
vars => [#{name => <<"'a">>}]}]}},
|
||||||
DecACI = <<"contract C =\n"
|
DecACI = <<"main contract C =\n"
|
||||||
" type state = unit\n"
|
" type state = unit\n"
|
||||||
" datatype event = SingleEventDefined\n"
|
" datatype event = SingleEventDefined\n"
|
||||||
" datatype bert('a) = Bin('a)\n"
|
" datatype bert('a) = Bin('a)\n"
|
||||||
@@ -101,17 +103,24 @@ aci_test_contract(Name) ->
|
|||||||
true -> [debug_mode];
|
true -> [debug_mode];
|
||||||
false -> []
|
false -> []
|
||||||
end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}],
|
||||||
{ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
|
JSON = case aeso_aci:contract_interface(json, String, Opts) of
|
||||||
{ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]),
|
{ok, J} -> J;
|
||||||
|
{error, ErrorStringJ} when is_binary(ErrorStringJ) -> error(ErrorStringJ);
|
||||||
|
{error, ErrorJ} -> aeso_compiler_tests:print_and_throw(ErrorJ)
|
||||||
|
end,
|
||||||
|
case aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]) of
|
||||||
|
{ok, #{aci := JSON1}} ->
|
||||||
?assertEqual(JSON, JSON1),
|
?assertEqual(JSON, JSON1),
|
||||||
|
|
||||||
io:format("JSON:\n~p\n", [JSON]),
|
io:format("JSON:\n~p\n", [JSON]),
|
||||||
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
|
{ok, ContractStub} = aeso_aci:render_aci_json(JSON),
|
||||||
|
|
||||||
io:format("STUB:\n~s\n", [ContractStub]),
|
io:format("STUB:\n~s\n", [ContractStub]),
|
||||||
check_stub(ContractStub, [{src_file, Name}]),
|
check_stub(ContractStub, [{src_file, Name}]),
|
||||||
|
|
||||||
ok.
|
ok;
|
||||||
|
{error, ErrorString} when is_binary(ErrorString) -> error(ErrorString);
|
||||||
|
{error, Error} -> aeso_compiler_tests:print_and_throw(Error)
|
||||||
|
end.
|
||||||
|
|
||||||
check_stub(Stub, Options) ->
|
check_stub(Stub, Options) ->
|
||||||
try aeso_parser:string(binary_to_list(Stub), Options) of
|
try aeso_parser:string(binary_to_list(Stub), Options) of
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ calldata_aci_test_() ->
|
|||||||
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
end} || {ContractName, Fun, Args} <- compilable_contracts()].
|
||||||
|
|
||||||
parse_args(Fun, Args) ->
|
parse_args(Fun, Args) ->
|
||||||
[{contract, _, _, [{letfun, _, _, _, _, {app, _, _, AST}}]}] =
|
[{contract_main, _, _, [{letfun, _, _, _, _, {app, _, _, AST}}]}] =
|
||||||
aeso_parser:string("contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
|
aeso_parser:string("main contract Temp = function foo() = " ++ Fun ++ "(" ++ string:join(Args, ", ") ++ ")"),
|
||||||
strip_ann(AST).
|
strip_ann(AST).
|
||||||
|
|
||||||
strip_ann(T) when is_tuple(T) ->
|
strip_ann(T) when is_tuple(T) ->
|
||||||
@@ -143,4 +143,4 @@ compilable_contracts() ->
|
|||||||
not_yet_compilable(fate) ->
|
not_yet_compilable(fate) ->
|
||||||
[];
|
[];
|
||||||
not_yet_compilable(aevm) ->
|
not_yet_compilable(aevm) ->
|
||||||
[].
|
["funargs", "strings"].
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ simple_compile_test_() ->
|
|||||||
#{fate_code := Code} when Backend == fate ->
|
#{fate_code := Code} when Backend == fate ->
|
||||||
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
|
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
|
||||||
?assertMatch({X, X}, {Code1, Code});
|
?assertMatch({X, X}, {Code1, Code});
|
||||||
ErrBin ->
|
Error -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error)
|
||||||
io:format("\n~s", [ErrBin]),
|
|
||||||
error(ErrBin)
|
|
||||||
end
|
end
|
||||||
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
|
end} || ContractName <- compilable_contracts(), Backend <- [aevm, fate],
|
||||||
not lists:member(ContractName, not_compilable_on(Backend))] ++
|
not lists:member(ContractName, not_compilable_on(Backend))] ++
|
||||||
@@ -92,6 +90,23 @@ simple_compile_test_() ->
|
|||||||
end} || Backend <- [aevm, fate] ] ++
|
end} || Backend <- [aevm, fate] ] ++
|
||||||
[].
|
[].
|
||||||
|
|
||||||
|
%% Check if all modules in the standard library compile
|
||||||
|
stdlib_test_() ->
|
||||||
|
{ok, Files} = file:list_dir(aeso_stdlib:stdlib_include_path()),
|
||||||
|
[ { "Testing " ++ File ++ " from the stdlib",
|
||||||
|
fun() ->
|
||||||
|
String = "include \"" ++ File ++ "\"\nmain contract Test =\n entrypoint f(x) = x",
|
||||||
|
Options = [{src_file, File}, {backend, fate}],
|
||||||
|
case aeso_compiler:from_string(String, Options) of
|
||||||
|
{ok, #{fate_code := Code}} ->
|
||||||
|
Code1 = aeb_fate_code:deserialize(aeb_fate_code:serialize(Code)),
|
||||||
|
?assertMatch({X, X}, {Code1, Code});
|
||||||
|
{error, Error} -> io:format("\n\n~p\n\n", [Error]), print_and_throw(Error)
|
||||||
|
end
|
||||||
|
end} || File <- Files,
|
||||||
|
lists:suffix(".aes", File)
|
||||||
|
].
|
||||||
|
|
||||||
check_errors(no_error, Actual) -> ?assertMatch(#{}, Actual);
|
check_errors(no_error, Actual) -> ?assertMatch(#{}, Actual);
|
||||||
check_errors(Expect, #{}) ->
|
check_errors(Expect, #{}) ->
|
||||||
?assertEqual({error, Expect}, ok);
|
?assertEqual({error, Expect}, ok);
|
||||||
@@ -154,6 +169,7 @@ compilable_contracts() ->
|
|||||||
"events",
|
"events",
|
||||||
"include",
|
"include",
|
||||||
"basic_auth",
|
"basic_auth",
|
||||||
|
"basic_auth_tx",
|
||||||
"bitcoin_auth",
|
"bitcoin_auth",
|
||||||
"address_literals",
|
"address_literals",
|
||||||
"bytes_equality",
|
"bytes_equality",
|
||||||
@@ -162,6 +178,7 @@ compilable_contracts() ->
|
|||||||
"bytes_to_x",
|
"bytes_to_x",
|
||||||
"bytes_concat",
|
"bytes_concat",
|
||||||
"aens",
|
"aens",
|
||||||
|
"aens_update",
|
||||||
"tuple_match",
|
"tuple_match",
|
||||||
"cyclic_include",
|
"cyclic_include",
|
||||||
"stdlib_include",
|
"stdlib_include",
|
||||||
@@ -171,18 +188,22 @@ compilable_contracts() ->
|
|||||||
"payable",
|
"payable",
|
||||||
"unapplied_builtins",
|
"unapplied_builtins",
|
||||||
"underscore_number_literals",
|
"underscore_number_literals",
|
||||||
|
"pairing_crypto",
|
||||||
"qualified_constructor",
|
"qualified_constructor",
|
||||||
"let_patterns",
|
"let_patterns",
|
||||||
"lhs_matching",
|
"lhs_matching",
|
||||||
"hermetization_turnoff"
|
"more_strings",
|
||||||
|
"protected_call",
|
||||||
|
"hermetization_turnoff",
|
||||||
|
"multiple_contracts",
|
||||||
|
"clone",
|
||||||
|
"clone_simple",
|
||||||
|
"create",
|
||||||
|
"test" % Custom general-purpose test file. Keep it last on the list.
|
||||||
].
|
].
|
||||||
|
|
||||||
not_compilable_on(fate) -> [];
|
not_compilable_on(fate) -> [];
|
||||||
not_compilable_on(aevm) ->
|
not_compilable_on(aevm) -> compilable_contracts().
|
||||||
["stdlib_include",
|
|
||||||
"manual_stdlib_include",
|
|
||||||
"hermetization_turnoff"
|
|
||||||
].
|
|
||||||
|
|
||||||
debug_mode_contracts() ->
|
debug_mode_contracts() ->
|
||||||
["hermetization_turnoff"].
|
["hermetization_turnoff"].
|
||||||
@@ -387,10 +408,10 @@ failing_contracts() ->
|
|||||||
[<<?Pos(12, 42)
|
[<<?Pos(12, 42)
|
||||||
"Cannot unify int\n"
|
"Cannot unify int\n"
|
||||||
" and string\n"
|
" and string\n"
|
||||||
"when checking the record projection at line 12, column 42\n"
|
"when checking the type of the expression at line 12, column 42\n"
|
||||||
" r.foo : (gas : int, value : int) => Remote.themap\n"
|
" r.foo() : map(int, string)\n"
|
||||||
"against the expected type\n"
|
"against the expected type\n"
|
||||||
" (gas : int, value : int) => map(string, int)">>])
|
" map(string, int)">>])
|
||||||
, ?TYPE_ERROR(not_toplevel_include,
|
, ?TYPE_ERROR(not_toplevel_include,
|
||||||
[<<?Pos(2, 11)
|
[<<?Pos(2, 11)
|
||||||
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>])
|
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>])
|
||||||
@@ -555,11 +576,15 @@ failing_contracts() ->
|
|||||||
"Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">>
|
"Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(map_as_map_key,
|
, ?TYPE_ERROR(map_as_map_key,
|
||||||
[<<?Pos(5, 25)
|
[<<?Pos(5, 47)
|
||||||
"Invalid key type\n"
|
"Invalid key type\n"
|
||||||
" map(int, int)\n"
|
" map(int, int)\n"
|
||||||
"Map keys cannot contain other maps.">>,
|
"Map keys cannot contain other maps.">>,
|
||||||
<<?Pos(6, 25)
|
<<?Pos(6, 31)
|
||||||
|
"Invalid key type\n"
|
||||||
|
" list(map(int, int))\n"
|
||||||
|
"Map keys cannot contain other maps.">>,
|
||||||
|
<<?Pos(6, 31)
|
||||||
"Invalid key type\n"
|
"Invalid key type\n"
|
||||||
" lm\n"
|
" lm\n"
|
||||||
"Map keys cannot contain other maps.">>])
|
"Map keys cannot contain other maps.">>])
|
||||||
@@ -624,9 +649,9 @@ failing_contracts() ->
|
|||||||
<<?Pos(2, 1)
|
<<?Pos(2, 1)
|
||||||
"Cannot compile with this version of the compiler,\n"
|
"Cannot compile with this version of the compiler,\n"
|
||||||
"because it does not satisfy the constraint ", Version/binary, " == 9.9.9">>])
|
"because it does not satisfy the constraint ", Version/binary, " == 9.9.9">>])
|
||||||
, ?TYPE_ERROR(multiple_contracts,
|
, ?TYPE_ERROR(interface_with_defs,
|
||||||
[<<?Pos(2, 3)
|
[<<?Pos(2, 3)
|
||||||
"Only the main contract can contain defined functions or entrypoints.\n"
|
"Contract interfaces cannot contain defined functions or entrypoints.\n"
|
||||||
"Fix: replace the definition of 'foo' by a type signature.">>])
|
"Fix: replace the definition of 'foo' by a type signature.">>])
|
||||||
, ?TYPE_ERROR(contract_as_namespace,
|
, ?TYPE_ERROR(contract_as_namespace,
|
||||||
[<<?Pos(5, 28)
|
[<<?Pos(5, 28)
|
||||||
@@ -681,6 +706,12 @@ failing_contracts() ->
|
|||||||
"Empty record/map update\n"
|
"Empty record/map update\n"
|
||||||
" r {}">>
|
" r {}">>
|
||||||
])
|
])
|
||||||
|
, ?TYPE_ERROR(bad_protected_call,
|
||||||
|
[<<?Pos(6, 22)
|
||||||
|
"Invalid 'protected' argument\n"
|
||||||
|
" (0 : int) == (1 : int) : bool\n"
|
||||||
|
"It must be either 'true' or 'false'.">>
|
||||||
|
])
|
||||||
, ?TYPE_ERROR(bad_function_block,
|
, ?TYPE_ERROR(bad_function_block,
|
||||||
[<<?Pos(4, 5)
|
[<<?Pos(4, 5)
|
||||||
"Mismatch in the function block. Expected implementation/type declaration of g function">>,
|
"Mismatch in the function block. Expected implementation/type declaration of g function">>,
|
||||||
@@ -716,6 +747,39 @@ failing_contracts() ->
|
|||||||
, ?TYPE_ERROR(bad_state,
|
, ?TYPE_ERROR(bad_state,
|
||||||
[<<?Pos(4, 16)
|
[<<?Pos(4, 16)
|
||||||
"Conflicting updates for field 'foo'">>])
|
"Conflicting updates for field 'foo'">>])
|
||||||
|
, ?TYPE_ERROR(factories_type_errors,
|
||||||
|
[<<?Pos(10,18)
|
||||||
|
"Chain.clone requires `ref` named argument of contract type.">>,
|
||||||
|
<<?Pos(11,18)
|
||||||
|
"Cannot unify (gas : int, value : int, protected : bool) => if(protected, option(void), void)\n and (gas : int, value : int, protected : bool, int, bool) => 'b\n"
|
||||||
|
"when checking contract construction of type\n (gas : int, value : int, protected : bool) =>\n if(protected, option(void), void) (at line 11, column 18)\nagainst the expected type\n (gas : int, value : int, protected : bool, int, bool) => 'b">>,
|
||||||
|
<<?Pos(12,37)
|
||||||
|
"Cannot unify int\n and bool\n"
|
||||||
|
"when checking named argument\n gas : int\nagainst inferred type\n bool">>,
|
||||||
|
<<?Pos(13,18),
|
||||||
|
"Kaboom is not implemented.\n"
|
||||||
|
"when resolving arguments of variadic function\n Chain.create">>,
|
||||||
|
<<?Pos(18,18)
|
||||||
|
"Cannot unify (gas : int, value : int, protected : bool, int, bool) => if(protected, option(void), void)\n and (gas : int, value : int, protected : bool) => 'a\n"
|
||||||
|
"when checking contract construction of type\n (gas : int, value : int, protected : bool, int, bool) =>\n if(protected, option(void), void) (at line 18, column 18)\nagainst the expected type\n (gas : int, value : int, protected : bool) => 'a">>,
|
||||||
|
<<?Pos(19,42),
|
||||||
|
"Named argument protected (at line 19, column 42) is not one of the expected named arguments\n - value : int">>,
|
||||||
|
<<?Pos(20,42),
|
||||||
|
"Cannot unify int\n and bool\n"
|
||||||
|
"when checking named argument\n value : int\nagainst inferred type\n bool">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(ambiguous_main,
|
||||||
|
[<<?Pos(1,1)
|
||||||
|
"Could not deduce the main contract. You can point it out manually with the `main` keyword.">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(no_main_contract,
|
||||||
|
[<<?Pos(0,0)
|
||||||
|
"No contract defined.">>
|
||||||
|
])
|
||||||
|
, ?TYPE_ERROR(multiple_main_contracts,
|
||||||
|
[<<?Pos(1,6)
|
||||||
|
"Only one main contract can be defined.">>
|
||||||
|
])
|
||||||
].
|
].
|
||||||
|
|
||||||
-define(Path(File), "code_errors/" ??File).
|
-define(Path(File), "code_errors/" ??File).
|
||||||
@@ -729,9 +793,7 @@ failing_contracts() ->
|
|||||||
{fate, ?Msg(File, Line, Col, ErrFATE)}]}).
|
{fate, ?Msg(File, Line, Col, ErrFATE)}]}).
|
||||||
|
|
||||||
failing_code_gen_contracts() ->
|
failing_code_gen_contracts() ->
|
||||||
[ ?SAME(last_declaration_must_be_contract, 1, 1,
|
[ ?SAME(missing_definition, 2, 14,
|
||||||
"Expected a contract as the last declaration instead of the namespace 'LastDeclarationIsNotAContract'")
|
|
||||||
, ?SAME(missing_definition, 2, 14,
|
|
||||||
"Missing definition of function 'foo'.")
|
"Missing definition of function 'foo'.")
|
||||||
, ?AEVM(polymorphic_entrypoint, 2, 17,
|
, ?AEVM(polymorphic_entrypoint, 2, 17,
|
||||||
"The argument\n"
|
"The argument\n"
|
||||||
@@ -829,6 +891,8 @@ failing_code_gen_contracts() ->
|
|||||||
"Invalid state type\n"
|
"Invalid state type\n"
|
||||||
" {f : (int) => int}\n"
|
" {f : (int) => int}\n"
|
||||||
"The state cannot contain functions in the AEVM. Use FATE if you need this.")
|
"The state cannot contain functions in the AEVM. Use FATE if you need this.")
|
||||||
|
, ?FATE(child_with_decls, 2, 14,
|
||||||
|
"Missing definition of function 'f'.")
|
||||||
].
|
].
|
||||||
|
|
||||||
validation_test_() ->
|
validation_test_() ->
|
||||||
@@ -866,7 +930,8 @@ validation_fails() ->
|
|||||||
"Byte code contract is not payable, but source code contract is.">>]}].
|
"Byte code contract is not payable, but source code contract is.">>]}].
|
||||||
|
|
||||||
validate(Contract1, Contract2) ->
|
validate(Contract1, Contract2) ->
|
||||||
ByteCode = #{ fate_code := FCode } = compile(fate, Contract1),
|
case compile(fate, Contract1) of
|
||||||
|
ByteCode = #{ fate_code := FCode } ->
|
||||||
FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)),
|
FCode1 = aeb_fate_code:serialize(aeb_fate_code:strip_init_function(FCode)),
|
||||||
Source = aeso_test_utils:read_contract(Contract2),
|
Source = aeso_test_utils:read_contract(Contract2),
|
||||||
aeso_compiler:validate_byte_code(
|
aeso_compiler:validate_byte_code(
|
||||||
@@ -875,5 +940,16 @@ validate(Contract1, Contract2) ->
|
|||||||
true -> [debug_mode];
|
true -> [debug_mode];
|
||||||
false -> []
|
false -> []
|
||||||
end ++
|
end ++
|
||||||
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]).
|
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]);
|
||||||
|
Error -> print_and_throw(Error)
|
||||||
|
end.
|
||||||
|
|
||||||
|
print_and_throw(Err) ->
|
||||||
|
case Err of
|
||||||
|
ErrBin when is_binary(ErrBin) ->
|
||||||
|
io:format("\n~s", [ErrBin]),
|
||||||
|
error(ErrBin);
|
||||||
|
Errors ->
|
||||||
|
io:format("Compilation error:\n~s", [string:join([aeso_errors:pp(E) || E <- Errors], "\n\n")]),
|
||||||
|
error(compilation_error)
|
||||||
|
end.
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ simple_contracts_test_() ->
|
|||||||
fun(_) -> ok end,
|
fun(_) -> ok end,
|
||||||
[{"Parse a contract with an identity function.",
|
[{"Parse a contract with an identity function.",
|
||||||
fun() ->
|
fun() ->
|
||||||
Text = "contract Identity =\n"
|
Text = "main contract Identity =\n"
|
||||||
" function id(x) = x\n",
|
" function id(x) = x\n",
|
||||||
?assertMatch(
|
?assertMatch(
|
||||||
[{contract, _, {con, _, "Identity"},
|
[{contract_main, _, {con, _, "Identity"},
|
||||||
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
|
[{letfun, _, {id, _, "id"}, [{id, _, "x"}], {id, _, "_"},
|
||||||
{id, _, "x"}}]}], parse_string(Text)),
|
{id, _, "x"}}]}], parse_string(Text)),
|
||||||
ok
|
ok
|
||||||
@@ -63,7 +63,8 @@ simple_contracts_test_() ->
|
|||||||
%% Parse tests of example contracts
|
%% Parse tests of example contracts
|
||||||
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
||||||
fun() -> roundtrip_contract(Contract) end}
|
fun() -> roundtrip_contract(Contract) end}
|
||||||
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, multi_sig, simple_storage, fundme, dutch_auction] ]
|
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof,
|
||||||
|
multi_sig, simple_storage, fundme, dutch_auction, utf8] ]
|
||||||
}.
|
}.
|
||||||
|
|
||||||
parse_contract(Name) ->
|
parse_contract(Name) ->
|
||||||
@@ -85,7 +86,7 @@ parse_expr(Text) ->
|
|||||||
round_trip(Text) ->
|
round_trip(Text) ->
|
||||||
Contract = parse_string(Text),
|
Contract = parse_string(Text),
|
||||||
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
|
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
|
||||||
Contract1 = parse_string(Text1),
|
Contract1 = parse_string(aeso_scan:utf8_encode(Text1)),
|
||||||
NoSrcLoc = remove_line_numbers(Contract),
|
NoSrcLoc = remove_line_numbers(Contract),
|
||||||
NoSrcLoc1 = remove_line_numbers(Contract1),
|
NoSrcLoc1 = remove_line_numbers(Contract1),
|
||||||
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
contract Identity =
|
contract Identity =
|
||||||
function main (x:int) = x
|
function main_fun (x:int) = x
|
||||||
|
|
||||||
function __call() = 12
|
function __call() = 12
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
contract Remote =
|
contract interface Remote =
|
||||||
entrypoint main : (int) => unit
|
entrypoint main_fun : (int) => unit
|
||||||
|
|
||||||
contract AddrChain =
|
contract AddrChain =
|
||||||
type o_type = oracle(string, map(string, int))
|
type o_type = oracle(string, map(string, int))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
contract Remote =
|
contract interface Remote =
|
||||||
entrypoint foo : () => unit
|
entrypoint foo : () => unit
|
||||||
|
|
||||||
contract AddressLiterals =
|
contract AddressLiterals =
|
||||||
|
|||||||
+16
-1
@@ -33,7 +33,22 @@ contract AENSTest =
|
|||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
||||||
|
|
||||||
// TODO: update() -- how to handle pointers?
|
|
||||||
|
stateful entrypoint update(owner : address,
|
||||||
|
name : string,
|
||||||
|
ttl : option(Chain.ttl),
|
||||||
|
client_ttl : option(int),
|
||||||
|
pointers : option(map(string, AENS.pointee))) : unit =
|
||||||
|
AENS.update(owner, name, ttl, client_ttl, pointers)
|
||||||
|
|
||||||
|
stateful entrypoint signedUpdate(owner : address,
|
||||||
|
name : string,
|
||||||
|
ttl : option(Chain.ttl),
|
||||||
|
client_ttl : option(int),
|
||||||
|
pointers : option(map(string, AENS.pointee)),
|
||||||
|
sign : signature) : unit =
|
||||||
|
AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
||||||
|
|
||||||
|
|
||||||
stateful entrypoint transfer(owner : address,
|
stateful entrypoint transfer(owner : address,
|
||||||
new_owner : address,
|
new_owner : address,
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
contract AENSUpdate =
|
||||||
|
stateful entrypoint update_name(owner : address, name : string) =
|
||||||
|
let p1 : AENS.pointee = AENS.AccountPt(Call.caller)
|
||||||
|
let p2 : AENS.pointee = AENS.OraclePt(Call.caller)
|
||||||
|
let p3 : AENS.pointee = AENS.ContractPt(Call.caller)
|
||||||
|
let p4 : AENS.pointee = AENS.ChannelPt(Call.caller)
|
||||||
|
AENS.update(owner, name, None, None,
|
||||||
|
Some({ ["account_pubkey"] = p1, ["oracle_pubkey"] = p2,
|
||||||
|
["contract_pubkey"] = p3, ["misc"] = p4 }))
|
||||||
|
|
||||||
|
entrypoint get_ttl(name : string) =
|
||||||
|
switch(AENS.lookup(name))
|
||||||
|
Some(AENS.Name(_, FixedTTL(ttl), _)) => ttl
|
||||||
|
|
||||||
|
entrypoint expiry(o : oracle(int, int)) : int =
|
||||||
|
Oracle.expiry(o)
|
||||||
|
|
||||||
@@ -36,6 +36,8 @@ contract AllSyntax =
|
|||||||
entrypoint init() = {
|
entrypoint init() = {
|
||||||
johann = 1000,
|
johann = 1000,
|
||||||
wolfgang = -10,
|
wolfgang = -10,
|
||||||
|
|
||||||
|
/* TODO: This does not compile because of bug in the parser tester.
|
||||||
von = (2 + 2, 0, List.sum([x | k <- [1,2,3]
|
von = (2 + 2, 0, List.sum([x | k <- [1,2,3]
|
||||||
, let l = k + 1
|
, let l = k + 1
|
||||||
, if(l < 10)
|
, if(l < 10)
|
||||||
@@ -43,11 +45,13 @@ contract AllSyntax =
|
|||||||
, Adam <- [Adam, Mickiewicz]
|
, Adam <- [Adam, Mickiewicz]
|
||||||
, let x = f(l)
|
, let x = f(l)
|
||||||
])),
|
])),
|
||||||
|
*/
|
||||||
|
von = (2 + 2, 0, List.sum([1,2,3,4])),
|
||||||
goethe = () }
|
goethe = () }
|
||||||
|
|
||||||
function f() =
|
function f() =
|
||||||
let kp = "nietzsche"
|
let kp = "nietzsche"
|
||||||
let p = "Пушкин"
|
// let p = "Пушкин" // TODO: this also doesn't do right round_trip...
|
||||||
let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210)
|
let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210)
|
||||||
|
|
||||||
let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract C =
|
||||||
|
entrypoint f() = 123
|
||||||
|
|
||||||
|
contract D =
|
||||||
|
entrypoint f() = 123
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
contract Remote =
|
contract interface Remote =
|
||||||
entrypoint foo : () => unit
|
entrypoint foo : () => unit
|
||||||
|
|
||||||
contract AddressLiterals =
|
contract AddressLiterals =
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
contract interface Remote =
|
||||||
|
entrypoint id : int => int
|
||||||
|
|
||||||
|
contract ProtectedCall =
|
||||||
|
entrypoint bad(r : Remote) =
|
||||||
|
r.id(protected = 0 == 1, 18)
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
function square(x) = x ^ 2
|
function square(x) = x ^ 2
|
||||||
contract Main =
|
contract Main =
|
||||||
entrypoint main() = square(10)
|
entrypoint main_fun() = square(10)
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
// namespace Chain =
|
||||||
|
// record tx = { paying_for : option(Chain.paying_for_tx)
|
||||||
|
// , ga_metas : list(Chain.ga_meta_tx)
|
||||||
|
// , actor : address
|
||||||
|
// , fee : int
|
||||||
|
// , ttl : int
|
||||||
|
// , tx : Chain.base_tx }
|
||||||
|
|
||||||
|
// datatype ga_meta_tx = GAMetaTx(address, int)
|
||||||
|
// datatype paying_for_tx = PayingForTx(address, int)
|
||||||
|
// datatype base_tx = SpendTx(address, int, string)
|
||||||
|
// | OracleRegisterTx | OracleQueryTx | OracleResponseTx | OracleExtendTx
|
||||||
|
// | NamePreclaimTx | NameClaimTx(hash) | NameUpdateTx(string)
|
||||||
|
// | NameRevokeTx(hash) | NameTransferTx(address, string)
|
||||||
|
// | ChannelCreateTx(address) | ChannelDepositTx(address, int) | ChannelWithdrawTx(address, int) |
|
||||||
|
// | ChannelForceProgressTx(address) | ChannelCloseMutualTx(address) | ChannelCloseSoloTx(address)
|
||||||
|
// | ChannelSlashTx(address) | ChannelSettleTx(address) | ChannelSnapshotSoloTx(address)
|
||||||
|
// | ContractCreateTx(int) | ContractCallTx(address, int)
|
||||||
|
// | GAAttachTx
|
||||||
|
|
||||||
|
|
||||||
|
// Contract replicating "normal" Aeternity authentication
|
||||||
|
contract BasicAuthTx =
|
||||||
|
record state = { nonce : int, owner : address }
|
||||||
|
datatype foo = Bar | Baz()
|
||||||
|
|
||||||
|
entrypoint init() = { nonce = 1, owner = Call.caller }
|
||||||
|
|
||||||
|
stateful entrypoint authorize(n : int, s : signature) : bool =
|
||||||
|
require(n >= state.nonce, "Nonce too low")
|
||||||
|
require(n =< state.nonce, "Nonce too high")
|
||||||
|
put(state{ nonce = n + 1 })
|
||||||
|
switch(Auth.tx_hash)
|
||||||
|
None => abort("Not in Auth context")
|
||||||
|
Some(tx_hash) =>
|
||||||
|
let Some(tx0) = Auth.tx
|
||||||
|
let x : option(Chain.paying_for_tx) = tx0.paying_for
|
||||||
|
let x : list(Chain.ga_meta_tx) = tx0.ga_metas
|
||||||
|
let x : int = tx0.fee + tx0.ttl
|
||||||
|
let x : address = tx0.actor
|
||||||
|
let x : Chain.tx = { tx = Chain.NamePreclaimTx, paying_for = None, ga_metas = [],
|
||||||
|
fee = 123, ttl = 0, actor = Call.caller }
|
||||||
|
switch(tx0.tx)
|
||||||
|
Chain.SpendTx(receiver, amount, payload) => verify(tx_hash, n, s)
|
||||||
|
Chain.OracleRegisterTx => false
|
||||||
|
Chain.OracleQueryTx => false
|
||||||
|
Chain.OracleResponseTx => false
|
||||||
|
Chain.OracleExtendTx => false
|
||||||
|
Chain.NamePreclaimTx => false
|
||||||
|
Chain.NameClaimTx(name) => false
|
||||||
|
Chain.NameUpdateTx(name) => false
|
||||||
|
Chain.NameRevokeTx(name) => false
|
||||||
|
Chain.NameTransferTx(to, name) => false
|
||||||
|
Chain.ChannelCreateTx(other_party) => false
|
||||||
|
Chain.ChannelDepositTx(channel, amount) => false
|
||||||
|
Chain.ChannelWithdrawTx(channel, amount) => false
|
||||||
|
Chain.ChannelForceProgressTx(channel) => false
|
||||||
|
Chain.ChannelCloseMutualTx(channel) => false
|
||||||
|
Chain.ChannelCloseSoloTx(channel) => false
|
||||||
|
Chain.ChannelSlashTx(channel) => false
|
||||||
|
Chain.ChannelSettleTx(channel) => false
|
||||||
|
Chain.ChannelSnapshotSoloTx(channel) => false
|
||||||
|
Chain.ContractCreateTx(amount) => false
|
||||||
|
Chain.ContractCallTx(ct_address, amount) => false
|
||||||
|
Chain.GAAttachTx => false
|
||||||
|
|
||||||
|
function verify(tx_hash, n, s) =
|
||||||
|
Crypto.verify_sig(to_sign(tx_hash, n), state.owner, s)
|
||||||
|
|
||||||
|
entrypoint to_sign(h : hash, n : int) =
|
||||||
|
Crypto.blake2b((h, n))
|
||||||
|
|
||||||
|
entrypoint weird_string() : string =
|
||||||
|
"\x19Weird String\x42\nMore\n"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
|
include "String.aes"
|
||||||
contract BytesToX =
|
contract BytesToX =
|
||||||
|
|
||||||
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
|
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ contract ChannelOnChainContractOracle =
|
|||||||
Some(_value) =>
|
Some(_value) =>
|
||||||
"bet_already_taken"
|
"bet_already_taken"
|
||||||
|
|
||||||
|
public function expiry() =
|
||||||
|
Oracle.expiry(state.oracle)
|
||||||
|
|
||||||
public function query_fee() =
|
public function query_fee() =
|
||||||
Oracle.query_fee(state.oracle)
|
Oracle.query_fee(state.oracle)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
contract interface HigherOrderState =
|
||||||
|
entrypoint init : () => void
|
||||||
|
entrypoint apply : int => int
|
||||||
|
stateful entrypoint inc : int => unit
|
||||||
|
|
||||||
|
contract interface LowerDisorderAnarchy =
|
||||||
|
entrypoint init : (int) => void
|
||||||
|
|
||||||
|
|
||||||
|
main contract C =
|
||||||
|
// both `s` and `l` should be of type `HigherOrderState` in this test
|
||||||
|
stateful entrypoint run_clone(s : HigherOrderState, l : LowerDisorderAnarchy) : HigherOrderState =
|
||||||
|
let s1 = Chain.clone(ref=s)
|
||||||
|
let Some(s2) = Chain.clone(ref=s, protected=true)
|
||||||
|
let None = Chain.clone(ref=s, protected=true, gas=1)
|
||||||
|
let None = Chain.clone(ref=l, protected=true, 123) // since it should be HigherOrderState underneath
|
||||||
|
let s3 = Chain.clone(ref=s1)
|
||||||
|
require(s1.apply(2137) == 2137, "APPLY_S1_0")
|
||||||
|
require(s2.apply(2137) == 2137, "APPLY_S2_0")
|
||||||
|
require(s3.apply(2137) == 2137, "APPLY_S3_0")
|
||||||
|
s1.inc(1)
|
||||||
|
s2.inc(1)
|
||||||
|
s1.inc(1)
|
||||||
|
require(s1.apply(2137) == 2139, "APPLY_S1_2")
|
||||||
|
require(s2.apply(2137) == 2138, "APPLY_S2_1")
|
||||||
|
require(s3.apply(2137) == 2137, "APPLY_S3_0")
|
||||||
|
s1
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
contract interface I =
|
||||||
|
entrypoint init : () => void
|
||||||
|
|
||||||
|
contract C =
|
||||||
|
stateful entrypoint f(i : I) =
|
||||||
|
let Some(c1) = Chain.clone(ref=i, protected = true)
|
||||||
|
2
|
||||||
@@ -5,5 +5,5 @@ contract BadAENSresolve =
|
|||||||
function fail() : t(int) =
|
function fail() : t(int) =
|
||||||
AENS.resolve("foo.aet", "whatever")
|
AENS.resolve("foo.aet", "whatever")
|
||||||
|
|
||||||
entrypoint main() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract C =
|
||||||
|
entrypoint f : () => unit
|
||||||
|
|
||||||
|
main contract M =
|
||||||
|
entrypoint f() = 123
|
||||||
@@ -3,4 +3,4 @@ contract MapAsMapKey =
|
|||||||
|
|
||||||
function foo(m) : t(int => int) = {[m] = 0}
|
function foo(m) : t(int => int) = {[m] = 0}
|
||||||
|
|
||||||
entrypoint main() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ contract HigherOrderQueryType =
|
|||||||
stateful function foo(o) : oracle_query(_, string ) =
|
stateful function foo(o) : oracle_query(_, string ) =
|
||||||
Oracle.query(o, (x) => x + 1, 100, RelativeTTL(100), RelativeTTL(100))
|
Oracle.query(o, (x) => x + 1, 100, RelativeTTL(100), RelativeTTL(100))
|
||||||
|
|
||||||
entrypoint main() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ contract HigherOrderResponseType =
|
|||||||
stateful function foo(o, q : oracle_query(string, _)) =
|
stateful function foo(o, q : oracle_query(string, _)) =
|
||||||
Oracle.respond(o, q, (x) => x + 1)
|
Oracle.respond(o, q, (x) => x + 1)
|
||||||
|
|
||||||
entrypoint main() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
namespace LastDeclarationIsNotAContract =
|
|
||||||
function add(x, y) = x + y
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
contract MissingDefinition =
|
contract MissingDefinition =
|
||||||
entrypoint foo : int => int
|
entrypoint foo : int => int
|
||||||
entrypoint main() = foo(0)
|
entrypoint main_fun() = foo(0)
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ contract PolymorphicAENSresolve =
|
|||||||
function fail() : option('a) =
|
function fail() : option('a) =
|
||||||
AENS.resolve("foo.aet", "whatever")
|
AENS.resolve("foo.aet", "whatever")
|
||||||
|
|
||||||
entrypoint main() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ contract MapAsMapKey =
|
|||||||
|
|
||||||
function foo(m) : t('a) = {[m] = 0}
|
function foo(m) : t('a) = {[m] = 0}
|
||||||
|
|
||||||
entrypoint main() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ contract PolymorphicQueryType =
|
|||||||
stateful function is_oracle(o) =
|
stateful function is_oracle(o) =
|
||||||
Oracle.check(o)
|
Oracle.check(o)
|
||||||
|
|
||||||
entrypoint main() = ()
|
entrypoint main_fun() = ()
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ contract PolymorphicResponseType =
|
|||||||
function is_oracle(o : oracle(string, 'r)) =
|
function is_oracle(o : oracle(string, 'r)) =
|
||||||
Oracle.check(o)
|
Oracle.check(o)
|
||||||
|
|
||||||
entrypoint main(o : oracle(string, int)) = is_oracle(o)
|
entrypoint main_fun(o : oracle(string, int)) = is_oracle(o)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
contract Remote =
|
contract interface Remote =
|
||||||
entrypoint foo : int => int
|
entrypoint foo : int => int
|
||||||
|
|
||||||
contract UnappliedContractCall =
|
contract UnappliedContractCall =
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
contract UnappliedNamedArgBuiltin =
|
contract UnappliedNamedArgBuiltin =
|
||||||
// Allowed in FATE, but not AEVM
|
// Allowed in FATE, but not AEVM
|
||||||
stateful entrypoint main(s) =
|
stateful entrypoint main_fun(s) =
|
||||||
let reg = Oracle.register
|
let reg = Oracle.register
|
||||||
reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int)
|
reg(signature = s, Contract.address, 100, RelativeTTL(100)) : oracle(int, int)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
contract Remote =
|
contract interface Remote =
|
||||||
entrypoint up_to : (int) => list(int)
|
entrypoint up_to : (int) => list(int)
|
||||||
entrypoint sum : (list(int)) => int
|
entrypoint sum : (list(int)) => int
|
||||||
entrypoint some_string : () => string
|
entrypoint some_string : () => string
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
contract Foo =
|
contract interface Foo =
|
||||||
entrypoint foo : () => int
|
entrypoint foo : () => int
|
||||||
|
|
||||||
contract Fail =
|
contract Fail =
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
contract IntegerAdder =
|
||||||
|
entrypoint init() = ()
|
||||||
|
entrypoint addIntegers(x, y) = x + y
|
||||||
|
|
||||||
|
contract IntegerAdderHolder =
|
||||||
|
type state = IntegerAdder
|
||||||
|
stateful entrypoint init() = Chain.create() : IntegerAdder
|
||||||
|
entrypoint get() = state
|
||||||
|
|
||||||
|
contract IntegerAdderFactory =
|
||||||
|
entrypoint init() = ()
|
||||||
|
stateful entrypoint new() =
|
||||||
|
let i = Chain.create() : IntegerAdderHolder
|
||||||
|
i.get()
|
||||||
|
|
||||||
|
payable contract ValueAdder =
|
||||||
|
entrypoint init() = ()
|
||||||
|
stateful entrypoint addValue(x) =
|
||||||
|
let integerAdderFactory = Chain.create()
|
||||||
|
let adder = integerAdderFactory.new()
|
||||||
|
adder.addIntegers(x, Contract.balance)
|
||||||
|
|
||||||
|
main contract EnterpriseContract =
|
||||||
|
entrypoint init() = ()
|
||||||
|
stateful payable entrypoint increaseByThree(x) =
|
||||||
|
require(Call.value >= 3, "Price for addition = 3AEtto, insufficient funds")
|
||||||
|
let threeAdder = Chain.create(value = 3)
|
||||||
|
threeAdder.addValue(x)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
// Testing primitives for accessing the block chain environment
|
// Testing primitives for accessing the block chain environment
|
||||||
contract Interface =
|
contract interface Interface =
|
||||||
entrypoint contract_address : () => address
|
entrypoint contract_address : () => address
|
||||||
entrypoint call_origin : () => address
|
entrypoint call_origin : () => address
|
||||||
entrypoint call_caller : () => address
|
entrypoint call_caller : () => address
|
||||||
@@ -44,6 +44,9 @@ contract Environment =
|
|||||||
// Gas price
|
// Gas price
|
||||||
entrypoint call_gas_price() : int = Call.gas_price
|
entrypoint call_gas_price() : int = Call.gas_price
|
||||||
|
|
||||||
|
// Fee
|
||||||
|
entrypoint call_fee() : int = Call.fee
|
||||||
|
|
||||||
// -- Information about the chain ---
|
// -- Information about the chain ---
|
||||||
|
|
||||||
// Account balances
|
// Account balances
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
contract Remote =
|
contract interface Remote =
|
||||||
entrypoint dummy : () => unit
|
entrypoint dummy : () => unit
|
||||||
|
|
||||||
contract Events =
|
contract Events =
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// An implementation of the factorial function where each recursive
|
// An implementation of the factorial function where each recursive
|
||||||
// call is to another contract. Not the cheapest way to compute factorial.
|
// call is to another contract. Not the cheapest way to compute factorial.
|
||||||
contract FactorialServer =
|
contract interface FactorialServer =
|
||||||
entrypoint fac : (int) => int
|
entrypoint fac : (int) => int
|
||||||
|
|
||||||
contract Factorial =
|
contract Factorial =
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
contract interface Kaboom =
|
||||||
|
entrypoint init : () => void
|
||||||
|
|
||||||
|
contract Bakoom =
|
||||||
|
type state = int
|
||||||
|
entrypoint init(x, b) = if(b) x else 0
|
||||||
|
|
||||||
|
main contract Test =
|
||||||
|
stateful entrypoint test(k : Kaboom) =
|
||||||
|
let k_bad1 = Chain.clone() : Kaboom
|
||||||
|
let k_bad2 = Chain.clone(ref=k, 123, true)
|
||||||
|
let k_bad3 = Chain.clone(ref=k, gas=true)
|
||||||
|
let k_bad4 = Chain.create() : Kaboom
|
||||||
|
let k_gud1 = Chain.clone(ref=k)
|
||||||
|
let Some(k_gud2) = Chain.clone(ref=k, protected=true)
|
||||||
|
let Some(k_gud3) = Chain.clone(ref=k, value=10, protected=true, gas=123)
|
||||||
|
|
||||||
|
let b_bad1 = Chain.create() : Bakoom
|
||||||
|
let b_bad2 = Chain.create(123, true, protected=true) : Bakoom
|
||||||
|
let b_bad3 = Chain.create(123, true, value=true) : Bakoom
|
||||||
|
let b_gud1 = Chain.create(123, true) : Bakoom
|
||||||
|
let b_gud2 = Chain.create(123, true, value=100) : Bakoom
|
||||||
|
|
||||||
|
b_gud1
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
|
include "String.aes"
|
||||||
contract FunctionArguments =
|
contract FunctionArguments =
|
||||||
|
|
||||||
entrypoint sum(n : int, m: int) =
|
entrypoint sum(n : int, m: int) =
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
|
main contract Identity =
|
||||||
contract Identity =
|
entrypoint main_fun (x:int) = x
|
||||||
entrypoint main (x:int) = x
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
contract interface ContractOne =
|
||||||
|
entrypoint foo() = "foo"
|
||||||
|
|
||||||
|
contract ContractTwo =
|
||||||
|
entrypoint bar() = "bar"
|
||||||
@@ -16,7 +16,7 @@ contract LHSMatching =
|
|||||||
let null(_ :: _) = false
|
let null(_ :: _) = false
|
||||||
!null(xs)
|
!null(xs)
|
||||||
|
|
||||||
entrypoint main() =
|
entrypoint main_fun() =
|
||||||
from_some(Some([0]))
|
from_some(Some([0]))
|
||||||
++ append([length([true]), 2, 3], [4, 5, 6])
|
++ append([length([true]), 2, 3], [4, 5, 6])
|
||||||
++ [7 | if (local_match([false]))]
|
++ [7 | if (local_match([false]))]
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
contract MissingEventType =
|
contract MissingEventType =
|
||||||
entrypoint main() =
|
entrypoint main_fun() =
|
||||||
Chain.event("MAIN")
|
Chain.event("MAIN")
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
include "String.aes"
|
||||||
|
contract StringX =
|
||||||
|
entrypoint test() =
|
||||||
|
let s1 = "a string"
|
||||||
|
let s2 = "another string"
|
||||||
|
let s = String.concat(s1, s2)
|
||||||
|
String.sha256(s)
|
||||||
|
String.length(s1)
|
||||||
|
String.from_list(String.to_list(s))
|
||||||
|
String.split(4, s1)
|
||||||
|
String.at(2, s2)
|
||||||
|
String.tokens(s, ",")
|
||||||
|
String.to_upper(s1)
|
||||||
|
String.to_lower(s2)
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
contract ContractOne =
|
contract Child =
|
||||||
entrypoint foo() = "foo"
|
entrypoint
|
||||||
|
add2 : int => int
|
||||||
|
add2(x) = x + 2
|
||||||
|
|
||||||
contract ContractTwo =
|
main contract Main =
|
||||||
entrypoint bar() = "bar"
|
entrypoint add4(x, c : Child) = c.add2(x) + 2
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
main contract C =
|
||||||
|
entrypoint f() = 123
|
||||||
|
|
||||||
|
main contract D =
|
||||||
|
entrypoint f() = 123
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
contract interface C =
|
||||||
|
entrypoint f : () => unit
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
contract C1 =
|
contract interface C1 =
|
||||||
entrypoint f : int
|
entrypoint f : int
|
||||||
|
|
||||||
contract C =
|
contract C =
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
include "BLS12_381.aes"
|
||||||
|
|
||||||
|
contract GrothVerify =
|
||||||
|
type fr = BLS12_381.fr
|
||||||
|
type g1 = BLS12_381.g1
|
||||||
|
type g2 = BLS12_381.g2
|
||||||
|
|
||||||
|
record proof = { a : g1, b : g2, c : g1 }
|
||||||
|
record verify_key = { a : g1, b : g2, c : g2, d : g2, ic : list(g1) }
|
||||||
|
|
||||||
|
record state = { vk : verify_key }
|
||||||
|
|
||||||
|
entrypoint init(vk0 : verify_key) = {vk = vk0}
|
||||||
|
|
||||||
|
entrypoint verify_proof(p : proof, input : list(fr)) =
|
||||||
|
let vk = state.vk
|
||||||
|
let vk_x = calc_vk_x(vk.ic, input)
|
||||||
|
|
||||||
|
BLS12_381.pairing_check([BLS12_381.g1_neg(p.a), vk.a, vk_x, p.c],
|
||||||
|
[p.b, vk.b, vk.c, vk.d])
|
||||||
|
|
||||||
|
function calc_vk_x(ics : list(g1), xs : list(fr)) =
|
||||||
|
switch(ics)
|
||||||
|
(ic :: ics) => calc_vk_x_(ic, ics, xs)
|
||||||
|
|
||||||
|
function calc_vk_x_(vk_x : g1, ics : list(g1), xs : list(fr)) =
|
||||||
|
switch((ics, xs))
|
||||||
|
([], []) => vk_x
|
||||||
|
(ic :: ics, x :: xs) => calc_vk_x_(BLS12_381.g1_add(vk_x, BLS12_381.g1_mul(x, ic)), ics, xs)
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
contract interface Remote =
|
||||||
|
entrypoint id : int => int
|
||||||
|
|
||||||
|
contract ProtectedCall =
|
||||||
|
|
||||||
|
function higher_order(r : Remote) =
|
||||||
|
r.id
|
||||||
|
|
||||||
|
entrypoint test_ok(r : Remote) =
|
||||||
|
let f = higher_order(r)
|
||||||
|
let Some(n) = r.id(protected = true, 10)
|
||||||
|
let Some(m) = f(protected = true, 5)
|
||||||
|
n + m + r.id(protected = false, 100) + f(1)
|
||||||
|
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
|
|
||||||
contract Remote1 =
|
contract interface Remote1 =
|
||||||
entrypoint main : (int) => int
|
entrypoint main_fun : (int) => int
|
||||||
|
|
||||||
contract Remote2 =
|
contract interface Remote2 =
|
||||||
entrypoint call : (Remote1, int) => int
|
entrypoint call : (Remote1, int) => int
|
||||||
|
|
||||||
contract Remote3 =
|
contract interface Remote3 =
|
||||||
entrypoint get : () => int
|
entrypoint get : () => int
|
||||||
entrypoint tick : () => unit
|
entrypoint tick : () => unit
|
||||||
|
|
||||||
contract RemoteCall =
|
contract RemoteCall =
|
||||||
|
|
||||||
stateful entrypoint call(r : Remote1, x : int) : int =
|
stateful entrypoint call(r : Remote1, x : int) : int =
|
||||||
r.main(gas = 10000, value = 10, x)
|
r.main_fun(gas = 10000, value = 10, x)
|
||||||
|
|
||||||
entrypoint staged_call(r1 : Remote1, r2 : Remote2, x : int) =
|
entrypoint staged_call(r1 : Remote1, r2 : Remote2, x : int) =
|
||||||
r2.call(r1, x)
|
r2.call(r1, x)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
contract SpendContract =
|
contract interface SpendContract =
|
||||||
entrypoint withdraw : (int) => int
|
entrypoint withdraw : (int) => int
|
||||||
|
|
||||||
contract SpendTest =
|
contract SpendTest =
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
contract Remote =
|
include "String.aes"
|
||||||
|
contract interface Remote =
|
||||||
record rstate = { i : int, s : string, m : map(int, int) }
|
record rstate = { i : int, s : string, m : map(int, int) }
|
||||||
|
|
||||||
entrypoint look_at : (rstate) => unit
|
entrypoint look_at : (rstate) => unit
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
contract Remote =
|
contract interface Remote =
|
||||||
stateful entrypoint remote_spend : (address, int) => unit
|
stateful entrypoint remote_spend : (address, int) => unit
|
||||||
entrypoint remote_pure : int => int
|
entrypoint remote_pure : int => int
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
include "String.aes"
|
||||||
contract Strings =
|
contract Strings =
|
||||||
entrypoint str_len(s) = String.length(s)
|
entrypoint str_len(s) = String.length(s)
|
||||||
entrypoint str_concat(s1, s2) = String.concat(s1, s2)
|
entrypoint str_concat(s1, s2) = String.concat(s1, s2)
|
||||||
|
|||||||
+9
-99
@@ -1,102 +1,12 @@
|
|||||||
|
// This is a custom test file if you need to run a compiler without
|
||||||
|
// changing aeso_compiler_tests.erl
|
||||||
|
|
||||||
contract Identity =
|
include "List.aes"
|
||||||
// type xy = {x:int, y:int}
|
|
||||||
// type xz = {x:int, z:int}
|
|
||||||
// type yz = {y:int, z:int}
|
|
||||||
record point = {x:int,y:int}
|
|
||||||
record cp('a) = {color: string, p:'a}
|
|
||||||
//type intpoint = point(int)
|
|
||||||
// //if (x==42) 1 else (x*x)
|
|
||||||
// }
|
|
||||||
//let baz() = {age:3, name:(4:int)}
|
|
||||||
//let foo(a,b,c) = c
|
|
||||||
// let rec fac(n) = if((n:int)==0) 1 else (n*fac(n-1))
|
|
||||||
// and main(x) = x::[x+1]
|
|
||||||
// let lentr(l) = lent(0,l)
|
|
||||||
// let rec len(l) =
|
|
||||||
// switch(l) {
|
|
||||||
// | [] => 0
|
|
||||||
// | x::xs => 1+len(xs)
|
|
||||||
// }
|
|
||||||
// let lent(n,l) =
|
|
||||||
// switch (l) {
|
|
||||||
// | [] => n
|
|
||||||
// | (x::xs) => lent(n+1,xs)
|
|
||||||
// }
|
|
||||||
// let rec app(a,b) =
|
|
||||||
// switch(a) {
|
|
||||||
// | [] => b
|
|
||||||
// | (x::xs) => x::app(xs,b)
|
|
||||||
// }
|
|
||||||
// let rec revt(l,r) =
|
|
||||||
// switch(l) {
|
|
||||||
// | [] => r
|
|
||||||
// | x::xs => revt(xs,x::r)
|
|
||||||
// }
|
|
||||||
// let rev(l) = revt(l,[])
|
|
||||||
// let main(x:int) = {
|
|
||||||
// switch(rev([1,2,3])) {
|
|
||||||
// | h::_ => h
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//let fac(n:int) = {
|
|
||||||
// if (n==0) 1 else (n*fac(n-1))
|
|
||||||
//}
|
|
||||||
//let main(x) = switch((12,34)) {
|
|
||||||
//| (13,_) => x
|
|
||||||
//| (_,a) => x+a
|
|
||||||
// | y => y+1
|
|
||||||
// }
|
|
||||||
//let main(x) = ({y:0>1, x:x==0}:point(bool))
|
|
||||||
//let main(x) = x
|
|
||||||
//let main(x) = len(1::2::[])
|
|
||||||
//let main(x) = ((x,x):list('a))
|
|
||||||
// let main(x) = switch("a") {
|
|
||||||
// | "b" => 0
|
|
||||||
// | "a" => 1
|
|
||||||
// | "c" => 2
|
|
||||||
// }
|
|
||||||
//let main(x) = x.color+1
|
|
||||||
//let main(x) = switch(({x:x, y:x+1}:cp(int))) {
|
|
||||||
// | {y:xx} => xx
|
|
||||||
// }
|
|
||||||
//let main(x) = {x:0, y:1, z:2}
|
|
||||||
// let id(x) = x
|
|
||||||
// let double(x) = x+x
|
|
||||||
// let pair(x) = (1,2)
|
|
||||||
// let unit(x) = ()
|
|
||||||
// let tuples(x) = ((1,x),(2,3,4))
|
|
||||||
// let singleton(x) = [x]
|
|
||||||
// let rec seq(n) = if (n==0) [] else (app(seq(n-1),[n]))
|
|
||||||
// let idString(s:string) = s
|
|
||||||
// let pairString(s:string) = (s,s)
|
|
||||||
// let revStrings(ss:list(string))=rev(ss)
|
|
||||||
// let makePoint(x,y) = {x:x, y:y}
|
|
||||||
// let getx(x) = x.x
|
|
||||||
// let updatex(p,x) = p{x:x}
|
|
||||||
// let quad(x) = {let y=x+x; let z=y+y; z;}
|
|
||||||
// let noblock(x) = {x; x}
|
|
||||||
// let unit(x) = ()
|
|
||||||
// let foo(x) = switch (x) {
|
|
||||||
// | y => y+1
|
|
||||||
// }
|
|
||||||
// let p(x) = {color:"blue", p:{x:x, y:x+1}}
|
|
||||||
//let twice(f,x) = f(f(x))
|
|
||||||
// let twice(f,x) = f(f(x))
|
|
||||||
// let double(x) = x+x
|
|
||||||
// let main(x) = twice((y=>y+y),x)
|
|
||||||
// let rec map(f,xs) = switch(xs) {
|
|
||||||
// | [] => []
|
|
||||||
// | (x::ys) => f(x)::map(f,ys)
|
|
||||||
// }
|
|
||||||
// let id(x) = x
|
|
||||||
// let main(xs) = map(double,xs)
|
|
||||||
function z(f,x) = x
|
|
||||||
function s(n) = (f,x)=>f(n(f,x))
|
|
||||||
function add(m,n) = (f,x)=>m(f,n(f,x))
|
|
||||||
|
|
||||||
entrypoint main() =
|
contract IntegerHolder =
|
||||||
let three=s(s(s(z)))
|
type state = int
|
||||||
add(three,three)
|
entrypoint init(x) = x
|
||||||
(((i)=>i+1),0)
|
entrypoint get() = state
|
||||||
|
|
||||||
|
main contract Test =
|
||||||
|
stateful entrypoint f(c) = Chain.clone(ref=c, 123)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
contract Remote =
|
contract interface Remote =
|
||||||
|
|
||||||
type themap = map(int, string)
|
type themap = map(int, string)
|
||||||
entrypoint foo : () => themap
|
entrypoint foo : () => themap
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
// AENS.transfer
|
// AENS.transfer
|
||||||
// AENS.revoke
|
// AENS.revoke
|
||||||
// Oracle.extend
|
// Oracle.extend
|
||||||
|
include "String.aes"
|
||||||
contract UnappliedBuiltins =
|
contract UnappliedBuiltins =
|
||||||
entrypoint main() = ()
|
entrypoint main_fun() = ()
|
||||||
type o = oracle(int, int)
|
type o = oracle(int, int)
|
||||||
type t = list(int * string)
|
type t = list(int * string)
|
||||||
type m = map(int, int)
|
type m = map(int, int)
|
||||||
@@ -21,6 +22,7 @@ contract UnappliedBuiltins =
|
|||||||
function b_abort() = abort
|
function b_abort() = abort
|
||||||
function b_require() = require
|
function b_require() = require
|
||||||
function oracle_query_fee() = Oracle.query_fee
|
function oracle_query_fee() = Oracle.query_fee
|
||||||
|
function oracle_expiry() = Oracle.expiry
|
||||||
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
||||||
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
||||||
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
contract UTF8 =
|
||||||
|
entrypoint f1() : char = '1'
|
||||||
|
entrypoint f2() : char = '+'
|
||||||
|
entrypoint f3() : char = 'd'
|
||||||
|
entrypoint f4() : char = 'X'
|
||||||
|
entrypoint f5() : char = 'å'
|
||||||
|
entrypoint f6() : char = 'Ä'
|
||||||
|
entrypoint f7() : char = 'æ'
|
||||||
|
entrypoint f8() : char = 'ë'
|
||||||
|
entrypoint f9() : char = 'ẻ'
|
||||||
|
entrypoint f10() : char = '\x27'
|
||||||
|
entrypoint f11() : char = '\x{2200}'
|
||||||
|
entrypoint f12() : char = '💩'
|
||||||
|
entrypoint f13() : char = '\n'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// entrypoint f13() : char = 'e̊'
|
||||||
|
// entrypoint f14() : char = '\Ì'
|
||||||
|
|
||||||
|
// '💩' vs. map('a,'b)
|
||||||
Reference in New Issue
Block a user