Add bitwise operations, Address.to_bytes and Crypto.poseidon
This commit is contained in:
parent
11ad35fb47
commit
eae1b38bd9
@ -6,8 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
|
- Bitwise operations for integers: `band`, `bor`, `bxor`, `bnot`, `<<` and `>>`.
|
||||||
|
- `Int.mulmod` - combined builtin operation for multiplication and modulus.
|
||||||
|
- `Crypto.poseidon` - a ZK/SNARK-friendly hash function (over the BLS12-381 scalar field).
|
||||||
|
- `Address.to_bytes` - convert an address to its binary representation (for hashing, etc.).
|
||||||
### Changed
|
### Changed
|
||||||
### Removed
|
### Removed
|
||||||
|
- `Bitwise.aes` standard library - it is now obsolete.
|
||||||
|
|
||||||
## [6.1.0] - 2021-10-20
|
## [6.1.0] - 2021-10-20
|
||||||
### Added
|
### Added
|
||||||
|
@ -419,6 +419,14 @@ fails rather than wrapping around to -2²⁵⁵.
|
|||||||
The division and modulo operations also throw an arithmetic error if the
|
The division and modulo operations also throw an arithmetic error if the
|
||||||
second argument is zero.
|
second argument is zero.
|
||||||
|
|
||||||
|
Sophia arbitrary-sized integers (FATE) also supports the following bitwise operations:
|
||||||
|
- bitwise and (`x band y`)
|
||||||
|
- bitwise or (`x bor y`)
|
||||||
|
- bitwise xor (`x bxor y`)
|
||||||
|
- bitwise not (`bnot x`)
|
||||||
|
- arithmetic bitshift left (`x << n`)
|
||||||
|
- arithmetic bitshift right (`x >> n`)
|
||||||
|
|
||||||
## Bit fields
|
## Bit fields
|
||||||
|
|
||||||
Sophia integers do not support bit arithmetic. Instead there is a separate
|
Sophia integers do not support bit arithmetic. Instead there is a separate
|
||||||
|
@ -177,11 +177,17 @@ Opposite of [to_int](#to_int). Returns `None` if the integer doesn't correspond
|
|||||||
|
|
||||||
#### to_str
|
#### to_str
|
||||||
```
|
```
|
||||||
Int.to_str : int => string
|
Int.to_str(i : int) : string
|
||||||
```
|
```
|
||||||
|
|
||||||
Casts integer to string using decimal representation
|
Casts integer to string using decimal representation
|
||||||
|
|
||||||
|
#### mulmod
|
||||||
|
```
|
||||||
|
Int.mulmod : (a : int, b : int, q : int) : int
|
||||||
|
```
|
||||||
|
|
||||||
|
Combined multiplication and modulus, returns `(a * b) mod q`.
|
||||||
|
|
||||||
### Map
|
### Map
|
||||||
|
|
||||||
@ -239,6 +245,13 @@ Address.to_str(a : address) : string
|
|||||||
|
|
||||||
Base58 encoded string
|
Base58 encoded string
|
||||||
|
|
||||||
|
#### to_bytes
|
||||||
|
```
|
||||||
|
Address.to_bytes(a : address) : bytes(32)
|
||||||
|
```
|
||||||
|
|
||||||
|
The binary representation of the address.
|
||||||
|
|
||||||
|
|
||||||
#### is_contract
|
#### is_contract
|
||||||
```
|
```
|
||||||
@ -297,6 +310,13 @@ Crypto.blake2b(x : 'a) : hash
|
|||||||
|
|
||||||
Hash any object to blake2b
|
Hash any object to blake2b
|
||||||
|
|
||||||
|
#### poseidon
|
||||||
|
```
|
||||||
|
Crypto.poseidon(x1 : int, x2 : int) : int
|
||||||
|
```
|
||||||
|
|
||||||
|
Hash two integers (in the scalar field of BLS12-381) to another integer (in the scalar
|
||||||
|
field of BLS12-281). This is a ZK/SNARK-friendly hash function.
|
||||||
|
|
||||||
#### verify_sig
|
#### verify_sig
|
||||||
```
|
```
|
||||||
|
@ -234,7 +234,8 @@ Path ::= Id // Record field
|
|||||||
|
|
||||||
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||||
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
|
||||||
UnOp ::= '-' | '!'
|
| 'band' | 'bor' | 'bxor' | '<<' | '>>'
|
||||||
|
UnOp ::= '-' | '!' | 'bnot'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Operators types
|
## Operators types
|
||||||
@ -242,6 +243,7 @@ UnOp ::= '-' | '!'
|
|||||||
| Operators | Type
|
| Operators | Type
|
||||||
| --- | ---
|
| --- | ---
|
||||||
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
| `-` `+` `*` `/` `mod` `^` | arithmetic operators
|
||||||
|
| `band` `bor` `bxor` `bnot` `<<` `>>` | bitwise operators
|
||||||
| `!` `&&` `\|\|` | logical operators
|
| `!` `&&` `\|\|` | logical operators
|
||||||
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
|
||||||
| `::` `++` | list operators
|
| `::` `++` | list operators
|
||||||
@ -252,12 +254,16 @@ In order of highest to lowest precedence.
|
|||||||
|
|
||||||
| Operators | Associativity
|
| Operators | Associativity
|
||||||
| --- | ---
|
| --- | ---
|
||||||
| `!` | right
|
| `!` `bnot`| right
|
||||||
| `^` | left
|
| `^` | left
|
||||||
| `*` `/` `mod` | left
|
| `*` `/` `mod` | left
|
||||||
| `-` (unary) | right
|
| `-` (unary) | right
|
||||||
| `+` `-` | left
|
| `+` `-` | left
|
||||||
|
| `<<` `>>` | left
|
||||||
| `::` `++` | right
|
| `::` `++` | right
|
||||||
| `<` `>` `=<` `>=` `==` `!=` | none
|
| `<` `>` `=<` `>=` `==` `!=` | none
|
||||||
|
| `band` | left
|
||||||
|
| `bxor` | left
|
||||||
|
| `bor` | left
|
||||||
| `&&` | right
|
| `&&` | right
|
||||||
| `\|\|` | right
|
| `\|\|` | right
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
@compiler >= 4.3
|
|
||||||
|
|
||||||
namespace Bitwise =
|
|
||||||
|
|
||||||
// bit shift 'x' right 'n' postions
|
|
||||||
function bsr(n : int, x : int) : int =
|
|
||||||
let step = 2^n
|
|
||||||
let res = x / step
|
|
||||||
if (x >= 0 || x mod step == 0)
|
|
||||||
res
|
|
||||||
else
|
|
||||||
res - 1
|
|
||||||
|
|
||||||
// bit shift 'x' left 'n' positions
|
|
||||||
function bsl(n : int, x : int) : int =
|
|
||||||
x * 2^n
|
|
||||||
|
|
||||||
// bit shift 'x' left 'n' positions, limit at 'lim' bits
|
|
||||||
function bsli(n : int, x : int, lim : int) : int =
|
|
||||||
(x * 2^n) mod (2^lim)
|
|
||||||
|
|
||||||
// bitwise 'and' for arbitrary precision integers
|
|
||||||
function band(a : int, b : int) : int =
|
|
||||||
if (a >= 0 && b >= 0)
|
|
||||||
uband_(a, b)
|
|
||||||
elif (b >= 0)
|
|
||||||
ubnand_(b, -1 - a)
|
|
||||||
elif (a >= 0)
|
|
||||||
ubnand_(a, -1 - b)
|
|
||||||
else
|
|
||||||
-1 - ubor_(-1 - a, -1 - b)
|
|
||||||
|
|
||||||
// bitwise 'or' for arbitrary precision integers
|
|
||||||
function
|
|
||||||
bor : (int, int) => int
|
|
||||||
bor(0, b) = b
|
|
||||||
bor(a, 0) = a
|
|
||||||
bor(a : int, b : int) : int =
|
|
||||||
if (a >= 0 && b >= 0)
|
|
||||||
ubor_(a, b)
|
|
||||||
elif (b >= 0)
|
|
||||||
-1 - ubnand_(-1 - a, b)
|
|
||||||
elif (a >= 0)
|
|
||||||
-1 - ubnand_(-1 - b, a)
|
|
||||||
else
|
|
||||||
-1 - uband_(-1 - a, -1 - b)
|
|
||||||
|
|
||||||
// bitwise 'xor' for arbitrary precision integers
|
|
||||||
function
|
|
||||||
bxor : (int, int) => int
|
|
||||||
bxor(0, b) = b
|
|
||||||
bxor(a, 0) = a
|
|
||||||
bxor(a, b) =
|
|
||||||
if (a >= 0 && b >= 0)
|
|
||||||
ubxor_(a, b)
|
|
||||||
elif (b >= 0)
|
|
||||||
-1 - ubxor_(-1 - a, b)
|
|
||||||
elif (a >= 0)
|
|
||||||
-1 - ubxor_(a, -1 - b)
|
|
||||||
else
|
|
||||||
ubxor_(-1 - a, -1 - b)
|
|
||||||
|
|
||||||
// bitwise 'not' for arbitrary precision integers
|
|
||||||
function bnot(a : int) = bxor(a, -1)
|
|
||||||
|
|
||||||
// Bitwise 'and' for non-negative integers
|
|
||||||
function uband(a : int, b : int) : int =
|
|
||||||
require(a >= 0 && b >= 0, "uband is only defined for non-negative integers")
|
|
||||||
switch((a, b))
|
|
||||||
(0, _) => 0
|
|
||||||
(_, 0) => 0
|
|
||||||
_ => uband__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function uband_(a, b) = uband__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function
|
|
||||||
uband__(0, b, val, acc) = acc
|
|
||||||
uband__(a, 0, val, acc) = acc
|
|
||||||
uband__(a, b, val, acc) =
|
|
||||||
switch (a mod 2 + b mod 2)
|
|
||||||
2 => uband__(a / 2, b / 2, val * 2, acc + val)
|
|
||||||
_ => uband__(a / 2, b / 2, val * 2, acc)
|
|
||||||
|
|
||||||
// Bitwise 'or' for non-negative integers
|
|
||||||
function ubor(a, b) =
|
|
||||||
require(a >= 0 && b >= 0, "ubor is only defined for non-negative integers")
|
|
||||||
switch((a, b))
|
|
||||||
(0, _) => b
|
|
||||||
(_, 0) => a
|
|
||||||
_ => ubor__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function ubor_(a, b) = ubor__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function
|
|
||||||
ubor__(0, 0, val, acc) = acc
|
|
||||||
ubor__(a, b, val, acc) =
|
|
||||||
switch (a mod 2 + b mod 2)
|
|
||||||
0 => ubor__(a / 2, b / 2, val * 2, acc)
|
|
||||||
_ => ubor__(a / 2, b / 2, val * 2, acc + val)
|
|
||||||
|
|
||||||
//Bitwise 'xor' for non-negative integers
|
|
||||||
function
|
|
||||||
ubxor : (int, int) => int
|
|
||||||
ubxor(0, b) = b
|
|
||||||
ubxor(a, 0) = a
|
|
||||||
ubxor(a, b) =
|
|
||||||
require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers")
|
|
||||||
ubxor__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function ubxor_(a, b) = ubxor__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function
|
|
||||||
ubxor__(0, 0, val, acc) = acc
|
|
||||||
ubxor__(a, b, val, acc) =
|
|
||||||
switch(a mod 2 + b mod 2)
|
|
||||||
1 => ubxor__(a / 2, b / 2, val * 2, acc + val)
|
|
||||||
_ => ubxor__(a / 2, b / 2, val * 2, acc)
|
|
||||||
|
|
||||||
// Bitwise combined 'and' and 'not' of second argument for positive integers
|
|
||||||
// x 'bnand' y = x 'band' ('bnot' y)
|
|
||||||
// The tricky bit is that after negation the second argument has an infinite number of 1's
|
|
||||||
// use as many as needed!
|
|
||||||
//
|
|
||||||
// NOTE: this function is not symmetric!
|
|
||||||
private function ubnand(a, b) =
|
|
||||||
require(a >= 0 && b >= 0, "ubxor is only defined for non-negative integers")
|
|
||||||
ubnand__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function ubnand_(a, b) = ubnand__(a, b, 1, 0)
|
|
||||||
|
|
||||||
private function
|
|
||||||
ubnand__(0, b, val, acc) = acc
|
|
||||||
ubnand__(a, b, val, acc) =
|
|
||||||
switch((a mod 2, b mod 2))
|
|
||||||
(1, 0) => ubnand__(a / 2, b / 2, val * 2, acc + val)
|
|
||||||
_ => ubnand__(a / 2, b / 2, val * 2, acc)
|
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{erl_opts, [debug_info]}.
|
{erl_opts, [debug_info]}.
|
||||||
|
|
||||||
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"05dfd7f"}}}
|
{deps, [ {aebytecode, {git, "https://github.com/aeternity/aebytecode.git", {ref,"822a269"}}}
|
||||||
, {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,"05dfd7ffc7fb1e07ecc0b1e516da571f56d7dc8f"}},
|
{ref,"822a269f754f174f6e20c7ba133a13c6f4a1f83b"}},
|
||||||
0},
|
0},
|
||||||
{<<"aeserialization">>,
|
{<<"aeserialization">>,
|
||||||
{git,"https://github.com/aeternity/aeserialization.git",
|
{git,"https://github.com/aeternity/aeserialization.git",
|
||||||
|
@ -655,7 +655,8 @@ global_env() ->
|
|||||||
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
|
{"ecrecover_secp256k1", Fun([Hash, Bytes(65)], Option(Bytes(20)))},
|
||||||
{"sha3", Fun1(A, Hash)},
|
{"sha3", Fun1(A, Hash)},
|
||||||
{"sha256", Fun1(A, Hash)},
|
{"sha256", Fun1(A, Hash)},
|
||||||
{"blake2b", Fun1(A, Hash)}]) },
|
{"blake2b", Fun1(A, Hash)},
|
||||||
|
{"poseidon", Fun([Int, Int], Int)}]) },
|
||||||
|
|
||||||
%% Fancy BLS12-381 crypto operations
|
%% Fancy BLS12-381 crypto operations
|
||||||
MCL_BLS12_381_Scope = #scope
|
MCL_BLS12_381_Scope = #scope
|
||||||
@ -740,14 +741,16 @@ global_env() ->
|
|||||||
]) },
|
]) },
|
||||||
|
|
||||||
%% Conversion
|
%% Conversion
|
||||||
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)}]) },
|
IntScope = #scope{ funs = MkDefs([{"to_str", Fun1(Int, String)},
|
||||||
|
{"mulmod", Fun([Int, Int, Int], Int)}]) },
|
||||||
|
|
||||||
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
|
AddressScope = #scope{ funs = MkDefs([{"to_str", Fun1(Address, String)},
|
||||||
|
{"to_bytes", Fun1(Address, Bytes(32))},
|
||||||
{"to_contract", FunC(address_to_contract, [Address], A)},
|
{"to_contract", FunC(address_to_contract, [Address], A)},
|
||||||
{"is_oracle", Fun1(Address, Bool)},
|
{"is_oracle", Fun1(Address, Bool)},
|
||||||
{"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
|
||||||
@ -1890,6 +1893,11 @@ infer_infix({IntOp, As})
|
|||||||
IntOp == '^'; IntOp == 'mod' ->
|
IntOp == '^'; IntOp == 'mod' ->
|
||||||
Int = {id, As, "int"},
|
Int = {id, As, "int"},
|
||||||
{fun_t, As, [], [Int, Int], Int};
|
{fun_t, As, [], [Int, Int], Int};
|
||||||
|
infer_infix({BitOp, As})
|
||||||
|
when BitOp == 'band'; BitOp == 'bor'; BitOp == 'bxor';
|
||||||
|
BitOp == '<<'; BitOp == '>>' ->
|
||||||
|
Int = {id, As, "int"},
|
||||||
|
{fun_t, As, [], [Int, Int], Int};
|
||||||
infer_infix({RelOp, As})
|
infer_infix({RelOp, As})
|
||||||
when RelOp == '=='; RelOp == '!=';
|
when RelOp == '=='; RelOp == '!=';
|
||||||
RelOp == '<'; RelOp == '>';
|
RelOp == '<'; RelOp == '>';
|
||||||
@ -1912,6 +1920,9 @@ infer_infix({'++', As}) ->
|
|||||||
infer_prefix({'!',As}) ->
|
infer_prefix({'!',As}) ->
|
||||||
Bool = {id, As, "bool"},
|
Bool = {id, As, "bool"},
|
||||||
{fun_t, As, [], [Bool], Bool};
|
{fun_t, As, [], [Bool], Bool};
|
||||||
|
infer_prefix({BitOp,As}) when BitOp =:= 'bnot' ->
|
||||||
|
Int = {id, As, "int"},
|
||||||
|
{fun_t, As, [], [Int], Int};
|
||||||
infer_prefix({IntOp,As}) when IntOp =:= '-' ->
|
infer_prefix({IntOp,As}) when IntOp =:= '-' ->
|
||||||
Int = {id, As, "int"},
|
Int = {id, As, "int"},
|
||||||
{fun_t, As, [], [Int], Int}.
|
{fun_t, As, [], [Int], Int}.
|
||||||
|
@ -32,12 +32,13 @@
|
|||||||
|
|
||||||
-type op() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' |
|
-type op() :: '+' | '-' | '*' | '/' | mod | '^' | '++' | '::' |
|
||||||
'<' | '>' | '=<' | '>=' | '==' | '!=' | '!' |
|
'<' | '>' | '=<' | '>=' | '==' | '!=' | '!' |
|
||||||
|
'band' | 'bor' | 'bxor' | 'bnot' | '<<' | '>>' |
|
||||||
map_get | map_get_d | map_set | map_from_list | map_to_list |
|
map_get | map_get_d | map_set | map_from_list | map_to_list |
|
||||||
map_delete | map_member | map_size | string_length |
|
map_delete | map_member | map_size | string_length |
|
||||||
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
string_concat | bits_set | bits_clear | bits_test | bits_sum |
|
||||||
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_poseidon |
|
||||||
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_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_g1_is_zero | mcl_bls12_381_g1_add | mcl_bls12_381_g1_mul |
|
||||||
@ -258,7 +259,7 @@ builtins() ->
|
|||||||
{"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}, {"poseidon", 2}]},
|
||||||
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
{["MCL_BLS12_381"], [{"g1_neg", 1}, {"g1_norm", 1}, {"g1_valid", 1}, {"g1_is_zero", 1}, {"g1_add", 2}, {"g1_mul", 2},
|
||||||
{"g2_neg", 1}, {"g2_norm", 1}, {"g2_valid", 1}, {"g2_is_zero", 1}, {"g2_add", 2}, {"g2_mul", 2},
|
{"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},
|
{"gt_inv", 1}, {"gt_add", 2}, {"gt_mul", 2}, {"gt_pow", 2}, {"gt_is_one", 1},
|
||||||
@ -271,8 +272,9 @@ builtins() ->
|
|||||||
{["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}]},
|
||||||
{["Int"], [{"to_str", 1}]},
|
{["Int"], [{"to_str", 1}, {"mulmod", 2}]},
|
||||||
{["Address"], [{"to_str", 1}, {"to_contract", 1}, {"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
{["Address"], [{"to_str", 1}, {"to_bytes", 1}, {"to_contract", 1},
|
||||||
|
{"is_oracle", 1}, {"is_contract", 1}, {"is_payable", 1}]}
|
||||||
],
|
],
|
||||||
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|
maps:from_list([ {NS ++ [Fun], {MkName(NS, Fun), Arity}}
|
||||||
|| {NS, Funs} <- Scopes,
|
|| {NS, Funs} <- Scopes,
|
||||||
@ -707,8 +709,9 @@ expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A, B]}) when is_atom(Op) ->
|
|||||||
{op, Op, [expr_to_fcode(Env, A), expr_to_fcode(Env, B)]};
|
{op, Op, [expr_to_fcode(Env, A), expr_to_fcode(Env, B)]};
|
||||||
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
|
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
|
||||||
case Op of
|
case Op of
|
||||||
'-' -> {op, '-', [{lit, {int, 0}}, expr_to_fcode(Env, A)]};
|
'-' -> {op, '-', [{lit, {int, 0}}, expr_to_fcode(Env, A)]};
|
||||||
'!' -> {op, '!', [expr_to_fcode(Env, A)]}
|
'bnot' -> {op, 'bnot', [expr_to_fcode(Env, A)]};
|
||||||
|
'!' -> {op, '!', [expr_to_fcode(Env, A)]}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
%% Function calls
|
%% Function calls
|
||||||
@ -1146,9 +1149,9 @@ op_builtins() ->
|
|||||||
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
stringinternal_sha3, stringinternal_sha256, stringinternal_blake2b,
|
||||||
char_to_int, char_from_int, stringinternal_to_lower, stringinternal_to_upper,
|
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, int_mulmod, address_to_str, address_to_bytes, 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_poseidon,
|
||||||
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_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_g1_is_zero, mcl_bls12_381_g1_add, mcl_bls12_381_g1_mul,
|
||||||
|
@ -620,6 +620,12 @@ op_to_scode('>=') -> aeb_fate_ops:egt(?a, ?a, ?a);
|
|||||||
op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a);
|
op_to_scode('==') -> aeb_fate_ops:eq(?a, ?a, ?a);
|
||||||
op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a);
|
op_to_scode('!=') -> aeb_fate_ops:neq(?a, ?a, ?a);
|
||||||
op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a);
|
op_to_scode('!') -> aeb_fate_ops:not_op(?a, ?a);
|
||||||
|
op_to_scode('bnot') -> aeb_fate_ops:bin_not(?a, ?a);
|
||||||
|
op_to_scode('band') -> aeb_fate_ops:bin_and(?a, ?a, ?a);
|
||||||
|
op_to_scode('bor') -> aeb_fate_ops:bin_or(?a, ?a, ?a);
|
||||||
|
op_to_scode('bxor') -> aeb_fate_ops:bin_xor(?a, ?a, ?a);
|
||||||
|
op_to_scode('<<') -> aeb_fate_ops:bin_sl(?a, ?a, ?a);
|
||||||
|
op_to_scode('>>') -> aeb_fate_ops:bin_sr(?a, ?a, ?a);
|
||||||
op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a);
|
op_to_scode(map_get) -> aeb_fate_ops:map_lookup(?a, ?a, ?a);
|
||||||
op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
|
op_to_scode(map_get_d) -> aeb_fate_ops:map_lookup(?a, ?a, ?a, ?a);
|
||||||
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
|
op_to_scode(map_set) -> aeb_fate_ops:map_update(?a, ?a, ?a, ?a);
|
||||||
@ -644,7 +650,9 @@ op_to_scode(bits_intersection) -> aeb_fate_ops:bits_and(?a, ?a, ?a);
|
|||||||
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
|
op_to_scode(bits_union) -> aeb_fate_ops:bits_or(?a, ?a, ?a);
|
||||||
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
|
op_to_scode(bits_difference) -> aeb_fate_ops:bits_diff(?a, ?a, ?a);
|
||||||
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
|
op_to_scode(address_to_str) -> aeb_fate_ops:addr_to_str(?a, ?a);
|
||||||
|
op_to_scode(address_to_bytes) -> aeb_fate_ops:addr_to_bytes(?a, ?a);
|
||||||
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
op_to_scode(int_to_str) -> aeb_fate_ops:int_to_str(?a, ?a);
|
||||||
|
op_to_scode(int_mulmod) -> aeb_fate_ops:mulmod(?a, ?a, ?a, ?a);
|
||||||
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
|
op_to_scode(contract_to_address) -> aeb_fate_ops:contract_to_address(?a, ?a);
|
||||||
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
|
op_to_scode(address_to_contract) -> aeb_fate_ops:address_to_contract(?a, ?a);
|
||||||
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
|
op_to_scode(crypto_verify_sig) -> aeb_fate_ops:verify_sig(?a, ?a, ?a, ?a);
|
||||||
@ -654,6 +662,7 @@ 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(crypto_poseidon) -> aeb_fate_ops:poseidon(?a, ?a, ?a);
|
||||||
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
op_to_scode(stringinternal_sha3) -> aeb_fate_ops:sha3(?a, ?a);
|
||||||
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
op_to_scode(stringinternal_sha256) -> aeb_fate_ops:sha256(?a, ?a);
|
||||||
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
op_to_scode(stringinternal_blake2b) -> aeb_fate_ops:blake2b(?a, ?a);
|
||||||
@ -851,6 +860,13 @@ attributes(I) ->
|
|||||||
{'DIV', A, B, C} -> Pure(A, [B, C]);
|
{'DIV', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'MOD', A, B, C} -> Pure(A, [B, C]);
|
{'MOD', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'POW', A, B, C} -> Pure(A, [B, C]);
|
{'POW', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'MULMOD', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
|
{'BAND', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BOR', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BXOR', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BNOT', A, B} -> Pure(A, [B]);
|
||||||
|
{'BSL', A, B, C} -> Pure(A, [B, C]);
|
||||||
|
{'BSR', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'LT', A, B, C} -> Pure(A, [B, C]);
|
{'LT', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'GT', A, B, C} -> Pure(A, [B, C]);
|
{'GT', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'EQ', A, B, C} -> Pure(A, [B, C]);
|
{'EQ', A, B, C} -> Pure(A, [B, C]);
|
||||||
@ -903,12 +919,14 @@ attributes(I) ->
|
|||||||
{'SHA3', A, B} -> Pure(A, [B]);
|
{'SHA3', A, B} -> Pure(A, [B]);
|
||||||
{'SHA256', A, B} -> Pure(A, [B]);
|
{'SHA256', A, B} -> Pure(A, [B]);
|
||||||
{'BLAKE2B', A, B} -> Pure(A, [B]);
|
{'BLAKE2B', A, B} -> Pure(A, [B]);
|
||||||
|
{'POSEIDON', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'VERIFY_SIG', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'VERIFY_SIG_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
{'ECVERIFY_SECP256K1', A, B, C, D} -> Pure(A, [B, C, D]);
|
||||||
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
|
{'ECRECOVER_SECP256K1', A, B, C} -> Pure(A, [B, C]);
|
||||||
{'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]);
|
||||||
|
{'ADDRESS_TO_BYTES', A, B} -> Pure(A, [B]);
|
||||||
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
{'AUTH_TX_HASH', A} -> Pure(A, []);
|
||||||
{'AUTH_TX', A} -> Pure(A, []);
|
{'AUTH_TX', A} -> Pure(A, []);
|
||||||
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
{'BYTES_TO_INT', A, B} -> Pure(A, [B]);
|
||||||
|
@ -318,14 +318,19 @@ expr100() ->
|
|||||||
]).
|
]).
|
||||||
|
|
||||||
expr200() -> infixr(expr300(), binop('||')).
|
expr200() -> infixr(expr300(), binop('||')).
|
||||||
expr300() -> infixr(expr400(), binop('&&')).
|
expr300() -> infixr(expr325(), binop('&&')).
|
||||||
|
expr325() -> infixl(expr350(), binop('bor')).
|
||||||
|
expr350() -> infixl(expr375(), binop('bxor')).
|
||||||
|
expr375() -> infixl(expr400(), binop('band')).
|
||||||
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
|
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
|
||||||
expr500() -> infixr(expr600(), binop(['::', '++'])).
|
expr500() -> infixr(expr550(), binop(['::', '++'])).
|
||||||
|
expr550() -> infixl(expr600(), binop(['<<', '>>'])).
|
||||||
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
expr600() -> infixl(expr650(), binop(['+', '-'])).
|
||||||
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
expr650() -> ?RULE(many(token('-')), expr700(), prefixes(_1, _2)).
|
||||||
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
expr700() -> infixl(expr750(), binop(['*', '/', mod])).
|
||||||
expr750() -> infixl(expr800(), binop(['^'])).
|
expr750() -> infixl(expr800(), binop(['^'])).
|
||||||
expr800() -> ?RULE(many(token('!')), expr900(), prefixes(_1, _2)).
|
expr800() -> ?RULE(many(token('!')), expr850(), prefixes(_1, _2)).
|
||||||
|
expr850() -> ?RULE(many(token('bnot')), expr900(), prefixes(_1, _2)).
|
||||||
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
|
expr900() -> ?RULE(exprAtom(), many(elim()), elim(_1, _2)).
|
||||||
|
|
||||||
exprAtom() ->
|
exprAtom() ->
|
||||||
|
@ -427,15 +427,20 @@ bin_prec('..') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
|||||||
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
bin_prec('=') -> { 0, 0, 0}; %% Always printed inside '[ ]'
|
||||||
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
bin_prec('@') -> { 0, 0, 0}; %% Only in error messages
|
||||||
bin_prec('||') -> {200, 300, 200};
|
bin_prec('||') -> {200, 300, 200};
|
||||||
bin_prec('&&') -> {300, 400, 300};
|
bin_prec('&&') -> {300, 325, 300};
|
||||||
|
bin_prec('bor') -> {325, 350, 325};
|
||||||
|
bin_prec('bxor') -> {350, 375, 350};
|
||||||
|
bin_prec('band') -> {375, 400, 375};
|
||||||
bin_prec('<') -> {400, 500, 500};
|
bin_prec('<') -> {400, 500, 500};
|
||||||
bin_prec('>') -> {400, 500, 500};
|
bin_prec('>') -> {400, 500, 500};
|
||||||
bin_prec('=<') -> {400, 500, 500};
|
bin_prec('=<') -> {400, 500, 500};
|
||||||
bin_prec('>=') -> {400, 500, 500};
|
bin_prec('>=') -> {400, 500, 500};
|
||||||
bin_prec('==') -> {400, 500, 500};
|
bin_prec('==') -> {400, 500, 500};
|
||||||
bin_prec('!=') -> {400, 500, 500};
|
bin_prec('!=') -> {400, 500, 500};
|
||||||
bin_prec('++') -> {500, 600, 500};
|
bin_prec('++') -> {500, 550, 500};
|
||||||
bin_prec('::') -> {500, 600, 500};
|
bin_prec('::') -> {500, 550, 500};
|
||||||
|
bin_prec('<<') -> {550, 600, 550};
|
||||||
|
bin_prec('>>') -> {550, 600, 550};
|
||||||
bin_prec('+') -> {600, 600, 650};
|
bin_prec('+') -> {600, 600, 650};
|
||||||
bin_prec('-') -> {600, 600, 650};
|
bin_prec('-') -> {600, 600, 650};
|
||||||
bin_prec('*') -> {700, 700, 750};
|
bin_prec('*') -> {700, 700, 750};
|
||||||
@ -445,7 +450,8 @@ bin_prec('^') -> {750, 750, 800}.
|
|||||||
|
|
||||||
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
-spec un_prec(aeso_syntax:un_op()) -> {integer(), integer()}.
|
||||||
un_prec('-') -> {650, 650};
|
un_prec('-') -> {650, 650};
|
||||||
un_prec('!') -> {800, 800}.
|
un_prec('!') -> {800, 800};
|
||||||
|
un_prec('bnot') -> {850, 850}.
|
||||||
|
|
||||||
equals(Ann, A, B) ->
|
equals(Ann, A, B) ->
|
||||||
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
{app, [{format, infix} | Ann], {'=', Ann}, [A, B]}.
|
||||||
|
@ -45,7 +45,7 @@ lexer() ->
|
|||||||
|
|
||||||
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
Keywords = ["contract", "include", "let", "switch", "type", "record", "datatype", "if", "elif", "else", "function",
|
||||||
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
"stateful", "payable", "true", "false", "mod", "public", "entrypoint", "private", "indexed", "namespace",
|
||||||
"interface", "main", "using", "as", "for", "hiding"
|
"interface", "main", "using", "as", "for", "hiding", "band", "bor", "bxor", "bnot"
|
||||||
],
|
],
|
||||||
KW = string:join(Keywords, "|"),
|
KW = string:join(Keywords, "|"),
|
||||||
|
|
||||||
|
@ -105,8 +105,8 @@
|
|||||||
|
|
||||||
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
-type bin_op() :: '+' | '-' | '*' | '/' | mod | '^'
|
||||||
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
| '++' | '::' | '<' | '>' | '=<' | '>=' | '==' | '!='
|
||||||
| '||' | '&&' | '..'.
|
| '||' | '&&' | '..' | 'band' | 'bor' | 'bxor' | '>>' | '<<'.
|
||||||
-type un_op() :: '-' | '!'.
|
-type un_op() :: '-' | '!' | 'bnot'.
|
||||||
|
|
||||||
-type expr()
|
-type expr()
|
||||||
:: {lam, ann(), [arg()], expr()}
|
:: {lam, ann(), [arg()], expr()}
|
||||||
|
@ -203,6 +203,7 @@ compilable_contracts() ->
|
|||||||
"using_namespace",
|
"using_namespace",
|
||||||
"assign_patterns",
|
"assign_patterns",
|
||||||
"patterns_guards",
|
"patterns_guards",
|
||||||
|
"ceres",
|
||||||
"test" % Custom general-purpose test file. Keep it last on the list.
|
"test" % Custom general-purpose test file. Keep it last on the list.
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ simple_contracts_test_() ->
|
|||||||
%% associativity
|
%% associativity
|
||||||
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
[ RightAssoc(Op) || Op <- ["||", "&&", "::", "++"] ],
|
||||||
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
[ NonAssoc(Op) || Op <- ["==", "!=", "<", ">", "=<", ">="] ],
|
||||||
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod"] ],
|
[ LeftAssoc(Op) || Op <- ["+", "-", "*", "/", "mod", "band", "bor", "bxor", "<<", ">>"] ],
|
||||||
|
|
||||||
%% precedence
|
%% precedence
|
||||||
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
[ Stronger(Op2, Op1) || [T1 , T2 | _] <- tails(Tiers), Op1 <- T1, Op2 <- T2 ],
|
||||||
|
@ -39,7 +39,8 @@ all_tokens() ->
|
|||||||
%% Symbols
|
%% Symbols
|
||||||
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
|
lists:map(Lit, [',', '.', ';', '|', ':', '(', ')', '[', ']', '{', '}']) ++
|
||||||
%% Operators
|
%% Operators
|
||||||
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod, ':', '::', '->', '=>', '||', '&&', '!']) ++
|
lists:map(Lit, ['=', '==', '!=', '>', '<', '>=', '=<', '-', '+', '++', '*', '/', mod,
|
||||||
|
':', '::', '->', '=>', '||', '&&', '!', 'band', 'bor', 'bxor', 'bnot' ,'<<', '>>']) ++
|
||||||
%% Keywords
|
%% Keywords
|
||||||
lists:map(Lit, [contract, type, 'let', switch]) ++
|
lists:map(Lit, [contract, type, 'let', switch]) ++
|
||||||
%% Comment token (not an actual token), just for tests
|
%% Comment token (not an actual token), just for tests
|
||||||
|
14
test/contracts/ceres.aes
Normal file
14
test/contracts/ceres.aes
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
contract C =
|
||||||
|
entrypoint test() =
|
||||||
|
let a : int = 23
|
||||||
|
let b : int = 52
|
||||||
|
let c = a bor b
|
||||||
|
let d = c bxor b
|
||||||
|
let e = d band b
|
||||||
|
let f = bnot a
|
||||||
|
let g = f << 2
|
||||||
|
let h = g >> 2
|
||||||
|
let i = Int.mulmod(a, b, h)
|
||||||
|
let j = Crypto.poseidon(i, a)
|
||||||
|
let k : bytes(32) = Address.to_bytes(Call.origin)
|
||||||
|
(a bor b band c bxor a << bnot b >> a, k)
|
Loading…
x
Reference in New Issue
Block a user