Compare commits

...

61 Commits

Author SHA1 Message Date
radrow 1266d9ea99 Fix one test2 2020-10-13 10:42:38 +02:00
radrow bbb049cb2e Merge branch 'lima' into lima-master-merge 2020-10-13 10:33:30 +02:00
radrow 787551b8bc Fix one test 2020-10-13 10:29:14 +02:00
radrow ac673602b9 Merge branch 'lima' into master 2020-10-13 10:22:05 +02:00
Radosław Rowicki 0b83422189 Merge pull request #284 from aeternity/hermetization-turnoff
Debug mode to turn off hermetization
2020-10-12 13:17:40 +02:00
radrow 1a5017ce2b Debug mode turns off hermetization
Added tests and fixed bugs
2020-10-09 18:41:30 +02:00
Hans Svensson 079b3a45c9 Merge pull request #283 from aeternity/GH-3002-blockhash_at_current_height
Document Chain.block_hash at current height
2020-10-08 10:48:41 +02:00
Hans Svensson c0d9759e60 Whitespace fixes 2020-10-07 16:53:24 +02:00
Hans Svensson b7b242bc66 Document the (changed) behavior of Chain.block_hash 2020-10-07 16:53:10 +02:00
Grzegorz Uriasz 25fa365c29 Merge pull request #280 from aeternity/optionally_generate_aci
Provide the ACI along with the bytecode
2020-09-10 10:05:51 +02:00
Hans Svensson 05b87fe200 Merge pull request #259 from kryptokrauts/master
fix param description for AENS interface
2020-05-07 16:07:10 +02:00
Marco Walz cc07e3a638 fix param description for AENS interface 2020-05-05 11:21:27 +02:00
Radosław Rowicki ea5850cf93 Merge pull request #256 from radrow/master
Merge lima – 4.3.0
2020-04-30 13:52:50 +02:00
radrow d2dcb9e249 Add AENS example, readd delegation signature chapter, fix links 2020-04-30 00:02:26 +02:00
radrow adb3cf5406 Update documentation to master 2020-04-29 15:27:40 +02:00
radrow ad78f440d9 Merge lima 2020-04-02 15:32:26 +02:00
Ulf Norell 42cd47d1b3 Merge pull request #241 from aeternity/GH-203-protected-calls
Add support for protected contract calls
2020-03-02 16:15:05 +01:00
Ulf Norell 93d2086ddf aebytecode commit 2020-03-02 12:08:41 +01:00
Ulf Norell 9487b79f42 Fix dialyzer complaints 2020-03-02 12:08:41 +01:00
Ulf Norell e64ac9396a Test cases for protected calls 2020-03-02 12:08:41 +01:00
Ulf Norell 4a812b6f3b Error message tweaks 2020-03-02 11:35:55 +01:00
Ulf Norell fe2d93ea8a Compile protected calls to CALL_PGR 2020-03-02 11:35:55 +01:00
Ulf Norell ecbc15db1b Add 'protected' named argument to remote calls
If protected = true, the return type is wrapped in an option() which comes back
None if the remote call fails for any reason.
2020-03-02 08:51:33 +01:00
Hans Svensson d0caee24d9 Merge pull request #240 from aeternity/GH-176-additional_documentation
Add some comments to String.aes
2020-02-28 11:16:38 +01:00
Hans Svensson 57eb77f2f8 Add some comments to String.aes 2020-02-28 11:07:02 +01:00
Hans Svensson 53ed60b498 Merge pull request #238 from aeternity/GH-176-more_string_functions
Change Char.to_lower/upper into String.to_lower/upper
2020-02-26 20:01:10 +01:00
Hans Svensson e49738c90c New aebytecode reference 2020-02-26 18:23:12 +01:00
Hans Svensson a38a365181 Change Char.to_lower/upper into String.to_lower/upper 2020-02-26 14:15:49 +01:00
Hans Svensson 0dddac3d86 Merge pull request #236 from aeternity/GH-176-more_string_functions
Gh 176 more string functions
2020-02-25 09:49:00 +01:00
Hans Svensson 3da694e798 New aebytecode commit hash 2020-02-25 09:02:18 +01:00
Hans Svensson e98edd4eef Handle UTF-8 in character literals
Also handle `\x{hhh..}` in strings... Character literals has to be a single character, not composite.

