Expose interface fix (lima) #778
@ -8,6 +8,15 @@ executors:
|
|||||||
working_directory: ~/aesophia
|
working_directory: ~/aesophia
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
verify_rebar_lock:
|
||||||
|
executor: aebuilder
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Ensure lock file is up-to-date
|
||||||
|
command: |
|
||||||
|
./rebar3 upgrade
|
||||||
|
git diff --quiet -- rebar.lock || (echo "rebar.lock is not up-to-date" && exit 1)
|
||||||
build:
|
build:
|
||||||
executor: aebuilder
|
executor: aebuilder
|
||||||
steps:
|
steps:
|
||||||
@ -35,3 +44,10 @@ jobs:
|
|||||||
- _build/default/rebar3_20.3.8_plt
|
- _build/default/rebar3_20.3.8_plt
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: _build/test/logs
|
path: _build/test/logs
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
build_test:
|
||||||
|
jobs:
|
||||||
|
- build
|
||||||
|
- verify_rebar_lock
|
||||||
|
100
docs/sophia.md
100
docs/sophia.md
@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
**Table of Contents**
|
**Table of Contents**
|
||||||
|
|
||||||
|
- [-](#-)
|
||||||
- [Language Features](#language-features)
|
- [Language Features](#language-features)
|
||||||
- [Contracts](#contracts)
|
- [Contracts](#contracts)
|
||||||
- [Calling other contracts](#calling-other-contracts)
|
- [Calling other contracts](#calling-other-contracts)
|
||||||
|
- [Protected contract calls](#protected-contract-calls)
|
||||||
- [Mutable state](#mutable-state)
|
- [Mutable state](#mutable-state)
|
||||||
- [Stateful functions](#stateful-functions)
|
- [Stateful functions](#stateful-functions)
|
||||||
- [Payable](#payable)
|
- [Payable](#payable)
|
||||||
@ -26,6 +28,7 @@
|
|||||||
- [Updating a value](#updating-a-value)
|
- [Updating a value](#updating-a-value)
|
||||||
- [Map implementation](#map-implementation)
|
- [Map implementation](#map-implementation)
|
||||||
- [Strings](#strings)
|
- [Strings](#strings)
|
||||||
|
- [Chars](#chars)
|
||||||
- [Byte arrays](#byte-arrays)
|
- [Byte arrays](#byte-arrays)
|
||||||
- [Cryptographic builins](#cryptographic-builins)
|
- [Cryptographic builins](#cryptographic-builins)
|
||||||
- [AEVM note](#aevm-note)
|
- [AEVM note](#aevm-note)
|
||||||
@ -34,6 +37,7 @@
|
|||||||
- [Example](#example)
|
- [Example](#example)
|
||||||
- [Sanity checks](#sanity-checks)
|
- [Sanity checks](#sanity-checks)
|
||||||
- [AENS interface](#aens-interface)
|
- [AENS interface](#aens-interface)
|
||||||
|
- [Example](#example-1)
|
||||||
- [Events](#events)
|
- [Events](#events)
|
||||||
- [Argument order](#argument-order)
|
- [Argument order](#argument-order)
|
||||||
- [Compiler pragmas](#compiler-pragmas)
|
- [Compiler pragmas](#compiler-pragmas)
|
||||||
@ -52,6 +56,7 @@
|
|||||||
- [Operators types](#operators-types)
|
- [Operators types](#operators-types)
|
||||||
- [Operator precendences](#operator-precendences)
|
- [Operator precendences](#operator-precendences)
|
||||||
- [Examples](#examples)
|
- [Examples](#examples)
|
||||||
|
- [Delegation signature](#delegation-signature)
|
||||||
|
|
||||||
|
|
||||||
## The Sophia Language
|
## The Sophia Language
|
||||||
@ -136,6 +141,36 @@ without calling it you can write
|
|||||||
Chain.spend(v.address, amount)
|
Chain.spend(v.address, amount)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Protected contract calls
|
||||||
|
|
||||||
|
If a contract call fails for any reason (for instance, the remote contract
|
||||||
|
crashes or runs out of gas, or the entrypoint doesn't exist or has the wrong
|
||||||
|
type) the parent call also fails. To make it possible to recover from failures,
|
||||||
|
contract calls takes a named argument `protected : bool` (default `false`).
|
||||||
|
|
||||||
|
The protected argument must be a literal boolean, and when set to `true`
|
||||||
|
changes the type of the contract call, wrapping the result in an `option` type.
|
||||||
|
If the call fails the result is `None`, otherwise it's `Some(r)` where `r` is
|
||||||
|
the return value of the call.
|
||||||
|
|
||||||
|
```sophia
|
||||||
|
contract VotingType =
|
||||||
|
entrypoint : vote : string => unit
|
||||||
|
|
||||||
|
contract Voter =
|
||||||
|
entrypoint tryVote(v : VotingType, alt : string) =
|
||||||
|
switch(v.vote(alt, protected = true) : option(unit))
|
||||||
|
None => "Voting failed"
|
||||||
|
Some(_) => "Voting successful"
|
||||||
|
```
|
||||||
|
|
||||||
|
Any gas that was consumed by the contract call before the failure stays
|
||||||
|
consumed, which means that in order to protect against the remote contract
|
||||||
|
running out of gas it is necessary to set a gas limit using the `gas` argument.
|
||||||
|
However, note that errors that would normally consume all the gas in the
|
||||||
|
transaction still only uses up the gas spent running the contract.
|
||||||
|
|
||||||
|
|
||||||
### Mutable state
|
### Mutable state
|
||||||
|
|
||||||
Sophia does not have arbitrary mutable state, but only a limited form of
|
Sophia does not have arbitrary mutable state, but only a limited form of
|
||||||
@ -538,7 +573,16 @@ Strings can be compared for equality (`==`, `!=`), used as keys in maps and
|
|||||||
records, and used in builtin functions `String.length`, `String.concat` and
|
records, and used in builtin functions `String.length`, `String.concat` and
|
||||||
the hash functions described below.
|
the hash functions described below.
|
||||||
|
|
||||||
Please refer to the `Map` [library documentation](sophia_stdlib.md#String).
|
Please refer to the `String` [library documentation](sophia_stdlib.md#String).
|
||||||
|
|
||||||
|
### Chars
|
||||||
|
|
||||||
|
There is a builtin type `char` (the underlying representation being an integer),
|
||||||
|
mainly used to manipulate strings via `String.to_list`/`String.from_list`.
|
||||||
|
|
||||||
|
Characters can also be introduced as character literals (`'x', '+', ...).
|
||||||
|
|
||||||
|
Please refer to the `Char` [library documentation](sophia_stdlib.md#Char).
|
||||||
|
|
||||||
### Byte arrays
|
### Byte arrays
|
||||||
|
|
||||||
@ -565,11 +609,10 @@ string`, `String.sha3(s)` and `Crypto.sha3(s)` will give different results on AE
|
|||||||
### Authorization interface
|
### Authorization interface
|
||||||
|
|
||||||
When a Generalized account is authorized, the authorization function needs
|
When a Generalized account is authorized, the authorization function needs
|
||||||
access to the transaction hash for the wrapped transaction. (A `GAMetaTx`
|
access to the transaction and the transaction hash for the wrapped transaction. (A `GAMetaTx`
|
||||||
wrapping a transaction.) The transaction hash is available in the primitive
|
wrapping a transaction.) The transaction and the transaction hash is available in the primitive
|
||||||
`Auth.tx_hash`, it is *only* available during authentication if invoked by a
|
`Auth.tx` and `Auth.tx_hash` respectively, they are *only* available during authentication if invoked by a
|
||||||
normal contract call it returns `None`.
|
normal contract call they return `None`.
|
||||||
|
|
||||||
|
|
||||||
### Oracle interface
|
### Oracle interface
|
||||||
You can attach an oracle to the current contract and you can interact with oracles
|
You can attach an oracle to the current contract and you can interact with oracles
|
||||||
@ -645,6 +688,43 @@ Contracts can interact with the
|
|||||||
[Aeternity Naming System](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
[Aeternity Naming System](https://github.com/aeternity/protocol/blob/master/AENS.md).
|
||||||
For this purpose the [AENS](sophia_stdlib.md#AENS) library was exposed.
|
For this purpose the [AENS](sophia_stdlib.md#AENS) library was exposed.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
In this example we assume that the name `name` already exists, and is owned by
|
||||||
|
an account with address `addr`. In order to allow a contract `ct` to handle
|
||||||
|
`name` the account holder needs to create a
|
||||||
|
[signature](#delegation-signature) `sig` of `addr | name.hash | ct.address`.
|
||||||
|
|
||||||
|
Armed with this information we can for example write a function that extends
|
||||||
|
the name if it expires within 1000 blocks:
|
||||||
|
```
|
||||||
|
stateful entrypoint extend_if_necessary(addr : address, name : string, sig : signature) =
|
||||||
|
switch(AENS.lookup(name))
|
||||||
|
None => ()
|
||||||
|
Some(AENS.Name(_, FixedTTL(expiry), _)) =>
|
||||||
|
if(Chain.block_height + 1000 > expiry)
|
||||||
|
AENS.update(addr, name, Some(RelativeTTL(50000)), None, None, signature = sig)
|
||||||
|
```
|
||||||
|
|
||||||
|
And we can write functions that adds and removes keys from the pointers of the
|
||||||
|
name:
|
||||||
|
```
|
||||||
|
stateful entrypoint add_key(addr : address, name : string, key : string,
|
||||||
|
pt : AENS.pointee, sig : signature) =
|
||||||
|
switch(AENS.lookup(name))
|
||||||
|
None => ()
|
||||||
|
Some(AENS.Name(_, _, ptrs)) =>
|
||||||
|
AENS.update(addr, name, None, None, Some(ptrs{[key] = pt}), signature = sig)
|
||||||
|
|
||||||
|
stateful entrypoint delete_key(addr : address, name : string,
|
||||||
|
key : string, sig : signature) =
|
||||||
|
switch(AENS.lookup(name))
|
||||||
|
None => ()
|
||||||
|
Some(AENS.Name(_, _, ptrs)) =>
|
||||||
|
let ptrs = Map.delete(key, ptrs)
|
||||||
|
AENS.update(addr, name, None, None, Some(ptrs), signature = sig)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
@ -1069,3 +1149,11 @@ contract FundMe =
|
|||||||
amount = state.contributions[to]})
|
amount = state.contributions[to]})
|
||||||
put(state{ contributions @ c = Map.delete(to, c) })
|
put(state{ contributions @ c = Map.delete(to, c) })
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Delegation signature
|
||||||
|
|
||||||
|
Some chain operations (`Oracle.<operation>` and `AENS.<operation>`) have an
|
||||||
|
optional delegation signature. This is typically used when a user/accounts
|
||||||
|
would like to allow a contract to act on it's behalf. The exact data to be
|
||||||
|
signed varies for the different operations, but in all cases you should prepend
|
||||||
|
the signature data with the `network_id` (`ae_mainnet` for the Aeternity mainnet, etc.).
|
||||||
|
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)
|
[x] => Some(x)
|
||||||
_::t => last(t)
|
_::t => last(t)
|
||||||
|
|
||||||
|
function drop_last(l : list('a)) : option(list('a)) = switch(l)
|
||||||
|
[] => None
|
||||||
|
_ => Some(drop_last_unsafe(l))
|
||||||
|
|
||||||
|
function drop_last_unsafe(l : list('a)) : list('a) = switch(l)
|
||||||
|
[_] => []
|
||||||
|
h::t => h::drop_last_unsafe(t)
|
||||||
|
[] => abort("drop_last_unsafe: list empty")
|
||||||
|
|
||||||
|
|
||||||
function contains(e : 'a, l : list('a)) = switch(l)
|
function contains(e : 'a, l : list('a)) = switch(l)
|
||||||
[] => false
|
[] => false
|
||||||
h::t => h == e || contains(e, t)
|
h::t => h == e || contains(e, t)
|
||||||
@ -32,14 +42,15 @@ namespace List =
|
|||||||
|
|
||||||
/** Returns list of all indices of elements from `l` that fulfill the predicate `p`.
|
/** Returns list of all indices of elements from `l` that fulfill the predicate `p`.
|
||||||
*/
|
*/
|
||||||
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0, [])
|
function find_indices(p : 'a => bool, l : list('a)) : list(int) = find_indices_(p, l, 0)
|
||||||
private function find_indices_( p : 'a => bool
|
private function find_indices_( p : 'a => bool
|
||||||
, l : list('a)
|
, l : list('a)
|
||||||
, n : int
|
, n : int
|
||||||
, acc : list(int)
|
|
||||||
) : list(int) = switch(l)
|
) : list(int) = switch(l)
|
||||||
[] => reverse(acc)
|
[] => []
|
||||||
h::t => find_indices_(p, t, n+1, if(p(h)) n::acc else acc)
|
h::t =>
|
||||||
|
let rest = find_indices_(p, t, n+1)
|
||||||
|
if(p(h)) n::rest else rest
|
||||||
|
|
||||||
function nth(n : int, l : list('a)) : option('a) =
|
function nth(n : int, l : list('a)) : option('a) =
|
||||||
switch(l)
|
switch(l)
|
||||||
@ -68,44 +79,44 @@ namespace List =
|
|||||||
* `a` and `b` jumping by given `step`. Includes `a` and takes
|
* `a` and `b` jumping by given `step`. Includes `a` and takes
|
||||||
* `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0.
|
* `b` only if `(b - a) mod step == 0`. `step` should be bigger than 0.
|
||||||
*/
|
*/
|
||||||
function from_to_step(a : int, b : int, s : int) : list(int) = from_to_step_(a, b, s, [])
|
function from_to_step(a : int, b : int, s : int) : list(int) =
|
||||||
private function from_to_step_(a, b, s, acc) =
|
from_to_step_(a, b - (b-a) mod s, s, [])
|
||||||
if (a > b) reverse(acc) else from_to_step_(a + s, b, s, a :: acc)
|
private function from_to_step_(a : int, b : int, s : int, acc : list(int)) : list(int) =
|
||||||
|
if(b < a) acc
|
||||||
|
else from_to_step_(a, b - s, s, b::acc)
|
||||||
|
|
||||||
|
|
||||||
/** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow
|
/** Unsafe. Replaces `n`th element of `l` with `e`. Crashes on over/underflow
|
||||||
*/
|
*/
|
||||||
function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
|
function replace_at(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l, [])
|
if(n<0) abort("insert_at underflow") else replace_at_(n, e, l)
|
||||||
private function replace_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
|
private function replace_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
switch(l)
|
switch(l)
|
||||||
[] => abort("replace_at overflow")
|
[] => abort("replace_at overflow")
|
||||||
h::t => if (n == 0) reverse(e::acc) ++ t
|
h::t => if (n == 0) e::t
|
||||||
else replace_at_(n-1, e, t, h::acc)
|
else h::replace_at_(n-1, e, t)
|
||||||
|
|
||||||
/** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow
|
/** Unsafe. Adds `e` to `l` to be its `n`th element. Crashes on over/underflow
|
||||||
*/
|
*/
|
||||||
function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
|
function insert_at(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l, [])
|
if(n<0) abort("insert_at underflow") else insert_at_(n, e, l)
|
||||||
private function insert_at_(n : int, e : 'a, l : list('a), acc : list('a)) : list('a) =
|
private function insert_at_(n : int, e : 'a, l : list('a)) : list('a) =
|
||||||
if (n == 0) reverse(e::acc) ++ l
|
if (n == 0) e::l
|
||||||
else switch(l)
|
else switch(l)
|
||||||
[] => abort("insert_at overflow")
|
[] => abort("insert_at overflow")
|
||||||
h::t => insert_at_(n-1, e, t, h::acc)
|
h::t => h::insert_at_(n-1, e, t)
|
||||||
|
|
||||||
/** Assuming that cmp represents `<` comparison, inserts `x` before
|
/** Assuming that cmp represents `<` comparison, inserts `x` before
|
||||||
* the first element in the list `l` which is greater than it
|
* the first element in the list `l` which is greater than it
|
||||||
*/
|
*/
|
||||||
function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
|
function insert_by(cmp : (('a, 'a) => bool), x : 'a, l : list('a)) : list('a) =
|
||||||
insert_by_(cmp, x, l, [])
|
|
||||||
private function insert_by_(cmp : (('a, 'a) => bool), x : 'a, l : list('a), acc : list('a)) : list('a) =
|
|
||||||
switch(l)
|
switch(l)
|
||||||
[] => reverse(x::acc)
|
[] => [x]
|
||||||
h::t =>
|
h::t =>
|
||||||
if(cmp(x, h)) // x < h
|
if(cmp(x, h)) // x < h
|
||||||
reverse(acc) ++ (x::l)
|
x::l
|
||||||
else
|
else
|
||||||
insert_by_(cmp, x, t, h::acc)
|
h::insert_by(cmp, x, t)
|
||||||
|
|
||||||
|
|
||||||
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
|
function foldr(cons : ('a, 'b) => 'b, nil : 'b, l : list('a)) : 'b = switch(l)
|
||||||
@ -123,49 +134,52 @@ namespace List =
|
|||||||
f(e)
|
f(e)
|
||||||
foreach(l', f)
|
foreach(l', f)
|
||||||
|
|
||||||
function reverse(l : list('a)) : list('a) = foldl((lst, el) => el :: lst, [], l)
|
function reverse(l : list('a)) : list('a) = reverse_(l, [])
|
||||||
|
private function reverse_(l : list('a), acc : list('a)) : list('a) = switch(l)
|
||||||
|
[] => acc
|
||||||
|
h::t => reverse_(t, h::acc)
|
||||||
|
|
||||||
function map(f : 'a => 'b, l : list('a)) : list('b) = map_(f, l, [])
|
function map(f : 'a => 'b, l : list('a)) : list('b) = switch(l)
|
||||||
private function map_(f : 'a => 'b, l : list('a), acc : list('b)) : list('b) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
h::t => f(h)::map(f, t)
|
||||||
h::t => map_(f, t, f(h)::acc)
|
|
||||||
|
|
||||||
/** Effectively composition of `map` and `flatten`
|
/** Effectively composition of `map` and `flatten`
|
||||||
*/
|
*/
|
||||||
function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
|
function flat_map(f : 'a => list('b), l : list('a)) : list('b) =
|
||||||
ListInternal.flat_map(f, l)
|
ListInternal.flat_map(f, l)
|
||||||
|
|
||||||
function filter(p : 'a => bool, l : list('a)) : list('a) = filter_(p, l, [])
|
function filter(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||||
private function filter_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
h::t =>
|
||||||
h::t => filter_(p, t, if(p(h)) h::acc else acc)
|
let rest = filter(p, t)
|
||||||
|
if(p(h)) h::rest else rest
|
||||||
|
|
||||||
/** Take `n` first elements
|
/** Take up to `n` first elements
|
||||||
*/
|
*/
|
||||||
function take(n : int, l : list('a)) : list('a) =
|
function take(n : int, l : list('a)) : list('a) =
|
||||||
if(n < 0) abort("Take negative number of elements") else take_(n, l, [])
|
if(n < 0) abort("Take negative number of elements") else take_(n, l)
|
||||||
private function take_(n : int, l : list('a), acc : list('a)) : list('a) =
|
private function take_(n : int, l : list('a)) : list('a) =
|
||||||
if(n == 0) reverse(acc)
|
if(n == 0) []
|
||||||
else switch(l)
|
else switch(l)
|
||||||
[] => reverse(acc)
|
[] => []
|
||||||
h::t => take_(n-1, t, h::acc)
|
h::t => h::take_(n-1, t)
|
||||||
|
|
||||||
/** Drop `n` first elements
|
/** Drop up to `n` first elements
|
||||||
*/
|
*/
|
||||||
function drop(n : int, l : list('a)) : list('a) =
|
function drop(n : int, l : list('a)) : list('a) =
|
||||||
if(n < 0) abort("Drop negative number of elements")
|
if(n < 0) abort("Drop negative number of elements") else drop_(n, l)
|
||||||
elif (n == 0) l
|
private function drop_(n : int, l : list('a)) : list('a) =
|
||||||
|
if (n == 0) l
|
||||||
else switch(l)
|
else switch(l)
|
||||||
[] => []
|
[] => []
|
||||||
h::t => drop(n-1, t)
|
h::t => drop_(n-1, t)
|
||||||
|
|
||||||
/** Get the longest prefix of a list in which every element
|
/** Get the longest prefix of a list in which every element
|
||||||
* matches predicate `p`
|
* matches predicate `p`
|
||||||
*/
|
*/
|
||||||
function take_while(p : 'a => bool, l : list('a)) : list('a) = take_while_(p, l, [])
|
function take_while(p : 'a => bool, l : list('a)) : list('a) = switch(l)
|
||||||
private function take_while_(p : 'a => bool, l : list('a), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
h::t => if(p(h)) h::take_while(p, t) else []
|
||||||
h::t => if(p(h)) take_while_(p, t, h::acc) else reverse(acc)
|
|
||||||
|
|
||||||
/** Drop elements from `l` until `p` holds
|
/** Drop elements from `l` until `p` holds
|
||||||
*/
|
*/
|
||||||
@ -176,18 +190,15 @@ namespace List =
|
|||||||
/** Splits list into two lists of elements that respectively
|
/** Splits list into two lists of elements that respectively
|
||||||
* match and don't match predicate `p`
|
* match and don't match predicate `p`
|
||||||
*/
|
*/
|
||||||
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = partition_(p, l, [], [])
|
function partition(p : 'a => bool, l : list('a)) : (list('a) * list('a)) = switch(l)
|
||||||
private function partition_( p : 'a => bool
|
[] => ([], [])
|
||||||
, l : list('a)
|
h::t =>
|
||||||
, acc_t : list('a)
|
let (l, r) = partition(p, t)
|
||||||
, acc_f : list('a)
|
if(p(h)) (h::l, r) else (l, h::r)
|
||||||
) : (list('a) * list('a)) = switch(l)
|
|
||||||
[] => (reverse(acc_t), reverse(acc_f))
|
|
||||||
h::t => if(p(h)) partition_(p, t, h::acc_t, acc_f) else partition_(p, t, acc_t, h::acc_f)
|
|
||||||
|
|
||||||
/** Flattens list of lists into a single list
|
function flatten(l : list(list('a))) : list('a) = switch(l)
|
||||||
*/
|
[] => []
|
||||||
function flatten(ll : list(list('a))) : list('a) = foldr((l1, l2) => l1 ++ l2, [], ll)
|
h::t => h ++ flatten(t)
|
||||||
|
|
||||||
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
function all(p : 'a => bool, l : list('a)) : bool = switch(l)
|
||||||
[] => true
|
[] => true
|
||||||
@ -203,28 +214,25 @@ namespace List =
|
|||||||
|
|
||||||
|
|
||||||
/** Zips two list by applying bimapping function on respective elements.
|
/** Zips two list by applying bimapping function on respective elements.
|
||||||
* Drops longer tail.
|
* Drops the tail of the longer list.
|
||||||
*/
|
*/
|
||||||
function zip_with(f : ('a, 'b) => 'c, l1 : list('a), l2 : list('b)) : list('c) = zip_with_(f, l1, l2, [])
|
private function zip_with( f : ('a, 'b) => 'c
|
||||||
private function zip_with_( f : ('a, 'b) => 'c
|
|
||||||
, l1 : list('a)
|
, l1 : list('a)
|
||||||
, l2 : list('b)
|
, l2 : list('b)
|
||||||
, acc : list('c)
|
|
||||||
) : list('c) = switch ((l1, l2))
|
) : list('c) = switch ((l1, l2))
|
||||||
(h1::t1, h2::t2) => zip_with_(f, t1, t2, f(h1, h2)::acc)
|
(h1::t1, h2::t2) => f(h1, h2)::zip_with(f, t1, t2)
|
||||||
_ => reverse(acc)
|
_ => []
|
||||||
|
|
||||||
/** Zips two lists into list of pairs. Drops longer tail.
|
/** Zips two lists into list of pairs.
|
||||||
|
* Drops the tail of the longer list.
|
||||||
*/
|
*/
|
||||||
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
|
function zip(l1 : list('a), l2 : list('b)) : list('a * 'b) = zip_with((a, b) => (a, b), l1, l2)
|
||||||
|
|
||||||
function unzip(l : list('a * 'b)) : list('a) * list('b) = unzip_(l, [], [])
|
function unzip(l : list('a * 'b)) : (list('a) * list('b)) = switch(l)
|
||||||
private function unzip_( l : list('a * 'b)
|
[] => ([], [])
|
||||||
, acc_l : list('a)
|
(h1, h2)::t =>
|
||||||
, acc_r : list('b)
|
let (t1, t2) = unzip(t)
|
||||||
) : (list('a) * list('b)) = switch(l)
|
(h1::t1, h2::t2)
|
||||||
[] => (reverse(acc_l), reverse(acc_r))
|
|
||||||
(left, right)::t => unzip_(t, left::acc_l, right::acc_r)
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Improve?
|
// TODO: Improve?
|
||||||
@ -235,17 +243,14 @@ namespace List =
|
|||||||
|
|
||||||
/** Puts `delim` between every two members of the list
|
/** Puts `delim` between every two members of the list
|
||||||
*/
|
*/
|
||||||
function intersperse(delim : 'a, l : list('a)) : list('a) = intersperse_(delim, l, [])
|
function intersperse(delim : 'a, l : list('a)) : list('a) = switch(l)
|
||||||
private function intersperse_(delim : 'a, l : list('a), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => reverse(acc)
|
[e] => [e]
|
||||||
[e] => reverse(e::acc)
|
h::t => h::delim::intersperse(delim, t)
|
||||||
h::t => intersperse_(delim, t, delim::h::acc)
|
|
||||||
|
|
||||||
|
|
||||||
/** Effectively a zip with an infinite sequence of natural numbers
|
/** Effectively a zip with an infinite sequence of natural numbers
|
||||||
*/
|
*/
|
||||||
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0, [])
|
function enumerate(l : list('a)) : list(int * 'a) = enumerate_(l, 0)
|
||||||
private function enumerate_(l : list('a), n : int, acc : list(int * 'a)) : list(int * 'a) = switch(l)
|
private function enumerate_(l : list('a), n : int) : list(int * 'a) = switch(l)
|
||||||
[] => reverse(acc)
|
[] => []
|
||||||
h::t => enumerate_(t, n + 1, (n, h)::acc)
|
h::t => (n, h)::enumerate_(t, n + 1)
|
||||||
|
|
@ -69,20 +69,21 @@ namespace Option =
|
|||||||
/** Turns list of options into a list of elements that are under `Some`s.
|
/** Turns list of options into a list of elements that are under `Some`s.
|
||||||
* Safe.
|
* Safe.
|
||||||
*/
|
*/
|
||||||
function filter_options(l : list(option('a))) : list('a) = filter_options_(l, [])
|
function filter_options(l : list(option('a))) : list('a) = switch(l)
|
||||||
private function filter_options_(l : list (option('a)), acc : list('a)) : list('a) = switch(l)
|
[] => []
|
||||||
[] => List.reverse(acc)
|
None::t => filter_options(t)
|
||||||
None::t => filter_options_(t, acc)
|
Some(x)::t => x::filter_options(t)
|
||||||
Some(x)::t => filter_options_(t, x::acc)
|
|
||||||
|
|
||||||
/** Just like `filter_options` but requires all elements to be `Some` and returns
|
/** Just like `filter_options` but requires all elements to be `Some` and returns
|
||||||
* None if any of them is not
|
* None if any of them is not
|
||||||
*/
|
*/
|
||||||
function seq_options(l : list (option('a))) : option (list('a)) = seq_options_(l, [])
|
function seq_options(l : list (option('a))) : option (list('a)) = switch(l)
|
||||||
private function seq_options_(l : list (option('a)), acc : list('a)) : option(list('a)) = switch(l)
|
[] => Some([])
|
||||||
[] => Some(List.reverse(acc))
|
None::_ => None
|
||||||
None::t => None
|
Some(x)::t => switch(seq_options(t))
|
||||||
Some(x)::t => seq_options_(t, x::acc)
|
None => None
|
||||||
|
Some(st) => Some(x::st)
|
||||||
|
|
||||||
|
|
||||||
/** Choose `Some` out of two if possible
|
/** Choose `Some` out of two if possible
|
||||||
*/
|
*/
|
||||||
@ -95,4 +96,3 @@ namespace Option =
|
|||||||
[] => None
|
[] => None
|
||||||
None::t => choose_first(t)
|
None::t => choose_first(t)
|
||||||
Some(x)::_ => Some(x)
|
Some(x)::_ => Some(x)
|
||||||
|
|
||||||
|
117
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]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"4f4d6d3"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"7f0d309"}}}
|
||||||
, {getopt, "1.0.1"}
|
, {getopt, "1.0.1"}
|
||||||
, {eblake2, "1.0.0"}
|
, {eblake2, "1.0.0"}
|
||||||
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
, {jsx, {git, "https://github.com/talentdeficit/jsx.git",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{"1.1.0",
|
{"1.1.0",
|
||||||
[{<<"aebytecode">>,
|
[{<<"aebytecode">>,
|
||||||
{git,"https://github.com/aeternity/aebytecode.git",
|
{git,"https://github.com/aeternity/aebytecode.git",
|
||||||
{ref,"4f4d6d30cd2c46b3830454d650a424d513f69134"}},
|
{ref,"7f0d3090d4dc6c4d5fca7645b0c21eb0e65ad208"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
| aeso_syntax:id() | aeso_syntax:qid()
|
| aeso_syntax:id() | aeso_syntax:qid()
|
||||||
| aeso_syntax:con() | aeso_syntax:qcon() %% contracts
|
| aeso_syntax:con() | aeso_syntax:qcon() %% contracts
|
||||||
| aeso_syntax:tvar()
|
| aeso_syntax:tvar()
|
||||||
|
| {if_t, aeso_syntax:ann(), aeso_syntax:id(), utype(), utype()} %% Can branch on named argument (protected)
|
||||||
| uvar().
|
| uvar().
|
||||||
|
|
||||||
-type uvar() :: {uvar, aeso_syntax:ann(), reference()}.
|
-type uvar() :: {uvar, aeso_syntax:ann(), reference()}.
|
||||||
@ -47,7 +48,14 @@
|
|||||||
name :: aeso_syntax:id(),
|
name :: aeso_syntax:id(),
|
||||||
type :: utype()}).
|
type :: utype()}).
|
||||||
|
|
||||||
-type named_argument_constraint() :: #named_argument_constraint{}.
|
-record(dependent_type_constraint,
|
||||||
|
{ named_args_t :: named_args_t()
|
||||||
|
, named_args :: [aeso_syntax:arg_expr()]
|
||||||
|
, general_type :: utype()
|
||||||
|
, specialized_type :: utype()
|
||||||
|
, context :: term() }).
|
||||||
|
|
||||||
|
-type named_argument_constraint() :: #named_argument_constraint{} | #dependent_type_constraint{}.
|
||||||
|
|
||||||
-record(field_constraint,
|
-record(field_constraint,
|
||||||
{ record_t :: utype()
|
{ record_t :: utype()
|
||||||
@ -236,16 +244,21 @@ bind_fields([], Env) -> Env;
|
|||||||
bind_fields([{Id, Info} | Rest], Env) ->
|
bind_fields([{Id, Info} | Rest], Env) ->
|
||||||
bind_fields(Rest, bind_field(Id, Info, Env)).
|
bind_fields(Rest, bind_field(Id, Info, Env)).
|
||||||
|
|
||||||
%% Contract entrypoints take two named arguments (gas : int = Call.gas_left(), value : int = 0).
|
%% Contract entrypoints take three named arguments
|
||||||
|
%% gas : int = Call.gas_left()
|
||||||
|
%% value : int = 0
|
||||||
|
%% protected : bool = false
|
||||||
contract_call_type({fun_t, Ann, [], Args, Ret}) ->
|
contract_call_type({fun_t, Ann, [], Args, Ret}) ->
|
||||||
Id = fun(X) -> {id, Ann, X} end,
|
Id = fun(X) -> {id, Ann, X} end,
|
||||||
Int = Id("int"),
|
Int = Id("int"),
|
||||||
Typed = fun(E, T) -> {typed, Ann, E, T} end,
|
Typed = fun(E, T) -> {typed, Ann, E, T} end,
|
||||||
Named = fun(Name, Default) -> {named_arg_t, Ann, Id(Name), Int, Default} end,
|
Named = fun(Name, Default = {typed, _, _, T}) -> {named_arg_t, Ann, Id(Name), T, Default} end,
|
||||||
{fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]},
|
{fun_t, Ann, [Named("gas", Typed({app, Ann, Typed({qid, Ann, ["Call", "gas_left"]},
|
||||||
{fun_t, Ann, [], [], Int}),
|
{fun_t, Ann, [], [], Int}),
|
||||||
[]}, Int)),
|
[]}, Int)),
|
||||||
Named("value", Typed({int, Ann, 0}, Int))], Args, Ret}.
|
Named("value", Typed({int, Ann, 0}, Int)),
|
||||||
|
Named("protected", Typed({bool, Ann, false}, Id("bool")))],
|
||||||
|
Args, {if_t, Ann, Id("protected"), {app_t, Ann, {id, Ann, "option"}, [Ret]}, Ret}}.
|
||||||
|
|
||||||
-spec bind_contract(aeso_syntax:decl(), env()) -> env().
|
-spec bind_contract(aeso_syntax:decl(), env()) -> env().
|
||||||
bind_contract({contract, Ann, Id, Contents}, Env) ->
|
bind_contract({contract, Ann, Id, Contents}, Env) ->
|
||||||
@ -368,6 +381,7 @@ is_private(Ann) -> proplists:get_value(private, Ann, false).
|
|||||||
global_env() ->
|
global_env() ->
|
||||||
Ann = [{origin, system}],
|
Ann = [{origin, system}],
|
||||||
Int = {id, Ann, "int"},
|
Int = {id, Ann, "int"},
|
||||||
|
Char = {id, Ann, "char"},
|
||||||
Bool = {id, Ann, "bool"},
|
Bool = {id, Ann, "bool"},
|
||||||
String = {id, Ann, "string"},
|
String = {id, Ann, "string"},
|
||||||
Address = {id, Ann, "address"},
|
Address = {id, Ann, "address"},
|
||||||
@ -393,6 +407,24 @@ global_env() ->
|
|||||||
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
|
Signature = {named_arg_t, Ann, SignId, SignId, {typed, Ann, SignDef, SignId}},
|
||||||
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
|
SignFun = fun(Ts, T) -> {type_sig, [stateful|Ann], none, [Signature], Ts, T} end,
|
||||||
TTL = {qid, Ann, ["Chain", "ttl"]},
|
TTL = {qid, Ann, ["Chain", "ttl"]},
|
||||||
|
Pointee = {qid, Ann, ["AENS", "pointee"]},
|
||||||
|
AENSName = {qid, Ann, ["AENS", "name"]},
|
||||||
|
Fr = {qid, Ann, ["MCL_BLS12_381", "fr"]},
|
||||||
|
Fp = {qid, Ann, ["MCL_BLS12_381", "fp"]},
|
||||||
|
Fp2 = {tuple_t, Ann, [Fp, Fp]},
|
||||||
|
G1 = {tuple_t, Ann, [Fp, Fp, Fp]},
|
||||||
|
G2 = {tuple_t, Ann, [Fp2, Fp2, Fp2]},
|
||||||
|
GT = {tuple_t, Ann, lists:duplicate(12, Fp)},
|
||||||
|
Tx = {qid, Ann, ["Chain", "tx"]},
|
||||||
|
GAMetaTx = {qid, Ann, ["Chain", "ga_meta_tx"]},
|
||||||
|
BaseTx = {qid, Ann, ["Chain", "base_tx"]},
|
||||||
|
PayForTx = {qid, Ann, ["Chain", "paying_for_tx"]},
|
||||||
|
|
||||||
|
FldT = fun(Id, T) -> {field_t, Ann, {id, Ann, Id}, T} end,
|
||||||
|
TxFlds = [{"paying_for", Option(PayForTx)}, {"ga_metas", List(GAMetaTx)},
|
||||||
|
{"actor", Address}, {"fee", Int}, {"ttl", Int}, {"tx", BaseTx}],
|
||||||
|
TxType = {record_t, [FldT(N, T) || {N, T} <- TxFlds ]},
|
||||||
|
|
||||||
Fee = Int,
|
Fee = Int,
|
||||||
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
|
[A, Q, R, K, V] = lists:map(TVar, ["a", "q", "r", "k", "v"]),
|
||||||
|
|
||||||
@ -430,8 +462,36 @@ global_env() ->
|
|||||||
{"timestamp", Int},
|
{"timestamp", Int},
|
||||||
{"block_height", Int},
|
{"block_height", Int},
|
||||||
{"difficulty", Int},
|
{"difficulty", Int},
|
||||||
{"gas_limit", Int}])
|
{"gas_limit", Int},
|
||||||
, types = MkDefs([{"ttl", 0}]) },
|
%% Tx constructors
|
||||||
|
{"GAMetaTx", Fun([Address, Int], GAMetaTx)},
|
||||||
|
{"PayingForTx", Fun([Address, Int], PayForTx)},
|
||||||
|
{"SpendTx", Fun([Address, Int, String], BaseTx)},
|
||||||
|
{"OracleRegisterTx", BaseTx},
|
||||||
|
{"OracleQueryTx", BaseTx},
|
||||||
|
{"OracleResponseTx", BaseTx},
|
||||||
|
{"OracleExtendTx", BaseTx},
|
||||||
|
{"NamePreclaimTx", BaseTx},
|
||||||
|
{"NameClaimTx", Fun([String], BaseTx)},
|
||||||
|
{"NameUpdateTx", Fun([Hash], BaseTx)},
|
||||||
|
{"NameRevokeTx", Fun([Hash], BaseTx)},
|
||||||
|
{"NameTransferTx", Fun([Address, Hash], BaseTx)},
|
||||||
|
{"ChannelCreateTx", Fun([Address], BaseTx)},
|
||||||
|
{"ChannelDepositTx", Fun([Address, Int], BaseTx)},
|
||||||
|
{"ChannelWithdrawTx", Fun([Address, Int], BaseTx)},
|
||||||
|
{"ChannelForceProgressTx", Fun([Address], BaseTx)},
|
||||||
|
{"ChannelCloseMutualTx", Fun([Address], BaseTx)},
|
||||||
|
{"ChannelCloseSoloTx", Fun([Address], BaseTx)},
|
||||||
|
{"ChannelSlashTx", Fun([Address], BaseTx)},
|
||||||
|
{"ChannelSettleTx", Fun([Address], BaseTx)},
|
||||||
|
{"ChannelSnapshotSoloTx", Fun([Address], BaseTx)},
|
||||||
|
{"ContractCreateTx", Fun([Int], BaseTx)},
|
||||||
|
{"ContractCallTx", Fun([Address, Int], BaseTx)},
|
||||||
|
{"GAAttachTx", BaseTx}
|
||||||
|
])
|
||||||
|
, types = MkDefs([{"ttl", 0}, {"tx", {[], TxType}},
|
||||||
|
{"base_tx", 0},
|
||||||
|
{"paying_for_tx", 0}, {"ga_meta_tx", 0}]) },
|
||||||
|
|
||||||
ContractScope = #scope
|
ContractScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
@ -451,6 +511,7 @@ global_env() ->
|
|||||||
OracleScope = #scope
|
OracleScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
|
[{"register", SignFun([Address, Fee, TTL], Oracle(Q, R))},
|
||||||
|
{"expiry", Fun([Oracle(Q, R)], Fee)},
|
||||||
{"query_fee", Fun([Oracle(Q, R)], Fee)},
|
{"query_fee", Fun([Oracle(Q, R)], Fee)},
|
||||||
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
|
{"query", StateFun([Oracle(Q, R), Q, Fee, TTL, TTL], Query(Q, R))},
|
||||||
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
|
{"get_question", Fun([Oracle(Q, R), Query(Q, R)], Q)},
|
||||||
@ -466,7 +527,18 @@ global_env() ->
|
|||||||
{"preclaim", SignFun([Address, Hash], Unit)},
|
{"preclaim", SignFun([Address, Hash], Unit)},
|
||||||
{"claim", SignFun([Address, String, Int, Int], Unit)},
|
{"claim", SignFun([Address, String, Int, Int], Unit)},
|
||||||
{"transfer", SignFun([Address, Address, String], Unit)},
|
{"transfer", SignFun([Address, Address, String], Unit)},
|
||||||
{"revoke", SignFun([Address, String], Unit)}]) },
|
{"revoke", SignFun([Address, String], Unit)},
|
||||||
|
{"update", SignFun([Address, String, Option(TTL), Option(Int), Option(Map(String, Pointee))], Unit)},
|
||||||
|
{"lookup", Fun([String], option_t(Ann, AENSName))},
|
||||||
|
%% AENS pointee constructors
|
||||||
|
{"AccountPt", Fun1(Address, Pointee)},
|
||||||
|
{"OraclePt", Fun1(Address, Pointee)},
|
||||||
|
{"ContractPt", Fun1(Address, Pointee)},
|
||||||
|
{"ChannelPt", Fun1(Address, Pointee)},
|
||||||
|
%% Name object constructor
|
||||||
|
{"Name", Fun([Address, TTL, Map(String, Pointee)], AENSName)}
|
||||||
|
])
|
||||||
|
, types = MkDefs([{"pointee", 0}, {"name", 0}]) },
|
||||||
|
|
||||||
MapScope = #scope
|
MapScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
@ -489,19 +561,65 @@ global_env() ->
|
|||||||
{"sha256", Fun1(A, Hash)},
|
{"sha256", Fun1(A, Hash)},
|
||||||
{"blake2b", Fun1(A, Hash)}]) },
|
{"blake2b", Fun1(A, Hash)}]) },
|
||||||
|
|
||||||
|
%% Fancy BLS12-381 crypto operations
|
||||||
|
MCL_BLS12_381_Scope = #scope
|
||||||
|
{ funs = MkDefs(
|
||||||
|
[{"g1_neg", Fun1(G1, G1)},
|
||||||
|
{"g1_norm", Fun1(G1, G1)},
|
||||||
|
{"g1_valid", Fun1(G1, Bool)},
|
||||||
|
{"g1_is_zero", Fun1(G1, Bool)},
|
||||||
|
{"g1_add", Fun ([G1, G1], G1)},
|
||||||
|
{"g1_mul", Fun ([Fr, G1], G1)},
|
||||||
|
|
||||||
|
{"g2_neg", Fun1(G2, G2)},
|
||||||
|
{"g2_norm", Fun1(G2, G2)},
|
||||||
|
{"g2_valid", Fun1(G2, Bool)},
|
||||||
|
{"g2_is_zero", Fun1(G2, Bool)},
|
||||||
|
{"g2_add", Fun ([G2, G2], G2)},
|
||||||
|
{"g2_mul", Fun ([Fr, G2], G2)},
|
||||||
|
|
||||||
|
{"gt_inv", Fun1(GT, GT)},
|
||||||
|
{"gt_add", Fun ([GT, GT], GT)},
|
||||||
|
{"gt_mul", Fun ([GT, GT], GT)},
|
||||||
|
{"gt_pow", Fun ([GT, Fr], GT)},
|
||||||
|
{"gt_is_one", Fun1(GT, Bool)},
|
||||||
|
{"pairing", Fun ([G1, G2], GT)},
|
||||||
|
{"miller_loop", Fun ([G1, G2], GT)},
|
||||||
|
{"final_exp", Fun1(GT, GT)},
|
||||||
|
|
||||||
|
{"int_to_fr", Fun1(Int, Fr)},
|
||||||
|
{"int_to_fp", Fun1(Int, Fp)},
|
||||||
|
{"fr_to_int", Fun1(Fr, Int)},
|
||||||
|
{"fp_to_int", Fun1(Fp, Int)}
|
||||||
|
]),
|
||||||
|
types = MkDefs(
|
||||||
|
[{"fr", 0}, {"fp", 0}]) },
|
||||||
|
|
||||||
%% Authentication
|
%% Authentication
|
||||||
AuthScope = #scope
|
AuthScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
[{"tx_hash", Option(Hash)}]) },
|
[{"tx_hash", Option(Hash)},
|
||||||
|
{"tx", Option(Tx)} ]) },
|
||||||
|
|
||||||
%% Strings
|
%% Strings
|
||||||
StringScope = #scope
|
StringScope = #scope
|
||||||
{ funs = MkDefs(
|
{ funs = MkDefs(
|
||||||
[{"length", Fun1(String, Int)},
|
[{"length", Fun1(String, Int)},
|
||||||
{"concat", Fun([String, String], String)},
|
{"concat", Fun([String, String], String)},
|
||||||
{"sha3", Fun1(String, Hash)},
|
{"to_list", Fun1(String, List(Char))},
|
||||||
{"sha256", Fun1(String, Hash)},
|
{"from_list", Fun1(List(Char), String)},
|
||||||
{"blake2b", Fun1(String, Hash)}]) },
|
{"to_upper", Fun1(String, String)},
|
||||||
|
{"to_lower", Fun1(String, String)},
|
||||||
|
{"sha3", Fun1(String, Hash)},
|
||||||
|
{"sha256", Fun1(String, Hash)},
|
||||||
|
{"blake2b", Fun1(String, Hash)}
|
||||||
|
]) },
|
||||||
|
|
||||||
|
%% Chars
|
||||||
|
CharScope = #scope
|
||||||
|
{ funs = MkDefs(
|
||||||
|
[{"to_int", Fun1(Char, Int)},
|
||||||
|
{"from_int", Fun1(Int, Option(Char))}]) },
|
||||||
|
|
||||||
%% Bits
|
%% Bits
|
||||||
BitsScope = #scope
|
BitsScope = #scope
|
||||||
@ -533,6 +651,7 @@ global_env() ->
|
|||||||
{"is_contract", Fun1(Address, Bool)},
|
{"is_contract", Fun1(Address, Bool)},
|
||||||
{"is_payable", Fun1(Address, Bool)}]) },
|
{"is_payable", Fun1(Address, Bool)}]) },
|
||||||
|
|
||||||
|
|
||||||
#env{ scopes =
|
#env{ scopes =
|
||||||
#{ [] => TopScope
|
#{ [] => TopScope
|
||||||
, ["Chain"] => ChainScope
|
, ["Chain"] => ChainScope
|
||||||
@ -543,12 +662,19 @@ global_env() ->
|
|||||||
, ["Map"] => MapScope
|
, ["Map"] => MapScope
|
||||||
, ["Auth"] => AuthScope
|
, ["Auth"] => AuthScope
|
||||||
, ["Crypto"] => CryptoScope
|
, ["Crypto"] => CryptoScope
|
||||||
, ["String"] => StringScope
|
, ["MCL_BLS12_381"] => MCL_BLS12_381_Scope
|
||||||
|
, ["StringInternal"] => StringScope
|
||||||
|
, ["Char"] => CharScope
|
||||||
, ["Bits"] => BitsScope
|
, ["Bits"] => BitsScope
|
||||||
, ["Bytes"] => BytesScope
|
, ["Bytes"] => BytesScope
|
||||||
, ["Int"] => IntScope
|
, ["Int"] => IntScope
|
||||||
, ["Address"] => AddressScope
|
, ["Address"] => AddressScope
|
||||||
} }.
|
}
|
||||||
|
, fields =
|
||||||
|
maps:from_list([{N, [#field_info{ ann = [], field_t = T, record_t = Tx, kind = record }]}
|
||||||
|
|| {N, T} <- TxFlds ])
|
||||||
|
}.
|
||||||
|
|
||||||
|
|
||||||
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
|
option_t(As, T) -> {app_t, As, {id, As, "option"}, [T]}.
|
||||||
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
|
map_t(As, K, V) -> {app_t, As, {id, As, "map"}, [K, V]}.
|
||||||
@ -706,7 +832,12 @@ expose_internals(Defs, What) ->
|
|||||||
main_contract -> [{entrypoint, true}|Ann]; % minor duplication
|
main_contract -> [{entrypoint, true}|Ann]; % minor duplication
|
||||||
contract -> Ann
|
contract -> Ann
|
||||||
end,
|
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
|
end
|
||||||
|| Def <- Defs
|
|| Def <- Defs
|
||||||
].
|
].
|
||||||
@ -1274,7 +1405,7 @@ infer_expr(Env, {typed, As, Body, Type}) ->
|
|||||||
Type1 = check_type(Env, Type),
|
Type1 = check_type(Env, Type),
|
||||||
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
|
{typed, _, NewBody, NewType} = check_expr(Env, Body, Type1),
|
||||||
{typed, As, NewBody, NewType};
|
{typed, As, NewBody, NewType};
|
||||||
infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
infer_expr(Env, {app, Ann, Fun, Args0} = App) ->
|
||||||
NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ],
|
NamedArgs = [ Arg || Arg = {named_arg, _, _, _} <- Args0 ],
|
||||||
Args = Args0 -- NamedArgs,
|
Args = Args0 -- NamedArgs,
|
||||||
case aeso_syntax:get_ann(format, Ann) of
|
case aeso_syntax:get_ann(format, Ann) of
|
||||||
@ -1289,8 +1420,16 @@ infer_expr(Env, {app, Ann, Fun, Args0}) ->
|
|||||||
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
|
NewFun={typed, _, _, FunType} = infer_expr(Env, Fun),
|
||||||
NewArgs = [infer_expr(Env, A) || A <- Args],
|
NewArgs = [infer_expr(Env, A) || A <- Args],
|
||||||
ArgTypes = [T || {typed, _, _, T} <- NewArgs],
|
ArgTypes = [T || {typed, _, _, T} <- NewArgs],
|
||||||
|
GeneralResultType = fresh_uvar(Ann),
|
||||||
ResultType = fresh_uvar(Ann),
|
ResultType = fresh_uvar(Ann),
|
||||||
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, ResultType}, {infer_app, Fun, Args, FunType, ArgTypes}),
|
When = {infer_app, Fun, NamedArgs1, Args, FunType, ArgTypes},
|
||||||
|
unify(Env, FunType, {fun_t, [], NamedArgsVar, ArgTypes, GeneralResultType}, When),
|
||||||
|
add_named_argument_constraint(
|
||||||
|
#dependent_type_constraint{ named_args_t = NamedArgsVar,
|
||||||
|
named_args = NamedArgs1,
|
||||||
|
general_type = GeneralResultType,
|
||||||
|
specialized_type = ResultType,
|
||||||
|
context = {check_return, App} }),
|
||||||
{typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
|
{typed, Ann, {app, Ann, NewFun, NamedArgs1 ++ NewArgs}, dereference(ResultType)}
|
||||||
end;
|
end;
|
||||||
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
|
infer_expr(Env, {'if', Attrs, Cond, Then, Else}) ->
|
||||||
@ -1449,7 +1588,7 @@ infer_op(Env, As, Op, Args, InferOp) ->
|
|||||||
TypedArgs = [infer_expr(Env, A) || A <- Args],
|
TypedArgs = [infer_expr(Env, A) || A <- Args],
|
||||||
ArgTypes = [T || {typed, _, _, T} <- TypedArgs],
|
ArgTypes = [T || {typed, _, _, T} <- TypedArgs],
|
||||||
Inferred = {fun_t, _, _, OperandTypes, ResultType} = InferOp(Op),
|
Inferred = {fun_t, _, _, OperandTypes, ResultType} = InferOp(Op),
|
||||||
unify(Env, ArgTypes, OperandTypes, {infer_app, Op, Args, Inferred, ArgTypes}),
|
unify(Env, ArgTypes, OperandTypes, {infer_app, Op, [], Args, Inferred, ArgTypes}),
|
||||||
{typed, As, {app, As, Op, TypedArgs}, ResultType}.
|
{typed, As, {app, As, Op, TypedArgs}, ResultType}.
|
||||||
|
|
||||||
infer_pattern(Env, Pattern) ->
|
infer_pattern(Env, Pattern) ->
|
||||||
@ -1620,12 +1759,12 @@ create_constraints() ->
|
|||||||
create_field_constraints().
|
create_field_constraints().
|
||||||
|
|
||||||
destroy_and_report_unsolved_constraints(Env) ->
|
destroy_and_report_unsolved_constraints(Env) ->
|
||||||
|
solve_field_constraints(Env),
|
||||||
solve_named_argument_constraints(Env),
|
solve_named_argument_constraints(Env),
|
||||||
solve_bytes_constraints(Env),
|
solve_bytes_constraints(Env),
|
||||||
solve_field_constraints(Env),
|
|
||||||
destroy_and_report_unsolved_field_constraints(Env),
|
|
||||||
destroy_and_report_unsolved_bytes_constraints(Env),
|
destroy_and_report_unsolved_bytes_constraints(Env),
|
||||||
destroy_and_report_unsolved_named_argument_constraints(Env).
|
destroy_and_report_unsolved_named_argument_constraints(Env),
|
||||||
|
destroy_and_report_unsolved_field_constraints(Env).
|
||||||
|
|
||||||
%% -- Named argument constraints --
|
%% -- Named argument constraints --
|
||||||
|
|
||||||
@ -1665,8 +1804,43 @@ check_named_argument_constraint(Env,
|
|||||||
type_error({bad_named_argument, Args, Id}),
|
type_error({bad_named_argument, Args, Id}),
|
||||||
false;
|
false;
|
||||||
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
|
[T] -> unify(Env, T, Type, {check_named_arg_constraint, C}), true
|
||||||
|
end;
|
||||||
|
check_named_argument_constraint(Env,
|
||||||
|
#dependent_type_constraint{ named_args_t = NamedArgsT0,
|
||||||
|
named_args = NamedArgs,
|
||||||
|
general_type = GenType,
|
||||||
|
specialized_type = SpecType,
|
||||||
|
context = {check_return, App} }) ->
|
||||||
|
NamedArgsT = dereference(NamedArgsT0),
|
||||||
|
case dereference(NamedArgsT0) of
|
||||||
|
[_ | _] = NamedArgsT ->
|
||||||
|
GetVal = fun(Name, Default) ->
|
||||||
|
hd([ Val || {named_arg, _, {id, _, N}, Val} <- NamedArgs, N == Name] ++
|
||||||
|
[ Default ])
|
||||||
|
end,
|
||||||
|
ArgEnv = maps:from_list([ {Name, GetVal(Name, Default)}
|
||||||
|
|| {named_arg_t, _, {id, _, Name}, _, Default} <- NamedArgsT ]),
|
||||||
|
GenType1 = specialize_dependent_type(ArgEnv, GenType),
|
||||||
|
unify(Env, GenType1, SpecType, {check_expr, App, GenType1, SpecType}),
|
||||||
|
true;
|
||||||
|
_ -> unify(Env, GenType, SpecType, {check_expr, App, GenType, SpecType}), true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
specialize_dependent_type(Env, Type) ->
|
||||||
|
case dereference(Type) of
|
||||||
|
{if_t, _, {id, _, Arg}, Then, Else} ->
|
||||||
|
Val = maps:get(Arg, Env),
|
||||||
|
case Val of
|
||||||
|
{typed, _, {bool, _, true}, _} -> Then;
|
||||||
|
{typed, _, {bool, _, false}, _} -> Else;
|
||||||
|
_ ->
|
||||||
|
type_error({named_argument_must_be_literal_bool, Arg, Val}),
|
||||||
|
fresh_uvar(aeso_syntax:get_ann(Val))
|
||||||
|
end;
|
||||||
|
_ -> Type %% Currently no deep dependent types
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
destroy_and_report_unsolved_named_argument_constraints(Env) ->
|
destroy_and_report_unsolved_named_argument_constraints(Env) ->
|
||||||
Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()),
|
Unsolved = solve_named_argument_constraints(Env, get_named_argument_constraints()),
|
||||||
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
|
[ type_error({unsolved_named_argument_constraint, C}) || C <- Unsolved ],
|
||||||
@ -1892,9 +2066,11 @@ destroy_and_report_unsolved_field_constraints(Env) ->
|
|||||||
{FieldCs, OtherCs} =
|
{FieldCs, OtherCs} =
|
||||||
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
lists:partition(fun(#field_constraint{}) -> true; (_) -> false end,
|
||||||
get_field_constraints()),
|
get_field_constraints()),
|
||||||
{CreateCs, ContractCs} =
|
{CreateCs, OtherCs1} =
|
||||||
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
|
lists:partition(fun(#record_create_constraint{}) -> true; (_) -> false end,
|
||||||
OtherCs),
|
OtherCs),
|
||||||
|
{ContractCs, []} =
|
||||||
|
lists:partition(fun(#is_contract_constraint{}) -> true; (_) -> false end, OtherCs1),
|
||||||
Unknown = solve_known_record_types(Env, FieldCs),
|
Unknown = solve_known_record_types(Env, FieldCs),
|
||||||
if Unknown == [] -> ok;
|
if Unknown == [] -> ok;
|
||||||
true ->
|
true ->
|
||||||
@ -1968,7 +2144,8 @@ unfold_record_types(Env, T) ->
|
|||||||
unfold_types(Env, T, [unfold_record_types]).
|
unfold_types(Env, T, [unfold_record_types]).
|
||||||
|
|
||||||
unfold_types(Env, {typed, Attr, E, Type}, Options) ->
|
unfold_types(Env, {typed, Attr, E, Type}, Options) ->
|
||||||
{typed, Attr, unfold_types(Env, E, Options), unfold_types_in_type(Env, Type, Options)};
|
Options1 = [{ann, Attr} | lists:keydelete(ann, 1, Options)],
|
||||||
|
{typed, Attr, unfold_types(Env, E, Options), unfold_types_in_type(Env, Type, Options1)};
|
||||||
unfold_types(Env, {arg, Attr, Id, Type}, Options) ->
|
unfold_types(Env, {arg, Attr, Id, Type}, Options) ->
|
||||||
{arg, Attr, Id, unfold_types_in_type(Env, Type, Options)};
|
{arg, Attr, Id, unfold_types_in_type(Env, Type, Options)};
|
||||||
unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, Options) ->
|
unfold_types(Env, {type_sig, Ann, Constr, NamedArgs, Args, Ret}, Options) ->
|
||||||
@ -1994,7 +2171,8 @@ unfold_types_in_type(Env, T) ->
|
|||||||
|
|
||||||
unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) ->
|
unfold_types_in_type(Env, {app_t, Ann, Id = {id, _, "map"}, Args = [KeyType0, _]}, Options) ->
|
||||||
Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, Options),
|
Args1 = [KeyType, _] = unfold_types_in_type(Env, Args, Options),
|
||||||
[ type_error({map_in_map_key, KeyType0}) || has_maps(KeyType) ],
|
Ann1 = proplists:get_value(ann, Options, aeso_syntax:get_ann(KeyType0)),
|
||||||
|
[ type_error({map_in_map_key, Ann1, KeyType0}) || has_maps(KeyType) ],
|
||||||
{app_t, Ann, Id, Args1};
|
{app_t, Ann, Id, Args1};
|
||||||
unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) ->
|
unfold_types_in_type(Env, {app_t, Ann, Id, Args}, Options) when ?is_type_id(Id) ->
|
||||||
UnfoldRecords = proplists:get_value(unfold_record_types, Options, false),
|
UnfoldRecords = proplists:get_value(unfold_record_types, Options, false),
|
||||||
@ -2068,8 +2246,13 @@ subst_tvars1(_Env, X) ->
|
|||||||
unify(_, {id, _, "_"}, _, _When) -> true;
|
unify(_, {id, _, "_"}, _, _When) -> true;
|
||||||
unify(_, _, {id, _, "_"}, _When) -> true;
|
unify(_, _, {id, _, "_"}, _When) -> true;
|
||||||
unify(Env, A, B, When) ->
|
unify(Env, A, B, When) ->
|
||||||
A1 = dereference(unfold_types_in_type(Env, A)),
|
Options =
|
||||||
B1 = dereference(unfold_types_in_type(Env, B)),
|
case When of %% Improve source location for map_in_map_key errors
|
||||||
|
{check_expr, E, _, _} -> [{ann, aeso_syntax:get_ann(E)}];
|
||||||
|
_ -> []
|
||||||
|
end,
|
||||||
|
A1 = dereference(unfold_types_in_type(Env, A, Options)),
|
||||||
|
B1 = dereference(unfold_types_in_type(Env, B, Options)),
|
||||||
unify1(Env, A1, B1, When).
|
unify1(Env, A1, B1, When).
|
||||||
|
|
||||||
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) ->
|
unify1(_Env, {uvar, _, R}, {uvar, _, R}, _When) ->
|
||||||
@ -2100,6 +2283,9 @@ unify1(_Env, {qcon, _, Name}, {qcon, _, Name}, _When) ->
|
|||||||
true;
|
true;
|
||||||
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
|
unify1(_Env, {bytes_t, _, Len}, {bytes_t, _, Len}, _When) ->
|
||||||
true;
|
true;
|
||||||
|
unify1(Env, {if_t, _, {id, _, Id}, Then1, Else1}, {if_t, _, {id, _, Id}, Then2, Else2}, When) ->
|
||||||
|
unify(Env, Then1, Then2, When) andalso
|
||||||
|
unify(Env, Else1, Else2, When);
|
||||||
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When)
|
unify1(Env, {fun_t, _, Named1, Args1, Result1}, {fun_t, _, Named2, Args2, Result2}, When)
|
||||||
when length(Args1) == length(Args2) ->
|
when length(Args1) == length(Args2) ->
|
||||||
unify(Env, Named1, Named2, When) andalso
|
unify(Env, Named1, Named2, When) andalso
|
||||||
@ -2161,6 +2347,8 @@ occurs_check1(R, {record_t, Fields}) ->
|
|||||||
occurs_check(R, Fields);
|
occurs_check(R, Fields);
|
||||||
occurs_check1(R, {field_t, _, _, T}) ->
|
occurs_check1(R, {field_t, _, _, T}) ->
|
||||||
occurs_check(R, T);
|
occurs_check(R, T);
|
||||||
|
occurs_check1(R, {if_t, _, _, Then, Else}) ->
|
||||||
|
occurs_check(R, [Then, Else]);
|
||||||
occurs_check1(R, [H | T]) ->
|
occurs_check1(R, [H | T]) ->
|
||||||
occurs_check(R, H) orelse occurs_check(R, T);
|
occurs_check(R, H) orelse occurs_check(R, T);
|
||||||
occurs_check1(_, []) -> false.
|
occurs_check1(_, []) -> false.
|
||||||
@ -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",
|
Msg = io_lib:format("Invalid type\n~s (at ~s)\nThe syntax of tuple types changed in Sophia version 4.0. Did you mean\n~s\n",
|
||||||
[pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]),
|
[pp_type(" ", {args_t, Ann, Ts}), pp_loc(Ann), pp_type(" ", {tuple_t, Ann, Ts})]),
|
||||||
mk_t_err(pos(Ann), Msg);
|
mk_t_err(pos(Ann), Msg);
|
||||||
mk_error({map_in_map_key, KeyType}) ->
|
mk_error({map_in_map_key, Ann, KeyType}) ->
|
||||||
Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]),
|
Msg = io_lib:format("Invalid key type\n~s\n", [pp_type(" ", KeyType)]),
|
||||||
Cxt = "Map keys cannot contain other maps.\n",
|
Cxt = "Map keys cannot contain other maps.\n",
|
||||||
mk_t_err(pos(KeyType), Msg, Cxt);
|
mk_t_err(pos(Ann), Msg, Cxt);
|
||||||
mk_error({cannot_call_init_function, Ann}) ->
|
mk_error({cannot_call_init_function, Ann}) ->
|
||||||
Msg = "The 'init' function is called exclusively by the create contract transaction\n"
|
Msg = "The 'init' function is called exclusively by the create contract transaction\n"
|
||||||
"and cannot be called from the contract code.\n",
|
"and cannot be called from the contract code.\n",
|
||||||
@ -2581,6 +2769,9 @@ mk_error({mixed_record_and_map, Expr}) ->
|
|||||||
Msg = io_lib:format("Mixed record fields and map keys in\n~s",
|
Msg = io_lib:format("Mixed record fields and map keys in\n~s",
|
||||||
[pp_expr(" ", Expr)]),
|
[pp_expr(" ", Expr)]),
|
||||||
mk_t_err(pos(Expr), Msg);
|
mk_t_err(pos(Expr), Msg);
|
||||||
|
mk_error({named_argument_must_be_literal_bool, Name, Arg}) ->
|
||||||
|
Msg = io_lib:format("Invalid '~s' argument\n~s\nIt must be either 'true' or 'false'.", [Name, pp_expr(" ", instantiate(Arg))]),
|
||||||
|
mk_t_err(pos(Arg), Msg);
|
||||||
mk_error(Err) ->
|
mk_error(Err) ->
|
||||||
Msg = io_lib:format("Unknown error: ~p\n", [Err]),
|
Msg = io_lib:format("Unknown error: ~p\n", [Err]),
|
||||||
mk_t_err(pos(0, 0), Msg).
|
mk_t_err(pos(0, 0), Msg).
|
||||||
@ -2599,7 +2790,7 @@ pp_when({check_typesig, Name, Inferred, Given}) ->
|
|||||||
" inferred type: ~s\n"
|
" inferred type: ~s\n"
|
||||||
" given type: ~s\n",
|
" given type: ~s\n",
|
||||||
[Name, pp_loc(Given), pp(instantiate(Inferred)), pp(instantiate(Given))])};
|
[Name, pp_loc(Given), pp(instantiate(Inferred)), pp(instantiate(Given))])};
|
||||||
pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) ->
|
pp_when({infer_app, Fun, NamedArgs, Args, Inferred0, ArgTypes0}) ->
|
||||||
Inferred = instantiate(Inferred0),
|
Inferred = instantiate(Inferred0),
|
||||||
ArgTypes = instantiate(ArgTypes0),
|
ArgTypes = instantiate(ArgTypes0),
|
||||||
{pos(Fun),
|
{pos(Fun),
|
||||||
@ -2608,6 +2799,7 @@ pp_when({infer_app, Fun, Args, Inferred0, ArgTypes0}) ->
|
|||||||
"to arguments\n~s",
|
"to arguments\n~s",
|
||||||
[pp_loc(Fun),
|
[pp_loc(Fun),
|
||||||
pp_typed(" ", Fun, Inferred),
|
pp_typed(" ", Fun, Inferred),
|
||||||
|
[ [pp_expr(" ", NamedArg), "\n"] || NamedArg <- NamedArgs ] ++
|
||||||
[ [pp_typed(" ", Arg, ArgT), "\n"]
|
[ [pp_typed(" ", Arg, ArgT), "\n"]
|
||||||
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
|
|| {Arg, ArgT} <- lists:zip(Args, ArgTypes) ] ])};
|
||||||
pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
|
pp_when({field_constraint, FieldType0, InferredType0, Fld}) ->
|
||||||
@ -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"
|
io_lib:format("when checking rvalue of list comprehension binding at ~s\n~s\n"
|
||||||
"against type \n~s\n",
|
"against type \n~s\n",
|
||||||
[pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)])};
|
[pp_loc(BindExpr), pp_typed(" ", BindExpr, Inferred), pp_type(" ", Expected)])};
|
||||||
|
pp_when({check_named_arg_constraint, C}) ->
|
||||||
|
{id, _, Name} = Arg = C#named_argument_constraint.name,
|
||||||
|
[Type | _] = [ Type || {named_arg_t, _, {id, _, Name1}, Type, _} <- C#named_argument_constraint.args, Name1 == Name ],
|
||||||
|
Err = io_lib:format("when checking named argument\n~s\nagainst inferred type\n~s",
|
||||||
|
[pp_typed(" ", Arg, Type), pp_type(" ", C#named_argument_constraint.type)]),
|
||||||
|
{pos(Arg), Err};
|
||||||
pp_when(unknown) -> {pos(0,0), ""}.
|
pp_when(unknown) -> {pos(0,0), ""}.
|
||||||
|
|
||||||
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
-spec pp_why_record(why_record()) -> {pos(), iolist()}.
|
||||||
@ -2761,6 +2959,8 @@ pp({uvar, _, Ref}) ->
|
|||||||
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
|
["?u" | integer_to_list(erlang:phash2(Ref, 16384)) ];
|
||||||
pp({tvar, _, Name}) ->
|
pp({tvar, _, Name}) ->
|
||||||
Name;
|
Name;
|
||||||
|
pp({if_t, _, Id, Then, Else}) ->
|
||||||
|
["if(", pp([Id, Then, Else]), ")"];
|
||||||
pp({tuple_t, _, []}) ->
|
pp({tuple_t, _, []}) ->
|
||||||
"unit";
|
"unit";
|
||||||
pp({tuple_t, _, Cpts}) ->
|
pp({tuple_t, _, Cpts}) ->
|
||||||
@ -2772,8 +2972,8 @@ pp({app_t, _, T, []}) ->
|
|||||||
pp(T);
|
pp(T);
|
||||||
pp({app_t, _, Type, Args}) ->
|
pp({app_t, _, Type, Args}) ->
|
||||||
[pp(Type), "(", pp(Args), ")"];
|
[pp(Type), "(", pp(Args), ")"];
|
||||||
pp({named_arg_t, _, Name, Type, Default}) ->
|
pp({named_arg_t, _, Name, Type, _Default}) ->
|
||||||
[pp(Name), " : ", pp(Type), " = ", pp(Default)];
|
[pp(Name), " : ", pp(Type)];
|
||||||
pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
|
pp({fun_t, _, Named = {uvar, _, _}, As, B}) ->
|
||||||
["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
|
["(", pp(Named), " | ", pp(As), ") => ", pp(B)];
|
||||||
pp({fun_t, _, Named, As, B}) when is_list(Named) ->
|
pp({fun_t, _, Named, As, B}) when is_list(Named) ->
|
||||||
|
@ -36,7 +36,14 @@
|
|||||||
bits_intersection | bits_union | bits_difference |
|
bits_intersection | bits_union | bits_difference |
|
||||||
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
contract_to_address | address_to_contract | crypto_verify_sig | crypto_verify_sig_secp256k1 |
|
||||||
crypto_sha3 | crypto_sha256 | crypto_blake2b |
|
crypto_sha3 | crypto_sha256 | crypto_blake2b |
|
||||||
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1.
|
crypto_ecverify_secp256k1 | crypto_ecrecover_secp256k1 |
|
||||||
|
mcl_bls12_381_g1_neg | mcl_bls12_381_g1_norm | mcl_bls12_381_g1_valid |
|
||||||
|
mcl_bls12_381_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul |
|
||||||
|
mcl_bls12_381_g2_neg | mcl_bls12_381_g2_norm | mcl_bls12_381_g2_valid |
|
||||||
|
mcl_bls12_381_g2_is_zero | mcl_bls12_381_g2_add | mcl_bls12_381_g2_mul |
|
||||||
|
mcl_bls12_381_gt_inv | mcl_bls12_381_gt_add | mcl_bls12_381_gt_mul | mcl_bls12_381_gt_pow |
|
||||||
|
mcl_bls12_381_gt_is_one | mcl_bls12_381_pairing | mcl_bls12_381_miller_loop | mcl_bls12_381_final_exp |
|
||||||
|
mcl_bls12_381_int_to_fr | mcl_bls12_381_int_to_fp | mcl_bls12_381_fr_to_int | mcl_bls12_381_fp_to_int.
|
||||||
|
|
||||||
-type flit() :: {int, integer()}
|
-type flit() :: {int, integer()}
|
||||||
| {string, binary()}
|
| {string, binary()}
|
||||||
@ -171,13 +178,43 @@ ast_to_fcode(Code, Options) ->
|
|||||||
|
|
||||||
-spec init_env([option()]) -> env().
|
-spec init_env([option()]) -> env().
|
||||||
init_env(Options) ->
|
init_env(Options) ->
|
||||||
|
ChainTxArities = [3, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 0],
|
||||||
#{ type_env => init_type_env(),
|
#{ type_env => init_type_env(),
|
||||||
fun_env => #{},
|
fun_env => #{},
|
||||||
builtins => builtins(),
|
builtins => builtins(),
|
||||||
con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] },
|
con_env => #{["None"] => #con_tag{ tag = 0, arities = [0, 1] },
|
||||||
["Some"] => #con_tag{ tag = 1, arities = [0, 1] },
|
["Some"] => #con_tag{ tag = 1, arities = [0, 1] },
|
||||||
["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] },
|
["RelativeTTL"] => #con_tag{ tag = 0, arities = [1, 1] },
|
||||||
["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] }
|
["FixedTTL"] => #con_tag{ tag = 1, arities = [1, 1] },
|
||||||
|
["AENS", "AccountPt"] => #con_tag{ tag = 0, arities = [1, 1, 1, 1] },
|
||||||
|
["AENS", "OraclePt"] => #con_tag{ tag = 1, arities = [1, 1, 1, 1] },
|
||||||
|
["AENS", "ContractPt"] => #con_tag{ tag = 2, arities = [1, 1, 1, 1] },
|
||||||
|
["AENS", "ChannelPt"] => #con_tag{ tag = 3, arities = [1, 1, 1, 1] },
|
||||||
|
["AENS", "Name"] => #con_tag{ tag = 0, arities = [3] },
|
||||||
|
["Chain", "GAMetaTx"] => #con_tag{ tag = 0, arities = [2] },
|
||||||
|
["Chain", "PayingForTx"] => #con_tag{ tag = 0, arities = [2] },
|
||||||
|
["Chain", "SpendTx"] => #con_tag{ tag = 0, arities = ChainTxArities },
|
||||||
|
["Chain", "OracleRegisterTx"] => #con_tag{ tag = 1, arities = ChainTxArities },
|
||||||
|
["Chain", "OracleQueryTx"] => #con_tag{ tag = 2, arities = ChainTxArities },
|
||||||
|
["Chain", "OracleResponseTx"] => #con_tag{ tag = 3, arities = ChainTxArities },
|
||||||
|
["Chain", "OracleExtendTx"] => #con_tag{ tag = 4, arities = ChainTxArities },
|
||||||
|
["Chain", "NamePreclaimTx"] => #con_tag{ tag = 5, arities = ChainTxArities },
|
||||||
|
["Chain", "NameClaimTx"] => #con_tag{ tag = 6, arities = ChainTxArities },
|
||||||
|
["Chain", "NameUpdateTx"] => #con_tag{ tag = 7, arities = ChainTxArities },
|
||||||
|
["Chain", "NameRevokeTx"] => #con_tag{ tag = 8, arities = ChainTxArities },
|
||||||
|
["Chain", "NameTransferTx"] => #con_tag{ tag = 9, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelCreateTx"] => #con_tag{ tag = 10, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelDepositTx"] => #con_tag{ tag = 11, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelWithdrawTx"] => #con_tag{ tag = 12, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelForceProgressTx"] => #con_tag{ tag = 13, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelCloseMutualTx"] => #con_tag{ tag = 14, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelCloseSoloTx"] => #con_tag{ tag = 15, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelSlashTx"] => #con_tag{ tag = 16, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelSettleTx"] => #con_tag{ tag = 17, arities = ChainTxArities },
|
||||||
|
["Chain", "ChannelSnapshotSoloTx"] => #con_tag{ tag = 18, arities = ChainTxArities },
|
||||||
|
["Chain", "ContractCreateTx"] => #con_tag{ tag = 19, arities = ChainTxArities },
|
||||||
|
["Chain", "ContractCallTx"] => #con_tag{ tag = 20, arities = ChainTxArities },
|
||||||
|
["Chain", "GAAttachTx"] => #con_tag{ tag = 21, arities = ChainTxArities }
|
||||||
},
|
},
|
||||||
options => Options,
|
options => Options,
|
||||||
functions => #{} }.
|
functions => #{} }.
|
||||||
@ -194,18 +231,25 @@ builtins() ->
|
|||||||
{["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]},
|
{["Contract"], [{"address", none}, {"balance", none}, {"creator", none}]},
|
||||||
{["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none},
|
{["Call"], [{"origin", none}, {"caller", none}, {"value", none}, {"gas_price", none},
|
||||||
{"gas_left", 0}]},
|
{"gas_left", 0}]},
|
||||||
{["Oracle"], [{"register", 4}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
|
{["Oracle"], [{"register", 4}, {"expiry", 1}, {"query_fee", 1}, {"query", 5}, {"get_question", 2},
|
||||||
{"respond", 4}, {"extend", 3}, {"get_answer", 2},
|
{"respond", 4}, {"extend", 3}, {"get_answer", 2},
|
||||||
{"check", 1}, {"check_query", 2}]},
|
{"check", 1}, {"check_query", 2}]},
|
||||||
{["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
|
{["AENS"], [{"resolve", 2}, {"preclaim", 3}, {"claim", 5}, {"transfer", 4},
|
||||||
{"revoke", 3}]},
|
{"revoke", 3}, {"update", 6}, {"lookup", 1}]},
|
||||||
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
|
{["Map"], [{"from_list", 1}, {"to_list", 1}, {"lookup", 2},
|
||||||
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
{"lookup_default", 3}, {"delete", 2}, {"member", 2}, {"size", 1}]},
|
||||||
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
{["Crypto"], [{"verify_sig", 3}, {"verify_sig_secp256k1", 3},
|
||||||
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
{"ecverify_secp256k1", 3}, {"ecrecover_secp256k1", 2},
|
||||||
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
||||||
{["Auth"], [{"tx_hash", none}]},
|
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
||||||
{["String"], [{"length", 1}, {"concat", 2}, {"sha3", 1}, {"sha256", 1}, {"blake2b", 1}]},
|
{"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2},
|
||||||
|
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
|
||||||
|
{"pairing", 2}, {"miller_loop", 2}, {"final_exp", 1},
|
||||||
|
{"int_to_fr", 1}, {"int_to_fp", 1}, {"fr_to_int", 1}, {"fp_to_int", 1}]},
|
||||||
|
{["StringInternal"], [{"length", 1}, {"concat", 2}, {"to_list", 1}, {"from_list", 1},
|
||||||
|
{"sha3", 1}, {"sha256", 1}, {"blake2b", 1}, {"to_lower", 1}, {"to_upper", 1}]},
|
||||||
|
{["Char"], [{"to_int", 1}, {"from_int", 1}]},
|
||||||
|
{["Auth"], [{"tx_hash", none}, {"tx", none}]},
|
||||||
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
{["Bits"], [{"set", 2}, {"clear", 2}, {"test", 2}, {"sum", 1}, {"intersection", 2},
|
||||||
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
{"union", 2}, {"difference", 2}, {"none", none}, {"all", none}]},
|
||||||
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
{["Bytes"], [{"to_int", 1}, {"to_str", 1}, {"concat", 2}, {"split", 1}]},
|
||||||
@ -224,20 +268,32 @@ state_layout(Env) -> maps:get(state_layout, Env, {reg, 1}).
|
|||||||
|
|
||||||
-spec init_type_env() -> type_env().
|
-spec init_type_env() -> type_env().
|
||||||
init_type_env() ->
|
init_type_env() ->
|
||||||
#{ ["int"] => ?type(integer),
|
BaseTx = {variant, [[address, integer, string], [], [], [], [], [], [string],
|
||||||
["bool"] => ?type(boolean),
|
[hash], [hash], [address, hash], [address],
|
||||||
["bits"] => ?type(bits),
|
[address, integer], [address, integer], [address],
|
||||||
["char"] => ?type(integer),
|
[address], [address], [address], [address], [address],
|
||||||
["string"] => ?type(string),
|
[integer], [address, integer], []]},
|
||||||
["address"] => ?type(address),
|
#{ ["int"] => ?type(integer),
|
||||||
["hash"] => ?type(hash),
|
["bool"] => ?type(boolean),
|
||||||
["signature"] => ?type(signature),
|
["bits"] => ?type(bits),
|
||||||
["oracle"] => ?type(Q, R, {oracle, Q, R}),
|
["char"] => ?type(integer),
|
||||||
["oracle_query"] => ?type(_, _, oracle_query),
|
["string"] => ?type(string),
|
||||||
["list"] => ?type(T, {list, T}),
|
["address"] => ?type(address),
|
||||||
["map"] => ?type(K, V, {map, K, V}),
|
["hash"] => ?type(hash),
|
||||||
["option"] => ?type(T, {variant, [[], [T]]}),
|
["signature"] => ?type(signature),
|
||||||
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]})
|
["oracle"] => ?type(Q, R, {oracle, Q, R}),
|
||||||
|
["oracle_query"] => ?type(_, _, oracle_query), %% TODO: not in Fate
|
||||||
|
["list"] => ?type(T, {list, T}),
|
||||||
|
["map"] => ?type(K, V, {map, K, V}),
|
||||||
|
["option"] => ?type(T, {variant, [[], [T]]}),
|
||||||
|
["Chain", "ttl"] => ?type({variant, [[integer], [integer]]}),
|
||||||
|
["AENS", "pointee"] => ?type({variant, [[address], [address], [address], [address]]}),
|
||||||
|
["AENS", "name"] => ?type({variant, [[address, {variant, [[integer], [integer]]}, {map, string, {variant, [[address], [address], [address], [address]]}}]]}),
|
||||||
|
["Chain", "ga_meta_tx"] => ?type({variant, [[address, integer]]}),
|
||||||
|
["Chain", "paying_for_tx"] => ?type({variant, [[address, integer]]}),
|
||||||
|
["Chain", "base_tx"] => ?type(BaseTx),
|
||||||
|
["MCL_BLS12_381", "fr"] => ?type({bytes, 32}),
|
||||||
|
["MCL_BLS12_381", "fp"] => ?type({bytes, 48})
|
||||||
}.
|
}.
|
||||||
|
|
||||||
is_no_code(Env) ->
|
is_no_code(Env) ->
|
||||||
@ -411,6 +467,8 @@ type_to_fcode(Env, Sub, {fun_t, _, Named, Args, Res}) ->
|
|||||||
FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named],
|
FNamed = [type_to_fcode(Env, Sub, Arg) || {named_arg_t, _, _, Arg, _} <- Named],
|
||||||
FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args],
|
FArgs = [type_to_fcode(Env, Sub, Arg) || Arg <- Args],
|
||||||
{function, FNamed ++ FArgs, type_to_fcode(Env, Sub, Res)};
|
{function, FNamed ++ FArgs, type_to_fcode(Env, Sub, Res)};
|
||||||
|
type_to_fcode(Env, Sub, {if_t, _, _, _, Else}) ->
|
||||||
|
type_to_fcode(Env, Sub, Else); %% Hacky: this is only for remote calls, in which case we want the unprotected type
|
||||||
type_to_fcode(_Env, _Sub, Type) ->
|
type_to_fcode(_Env, _Sub, Type) ->
|
||||||
error({todo, Type}).
|
error({todo, Type}).
|
||||||
|
|
||||||
@ -985,12 +1043,21 @@ stmts_to_fcode(Env, [Expr | Stmts]) ->
|
|||||||
|
|
||||||
op_builtins() ->
|
op_builtins() ->
|
||||||
[map_from_list, map_to_list, map_delete, map_member, map_size,
|
[map_from_list, map_to_list, map_delete, map_member, map_size,
|
||||||
string_length, string_concat, string_sha3, string_sha256, string_blake2b,
|
stringinternal_length, stringinternal_concat, stringinternal_to_list, stringinternal_from_list,
|
||||||
|
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
||||||
|
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
|
||||||
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
bits_set, bits_clear, bits_test, bits_sum, bits_intersection, bits_union,
|
||||||
bits_difference, int_to_str, address_to_str, crypto_verify_sig,
|
bits_difference, int_to_str, address_to_str, crypto_verify_sig,
|
||||||
address_to_contract,
|
address_to_contract,
|
||||||
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
|
crypto_verify_sig_secp256k1, crypto_sha3, crypto_sha256, crypto_blake2b,
|
||||||
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1
|
crypto_ecverify_secp256k1, crypto_ecrecover_secp256k1,
|
||||||
|
mcl_bls12_381_g1_neg, mcl_bls12_381_g1_norm, mcl_bls12_381_g1_valid,
|
||||||
|
mcl_bls12_381_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
|
||||||
|
mcl_bls12_381_g2_neg, mcl_bls12_381_g2_norm, mcl_bls12_381_g2_valid,
|
||||||
|
mcl_bls12_381_g2_is_zero, mcl_bls12_381_g2_add, mcl_bls12_381_g2_mul,
|
||||||
|
mcl_bls12_381_gt_inv, mcl_bls12_381_gt_add, mcl_bls12_381_gt_mul, mcl_bls12_381_gt_pow,
|
||||||
|
mcl_bls12_381_gt_is_one, mcl_bls12_381_pairing, mcl_bls12_381_miller_loop, mcl_bls12_381_final_exp,
|
||||||
|
mcl_bls12_381_int_to_fr, mcl_bls12_381_int_to_fp, mcl_bls12_381_fr_to_int, mcl_bls12_381_fp_to_int
|
||||||
].
|
].
|
||||||
|
|
||||||
set_state({reg, R}, Val) ->
|
set_state({reg, R}, Val) ->
|
||||||
@ -1151,8 +1218,8 @@ lambda_lift_expr(Layout, UExpr) when element(1, UExpr) == def_u; element(1, UExp
|
|||||||
lambda_lift_expr(Layout, {remote_u, ArgsT, RetT, Ct, F}) ->
|
lambda_lift_expr(Layout, {remote_u, ArgsT, RetT, Ct, F}) ->
|
||||||
FVs = free_vars(Ct),
|
FVs = free_vars(Ct),
|
||||||
Ct1 = lambda_lift_expr(Layout, Ct),
|
Ct1 = lambda_lift_expr(Layout, Ct),
|
||||||
GasAndValueArgs = 2,
|
NamedArgCount = 3,
|
||||||
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + GasAndValueArgs) ],
|
Xs = [ lists:concat(["arg", I]) || I <- lists:seq(1, length(ArgsT) + NamedArgCount) ],
|
||||||
Args = [{var, X} || X <- Xs],
|
Args = [{var, X} || X <- Xs],
|
||||||
make_closure(FVs, Xs, {remote, ArgsT, RetT, Ct1, F, Args});
|
make_closure(FVs, Xs, {remote, ArgsT, RetT, Ct1, F, Args});
|
||||||
lambda_lift_expr(Layout, Expr) ->
|
lambda_lift_expr(Layout, Expr) ->
|
||||||
|
@ -469,6 +469,7 @@ is_builtin_fun({qid, _, ["AENS", "preclaim"]}, _Icode) ->
|
|||||||
is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["AENS", "claim"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["AENS", "transfer"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["AENS", "revoke"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["AENS", "revoke"]}, _Icode) -> true;
|
||||||
|
is_builtin_fun({qid, _, ["AENS", "update"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["Map", "lookup"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["Map", "lookup"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["Map", "lookup_default"]}, _Icode) -> true;
|
||||||
is_builtin_fun({qid, _, ["Map", "member"]}, _Icode) -> true;
|
is_builtin_fun({qid, _, ["Map", "member"]}, _Icode) -> true;
|
||||||
@ -624,6 +625,12 @@ builtin_code(_, {qid, _, ["AENS", "revoke"]}, Args, _, _, Icode) ->
|
|||||||
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
|
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(Sign, Icode)],
|
||||||
[word, word, sign_t()], {tuple, []});
|
[word, word, sign_t()], {tuple, []});
|
||||||
|
|
||||||
|
builtin_code(_, {qid, _, ["AENS", "update"]}, Args, _, _, Icode) ->
|
||||||
|
{Sign, [Addr, Name, TTL, ClientTTL, Pointers]} = get_signature_arg(Args),
|
||||||
|
prim_call(?PRIM_CALL_AENS_UPDATE, #integer{value = 0},
|
||||||
|
[ast_body(Addr, Icode), ast_body(Name, Icode), ast_body(TTL, Icode), ast_body(ClientTTL, Icode), ast_body(Pointers, Icode), ast_body(Sign, Icode)],
|
||||||
|
[word, string, word, word, word, sign_t()], {tuple, []});
|
||||||
|
|
||||||
%% -- Maps
|
%% -- Maps
|
||||||
%% -- lookup functions
|
%% -- lookup functions
|
||||||
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
|
builtin_code(_, {qid, _, ["Map", "lookup"]}, [Key, Map], _, _, Icode) ->
|
||||||
@ -927,11 +934,16 @@ ast_typerep1({variant_t, Cons}, Icode) ->
|
|||||||
{variant, [ begin
|
{variant, [ begin
|
||||||
{constr_t, _, _, Args} = Con,
|
{constr_t, _, _, Args} = Con,
|
||||||
[ ast_typerep1(Arg, Icode) || Arg <- Args ]
|
[ ast_typerep1(Arg, Icode) || Arg <- Args ]
|
||||||
end || Con <- Cons ]}.
|
end || Con <- Cons ]};
|
||||||
|
ast_typerep1({if_t, _, _, _, Else}, Icode) ->
|
||||||
|
ast_typerep1(Else, Icode). %% protected remote calls are not in AEVM
|
||||||
|
|
||||||
ttl_t(Icode) ->
|
ttl_t(Icode) ->
|
||||||
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
ast_typerep({qid, [], ["Chain", "ttl"]}, Icode).
|
||||||
|
|
||||||
|
%% pointee_t(Icode) ->
|
||||||
|
%% ast_typerep({qid, [], ["AENS", "pointee"]}, Icode).
|
||||||
|
|
||||||
sign_t() -> bytes_t(64).
|
sign_t() -> bytes_t(64).
|
||||||
bytes_t(Len) when Len =< 32 -> word;
|
bytes_t(Len) when Len =< 32 -> word;
|
||||||
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
|
bytes_t(Len) -> {tuple, lists:duplicate((31 + Len) div 32, word)}.
|
||||||
|
@ -302,18 +302,27 @@ to_scode1(Env, {funcall, Fun, Args}) ->
|
|||||||
to_scode1(Env, {builtin, B, Args}) ->
|
to_scode1(Env, {builtin, B, Args}) ->
|
||||||
builtin_to_scode(Env, B, Args);
|
builtin_to_scode(Env, B, Args);
|
||||||
|
|
||||||
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value | Args]}) ->
|
to_scode1(Env, {remote, ArgsT, RetT, Ct, Fun, [Gas, Value, Protected | Args]}) ->
|
||||||
Lbl = make_function_id(Fun),
|
Lbl = make_function_id(Fun),
|
||||||
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
{ArgTypes, RetType0} = typesig_to_scode([{"_", T} || T <- ArgsT], RetT),
|
||||||
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
ArgType = ?i(aeb_fate_data:make_typerep({tuple, ArgTypes})),
|
||||||
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
RetType = ?i(aeb_fate_data:make_typerep(RetType0)),
|
||||||
case Gas of
|
case Protected of
|
||||||
{builtin, call_gas_left, _} ->
|
{lit, {bool, false}} ->
|
||||||
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
case Gas of
|
||||||
call_to_scode(Env, Call, [Ct, Value | Args]);
|
{builtin, call_gas_left, _} ->
|
||||||
|
Call = aeb_fate_ops:call_r(?a, Lbl, ArgType, RetType, ?a),
|
||||||
|
call_to_scode(Env, Call, [Ct, Value | Args]);
|
||||||
|
_ ->
|
||||||
|
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
||||||
|
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
||||||
|
end;
|
||||||
|
{lit, {bool, true}} ->
|
||||||
|
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?i(true)),
|
||||||
|
call_to_scode(Env, Call, [Ct, Value, Gas | Args]);
|
||||||
_ ->
|
_ ->
|
||||||
Call = aeb_fate_ops:call_gr(?a, Lbl, ArgType, RetType, ?a, ?a),
|
Call = aeb_fate_ops:call_pgr(?a, Lbl, ArgType, RetType, ?a, ?a, ?a),
|
||||||
call_to_scode(Env, Call, [Ct, Value, Gas | Args])
|
call_to_scode(Env, Call, [Ct, Value, Gas, Protected | Args])
|
||||||
end;
|
end;
|
||||||
|
|
||||||
to_scode1(_Env, {get_state, Reg}) ->
|
to_scode1(_Env, {get_state, Reg}) ->
|
||||||
@ -498,6 +507,8 @@ builtin_to_scode(_Env, call_gas_left, []) ->
|
|||||||
[aeb_fate_ops:gas(?a)];
|
[aeb_fate_ops:gas(?a)];
|
||||||
builtin_to_scode(Env, oracle_register, [_Sign,_Account,_QFee,_TTL,_QType,_RType] = Args) ->
|
builtin_to_scode(Env, oracle_register, [_Sign,_Account,_QFee,_TTL,_QType,_RType] = Args) ->
|
||||||
call_to_scode(Env, aeb_fate_ops:oracle_register(?a, ?a, ?a, ?a, ?a, ?a, ?a), Args);
|
call_to_scode(Env, aeb_fate_ops:oracle_register(?a, ?a, ?a, ?a, ?a, ?a, ?a), Args);
|
||||||
|
builtin_to_scode(Env, oracle_expiry, [_Oracle] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:oracle_expiry(?a, ?a), Args);
|
||||||
builtin_to_scode(Env, oracle_query_fee, [_Oracle] = Args) ->
|
builtin_to_scode(Env, oracle_query_fee, [_Oracle] = Args) ->
|
||||||
call_to_scode(Env, aeb_fate_ops:oracle_query_fee(?a, ?a), Args);
|
call_to_scode(Env, aeb_fate_ops:oracle_query_fee(?a, ?a), Args);
|
||||||
builtin_to_scode(Env, oracle_query, [_Oracle, _Question, _QFee, _QTTL, _RTTL, _QType, _RType] = Args) ->
|
builtin_to_scode(Env, oracle_query, [_Oracle, _Question, _QFee, _QTTL, _RTTL, _QType, _RType] = Args) ->
|
||||||
@ -536,8 +547,15 @@ builtin_to_scode(Env, aens_transfer, [_Sign, _From, _To, _Name] = Args) ->
|
|||||||
builtin_to_scode(Env, aens_revoke, [_Sign, _Account, _Name] = Args) ->
|
builtin_to_scode(Env, aens_revoke, [_Sign, _Account, _Name] = Args) ->
|
||||||
call_to_scode(Env, [aeb_fate_ops:aens_revoke(?a, ?a, ?a),
|
call_to_scode(Env, [aeb_fate_ops:aens_revoke(?a, ?a, ?a),
|
||||||
tuple(0)], Args);
|
tuple(0)], Args);
|
||||||
|
builtin_to_scode(Env, aens_update, [_Sign, _Account, _NameString, _TTL, _ClientTTL, _Pointers] = Args) ->
|
||||||
|
call_to_scode(Env, [aeb_fate_ops:aens_update(?a, ?a, ?a, ?a, ?a, ?a),
|
||||||
|
tuple(0)], Args);
|
||||||
|
builtin_to_scode(Env, aens_lookup, [_Name] = Args) ->
|
||||||
|
call_to_scode(Env, aeb_fate_ops:aens_lookup(?a, ?a), Args);
|
||||||
builtin_to_scode(_Env, auth_tx_hash, []) ->
|
builtin_to_scode(_Env, auth_tx_hash, []) ->
|
||||||
[aeb_fate_ops:auth_tx_hash(?a)].
|
[aeb_fate_ops:auth_tx_hash(?a)];
|
||||||
|
builtin_to_scode(_Env, auth_tx, []) ->
|
||||||
|
[aeb_fate_ops:auth_tx(?a)].
|
||||||
|
|
||||||
%% -- Operators --
|
%% -- Operators --
|
||||||
|
|
||||||
@ -564,8 +582,14 @@ op_to_scode(map_to_list) -> aeb_fate_ops:map_to_list(?a, ?a);
|
|||||||
op_to_scode(map_delete) -> aeb_fate_ops:map_delete(?a, ?a, ?a);
|
op_to_scode(map_delete) -> aeb_fate_ops:map_delete(?a, ?a, ?a);
|
||||||
op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
|
op_to_scode(map_member) -> aeb_fate_ops:map_member(?a, ?a, ?a);
|
||||||
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
|
op_to_scode(map_size) -> aeb_fate_ops:map_size_(?a, ?a);
|
||||||
op_to_scode(string_length) -> aeb_fate_ops:str_length(?a, ?a);
|
op_to_scode(stringinternal_length) -> aeb_fate_ops:str_length(?a, ?a);
|
||||||
op_to_scode(string_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
|
op_to_scode(stringinternal_concat) -> aeb_fate_ops:str_join(?a, ?a, ?a);
|
||||||
|
op_to_scode(stringinternal_to_list) -> aeb_fate_ops:str_to_list(?a, ?a);
|
||||||
|
op_to_scode(stringinternal_from_list) -> aeb_fate_ops:str_from_list(?a, ?a);
|
||||||
|
op_to_scode(stringinternal_to_lower) -> aeb_fate_ops:str_to_lower(?a, ?a);
|
||||||
|
op_to_scode(stringinternal_to_upper) -> aeb_fate_ops:str_to_upper(?a, ?a);
|
||||||
|
op_to_scode(char_to_int) -> aeb_fate_ops:char_to_int(?a, ?a);
|
||||||
|
op_to_scode(char_from_int) -> aeb_fate_ops:char_from_int(?a, ?a);
|
||||||
op_to_scode(bits_set) -> aeb_fate_ops:bits_set(?a, ?a, ?a);
|
op_to_scode(bits_set) -> aeb_fate_ops:bits_set(?a, ?a, ?a);
|
||||||
op_to_scode(bits_clear) -> aeb_fate_ops:bits_clear(?a, ?a, ?a);
|
op_to_scode(bits_clear) -> aeb_fate_ops:bits_clear(?a, ?a, ?a);
|
||||||
op_to_scode(bits_test) -> aeb_fate_ops:bits_test(?a, ?a, ?a);
|
op_to_scode(bits_test) -> aeb_fate_ops:bits_test(?a, ?a, ?a);
|
||||||
@ -584,9 +608,33 @@ op_to_scode(crypto_ecrecover_secp256k1) -> aeb_fate_ops:ecrecover_secp256k1(?a,
|
|||||||
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(crypto_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(crypto_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
op_to_scode(crypto_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
op_to_scode(string_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(string_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(string_blake2b) -> aeb_fate_ops:blake2b(?a, ?a).
|
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_neg) -> aeb_fate_ops:bls12_381_g1_neg(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_norm) -> aeb_fate_ops:bls12_381_g1_norm(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_valid) -> aeb_fate_ops:bls12_381_g1_valid(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_is_zero) -> aeb_fate_ops:bls12_381_g1_is_zero(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_add) -> aeb_fate_ops:bls12_381_g1_add(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g1_mul) -> aeb_fate_ops:bls12_381_g1_mul(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_neg) -> aeb_fate_ops:bls12_381_g2_neg(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_norm) -> aeb_fate_ops:bls12_381_g2_norm(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_valid) -> aeb_fate_ops:bls12_381_g2_valid(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_is_zero) -> aeb_fate_ops:bls12_381_g2_is_zero(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_add) -> aeb_fate_ops:bls12_381_g2_add(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_g2_mul) -> aeb_fate_ops:bls12_381_g2_mul(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_inv) -> aeb_fate_ops:bls12_381_gt_inv(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_add) -> aeb_fate_ops:bls12_381_gt_add(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_mul) -> aeb_fate_ops:bls12_381_gt_mul(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_pow) -> aeb_fate_ops:bls12_381_gt_pow(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_gt_is_one) -> aeb_fate_ops:bls12_381_gt_is_one(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_pairing) -> aeb_fate_ops:bls12_381_pairing(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_miller_loop) -> aeb_fate_ops:bls12_381_miller_loop(?a, ?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_final_exp) -> aeb_fate_ops:bls12_381_final_exp(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_int_to_fr) -> aeb_fate_ops:bls12_381_int_to_fr(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_int_to_fp) -> aeb_fate_ops:bls12_381_int_to_fp(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_fr_to_int) -> aeb_fate_ops:bls12_381_fr_to_int(?a, ?a);
|
||||||
|
op_to_scode(mcl_bls12_381_fp_to_int) -> aeb_fate_ops:bls12_381_fp_to_int(?a, ?a).
|
||||||
|
|
||||||
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
|
%% PUSH and STORE ?a are the same, so we use STORE to make optimizations
|
||||||
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
%% easier, and specialize to PUSH (which is cheaper) at the end.
|
||||||
@ -734,6 +782,7 @@ attributes(I) ->
|
|||||||
{'CALL', A} -> Impure(?a, [A]);
|
{'CALL', A} -> Impure(?a, [A]);
|
||||||
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
{'CALL_R', A, _, B, C, D} -> Impure(?a, [A, B, C, D]);
|
||||||
{'CALL_GR', A, _, B, C, D, E} -> Impure(?a, [A, B, C, D, E]);
|
{'CALL_GR', A, _, B, C, D, E} -> Impure(?a, [A, B, C, D, E]);
|
||||||
|
{'CALL_PGR', A, _, B, C, D, E, F} -> Impure(?a, [A, B, C, D, E, F]);
|
||||||
{'CALL_T', A} -> Impure(pc, [A]);
|
{'CALL_T', A} -> Impure(pc, [A]);
|
||||||
{'CALL_VALUE', A} -> Pure(A, []);
|
{'CALL_VALUE', A} -> Pure(A, []);
|
||||||
{'JUMP', _} -> Impure(pc, []);
|
{'JUMP', _} -> Impure(pc, []);
|
||||||
@ -815,6 +864,7 @@ attributes(I) ->
|
|||||||
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
{'CONTRACT_TO_ADDRESS', A, B} -> Pure(A, [B]);
|
||||||
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
{'ADDRESS_TO_CONTRACT', A, B} -> Pure(A, [B]);
|
||||||
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
||||||
|
{'AUTH_TX', A} -> Pure(A, []);
|
||||||
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
{'BYTES_TO_STR', A, B} -> Pure(A, [B]);
|
||||||
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
|
{'BYTES_CONCAT', A, B, C} -> Pure(A, [B, C]);
|
||||||
@ -853,12 +903,44 @@ attributes(I) ->
|
|||||||
{'ORACLE_GET_ANSWER', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
|
{'ORACLE_GET_ANSWER', A, B, C, D, E} -> Pure(A, [B, C, D, E]);
|
||||||
{'ORACLE_GET_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]);
|
{'ORACLE_GET_QUESTION', A, B, C, D, E}-> Pure(A, [B, C, D, E]);
|
||||||
{'ORACLE_QUERY_FEE', A, B} -> Pure(A, [B]);
|
{'ORACLE_QUERY_FEE', A, B} -> Pure(A, [B]);
|
||||||
{'AENS_RESOLVE', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'ORACLE_EXPIRY', A, B} -> Impure(A, [B]);
|
||||||
|
{'AENS_RESOLVE', A, B, C, D} -> Impure(A, [B, C, D]);
|
||||||
{'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]);
|
{'AENS_PRECLAIM', A, B, C} -> Impure(none, [A, B, C]);
|
||||||
{'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]);
|
{'AENS_CLAIM', A, B, C, D, E} -> Impure(none, [A, B, C, D, E]);
|
||||||
'AENS_UPDATE' -> Impure(none, []);%% TODO
|
{'AENS_UPDATE', A, B, C, D, E, F} -> Impure(none, [A, B, C, D, E, F]);
|
||||||
{'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]);
|
{'AENS_TRANSFER', A, B, C, D} -> Impure(none, [A, B, C, D]);
|
||||||
{'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]);
|
{'AENS_REVOKE', A, B, C} -> Impure(none, [A, B, C]);
|
||||||
|
{'AENS_LOOKUP', A, B} -> Impure(A, [B]);
|
||||||
|
{'BLS12_381_G1_NEG', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G1_NORM', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G1_VALID', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G1_IS_ZERO', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G1_ADD', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_G1_MUL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_G2_NEG', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G2_NORM', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G2_VALID', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G2_IS_ZERO', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_G2_ADD', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_G2_MUL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_GT_INV', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_GT_ADD', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_GT_MUL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_GT_POW', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_GT_IS_ONE', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_PAIRING', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_MILLER_LOOP', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BLS12_381_FINAL_EXP', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_INT_TO_FR', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_INT_TO_FP', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_FR_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
|
{'BLS12_381_FP_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
|
{'STR_TO_LIST', A, B} -> Pure(A, [B]);
|
||||||
|
{'STR_FROM_LIST', A, B} -> Pure(A, [B]);
|
||||||
|
{'STR_TO_UPPER', A, B} -> Pure(A, [B]);
|
||||||
|
{'STR_TO_LOWER', A, B} -> Pure(A, [B]);
|
||||||
|
{'CHAR_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
|
{'CHAR_FROM_INT', A, B} -> Pure(A, [B]);
|
||||||
{'ABORT', A} -> Impure(pc, A);
|
{'ABORT', A} -> Impure(pc, A);
|
||||||
{'EXIT', A} -> Impure(pc, A);
|
{'EXIT', A} -> Impure(pc, A);
|
||||||
'NOP' -> Pure(none, [])
|
'NOP' -> Pure(none, [])
|
||||||
@ -1611,6 +1693,7 @@ split_calls(Ref, [], Acc, Blocks) ->
|
|||||||
split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
|
split_calls(Ref, [I | Code], Acc, Blocks) when element(1, I) == 'CALL';
|
||||||
element(1, I) == 'CALL_R';
|
element(1, I) == 'CALL_R';
|
||||||
element(1, I) == 'CALL_GR';
|
element(1, I) == 'CALL_GR';
|
||||||
|
element(1, I) == 'CALL_PGR';
|
||||||
element(1, I) == 'jumpif' ->
|
element(1, I) == 'jumpif' ->
|
||||||
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
|
split_calls(make_ref(), Code, [], [{Ref, lists:reverse([I | Acc])} | Blocks]);
|
||||||
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
|
split_calls(Ref, [{'ABORT', _} = I | _Code], Acc, Blocks) ->
|
||||||
|
@ -73,28 +73,33 @@ new(Options) ->
|
|||||||
|
|
||||||
builtin_types() ->
|
builtin_types() ->
|
||||||
Word = fun([]) -> word end,
|
Word = fun([]) -> word end,
|
||||||
#{ "bool" => Word
|
#{ "bool" => Word
|
||||||
, "int" => Word
|
, "int" => Word
|
||||||
, "char" => Word
|
, "char" => Word
|
||||||
, "bits" => Word
|
, "bits" => Word
|
||||||
, "string" => fun([]) -> string end
|
, "string" => fun([]) -> string end
|
||||||
, "address" => Word
|
, "address" => Word
|
||||||
, "hash" => Word
|
, "hash" => Word
|
||||||
, "unit" => fun([]) -> {tuple, []} end
|
, "unit" => fun([]) -> {tuple, []} end
|
||||||
, "signature" => fun([]) -> {tuple, [word, word]} end
|
, "signature" => fun([]) -> {tuple, [word, word]} end
|
||||||
, "oracle" => fun([_, _]) -> word end
|
, "oracle" => fun([_, _]) -> word end
|
||||||
, "oracle_query" => fun([_, _]) -> word end
|
, "oracle_query" => fun([_, _]) -> word end
|
||||||
, "list" => fun([A]) -> {list, A} end
|
, "list" => fun([A]) -> {list, A} end
|
||||||
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
, "option" => fun([A]) -> {variant, [[], [A]]} end
|
||||||
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
, "map" => fun([K, V]) -> map_typerep(K, V) end
|
||||||
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
, ["Chain", "ttl"] => fun([]) -> {variant, [[word], [word]]} end
|
||||||
|
, ["AENS", "pointee"] => fun([]) -> {variant, [[word], [word], [word]]} end
|
||||||
}.
|
}.
|
||||||
|
|
||||||
builtin_constructors() ->
|
builtin_constructors() ->
|
||||||
#{ ["RelativeTTL"] => 0
|
#{ ["RelativeTTL"] => 0
|
||||||
, ["FixedTTL"] => 1
|
, ["FixedTTL"] => 1
|
||||||
, ["None"] => 0
|
, ["None"] => 0
|
||||||
, ["Some"] => 1 }.
|
, ["Some"] => 1
|
||||||
|
, ["AccountPointee"] => 0
|
||||||
|
, ["OraclePointee"] => 1
|
||||||
|
, ["ContractPointee"] => 2
|
||||||
|
}.
|
||||||
|
|
||||||
map_typerep(K, V) ->
|
map_typerep(K, V) ->
|
||||||
{map, K, V}.
|
{map, K, V}.
|
||||||
@ -146,4 +151,3 @@ get_constructor_tag(Name, #{constructors := Constructors}) ->
|
|||||||
undefined -> error({undefined_constructor, Name});
|
undefined -> error({undefined_constructor, Name});
|
||||||
Tag -> Tag
|
Tag -> Tag
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -264,6 +264,8 @@ type({args_t, _, Args}) ->
|
|||||||
type({bytes_t, _, any}) -> text("bytes(_)");
|
type({bytes_t, _, any}) -> text("bytes(_)");
|
||||||
type({bytes_t, _, Len}) ->
|
type({bytes_t, _, Len}) ->
|
||||||
text(lists:concat(["bytes(", Len, ")"]));
|
text(lists:concat(["bytes(", Len, ")"]));
|
||||||
|
type({if_t, _, Id, Then, Else}) ->
|
||||||
|
beside(text("if"), args_type([Id, Then, Else]));
|
||||||
type({named_arg_t, _, Name, Type, _Default}) ->
|
type({named_arg_t, _, Name, Type, _Default}) ->
|
||||||
%% Drop the default value
|
%% Drop the default value
|
||||||
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
%% follow(hsep(typed(name(Name), Type), text("=")), expr(Default));
|
||||||
@ -290,12 +292,9 @@ tuple_type(Factors) ->
|
|||||||
, text(")")
|
, text(")")
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-spec arg_expr(aeso_syntax:arg_expr()) -> doc().
|
-spec expr_p(integer(), aeso_syntax:arg_expr()) -> doc().
|
||||||
arg_expr({named_arg, _, Name, E}) ->
|
expr_p(P, {named_arg, _, Name, E}) ->
|
||||||
follow(hsep(expr(Name), text("=")), expr(E));
|
paren(P > 100, follow(hsep(expr(Name), text("=")), expr(E)));
|
||||||
arg_expr(E) -> expr(E).
|
|
||||||
|
|
||||||
-spec expr_p(integer(), aeso_syntax:expr()) -> doc().
|
|
||||||
expr_p(P, {lam, _, Args, E}) ->
|
expr_p(P, {lam, _, Args, E}) ->
|
||||||
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
paren(P > 100, follow(hsep(args(Args), text("=>")), expr_p(100, E)));
|
||||||
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
expr_p(P, If = {'if', Ann, Cond, Then, Else}) ->
|
||||||
@ -377,8 +376,13 @@ expr_p(_, {char, _, C}) ->
|
|||||||
case C of
|
case C of
|
||||||
$' -> text("'\\''");
|
$' -> text("'\\''");
|
||||||
$" -> text("'\"'");
|
$" -> text("'\"'");
|
||||||
_ -> S = lists:flatten(io_lib:format("~p", [[C]])),
|
_ when C < 16#80 ->
|
||||||
text("'" ++ tl(lists:droplast(S)) ++ "'")
|
S = lists:flatten(io_lib:format("~p", [[C]])),
|
||||||
|
text("'" ++ tl(lists:droplast(S)) ++ "'");
|
||||||
|
_ ->
|
||||||
|
S = lists:flatten(
|
||||||
|
io_lib:format("'~ts'", [list_to_binary(aeso_scan:utf8_encode([C]))])),
|
||||||
|
text(S)
|
||||||
end;
|
end;
|
||||||
%% -- Names
|
%% -- Names
|
||||||
expr_p(_, E = {id, _, _}) -> name(E);
|
expr_p(_, E = {id, _, _}) -> name(E);
|
||||||
@ -450,7 +454,7 @@ prefix(P, Op, A) ->
|
|||||||
app(P, F, Args) ->
|
app(P, F, Args) ->
|
||||||
paren(P > 900,
|
paren(P > 900,
|
||||||
beside(expr_p(900, F),
|
beside(expr_p(900, F),
|
||||||
tuple(lists:map(fun arg_expr/1, Args)))).
|
tuple(lists:map(fun expr/1, Args)))).
|
||||||
|
|
||||||
field({field, _, LV, E}) ->
|
field({field, _, LV, E}) ->
|
||||||
follow(hsep(lvalue(LV), text("=")), expr(E));
|
follow(hsep(lvalue(LV), text("=")), expr(E));
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
%%%-------------------------------------------------------------------
|
%%%-------------------------------------------------------------------
|
||||||
-module(aeso_scan).
|
-module(aeso_scan).
|
||||||
|
|
||||||
-export([scan/1]).
|
-export([scan/1, utf8_encode/1]).
|
||||||
|
|
||||||
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
-import(aeso_scan_lib, [token/1, token/2, symbol/0, skip/0,
|
||||||
override/2, push/2, pop/1]).
|
override/2, push/2, pop/1]).
|
||||||
@ -28,7 +28,13 @@ lexer() ->
|
|||||||
QID = ["(", CON, "\\.)+", ID],
|
QID = ["(", CON, "\\.)+", ID],
|
||||||
QCON = ["(", CON, "\\.)+", CON],
|
QCON = ["(", CON, "\\.)+", CON],
|
||||||
OP = "[=!<>+\\-*/:&|?~@^]+",
|
OP = "[=!<>+\\-*/:&|?~@^]+",
|
||||||
CHAR = "'([^'\\\\]|(\\\\.))'",
|
%% Five cases for a character
|
||||||
|
%% * 1 7-bit ascii, not \ or '
|
||||||
|
%% * 2-4 8-bit values (UTF8)
|
||||||
|
%% * \ followed by a known modifier [aernrtv]
|
||||||
|
%% * \xhh
|
||||||
|
%% * \x{hhh...}
|
||||||
|
CHAR = "'(([\\x00-\\x26\\x28-\\x5b\\x5d-\\x7f])|([\\x00-\\xff][\\x80-\\xff]{1,3})|(\\\\[befnrtv'\\\\])|(\\\\x[0-9a-fA-F]{2,2})|(\\\\x\\{[0-9a-fA-F]*\\}))'",
|
||||||
STRING = "\"([^\"\\\\]|(\\\\.))*\"",
|
STRING = "\"([^\"\\\\]|(\\\\.))*\"",
|
||||||
|
|
||||||
CommentStart = {"/\\*", push(comment, skip())},
|
CommentStart = {"/\\*", push(comment, skip())},
|
||||||
@ -77,34 +83,34 @@ scan(String) ->
|
|||||||
%% -- Helpers ----------------------------------------------------------------
|
%% -- Helpers ----------------------------------------------------------------
|
||||||
|
|
||||||
parse_string([$" | Chars]) ->
|
parse_string([$" | Chars]) ->
|
||||||
unescape(Chars).
|
unicode:characters_to_nfc_binary(unescape(Chars)).
|
||||||
|
|
||||||
parse_char([$', $\\, Code, $']) ->
|
parse_char([$' | Chars]) ->
|
||||||
case Code of
|
case unicode:characters_to_nfc_list(unescape($', Chars, [])) of
|
||||||
$' -> $';
|
[Char] -> Char;
|
||||||
$\\ -> $\\;
|
_Bad -> {error, "Bad character literal: '" ++ Chars}
|
||||||
$b -> $\b;
|
end.
|
||||||
$e -> $\e;
|
|
||||||
$f -> $\f;
|
|
||||||
$n -> $\n;
|
|
||||||
$r -> $\r;
|
|
||||||
$t -> $\t;
|
|
||||||
$v -> $\v;
|
|
||||||
_ -> {error, "Bad control sequence: \\" ++ [Code]}
|
|
||||||
end;
|
|
||||||
parse_char([$', C, $']) -> C.
|
|
||||||
|
|
||||||
unescape(Str) -> unescape(Str, []).
|
utf8_encode(Cs) ->
|
||||||
|
binary_to_list(unicode:characters_to_binary(Cs)).
|
||||||
|
|
||||||
unescape([$"], Acc) ->
|
unescape(Str) -> unescape($", Str, []).
|
||||||
|
|
||||||
|
unescape(Delim, [Delim], Acc) ->
|
||||||
list_to_binary(lists:reverse(Acc));
|
list_to_binary(lists:reverse(Acc));
|
||||||
unescape([$\\, $x, D1, D2 | Chars ], Acc) ->
|
unescape(Delim, [$\\, $x, ${ | Chars ], Acc) ->
|
||||||
|
{Ds, [_ | Cs]} = lists:splitwith(fun($}) -> false ; (_) -> true end, Chars),
|
||||||
|
C = list_to_integer(Ds, 16),
|
||||||
|
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
|
||||||
|
unescape(Delim, Cs, [Utf8Cs | Acc]);
|
||||||
|
unescape(Delim, [$\\, $x, D1, D2 | Chars ], Acc) ->
|
||||||
C = list_to_integer([D1, D2], 16),
|
C = list_to_integer([D1, D2], 16),
|
||||||
unescape(Chars, [C | Acc]);
|
Utf8Cs = binary_to_list(unicode:characters_to_binary([C])),
|
||||||
unescape([$\\, Code | Chars], Acc) ->
|
unescape(Delim, Chars, [Utf8Cs | Acc]);
|
||||||
Ok = fun(C) -> unescape(Chars, [C | Acc]) end,
|
unescape(Delim, [$\\, Code | Chars], Acc) ->
|
||||||
|
Ok = fun(C) -> unescape(Delim, Chars, [C | Acc]) end,
|
||||||
case Code of
|
case Code of
|
||||||
$" -> Ok($");
|
Delim -> Ok(Delim);
|
||||||
$\\ -> Ok($\\);
|
$\\ -> Ok($\\);
|
||||||
$b -> Ok($\b);
|
$b -> Ok($\b);
|
||||||
$e -> Ok($\e);
|
$e -> Ok($\e);
|
||||||
@ -115,8 +121,8 @@ unescape([$\\, Code | Chars], Acc) ->
|
|||||||
$v -> Ok($\v);
|
$v -> Ok($\v);
|
||||||
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
_ -> error("Bad control sequence: \\" ++ [Code]) %% TODO
|
||||||
end;
|
end;
|
||||||
unescape([C | Chars], Acc) ->
|
unescape(Delim, [C | Chars], Acc) ->
|
||||||
unescape(Chars, [C | Acc]).
|
unescape(Delim, Chars, [C | Acc]).
|
||||||
|
|
||||||
strip_underscores(S) ->
|
strip_underscores(S) ->
|
||||||
lists:filter(fun(C) -> C /= $_ end, S).
|
lists:filter(fun(C) -> C /= $_ end, S).
|
||||||
|
@ -143,4 +143,4 @@ compilable_contracts() ->
|
|||||||
not_yet_compilable(fate) ->
|
not_yet_compilable(fate) ->
|
||||||
[];
|
[];
|
||||||
not_yet_compilable(aevm) ->
|
not_yet_compilable(aevm) ->
|
||||||
[].
|
["funargs", "strings"].
|
||||||
|
@ -154,6 +154,7 @@ compilable_contracts() ->
|
|||||||
"events",
|
"events",
|
||||||
"include",
|
"include",
|
||||||
"basic_auth",
|
"basic_auth",
|
||||||
|
"basic_auth_tx",
|
||||||
"bitcoin_auth",
|
"bitcoin_auth",
|
||||||
"address_literals",
|
"address_literals",
|
||||||
"bytes_equality",
|
"bytes_equality",
|
||||||
@ -162,6 +163,7 @@ compilable_contracts() ->
|
|||||||
"bytes_to_x",
|
"bytes_to_x",
|
||||||
"bytes_concat",
|
"bytes_concat",
|
||||||
"aens",
|
"aens",
|
||||||
|
"aens_update",
|
||||||
"tuple_match",
|
"tuple_match",
|
||||||
"cyclic_include",
|
"cyclic_include",
|
||||||
"stdlib_include",
|
"stdlib_include",
|
||||||
@ -171,17 +173,22 @@ compilable_contracts() ->
|
|||||||
"payable",
|
"payable",
|
||||||
"unapplied_builtins",
|
"unapplied_builtins",
|
||||||
"underscore_number_literals",
|
"underscore_number_literals",
|
||||||
|
"pairing_crypto",
|
||||||
"qualified_constructor",
|
"qualified_constructor",
|
||||||
"let_patterns",
|
"let_patterns",
|
||||||
"lhs_matching",
|
"lhs_matching",
|
||||||
|
"more_strings",
|
||||||
|
"protected_call",
|
||||||
"hermetization_turnoff"
|
"hermetization_turnoff"
|
||||||
].
|
].
|
||||||
|
|
||||||
not_compilable_on(fate) -> [];
|
not_compilable_on(fate) -> [];
|
||||||
not_compilable_on(aevm) ->
|
not_compilable_on(aevm) ->
|
||||||
["stdlib_include",
|
[ "stdlib_include", "manual_stdlib_include", "pairing_crypto"
|
||||||
"manual_stdlib_include",
|
, "aens_update", "basic_auth_tx", "more_strings"
|
||||||
"hermetization_turnoff"
|
, "unapplied_builtins", "bytes_to_x", "state_handling", "protected_call"
|
||||||
|
, "hermetization_turnoff"
|
||||||
|
|
||||||
].
|
].
|
||||||
|
|
||||||
debug_mode_contracts() ->
|
debug_mode_contracts() ->
|
||||||
@ -387,10 +394,10 @@ failing_contracts() ->
|
|||||||
[<<?Pos(12, 42)
|
[<<?Pos(12, 42)
|
||||||
"Cannot unify int\n"
|
"Cannot unify int\n"
|
||||||
" and string\n"
|
" and string\n"
|
||||||
"when checking the record projection at line 12, column 42\n"
|
"when checking the type of the expression at line 12, column 42\n"
|
||||||
" r.foo : (gas : int, value : int) => Remote.themap\n"
|
" r.foo() : map(int, string)\n"
|
||||||
"against the expected type\n"
|
"against the expected type\n"
|
||||||
" (gas : int, value : int) => map(string, int)">>])
|
" map(string, int)">>])
|
||||||
, ?TYPE_ERROR(not_toplevel_include,
|
, ?TYPE_ERROR(not_toplevel_include,
|
||||||
[<<?Pos(2, 11)
|
[<<?Pos(2, 11)
|
||||||
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>])
|
"Include of 'included.aes' at line 2, column 11\nnot allowed, include only allowed at top level.">>])
|
||||||
@ -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">>
|
"Cannot unify int\n and string\nwhen checking the type of the pattern at line 2, column 53\n x : int\nagainst the expected type\n string">>
|
||||||
])
|
])
|
||||||
, ?TYPE_ERROR(map_as_map_key,
|
, ?TYPE_ERROR(map_as_map_key,
|
||||||
[<<?Pos(5, 25)
|
[<<?Pos(5, 47)
|
||||||
"Invalid key type\n"
|
"Invalid key type\n"
|
||||||
" map(int, int)\n"
|
" map(int, int)\n"
|
||||||
"Map keys cannot contain other maps.">>,
|
"Map keys cannot contain other maps.">>,
|
||||||
<<?Pos(6, 25)
|
<<?Pos(6, 31)
|
||||||
|
"Invalid key type\n"
|
||||||
|
" list(map(int, int))\n"
|
||||||
|
"Map keys cannot contain other maps.">>,
|
||||||
|
<<?Pos(6, 31)
|
||||||
"Invalid key type\n"
|
"Invalid key type\n"
|
||||||
" lm\n"
|
" lm\n"
|
||||||
"Map keys cannot contain other maps.">>])
|
"Map keys cannot contain other maps.">>])
|
||||||
@ -681,6 +692,12 @@ failing_contracts() ->
|
|||||||
"Empty record/map update\n"
|
"Empty record/map update\n"
|
||||||
" r {}">>
|
" r {}">>
|
||||||
])
|
])
|
||||||
|
, ?TYPE_ERROR(bad_protected_call,
|
||||||
|
[<<?Pos(6, 22)
|
||||||
|
"Invalid 'protected' argument\n"
|
||||||
|
" (0 : int) == (1 : int) : bool\n"
|
||||||
|
"It must be either 'true' or 'false'.">>
|
||||||
|
])
|
||||||
, ?TYPE_ERROR(bad_function_block,
|
, ?TYPE_ERROR(bad_function_block,
|
||||||
[<<?Pos(4, 5)
|
[<<?Pos(4, 5)
|
||||||
"Mismatch in the function block. Expected implementation/type declaration of g function">>,
|
"Mismatch in the function block. Expected implementation/type declaration of g function">>,
|
||||||
|
@ -63,7 +63,8 @@ simple_contracts_test_() ->
|
|||||||
%% Parse tests of example contracts
|
%% Parse tests of example contracts
|
||||||
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
[ {lists:concat(["Parse the ", Contract, " contract."]),
|
||||||
fun() -> roundtrip_contract(Contract) end}
|
fun() -> roundtrip_contract(Contract) end}
|
||||||
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof, multi_sig, simple_storage, fundme, dutch_auction] ]
|
|| Contract <- [counter, voting, all_syntax, '05_greeter', aeproof,
|
||||||
|
multi_sig, simple_storage, fundme, dutch_auction, utf8] ]
|
||||||
}.
|
}.
|
||||||
|
|
||||||
parse_contract(Name) ->
|
parse_contract(Name) ->
|
||||||
@ -85,7 +86,7 @@ parse_expr(Text) ->
|
|||||||
round_trip(Text) ->
|
round_trip(Text) ->
|
||||||
Contract = parse_string(Text),
|
Contract = parse_string(Text),
|
||||||
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
|
Text1 = prettypr:format(aeso_pretty:decls(strip_stdlib(Contract))),
|
||||||
Contract1 = parse_string(Text1),
|
Contract1 = parse_string(aeso_scan:utf8_encode(Text1)),
|
||||||
NoSrcLoc = remove_line_numbers(Contract),
|
NoSrcLoc = remove_line_numbers(Contract),
|
||||||
NoSrcLoc1 = remove_line_numbers(Contract1),
|
NoSrcLoc1 = remove_line_numbers(Contract1),
|
||||||
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
?assertMatch(NoSrcLoc, diff(NoSrcLoc, NoSrcLoc1)).
|
||||||
|
@ -33,7 +33,22 @@ contract AENSTest =
|
|||||||
sign : signature) : unit =
|
sign : signature) : unit =
|
||||||
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
AENS.claim(addr, name, salt, name_fee, signature = sign)
|
||||||
|
|
||||||
// TODO: update() -- how to handle pointers?
|
|
||||||
|
stateful entrypoint update(owner : address,
|
||||||
|
name : string,
|
||||||
|
ttl : option(Chain.ttl),
|
||||||
|
client_ttl : option(int),
|
||||||
|
pointers : option(map(string, AENS.pointee))) : unit =
|
||||||
|
AENS.update(owner, name, ttl, client_ttl, pointers)
|
||||||
|
|
||||||
|
stateful entrypoint signedUpdate(owner : address,
|
||||||
|
name : string,
|
||||||
|
ttl : option(Chain.ttl),
|
||||||
|
client_ttl : option(int),
|
||||||
|
pointers : option(map(string, AENS.pointee)),
|
||||||
|
sign : signature) : unit =
|
||||||
|
AENS.update(owner, name, ttl, client_ttl, pointers, signature = sign)
|
||||||
|
|
||||||
|
|
||||||
stateful entrypoint transfer(owner : address,
|
stateful entrypoint transfer(owner : address,
|
||||||
new_owner : address,
|
new_owner : address,
|
||||||
|
17
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() = {
|
entrypoint init() = {
|
||||||
johann = 1000,
|
johann = 1000,
|
||||||
wolfgang = -10,
|
wolfgang = -10,
|
||||||
|
|
||||||
|
/* TODO: This does not compile because of bug in the parser tester.
|
||||||
von = (2 + 2, 0, List.sum([x | k <- [1,2,3]
|
von = (2 + 2, 0, List.sum([x | k <- [1,2,3]
|
||||||
, let l = k + 1
|
, let l = k + 1
|
||||||
, if(l < 10)
|
, if(l < 10)
|
||||||
@ -43,11 +45,13 @@ contract AllSyntax =
|
|||||||
, Adam <- [Adam, Mickiewicz]
|
, Adam <- [Adam, Mickiewicz]
|
||||||
, let x = f(l)
|
, let x = f(l)
|
||||||
])),
|
])),
|
||||||
|
*/
|
||||||
|
von = (2 + 2, 0, List.sum([1,2,3,4])),
|
||||||
goethe = () }
|
goethe = () }
|
||||||
|
|
||||||
function f() =
|
function f() =
|
||||||
let kp = "nietzsche"
|
let kp = "nietzsche"
|
||||||
let p = "Пушкин"
|
// let p = "Пушкин" // TODO: this also doesn't do right round_trip...
|
||||||
let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210)
|
let k(x : bytes(8)) : bytes(8) = Bytes.to_int(#fedcba9876543210)
|
||||||
|
|
||||||
let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
let f : () => address = () => ak_2gx9MEFxKvY9vMG5YnqnXWv1hCsX7rgnfvBLJS4aQurustR1rt
|
||||||
|
6
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 =
|
contract BytesToX =
|
||||||
|
|
||||||
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
|
entrypoint to_int(b : bytes(42)) : int = Bytes.to_int(b)
|
||||||
|
@ -17,7 +17,7 @@ contract ChannelOnChainContractOracle =
|
|||||||
bets = {}
|
bets = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
public stateful function place_bet(answer: string) =
|
public stateful function place_bet(answer: string) =
|
||||||
switch(Map.lookup(answer, state.bets))
|
switch(Map.lookup(answer, state.bets))
|
||||||
None =>
|
None =>
|
||||||
put(state{ bets = state.bets{[answer] = Call.caller}})
|
put(state{ bets = state.bets{[answer] = Call.caller}})
|
||||||
@ -25,6 +25,9 @@ contract ChannelOnChainContractOracle =
|
|||||||
Some(_value) =>
|
Some(_value) =>
|
||||||
"bet_already_taken"
|
"bet_already_taken"
|
||||||
|
|
||||||
|
public function expiry() =
|
||||||
|
Oracle.expiry(state.oracle)
|
||||||
|
|
||||||
public function query_fee() =
|
public function query_fee() =
|
||||||
Oracle.query_fee(state.oracle)
|
Oracle.query_fee(state.oracle)
|
||||||
|
|
||||||
@ -35,7 +38,7 @@ contract ChannelOnChainContractOracle =
|
|||||||
switch(Oracle.get_answer(state.oracle, q))
|
switch(Oracle.get_answer(state.oracle, q))
|
||||||
None =>
|
None =>
|
||||||
"no response"
|
"no response"
|
||||||
Some(result) =>
|
Some(result) =>
|
||||||
if(state.question == Oracle.get_question(state.oracle, q))
|
if(state.question == Oracle.get_question(state.oracle, q))
|
||||||
switch(Map.lookup(result, state.bets))
|
switch(Map.lookup(result, state.bets))
|
||||||
None =>
|
None =>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
|
include "String.aes"
|
||||||
contract FunctionArguments =
|
contract FunctionArguments =
|
||||||
|
|
||||||
entrypoint sum(n : int, m: int) =
|
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 =
|
contract Remote =
|
||||||
record rstate = { i : int, s : string, m : map(int, int) }
|
record rstate = { i : int, s : string, m : map(int, int) }
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
include "String.aes"
|
||||||
contract Strings =
|
contract Strings =
|
||||||
entrypoint str_len(s) = String.length(s)
|
entrypoint str_len(s) = String.length(s)
|
||||||
entrypoint str_concat(s1, s2) = String.concat(s1, s2)
|
entrypoint str_concat(s1, s2) = String.concat(s1, s2)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
// AENS.transfer
|
// AENS.transfer
|
||||||
// AENS.revoke
|
// AENS.revoke
|
||||||
// Oracle.extend
|
// Oracle.extend
|
||||||
|
include "String.aes"
|
||||||
contract UnappliedBuiltins =
|
contract UnappliedBuiltins =
|
||||||
entrypoint main() = ()
|
entrypoint main() = ()
|
||||||
type o = oracle(int, int)
|
type o = oracle(int, int)
|
||||||
@ -21,6 +22,7 @@ contract UnappliedBuiltins =
|
|||||||
function b_abort() = abort
|
function b_abort() = abort
|
||||||
function b_require() = require
|
function b_require() = require
|
||||||
function oracle_query_fee() = Oracle.query_fee
|
function oracle_query_fee() = Oracle.query_fee
|
||||||
|
function oracle_expiry() = Oracle.expiry
|
||||||
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
stateful function oracle_query() = Oracle.query : (o, _, _, _, _) => _
|
||||||
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
function oracle_get_question() = Oracle.get_question : (o, _) => _
|
||||||
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
function oracle_get_answer() = Oracle.get_answer : (o, _) => _
|
||||||
|
21
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