Expose interface fix (lima) #778
@ -8,6 +8,15 @@ executors:
|
||||
working_directory: ~/aesophia
|
||||
|
||||
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:
|
||||
executor: aebuilder
|
||||
steps:
|
||||
@ -35,3 +44,10 @@ jobs:
|
||||
- _build/default/rebar3_20.3.8_plt
|
||||
- store_artifacts:
|
||||
path: _build/test/logs
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_test:
|
||||
jobs:
|
||||
- build
|
||||
- verify_rebar_lock
|
||||
|
100
docs/sophia.md
100
docs/sophia.md
@ -2,9 +2,11 @@
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
- [-](#-)
|
||||
- [Language Features](#language-features)
|
||||
- [Contracts](#contracts)
|
||||
- [Calling other contracts](#calling-other-contracts)
|
||||
- [Protected contract calls](#protected-contract-calls)
|
||||
- [Mutable state](#mutable-state)
|
||||
- [Stateful functions](#stateful-functions)
|
||||
- [Payable](#payable)
|
||||
@ -26,6 +28,7 @@
|
||||
- [Updating a value](#updating-a-value)
|
||||
- [Map implementation](#map-implementation)
|
||||
- [Strings](#strings)
|
||||
- [Chars](#chars)
|
||||
- [Byte arrays](#byte-arrays)
|
||||
- [Cryptographic builins](#cryptographic-builins)
|
||||
- [AEVM note](#aevm-note)
|
||||
@ -34,6 +37,7 @@
|
||||
- [Example](#example)
|
||||
- [Sanity checks](#sanity-checks)
|
||||
- [AENS interface](#aens-interface)
|
||||
- [Example](#example-1)
|
||||
- [Events](#events)
|
||||
- [Argument order](#argument-order)
|
||||
- [Compiler pragmas](#compiler-pragmas)
|
||||
@ -52,6 +56,7 @@
|
||||
- [Operators types](#operators-types)
|
||||
- [Operator precendences](#operator-precendences)
|
||||
- [Examples](#examples)
|
||||
- [Delegation signature](#delegation-signature)
|
||||
|
||||
|
||||
## The Sophia Language
|
||||
@ -136,6 +141,36 @@ without calling it you can write
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
@ -565,11 +609,10 @@ string`, `String.sha3(s)` and `Crypto.sha3(s)` will give different results on AE
|
||||
### Authorization interface
|
||||
|
||||
When a Generalized account is authorized, the authorization function needs
|
||||
access to the transaction hash for the wrapped transaction. (A `GAMetaTx`
|
||||
wrapping a transaction.) The transaction hash is available in the primitive
|
||||
`Auth.tx_hash`, it is *only* available during authentication if invoked by a
|
||||
normal contract call it returns `None`.
|
||||
|
||||
access to the transaction and the transaction hash for the wrapped transaction. (A `GAMetaTx`
|
||||
wrapping a transaction.) The transaction and the transaction hash is available in the primitive
|
||||
`Auth.tx` and `Auth.tx_hash` respectively, they are *only* available during authentication if invoked by a
|
||||
normal contract call they return `None`.
|
||||
|
||||
### Oracle interface
|
||||
You can attach an oracle to the current contract and you can interact with oracles
|
||||
@ -645,6 +688,43 @@ Contracts can interact with the
|
||||
[Aeternity Naming System](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
||||
For this purpose the [AENS](sophia_stdlib.md#AENS) library was exposed.
|
||||
|
||||
#### Example
|
||||
|
||||
In this example we assume that the name `name` already exists, and is owned by
|
||||
an account with address `addr`. In order to allow a contract `ct` to handle
|
||||
`name` the account holder needs to create a
|
||||
[signature](#delegation-signature) `sig` of `addr | name.hash | ct.address`.
|
||||
|
||||
Armed with this information we can for example write a function that extends
|
||||
the name if it expires within 1000 blocks:
|
||||
```
|
||||
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
|
||||
|
||||
@ -1069,3 +1149,11 @@ contract FundMe =
|
||||
amount = state.contributions[to]})
|
||||
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.).
|
||||
|
File diff suppressed because it is too large
Load Diff
68
priv/stdlib/BLS12_381.aes
Normal file
68
priv/stdlib/BLS12_381.aes
Normal 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)))
|
@ -19,6 +19,16 @@ namespace List =
|
||||
[x] => Some(x)
|
||||
_::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)
|
||||
[] => false
|
||||
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`.
|
||||
*/
|
||||
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
|
||||
, l : list('a)
|
||||
, n : int
|
||||
, acc : list(int)
|
||||
) : 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) =
|
||||
switch(l)
|
||||
@ -68,44 +79,44 @@ namespace List =
|
||||
* `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.
|
||||
*/
|
||||
function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, [])
|
||||
private function from_to_step_(a, b, s, acc) =
|
||||
if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc)
|
||||
function from_to_step(a : int, b : int, s : int) : list(int) =
|
||||
from_to_step_(a, b - (b-a) mod s, s, [])
|
||||
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
|
||||
*/
|
||||
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, [])
|
||||
private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
|
||||
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l)
|
||||
private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||||
switch(l)
|
||||
[] => abort("replace_at overflow")
|
||||
h::t => if (n == 0) reverse(e::acc) ++ t
|
||||
else replace_at_(n-1, e, t, h::acc)
|
||||
h::t => if (n == 0) e::t
|
||||
else h::replace_at_(n-1, e, t)
|
||||
|
||||
/** 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) =
|
||||
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) =
|
||||
if (n == 0) reverse(e::acc) ++ 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)) : list('a) =
|
||||
if (n == 0) e::l
|
||||
else switch(l)
|
||||
[] => 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
|
||||
* 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) =
|
||||
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)
|
||||
[] => reverse(x::acc)
|
||||
[] => [x]
|
||||
h::t =>
|
||||
if(cmp(x, h)) // x < h
|
||||
reverse(acc) ++ (x::l)
|
||||
x::l
|
||||
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)
|
||||
@ -123,49 +134,52 @@ namespace List =
|
||||
f(e)
|
||||
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, [])
|
||||
private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l)
|
||||
[] => reverse(acc)
|
||||
h::t => map_(f, t, f(h)::acc)
|
||||
function map(f : 'a => 'b, l : list('a)) : list('b) = switch(l)
|
||||
[] => []
|
||||
h::t => f(h)::map(f, t)
|
||||
|
||||
/** Effectively composition of `map` and `flatten`
|
||||
*/
|
||||
function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
|
||||
ListInternal.flat_map(f, l)
|
||||
|
||||
function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, [])
|
||||
private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
|
||||
[] => reverse(acc)
|
||||
h::t => filter_(p, t, if(p(h)) h::acc else acc)
|
||||
function filter(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||
[] => []
|
||||
h::t =>
|
||||
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) =
|
||||
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) =
|
||||
if(n == 0) reverse(acc)
|
||||
if(n < 0) abort("Take negative number of elements") else take_(n, l)
|
||||
private function take_(n : int, l : list('a)) : list('a) =
|
||||
if(n == 0) []
|
||||
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) =
|
||||
if(n < 0) abort("Drop negative number of elements")
|
||||
elif (n == 0) l
|
||||
if(n < 0) abort("Drop negative number of elements") else drop_(n, l)
|
||||
private function drop_(n : int, l : list('a)) : list('a) =
|
||||
if (n == 0) l
|
||||
else switch(l)
|
||||
[] => []
|
||||
h::t => drop(n-1, t)
|
||||
h::t => drop_(n-1, t)
|
||||
|
||||
/** Get the longest prefix of a list in which every element
|
||||
* matches predicate `p`
|
||||
*/
|
||||
function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, 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)) take_while_(p, t, h::acc) else reverse(acc)
|
||||
function take_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||
[] => []
|
||||
h::t => if(p(h)) h::take_while(p, t) else []
|
||||
|
||||
/** Drop elements from `l` until `p` holds
|
||||
*/
|
||||
@ -176,18 +190,15 @@ namespace List =
|
||||
/** Splits list into two lists of elements that respectively
|
||||
* match and don't match predicate `p`
|
||||
*/
|
||||
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], [])
|
||||
private function partition_( p : 'a => bool
|
||||
, l : list('a)
|
||||
, acc_t : list('a)
|
||||
, acc_f : list('a)
|
||||
) : (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)
|
||||
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = switch(l)
|
||||
[] => ([], [])
|
||||
h::t =>
|
||||
let (l, r) = partition(p, t)
|
||||
if(p(h)) (h::l, r) else (l, h::r)
|
||||
|
||||
/** Flattens list of lists into a single list
|
||||
*/
|
||||
function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll)
|
||||
function flatten(l : list(list('a))) : list('a) = switch(l)
|
||||
[] => []
|
||||
h::t => h ++ flatten(t)
|
||||
|
||||
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
||||
[] => true
|
||||
@ -203,28 +214,25 @@ namespace List =
|
||||
|
||||
|
||||
/** 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)
|
||||
, l2 : list('b)
|
||||
, acc : list('c)
|
||||
) : list('c) = switch ((l1, l2))
|
||||
(h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc)
|
||||
_ => reverse(acc)
|
||||
(h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2)
|
||||
_ => []
|
||||
|
||||
/** 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 unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], [])
|
||||
private function unzip_( l : list('a * 'b)
|
||||
, acc_l : list('a)
|
||||
, acc_r : list('b)
|
||||
) : (list('a) * list('b)) = switch(l)
|
||||
[] => (reverse(acc_l), reverse(acc_r))
|
||||
(left, right)::t => unzip_(t, left::acc_l, right::acc_r)
|
||||
function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l)
|
||||
[] => ([], [])
|
||||
(h1, h2)::t =>
|
||||
let (t1, t2) = unzip(t)
|
||||
(h1::t1, h2::t2)
|
||||
|
||||
|
||||
// TODO: Improve?
|
||||
@ -235,17 +243,14 @@ namespace List =
|
||||
|
||||
/** Puts `delim` between every two members of the list
|
||||
*/
|
||||
function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, [])
|
||||
private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l)
|
||||
[] => reverse(acc)
|
||||
[e] => reverse(e::acc)
|
||||
h::t => intersperse_(delim, t, delim::h::acc)
|
||||
|
||||
function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l)
|
||||
[] => []
|
||||
[e] => [e]
|
||||
h::t => h::delim::intersperse(delim, t)
|
||||
|
||||
/** Effectively a zip with an infinite sequence of natural numbers
|
||||
*/
|
||||
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)
|
||||
[] => reverse(acc)
|
||||
h::t => enumerate_(t, n + 1, (n, h)::acc)
|
||||
|
||||
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0)
|
||||
private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l)
|
||||
[] => []
|
||||
h::t => (n, h)::enumerate_(t, n + 1)
|
@ -69,20 +69,21 @@ namespace Option =
|
||||
/** Turns list of options into a list of elements that are under `Some`s.
|
||||
* Safe.
|
||||
*/
|
||||
function filter_options(l : list(option('a))) : list('a) = filter_options_(l, [])
|
||||
private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l)
|
||||
[] => List.reverse(acc)
|
||||
None::t => filter_options_(t, acc)
|
||||
Some(x)::t => filter_options_(t, x::acc)
|
||||
function filter_options(l : list(option('a))) : list('a) = switch(l)
|
||||
[] => []
|
||||
None::t => filter_options(t)
|
||||
Some(x)::t => x::filter_options(t)
|
||||
|
||||
/** Just like `filter_options` but requires all elements to be `Some` and returns
|
||||
* None if any of them is not
|
||||
*/
|
||||
function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, [])
|
||||
private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l)
|
||||
[] => Some(List.reverse(acc))
|
||||
None::t => None
|
||||
Some(x)::t => seq_options_(t, x::acc)
|
||||
function seq_options(l : list (option('a))) : option (list('a)) = switch(l)
|
||||
[] => Some([])
|
||||
None::_ => None
|
||||
Some(x)::t => switch(seq_options(t))
|
||||
None => None
|
||||
Some(st) => Some(x::st)
|
||||
|
||||
|
||||
/** Choose `Some` out of two if possible
|
||||
*/
|
||||
@ -95,4 +96,3 @@ namespace Option =
|
||||
[] => None
|
||||
None::t => choose_first(t)
|
||||
Some(x)::_ => Some(x)
|
||||
|
||||
|
117
priv/stdlib/String.aes
Normal file
117
priv/stdlib/String.aes
Normal 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
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
{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"}
|
||||
, {eblake2, "1.0.0"}
|
||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{"1.1.0",
|
||||
[{<<"aebytecode">>,
|
||||
{git,"https://github.com/aeternity/aebytecode.git",
|
||||
{ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}},
|
||||
{ref,"7f0d3090d4dc6c4d5fca7645b0c21eb0e65ad208"}},
|
||||
0},
|
||||
{<<"aeserialization">>,
|
||||
{git,"https://github.com/aeternity/aeserialization.git",
|
||||
|
@ -24,6 +24,7 @@
|
||||
| aeso_syntax:id() | aeso_syntax:qid()
|
||||
| aeso_syntax:con() | aeso_syntax:qcon() %% contracts
|
||||
| aeso_syntax:tvar()
|
||||
| {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected)
|
||||
| uvar().
|
||||
|
||||
-type uvar() :: {uvar, aeso_syntax:ann(), reference()}.
|
||||
@ -47,7 +48,14 @@
|
||||
name :: aeso_syntax:id(),
|
||||
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_t :: utype()
|
||||
@ -236,16 +244,21 @@ bind_fields([], Env) -> Env;
|
||||
bind_fields([{Id, Info} | Rest], 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}) ->
|
||||
Id = fun(X) -> {id, Ann, X} end,
|
||||
Int = Id("int"),
|
||||
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, [], [], 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().
|
||||
bind_contract({contract, Ann, Id, Contents}, Env) ->
|
||||
@ -368,6 +381,7 @@ is_private(Ann) -> proplists:get_value(private, Ann, false).
|
||||
global_env() ->
|
||||
Ann = [{origin, system}],
|
||||
Int = {id, Ann, "int"},
|
||||
Char = {id, Ann, "char"},
|
||||
Bool = {id, Ann, "bool"},
|
||||
String = {id, Ann, "string"},
|
||||
Address = {id, Ann, "address"},
|
||||
@ -393,6 +407,24 @@ global_env() ->
|
||||
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
|
||||
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
|
||||
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,
|
||||
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
|
||||
|
||||
@ -430,8 +462,36 @@ global_env() ->
|
||||
{"timestamp", Int},
|
||||
{"block_height", Int},
|
||||
{"difficulty", Int},
|
||||
{"gas_limit", Int}])
|
||||
, types = MkDefs([{"ttl", 0}]) },
|
||||
{"gas_limit", Int},
|
||||
%% 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
|
||||
{ funs = MkDefs(
|
||||
@ -451,6 +511,7 @@ global_env() ->
|
||||
OracleScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
|
||||
{"expiry", Fun([Oracle(Q, R)], Fee)},
|
||||
{"query_fee", Fun([Oracle(Q, R)], Fee)},
|
||||
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
|
||||
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
|
||||
@ -466,7 +527,18 @@ global_env() ->
|
||||
{"preclaim", SignFun([Address, Hash], Unit)},
|
||||
{"claim", SignFun([Address, String, Int, Int], 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
|
||||
{ funs = MkDefs(
|
||||
@ -489,19 +561,65 @@ global_env() ->
|
||||
{"sha256", 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
|
||||
AuthScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"tx_hash", Option(Hash)}]) },
|
||||
[{"tx_hash", Option(Hash)},
|
||||
{"tx", Option(Tx)} ]) },
|
||||
|
||||
%% Strings
|
||||
StringScope = #scope
|
||||
{ funs = MkDefs(
|
||||
[{"length", Fun1(String, Int)},
|
||||
{"concat", Fun([String, String], String)},
|
||||
{"sha3", Fun1(String, Hash)},
|
||||
{"sha256", Fun1(String, Hash)},
|
||||
{"blake2b", Fun1(String, Hash)}]) },
|
||||
[{"length", Fun1(String, Int)},
|
||||
{"concat", Fun([String, String], String)},
|
||||
{"to_list", Fun1(String, List(Char))},
|
||||
{"from_list", Fun1(List(Char), String)},
|
||||
{"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
|
||||
BitsScope = #scope
|
||||
@ -533,6 +651,7 @@ global_env() ->
|
||||
{"is_contract", Fun1(Address, Bool)},
|
||||
{"is_payable", Fun1(Address, Bool)}]) },
|
||||
|
||||
|
||||
#env{ scopes =
|
||||
#{ [] => TopScope
|
||||
, ["Chain"] => ChainScope
|
||||
@ -543,12 +662,19 @@ global_env() ->
|
||||
, ["Map"] => MapScope
|
||||
, ["Auth"] => AuthScope
|
||||
, ["Crypto"] => CryptoScope
|
||||
, ["String"] => StringScope
|
||||
, ["MCL_BLS12_381"] => MCL_BLS12_381_Scope
|
||||
, ["StringInternal"] => StringScope
|
||||
, ["Char"] => CharScope
|
||||
, ["Bits"] => BitsScope
|
||||
, ["Bytes"] => BytesScope
|
||||
, ["Int"] => IntScope
|
||||
, ["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]}.
|
||||
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
|
||||
@ -706,7 +832,12 @@ expose_internals(Defs, What) ->
|
||||
main_contract -> [{entrypoint, true}|Ann]; % minor duplication
|
||||
contract -> Ann
|
||||
end,
|
||||
setelement(2, Def, NewAnn)
|
||||
Def1 = setelement(2, Def, NewAnn),
|
||||
case Def1 of % fix inner clauses
|
||||
{fun_clauses, Ans, Id, T, Clauses} ->
|
||||
{fun_clauses, Ans, Id, T, expose_internals(Clauses, What)};
|
||||
_ -> Def1
|
||||
end
|
||||
end
|
||||
|| Def <- Defs
|
||||
].
|
||||
@ -1274,7 +1405,7 @@ infer_expr(Env, {typed, As, Body, Type}) ->
|
||||
Type1 = check_type(Env, Type),
|
||||
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
|
||||
{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 ],
|
||||
Args = Args0 -- NamedArgs,
|
||||
case aeso_syntax:get_ann(format, Ann) of
|
||||
@ -1289,8 +1420,16 @@ infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
||||
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
|
||||
NewArgs = [infer_expr(Env, A) || A <- Args],
|
||||
ArgTypes = [T || {typed, _, _, T} <- NewArgs],
|
||||
GeneralResultType = 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)}
|
||||
end;
|
||||
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
|
||||
@ -1449,7 +1588,7 @@ infer_op(Env, As, Op, Args, InferOp) ->
|
||||
TypedArgs = [infer_expr(Env, A) || A <- Args],
|
||||
ArgTypes = [T || {typed, _, _, T} <- TypedArgs],
|
||||
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}.
|
||||
|
||||
infer_pattern(Env, Pattern) ->
|
||||
@ -1620,12 +1759,12 @@ create_constraints() ->
|
||||
create_field_constraints().
|
||||
|
||||
destroy_and_report_unsolved_constraints(Env) ->
|
||||
solve_field_constraints(Env),
|
||||
solve_named_argument_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_named_argument_constraints(Env).
|
||||
destroy_and_report_unsolved_named_argument_constraints(Env),
|
||||
destroy_and_report_unsolved_field_constraints(Env).
|
||||
|
||||
%% -- Named argument constraints --
|
||||
|
||||
@ -1665,8 +1804,43 @@ check_named_argument_constraint(Env,
|
||||
type_error({bad_named_argument, Args, Id}),
|
||||
false;
|
||||
[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.
|
||||
|
||||
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) ->
|
||||
Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()),
|
||||
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
|
||||
@ -1892,9 +2066,11 @@ destroy_and_report_unsolved_field_constraints(Env) ->
|
||||
{FieldCs, OtherCs} =
|
||||
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
||||
get_field_constraints()),
|
||||
{CreateCs, ContractCs} =
|
||||
{CreateCs, OtherCs1} =
|
||||
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
|
||||
OtherCs),
|
||||
{ContractCs, []} =
|
||||
lists:partition(fun(#is_contract_constraint{}) -> true; (_) -> false end, OtherCs1),
|
||||
Unknown = solve_known_record_types(Env, FieldCs),
|
||||
if Unknown == [] -> ok;
|
||||
true ->
|
||||
@ -1968,7 +2144,8 @@ unfold_record_types(Env, T) ->
|
||||
unfold_types(Env, T, [unfold_record_types]).
|
||||
|
||||
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) ->
|
||||
{arg, Attr, Id, unfold_types_in_type(Env, Type, Options)};
|
||||
unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, Options) ->
|
||||
@ -1994,7 +2171,8 @@ unfold_types_in_type(Env, T) ->
|
||||
|
||||
unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, 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};
|
||||
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),
|
||||
@ -2068,8 +2246,13 @@ subst_tvars1(_Env, X) ->
|
||||
unify(_, {id, _, "_"}, _, _When) -> true;
|
||||
unify(_, _, {id, _, "_"}, _When) -> true;
|
||||
unify(Env, A, B, When) ->
|
||||
A1 = dereference(unfold_types_in_type(Env, A)),
|
||||
B1 = dereference(unfold_types_in_type(Env, B)),
|
||||
Options =
|
||||
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, {uvar, _, R}, {uvar, _, R}, _When) ->
|
||||
@ -2100,6 +2283,9 @@ unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
|
||||
true;
|
||||
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
|
||||
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)
|
||||
when length(Args1) == length(Args2) ->
|
||||
unify(Env, Named1, Named2, When) andalso
|
||||
@ -2161,6 +2347,8 @@ occurs_check1(R, {record_t, Fields}) ->
|
||||
occurs_check(R, Fields);
|
||||
occurs_check1(R, {field_t, _, _, T}) ->
|
||||
occurs_check(R, T);
|
||||
occurs_check1(R, {if_t, _, _, Then, Else}) ->
|
||||
occurs_check(R, [Then, Else]);
|
||||
occurs_check1(R, [H | T]) ->
|
||||
occurs_check(R, H) orelse occurs_check(R, T);
|
||||
occurs_check1(_, []) -> false.
|
||||
@ -2528,10 +2716,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",
|
||||
[pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]),
|
||||
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)]),
|
||||
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}) ->
|
||||
Msg = "The 'init' function is called exclusively by the create contract transaction\n"
|
||||
"and cannot be called from the contract code.\n",
|
||||
@ -2581,6 +2769,9 @@ mk_error({mixed_record_and_map, Expr}) ->
|
||||
Msg = io_lib:format("Mixed record fields and map keys in\n~s",
|
||||
[pp_expr(" ", Expr)]),
|
||||
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) ->
|
||||
Msg = io_lib:format("Unknown error: ~p\n", [Err]),
|
||||
mk_t_err(pos(0, 0), Msg).
|
||||
@ -2599,7 +2790,7 @@ pp_when({check_typesig, Name, Inferred, Given}) ->
|
||||
" inferred type: ~s\n"
|
||||
" given type: ~s\n",
|
||||
[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),
|
||||
ArgTypes = instantiate(ArgTypes0),
|
||||
{pos(Fun),
|
||||
@ -2608,6 +2799,7 @@ pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) ->
|
||||
"to arguments\n~s",
|
||||
[pp_loc(Fun),
|
||||
pp_typed(" ", Fun, Inferred),
|
||||
[ [pp_expr(" ", NamedArg), "\n"] || NamedArg <- NamedArgs ] ++
|
||||
[ [pp_typed(" ", Arg, ArgT), "\n"]
|
||||
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
|
||||
pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
|
||||
@ -2684,6 +2876,12 @@ pp_when({list_comp, BindExpr, Inferred0, Expected0}) ->
|
||||
io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n"
|
||||
"against type \n~s\n",
|
||||
[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), ""}.
|
||||
|
||||
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
||||
@ -2761,6 +2959,8 @@ pp({uvar, _, Ref}) ->
|
||||
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
|
||||
pp({tvar, _, Name}) ->
|
||||
Name;
|
||||
pp({if_t, _, Id, Then, Else}) ->
|
||||
["if(", pp([Id, Then, Else]), ")"];
|
||||
pp({tuple_t, _, []}) ->
|
||||
"unit";
|
||||
pp({tuple_t, _, Cpts}) ->
|
||||
@ -2772,8 +2972,8 @@ pp({app_t, _, T, []}) ->
|
||||
pp(T);
|
||||
pp({app_t, _, Type, Args}) ->
|
||||
[pp(Type), "(", pp(Args), ")"];
|
||||
pp({named_arg_t, _, Name, Type, Default}) ->
|
||||
[pp(Name), " : ", pp(Type), " = ", pp(Default)];
|
||||
pp({named_arg_t, _, Name, Type, _Default}) ->
|
||||
[pp(Name), " : ", pp(Type)];
|
||||
pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
|
||||
["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
|
||||
pp({fun_t, _, Named, As, B}) when is_list(Named) ->
|
||||
|
@ -36,7 +36,14 @@
|
||||
bits_intersection | bits_union | bits_difference |
|
||||
contract_to_address | address_to_contract | crypto_verify_sig | 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.
|
||||
|
||||
-type flit() :: {int, integer()}
|
||||
| {string, binary()}
|
||||
@ -171,13 +178,43 @@ ast_to_fcode(Code, Options) ->
|
||||
|
||||
-spec init_env([option()]) -> env().
|
||||
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(),
|
||||
fun_env => #{},
|
||||
builtins => builtins(),
|
||||
con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] },
|
||||
["Some"] => #con_tag{ tag = 1, arities = [0, 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,
|
||||
functions => #{} }.
|
||||
@ -194,18 +231,25 @@ builtins() ->
|
||||
{["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]},
|
||||
{["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none},
|
||||
{"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},
|
||||
{"check", 1}, {"check_query", 2}]},
|
||||
{["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},
|
||||
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
||||
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
||||
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
||||
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
||||
{["Auth"], [{"tx_hash", none}]},
|
||||
{["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
||||
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
||||
{"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},
|
||||
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
||||
{["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().
|
||||
init_type_env() ->
|
||||
#{ ["int"] => ?type(integer),
|
||||
["bool"] => ?type(boolean),
|
||||
["bits"] => ?type(bits),
|
||||
["char"] => ?type(integer),
|
||||
["string"] => ?type(string),
|
||||
["address"] => ?type(address),
|
||||
["hash"] => ?type(hash),
|
||||
["signature"] => ?type(signature),
|
||||
["oracle"] => ?type(Q, R, {oracle, Q, R}),
|
||||
["oracle_query"] => ?type(_, _, oracle_query),
|
||||
["list"] => ?type(T, {list, T}),
|
||||
["map"] => ?type(K, V, {map, K, V}),
|
||||
["option"] => ?type(T, {variant, [[], [T]]}),
|
||||
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]})
|
||||
BaseTx = {variant, [[address, integer, string], [], [], [], [], [], [string],
|
||||
[hash], [hash], [address, hash], [address],
|
||||
[address, integer], [address, integer], [address],
|
||||
[address], [address], [address], [address], [address],
|
||||
[integer], [address, integer], []]},
|
||||
#{ ["int"] => ?type(integer),
|
||||
["bool"] => ?type(boolean),
|
||||
["bits"] => ?type(bits),
|
||||
["char"] => ?type(integer),
|
||||
["string"] => ?type(string),
|
||||
["address"] => ?type(address),
|
||||
["hash"] => ?type(hash),
|
||||
["signature"] => ?type(signature),
|
||||
["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) ->
|
||||
@ -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],
|
||||
FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args],
|
||||
{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) ->
|
||||
error({todo, Type}).
|
||||
|
||||
@ -985,12 +1043,21 @@ stmts_to_fcode(Env, [Expr | Stmts]) ->
|
||||
|
||||
op_builtins() ->
|
||||
[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_difference, int_to_str, address_to_str, crypto_verify_sig,
|
||||
address_to_contract,
|
||||
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) ->
|
||||
@ -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}) ->
|
||||
FVs = free_vars(Ct),
|
||||
Ct1 = lambda_lift_expr(Layout, Ct),
|
||||
GasAndValueArgs = 2,
|
||||
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + GasAndValueArgs) ],
|
||||
NamedArgCount = 3,
|
||||
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + NamedArgCount) ],
|
||||
Args = [{var, X} || X <- Xs],
|
||||
make_closure(FVs, Xs, {remote, ArgsT, RetT, Ct1, F, Args});
|
||||
lambda_lift_expr(Layout, Expr) ->
|
||||
|
@ -469,6 +469,7 @@ is_builtin_fun({qid, _, ["AENS", "preclaim"]}, _Icode) ->
|
||||
is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true;
|
||||
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _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_default"]}, _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)],
|
||||
[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
|
||||
%% -- lookup functions
|
||||
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
|
||||
@ -927,11 +934,16 @@ ast_typerep1({variant_t, Cons}, Icode) ->
|
||||
{variant, [ begin
|
||||
{constr_t, _, _, Args} = Con,
|
||||
[ 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) ->
|
||||
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
||||
|
||||
%% pointee_t(Icode) ->
|
||||
%% ast_typerep({qid, [], ["AENS", "pointee"]}, Icode).
|
||||
|
||||
sign_t() -> bytes_t(64).
|
||||
bytes_t(Len) when Len =< 32 -> word;
|
||||
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
|
||||
|
@ -302,18 +302,27 @@ to_scode1(Env, {funcall, Fun, Args}) ->
|
||||
to_scode1(Env, {builtin, 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),
|
||||
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
||||
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
||||
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
||||
case Gas of
|
||||
{builtin, call_gas_left, _} ->
|
||||
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
||||
call_to_scode(Env, Call, [Ct, Value | Args]);
|
||||
case Protected of
|
||||
{lit, {bool, false}} ->
|
||||
case Gas of
|
||||
{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_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
||||
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
|
||||
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
|
||||
end;
|
||||
|
||||
to_scode1(_Env, {get_state, Reg}) ->
|
||||
@ -498,6 +507,8 @@ builtin_to_scode(_Env, call_gas_left, []) ->
|
||||
[aeb_fate_ops:gas(?a)];
|
||||
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);
|
||||
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) ->
|
||||
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) ->
|
||||
@ -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) ->
|
||||
call_to_scode(Env, [aeb_fate_ops:aens_revoke(?a, ?a, ?a),
|
||||
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, []) ->
|
||||
[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 --
|
||||
|
||||
@ -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_member) -> aeb_fate_ops:map_member(?a, ?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(string_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
|
||||
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?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_clear) -> aeb_fate_ops:bits_clear(?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_sha256) -> aeb_fate_ops:sha256(?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(string_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||
op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a).
|
||||
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?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
|
||||
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
||||
@ -734,6 +782,7 @@ attributes(I) ->
|
||||
{'CALL', A} -> Impure(?a, [A]);
|
||||
{'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_PGR', A, _, B, C, D, E, F} -> Impure(?a, [A, B, C, D, E, F]);
|
||||
{'CALL_T', A} -> Impure(pc, [A]);
|
||||
{'CALL_VALUE', A} -> Pure(A, []);
|
||||
{'JUMP', _} -> Impure(pc, []);
|
||||
@ -815,6 +864,7 @@ attributes(I) ->
|
||||
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
||||
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
||||
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
||||
{'AUTH_TX', A} -> Pure(A, []);
|
||||
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
||||
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
||||
{'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_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]);
|
||||
{'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_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_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);
|
||||
{'EXIT', A} -> Impure(pc, A);
|
||||
'NOP' -> Pure(none, [])
|
||||
@ -1611,6 +1693,7 @@ split_calls(Ref, [], Acc, Blocks) ->
|
||||
split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
|
||||
element(1, I) == 'CALL_R';
|
||||
element(1, I) == 'CALL_GR';
|
||||
element(1, I) == 'CALL_PGR';
|
||||
element(1, I) == 'jumpif' ->
|
||||
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
|
||||
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
|
||||
|
@ -73,28 +73,33 @@ new(Options) ->
|
||||
|
||||
builtin_types() ->
|
||||
Word = fun([]) -> word end,
|
||||
#{ "bool" => Word
|
||||
, "int" => Word
|
||||
, "char" => Word
|
||||
, "bits" => Word
|
||||
, "string" => fun([]) -> string end
|
||||
, "address" => Word
|
||||
, "hash" => Word
|
||||
, "unit" => fun([]) -> {tuple, []} end
|
||||
, "signature" => fun([]) -> {tuple, [word, word]} end
|
||||
, "oracle" => fun([_, _]) -> word end
|
||||
, "oracle_query" => fun([_, _]) -> word end
|
||||
, "list" => fun([A]) -> {list, A} end
|
||||
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
||||
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
||||
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
||||
#{ "bool" => Word
|
||||
, "int" => Word
|
||||
, "char" => Word
|
||||
, "bits" => Word
|
||||
, "string" => fun([]) -> string end
|
||||
, "address" => Word
|
||||
, "hash" => Word
|
||||
, "unit" => fun([]) -> {tuple, []} end
|
||||
, "signature" => fun([]) -> {tuple, [word, word]} end
|
||||
, "oracle" => fun([_, _]) -> word end
|
||||
, "oracle_query" => fun([_, _]) -> word end
|
||||
, "list" => fun([A]) -> {list, A} end
|
||||
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
||||
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
||||
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
||||
, ["AENS", "pointee"] => fun([]) -> {variant, [[word], [word], [word]]} end
|
||||
}.
|
||||
|
||||
builtin_constructors() ->
|
||||
#{ ["RelativeTTL"] => 0
|
||||
, ["FixedTTL"] => 1
|
||||
, ["None"] => 0
|
||||
, ["Some"] => 1 }.
|
||||
#{ ["RelativeTTL"] => 0
|
||||
, ["FixedTTL"] => 1
|
||||
, ["None"] => 0
|
||||
, ["Some"] => 1
|
||||
, ["AccountPointee"] => 0
|
||||
, ["OraclePointee"] => 1
|
||||
, ["ContractPointee"] => 2
|
||||
}.
|
||||
|
||||
map_typerep(K, V) ->
|
||||
{map, K, V}.
|
||||
@ -146,4 +151,3 @@ get_constructor_tag(Name, #{constructors := Constructors}) ->
|
||||
undefined -> error({undefined_constructor, Name});
|
||||
Tag -> Tag
|
||||
end.
|
||||
|
||||
|
@ -264,6 +264,8 @@ type({args_t, _, Args}) ->
|
||||
type({bytes_t, _, any}) -> text("bytes(_)");
|
||||
type({bytes_t, _, 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}) ->
|
||||
%% Drop the default value
|
||||
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
||||
@ -290,12 +292,9 @@ tuple_type(Factors) ->
|
||||
, text(")")
|
||||
]).
|
||||
|
||||
-spec arg_expr(aeso_syntax:arg_expr()) -> doc().
|
||||
arg_expr({named_arg, _, Name, E}) ->
|
||||
follow(hsep(expr(Name), text("=")), expr(E));
|
||||
arg_expr(E) -> expr(E).
|
||||
|
||||
-spec expr_p(integer(), aeso_syntax:expr()) -> doc().
|
||||
-spec expr_p(integer(), aeso_syntax:arg_expr()) -> doc().
|
||||
expr_p(P, {named_arg, _, Name, E}) ->
|
||||
paren(P > 100, follow(hsep(expr(Name), text("=")), expr(E)));
|
||||
expr_p(P, {lam, _, Args, E}) ->
|
||||
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
||||
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
||||
@ -377,8 +376,13 @@ expr_p(_, {char, _, C}) ->
|
||||
case C of
|
||||
$' -> text("'\\''");
|
||||
$" -> text("'\"'");
|
||||
_ -> S = lists:flatten(io_lib:format("~p", [[C]])),
|
||||
text("'" ++ tl(lists:droplast(S)) ++ "'")
|
||||
_ when C < 16#80 ->
|
||||
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;
|
||||
%% -- Names
|
||||
expr_p(_, E = {id, _, _}) -> name(E);
|
||||
@ -450,7 +454,7 @@ prefix(P, Op, A) ->
|
||||
app(P, F, Args) ->
|
||||
paren(P > 900,
|
||||
beside(expr_p(900, F),
|
||||
tuple(lists:map(fun arg_expr/1, Args)))).
|
||||
tuple(lists:map(fun expr/1, Args)))).
|
||||
|
||||
field({field, _, LV, E}) ->
|
||||
follow(hsep(lvalue(LV), text("=")), expr(E));
|
||||
|
@ -7,7 +7,7 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(aeso_scan).
|
||||
|
||||
-export([scan/1]).
|
||||
-export([scan/1, utf8_encode/1]).
|
||||
|
||||
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
||||
override/2, push/2, pop/1]).
|
||||
@ -28,7 +28,13 @@ lexer() ->
|
||||
QID = ["(", CON, "\\.)+", ID],
|
||||
QCON = ["(", CON, "\\.)+", CON],
|
||||
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 = "\"([^\"\\\\]|(\\\\.))*\"",
|
||||
|
||||
CommentStart = {"/\\*", push(comment, skip())},
|
||||
@ -77,34 +83,34 @@ scan(String) ->
|
||||
%% -- Helpers ----------------------------------------------------------------
|
||||
|
||||
parse_string([$" | Chars]) ->
|
||||
unescape(Chars).
|
||||
unicode:characters_to_nfc_binary(unescape(Chars)).
|
||||
|
||||
parse_char([$', $\\, Code, $']) ->
|
||||
case Code of
|
||||
$' -> $';
|
||||
$\\ -> $\\;
|
||||
$b -> $\b;
|
||||
$e -> $\e;
|
||||
$f -> $\f;
|
||||
$n -> $\n;
|
||||
$r -> $\r;
|
||||
$t -> $\t;
|
||||
$v -> $\v;
|
||||
_ -> {error, "Bad control sequence: \\" ++ [Code]}
|
||||
end;
|
||||
parse_char([$', C, $']) -> C.
|
||||
parse_char([$' | Chars]) ->
|
||||
case unicode:characters_to_nfc_list(unescape($', Chars, [])) of
|
||||
[Char] -> Char;
|
||||
_Bad -> {error, "Bad character literal: '" ++ Chars}
|
||||
end.
|
||||
|
||||
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));
|
||||
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),
|
||||
unescape(Chars, [C | Acc]);
|
||||
unescape([$\\, Code | Chars], Acc) ->
|
||||
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
|
||||
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
|
||||
unescape(Delim, Chars, [Utf8Cs | Acc]);
|
||||
unescape(Delim, [$\\, Code | Chars], Acc) ->
|
||||
Ok = fun(C) -> unescape(Delim, Chars, [C | Acc]) end,
|
||||
case Code of
|
||||
$" -> Ok($");
|
||||
Delim -> Ok(Delim);
|
||||
$\\ -> Ok($\\);
|
||||
$b -> Ok($\b);
|
||||
$e -> Ok($\e);
|
||||
@ -115,8 +121,8 @@ unescape([$\\, Code | Chars], Acc) ->
|
||||
$v -> Ok($\v);
|
||||
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
||||
end;
|
||||
unescape([C | Chars], Acc) ->
|
||||
unescape(Chars, [C | Acc]).
|
||||
unescape(Delim, [C | Chars], Acc) ->
|
||||
unescape(Delim, Chars, [C | Acc]).
|
||||
|
||||
strip_underscores(S) ->
|
||||
lists:filter(fun(C) -> C /= $_ end, S).
|
||||
|
@ -143,4 +143,4 @@ compilable_contracts() ->
|
||||
not_yet_compilable(fate) ->
|
||||
[];
|
||||
not_yet_compilable(aevm) ->
|
||||
[].
|
||||
["funargs", "strings"].
|
||||
|
@ -154,6 +154,7 @@ compilable_contracts() ->
|
||||
"events",
|
||||
"include",
|
||||
"basic_auth",
|
||||
"basic_auth_tx",
|
||||
"bitcoin_auth",
|
||||
"address_literals",
|
||||
"bytes_equality",
|
||||
@ -162,6 +163,7 @@ compilable_contracts() ->
|
||||
"bytes_to_x",
|
||||
"bytes_concat",
|
||||
"aens",
|
||||
"aens_update",
|
||||
"tuple_match",
|
||||
"cyclic_include",
|
||||
"stdlib_include",
|
||||
@ -171,17 +173,22 @@ compilable_contracts() ->
|
||||
"payable",
|
||||
"unapplied_builtins",
|
||||
"underscore_number_literals",
|
||||
"pairing_crypto",
|
||||
"qualified_constructor",
|
||||
"let_patterns",
|
||||
"lhs_matching",
|
||||
"more_strings",
|
||||
"protected_call",
|
||||
"hermetization_turnoff"
|
||||
].
|
||||
|
||||
not_compilable_on(fate) -> [];
|
||||
not_compilable_on(aevm) ->
|
||||
["stdlib_include",
|
||||
"manual_stdlib_include",
|
||||
"hermetization_turnoff"
|
||||
[ "stdlib_include", "manual_stdlib_include", "pairing_crypto"
|
||||
, "aens_update", "basic_auth_tx", "more_strings"
|
||||
, "unapplied_builtins", "bytes_to_x", "state_handling", "protected_call"
|
||||
, "hermetization_turnoff"
|
||||
|
||||
].
|
||||
|
||||
debug_mode_contracts() ->
|
||||
@ -387,10 +394,10 @@ failing_contracts() ->
|
||||
[<<?Pos(12, 42)
|
||||
"Cannot unify int\n"
|
||||
" and string\n"
|
||||
"when checking the record projection at line 12, column 42\n"
|
||||
" r.foo : (gas : int, value : int) => Remote.themap\n"
|
||||
"when checking the type of the expression at line 12, column 42\n"
|
||||
" r.foo() : map(int, string)\n"
|
||||
"against the expected type\n"
|
||||
" (gas : int, value : int) => map(string, int)">>])
|
||||
" map(string, int)">>])
|
||||
, ?TYPE_ERROR(not_toplevel_include,
|
||||
[<<?Pos(2, 11)
|
||||
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>])
|
||||
@ -555,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">>
|
||||
])
|
||||
, ?TYPE_ERROR(map_as_map_key,
|
||||
[<<?Pos(5, 25)
|
||||
[<<?Pos(5, 47)
|
||||
"Invalid key type\n"
|
||||
" map(int, int)\n"
|
||||
"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"
|
||||
" lm\n"
|
||||
"Map keys cannot contain other maps.">>])
|
||||
@ -681,6 +692,12 @@ failing_contracts() ->
|
||||
"Empty record/map update\n"
|
||||
" 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,
|
||||
[<<?Pos(4, 5)
|
||||
"Mismatch in the function block. Expected implementation/type declaration of g function">>,
|
||||
|
@ -63,7 +63,8 @@ simple_contracts_test_() ->
|
||||
%% Parse tests of example contracts
|
||||
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
||||
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) ->
|
||||
@ -85,7 +86,7 @@ parse_expr(Text) ->
|
||||
round_trip(Text) ->
|
||||
Contract = parse_string(Text),
|
||||
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),
|
||||
NoSrcLoc1 = remove_line_numbers(Contract1),
|
||||
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
||||
|
@ -33,7 +33,22 @@ contract AENSTest =
|
||||
sign : signature) : unit =
|
||||
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,
|
||||
new_owner : address,
|
||||
|
17
test/contracts/aens_update.aes
Normal file
17
test/contracts/aens_update.aes
Normal 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)
|
||||
|
@ -36,6 +36,8 @@ contract AllSyntax =
|
||||
entrypoint init() = {
|
||||
johann = 1000,
|
||||
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]
|
||||
, let l = k + 1
|
||||
, if(l < 10)
|
||||
@ -43,11 +45,13 @@ contract AllSyntax =
|
||||
, Adam <- [Adam, Mickiewicz]
|
||||
, let x = f(l)
|
||||
])),
|
||||
*/
|
||||
von = (2 + 2, 0, List.sum([1,2,3,4])),
|
||||
goethe = () }
|
||||
|
||||
function f() =
|
||||
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 f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||
|
6
test/contracts/bad_protected_call.aes
Normal file
6
test/contracts/bad_protected_call.aes
Normal file
@ -0,0 +1,6 @@
|
||||
contract Remote =
|
||||
entrypoint id : int => int
|
||||
|
||||
contract ProtectedCall =
|
||||
entrypoint bad(r : Remote) =
|
||||
r.id(protected = 0 == 1, 18)
|
74
test/contracts/basic_auth_tx.aes
Normal file
74
test/contracts/basic_auth_tx.aes
Normal 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,4 +1,4 @@
|
||||
|
||||
include "String.aes"
|
||||
contract BytesToX =
|
||||
|
||||
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
|
||||
|
@ -25,6 +25,9 @@ contract ChannelOnChainContractOracle =
|
||||
Some(_value) =>
|
||||
"bet_already_taken"
|
||||
|
||||
public function expiry() =
|
||||
Oracle.expiry(state.oracle)
|
||||
|
||||
public function query_fee() =
|
||||
Oracle.query_fee(state.oracle)
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
|
||||
include "String.aes"
|
||||
contract FunctionArguments =
|
||||
|
||||
entrypoint sum(n : int, m: int) =
|
||||
|
14
test/contracts/more_strings.aes
Normal file
14
test/contracts/more_strings.aes
Normal 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
test/contracts/pairing_crypto.aes
Normal file
30
test/contracts/pairing_crypto.aes
Normal 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
test/contracts/protected_call.aes
Normal file
14
test/contracts/protected_call.aes
Normal 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,3 +1,4 @@
|
||||
include "String.aes"
|
||||
contract Remote =
|
||||
record rstate = { i : int, s : string, m : map(int, int) }
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
include "String.aes"
|
||||
contract Strings =
|
||||
entrypoint str_len(s) = String.length(s)
|
||||
entrypoint str_concat(s1, s2) = String.concat(s1, s2)
|
||||
|
@ -7,6 +7,7 @@
|
||||
// AENS.transfer
|
||||
// AENS.revoke
|
||||
// Oracle.extend
|
||||
include "String.aes"
|
||||
contract UnappliedBuiltins =
|
||||
entrypoint main() = ()
|
||||
type o = oracle(int, int)
|
||||
@ -21,6 +22,7 @@ contract UnappliedBuiltins =
|
||||
function b_abort() = abort
|
||||
function b_require() = require
|
||||
function oracle_query_fee() = Oracle.query_fee
|
||||
function oracle_expiry() = Oracle.expiry
|
||||
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
||||
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
||||
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
||||
|
21
test/contracts/utf8.aes
Normal file
21
test/contracts/utf8.aes
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user