+ tests (and the corresponding fix to the char literal pretty printer)
2020-02-24 15:35:54 +01:00
Hans Svensson 2bad76314f More efficient implementations in String.aes 2020-02-24 15:34:23 +01:00
Hans Svensson b9acf24dca Make String.aes a stdlib + add more string functions
This means moving the FATE operations to StringInternal and adding to/from_list (and Char.to/from_int
+ Char.to_upper/lower).
2020-02-21 09:45:11 +01:00
Hans Svensson 6682b24156 Merge pull request #223 from aeternity/GH-202-Auth_tx_introspection
Gh 202 auth tx introspection
2020-02-11 15:48:46 +01:00
Hans Svensson b31be6227d Update aebytecode reference 2020-02-11 14:20:41 +01:00
Hans Svensson bbc8555331 Auth.tx implementation and types
The arities field of the Chain.base_tx constructor is 22 integers long... This isn't very pretty but
that is the design we've chosen.
2020-02-11 10:00:46 +01:00
Radosław Rowicki 13bc821211 Optimize stdlib (#215) 2020-02-07 19:51:12 +01:00
Hans Svensson 34c10e1518 Merge pull request #214 from aeternity/GH-188-AENS_lookup
Add AENS.lookup and Oracle.expiry
2020-02-03 14:37:55 +01:00
Hans Svensson bb79e7dd89 Update aebytecode reference 2020-02-03 14:26:13 +01:00
Hans Svensson c3426f0e65 Add AENS.lookup
Also move Pointee-constructors inside AENS namespace.
2020-02-03 12:52:00 +01:00
Hans Svensson db01e237c1 Add Oracle.expiry 2020-02-03 12:24:27 +01:00
Hans Svensson 760d2841d1 From Iris (with AENS.update) AENS.resolve is not Pure 2020-02-03 12:22:34 +01:00
Hans Svensson 43013ec920 Merge pull request #213 from aeternity/merge_lima_to_master
Merge lima to master
2020-01-15 15:54:41 +01:00
Hans Svensson d821de6381 Merge 'origin/lima' into 'origin/master' 2020-01-15 15:03:11 +01:00
Hans Svensson 282f743925 Merge pull request #209 from aeternity/merge_lima_to_master
Merge lima to master
2019-12-19 15:34:26 +01:00
Hans Svensson cf1072140e Merge 'origin/lima' into merge_lima_to_master 2019-12-19 15:09:48 +01:00
Hans Svensson 75797686ad Merge pull request #187 from aeternity/GH-2850-add_fancy_crypto_bls12_381
Add BLS12-381 operations (to FATE)
2019-11-29 15:27:38 +01:00
Tino Breddin ed9384c2af Merge pull request #185 from aeternity/tb-rebar-lock-check
Add CI check to verify rebar.lock
2019-11-27 17:28:31 +01:00
Hans Svensson 1c24a700dc Add BLS12-381 operations (to FATE) 2019-11-27 10:57:24 +01:00
Hans Svensson f2e9fbcc51 Merge pull request #186 from aeternity/merge_lima
Merge lima into master
2019-11-27 10:50:29 +01:00
Hans Svensson 2d49426fe0 Merge 'origin/lima' into master 2019-11-27 10:46:18 +01:00
Tino Breddin f5df2c1a5f Add CI workflow 2019-11-26 17:27:26 +01:00
Tino Breddin 04445e4dee Add CI check to verify rebar.lock 2019-11-26 17:24:02 +01:00
Ulf Norell eec70f03a5 Merge pull request #183 from aeternity/lima
Merge lima into master
2019-11-25 13:16:17 +01:00
skkw c2c8e297ae Merge pull request #178 from aeternity/PT-158904718-aens-update-sophia
changed type of client ttl to int option
2019-11-18 18:39:54 +01:00
skkw 5c5d3c60ef changed type of client ttl to int option 2019-11-18 16:25:47 +01:00
Ulf Norell 2a3274ba25 Merge pull request #175 from aeternity/GH-174-encode-decode-bits
Handle encoding/decoding bits
2019-11-18 12:12:45 +01:00
Ulf Norell 13b7bde44b Prefix format annotation for negative numbers 2019-11-18 11:55:04 +01:00
Ulf Norell baf527b5fa Handle encoding/decoding bits
Fixes GH-174
2019-11-18 11:35:08 +01:00
skkw 422baa5b65 Merge pull request #155 from aeternity/PT-158904718-aens-update-sophia
support for AENS.update call
2019-11-12 14:49:42 +01:00
skkw 126e04ae42 support for AENS.update call 2019-11-12 14:27:46 +01:00
38 changed files with 1693 additions and 519 deletions
+16
View File
@@ -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
+94 -6
View File
@@ -2,9 +2,11 @@
**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)
- [Mutable state](#mutable-state) - [Mutable state](#mutable-state)
- [Stateful functions](#stateful-functions) - [Stateful functions](#stateful-functions)
- [Payable](#payable) - [Payable](#payable)
@@ -26,6 +28,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 +37,7 @@
- [Example](#example) - [Example](#example)
- [Sanity checks](#sanity-checks) - [Sanity checks](#sanity-checks)
- [AENS interface](#aens-interface) - [AENS interface](#aens-interface)
- [Example](#example-1)
- [Events](#events) - [Events](#events)
- [Argument order](#argument-order) - [Argument order](#argument-order)
- [Compiler pragmas](#compiler-pragmas) - [Compiler pragmas](#compiler-pragmas)
@@ -52,6 +56,7 @@
- [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 ## The Sophia Language
@@ -136,6 +141,36 @@ without calling it you can write
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 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.
### 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
@@ -538,7 +573,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 +609,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
@@ -645,6 +688,43 @@ 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:
```
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:
```
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)
```
### Events ### Events
@@ -1069,3 +1149,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.).
+501 -263
View File
File diff suppressed because it is too large Load Diff
+68
View File
@@ -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)))
+84 -79
View File
@@ -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) else switch(l)
[] => reverse(acc) [] => []
h::t => take_(n-1, t, h::acc) h::t => h::take_(n-1, t)
/** Drop `n` first elements /** Drop up to `n` first elements
*/ */
function drop(n : int, l : list('a)) : list('a) = function drop(n : int, l : list('a)) : list('a) =
if(n < 0) abort("Drop negative number of elements") if(n < 0) abort("Drop negative number of elements") else drop_(n, l)
elif (n == 0) l private function drop_(n : int, l : list('a)) : list('a) =
if (n == 0) l
else switch(l) else switch(l)
[] => [] [] => []
h::t => drop(n-1, t) 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
@@ -203,28 +214,25 @@ namespace List =
/** 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? // TODO: Improve?
@@ -235,17 +243,14 @@ namespace List =
/** 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
View File
@@ -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)
+117
View File
@@ -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
+1 -1
View File
@@ -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,"7f0d309"}}}
, {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",
+1 -1
View File
@@ -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,"7f0d3090d4dc6c4d5fca7645b0c21eb0e65ad208"}},
0}, 0},
{<<"aeserialization">>, {<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",
+1 -1
View File
@@ -69,7 +69,7 @@ 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]), %% io:format("~p\n", [Ast]),
{TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold]), {TypedAst, _} = aeso_ast_infer_types:infer(Ast, [dont_unfold | Options]),
%% io:format("~p\n", [TypedAst]), %% io:format("~p\n", [TypedAst]),
from_typed_ast(Type, TypedAst) from_typed_ast(Type, TypedAst)
catch catch
+251 -38
View File
@@ -24,6 +24,7 @@
| aeso_syntax:id() | aeso_syntax:qid() | aeso_syntax:id() | aeso_syntax:qid()
| aeso_syntax:con() | aeso_syntax:qcon() %% contracts | aeso_syntax:con() | aeso_syntax:qcon() %% contracts
| aeso_syntax:tvar() | aeso_syntax:tvar()
| {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected)
| uvar(). | uvar().
-type uvar() :: {uvar, aeso_syntax:ann(), reference()}. -type uvar() :: {uvar, aeso_syntax:ann(), reference()}.
@@ -47,7 +48,14 @@
name :: aeso_syntax:id(), name :: aeso_syntax:id(),
type :: utype()}). type :: utype()}).
-type named_argument_constraint() :: #named_argument_constraint{}. -record(dependent_type_constraint,
{ named_args_t :: named_args_t()
, named_args :: [aeso_syntax:arg_expr()]
, general_type :: utype()
, specialized_type :: utype()
, context :: term() }).
-type named_argument_constraint() :: #named_argument_constraint{} | #dependent_type_constraint{}.
-record(field_constraint, -record(field_constraint,
{ record_t :: utype() { record_t :: utype()
@@ -236,16 +244,21 @@ bind_fields([], Env) -> Env;
bind_fields([{Id, Info} | Rest], Env) -> bind_fields([{Id, Info} | Rest], Env) ->
bind_fields(Rest, bind_field(Id, Info, Env)). bind_fields(Rest, bind_field(Id, Info, Env)).
%% Contract entrypoints take two named arguments (gas : int = Call.gas_left(), value : int = 0). %% Contract entrypoints take three named arguments
%% gas : int = Call.gas_left()
%% value : int = 0
%% protected : bool = false
contract_call_type({fun_t, Ann, [], Args, Ret}) -> contract_call_type({fun_t, Ann, [], Args, Ret}) ->
Id = fun(X) -> {id, Ann, X} end, Id = fun(X) -> {id, Ann, X} end,
Int = Id("int"), Int = Id("int"),
Typed = fun(E, T) -> {typed, Ann, E, T} end, Typed = fun(E, T) -> {typed, Ann, E, T} end,
Named = fun(Name, Default) -> {named_arg_t, Ann, Id(Name), Int, Default} end, Named = fun(Name, Default = {typed, _, _, T}) -> {named_arg_t, Ann, Id(Name), T, Default} end,
{fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]}, {fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]},
{fun_t, Ann, [], [], Int}), {fun_t, Ann, [], [], Int}),
[]}, Int)), []}, Int)),
Named("value", Typed({int, Ann, 0}, Int))], Args, Ret}. Named("value", Typed({int, Ann, 0}, Int)),
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
-spec bind_contract(aeso_syntax:decl(), env()) -> env(). -spec bind_contract(aeso_syntax:decl(), env()) -> env().
bind_contract({contract, Ann, Id, Contents}, Env) -> bind_contract({contract, Ann, Id, Contents}, Env) ->
@@ -368,6 +381,7 @@ is_private(Ann) -> proplists:get_value(private, Ann, false).
global_env() -> global_env() ->
Ann = [{origin, system}], Ann = [{origin, system}],
Int = {id, Ann, "int"}, Int = {id, Ann, "int"},
Char = {id, Ann, "char"},
Bool = {id, Ann, "bool"}, Bool = {id, Ann, "bool"},
String = {id, Ann, "string"}, String = {id, Ann, "string"},
Address = {id, Ann, "address"}, Address = {id, Ann, "address"},
@@ -393,6 +407,24 @@ global_env() ->
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}}, Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end, SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
TTL = {qid, Ann, ["Chain", "ttl"]}, TTL = {qid, Ann, ["Chain", "ttl"]},
Pointee = {qid, Ann, ["AENS", "pointee"]},
AENSName = {qid, Ann, ["AENS", "name"]},
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
Fp2 = {tuple_t, Ann, [Fp, Fp]},
G1 = {tuple_t, Ann, [Fp, Fp, Fp]},
G2 = {tuple_t, Ann, [Fp2, Fp2, Fp2]},
GT = {tuple_t, Ann, lists:duplicate(12, Fp)},
Tx = {qid, Ann, ["Chain", "tx"]},
GAMetaTx = {qid, Ann, ["Chain", "ga_meta_tx"]},
BaseTx = {qid, Ann, ["Chain", "base_tx"]},
PayForTx = {qid, Ann, ["Chain", "paying_for_tx"]},
FldT = fun(Id, T) -> {field_t, Ann, {id, Ann, Id}, T} end,
TxFlds = [{"paying_for", Option(PayForTx)}, {"ga_metas", List(GAMetaTx)},
{"actor", Address}, {"fee", Int}, {"ttl", Int}, {"tx", BaseTx}],
TxType = {record_t, [FldT(N, T) || {N, T} <- TxFlds ]},
Fee = Int, Fee = Int,
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]), [A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
@@ -430,8 +462,36 @@ global_env() ->
{"timestamp", Int}, {"timestamp", Int},
{"block_height", Int}, {"block_height", Int},
{"difficulty", Int}, {"difficulty", Int},
{"gas_limit", Int}]) {"gas_limit", Int},
, types = MkDefs([{"ttl", 0}]) }, %% Tx constructors
{"GAMetaTx", Fun([Address, Int], GAMetaTx)},
{"PayingForTx", Fun([Address, Int], PayForTx)},
{"SpendTx", Fun([Address, Int, String], BaseTx)},
{"OracleRegisterTx", BaseTx},
{"OracleQueryTx", BaseTx},
{"OracleResponseTx", BaseTx},
{"OracleExtendTx", BaseTx},
{"NamePreclaimTx", BaseTx},
{"NameClaimTx", Fun([String], BaseTx)},
{"NameUpdateTx", Fun([Hash], BaseTx)},
{"NameRevokeTx", Fun([Hash], BaseTx)},
{"NameTransferTx", Fun([Address, Hash], BaseTx)},
{"ChannelCreateTx", Fun([Address], BaseTx)},
{"ChannelDepositTx", Fun([Address, Int], BaseTx)},
{"ChannelWithdrawTx", Fun([Address, Int], BaseTx)},
{"ChannelForceProgressTx", Fun([Address], BaseTx)},
{"ChannelCloseMutualTx", Fun([Address], BaseTx)},
{"ChannelCloseSoloTx", Fun([Address], BaseTx)},
{"ChannelSlashTx", Fun([Address], BaseTx)},
{"ChannelSettleTx", Fun([Address], BaseTx)},
{"ChannelSnapshotSoloTx", Fun([Address], BaseTx)},
{"ContractCreateTx", Fun([Int], BaseTx)},
{"ContractCallTx", Fun([Address, Int], BaseTx)},
{"GAAttachTx", BaseTx}
])
, types = MkDefs([{"ttl", 0}, {"tx", {[], TxType}},
{"base_tx", 0},
{"paying_for_tx", 0}, {"ga_meta_tx", 0}]) },
ContractScope = #scope ContractScope = #scope
{ funs = MkDefs( { funs = MkDefs(
@@ -451,6 +511,7 @@ global_env() ->
OracleScope = #scope OracleScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))}, [{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
{"expiry", Fun([Oracle(Q, R)], Fee)},
{"query_fee", Fun([Oracle(Q, R)], Fee)}, {"query_fee", Fun([Oracle(Q, R)], Fee)},
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))}, {"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)}, {"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
@@ -466,7 +527,18 @@ global_env() ->
{"preclaim", SignFun([Address, Hash], Unit)}, {"preclaim", SignFun([Address, Hash], Unit)},
{"claim", SignFun([Address, String, Int, Int], Unit)}, {"claim", SignFun([Address, String, Int, Int], Unit)},
{"transfer", SignFun([Address, Address, String], Unit)}, {"transfer", SignFun([Address, Address, String], Unit)},
{"revoke", SignFun([Address, String], Unit)}]) }, {"revoke", SignFun([Address, String], Unit)},
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)},
{"lookup", Fun([String], option_t(Ann, AENSName))},
%% AENS pointee constructors
{"AccountPt", Fun1(Address, Pointee)},
{"OraclePt", Fun1(Address, Pointee)},
{"ContractPt", Fun1(Address, Pointee)},
{"ChannelPt", Fun1(Address, Pointee)},
%% Name object constructor
{"Name", Fun([Address, TTL, Map(String, Pointee)], AENSName)}
])
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
MapScope = #scope MapScope = #scope
{ funs = MkDefs( { funs = MkDefs(
@@ -489,19 +561,65 @@ global_env() ->
{"sha256", Fun1(A, Hash)}, {"sha256", Fun1(A, Hash)},
{"blake2b", Fun1(A, Hash)}]) }, {"blake2b", Fun1(A, Hash)}]) },
%% Fancy BLS12-381 crypto operations
MCL_BLS12_381_Scope = #scope
{ funs = MkDefs(
[{"g1_neg", Fun1(G1, G1)},
{"g1_norm", Fun1(G1, G1)},
{"g1_valid", Fun1(G1, Bool)},
{"g1_is_zero", Fun1(G1, Bool)},
{"g1_add", Fun ([G1, G1], G1)},
{"g1_mul", Fun ([Fr, G1], G1)},
{"g2_neg", Fun1(G2, G2)},
{"g2_norm", Fun1(G2, G2)},
{"g2_valid", Fun1(G2, Bool)},
{"g2_is_zero", Fun1(G2, Bool)},
{"g2_add", Fun ([G2, G2], G2)},
{"g2_mul", Fun ([Fr, G2], G2)},
{"gt_inv", Fun1(GT, GT)},
{"gt_add", Fun ([GT, GT], GT)},
{"gt_mul", Fun ([GT, GT], GT)},
{"gt_pow", Fun ([GT, Fr], GT)},
{"gt_is_one", Fun1(GT, Bool)},
{"pairing", Fun ([G1, G2], GT)},
{"miller_loop", Fun ([G1, G2], GT)},
{"final_exp", Fun1(GT, GT)},
{"int_to_fr", Fun1(Int, Fr)},
{"int_to_fp", Fun1(Int, Fp)},
{"fr_to_int", Fun1(Fr, Int)},
{"fp_to_int", Fun1(Fp, Int)}
]),
types = MkDefs(
[{"fr", 0}, {"fp", 0}]) },
%% Authentication %% Authentication
AuthScope = #scope AuthScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"tx_hash", Option(Hash)}]) }, [{"tx_hash", Option(Hash)},
{"tx", Option(Tx)} ]) },
%% Strings %% Strings
StringScope = #scope StringScope = #scope
{ funs = MkDefs( { funs = MkDefs(
[{"length", Fun1(String, Int)}, [{"length", Fun1(String, Int)},
{"concat", Fun([String, String], String)}, {"concat", Fun([String, String], String)},
{"sha3", Fun1(String, Hash)}, {"to_list", Fun1(String, List(Char))},
{"sha256", Fun1(String, Hash)}, {"from_list", Fun1(List(Char), String)},
{"blake2b", Fun1(String, Hash)}]) }, {"to_upper", Fun1(String, String)},
{"to_lower", Fun1(String, String)},
{"sha3", Fun1(String, Hash)},
{"sha256", Fun1(String, Hash)},
{"blake2b", Fun1(String, Hash)}
]) },
%% Chars
CharScope = #scope
{ funs = MkDefs(
[{"to_int", Fun1(Char, Int)},
{"from_int", Fun1(Int, Option(Char))}]) },
%% Bits %% Bits
BitsScope = #scope BitsScope = #scope
@@ -533,6 +651,7 @@ global_env() ->
{"is_contract", Fun1(Address, Bool)}, {"is_contract", Fun1(Address, Bool)},
{"is_payable", Fun1(Address, Bool)}]) }, {"is_payable", Fun1(Address, Bool)}]) },
#env{ scopes = #env{ scopes =
#{ [] => TopScope #{ [] => TopScope
, ["Chain"] => ChainScope , ["Chain"] => ChainScope
@@ -543,12 +662,19 @@ global_env() ->
, ["Map"] => MapScope , ["Map"] => MapScope
, ["Auth"] => AuthScope , ["Auth"] => AuthScope
, ["Crypto"] => CryptoScope , ["Crypto"] => CryptoScope
, ["String"] => StringScope , ["MCL_BLS12_381"] => MCL_BLS12_381_Scope
, ["StringInternal"] => StringScope
, ["Char"] => CharScope
, ["Bits"] => BitsScope , ["Bits"] => BitsScope
, ["Bytes"] => BytesScope , ["Bytes"] => BytesScope
, ["Int"] => IntScope , ["Int"] => IntScope
, ["Address"] => AddressScope , ["Address"] => AddressScope
} }. }
, fields =
maps:from_list([{N, [#field_info{ ann = [], field_t = T, record_t = Tx, kind = record }]}
|| {N, T} <- TxFlds ])
}.
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}. option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}. map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
@@ -557,7 +683,7 @@ map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
infer(Contracts) -> infer(Contracts) ->
infer(Contracts, []). infer(Contracts, []).
-type option() :: return_env | dont_unfold | no_code | term(). -type option() :: return_env | dont_unfold | no_code | debug_mode | term().
-spec init_env(list(option())) -> env(). -spec init_env(list(option())) -> env().
init_env(_Options) -> global_env(). init_env(_Options) -> global_env().
@@ -622,16 +748,20 @@ check_scope_name_clash(Env, Kind, Name) ->
-spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> -spec infer_contract_top(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) ->
{env(), [aeso_syntax:decl()]}. {env(), [aeso_syntax:decl()]}.
infer_contract_top(Env, Kind, Defs0, _Options) -> infer_contract_top(Env, Kind, Defs0, Options) ->
Defs = desugar(Defs0), Defs = desugar(Defs0),
infer_contract(Env, Kind, Defs). infer_contract(Env, Kind, Defs, Options).
%% infer_contract takes a proplist mapping global names to types, and %% infer_contract takes a proplist mapping global names to types, and
%% a list of definitions. %% a list of definitions.
-spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}. -spec infer_contract(env(), main_contract | contract | namespace, [aeso_syntax:decl()], list(option())) -> {env(), [aeso_syntax:decl()]}.
infer_contract(Env0, What, Defs0) -> infer_contract(Env0, What, Defs0, Options) ->
create_type_errors(), create_type_errors(),
Defs = process_blocks(Defs0), Defs01 = process_blocks(Defs0),
Defs = case lists:member(debug_mode, Options) of
true -> expose_internals(Defs01, What);
false -> Defs01
end,
destroy_and_report_type_errors(Env0), destroy_and_report_type_errors(Env0),
Env = Env0#env{ what = What }, Env = Env0#env{ what = What },
Kind = fun({type_def, _, _, _, _}) -> type; Kind = fun({type_def, _, _, _, _}) -> type;
@@ -679,7 +809,7 @@ process_blocks(Decls) ->
-spec process_block(aeso_syntax:ann(), [aeso_syntax:decl()]) -> [aeso_syntax:decl()]. -spec process_block(aeso_syntax:ann(), [aeso_syntax:decl()]) -> [aeso_syntax:decl()].
process_block(_, []) -> []; process_block(_, []) -> [];
process_block(_, [Decl]) -> [Decl]; process_block(_, [Decl]) -> [Decl];
process_block(Ann, [Decl | Decls]) -> process_block(_Ann, [Decl | Decls]) ->
IsThis = fun(Name) -> fun({letfun, _, {id, _, Name1}, _, _, _}) -> Name == Name1; IsThis = fun(Name) -> fun({letfun, _, {id, _, Name1}, _, _, _}) -> Name == Name1;
(_) -> false end end, (_) -> false end end,
case Decl of case Decl of
@@ -693,6 +823,20 @@ process_block(Ann, [Decl | Decls]) ->
[{fun_clauses, Ann1, Id, {id, [{origin, system} | Ann1], "_"}, Clauses}] [{fun_clauses, Ann1, Id, {id, [{origin, system} | Ann1], "_"}, Clauses}]
end. end.
%% Turns private stuff into public stuff
expose_internals(Defs, What) ->
[ begin
Ann = element(2, Def),
NewAnn = case What of
namespace -> [A ||A <- Ann, A /= {private, true}, A /= private];
main_contract -> [{entrypoint, true}|Ann]; % minor duplication
contract -> Ann
end,
setelement(2, Def, NewAnn)
end
|| Def <- Defs
].
-spec check_typedefs(env(), [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}. -spec check_typedefs(env(), [aeso_syntax:decl()]) -> {env(), [aeso_syntax:decl()]}.
check_typedefs(Env = #env{ namespace = Ns }, Defs) -> check_typedefs(Env = #env{ namespace = Ns }, Defs) ->
create_type_errors(), create_type_errors(),
@@ -1256,7 +1400,7 @@ infer_expr(Env, {typed, As, Body, Type}) ->
Type1 = check_type(Env, Type), Type1 = check_type(Env, Type),
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1), {typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
{typed, As, NewBody, NewType}; {typed, As, NewBody, NewType};
infer_expr(Env, {app, Ann, Fun, Args0}) -> infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ], NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ],
Args = Args0 -- NamedArgs, Args = Args0 -- NamedArgs,
case aeso_syntax:get_ann(format, Ann) of case aeso_syntax:get_ann(format, Ann) of
@@ -1271,8 +1415,16 @@ infer_expr(Env, {app, Ann, Fun, Args0}) ->
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun), NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
NewArgs = [infer_expr(Env, A) || A <- Args], NewArgs = [infer_expr(Env, A) || A <- Args],
ArgTypes = [T || {typed, _, _, T} <- NewArgs], ArgTypes = [T || {typed, _, _, T} <- NewArgs],
GeneralResultType = fresh_uvar(Ann),
ResultType = fresh_uvar(Ann), ResultType = fresh_uvar(Ann),
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, ResultType}, {infer_app, Fun, Args, FunType, ArgTypes}), When = {infer_app, Fun, NamedArgs1, Args, FunType, ArgTypes},
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
add_named_argument_constraint(
#dependent_type_constraint{ named_args_t = NamedArgsVar,
named_args = NamedArgs1,
general_type = GeneralResultType,
specialized_type = ResultType,
context = {check_return, App} }),
{typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)} {typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
end; end;
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) -> infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
@@ -1431,7 +1583,7 @@ infer_op(Env, As, Op, Args, InferOp) ->
TypedArgs = [infer_expr(Env, A) || A <- Args], TypedArgs = [infer_expr(Env, A) || A <- Args],
ArgTypes = [T || {typed, _, _, T} <- TypedArgs], ArgTypes = [T || {typed, _, _, T} <- TypedArgs],
Inferred = {fun_t, _, _, OperandTypes, ResultType} = InferOp(Op), Inferred = {fun_t, _, _, OperandTypes, ResultType} = InferOp(Op),
unify(Env, ArgTypes, OperandTypes, {infer_app, Op, Args, Inferred, ArgTypes}), unify(Env, ArgTypes, OperandTypes, {infer_app, Op, [], Args, Inferred, ArgTypes}),
{typed, As, {app, As, Op, TypedArgs}, ResultType}. {typed, As, {app, As, Op, TypedArgs}, ResultType}.
infer_pattern(Env, Pattern) -> infer_pattern(Env, Pattern) ->
@@ -1602,12 +1754,12 @@ create_constraints() ->
create_field_constraints(). create_field_constraints().
destroy_and_report_unsolved_constraints(Env) -> destroy_and_report_unsolved_constraints(Env) ->
solve_field_constraints(Env),
solve_named_argument_constraints(Env), solve_named_argument_constraints(Env),
solve_bytes_constraints(Env), solve_bytes_constraints(Env),
solve_field_constraints(Env),
destroy_and_report_unsolved_field_constraints(Env),
destroy_and_report_unsolved_bytes_constraints(Env), destroy_and_report_unsolved_bytes_constraints(Env),
destroy_and_report_unsolved_named_argument_constraints(Env). destroy_and_report_unsolved_named_argument_constraints(Env),
destroy_and_report_unsolved_field_constraints(Env).
%% -- Named argument constraints -- %% -- Named argument constraints --
@@ -1647,8 +1799,43 @@ check_named_argument_constraint(Env,
type_error({bad_named_argument, Args, Id}), type_error({bad_named_argument, Args, Id}),
false; false;
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true [T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
end;
check_named_argument_constraint(Env,
#dependent_type_constraint{ named_args_t = NamedArgsT0,
named_args = NamedArgs,
general_type = GenType,
specialized_type = SpecType,
context = {check_return, App} }) ->
NamedArgsT = dereference(NamedArgsT0),
case dereference(NamedArgsT0) of
[_ | _] = NamedArgsT ->
GetVal = fun(Name, Default) ->
hd([ Val || {named_arg, _, {id, _, N}, Val} <- NamedArgs, N == Name] ++
[ Default ])
end,
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
GenType1 = specialize_dependent_type(ArgEnv, GenType),
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}),
true;
_ -> unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}), true
end. end.
specialize_dependent_type(Env, Type) ->
case dereference(Type) of
{if_t, _, {id, _, Arg}, Then, Else} ->
Val = maps:get(Arg, Env),
case Val of
{typed, _, {bool, _, true}, _} -> Then;
{typed, _, {bool, _, false}, _} -> Else;
_ ->
type_error({named_argument_must_be_literal_bool, Arg, Val}),
fresh_uvar(aeso_syntax:get_ann(Val))
end;
_ -> Type %% Currently no deep dependent types
end.
destroy_and_report_unsolved_named_argument_constraints(Env) -> destroy_and_report_unsolved_named_argument_constraints(Env) ->
Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()), Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()),
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ], [ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
@@ -1874,9 +2061,11 @@ destroy_and_report_unsolved_field_constraints(Env) ->
{FieldCs, OtherCs} = {FieldCs, OtherCs} =
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end, lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
get_field_constraints()), get_field_constraints()),
{CreateCs, ContractCs} = {CreateCs, OtherCs1} =
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end, lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
OtherCs), OtherCs),
{ContractCs, []} =
lists:partition(fun(#is_contract_constraint{}) -> true; (_) -> false end, OtherCs1),
Unknown = solve_known_record_types(Env, FieldCs), Unknown = solve_known_record_types(Env, FieldCs),
if Unknown == [] -> ok; if Unknown == [] -> ok;
true -> true ->
@@ -1950,7 +2139,8 @@ unfold_record_types(Env, T) ->
unfold_types(Env, T, [unfold_record_types]). unfold_types(Env, T, [unfold_record_types]).
unfold_types(Env, {typed, Attr, E, Type}, Options) -> unfold_types(Env, {typed, Attr, E, Type}, Options) ->
{typed, Attr, unfold_types(Env, E, Options), unfold_types_in_type(Env, Type, Options)}; Options1 = [{ann, Attr} | lists:keydelete(ann, 1, Options)],
{typed, Attr, unfold_types(Env, E, Options), unfold_types_in_type(Env, Type, Options1)};
unfold_types(Env, {arg, Attr, Id, Type}, Options) -> unfold_types(Env, {arg, Attr, Id, Type}, Options) ->
{arg, Attr, Id, unfold_types_in_type(Env, Type, Options)}; {arg, Attr, Id, unfold_types_in_type(Env, Type, Options)};
unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, Options) -> unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, Options) ->
@@ -1976,7 +2166,8 @@ unfold_types_in_type(Env, T) ->
unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) -> unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) ->
Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, Options), Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, Options),
[ type_error({map_in_map_key, KeyType0}) || has_maps(KeyType) ], Ann1 = proplists:get_value(ann, Options, aeso_syntax:get_ann(KeyType0)),
[ type_error({map_in_map_key, Ann1, KeyType0}) || has_maps(KeyType) ],
{app_t, Ann, Id, Args1}; {app_t, Ann, Id, Args1};
unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) -> unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) ->
UnfoldRecords = proplists:get_value(unfold_record_types, Options, false), UnfoldRecords = proplists:get_value(unfold_record_types, Options, false),
@@ -2050,8 +2241,13 @@ subst_tvars1(_Env, X) ->
unify(_, {id, _, "_"}, _, _When) -> true; unify(_, {id, _, "_"}, _, _When) -> true;
unify(_, _, {id, _, "_"}, _When) -> true; unify(_, _, {id, _, "_"}, _When) -> true;
unify(Env, A, B, When) -> unify(Env, A, B, When) ->
A1 = dereference(unfold_types_in_type(Env, A)), Options =
B1 = dereference(unfold_types_in_type(Env, B)), case When of %% Improve source location for map_in_map_key errors
{check_expr, E, _, _} -> [{ann, aeso_syntax:get_ann(E)}];
_ -> []
end,
A1 = dereference(unfold_types_in_type(Env, A, Options)),
B1 = dereference(unfold_types_in_type(Env, B, Options)),
unify1(Env, A1, B1, When). unify1(Env, A1, B1, When).
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) -> unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) ->
@@ -2082,6 +2278,9 @@ unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
true; true;
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) -> unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
true; true;
unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, Else2}, When) ->
unify(Env, Then1, Then2, When) andalso
unify(Env, Else1, Else2, When);
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When) unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When)
when length(Args1) == length(Args2) -> when length(Args1) == length(Args2) ->
unify(Env, Named1, Named2, When) andalso unify(Env, Named1, Named2, When) andalso
@@ -2143,6 +2342,8 @@ occurs_check1(R, {record_t, Fields}) ->
occurs_check(R, Fields); occurs_check(R, Fields);
occurs_check1(R, {field_t, _, _, T}) -> occurs_check1(R, {field_t, _, _, T}) ->
occurs_check(R, T); occurs_check(R, T);
occurs_check1(R, {if_t, _, _, Then, Else}) ->
occurs_check(R, [Then, Else]);
occurs_check1(R, [H | T]) -> occurs_check1(R, [H | T]) ->
occurs_check(R, H) orelse occurs_check(R, T); occurs_check(R, H) orelse occurs_check(R, T);
occurs_check1(_, []) -> false. occurs_check1(_, []) -> false.
@@ -2510,10 +2711,10 @@ mk_error({new_tuple_syntax, Ann, Ts}) ->
Msg = io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n", Msg = io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n",
[pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]), [pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]),
mk_t_err(pos(Ann), Msg); mk_t_err(pos(Ann), Msg);
mk_error({map_in_map_key, KeyType}) -> mk_error({map_in_map_key, Ann, KeyType}) ->
Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]), Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]),
Cxt = "Map keys cannot contain other maps.\n", Cxt = "Map keys cannot contain other maps.\n",
mk_t_err(pos(KeyType), Msg, Cxt); mk_t_err(pos(Ann), Msg, Cxt);
mk_error({cannot_call_init_function, Ann}) -> mk_error({cannot_call_init_function, Ann}) ->
Msg = "The 'init' function is called exclusively by the create contract transaction\n" Msg = "The 'init' function is called exclusively by the create contract transaction\n"
"and cannot be called from the contract code.\n", "and cannot be called from the contract code.\n",
@@ -2563,6 +2764,9 @@ mk_error({mixed_record_and_map, Expr}) ->
Msg = io_lib:format("Mixed record fields and map keys in\n~s", Msg = io_lib:format("Mixed record fields and map keys in\n~s",
[pp_expr(" ", Expr)]), [pp_expr(" ", Expr)]),
mk_t_err(pos(Expr), Msg); mk_t_err(pos(Expr), Msg);
mk_error({named_argument_must_be_literal_bool, Name, Arg}) ->
Msg = io_lib:format("Invalid '~s' argument\n~s\nIt must be either 'true' or 'false'.", [Name, pp_expr(" ", instantiate(Arg))]),
mk_t_err(pos(Arg), Msg);
mk_error(Err) -> mk_error(Err) ->
Msg = io_lib:format("Unknown error: ~p\n", [Err]), Msg = io_lib:format("Unknown error: ~p\n", [Err]),
mk_t_err(pos(0, 0), Msg). mk_t_err(pos(0, 0), Msg).
@@ -2581,7 +2785,7 @@ pp_when({check_typesig, Name, Inferred, Given}) ->
" inferred type: ~s\n" " inferred type: ~s\n"
" given type: ~s\n", " given type: ~s\n",
[Name, pp_loc(Given), pp(instantiate(Inferred)), pp(instantiate(Given))])}; [Name, pp_loc(Given), pp(instantiate(Inferred)), pp(instantiate(Given))])};
pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) -> pp_when({infer_app, Fun, NamedArgs, Args, Inferred0, ArgTypes0}) ->
Inferred = instantiate(Inferred0), Inferred = instantiate(Inferred0),
ArgTypes = instantiate(ArgTypes0), ArgTypes = instantiate(ArgTypes0),
{pos(Fun), {pos(Fun),
@@ -2590,6 +2794,7 @@ pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) ->
"to arguments\n~s", "to arguments\n~s",
[pp_loc(Fun), [pp_loc(Fun),
pp_typed(" ", Fun, Inferred), pp_typed(" ", Fun, Inferred),
[ [pp_expr(" ", NamedArg), "\n"] || NamedArg <- NamedArgs ] ++
[ [pp_typed(" ", Arg, ArgT), "\n"] [ [pp_typed(" ", Arg, ArgT), "\n"]
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])}; || {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
pp_when({field_constraint, FieldType0, InferredType0, Fld}) -> pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
@@ -2666,6 +2871,12 @@ pp_when({list_comp, BindExpr, Inferred0, Expected0}) ->
io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n" io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n"
"against type \n~s\n", "against type \n~s\n",
[pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)])}; [pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)])};
pp_when({check_named_arg_constraint, C}) ->
{id, _, Name} = Arg = C#named_argument_constraint.name,
[Type | _] = [ Type || {named_arg_t, _, {id, _, Name1}, Type, _} <- C#named_argument_constraint.args, Name1 == Name ],
Err = io_lib:format("when checking named argument\n~s\nagainst inferred type\n~s",
[pp_typed(" ", Arg, Type), pp_type(" ", C#named_argument_constraint.type)]),
{pos(Arg), Err};
pp_when(unknown) -> {pos(0,0), ""}. pp_when(unknown) -> {pos(0,0), ""}.
-spec pp_why_record(why_record()) -> {pos(), iolist()}. -spec pp_why_record(why_record()) -> {pos(), iolist()}.
@@ -2743,6 +2954,8 @@ pp({uvar, _, Ref}) ->
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ]; ["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
pp({tvar, _, Name}) -> pp({tvar, _, Name}) ->
Name; Name;
pp({if_t, _, Id, Then, Else}) ->
["if(", pp([Id, Then, Else]), ")"];
pp({tuple_t, _, []}) -> pp({tuple_t, _, []}) ->
"unit"; "unit";
pp({tuple_t, _, Cpts}) -> pp({tuple_t, _, Cpts}) ->
@@ -2754,8 +2967,8 @@ pp({app_t, _, T, []}) ->
pp(T); pp(T);
pp({app_t, _, Type, Args}) -> pp({app_t, _, Type, Args}) ->
[pp(Type), "(", pp(Args), ")"]; [pp(Type), "(", pp(Args), ")"];
pp({named_arg_t, _, Name, Type, Default}) -> pp({named_arg_t, _, Name, Type, _Default}) ->
[pp(Name), " : ", pp(Type), " = ", pp(Default)]; [pp(Name), " : ", pp(Type)];
pp({fun_t, _, Named = {uvar, _, _}, As, B}) -> pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
["(", pp(Named), " | ", pp(As), ") => ", pp(B)]; ["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
pp({fun_t, _, Named, As, B}) when is_list(Named) -> pp({fun_t, _, Named, As, B}) when is_list(Named) ->
+91 -24
View File
@@ -36,7 +36,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()}
@@ -171,13 +178,43 @@ ast_to_fcode(Code, Options) ->
-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(),
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 => #{} }.
@@ -194,18 +231,25 @@ builtins() ->
{["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},
{"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,20 +268,32 @@ 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() ->
#{ ["int"] => ?type(integer), BaseTx = {variant, [[address, integer, string], [], [], [], [], [], [string],
["bool"] => ?type(boolean), [hash], [hash], [address, hash], [address],
["bits"] => ?type(bits), [address, integer], [address, integer], [address],
["char"] => ?type(integer), [address], [address], [address], [address], [address],
["string"] => ?type(string), [integer], [address, integer], []]},
["address"] => ?type(address), #{ ["int"] => ?type(integer),
["hash"] => ?type(hash), ["bool"] => ?type(boolean),
["signature"] => ?type(signature), ["bits"] => ?type(bits),
["oracle"] => ?type(Q, R, {oracle, Q, R}), ["char"] => ?type(integer),
["oracle_query"] => ?type(_, _, oracle_query), ["string"] => ?type(string),
["list"] => ?type(T, {list, T}), ["address"] => ?type(address),
["map"] => ?type(K, V, {map, K, V}), ["hash"] => ?type(hash),
["option"] => ?type(T, {variant, [[], [T]]}), ["signature"] => ?type(signature),
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}) ["oracle"] => ?type(Q, R, {oracle, Q, R}),
["oracle_query"] => ?type(_, _, oracle_query), %% TODO: not in Fate
["list"] => ?type(T, {list, T}),
["map"] => ?type(K, V, {map, K, V}),
["option"] => ?type(T, {variant, [[], [T]]}),
["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) ->
@@ -411,6 +467,8 @@ 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}).
@@ -985,12 +1043,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 +1218,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) ->
+13 -1
View File
@@ -469,6 +469,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 +625,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 +934,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)}.
+2 -1
View File
@@ -38,7 +38,8 @@
| pp_assembler | pp_assembler
| pp_bytecode | pp_bytecode
| no_code | no_code
| keep_included | keep_included
| debug_mode
| {backend, aevm | fate} | {backend, aevm | fate}
| {include, {file_system, [string()]} | | {include, {file_system, [string()]} |
{explicit_files, #{string() => binary()}}} {explicit_files, #{string() => binary()}}}
+98 -15
View File
@@ -302,18 +302,27 @@ 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 Gas of case Protected of
{builtin, call_gas_left, _} -> {lit, {bool, false}} ->
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a), case Gas of
call_to_scode(Env, Call, [Ct, Value | Args]); {builtin, call_gas_left, _} ->
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
call_to_scode(Env, Call, [Ct, Value | Args]);
_ ->
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
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_gr(?a, Lbl, ArgType, RetType, ?a, ?a), Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
call_to_scode(Env, Call, [Ct, Value, Gas | Args]) call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
end; end;
to_scode1(_Env, {get_state, Reg}) -> to_scode1(_Env, {get_state, Reg}) ->
@@ -498,6 +507,8 @@ 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 +547,15 @@ 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)].
%% -- Operators -- %% -- Operators --
@@ -564,8 +582,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 +608,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 +782,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 +864,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]);
@@ -853,12 +903,44 @@ 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]);
{'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 +1693,7 @@ 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) == '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) ->
+24 -20
View File
@@ -73,28 +73,33 @@ new(Options) ->
builtin_types() -> builtin_types() ->
Word = fun([]) -> word end, Word = fun([]) -> word end,
#{ "bool" => Word #{ "bool" => Word
, "int" => Word , "int" => Word
, "char" => Word , "char" => Word
, "bits" => Word , "bits" => Word
, "string" => fun([]) -> string end , "string" => fun([]) -> string end
, "address" => Word , "address" => Word
, "hash" => Word , "hash" => Word
, "unit" => fun([]) -> {tuple, []} end , "unit" => fun([]) -> {tuple, []} end
, "signature" => fun([]) -> {tuple, [word, word]} end , "signature" => fun([]) -> {tuple, [word, word]} end
, "oracle" => fun([_, _]) -> word end , "oracle" => fun([_, _]) -> word end
, "oracle_query" => fun([_, _]) -> word end , "oracle_query" => fun([_, _]) -> word end
, "list" => fun([A]) -> {list, A} end , "list" => fun([A]) -> {list, A} end
, "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.
+13 -9
View File
@@ -264,6 +264,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 +292,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 +376,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 +454,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));
+32 -26
View File
@@ -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())},
@@ -77,34 +83,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 +121,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).
+6 -3
View File
@@ -83,8 +83,8 @@ test_cases(3) ->
DecACI = <<"contract C =\n" DecACI = <<"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"
" entrypoint a : (C.bert(string)) => int\n">>, " entrypoint a : (C.bert(string)) => int\n">>,
{Contract,MapACI,DecACI}. {Contract,MapACI,DecACI}.
%% Roundtrip %% Roundtrip
@@ -97,7 +97,10 @@ all_contracts() -> aeso_compiler_tests:compilable_contracts().
aci_test_contract(Name) -> aci_test_contract(Name) ->
String = aeso_test_utils:read_contract(Name), String = aeso_test_utils:read_contract(Name),
Opts = [{include, {file_system, [aeso_test_utils:contract_path()]}}], Opts = case lists:member(Name, aeso_compiler_tests:debug_mode_contracts()) of
true -> [debug_mode];
false -> []
end ++ [{include, {file_system, [aeso_test_utils:contract_path()]}}],
{ok, JSON} = aeso_aci:contract_interface(json, String, Opts), {ok, JSON} = aeso_aci:contract_interface(json, String, Opts),
{ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]), {ok, #{aci := JSON1}} = aeso_compiler:from_string(String, [{aci, json}, {backend, fate} | Opts]),
?assertEqual(JSON, JSON1), ?assertEqual(JSON, JSON1),
+1 -1
View File
@@ -143,4 +143,4 @@ compilable_contracts() ->
not_yet_compilable(fate) -> not_yet_compilable(fate) ->
[]; [];
not_yet_compilable(aevm) -> not_yet_compilable(aevm) ->
[]. ["funargs", "strings"].
+46 -11
View File
@@ -110,7 +110,15 @@ compile(Backend, Name) ->
compile(Backend, Name, Options) -> compile(Backend, Name, Options) ->
String = aeso_test_utils:read_contract(Name), String = aeso_test_utils:read_contract(Name),
case aeso_compiler:from_string(String, [{src_file, Name ++ ".aes"}, {backend, Backend} | Options]) of Options1 =
case lists:member(Name, debug_mode_contracts()) of
true -> [debug_mode];
false -> []
end ++
[ {src_file, Name ++ ".aes"}, {backend, Backend}
, {include, {file_system, [aeso_test_utils:contract_path()]}}
] ++ Options,
case aeso_compiler:from_string(String, Options1) of
{ok, Map} -> Map; {ok, Map} -> Map;
{error, ErrorString} when is_binary(ErrorString) -> ErrorString; {error, ErrorString} when is_binary(ErrorString) -> ErrorString;
{error, Errors} -> Errors {error, Errors} -> Errors
@@ -146,6 +154,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",
@@ -154,6 +163,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",
@@ -163,17 +173,27 @@ 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",
"more_strings",
"protected_call",
"hermetization_turnoff"
]. ].
not_compilable_on(fate) -> []; not_compilable_on(fate) -> [];
not_compilable_on(aevm) -> not_compilable_on(aevm) ->
["stdlib_include", [ "stdlib_include", "manual_stdlib_include", "pairing_crypto"
"manual_stdlib_include" , "aens_update", "basic_auth_tx", "more_strings"
, "unapplied_builtins", "bytes_to_x", "state_handling", "protected_call"
, "hermetization_turnoff"
]. ].
debug_mode_contracts() ->
["hermetization_turnoff"].
%% Contracts that should produce type errors %% Contracts that should produce type errors
-define(Pos(Kind, File, Line, Col), (list_to_binary(Kind))/binary, " error in '", -define(Pos(Kind, File, Line, Col), (list_to_binary(Kind))/binary, " error in '",
@@ -374,10 +394,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.">>])
@@ -542,11 +562,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.">>])
@@ -668,6 +692,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">>,
@@ -853,6 +883,11 @@ validate(Contract1, Contract2) ->
ByteCode = #{ fate_code := FCode } = compile(fate, Contract1), ByteCode = #{ fate_code := FCode } = compile(fate, Contract1),
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(ByteCode#{ byte_code := FCode1 }, Source, aeso_compiler:validate_byte_code(
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]). ByteCode#{ byte_code := FCode1 }, Source,
case lists:member(Contract2, debug_mode_contracts()) of
true -> [debug_mode];
false -> []
end ++
[{backend, fate}, {include, {file_system, [aeso_test_utils:contract_path()]}}]).
+3 -2
View File
@@ -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)).
+16 -1
View File
@@ -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,
+17
View File
@@ -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)
+5 -1
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
contract Remote =
entrypoint id : int => int
contract ProtectedCall =
entrypoint bad(r : Remote) =
r.id(protected = 0 == 1, 18)
+74
View File
@@ -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 -1
View File
@@ -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)
@@ -17,7 +17,7 @@ contract ChannelOnChainContractOracle =
bets = {} bets = {}
} }
public stateful function place_bet(answer: string) = public stateful function place_bet(answer: string) =
switch(Map.lookup(answer, state.bets)) switch(Map.lookup(answer, state.bets))
None => None =>
put(state{ bets = state.bets{[answer] = Call.caller}}) put(state{ bets = state.bets{[answer] = Call.caller}})
@@ -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)
@@ -35,7 +38,7 @@ contract ChannelOnChainContractOracle =
switch(Oracle.get_answer(state.oracle, q)) switch(Oracle.get_answer(state.oracle, q))
None => None =>
"no response" "no response"
Some(result) => Some(result) =>
if(state.question == Oracle.get_question(state.oracle, q)) if(state.question == Oracle.get_question(state.oracle, q))
switch(Map.lookup(result, state.bets)) switch(Map.lookup(result, state.bets))
None => None =>
+1 -1
View File
@@ -1,4 +1,4 @@
include "String.aes"
contract FunctionArguments = contract FunctionArguments =
entrypoint sum(n : int, m: int) = entrypoint sum(n : int, m: int) =
+11
View File
@@ -0,0 +1,11 @@
namespace M =
function mf() = mg()
function mg() = mf()
namespace N =
function nf() = ng() + M.mf() + M.mg()
private function ng() = nf() + M.mf() + M.mg()
contract C =
entrypoint f() = N.ng() + N.nf() + g()
function g() = N.ng() + N.nf() + f()
+14
View File
@@ -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)
+30
View File
@@ -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)
+14
View File
@@ -0,0 +1,14 @@
contract 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
View File
@@ -1,3 +1,4 @@
include "String.aes"
contract Remote = contract Remote =
record rstate = { i : int, s : string, m : map(int, int) } record rstate = { i : int, s : string, m : map(int, int) }
+1
View File
@@ -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)
+2
View File
@@ -7,6 +7,7 @@
// AENS.transfer // AENS.transfer
// AENS.revoke // AENS.revoke
// Oracle.extend // Oracle.extend
include "String.aes"
contract UnappliedBuiltins = contract UnappliedBuiltins =
entrypoint main() = () entrypoint main() = ()
type o = oracle(int, int) type o = oracle(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, _) => _
+21
View File
@@ -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)