Compare commits

...

58 Commits

Author SHA1 Message Date
Thomas Arts 163e805f55 Enable running properties as Eunit tests
Update model to address serialization

Update eunit test wrapper

Update tests

Add tests for serialize_type
2019-05-02 13:08:43 +02:00
Thomas Arts d69375e72b Fix types 2019-05-02 13:08:43 +02:00
Tobias Lindahl 2b3603e86f Merge pull request #33 from aeternity/PT-165760129-FATE-blockhash
Correct arity for BLOCKHASH
2019-05-02 12:28:58 +02:00
Tobias Lindahl 31d188ce81 Update test of BLOCKHASH to correct arity 2019-05-02 10:52:07 +02:00
Tobias Lindahl dfca41c98d Correct arity for BLOCKHASH 2019-05-02 10:33:26 +02:00
Hans Svensson 1526ad3bf0 Merge pull request #32 from aeternity/remove_bytes_from_aevm
Revert bytes(N) from ABI encode it in word/tuple
2019-04-23 17:43:41 +02:00
Hans Svensson 4e325ff203 Revert bytes(N) from ABI encode it in word/tuple 2019-04-23 16:21:11 +02:00
Hans Svensson e8253b0970 Merge pull request #31 from aeternity/PT-164629541-generic_hash_and_signature
Add bytes(int) as type + ecverify_secp256k1
2019-04-23 10:34:23 +02:00
Hans Svensson 230e2187ce Add bytes(int) as type + ecverify_secp256k1 2019-04-23 09:30:45 +02:00
Erik Stenman a6a2686d53 Fixing edoc build the easy way. Real documentation in Readme.md. (#30) 2019-04-15 12:18:16 +02:00
Erik Stenman 56cf62b487 Add setelement instruction. (#29) 2019-04-12 15:47:16 +02:00
Tobias Lindahl 15628c9a05 Merge pull request #28 from aeternity/PT-165079561-fate-chain-api
Differ between BALANCE for the current contract and BALANCE_OTHER for…
2019-04-12 11:43:13 +02:00
Tobias Lindahl 431b311ae5 Differ between BALANCE for the current contract and BALANCE_OTHER for a remote address 2019-04-12 11:04:56 +02:00
Thomas Arts 28f6c42647 Merge pull request #27 from aeternity/PT-165246396-prepare-remove-dependency
Pt 165246396 prepare remove dependency
2019-04-11 08:56:47 +02:00
Thomas Arts 734c8e8e40 ignore generated aefate file 2019-04-10 15:44:53 +02:00
Thomas Arts 436ba457e9 Robuster way to create binaries from strings 2019-04-10 15:43:50 +02:00
Thomas Arts 0261b76314 tuple_to_list is now replaced by a constructor and the "convenience" functions will be moved to apps/aefate tests were they are needed 2019-04-10 15:43:25 +02:00
Thomas Arts e1e2236a26 If we have destructors for maps, lists, etc, we need one for tuples as well 2019-04-10 15:42:33 +02:00
Thomas Arts 04d64dfe9b Extend QuickCheck model to use the make_BLA functions to create the fate data 2019-04-10 15:41:59 +02:00
Thomas Arts afcc6fd31a Merge pull request #26 from aeternity/PT-165180296-fix-fate-scanner
Pt 165180296 fix fate scanner
2019-04-10 10:49:33 +02:00
Thomas Arts 6466ddb866 Bump version 2019-04-09 14:02:21 +02:00
Thomas Arts 75f3eeffa7 Add quickcheck properties 2019-04-09 10:32:08 +02:00
Thomas Arts a670d1ca6c Remove the quotes from scanned string 2019-04-09 09:05:26 +02:00
Thomas Arts 96c0fab3ab Remove fate_type_type is not a fate_type 2019-04-09 08:29:55 +02:00
Thomas Arts 90659342c0 Quote the string 2019-04-08 16:29:55 +02:00
Thomas Arts f88fe008bf Add fate_bits as type 2019-04-08 16:29:45 +02:00
Thomas Arts f5f007e74d Since the binaries are likely to be strings, unicode conversion makes more sense. 2019-04-08 16:29:31 +02:00
Thomas Arts 0c01215b6a fix arities formatting 2019-04-08 14:49:17 +02:00
Thomas Arts fa64bbb56c Don't forget z 2019-04-08 13:41:23 +02:00
Thomas Arts fe7e3a638a Fix scan error 2019-04-08 12:29:18 +02:00
Thomas Arts d2c70509dd ignore more 2019-04-08 11:53:00 +02:00
Thomas Arts 15ee836ddb Explanatory comment 2019-04-08 11:53:00 +02:00
Thomas Arts 68e6a2163e fate_type_type should be {variant, List} without explicitly storing the size 2019-04-08 11:53:00 +02:00
Hans Svensson 9041423906 Merge pull request #25 from aeternity/generalized_accounts
Add Auth.tx_hash primop + primop range
2019-04-08 11:30:09 +02:00
Hans Svensson 913abb6c7b Add AUTH primops + primop range 2019-04-08 10:19:52 +02:00
Robert Virding 2d599df0ea Merge pull request #24 from aeternity/PT-164597852-move-aesophia-heap
PT-164597852 Move aesophia heap handling into aebytecode
2019-04-02 10:06:55 +02:00
Robert Virding 54aace97af Remove local blake2 module 2019-04-01 16:51:35 +02:00
Robert Virding 7e7f061b34 Move TYPEREP defs to aebytecode 2019-03-30 23:06:24 +01:00
Robert Virding 333bf53537 Change function references from aeso_sophia to aeb_aevm_data 2019-03-30 23:06:24 +01:00
Robert Virding 0528ee1229 Fix (some) references to aeso_ types 2019-03-30 23:06:24 +01:00
Robert Virding 8425eb80c5 Remove old_create_calldata function
This function contained calls back to major aesophia modules in this
module so these have now been cleared.
2019-03-30 23:06:24 +01:00
Robert Virding c00c4a5ac3 First commit of moved functions
Most function references to aeso_ have been converted to aeb_.
2019-03-30 23:06:24 +01:00
Erik Stenman 662b611e6d New representation of variant values. (#23)
* New representation of variant values.

* Specify type of elments (byte) in arities list.
2019-03-29 14:52:22 +01:00
Erik Stenman 9abeb21eee Pt 164601244 add hash and signature type (#22)
* Add hash, signature and object types.
* Add serilaize/deserialize test for new types.
* Document new types.
* Use aeserialization for base58c encoding/decoding.
2019-03-25 17:57:12 +01:00
Tobias Lindahl 34ae94e3e7 Merge pull request #21 from aeternity/PT-164626753-new-aeserialization
New version of aeserialization
2019-03-14 10:55:56 +01:00
Tobias Lindahl d4da5e69ad New version of aeserialization 2019-03-14 10:53:28 +01:00
Erik Stenman 417a34ecd1 Remove typespec from element op. (#20)
* Remove typespec from element op.

* Remove unused code from pretty printer.
2019-03-14 10:48:34 +01:00
Erik Stenman b35ccb8eb6 Pt 164460201 generate fate dispatch (#18)
* Generate docs.

* Test lists of length 16.

* Export ops definitions.
2019-03-14 10:48:11 +01:00
Tobias Lindahl 04571f757a Merge pull request #19 from aeternity/fortuna
Merge fortuna to master
2019-03-13 10:57:27 +01:00
Erik Stenman 9763a1a6f5 Pt 164460166 generate documentation (#17)
* Generate docs.

* Test lists of length 16.
2019-03-08 07:59:09 +01:00
Erik Stenman 6c60f1e37f Pre hook to build sources on Windoes also. (#16) 2019-03-06 11:41:16 +01:00
Erik Stenman 23695330ef Make rebar use make (#15)
* Handle 5 to 8 args. Generate a test file with all instructions for asm/disasm.

* Add ops to test 7 and 8 arguments.

* Make sure rebar builds sources before trying to build.

* Make CI use rebar to build to make sure it works on top level without make.
2019-03-04 13:28:15 +01:00
Erik Stenman 43652e0843 Handle 5 to 8 args. Generate a test file with all instructions for as… (#14)
* Handle 5 to 8 args. Generate a test file with all instructions for asm/disasm.

* Add ops to test 7 and 8 arguments.
2019-03-04 10:34:17 +01:00
Erik Stenman 6f67da1292 Pt 164325512 variant constants (#13)
* Handle varaint constants and types.

* Format Readme.

* Format Readme step 2.

* Format Readme step 3.

* Format Readme step 4.

* Format Readme step 5.

* Update src/aeb_fate_asm.erl

Co-Authored-By: happi <happi@stenmans.org>

* Update README.md

Co-Authored-By: happi <happi@stenmans.org>

* Get rid of size from varaint type representation.
2019-03-01 13:05:24 +01:00
Erik Stenman 20c8fbabc9 Fix bits formatting and parsing. (#12) 2019-03-01 10:36:17 +01:00
Erik Stenman fccc570bee Pt 164259596 generate format op (#10)
* Generate code for fate ops from spec.

* Generate the code from the makefile. Remove generated files.

* Test targets and cleanup.

* Spell eunit the right way.

* Use test target for ci.

* Renumber opcodes. Add primops.

* Generate tokens in scanner from definitions.

* Rename NUMBER op to GENERATION and add MICROBLOCK instruction.

* Since Tag < Size, Size cannot be zero

* unit is printed `()`

* Formatting differently

* Add eqc profile

* Generate code for fate ops from spec.

* Generate the code from the makefile. Remove generated files.

* Test targets and cleanup.

* Generate op pretty printer.

* Removed unused function.

* Polish Makefile file references (#11)

* Parse all types of values except variants.
2019-02-28 19:18:25 +01:00
Erik Stenman 8fc929b1ee Pt 164259596 generate fate ops (#9)
* Generate code for fate ops from spec.

* Generate the code from the makefile. Remove generated files.

* Test targets and cleanup.

* Spell eunit the right way.

* Use test target for ci.

* Renumber opcodes. Add primops.

* Generate tokens in scanner from definitions.

* Rename NUMBER op to GENERATION and add MICROBLOCK instruction.
2019-02-28 11:24:13 +01:00
Erik Stenman 01ae99f7e8 Removed unused enacl lib. Use eblake2 for hash. (#8)
* Removed unused lib.

* Replace local blake2 implementation with eblake2.

* Add eblake2 dep to app file.

* Add eblake2 to rebar config.

* Use hex for eblake2.

* Bump version.

* Replace local rlp with aeserialization repo. Use ref till first release is available.

* Remove unused vars.
2019-02-26 08:53:46 +01:00
37 changed files with 2952 additions and 1761 deletions
+3 -3
View File
@@ -22,13 +22,13 @@ jobs:
command: rebar3 compile
- run:
name: Static Analysis
command: rebar3 dialyzer
command: make dialyzer
- run:
name: Eunit
command: rebar3 eunit
command: make eunit
- run:
name: Common Tests
command: rebar3 ct
command: make test
- save_cache:
key: dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }}
paths:
+14
View File
@@ -10,5 +10,19 @@ rel/example_project
.rebar
aeb_asm_scan.erl
aeb_fate_asm_scan.erl
aeb_fate_asm_scan.xrl
_build/
aefateasm
include/aeb_fate_opcodes.hrl
src/aeb_fate_code.erl
src/aeb_fate_opcodes.erl
src/aeb_fate_pp.erl
*.erl~
*.hrl~
*.aes~
doc
cover
aefate
current_counterexample.eqc
.rebar3
ebin
+20 -4
View File
@@ -1,20 +1,36 @@
GENERATED_SRC = src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl
GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template
REBAR ?= rebar3
all: local
local:
sources: $(GENERATED_SRC)
local: $(GENERATED_SRC)
@$(REBAR) as local release
console:
console: local
@$(REBAR) as local shell
clean:
@$(REBAR) clean
rm -f $(GENERATED_SRC)
rm -f ebin/*
dialyzer: local
@$(REBAR) as local dialyzer
distclean: clean
@rm -rf _build/
eunit: local
@$(REBAR) as local eunit
test: local
@$(REBAR) as local eunit
ebin/%.beam: src/%.erl
erlc -o $(dir $@) $<
$(GENERATED_SRC): $(GENERATOR_DEPS)
erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/
+131 -6
View File
@@ -1,9 +1,134 @@
aebytecode
=====
# aebytecode
An library and stand alone assembler for aeternity bytecode.
An OTP library
This version supports Aevm bytecode and Fate bytecode.
Build
-----
## Build
$ make
## Fate Code
Fate code exists in 3 formats:
1. Fate byte code. This format is under consensus.
2. Fate assembler. This is a text represenation of fate code.
This is not under consensus and other
implemenation and toolchains could have
their own format.
3. Internal. This is an Erlang representation of fate code
Used by this particular engin implementation.
This library handles all tree representations.
The byte code format is described in a separate document.
The internal format is described in a separate document.
The text representation is described below.
### Fate Assembler Code
Assembler code can be read from a file.
The assembler has the following format:
Comments start with 2 semicolons and runs till end of line
`;; This is a comment`
Opcode mnemonics start with an upper case letter.
`DUP`
Identifiers start with a lower case letter
`an_identifier`
References to function arguments start with arg followed by an integer
`arg0`
References to variables/registers start with var followed by an integer
`var0`
References to stack postions is either a (for stack 0)
or start with stack followed by an integer
`stack1`
`a`
Immediate values can be of 11 types:
1. Integers as decimals: {Digits} or -{Digits}
`42`
`-2374683271468723648732648736498712634876147`
And integers as Hexadecimals:: 0x{Hexdigits}
`0x0deadbeef0`
2. Chain Objects. These are all addresses to different types of chain objects.
Each address is a 256 bits number encoded in base58 with checksum
with a prefix of "@" plus a type prefix followed by "_".
2a. Account Address: a base58c encoded number starting with @ak_ followed by a number of base58chars
'@ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2b. Contract address: @ct_{base58char}+
`@ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2c. Oracle address: @ok_{base58char}+
`@ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2d. Name address: @nm_{base58char}+
`@nm_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2e. Channel address: @ch_{base58char}+
`@ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
3. Boolean true or false
`true`
`false`
4. Strings "{Characters}"
`"Hello"`
5. Map { Key => Value }
`{}`
`{ 1 => { "foo" => true, "bar" => false}`
6. Lists [ Elements ]
`[]`
`[1, 2]`
7. Bit field < Bits > or !< Bits >
`<000>`
`<1010 1010>`
`<>`
`!<>`
8. Tuples ( Elements )
`()`
`(1, "foo")`
9. Variants: (| [Arities] | Tag | ( Elements ) |)
`(| [1,3,5,2] | 3 | ( "foo", 12) |)`
10. Hashes: #{base64char}+
`#AQIDCioLFQ==`
11. Signatures: $sg_{base58char}+
Where
Digits: [0123456789]
Hexdigits: [0123456789abcdef]
base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=]
Characters: any printable ascii character 0..255 (except " no quoting yet)
Key: any value except for a map
Bits: 01 or space
Elements: Nothing or Value , Elements
Size: Digits (0 < Size < 256)
Tag: Digits (0 =< Tag < Size)
$ rebar3 compile
View File
+49 -24
View File
@@ -1,43 +1,68 @@
-define(FATE_INTEGER_T, integer()).
-define(FATE_BYTE_T, 0..255).
-define(FATE_BOOLEAN_T, true | false).
-define(FATE_NIL_T, []).
-define(FATE_LIST_T, list()).
-define(FATE_UNIT_T, {tuple, {}}).
-define(FATE_MAP_T, #{ fate_type() => fate_type() }).
-define(FATE_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_VARIANT_T, {variant, ?FATE_BYTE_T, ?FATE_BYTE_T, tuple()}).
-define(FATE_VOID_T, void).
-define(FATE_TUPLE_T, {tuple, tuple()}).
-define(FATE_BITS_T, {bits, integer()}).
-define(FATE_INTEGER_T, integer()).
-define(FATE_BYTE_T, 0..255).
-define(FATE_BOOLEAN_T, true | false).
-define(FATE_NIL_T, []).
-define(FATE_LIST_T, list()).
-define(FATE_UNIT_T, {tuple, {}}).
-define(FATE_MAP_T, #{ fate_type() => fate_type() }).
-define(FATE_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_HASH_T, {hash, binary()}).
-define(FATE_SIGNATURE_T, {signature, binary()}).
-define(FATE_CONTRACT_T, {contract, <<_:256>>}).
-define(FATE_ORACLE_T, {oracle, <<_:256>>}).
-define(FATE_NAME_T, {name, <<_:256>>}).
-define(FATE_CHANNEL_T, {channel, <<_:256>>}).
-define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}).
-define(FATE_VOID_T, void).
-define(FATE_TUPLE_T, {tuple, tuple()}).
-define(FATE_BITS_T, {bits, integer()}).
-define(IS_FATE_INTEGER(X), is_integer(X)).
-define(IS_FATE_LIST(X), (is_list(X))).
-define(IS_FATE_STRING(X), (is_binary(X))).
-define(IS_FATE_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_INTEGER(X), (is_integer(X))).
-define(IS_FATE_LIST(X), (is_list(X))).
-define(IS_FATE_STRING(X), (is_binary(X))).
-define(IS_FATE_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_HASH(X), (is_tuple(X) andalso (hash == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_SIGNATURE(X), (is_tuple(X) andalso (signature == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_CONTRACT(X), (is_tuple(X) andalso (contract == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_ORACLE(X), (is_tuple(X) andalso (oracle == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_NAME(X), (is_tuple(X) andalso (name == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_CHANNEL(X), (is_tuple(X) andalso (channel == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_BITS(X), (is_tuple(X) andalso (bits == element(1, X) andalso is_integer(element(2, X))))).
-define(IS_FATE_VARIANT(X), (is_tuple(X)
andalso
(variant == element(1, X)
andalso is_integer(element(2, X))
andalso is_list(element(2, X))
andalso is_integer(element(3, X))
andalso is_tuple(element(4, X))
))).
-define(IS_FATE_BOOLEAN(X), is_boolean(X)).
-define(FATE_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}).
-define(FATE_ADDRESS(A), {address, A}).
-define(FATE_BITS(B), {bits, B}).
-define(FATE_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}).
-define(FATE_ADDRESS(A), {address, A}).
-define(FATE_HASH(X), {hash, X}).
-define(FATE_SIGNATURE(S), {signature, S}).
-define(FATE_CONTRACT(X), {contract, X}).
-define(FATE_ORACLE(X), {oracle, X}).
-define(FATE_NAME(X), {name, X}).
-define(FATE_CHANNEL(X), {channel, X}).
-define(FATE_BITS(B), {bits, B}).
-define(FATE_INTEGER_VALUE(X), (X)).
-define(FATE_LIST_VALUE(X), (X)).
-define(FATE_TUPLE_ELEMENTS(X), (tuple_to_list(element(2, X)))).
-define(FATE_STRING_VALUE(X), (X)).
-define(FATE_ADDRESS_VALUE(X), (element(2, X))).
-define(FATE_HASH_VALUE(X), (element(2, X))).
-define(FATE_SIGNATURE_VALUE(X), (element(2, X))).
-define(FATE_CONTRACT_VALUE(X), (element(2, X))).
-define(FATE_ORACLE_VALUE(X), (element(2, X))).
-define(FATE_NAME_VALUE(X), (element(2, X))).
-define(FATE_CHANNEL_VALUE(X), (element(2, X))).
-define(FATE_MAP_VALUE(X), (X)).
-define(FATE_MAP_SIZE(X), (map_size(X))).
-define(FATE_STRING_SIZE(X), (byte_size(X))).
@@ -47,7 +72,7 @@
-define(FATE_VOID, void).
-define(FATE_EMPTY_STRING, <<>>).
-define(FATE_STRING(S), S).
-define(FATE_VARIANT(Size, Tag,T), {variant, Size, Tag, T}).
-define(FATE_VARIANT(Arity, Tag,T), {variant, Arity, Tag, T}).
-define(MAKE_FATE_INTEGER(X), X).
-define(MAKE_FATE_LIST(X), X).
-121
View File
@@ -1,121 +0,0 @@
%% FATE opcodes
-define('NOP' , 16#00).
-define('RETURN' , 16#01).
-define('CALL' , 16#02).
-define('CALL_R' , 16#03).
-define('CALL_T' , 16#04).
-define('CALL_TR' , 16#05).
-define('JUMP' , 16#06).
-define('JUMPIF' , 16#07).
-define('SWITCH' , 16#08).
-define('PUSH' , 16#09).
-define('DUP' , 16#0a).
-define('POP' , 16#0b).
-define('STORE' , 16#10).
-define('ADD' , 16#11).
-define('MUL' , 16#12).
-define('SUB' , 16#13).
-define('DIV' , 16#14).
-define('MOD' , 16#15).
-define('POW' , 16#16).
-define('LT' , 16#17).
-define('GT' , 16#18).
-define('EQ' , 16#19).
-define('ELT' , 16#1a).
-define('EGT' , 16#1b).
-define('NEQ' , 16#1c).
-define('AND' , 16#1d).
-define('OR' , 16#1e).
-define('NOT' , 16#1f).
-define('TUPLE' , 16#20).
-define('ELEMENT' , 16#21).
-define('MAP_EMPTY' , 16#22).
-define('MAP_LOOKUP' , 16#23).
-define('MAP_UPDATE' , 16#24).
-define('MAP_DELETE' , 16#25).
-define('MAP_MEMBER' , 16#26).
-define('MAP_FROM_LIST' , 16#27).
-define('NIL' , 16#28).
-define('IS_NIL' , 16#29).
-define('CONS' , 16#2a).
-define('HD' , 16#2b).
-define('TL' , 16#2c).
-define('LENGTH' , 16#2d).
-define('STR_EQ' , 16#2e).
-define('STR_JOIN' , 16#2f).
-define('ADDR_TO_STR' , 16#30).
-define('STR_REVERSE' , 16#31).
-define('INT_TO_ADDR' , 16#32).
-define('VARIANT' , 16#33).
-define('VARIANT_TEST' , 16#34).
-define('VARIANT_ELEMENT', 16#35).
-define('BITS_NONE' , 16#36).
-define('BITS_ALL' , 16#37).
-define('BITS_SET' , 16#38).
-define('BITS_CLEAR' , 16#39).
-define('BITS_TEST' , 16#3a).
-define('BITS_SUM' , 16#3b).
-define('BITS_OR' , 16#3c).
-define('BITS_AND' , 16#3d).
-define('BITS_DIFF' , 16#3e).
-define('ADDRESS' , 16#3f).
-define('BALANCE' , 16#40).
-define('ORIGIN' , 16#41).
-define('CALLER' , 16#42).
-define('GASPRICE' , 16#43).
-define('BLOCKHASH' , 16#44).
-define('BENEFICIARY' , 16#45).
-define('TIMESTAMP' , 16#46).
-define('NUMBER' , 16#47).
-define('DIFFICULTY' , 16#48).
-define('GASLIMIT' , 16#49).
-define('GAS' , 16#4a).
-define('LOG0' , 16#4b).
-define('LOG1' , 16#4c).
-define('LOG2' , 16#4d).
-define('LOG3' , 16#4e).
-define('LOG4' , 16#4f).
-define('ABORT' , 16#50).
-define('EXIT' , 16#51).
-define('DEACTIVATE' , 16#52).
-define('INC' , 16#53).
-define('DEC' , 16#54).
-define('INT_TO_STR' , 16#55).
-define('SPEND' , 16#56).
-define('ORACLE_REGISTER', 16#57).
-define('ORACLE_QUERY' , 16#58).
-define('ORACLE_RESPOND' , 16#59).
-define('ORACLE_EXTEND' , 16#5a).
-define('ORACLE_GET_ANSWER', 16#5b).
-define('ORACLE_GET_QUESTION', 16#5c).
-define('ORACLE_QUERY_FEE', 16#5d).
-define('AENS_RESOLVE' , 16#5e).
-define('AENS_PRECLAIM' , 16#5f).
-define('AENS_CLAIM' , 16#60).
-define('AENS_UPDATE' , 16#61).
-define('AENS_TRANSFER' , 16#62).
-define('AENS_REVOKE' , 16#63).
-define('ECVERIFY' , 16#64).
-define('SHA3' , 16#65).
-define('SHA256' , 16#66).
-define('BLAKE2B' , 16#67).
-define('RETURNR' , 16#68).
-define('MAP_LOOKUPD' , 16#69).
-define('SWITCH_V2' , 16#6a).
-define('SWITCH_V3' , 16#6b).
-define('SWITCH_VN' , 16#6c).
-define('BITS_ALL_N' , 16#6d).
-define('BITS_NONEA' , 16#6e).
-define('BITS_ALLA' , 16#6f).
-define('DUPA' , 16#70).
-define('INCA' , 16#71).
-define('DECA' , 16#72).
-define('POPA' , 16#73).
-define('FUNCTION' , 16#fe).
-define('EXTEND' , 16#ff).
-define( COMMENT(X), {comment, X}).
+15
View File
@@ -0,0 +1,15 @@
-record(pmap, {key_t :: aeb_aevm_data:type(),
val_t :: aeb_aevm_data:type(),
parent :: none | non_neg_integer(),
size = 0 :: non_neg_integer(),
data :: #{aeb_heap:binary_value() => aeb_heap:binary_value() | tombstone}
| stored}).
-record(maps, { maps = #{} :: #{ non_neg_integer() => #pmap{} }
, next_id = 0 :: non_neg_integer() }).
-record(heap, { maps :: #maps{},
offset :: aeb_heap:offset(),
heap :: binary() | #{non_neg_integer() => non_neg_integer()} }).
+10 -6
View File
@@ -183,9 +183,13 @@
-define(PRIM_CALL_MAP_TOLIST, 305).
-define(PRIM_CALL_IN_CRYPTO_RANGE(__TTYPE__), (((__TTYPE__) > 399) andalso ((__TTYPE__) < 500))).
-define(PRIM_CALL_CRYPTO_ECVERIFY, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-define(PRIM_CALL_CRYPTO_ECVERIFY, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-define(PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, 410).
-define(PRIM_CALL_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))).
-define(PRIM_CALL_AUTH_TX_HASH, 500).
+11
View File
@@ -0,0 +1,11 @@
-define(Type(), aeb_aevm_data:type()).
-define(TYPEREP_WORD_TAG, 0).
-define(TYPEREP_STRING_TAG, 1).
-define(TYPEREP_LIST_TAG, 2).
-define(TYPEREP_TUPLE_TAG, 3).
-define(TYPEREP_VARIANT_TAG, 4).
-define(TYPEREP_TYPEREP_TAG, 5).
-define(TYPEREP_MAP_TAG, 6).
-define(TYPEREP_FUN_TAG, 7).
+25
View File
@@ -0,0 +1,25 @@
%%% @author Thomas Arts
%%% @doc Allow to run QuickCheck tests as eunit tests
%%% `rebar3 as eqc eunit --cover`
%%% or `rebar3 as eqc eunit --module=aeb_fate_data`
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
%%%
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aeb_fate_data_tests).
-include_lib("eunit/include/eunit.hrl").
-compile([export_all, nowarn_export_all]).
-define(EQC_EUNIT(Module, PropName, Ms),
{ atom_to_list(PropName),
{timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
quickcheck_test_() ->
{setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500),
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000)
]}.
+25
View File
@@ -0,0 +1,25 @@
%%% @author Thomas Arts
%%% @doc Allow to run QuickCheck tests as eunit tests
%%% `rebar3 as eqc eunit --cover`
%%% or `rebar3 as eqc eunit --module=aeb_fate_encoding`
%%% Note that for obtaining cover file, one needs `rebar3 as eqc cover
%%%
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts
-module(aeb_fate_encoding_tests).
-include_lib("eunit/include/eunit.hrl").
-compile([export_all, nowarn_export_all]).
-define(EQC_EUNIT(Module, PropName, Ms),
{ atom_to_list(PropName),
{timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
quickcheck_test_() ->
{setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000),
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000)
]}.
+122
View File
@@ -0,0 +1,122 @@
%%% @author Thomas Arts
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
%%%
%%% We need to be able to generate data that serializes with ?LONG_LIST, ?LONG_TUPLE etc.
%%% In other words make some rather broad terms as well as some deep terms
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile([export_all, nowarn_export_all]).
prop_roundtrip() ->
?FORALL(FateData, fate_data(),
measure(bytes, size(term_to_binary(FateData)),
begin
Serialized = aeb_fate_encoding:serialize(FateData),
?WHENFAIL(eqc:format("Serialized ~p to ~p~n", [FateData, Serialized]),
equals(aeb_fate_encoding:deserialize(Serialized), FateData))
end)).
prop_format_scan() ->
?FORALL(FateData, fate_data(),
?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]),
begin
String = aeb_fate_data:format(FateData),
{ok, _Scanned, _} = aeb_fate_asm_scan:scan(unicode:characters_to_list(String)),
true
end)).
prop_serializes() ->
?FORALL(FateDatas, non_empty(?SIZED(Size, resize(Size div 2, list(fate_data())))),
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateDatas]),
begin
{T1, Binary} =
timer:tc( fun() ->
<< begin B = aeb_fate_encoding:serialize(Data),
<<B/binary>> end || Data <- FateDatas >>
end),
{T2, {FateData, _}} =
timer:tc(fun() -> aeb_fate_encoding:deserialize_one(Binary) end),
measure(binary_size, size(Binary),
measure(encode, T1,
measure(decode, T2,
conjunction([{equal, equals(hd(FateDatas), FateData)},
{size, size(Binary) < 500000}]))))
end)).
fate_data() ->
?SIZED(Size, ?LET(Data, fate_data(Size, [map]), eqc_symbolic:eval(Data))).
fate_data(0, _Options) ->
?LAZY(
oneof([fate_integer(),
fate_boolean(),
fate_nil(),
fate_unit(),
fate_string(),
fate_address(),
fate_hash(),
fate_signature(),
fate_contract(),
fate_oracle(),
fate_name(),
fate_bits(),
fate_channel()]));
fate_data(Size, Options) ->
oneof([?LAZY(fate_data(Size - 1, Options)),
?LAZY(fate_list( fate_data(Size div 5, Options) )),
?LAZY(fate_tuple( list(fate_data(Size div 5, Options)) )),
?LAZY(fate_variant( list(fate_data(Size div 5, Options)))) ] ++
[
?LAZY(fate_map( fate_data(Size div 8, Options -- [map]),
fate_data(Size div 5, Options)))
|| lists:member(map, Options)
]).
fate_integer() -> {call, aeb_fate_data, make_integer, [oneof([int(), largeint()])]}.
fate_bits() -> {call, aeb_fate_data, make_bits, [oneof([int(), largeint()])]}.
fate_boolean() -> {call, aeb_fate_data, make_boolean, [elements([true, false])]}.
fate_nil() -> {call, aeb_fate_data, make_list, [[]]}.
fate_unit() -> {call, aeb_fate_data, make_unit, []}.
fate_string() -> {call, aeb_fate_data, make_string,
[frequency([{10, non_quote_string()}, {2, list(non_quote_string())},
{1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}])]}.
fate_address() -> {call, aeb_fate_data, make_address, [non_zero_binary(256 div 8)]}.
fate_hash() -> {call, aeb_fate_data, make_hash, [non_zero_binary(32)]}.
fate_signature() -> {call, aeb_fate_data, make_signature, [non_zero_binary(64)]}.
fate_contract() -> {call, aeb_fate_data, make_contract, [non_zero_binary(256 div 8)]}.
fate_oracle() -> {call, aeb_fate_data, make_oracle, [non_zero_binary(256 div 8)]}.
fate_name() -> {call, aeb_fate_data, make_name, [non_zero_binary(256 div 8)]}.
fate_channel() -> {call, aeb_fate_data, make_channel, [non_zero_binary(256 div 8)]}.
%% May shrink to fate_unit
fate_tuple(ListGen) ->
{call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}.
fate_variant(ListGen) ->
?LET({L1, L2, TupleAsList}, {list(choose(0, 255)), list(choose(0,255)), ListGen},
{call, aeb_fate_data, make_variant,
[L1 ++ [length(TupleAsList)] ++ L2, length(L1), list_to_tuple(TupleAsList)]}).
fate_list(Gen) ->
{call, aeb_fate_data, make_list, [frequency([{20, list(Gen)}, {1, ?LET(N, choose(64-3, 64+3), vector(N, Gen))}])]}.
fate_map(KeyGen, ValGen) ->
{call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}.
non_zero_binary(N) ->
Bits = N*8,
?SUCHTHAT(Bin, binary(N), begin <<V:Bits>> = Bin, V =/= 0 end).
non_quote_string() ->
?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []).
char() ->
choose(1, 255).
+49
View File
@@ -0,0 +1,49 @@
%%% @author Thomas Arts
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
%%% Properties for testing Fate type representations
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_type_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile([export_all, nowarn_export_all]).
prop_roundtrip() ->
?FORALL(FateType, fate_type(),
collect(FateType,
begin
Serialized = aeb_fate_encoding:serialize_type(FateType),
BinSerialized = list_to_binary(Serialized),
?WHENFAIL(eqc:format("Serialized ~p to ~p (~p)~n", [FateType, Serialized, BinSerialized]),
begin
{Type, <<>>} = aeb_fate_encoding:deserialize_type(BinSerialized),
equals(Type, FateType)
end)
end)).
fate_type() ->
?SIZED(Size, fate_type(Size)).
fate_type(0) ->
oneof([integer,
boolean,
address,
hash,
signature,
contract,
oracle,
name,
channel,
bits,
string]);
fate_type(Size) ->
oneof([?LAZY(fate_type(Size div 2)),
{list, ?LAZY(fate_type(Size div 2))},
{tuple, list(?LAZY(fate_type(Size div 2)))},
{variant, list(?LAZY(fate_type(Size div 2)))},
?LETSHRINK([T1, T2], [?LAZY(fate_type(Size div 2)), ?LAZY(fate_type(Size div 2))],
{map, T1, T2})]).
+23 -7
View File
@@ -1,14 +1,24 @@
%% -*- mode: erlang; indent-tabs-mode: nil -*-
{minimum_otp_vsn, "20.1"}.
{erl_opts, [debug_info]}.
{deps, [ {getopt, "1.0.1"}
{deps, [ {eblake2, "1.0.0"}
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
{ref, "6dce265"}}}
, {getopt, "1.0.1"}
]}.
{escript_incl_apps, [aebytecode, getopt]}.
{escript_incl_apps, [aebytecode, eblake2, aeserialization, getopt]}.
{escript_main_app, aebytecode}.
{escript_name, aefateasm}.
{escript_emu_args, "%%!"}.
{pre_hooks,
[{"(linux|darwin|solaris|win32)", compile, "make sources"},
{"(freebsd)", compile, "gmake sources"}]}.
{provider_hooks, [{post, [{compile, escriptize}]}]}.
@@ -19,8 +29,8 @@
]}.
{relx, [{release, {aessembler, "0.0.1"},
[aebytecode, getopt]},
{relx, [{release, {aebytecode, "2.0.1"},
[aebytecode, eblake2, getopt]},
{dev_mode, true},
{include_erts, false},
@@ -28,7 +38,10 @@
{extended_start_script, true}]}.
{profiles, [{binary, [
{deps, [ {getopt, "1.0.1"}
{deps, [ {eblake2, "1.0.0"}
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
{ref, "b55c372"}}}
, {getopt, "1.0.1"}
]},
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
@@ -39,5 +52,8 @@
"robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* "
"/njs /njh /nfl /ndl & exit /b 0"} % silence things
]}
]}]}.
]},
{eqc, [{erl_opts, [{parse_transform, eqc_cover}]},
{extra_src_dirs, ["quickcheck"]} %% May not be called eqc!
]}
]}.
+9 -3
View File
@@ -1,10 +1,16 @@
{"1.1.0",
[{<<"enacl">>,
{git,"https://github.com/aeternity/enacl.git",
{ref,"26180f42c0b3a450905d2efd8bc7fd5fd9cece75"}},
[{<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git",
{ref,"6dce265753af4e651f77746e77ea125145c85dd3"}},
0},
{<<"base58">>,
{git,"https://github.com/aeternity/erl-base58.git",
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
1},
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
[
{pkg_hash,[
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}
].
+150
View File
@@ -0,0 +1,150 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc
%%% Encode and decode data and function calls according to
%%% Sophia-AEVM-ABI.
%%% @end
%%% Created : 25 Jan 2018
%%%
%%%-------------------------------------------------------------------
-module(aeb_abi).
-define(HASH_SIZE, 32).
-export([ create_calldata/4
, check_calldata/2
, function_type_info/3
, function_type_hash/3
, arg_typerep_from_function/2
, type_hash_from_function_name/2
, typereps_from_type_hash/2
, function_name_from_type_hash/2
, get_function_hash_from_calldata/1
]).
-type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8.
-type function_name() :: binary(). %% String
-type typerep() :: aeb_aevm_data:type().
-type function_type_info() :: { FunctionHash :: hash()
, FunctionName :: function_name()
, ArgType :: binary() %% binary typerep
, OutType :: binary() %% binary typerep
}.
-type type_info() :: [function_type_info()].
%%%===================================================================
%%% API
%%%===================================================================
%%%===================================================================
%%% Handle calldata
create_calldata(FunName, Args, ArgTypes0, RetType) ->
ArgTypes = {tuple, ArgTypes0},
<<TypeHashInt:?HASH_SIZE/unit:8>> =
function_type_hash(list_to_binary(FunName), ArgTypes, RetType),
Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}),
{ok, Data, {tuple, [word, ArgTypes]}, RetType}.
-spec check_calldata(binary(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', atom()}.
check_calldata(CallData, TypeInfo) ->
%% The first element of the CallData should be the function name
case get_function_hash_from_calldata(CallData) of
{ok, Hash} ->
case typereps_from_type_hash(Hash, TypeInfo) of
{ok, ArgType, OutType} ->
try aeb_heap:from_binary({tuple, [word, ArgType]}, CallData) of
{ok, _Something} ->
{ok, {tuple, [word, ArgType]}, OutType};
{error, _} ->
{error, bad_call_data}
catch
_T:_E ->
{error, bad_call_data}
end;
{error, _} ->
{error, unknown_function}
end;
{error, _What} ->
{error, bad_call_data}
end.
-spec get_function_hash_from_calldata(CallData::binary()) ->
{ok, binary()} | {error, term()}.
get_function_hash_from_calldata(CallData) ->
case aeb_heap:from_binary({tuple, [word]}, CallData) of
{ok, {HashInt}} -> {ok, <<HashInt:?HASH_SIZE/unit:8>>};
{error, _} = Error -> Error
end.
%%%===================================================================
%%% Handle type info from contract meta data
-spec function_type_info(function_name(), [typerep()], typerep()) ->
function_type_info().
function_type_info(Name, ArgTypes, OutType) ->
ArgType = {tuple, ArgTypes},
{ function_type_hash(Name, ArgType, OutType)
, Name
, aeb_heap:to_binary(ArgType)
, aeb_heap:to_binary(OutType)
}.
-spec function_type_hash(function_name(), typerep(), typerep()) -> hash().
function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
Bin = iolist_to_binary([ Name
, aeb_heap:to_binary(ArgType)
, aeb_heap:to_binary(OutType)
]),
%% Calculate a 256 bit digest BLAKE2b hash value of a binary
{ok, Hash} = eblake2:blake2b(?HASH_SIZE, Bin),
Hash.
-spec arg_typerep_from_function(function_name(), type_info()) ->
{'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
arg_typerep_from_function(Function, TypeInfo) ->
case lists:keyfind(Function, 2, TypeInfo) of
{_TypeHash, Function, ArgTypeBin,_OutTypeBin} ->
case aeb_heap:from_binary(typerep, ArgTypeBin) of
{ok, ArgType} -> {ok, ArgType};
{error,_} -> {error, bad_type_data}
end;
false ->
{error, unknown_function}
end.
-spec typereps_from_type_hash(hash(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
typereps_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash,_Function, ArgTypeBin, OutTypeBin} ->
case {aeb_heap:from_binary(typerep, ArgTypeBin),
aeb_heap:from_binary(typerep, OutTypeBin)} of
{{ok, ArgType}, {ok, OutType}} -> {ok, ArgType, OutType};
{_, _} -> {error, bad_type_data}
end;
false ->
{error, unknown_function}
end.
-spec function_name_from_type_hash(hash(), type_info()) ->
{'ok', function_name()}
| {'error', 'unknown_function'}.
function_name_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash, Function,_ArgTypeBin,_OutTypeBin} ->
{ok, Function};
false ->
{error, unknown_function}
end.
-spec type_hash_from_function_name(function_name(), type_info()) ->
{'ok', hash()}
| {'error', 'unknown_function'}.
type_hash_from_function_name(Name, TypeInfo) ->
case lists:keyfind(Name, 2, TypeInfo) of
{TypeHash, Name,_ArgTypeBin,_OutTypeBin} ->
{ok, TypeHash};
false ->
{error, unknown_function}
end.
+30
View File
@@ -0,0 +1,30 @@
-module(aeb_aevm_data).
-export_type([data/0,
type/0,
heap/0]).
-type type() :: word | signed_word | string | typerep | function
| {list, type()}
| {option, type()}
| {tuple, [type()]}
| {variant, [[type()]]}.
-type data() :: none
| {some, data()}
| {option, data()}
| word
| string
| {list, data()}
| {tuple, [data()]}
| {variant, integer(), [data()]}
| integer()
| binary()
| [data()]
| {}
| {data()}
| {data(), data()}.
-type heap() :: binary().
-148
View File
@@ -1,148 +0,0 @@
%%%=============================================================================
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% BLAKE2b implementation in Erlang - for details see: https://blake2.net
%%% @end
%%%=============================================================================
-module(aeb_blake2).
-export([ blake2b/2
, blake2b/3
]).
-define(MAX_64BIT, 16#ffffffffffffffff).
-spec blake2b(HashLen :: integer(), Msg :: binary()) -> {ok, binary()}.
blake2b(HashLen, Msg) ->
blake2b(HashLen, Msg, <<>>).
-spec blake2b(HashLen :: integer(), Msg :: binary(), Key :: binary()) -> {ok, binary()}.
blake2b(HashLen, Msg0, Key) ->
%% If message should be keyed, prepend message with padded key.
Msg = <<(pad(128, Key))/binary, Msg0/binary>>,
%% Set up the initial state
Init = (16#01010000 + (byte_size(Key) bsl 8) + HashLen),
<<H0:64, H1_7/binary>> = blake_iv(),
H = <<(H0 bxor Init):64, H1_7/binary>>,
%% Perform the compression - message will be chopped into 128-byte chunks.
State = blake2b_compress(H, Msg, 0),
%% Just return the requested part of the hash
{ok, binary_part(to_little_endian(State), {0, HashLen})}.
blake2b_compress(H, <<Chunk:(128*8), Rest/binary>>, BCompr) when Rest /= <<>> ->
H1 = blake2b_compress(H, <<Chunk:(128*8)>>, BCompr + 128, false),
blake2b_compress(H1, Rest, BCompr + 128);
blake2b_compress(H, SmallChunk, BCompr) ->
Size = byte_size(SmallChunk),
FillSize = (128 - Size) * 8,
blake2b_compress(H, <<SmallChunk/binary, 0:FillSize>>, BCompr + Size, true).
blake2b_compress(H, Chunk0, BCompr, Last) ->
Chunk = to_big_endian(Chunk0),
<<V0_11:(12*64), V12:64, V13:64, V14:64, V15:64>> = <<H/binary, (blake_iv())/binary>>,
V12_ = V12 bxor (BCompr band ?MAX_64BIT),
V13_ = V13 bxor ((BCompr bsr 64) band ?MAX_64BIT),
V14_ = case Last of
false -> V14;
true -> V14 bxor ?MAX_64BIT
end,
V = <<V0_11:(12*64), V12_:64, V13_:64, V14_:64, V15:64>>,
<<VLow:(8*64), VHigh:(8*64)>> =
lists:foldl(fun(Round, Vx) -> blake2b_mix(Round, Chunk, Vx) end, V, lists:seq(0, 11)),
<<HInt:(8*64)>> = H,
<<((HInt bxor VLow) bxor VHigh):(8*64)>>.
blake2b_mix(Rnd, Chunk, V) ->
<<V0:64, V1:64, V2:64, V3:64, V4:64, V5:64, V6:64, V7:64, V8:64,
V9:64, V10:64, V11:64, V12:64, V13:64, V14:64, V15:64>> = V,
<<M0:64, M1:64, M2:64, M3:64, M4:64, M5:64, M6:64, M7:64, M8:64,
M9:64, M10:64, M11:64, M12:64, M13:64, M14:64, M15:64>> = Chunk,
Ms = {M0, M1, M2, M3, M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14, M15},
M = fun(Ix) -> element(Ix+1, Ms) end,
[S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15] = sigma(Rnd rem 10),
{Vx0, Vx4, Vx8, Vx12} = blake2b_mix(V0, V4, V8, V12, M(S0), M(S1)),
{Vx1, Vx5, Vx9, Vx13} = blake2b_mix(V1, V5, V9, V13, M(S2), M(S3)),
{Vx2, Vx6, Vx10, Vx14} = blake2b_mix(V2, V6, V10, V14, M(S4), M(S5)),
{Vx3, Vx7, Vx11, Vx15} = blake2b_mix(V3, V7, V11, V15, M(S6), M(S7)),
{Vy0, Vy5, Vy10, Vy15} = blake2b_mix(Vx0, Vx5, Vx10, Vx15, M(S8), M(S9)),
{Vy1, Vy6, Vy11, Vy12} = blake2b_mix(Vx1, Vx6, Vx11, Vx12, M(S10), M(S11)),
{Vy2, Vy7, Vy8, Vy13} = blake2b_mix(Vx2, Vx7, Vx8, Vx13, M(S12), M(S13)),
{Vy3, Vy4, Vy9, Vy14} = blake2b_mix(Vx3, Vx4, Vx9, Vx14, M(S14), M(S15)),
<<Vy0:64, Vy1:64, Vy2:64, Vy3:64, Vy4:64, Vy5:64, Vy6:64, Vy7:64, Vy8:64,
Vy9:64, Vy10:64, Vy11:64, Vy12:64, Vy13:64, Vy14:64, Vy15:64>>.
blake2b_mix(Va, Vb, Vc, Vd, X, Y) ->
Va1 = (Va + Vb + X) band ?MAX_64BIT,
Vd1 = rotr64(32, Vd bxor Va1),
Vc1 = (Vc + Vd1) band ?MAX_64BIT,
Vb1 = rotr64(24, Vb bxor Vc1),
Va2 = (Va1 + Vb1 + Y) band ?MAX_64BIT,
Vd2 = rotr64(16, Va2 bxor Vd1),
Vc2 = (Vc1 + Vd2) band ?MAX_64BIT,
Vb2 = rotr64(63, Vb1 bxor Vc2),
{Va2, Vb2, Vc2, Vd2}.
blake_iv() ->
IV0 = 16#6A09E667F3BCC908,
IV1 = 16#BB67AE8584CAA73B,
IV2 = 16#3C6EF372FE94F82B,
IV3 = 16#A54FF53A5F1D36F1,
IV4 = 16#510E527FADE682D1,
IV5 = 16#9B05688C2B3E6C1F,
IV6 = 16#1F83D9ABFB41BD6B,
IV7 = 16#5BE0CD19137E2179,
<<IV0:64, IV1:64, IV2:64, IV3:64, IV4:64, IV5:64, IV6:64, IV7:64>>.
sigma(N) ->
{_, Row} = lists:keyfind(N, 1, sigma()), Row.
sigma() ->
[{0, [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]},
{1, [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]},
{2, [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4]},
{3, [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8]},
{4, [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13]},
{5, [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]},
{6, [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11]},
{7, [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10]},
{8, [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5]},
{9, [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]}].
rotr64(N, I64) ->
<<I64rot:64>> = rotr641(N, <<I64:64>>),
I64rot.
rotr641(16, <<X:(64-16), Y:16>>) -> <<Y:16, X:(64-16)>>;
rotr641(24, <<X:(64-24), Y:24>>) -> <<Y:24, X:(64-24)>>;
rotr641(32, <<X:(64-32), Y:32>>) -> <<Y:32, X:(64-32)>>;
rotr641(63, <<X:(64-63), Y:63>>) -> <<Y:63, X:(64-63)>>.
pad(N, Bin) ->
case (N - (byte_size(Bin) rem N)) rem N of
0 -> Bin;
Pad -> <<Bin/binary, 0:(Pad *8)>>
end.
to_big_endian(Bin) -> to_big_endian(Bin, <<>>).
to_big_endian(<<>>, Acc) -> Acc;
to_big_endian(<<UInt64:1/little-unsigned-integer-unit:64, Rest/binary>>, Acc) ->
to_big_endian(Rest, <<Acc/binary, UInt64:1/big-unsigned-integer-unit:64>>).
to_little_endian(Bin) -> to_little_endian(Bin, <<>>).
to_little_endian(<<>>, Acc) -> Acc;
to_little_endian(<<UInt64:1/big-unsigned-integer-unit:64, Rest/binary>>, Acc) ->
to_little_endian(Rest, <<Acc/binary, UInt64:1/little-unsigned-integer-unit:64>>).
+447 -293
View File
@@ -1,6 +1,22 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc Assembler for Fate machine code.
%%% @end
%%%
%%% Fate code exists in 3 formats:
%%%
%%% 1. Fate byte code. This format is under consensus.
%%% 2. Fate assembler. This is a text represenation of fate code.
%%% This is not under consensus and other
%%% implemenation and toolchains could have
%%% their own format.
%%% 3. Internal. This is an Erlang representation of fate code
%%% Used by this particular engin implementation.
%%%
%%% This library handles all tree representations.
%%% The byte code format is described in a separate document.
%%% The internal format is described in a separate document.
%%% The text representation is described here:
%%%
%%% Assembler code can be read from a file.
%%% The assembler has the following format
@@ -10,38 +26,62 @@
%%% DUP
%%% Identifiers start with a lower case letter
%%% an_identifier
%%% References to function arguments start with arg
%%% References to function arguments start with arg followed by an integer
%%% arg0
%%% References to variables/registers start with var
%%% References to variables/registers start with var followed by an integer
%%% var0
%%% Immediates can be of 9 types:
%%% 1. Integers
%%% References to stack postions is either a (for stack 0)
%%% or start with stack followed by an integer
%%% stack1
%%% a
%%%
%%% Immediate values can be of 11 types:
%%% 1a. Integers as decimals: {Digits} or -{Digits}
%%% 42
%%% -2374683271468723648732648736498712634876147
%%% 2. Hexadecimal integers starting with 0x
%%% 1b. Integers as Hexadecimals:: 0x{Hexdigits}
%%% 0x0deadbeef0
%%% 3. addresses, a 256-bit hash strings starting with #
%%% followed by up to 64 hex chars
%%% #00000deadbeef
%%% 4. Boolean
%%% 2a. account addresses, a base58c encoded string prefixed with @ak_
%%% 2b. contract address: @ct_{base58char}+
%%% 2c. oracle address: @ok_{base58char}+
%%% 2d. name address: @nm_{base58char}+
%%% 2e. channel address: @ch_{base58char}+
%%% 3. Boolean true or false
%%% true
%%% false
%%% 5. Strings
%%% 4. Strings "{Characters}"
%%% "Hello"
%%% 6. Empty map
%%% 5. Map { Key => Value }
%%% {}
%%% 7. Lists
%%% { 1 => { "foo" => true, "bar" => false}
%%% 6. Lists [ Elements ]
%%% []
%%% [1, 2]
%%% 8. Bit field
%%% 7. Bit field < Bits > or !< Bits >
%%% <000>
%%% <1010>
%%% <1010 1010>
%%% <>
%%% !<>
%%% 9. Tuples
%%% 8. Tuples ( Elements )
%%% ()
%%% (1, "foo")
%%% @end
%%% 9. Variants: (| [Arities] | Tag | ( Elements ) |)
%%% (| [0,1,2] | 2 | ( "foo", 12) |)
%%% 10. Hashes: #{base64char}+
%%% #AQIDCioLFQ==
%%% 11. Signatures: $sg_{base58char}+
%%%
%%% Where Digits: [0123456789]
%%% Hexdigits: [0123456789abcdef]
%%% base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
%%% base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=]
%%% Characters any printable ascii character 0..255 (except " no quoting yet)
%%% Key: any value except for a map
%%% Bits: 01 or space
%%% Elements: Nothing or Value , Elements
%%% Size: Digits
%%% Tag: Digits
%%%
%%% Created : 21 Dec 2017
%%%-------------------------------------------------------------------
@@ -79,7 +119,7 @@ parse_function_call([{id,_,Name}, {'(',_}| Rest]) ->
to_args([{')', _}]) -> {[], []};
to_args(Tokens) ->
case to_data(Tokens) of
case parse_value(Tokens) of
{Arg, [{',', _} | Rest]} ->
{More, Rest2} = to_args(Rest),
{[Arg|More], Rest2};
@@ -87,13 +127,6 @@ to_args(Tokens) ->
{[Arg], Rest}
end.
to_data([{int,_line, Int}|Rest]) ->
{Int, Rest};
to_data([{boolean,_line, Bool}|Rest]) ->
{Bool, Rest};
to_data([{hash,_line, Hash}|Rest]) ->
{Hash, Rest}.
pp(FateCode) ->
Listing = to_asm(FateCode),
io_lib:format("~ts~n",[Listing]).
@@ -152,14 +185,6 @@ format_arg_types([T|Ts]) ->
, ", "
, format_arg_types(Ts)].
format_arg({immediate, I}) ->
aeb_fate_data:format(I);
format_arg({arg, N}) -> io_lib:format("arg~p", [N]);
format_arg({var, N}) -> io_lib:format("var~p", [N]);
format_arg({stack, 0}) -> "a";
format_arg({stack, N}) -> io_lib:format("a~p", [N]).
format_type(T) ->
%% TODO: Limit to ok types.
io_lib:format("~p", [T]).
@@ -175,157 +200,10 @@ format_code([], _) ->
"";
format_code([Op|Rest], Symbols) ->
[" ",
format_op(Op, Symbols),
aeb_fate_pp:format_op(Op, Symbols),
"\n",
format_code(Rest, Symbols)].
format_op('RETURN', _) -> "RETURN";
format_op({'RETURNR', Arg}, _) -> ["RETURNR ", format_arg(Arg)];
format_op({'CALL', {immediate, Function}}, Symbols) ->
["CALL ", lookup(Function, Symbols)];
format_op({'CALL_T', {immediate, Function}}, Symbols) ->
["CALL_T ", lookup(Function, Symbols)];
format_op({'CALL_R', {immediate, Contract}, {immediate, Function}}, Symbols) ->
["CALL_R ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)];
format_op({'CALL_R', Contract, {immediate, Function}}, Symbols) ->
["CALL_R ", format_arg(Contract), "." , lookup(Function, Symbols)];
format_op({'CALL_TR', {immediate, Contract}, {immediate, Function}}, Symbols) ->
["CALL_TR ", lookup(Contract, Symbols), "." , lookup(Function, Symbols)];
format_op({'CALL_TR', Contract, {immediate, Function}}, Symbols) ->
["CALL_TR ", format_arg(Contract), "." , lookup(Function, Symbols)];
format_op({'JUMP', {immediate, BB}}, _) ->
["JUMP ", io_lib:format("~p", [BB])];
format_op({'JUMPIF', Arg, {immediate, BB}}, _) ->
["JUMPIF ", format_arg(Arg), " ", io_lib:format("~p", [BB])];
format_op({'SWITCH_V2', Variant, {immediate, BB1}, {immediate, BB2}}, _) ->
["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2];
format_op({'SWITCH_V3', Variant, {immediate, BB1}, {immediate, BB2}, {immediate, BB3}}, _) ->
["SWITCH_V2 ", format_arg(Variant), " ", BB1, " ", BB2, " ", BB3];
format_op({'SWITCH_VN', Variant, BBs}, _) ->
["SWITCH_VN ", format_arg(Variant), [[" ", BB] || {immedate, BB} <- BBs]];
format_op({'PUSH', Arg0}, _) ->
["PUSH ", format_arg(Arg0)];
format_op('INCA', _) -> "INCA";
format_op({'INC', Name}, _) -> ["INC ", format_arg(Name)];
format_op({'DEC', Name}, _) -> ["DEC ", format_arg(Name)];
format_op('DECA', _) -> "DECA";
format_op({'ADD', Dest, Left, Right}, _) ->
["ADD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'SUB', Dest, Left, Right}, _) ->
["SUB ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'MUL', Dest, Left, Right}, _) ->
["MUL ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'DIV', Dest, Left, Right}, _) ->
["DIV ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'MOD', Dest, Left, Right}, _) ->
["MOD ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'POW', Dest, Left, Right}, _) ->
["POW ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'LT', Dest, Left, Right}, _) ->
["LT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'GT', Dest, Left, Right}, _) ->
["GT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'ELT', Dest, Left, Right}, _) ->
["ELT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'EGT', Dest, Left, Right}, _) ->
["EGT ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'EQ', Dest, Left, Right}, _) ->
["EQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'NEQ', Dest, Left, Right}, _) ->
["NEQ ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'AND', Dest, Left, Right}, _) ->
["AND ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'OR', Dest, Left, Right}, _) ->
["OR ", format_arg(Dest), " ", format_arg(Left), " ", format_arg(Right)];
format_op({'NOT', Dest, Name}, _) ->
["NOT ", format_arg(Dest), " ", format_arg(Name)];
format_op({'TUPLE', {immediate, Size}}, _) ->
["TUPLE ", io_lib:format("~p", [Size])];
format_op({'ELEMENT', Type, Dest, Which, Tuple}, _) ->
[ "ELEMENT "
, io_lib:format("~p ", [Type])
, format_arg(Dest), " "
, format_arg(Which), " "
, format_arg(Tuple)];
format_op({'MAP_EMPTY', Dest}, _) ->
["MAP_EMPTY ", format_arg(Dest)];
format_op({'MAP_LOOKUP', Dest, Map, Key}, _) ->
["MAP_LOOKUP ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key)];
format_op({'MAP_DELETE', Dest, Map, Key}, _) ->
["MAP_DELETE ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key)];
format_op({'MAP_LOOKUPD', Dest, Map, Key, Default}, _) ->
["MAP_LOOKUPD ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key), " ", format_arg(Default)];
format_op({'MAP_UPDATE', Dest, Map, Key, Value}, _) ->
["MAP_UPDATE ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key), " ", format_arg(Value)];
format_op({'MAP_MEMBER', Dest, Map, Key}, _) ->
["MAP_MEMBER ", format_arg(Dest), " "
, format_arg(Map), " ", format_arg(Key)];
format_op({'MAP_FROM_LIST', Dest, List}, _) ->
["MAP_FROM_LIST ", format_arg(Dest), " ", format_arg(List)];
format_op({'NIL', Dest}, _) ->
["NIL ", format_arg(Dest)];
format_op({'IS_NIL', Dest, List}, _) ->
["IS_NIL ", format_arg(Dest), " ", format_arg(List)];
format_op({'CONS', Dest, Hd, Tl}, _) ->
["CONS ", format_arg(Dest), " ", format_arg(Hd), " ", format_arg(Tl)];
format_op({'HD', Dest, List}, _) ->
["HD ", format_arg(Dest), " ", format_arg(List)];
format_op({'TL', Dest, List}, _) ->
["TL ", format_arg(Dest), " ", format_arg(List)];
format_op({'LENGTH', Dest, List}, _) ->
["LENGTH ", format_arg(Dest), " ", format_arg(List)];
format_op({'STR_EQ', Dest, Str1, Str2}, _) ->
["STR_EQ ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)];
format_op({'STR_JOIN', Dest, Str1, Str2}, _) ->
["STR_JOIN ", format_arg(Dest), " ", format_arg(Str1), format_arg(Str2)];
format_op({'INT_TO_STR', Dest, Str}, _) ->
["INT_TO_STR ", format_arg(Dest), " ", format_arg(Str)];
format_op({'ADDR_TO_STR', Dest, Str}, _) ->
["ADDR_TO_STR ", format_arg(Dest), " ", format_arg(Str)];
format_op({'STR_REVERSE', Dest, Str}, _) ->
["STR_REVERSE ", format_arg(Dest), " ", format_arg(Str)];
format_op({'INT_TO_ADDR', Dest, Str}, _) ->
["INT_TO_ADDR ", format_arg(Dest), " ", format_arg(Str)];
format_op({'VARIANT_TEST', Dest, Variant, Tag}, _) ->
["VARIANT_TEST ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Tag)];
format_op({'VARIANT_ELEMENT', Dest, Variant, Index}, _) ->
["VARIANT_ELEMENT ", format_arg(Dest), " ", format_arg(Variant), " ", format_arg(Index)];
format_op({'VARIANT', Dest, SizeA, TagA, ElementsA}, _) ->
["VARIANT ", format_arg(Dest), " ", format_arg(SizeA), " "
, format_arg(TagA), " ", format_arg(ElementsA)];
format_op('BITS_NONEA', _) -> "BITS_NONEA ";
format_op({'BITS_NONE', To}, _) -> ["BITS_NONE ", format_arg(To)];
format_op('BITS_ALLA', _) -> "BITS_ALLA";
format_op({'BITS_ALL', To}, _) -> ["BITS_ALL ", format_arg(To)];
format_op({'BITS_ALL_N', To, N}, _) ->
["BITS_ALL_N ", format_arg(To), " ", format_arg(N)];
format_op({'BITS_SET', To, Bits, Bit}, _) ->
["BITS_SET ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_CLEAR', To, Bits, Bit}, _) ->
["BITS_CLEAR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_TEST', To, Bits, Bit}, _) ->
["BITS_TEST ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_SUM', To, Bits}, _) ->
["BITS_SUM ", format_arg(To), " ", format_arg(Bits)];
format_op({'BITS_OR', To, Bits, Bit}, _) ->
["BITS_OR ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_AND', To, Bits, Bit}, _) ->
["BITS_AND ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op({'BITS_DIFF', To, Bits, Bit}, _) ->
["BITS_DIFF ", format_arg(To), " ", format_arg(Bits), " ", format_arg(Bit)];
format_op('DUPA', _) -> "DUPA";
format_op({'DUP', {immediate, N}}, _) ->
["DUP ", io_lib:format("~p", [N])];
format_op({'POP', Dest}, _) ->
["POP ", format_arg(Dest)];
format_op({'STORE', Var, What}, _) ->
["STORE ", format_arg(Var), " ", format_arg(What)];
format_op('NOP', _) -> "NOP".
read_file(Filename) ->
{ok, File} = file:read_file(Filename),
@@ -350,10 +228,10 @@ asm_to_bytecode(AssemblerCode, Options) ->
Signatures = serialize_sigs(Env),
SymbolTable = serialize_symbol_table(Env),
Annotatations = serialize_annotations(Env),
ByteCode = << (aeb_rlp:encode(list_to_binary(ByteList)))/binary,
(aeb_rlp:encode(list_to_binary(Signatures)))/binary,
(aeb_rlp:encode(SymbolTable))/binary,
(aeb_rlp:encode(Annotatations))/binary
ByteCode = << (aeser_rlp:encode(list_to_binary(ByteList)))/binary,
(aeser_rlp:encode(list_to_binary(Signatures)))/binary,
(aeser_rlp:encode(SymbolTable))/binary,
(aeser_rlp:encode(Annotatations))/binary
>>,
case proplists:lookup(pp_hex_string, Options) of
@@ -366,14 +244,14 @@ asm_to_bytecode(AssemblerCode, Options) ->
{Env, ByteCode}.
strip(ByteCode) ->
{Code, _Rest} = aeb_rlp:decode_one(ByteCode),
{Code, _Rest} = aeser_rlp:decode_one(ByteCode),
Code.
bytecode_to_fate_code(Bytes, _Options) ->
{ByteCode, Rest1} = aeb_rlp:decode_one(Bytes),
{Signatures, Rest2} = aeb_rlp:decode_one(Rest1),
{SymbolTable, Rest3} = aeb_rlp:decode_one(Rest2),
{Annotations, <<>>} = aeb_rlp:decode_one(Rest3),
{ByteCode, Rest1} = aeser_rlp:decode_one(Bytes),
{Signatures, Rest2} = aeser_rlp:decode_one(Rest1),
{SymbolTable, Rest3} = aeser_rlp:decode_one(Rest2),
{Annotations, <<>>} = aeser_rlp:decode_one(Rest3),
Env1 = deserialize(ByteCode, #{ function => none
, bb => 0
@@ -450,33 +328,17 @@ deserialize(<<>>, #{ function := {F, Sig}
, code => #{}
, functions => Funs#{F => {Sig, FunctionCode}}}.
deserialize_op(?ELEMENT, Rest, Code) ->
{Type, Rest2} = deserialize_type(Rest),
<<ArgType:8, Rest3/binary>> = Rest2,
{Arg0, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg1, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg2, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
{Rest6, [{ aeb_fate_opcodes:mnemonic(?ELEMENT)
, Type
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}}
| Code]};
deserialize_op(?SWITCH_VN, Rest, Code) ->
<<ArgType:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
case aeb_fate_encoding:deserialize_one(Rest3) of
{N, Rest4} when is_integer(N), N >= 0 ->
{L, Rest4} when is_list(L) ->
Modifier0 = bits_to_modifier(ArgType band 2#11),
immediate = bits_to_modifier((ArgType bsr 2) band 2#11),
{BBs, Rest5} = deserialize_n(N, Rest4),
{Rest5, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
{Rest4, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
, {Modifier0, Arg0}
, {immediate, N}
, list_to_tuple(BBs)}
, {immediate, L}
}
| Code]};
_ -> exit(bad_argument_to_switch_vn)
end;
@@ -525,17 +387,107 @@ deserialize_op(Op, Rest, Code) ->
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}}
| Code]}
| Code]};
5 ->
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
{Rest7, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}
, {Modifier4, Arg4}
}
| Code]};
6 ->
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
{Rest8, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}
, {Modifier4, Arg4}
, {Modifier5, Arg5}
}
| Code]};
7 ->
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
{Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11),
{Rest9, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}
, {Modifier4, Arg4}
, {Modifier5, Arg5}
, {Modifier6, Arg6}
}
| Code]};
8 ->
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
{Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8),
{Arg7, Rest10} = aeb_fate_encoding:deserialize_one(Rest9),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11),
Modifier7 = bits_to_modifier((ArgType2 bsr 6) band 2#11),
{Rest10, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}
, {Modifier4, Arg4}
, {Modifier5, Arg5}
, {Modifier6, Arg6}
, {Modifier7, Arg7}
}
| Code]}
end.
deserialize_n(N, Binary) ->
deserialize_n(N, Binary, []).
deserialize_n(0, Binary, Acc) ->
{lists:reverse(Acc), Binary};
deserialize_n(N, Binary, Acc) ->
{Value, Rest} = aeb_fate_encoding:deserialize_one(Binary),
deserialize_n(N-1, Rest, [Value|Acc]).
@@ -575,11 +527,114 @@ serialize(#{functions := Functions} =_Env) ->
%% bitpos: 6 4 2 0
%% xx xx xx xx
%% Arg3 Arg2 Arg1 Arg0
%% For 5-8 args another Argument Spec Byte is used
%% Bit pattern
%% 00 : stack/unused (depending on instruction)
%% 01 : argN
%% 10 : varN
%% 11 : immediate
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}
, {Arg4Type, Arg4}
, {Arg5Type, Arg5}
, {Arg6Type, Arg6}
, {Arg7Type, Arg7}
| Rest]) ->
ArgSpec1 =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
ArgSpec2 =
modifier_bits(Arg4Type) bor
(modifier_bits(Arg5Type) bsl 2) bor
(modifier_bits(Arg6Type) bsl 4) bor
(modifier_bits(Arg7Type) bsl 6),
[ ArgSpec1
, ArgSpec2
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
, serialize_data(Arg4Type, Arg4)
, serialize_data(Arg5Type, Arg5)
, serialize_data(Arg6Type, Arg6)
, serialize_data(Arg7Type, Arg7)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}
, {Arg4Type, Arg4}
, {Arg5Type, Arg5}
, {Arg6Type, Arg6}
| Rest]) ->
ArgSpec1 =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
ArgSpec2 =
modifier_bits(Arg4Type) bor
(modifier_bits(Arg5Type) bsl 2) bor
(modifier_bits(Arg6Type) bsl 4),
[ ArgSpec1
, ArgSpec2
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
, serialize_data(Arg4Type, Arg4)
, serialize_data(Arg5Type, Arg5)
, serialize_data(Arg6Type, Arg6)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}
, {Arg4Type, Arg4}
, {Arg5Type, Arg5}
| Rest]) ->
ArgSpec1 =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
ArgSpec2 =
modifier_bits(Arg4Type) bor
(modifier_bits(Arg5Type) bsl 2),
[ ArgSpec1
, ArgSpec2
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
, serialize_data(Arg4Type, Arg4)
, serialize_data(Arg5Type, Arg5)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}
, {Arg4Type, Arg4}
| Rest]) ->
ArgSpec1 =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
ArgSpec2 =
modifier_bits(Arg4Type),
[ ArgSpec1
, ArgSpec2
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
, serialize_data(Arg4Type, Arg4)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
@@ -625,43 +680,21 @@ serialize_code([ {Arg0Type, Arg0} | Rest]) ->
[ArgSpec
, serialize_data(Arg0Type, Arg0)
| serialize_code(Rest)];
serialize_code([ ?ELEMENT
, ResType
| Rest]) ->
[?ELEMENT,
serialize_type(ResType)
| serialize_code(Rest)];
serialize_code([ ?SWITCH_VN
, {Arg0Type, Arg0}
, {immediate, N}
| Rest]) when is_integer(N), N >= 0 ->
, {immediate, L}
| Rest]) ->
ArgSpec =
modifier_bits(Arg0Type) bor
(modifier_bits(immediate) bsl 2),
{Serialization, Rest2} = serialize_n_ints(N, Rest),
[?SWITCH_VN
, ArgSpec
, serialize_data(Arg0Type, Arg0)
, serialize_data(immediate, N)
| Serialization] ++ serialize_code(Rest2);
, serialize_data(immediate, L)] ++ serialize_code(Rest);
serialize_code([B|Rest]) ->
[B | serialize_code(Rest)];
serialize_code([]) -> [].
serialize_n_ints(N, Rest) ->
serialize_n_ints(N, Rest, []).
serialize_n_ints(0, Rest, Acc) ->
%% Acc is a list of binaries.
{lists:reverse(Acc), Rest};
serialize_n_ints(N, [Int|Rest], Acc) when is_integer(Int), Int >= 0 ->
serialize_n_ints(N - 1, Rest, [aeb_fate_encoding:serialize(Int)|Acc]);
serialize_n_ints(_, [], _) ->
exit(not_enough_bbs_for_switch_vn);
serialize_n_ints(_, _, _) ->
exit(bad_bbs_value_for_switch_vn).
%% 00 : stack/unused (depending on instruction)
%% 01 : argN
@@ -681,36 +714,16 @@ serialize_data(_, Data) ->
aeb_fate_encoding:serialize(Data).
serialize_signature({Args, RetType}) ->
[serialize_type({tuple, Args}) |
serialize_type(RetType)].
[aeb_fate_encoding:serialize_type({tuple, Args}) |
aeb_fate_encoding:serialize_type(RetType)].
deserialize_signature(Binary) ->
{{tuple, Args}, Rest} = deserialize_type(Binary),
{RetType, Rest2} = deserialize_type(Rest),
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary),
{RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest),
{{Args, RetType}, Rest2}.
deserialize_type(<<0, Rest/binary>>) -> {integer, Rest};
deserialize_type(<<1, Rest/binary>>) -> {boolean, Rest};
deserialize_type(<<2, Rest/binary>>) ->
{T, Rest2} = deserialize_type(Rest),
{{list, T}, Rest2};
deserialize_type(<<3, N, Rest/binary>>) ->
{Ts, Rest2} = deserialize_types(N, Rest, []),
{{tuple, Ts}, Rest2};
deserialize_type(<<4, Rest/binary>>) -> {address, Rest};
deserialize_type(<<5, Rest/binary>>) -> {bits, Rest};
deserialize_type(<<6, Rest/binary>>) ->
{K, Rest2} = deserialize_type(Rest),
{V, Rest3} = deserialize_type(Rest2),
{{map, K, V}, Rest3}.
deserialize_types(0, Binary, Acc) ->
{lists:reverse(Acc), Binary};
deserialize_types(N, Binary, Acc) ->
{T, Rest} = deserialize_type(Binary),
deserialize_types(N-1, Rest, [T | Acc]).
to_hexstring(ByteList) ->
@@ -729,10 +742,6 @@ to_bytecode([{function,_line, 'FUNCTION'}|Rest], Address, Env, Code, Opts) ->
Env2 = insert_fun(Address, Code, Env),
{Fun, Rest2} = to_fun_def(Rest),
to_bytecode(Rest2, Fun, Env2, [], Opts);
to_bytecode([{mnemonic,_line, 'ELEMENT'}|Rest], Address, Env, Code, Opts) ->
OpCode = aeb_fate_opcodes:m_to_op('ELEMENT'),
{RetType, Rest2} = to_type(Rest),
to_bytecode(Rest2, Address, Env, [RetType, OpCode|Code], Opts);
to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) ->
OpCode = aeb_fate_opcodes:m_to_op(Op),
to_bytecode(Rest, Address, Env, [OpCode|Code], Opts);
@@ -746,11 +755,66 @@ to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{immediate, Int}|Code], Opts);
to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{immediate, Bool}|Code], Opts);
to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{immediate, Hash}|Code], Opts);
to_bytecode([{string,_line, String}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_string(String)}|Code],
Opts);
to_bytecode([{object,_line, {address, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_address(Value)}|Code],
Opts);
to_bytecode([{object,_line, {contract, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_contract(Value)}|Code],
Opts);
to_bytecode([{object,_line, {oracle, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_oracle(Value)}|Code],
Opts);
to_bytecode([{object,_line, {name, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_name(Value)}|Code],
Opts);
to_bytecode([{object,_line, {channel, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_contract(Value)}|Code],
Opts);
to_bytecode([{hash,_line, Value}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_hash(Value)}|Code],
Opts);
to_bytecode([{signature,_line, {signature, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_signature(Value)}|Code],
Opts);
to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) ->
{Hash, Env2} = insert_symbol(ID, Env),
to_bytecode(Rest, Address, Env2, [{immediate, Hash}|Code], Opts);
to_bytecode([{'{',_line}|Rest], Address, Env, Code, Opts) ->
{Map, Rest2} = parse_map(Rest),
to_bytecode(Rest2, Address, Env, [{immediate, Map}|Code], Opts);
to_bytecode([{'[',_line}|Rest], Address, Env, Code, Opts) ->
{List, Rest2} = parse_list(Rest),
to_bytecode(Rest2, Address, Env, [{immediate, List}|Code], Opts);
to_bytecode([{'(',_line}|Rest], Address, Env, Code, Opts) ->
{Elements, Rest2} = parse_tuple(Rest),
Tuple = aeb_fate_data:make_tuple(list_to_tuple(Elements)),
to_bytecode(Rest2, Address, Env, [{immediate, Tuple}|Code], Opts);
to_bytecode([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) ->
{Arities, Tag, Values, Rest} = parse_variant(Tokens),
Variant = aeb_fate_data:make_variant(Arities, Tag, Values),
to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts);
to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts);
to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) ->
Env2 = insert_annotation(comment, Line, Comment, Env),
to_bytecode(Rest, Address, Env2, Code, Opts);
@@ -767,6 +831,95 @@ to_bytecode([], Address, Env, Code, Opts) ->
end,
Env2.
parse_map([{'}',_line}|Rest]) ->
{#{}, Rest};
parse_map(Tokens) ->
{Key, [{arrow, _} | Rest]} = parse_value(Tokens),
{Value, Rest2} = parse_value(Rest),
case Rest2 of
[{',',_} | Rest3] ->
{Map, Rest4} = parse_map(Rest3),
{Map#{Key => Value}, Rest4};
[{'}',_} | Rest3] ->
{#{Key => Value}, Rest3}
end.
parse_list([{']',_line}|Rest]) ->
{[], Rest};
parse_list(Tokens) ->
{Head , Rest} = parse_value(Tokens),
case Rest of
[{',',_} | Rest2] ->
{Tail, Rest3} = parse_list(Rest2),
{[Head | Tail], Rest3};
[{']',_} | Rest3] ->
{[Head], Rest3}
end.
parse_tuple([{')',_line}|Rest]) ->
{[], Rest};
parse_tuple(Tokens) ->
{Head , Rest} = parse_value(Tokens),
case Rest of
[{',',_} | Rest2] ->
{Tail, Rest3} = parse_tuple(Rest2),
{[Head | Tail], Rest3};
[{')',_} | Rest3] ->
{[Head], Rest3}
end.
parse_variant([{start_variant,_line}
, {'[', _line}
| Rest]) ->
{Arities, Rest2} = parse_list(Rest),
%% Make sure Arities is a list of bytes.
Arities = [A || A <- Arities,
is_integer(A), A < 256],
[{'|',_}
, {int,_line, Tag}
, {'|',_}
, {'(',_} | Rest3] = Rest2,
{Elements , [{end_variant, _} | Rest4]} = parse_tuple(Rest3),
Size = length(Arities),
if 0 =< Tag, Tag < Size ->
Arity = lists:nth(Tag+1, Arities),
if length(Elements) =:= Arity ->
{Arities, Tag, list_to_tuple(Elements), Rest4}
end
end.
parse_value([{int,_line, Int} | Rest]) -> {Int, Rest};
parse_value([{boolean,_line, Bool} | Rest]) -> {Bool, Rest};
parse_value([{'{',_line} | Rest]) -> parse_map(Rest);
parse_value([{'[',_line} | Rest]) -> parse_list(Rest);
parse_value([{'(',_line} | Rest]) ->
{T, Rest2} = parse_tuple(Rest),
{aeb_fate_data:make_tuple(list_to_tuple(T)), Rest2};
parse_value([{bits,_line, Bits} | Rest]) ->
{aeb_fate_data:make_bits(Bits), Rest};
parse_value([{start_variant,_line}|_] = Tokens) ->
{Arities, Tag, Values, Rest} = parse_variant(Tokens),
Variant = aeb_fate_data:make_variant(Arities, Tag, Values),
{Variant, Rest};
parse_value([{string,_line, String} | Rest]) ->
{aeb_fate_data:make_string(String), Rest};
parse_value([{address,_line, {address, Address}} | Rest]) ->
{aeb_fate_data:make_address(Address), Rest};
parse_value([{address,_line, {contract, Address}} | Rest]) ->
{aeb_fate_data:make_contract(Address), Rest};
parse_value([{address,_line, {oracle, Address}} | Rest]) ->
{aeb_fate_data:make_oracle(Address), Rest};
parse_value([{address,_line, {name, Address}} | Rest]) ->
{aeb_fate_data:make_name(Address), Rest};
parse_value([{address,_line, {channel, Address}} | Rest]) ->
{aeb_fate_data:make_channel(Address), Rest};
parse_value([{hash,_line, Hash} | Rest]) ->
{aeb_fate_data:make_hash(Hash), Rest};
parse_value([{signature,_line, Hash} | Rest]) ->
{aeb_fate_data:make_signature(Hash), Rest}.
to_fun_def([{id, _, Name}, {'(', _} | Rest]) ->
{ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest),
@@ -786,11 +939,17 @@ to_arg_types(Tokens) ->
%% Type handling
to_type([{id, _, "integer"} | Rest]) -> {integer, Rest};
to_type([{id, _, "boolean"} | Rest]) -> {boolean, Rest};
to_type([{id, _, "string"} | Rest]) -> {string, Rest};
to_type([{id, _, "address"} | Rest]) -> {address, Rest};
to_type([{id, _, "bits"} | Rest]) -> {bits, Rest};
to_type([{id, _, "integer"} | Rest]) -> {integer, Rest};
to_type([{id, _, "boolean"} | Rest]) -> {boolean, Rest};
to_type([{id, _, "string"} | Rest]) -> {string, Rest};
to_type([{id, _, "address"} | Rest]) -> {address, Rest};
to_type([{id, _, "contract"} | Rest]) -> {contract, Rest};
to_type([{id, _, "oracle"} | Rest]) -> {oracle, Rest};
to_type([{id, _, "name"} | Rest]) -> {name, Rest};
to_type([{id, _, "channel"} | Rest]) -> {channel, Rest};
to_type([{id, _, "hash"} | Rest]) -> {hash, Rest};
to_type([{id, _, "signature"} | Rest]) -> {signature, Rest};
to_type([{id, _, "bits"} | Rest]) -> {bits, Rest};
to_type([{'{', _}, {id, _, "list"}, {',', _} | Rest]) ->
%% TODO: Error handling
{ListType, [{'}', _}| Rest2]} = to_type(Rest),
@@ -803,7 +962,15 @@ to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) ->
%% TODO: Error handling
{KeyType, [{',', _}| Rest2]} = to_type(Rest),
{ValueType, [{'}', _}| Rest3]} = to_type(Rest2),
{{map, KeyType, ValueType}, Rest3}.
{{map, KeyType, ValueType}, Rest3};
to_type([{'{', _}
, {id, _, "variant"}
, {',', _}
, {'[', _}
| Rest]) ->
{ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest),
{{variant, ElementTypes}, Rest2}.
to_list_of_types([{']', _} | Rest]) -> {[], Rest};
to_list_of_types(Tokens) ->
@@ -816,19 +983,6 @@ to_list_of_types(Tokens) ->
end.
serialize_type(integer) -> [0];
serialize_type(boolean) -> [1];
serialize_type({list, T}) -> [2 | serialize_type(T)];
serialize_type({tuple, Ts}) ->
case length(Ts) of
N when N =< 255 ->
[3, N | [serialize_type(T) || T <- Ts]]
end;
serialize_type(address) -> 4;
serialize_type(bits) -> 5;
serialize_type({map, K, V}) -> [6 | serialize_type(K) ++ serialize_type(V)].
%% -------------------------------------------------------------------
%% Helper functions
%% -------------------------------------------------------------------
@@ -844,7 +998,7 @@ insert_fun({Name, Type, RetType}, Code, #{functions := Functions} = Env) ->
mk_hash(Id) ->
%% Use first 4 bytes of blake hash
{ok, <<A:8, B:8, C:8, D:8,_/binary>> } = aeb_blake2:blake2b(?HASH_BYTES, list_to_binary(Id)),
{ok, <<A:8, B:8, C:8, D:8,_/binary>> } = eblake2:blake2b(?HASH_BYTES, list_to_binary(Id)),
<<A,B,C,D>>.
%% Handle annotations
+142
View File
@@ -0,0 +1,142 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, aeternity Anstalt
%%% @doc
%%% Handling FATE code.
%%% @end
%%% ###REPLACEWITHNOTE###
%%%-------------------------------------------------------------------
Definitions.
DIGIT = [0-9]
HEXDIGIT = [0-9a-fA-F]
LOWER = [a-z_]
UPPER = [A-Z]
BASE58 = [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
BASE64 = [A-Za-z0-9+/=]
INT = {DIGIT}+
HEX = 0x{HEXDIGIT}+
OBJECT = @[a-z][a-z]_{BASE58}+
HASH = #{BASE64}+
SIG = \$sg_{BASE58}+
WS = [\000-\s]
ID = {LOWER}[a-zA-Z0-9_]*
STRING = "[^"]*"
BITS = (\!)?\<[\s01]*\>
Rules.
arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}.
var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}.
a : {token, {stack, TokenLine, 0}}.
a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}.
true : {token, {boolean, TokenLine, true}}.
false : {token, {boolean, TokenLine, false}}.
%% ###REPLACEWITHOPTOKENS###
FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
{HASH} :
{token, {hash, TokenLine, parse_hash(TokenChars)}}.
{SIG} :
{token, {signature, TokenLine, parse_object(TokenChars)}}.
{OBJECT} :
{token, {object, TokenLine, parse_object(TokenChars)}}.
{ID} :
{token, {id, TokenLine, TokenChars}}.
{HEX} :
{token, {int, TokenLine, parse_hex(TokenChars)}}.
{INT} :
{token, {int, TokenLine, parse_int(TokenChars)}}.
-{INT} :
{token, {int, TokenLine, parse_int(TokenChars)}}.
%% Due to the definition of STRING the tokens start and end with a quote ".
{STRING} :
{token, {string, TokenLine, unicode:characters_to_binary(
lists:sublist(TokenChars, 2, length(TokenChars) - 2))}}.
{BITS} :
{token, {bits, TokenLine, bits(TokenChars)}}.
%% Symbols
\-\> : {token, {to, TokenLine}}.
\: : {token, {to, TokenLine}}.
\=\> : {token, {arrow, TokenLine}}.
\(\| : {token, {start_variant, TokenLine}}.
\|\) : {token, {end_variant, TokenLine}}.
, : {token, {',', TokenLine}}.
\( : {token, {'(', TokenLine}}.
\) : {token, {')', TokenLine}}.
\[ : {token, {'[', TokenLine}}.
\] : {token, {']', TokenLine}}.
\{ : {token, {'{', TokenLine}}.
\} : {token, {'}', TokenLine}}.
\| : {token, {'|', TokenLine}}.
;;.* :
{token, {comment, TokenLine, drop_prefix($;, TokenChars)}}.
\. : skip_token.
%% Whitespace ignore
{WS} : skip_token.
%% Comments (TODO: nested comments)
. : {error, "Unexpected token: " ++ TokenChars}.
Erlang code.
-export([scan/1]).
-dialyzer({nowarn_function, yyrev/2}).
-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
parse_int(Chars) -> list_to_integer(Chars).
parse_arg("arg" ++ N) -> list_to_integer(N).
parse_var("var" ++ N) -> list_to_integer(N).
parse_acc("a" ++ N) -> list_to_integer(N).
parse_hash("#" ++ Chars) ->
base64:decode(Chars).
parse_object([_|Chars]) ->
case aeser_api_encoder:decode(unicode:characters_to_binary(Chars)) of
{account_pubkey, Bin} -> {address, Bin};
{contract_pubkey, Bin} -> {contract, Bin};
{oracle_pubkey, Bin} -> {oracle, Bin};
{name, Bin} -> {name, Bin};
{channel, Bin} -> {channel, Bin};
{signature, Bin} -> {signature, Bin}
end.
scan(S) ->
string(S).
drop_prefix(C, [C|Rest]) ->
drop_prefix(C, Rest);
drop_prefix(_, Tail) -> Tail.
bits([$!, $< | Rest]) ->
bits(Rest, -1);
bits([$< | Rest]) ->
bits(Rest, 0).
bits([$> |_Rest], Acc) -> Acc;
bits([$0 | Rest], Acc) -> bits(Rest, Acc bsl 1);
bits([$1 | Rest], Acc) -> bits(Rest, (Acc bsl 1) bor 1);
bits([$ | Rest], Acc) -> bits(Rest, Acc).
-207
View File
@@ -1,207 +0,0 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, aeternity Anstalt
%%% @doc
%%% Handling FATE code.
%%% @end
%%% Created : 9 Jan 2019
%%%-------------------------------------------------------------------
Definitions.
DIGIT = [0-9]
HEXDIGIT = [0-9a-fA-F]
LOWER = [a-z_]
UPPER = [A-Z]
INT = {DIGIT}+
HEX = 0x{HEXDIGIT}+
HASH = #{HEXDIGIT}+
WS = [\000-\s]
ID = {LOWER}[a-zA-Z0-9_]*
Rules.
arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}.
var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}.
a : {token, {stack, TokenLine, 0}}.
a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}.
true : {token, {boolean, TokenLine, true}}.
false : {token, {boolean, TokenLine, false}}.
RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}.
RETURNR : {token, {mnemonic, TokenLine, 'RETURNR'}}.
CALL : {token, {mnemonic, TokenLine, 'CALL'}}.
NOP : {token, {mnemonic, TokenLine, 'NOP'}}.
CALL_R : {token, {mnemonic, TokenLine, 'CALL_R'}}.
CALL_T : {token, {mnemonic, TokenLine, 'CALL_T'}}.
CALL_TR : {token, {mnemonic, TokenLine, 'CALL_TR'}}.
JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}.
JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}.
SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}.
SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}.
SWITCH_VN : {token, {mnemonic, TokenLine, 'SWITCH_VN'}}.
PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}.
DUP : {token, {mnemonic, TokenLine, 'DUP'}}.
DUPA : {token, {mnemonic, TokenLine, 'DUPA'}}.
POP : {token, {mnemonic, TokenLine, 'POP'}}.
STORE : {token, {mnemonic, TokenLine, 'STORE'}}.
ADD : {token, {mnemonic, TokenLine, 'ADD'}}.
MUL : {token, {mnemonic, TokenLine, 'MUL'}}.
SUB : {token, {mnemonic, TokenLine, 'SUB'}}.
DIV : {token, {mnemonic, TokenLine, 'DIV'}}.
MOD : {token, {mnemonic, TokenLine, 'MOD'}}.
POW : {token, {mnemonic, TokenLine, 'POW'}}.
INC : {token, {mnemonic, TokenLine, 'INC'}}.
DEC : {token, {mnemonic, TokenLine, 'DEC'}}.
INCA : {token, {mnemonic, TokenLine, 'INCA'}}.
DECA : {token, {mnemonic, TokenLine, 'DECA'}}.
LT : {token, {mnemonic, TokenLine, 'LT'}}.
GT : {token, {mnemonic, TokenLine, 'GT'}}.
EQ : {token, {mnemonic, TokenLine, 'EQ'}}.
ELT : {token, {mnemonic, TokenLine, 'ELT'}}.
EGT : {token, {mnemonic, TokenLine, 'EGT'}}.
NEQ : {token, {mnemonic, TokenLine, 'NEQ'}}.
AND : {token, {mnemonic, TokenLine, 'AND'}}.
OR : {token, {mnemonic, TokenLine, 'OR'}}.
NOT : {token, {mnemonic, TokenLine, 'NOT'}}.
TUPLE : {token, {mnemonic, TokenLine, 'TUPLE'}}.
ELEMENT : {token, {mnemonic, TokenLine, 'ELEMENT'}}.
MAP_EMPTY : {token, {mnemonic, TokenLine, 'MAP_EMPTY'}}.
MAP_LOOKUP : {token, {mnemonic, TokenLine, 'MAP_LOOKUP'}}.
MAP_LOOKUPD : {token, {mnemonic, TokenLine, 'MAP_LOOKUPD'}}.
MAP_UPDATE : {token, {mnemonic, TokenLine, 'MAP_UPDATE'}}.
MAP_MEMBER : {token, {mnemonic, TokenLine, 'MAP_MEMBER'}}.
MAP_DELETE : {token, {mnemonic, TokenLine, 'MAP_DELETE'}}.
MAP_FROM_LIST : {token, {mnemonic, TokenLine, 'MAP_FROM_LIST'}}.
NIL : {token, {mnemonic, TokenLine, 'NIL'}}.
IS_NIL : {token, {mnemonic, TokenLine, 'IS_NIL'}}.
CONS : {token, {mnemonic, TokenLine, 'CONS'}}.
HD : {token, {mnemonic, TokenLine, 'HD'}}.
TL : {token, {mnemonic, TokenLine, 'TL'}}.
LENGTH : {token, {mnemonic, TokenLine, 'LENGTH'}}.
STR_EQ : {token, {mnemonic, TokenLine, 'STR_EQ'}}.
STR_JOIN : {token, {mnemonic, TokenLine, 'STR_JOIN'}}.
INT_TO_STR : {token, {mnemonic, TokenLine, 'INT_TO_STR'}}.
ADDR_TO_STR : {token, {mnemonic, TokenLine, 'ADDR_TO_STR'}}.
STR_REVERSE : {token, {mnemonic, TokenLine, 'STR_REVERSE'}}.
INT_TO_ADDR : {token, {mnemonic, TokenLine, 'INT_TO_ADDR'}}.
VARIANT : {token, {mnemonic, TokenLine, 'VARIANT'}}.
VARIANT_TEST : {token, {mnemonic, TokenLine, 'VARIANT_TEST'}}.
VARIANT_ELEMENT : {token, {mnemonic, TokenLine, 'VARIANT_ELEMENT'}}.
BITS_NONE : {token, {mnemonic, TokenLine, 'BITS_NONE'}}.
BITS_NONEA : {token, {mnemonic, TokenLine, 'BITS_NONEA'}}.
BITS_ALL : {token, {mnemonic, TokenLine, 'BITS_ALL'}}.
BITS_ALLA : {token, {mnemonic, TokenLine, 'BITS_ALLA'}}.
BITS_ALL_N : {token, {mnemonic, TokenLine, 'BITS_ALL_N'}}.
BITS_SET : {token, {mnemonic, TokenLine, 'BITS_SET'}}.
BITS_CLEAR : {token, {mnemonic, TokenLine, 'BITS_CLEAR'}}.
BITS_TEST : {token, {mnemonic, TokenLine, 'BITS_TEST'}}.
BITS_SUM : {token, {mnemonic, TokenLine, 'BITS_SUM'}}.
BITS_OR : {token, {mnemonic, TokenLine, 'BITS_OR'}}.
BITS_AND : {token, {mnemonic, TokenLine, 'BITS_AND'}}.
BITS_DIFF : {token, {mnemonic, TokenLine, 'BITS_DIFF'}}.
ADDRESS : {token, {mnemonic, TokenLine, 'ADDRESS'}}.
BALANCE : {token, {mnemonic, TokenLine, 'BALANCE'}}.
ORIGIN : {token, {mnemonic, TokenLine, 'ORIGIN'}}.
CALLER : {token, {mnemonic, TokenLine, 'CALLER'}}.
GASPRICE : {token, {mnemonic, TokenLine, 'GASPRICE'}}.
BLOCKHASH : {token, {mnemonic, TokenLine, 'BLOCKHASH'}}.
BENEFICIARY : {token, {mnemonic, TokenLine, 'BENEFICIARY'}}.
TIMESTAMP : {token, {mnemonic, TokenLine, 'TIMESTAMP'}}.
NUMBER : {token, {mnemonic, TokenLine, 'NUMBER'}}.
DIFFICULTY : {token, {mnemonic, TokenLine, 'DIFFICULTY'}}.
GASLIMIT : {token, {mnemonic, TokenLine, 'GASLIMIT'}}.
GAS : {token, {mnemonic, TokenLine, 'GAS'}}.
LOG0 : {token, {mnemonic, TokenLine, 'LOG0'}}.
LOG1 : {token, {mnemonic, TokenLine, 'LOG1'}}.
LOG2 : {token, {mnemonic, TokenLine, 'LOG2'}}.
LOG3 : {token, {mnemonic, TokenLine, 'LOG3'}}.
LOG4 : {token, {mnemonic, TokenLine, 'LOG4'}}.
ABORT : {token, {mnemonic, TokenLine, 'ABORT'}}.
EXIT : {token, {mnemonic, TokenLine, 'EXIT'}}.
DEACTIVATE : {token, {mnemonic, TokenLine, 'DEACTIVATE'}}.
COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}.
FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
{ID} :
{token, {id, TokenLine, TokenChars}}.
{HEX} :
{token, {int, TokenLine, parse_hex(TokenChars)}}.
{INT} :
{token, {int, TokenLine, parse_int(TokenChars)}}.
{HASH} :
{token, {hash, TokenLine, parse_hash(TokenChars)}}.
%% Symbols
\-\> : {token, {'to', TokenLine}}.
\: : {token, {'to', TokenLine}}.
, : {token, {',', TokenLine}}.
\( : {token, {'(', TokenLine}}.
\) : {token, {')', TokenLine}}.
\[ : {token, {'[', TokenLine}}.
\] : {token, {']', TokenLine}}.
\{ : {token, {'{', TokenLine}}.
\} : {token, {'}', TokenLine}}.
;;.* :
{token, {comment, TokenLine, drop_prefix($;, TokenChars)}}.
\. : skip_token.
%% Whitespace ignore
{WS} : skip_token.
%% Comments (TODO: nested comments)
. : {error, "Unexpected token: " ++ TokenChars}.
Erlang code.
-export([scan/1]).
-dialyzer({nowarn_function, yyrev/2}).
-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
parse_int(Chars) -> list_to_integer(Chars).
parse_arg("arg" ++ N) -> list_to_integer(N).
parse_var("var" ++ N) -> list_to_integer(N).
parse_acc("a" ++ N) -> list_to_integer(N).
parse_hash("#" ++ Chars) ->
N = list_to_integer(Chars, 16),
<<N:256>>.
scan(S) ->
string(S).
drop_prefix(C, [C|Rest]) ->
drop_prefix(C, Rest);
drop_prefix(_, Tail) -> Tail.
-303
View File
@@ -1,303 +0,0 @@
-module(aeb_fate_code).
-export([ return/0
, return/1
, call/1
, call_t/1
, call_r/2
, call_tr/2
, jump/1
, jumpif/2
, switch/3
, switch/4
, switch/5
, switch/6
, push/1
, inc/0
, inc/1
, dec/0
, dec/1
, add/3
, sub/3
, mul/3
, divide/3
, modulo/3
, pow/3
, lt/3
, gt/3
, elt/3
, egt/3
, eq/3
, neq/3
, and_op/3
, or_op/3
, not_op/2
, tuple/1
, element_op/4
, map_empty/1
, map_lookup/3
, map_lookup/4
, map_update/4
, map_member/3
, map_from_list/2
, nil/1
, is_nil/2
, cons/3
, hd/2
, tl/2
, length/2
, str_eq/3
, str_join/3
, int_to_str/2
, addr_to_str/2
, str_reverse/2
, int_to_addr/2
, variant_test/3
, variant_element/3
, variant/4
, bits_none/0
, bits_none/1
, bits_all/0
, bits_all/1
, bits_all_n/2
, bits_set/3
, bits_clear/3
, bits_test/3
, bits_sum/2
, bits_or/3
, bits_and/3
, bits_diff/3
, dup/0
, dup/1
, pop/0
, store/2
, nop/0
]).
-define(i(__X__), {immediate, __X__ }).
return() ->
'RETURN'.
return(Arg) ->
{'RETURNR', Arg}.
call(Function) when is_binary(Function)->
{'CALL', ?i(Function) }.
call_t(Function) when is_binary(Function) ->
{'CALL_T', ?i(Function)}.
call_r(Contract, Function) when is_binary(Function) ->
{'CALL_R', Contract, ?i(Function)}.
call_tr(Contract, Function) when is_binary(Function) ->
{'CALL_TR', Contract, ?i(Function)}.
jump(BB) when is_integer(BB) ->
{'JUMP', ?i(BB)}.
jumpif(Arg, BB) when is_integer(BB) ->
{'JUMPIF', Arg, ?i(BB)}.
switch(Arg, BB1, BB2) when is_integer(BB1),
is_integer(BB2) ->
{'SWITCH_V2', Arg, ?i(BB1), ?i(BB2)}.
switch(Arg, BB1, BB2, BB3) when is_integer(BB1),
is_integer(BB2),
is_integer(BB3) ->
{'SWITCH_V3', Arg, ?i(BB1), ?i(BB2), ?i(BB3)}.
switch(Arg, BB1, BB2, BB3, BB4) when is_integer(BB1),
is_integer(BB2),
is_integer(BB3),
is_integer(BB4) ->
{'SWITCH_V4', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4)}.
switch(Arg, BB1, BB2, BB3, BB4, BB5) when is_integer(BB1),
is_integer(BB2),
is_integer(BB3),
is_integer(BB4),
is_integer(BB5) ->
{'SWITCH_V5', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4), ?i(BB5)}.
push(Arg) ->
{'PUSH', Arg}.
inc() ->
'INCA'.
inc(Arg) ->
{'INC', Arg}.
dec() ->
'DECA'.
dec(Arg) ->
{'DEC', Arg}.
add(Dest, Left, Right) ->
{'ADD', Dest, Left, Right}.
sub(Dest, Left, Right) ->
{'SUB', Dest, Left, Right}.
mul(Dest, Left, Right) ->
{'MUL', Dest, Left, Right}.
divide(Dest, Left, Right) ->
{'DIV', Dest, Left, Right}.
modulo(Dest, Left, Right) ->
{'MOD', Dest, Left, Right}.
pow(Dest, Left, Right) ->
{'POW', Dest, Left, Right}.
lt(Dest, Left, Right) ->
{'LT', Dest, Left, Right}.
gt(Dest, Left, Right) ->
{'GT', Dest, Left, Right}.
elt(Dest, Left, Right) ->
{'ELT', Dest, Left, Right}.
egt(Dest, Left, Right) ->
{'EGT', Dest, Left, Right}.
eq(Dest, Left, Right) ->
{'EQ', Dest, Left, Right}.
neq(Dest, Left, Right) ->
{'NEQ', Dest, Left, Right}.
and_op(Dest, Left, Right) ->
{'AND', Dest, Left, Right}.
or_op(Dest, Left, Right) ->
{'OR', Dest, Left, Right}.
not_op(Dest, Arg) ->
{'NOT', Dest, Arg}.
tuple(Size) when is_integer(Size) ->
{'TUPLE', ?i(Size)}.
element_op(Type, Dest, N, T) ->
{'ELEMENT', Type, Dest, N, T}.
map_empty(Dest) ->
{'MAP_EMPTY', Dest}.
map_lookup(Dest, Map, Key) ->
{'MAP_LOOKUP', Dest, Map, Key}.
map_lookup(Dest, Map, Key, Default) ->
{'MAP_LOOKUPD', Dest, Map, Key, Default}.
map_update(Dest, Map, Key, Value) ->
{'MAP_UPDATE', Dest, Map, Key, Value}.
map_member(Dest, Map, Key) ->
{'MAP_MEMBER', Dest, Map, Key}.
map_from_list(Dest, List) ->
{'MAP_MEMBER', Dest, List}.
nil(Dest) ->
{'NIL', Dest}.
is_nil(Dest, List) ->
{'IS_NIL', Dest, List}.
cons(Dest, Hd, Tl) ->
{'CONS', Dest, Hd, Tl}.
hd(Dest, List) ->
{'HD', Dest, List}.
tl(Dest, List) ->
{'TL', Dest, List}.
length(Dest, List) ->
{'LENGTH', Dest, List}.
str_eq(Dest, Str1, Str2) ->
{'STR_EQ', Dest, Str1, Str2}.
str_join(Dest, Str1, Str2) ->
{'STR_JOIN', Dest, Str1, Str2}.
int_to_str(Dest, Str) ->
{'INT_TO_STR', Dest, Str}.
addr_to_str(Dest, Str) ->
{'ADDR_TO_STR', Dest, Str}.
str_reverse(Dest, Str) ->
{'STR_REVERSE', Dest, Str}.
int_to_addr(Dest, Str) ->
{'INT_TO_ADDR', Dest, Str}.
variant_test(Dest, Variant, Tag) ->
{'VARIANT_TEST', Dest, Variant, Tag}.
variant_element( Dest, Variant, Index) ->
{'VARIANT_ELEMENT', Dest, Variant, Index}.
variant(Dest, SizeA, TagA, ElementsA) ->
{'VARIANT', Dest, SizeA, TagA, ElementsA}.
bits_none() ->
'BITS_NONEA'.
bits_none(To) ->
{'BITS_NONE', To}.
bits_all() ->
'BITS_ALLA'.
bits_all(To) ->
{'BITS_ALL', To}.
bits_all_n(To, N) ->
{'BITS_ALL_N', To, N}.
bits_set(To, Bits, Bit) ->
{'BITS_SET', To, Bits, Bit}.
bits_clear(To, Bits, Bit) ->
{'BITS_CLEAR', To, Bits, Bit}.
bits_test(To, Bits, Bit) ->
{'BITS_TEST', To, Bits, Bit}.
bits_sum(To, Bits) ->
{'BITS_SUM', To, Bits}.
bits_or(To, Bits, Bit) ->
{'BITS_OR', To, Bits, Bit}.
bits_and(To, Bits, Bit) ->
{'BITS_AND', To, Bits, Bit}.
bits_diff(To, Bits, Bit) ->
{'BITS_DIFF', To, Bits, Bit}.
dup() ->
'DUPA'.
dup(N) when is_integer(N) ->
{'DUP', ?i(N)}.
pop() ->
'POP'.
store(Var, What) ->
{'STORE', Var, What}.
nop() ->
'NOP'.
+139 -126
View File
@@ -4,20 +4,40 @@
-module(aeb_fate_data).
-type fate_integer() :: ?FATE_INTEGER_T.
-type fate_boolean() :: ?FATE_BOOLEAN_T.
-type fate_nil() :: ?FATE_NIL_T.
-type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T.
-type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T.
-type fate_integer() :: ?FATE_INTEGER_T.
-type fate_boolean() :: ?FATE_BOOLEAN_T.
-type fate_nil() :: ?FATE_NIL_T.
-type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T.
-type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T.
-type fate_hash() :: ?FATE_HASH_T.
-type fate_contract() :: ?FATE_CONTRACT_T.
-type fate_oracle() :: ?FATE_ORACLE_T.
-type fate_name() :: ?FATE_NAME_T.
-type fate_channel() :: ?FATE_CHANNEL_T.
-type fate_signature() :: ?FATE_SIGNATURE_T.
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_bits() :: ?FATE_BITS_T.
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_type_type() :: integer
| boolean
| {list, fate_type_type()}
| {map, fate_type_type(), fate_type_type()}
| {tuple, [fate_type_type()]}
| address
| hash
| signature
| contract
| oracle
| name
| channel
| bits
| string
| {variant, [fate_type_type()]}.
-type fate_void() :: ?FATE_VOID_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_type() ::
fate_boolean()
@@ -28,13 +48,36 @@
| fate_tuple()
| fate_string()
| fate_address()
| fate_hash()
| fate_signature()
| fate_contract()
| fate_oracle()
| fate_name()
| fate_channel()
| fate_variant()
| fate_map()
| fate_list()
| fate_tuple()
| fate_void(). %% Not sure we need this.
| fate_bits().
-export_type([fate_type/0]).
-export_type([fate_type/0
, fate_boolean/0
, fate_integer/0
, fate_nil/0
, fate_list/0
, fate_unit/0
, fate_tuple/0
, fate_string/0
, fate_address/0
, fate_hash/0
, fate_signature/0
, fate_contract/0
, fate_oracle/0
, fate_name/0
, fate_channel/0
, fate_variant/0
, fate_map/0
, fate_bits/0
, fate_type_type/0
]).
-export([ make_integer/1
, make_boolean/1
@@ -44,139 +87,109 @@
, make_string/1
, make_map/1
, make_address/1
, make_hash/1
, make_signature/1
, make_contract/1
, make_oracle/1
, make_name/1
, make_channel/1
, make_bits/1
, make_unit/0
, tuple_to_list/1
, decode/1
, encode/1
]).
-export([format/1]).
make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I).
make_boolean(true) -> ?FATE_TRUE;
make_boolean(false) -> ?FATE_FALSE.
make_list([]) -> ?FATE_NIL;
make_list(L) -> ?MAKE_FATE_LIST(L).
make_string(S) when is_list(S) ->
?FATE_STRING(list_to_binary(lists:flatten(S)));
make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M).
make_address(A) -> ?FATE_ADDRESS(A).
make_bits(I) when is_integer(I) -> ?FATE_BITS(I).
make_list([]) -> ?FATE_NIL;
make_list(L) -> ?MAKE_FATE_LIST(L).
make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M).
make_address(X) -> ?FATE_ADDRESS(X).
make_hash(X) -> ?FATE_HASH(X).
make_signature(X) -> ?FATE_SIGNATURE(X).
make_contract(X) -> ?FATE_CONTRACT(X).
make_oracle(X) -> ?FATE_ORACLE(X).
make_name(X) -> ?FATE_NAME(X).
make_channel(X) -> ?FATE_CHANNEL(X).
make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I).
make_bits(I) when is_integer(I) -> ?FATE_BITS(I).
make_string(S) when is_list(S) ->
?FATE_STRING(iolist_to_binary(S));
make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_variant(Size, Tag, Values) when is_integer(Size), is_integer(Tag)
, 0 =< Size
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
?FATE_VARIANT(Size, Tag, Values).
tuple_to_list(?FATE_TUPLE(T)) -> erlang:tuple_to_list(T).
%% Encode is a convinience function for testing, encoding an Erlang term
%% to a Fate term, but it can not distinguish between e.g. 32-byte strings
%% and addresses. Therfore an extra tuple layer on the erlang side for
%% addresses and bits.
encode({bits, Term}) when is_integer(Term) -> make_bits(Term);
%% TODO: check that each byte is in base58
encode({address, B}) when is_binary(B) -> make_address(B);
encode({address, I}) when is_integer(I) -> B = <<I:256>>, make_address(B);
encode({address, S}) when is_list(S) -> make_address(base58_to_address(S));
encode({variant, Size, Tag, Values}) -> make_variant(Size, Tag, Values);
encode(Term) when is_integer(Term) -> make_integer(Term);
encode(Term) when is_boolean(Term) -> make_boolean(Term);
encode(Term) when is_list(Term) -> make_list([encode(E) || E <- Term]);
encode(Term) when is_tuple(Term) ->
make_tuple(list_to_tuple([encode(E) || E <- erlang:tuple_to_list(Term)]));
encode(Term) when is_map(Term) ->
make_map(maps:from_list([{encode(K), encode(V)} || {K,V} <- maps:to_list(Term)]));
encode(Term) when is_binary(Term) -> make_string(Term).
%% Tag points to the selected variant (zero based)
%% The arity of this variant is read from the list of provided arities
%% and should match the size of the given tuple.
make_variant(Arities, Tag, Values) ->
Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities),
if is_integer(Tag)
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
Arity = lists:nth(Tag+1, Arities),
if size(Values) =:= Arity ->
?FATE_VARIANT(Arities, Tag, Values)
end
end.
decode(I) when ?IS_FATE_INTEGER(I) -> I;
decode(?FATE_TRUE) -> true;
decode(?FATE_FALSE) -> false;
decode(L) when ?IS_FATE_LIST(L) -> [decode(E) || E <- L];
decode(?FATE_ADDRESS(<<Address:256>>)) -> {address, Address};
decode(?FATE_BITS(Bits)) -> {bits, Bits};
decode(?FATE_TUPLE(T)) -> erlang:list_to_tuple([decode(E) || E <- T]);
decode(?FATE_VARIANT(Size, Tag, Values)) -> {variant, Size, Tag, Values};
decode(S) when ?IS_FATE_STRING(S) -> binary_to_list(S);
decode(M) when ?IS_FATE_MAP(M) ->
maps:from_list([{decode(K), decode(V)} || {K, V} <- maps:to_list(M)]).
-spec format(fate_type()) -> iolist().
format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I));
format(?FATE_VOID) -> "void";
format(?FATE_TRUE) -> "true";
format(?FATE_FALSE) -> "false";
format(?FATE_NIL) -> "[]";
format(L) when ?IS_FATE_LIST(L) -> format_list(?FATE_LIST_VALUE(L));
format(?FATE_UNIT) -> "unit";
format(?FATE_UNIT) -> "()";
format(?FATE_TUPLE(T)) ->
"{ " ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] ++ "}";
format(S) when ?IS_FATE_STRING(S) -> [S];
format(?FATE_VARIANT(Size, Tag, T)) ->
"( " ++ integer_to_list(Size) ++ ", "
++ integer_to_list(Tag) ++ ", "
++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)]
++ " )";
["( ", lists:join(", ", [ format(E) || E <- erlang:tuple_to_list(T)]), " )"];
format(S) when ?IS_FATE_STRING(S) -> ["\"", S, "\""];
format(?FATE_BITS(B)) when B >= 0 ->
["<", format_bits(B, "") , ">"];
format(?FATE_BITS(B)) when B < 0 ->
["!< ", format_nbits(-B-1, "") , " >"];
format(?FATE_VARIANT(Arities, Tag, T)) ->
["(| ",
lists:join("| ",
[format_arities(Arities),
integer_to_list(Tag) |
[format(make_tuple(T))]]),
" |)"];
format(M) when ?IS_FATE_MAP(M) ->
"#{ "
++ format_kvs(maps:to_list(?FATE_MAP_VALUE(M)))
++" }";
format(?FATE_ADDRESS(Address)) -> address_to_base58(Address);
["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
format(?FATE_HASH(X)) -> ["#", base64:encode(X)];
format(?FATE_ADDRESS(X)) ->
["@", aeser_api_encoder:encode(account_pubkey, X)];
format(?FATE_SIGNATURE(X)) ->
["$", aeser_api_encoder:encode(signature, X)];
format(?FATE_CONTRACT(X)) ->
["@", aeser_api_encoder:encode(contract_pubkey, X)];
format(?FATE_ORACLE(X)) ->
["@", aeser_api_encoder:encode(oracle_pubkey, X)];
format(?FATE_NAME(X)) ->
["@", aeser_api_encoder:encode(name, X)];
format(?FATE_CHANNEL(X)) ->
["@", aeser_api_encoder:encode(channel, X)];
format(V) -> exit({not_a_fate_type, V}).
format_list([]) -> " ]";
format_list([E]) -> format(E) ++ " ]";
format_list([H|T]) -> format(H) ++ ", " ++ format_list(T).
format_bits(0, Acc) -> Acc;
format_bits(N, Acc) ->
Bit = $0 + (N band 1),
format_bits(N bsr 1, [Bit|Acc]).
format_kvs([]) -> "";
format_kvs([{K,V}]) -> "( " ++ format(K) ++ " => " ++ format(V) ++ " )";
format_kvs([{K,V} | Rest]) ->
"( " ++ format(K) ++ " => " ++ format(V) ++ " ), " ++ format_kvs(Rest).
format_nbits(0, Acc) -> Acc;
format_nbits(N, Acc) ->
Bit = $1 - (N band 1),
format_nbits(N bsr 1, [Bit|Acc]).
format_arities(Arities) ->
["[ ", lists:join(", ", [integer_to_list(E) || E <- Arities]), " ]"].
%% -- Local base 58 library
format_list(List) ->
["[ ", lists:join(", ", [format(E) || E <- List]), " ]"].
base58char(Char) ->
binary:at(<<"123456789ABCDEFGHJKLMNPQRSTUVWXYZ"
"abcdefghijkmnopqrstuvwxyz">>, Char).
char_to_base58(C) ->
binary:at(<<0,1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,9,10,11,12,13,14,15,16,0,17,
18,19,20,21,0,22,23,24,25,26,27,28,29,30,31,32,0,0,0,0,0,0,
33,34,35,36,37,38,39,40,41,42,43,0,44,45,46,47,48,49,50,51,
52,53,54,55,56,57>>, C-$1).
base58_to_integer(C, []) -> C;
base58_to_integer(C, [X | Xs]) ->
base58_to_integer(C * 58 + char_to_base58(X), Xs).
base58_to_integer([]) -> error;
base58_to_integer([Char]) -> char_to_base58(Char);
base58_to_integer([Char | Str]) ->
base58_to_integer(char_to_base58(Char), Str).
base58_to_address(Base58) ->
I = base58_to_integer(Base58),
Bin = <<I:256>>,
Bin.
address_to_base58(<<A:256>>) ->
integer_to_base58(A).
integer_to_base58(0) -> <<"1">>;
integer_to_base58(Integer) ->
Base58String = integer_to_base58(Integer, []),
list_to_binary(Base58String).
integer_to_base58(0, Acc) -> Acc;
integer_to_base58(Integer, Acc) ->
Quot = Integer div 58,
Rem = Integer rem 58,
integer_to_base58(Quot, [base58char(Rem)|Acc]).
format_kvs(List) ->
lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]).
+183 -44
View File
@@ -28,8 +28,6 @@
%% TODO:
%% * Make the code production ready.
%% (add tests, document exported functions).
%% * Handle Variant types better.
%% * Handle type representations.
%% * Handle instructions.
%%
%% ------------------------------------------------------------------------
@@ -37,7 +35,9 @@
-export([ deserialize/1
, deserialize_one/1
, deserialize_type/1
, serialize/1
, serialize_type/1
]).
-include("aeb_fate_data.hrl").
@@ -47,38 +47,62 @@
-define(SMALL_INT , 2#0). %% sxxxxxx 0 - 6 bit integer with sign bit
%% 1 Set below
-define(LONG_STRING , 2#00000001). %% 000000 01 - RLP encoded array, size >= 64
-define(SHORT_STRING , 2#01). %% xxxxxx 01 - [bytes], 0 < xxxxxx:size < 64
-define(LONG_STRING , 2#00000001). %% 000000 01 | RLP encoded array - when size >= 64
-define(SHORT_STRING , 2#01). %% xxxxxx 01 | [bytes] - when 0 < xxxxxx:size < 64
%% 11 Set below
-define(SHORT_LIST , 2#0011). %% xxxx 0011 - [encoded elements], 0 < length < 16
%% xxxx 0111 - FREE (For typedefs in future)
-define(LONG_TUPLE , 2#00001011). %% 0000 1011 - RLP encoded (size - 16) + [encoded elements],
-define(SHORT_TUPLE , 2#1011). %% xxxx 1011 - [encoded elements], 0 < size < 16
-define(SHORT_LIST , 2#0011). %% xxxx 0011 | [encoded elements] when 0 < length < 16
%% xxxx 0111
-define(TYPE_INTEGER , 2#00000111). %% 0000 0111 - Integer typedef
-define(TYPE_BOOLEAN , 2#00010111). %% 0001 0111 - Boolean typedef
-define(TYPE_LIST , 2#00100111). %% 0010 0111 | Type
-define(TYPE_TUPLE , 2#00110111). %% 0011 0111 | Size | [Element Types]
-define(TYPE_OBJECT , 2#01000111). %% 0100 0111 | ObjectType
-define(TYPE_BITS , 2#01010111). %% 0101 0111 - Bits typedef
-define(TYPE_MAP , 2#01100111). %% 0110 0111 | Type | Type
-define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef
-define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type]
%% 1001 0111
%% 1010 0111
%% 1011 0111
%% 1100 0111
%% 1101 0111
%% 1110 0111
%% 1111 0111
-define(LONG_TUPLE , 2#00001011). %% 0000 1011 | RLP encoded (size - 16) | [encoded elements],
-define(SHORT_TUPLE , 2#1011). %% xxxx 1011 | [encoded elements] when 0 < size < 16
%% 1111 Set below
-define(LONG_LIST , 2#00011111). %% 0001 1111 - RLP encoded (length - 16) + [Elements]
-define(MAP , 2#00101111). %% 0010 1111 - RLP encoded size + [encoded key, encoded value]
-define(LONG_LIST , 2#00011111). %% 0001 1111 | RLP encoded (length - 16) | [encoded lements]
-define(MAP , 2#00101111). %% 0010 1111 | RLP encoded size | [encoded key, encoded value]
-define(EMPTY_TUPLE , 2#00111111). %% 0011 1111
-define(POS_BITS , 2#01001111). %% 0100 1111 - RLP encoded integer (to be interpreted as bitfield)
-define(POS_BITS , 2#01001111). %% 0100 1111 | RLP encoded integer (to be interpreted as bitfield)
-define(EMPTY_STRING , 2#01011111). %% 0101 1111
-define(POS_BIG_INT , 2#01101111). %% 0110 1111 - RLP encoded (integer - 64)
-define(POS_BIG_INT , 2#01101111). %% 0110 1111 | RLP encoded (integer - 64)
-define(FALSE , 2#01111111). %% 0111 1111
%% %% 1000 1111 - FREE (Possibly for bytecode in the future.)
-define(ADDRESS , 2#10011111). %% 1001 1111 - [32 bytes]
-define(VARIANT , 2#10101111). %% 1010 1111 - encoded size + encoded tag + encoded values
-define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array
-define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values]
-define(NIL , 2#10111111). %% 1011 1111 - Empty list
-define(NEG_BITS , 2#11001111). %% 1100 1111 - RLP encoded integer (infinite 1:s bitfield)
-define(NEG_BITS , 2#11001111). %% 1100 1111 | RLP encoded integer (infinite 1:s bitfield)
-define(EMPTY_MAP , 2#11011111). %% 1101 1111
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 - RLP encoded (integer - 64)
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 | RLP encoded (integer - 64)
-define(TRUE , 2#11111111). %% 1111 1111
-define(SHORT_TUPLE_SIZE, 16).
-define(SHORT_LIST_SIZE , 16).
-define(SMALL_INT_SIZE , 64).
-define(SHORT_TUPLE_SIZE, 16).
-define(SHORT_LIST_SIZE, 16).
-define(SMALL_INT_SIZE, 64).
-define(SHORT_STRING_SIZE, 64).
-define(POS_SIGN, 0).
-define(NEG_SIGN, 1).
%% Object types
-define(OTYPE_ADDRESS, 0).
-define(OTYPE_HASH, 1).
-define(OTYPE_SIGNATURE, 2).
-define(OTYPE_CONTRACT, 3).
-define(OTYPE_ORACLE, 4).
-define(OTYPE_NAME, 5).
-define(OTYPE_CHANNEL, 6).
%% --------------------------------------------------
%% Serialize
@@ -104,9 +128,21 @@ serialize(String) when ?IS_FATE_STRING(String),
?FATE_STRING_SIZE(String) > 0,
?FATE_STRING_SIZE(String) >= ?SHORT_STRING_SIZE ->
Bytes = ?FATE_STRING_VALUE(String),
<<?LONG_STRING, (aeb_rlp:encode(Bytes))/binary>>;
<<?LONG_STRING, (aeser_rlp:encode(Bytes))/binary>>;
serialize(?FATE_ADDRESS(Address)) when is_binary(Address) ->
<<?ADDRESS, (aeb_rlp:encode(Address))/binary>>;
<<?OBJECT, ?OTYPE_ADDRESS, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_HASH(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_HASH, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_SIGNATURE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_SIGNATURE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CONTRACT(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CONTRACT, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_NAME(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_NAME, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CHANNEL(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CHANNEL, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
S = size(T),
L = tuple_to_list(T),
@@ -128,7 +164,7 @@ serialize(L) when ?IS_FATE_LIST(L) ->
<<?LONG_LIST, Val/binary, Rest/binary>>
end;
serialize(Map) when ?IS_FATE_MAP(Map) ->
L = [{_K,_V}|_] = maps:to_list(?FATE_MAP_VALUE(Map)),
L = [{_K,_V}|_] = lists:sort(maps:to_list(?FATE_MAP_VALUE(Map))),
Size = length(L),
%% TODO: check all K same type, and all V same type
%% check K =/= map
@@ -136,19 +172,103 @@ serialize(Map) when ?IS_FATE_MAP(Map) ->
<<?MAP,
(rlp_integer(Size))/binary,
(Elements)/binary>>;
serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 =< Size
, Size < 256
, 0 =< Tag
, Tag < Size ->
<<?VARIANT, Size:8, Tag:8,
(serialize(?FATE_TUPLE(Values)))/binary
>>.
serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities),
if is_integer(Tag)
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
Arity = lists:nth(Tag+1, Arities),
if size(Values) =:= Arity ->
EncodedArities = aeser_rlp:encode(list_to_binary(Arities)),
<<?VARIANT,
EncodedArities/binary,
Tag:8,
(serialize(?FATE_TUPLE(Values)))/binary
>>
end
end.
%% -----------------------------------------------------
-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()].
serialize_type(integer) -> [?TYPE_INTEGER];
serialize_type(boolean) -> [?TYPE_BOOLEAN];
serialize_type({list, T}) -> [?TYPE_LIST | serialize_type(T)];
serialize_type({tuple, Ts}) ->
case length(Ts) of
N when N =< 255 ->
[?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]]
end;
serialize_type(address) -> [?TYPE_OBJECT, ?OTYPE_ADDRESS];
serialize_type(hash) -> [?TYPE_OBJECT, ?OTYPE_HASH];
serialize_type(signature) -> [?TYPE_OBJECT, ?OTYPE_SIGNATURE];
serialize_type(contract) -> [?TYPE_OBJECT, ?OTYPE_CONTRACT];
serialize_type(oracle) -> [?TYPE_OBJECT, ?OTYPE_ORACLE];
serialize_type(name) -> [?TYPE_OBJECT, ?OTYPE_NAME];
serialize_type(channel) -> [?TYPE_OBJECT, ?OTYPE_CHANNEL];
serialize_type(bits) -> [?TYPE_BITS];
serialize_type({map, K, V}) -> [?TYPE_MAP
| serialize_type(K) ++ serialize_type(V)];
serialize_type(string) -> [?TYPE_STRING];
serialize_type({variant, ListOfVariants}) ->
Size = length(ListOfVariants),
if Size < 256 ->
[?TYPE_VARIANT, Size | [serialize_type(T) || T <- ListOfVariants]]
end.
-spec deserialize_type(binary()) -> {aeb_fate_data:fate_type_type(), binary()}.
deserialize_type(<<?TYPE_INTEGER, Rest/binary>>) -> {integer, Rest};
deserialize_type(<<?TYPE_BOOLEAN, Rest/binary>>) -> {boolean, Rest};
deserialize_type(<<?TYPE_LIST, Rest/binary>>) ->
{T, Rest2} = deserialize_type(Rest),
{{list, T}, Rest2};
deserialize_type(<<?TYPE_TUPLE, N, Rest/binary>>) ->
{Ts, Rest2} = deserialize_types(N, Rest, []),
{{tuple, Ts}, Rest2};
deserialize_type(<<?TYPE_OBJECT, ObjectType, Rest/binary>>) ->
case ObjectType of
?OTYPE_ADDRESS -> {address, Rest};
?OTYPE_HASH -> {hash, Rest};
?OTYPE_SIGNATURE -> {signature, Rest};
?OTYPE_CONTRACT -> {contract, Rest};
?OTYPE_ORACLE -> {oracle, Rest};
?OTYPE_NAME -> {name, Rest};
?OTYPE_CHANNEL -> {channel, Rest}
end;
deserialize_type(<<?TYPE_BITS, Rest/binary>>) -> {bits, Rest};
deserialize_type(<<?TYPE_MAP, Rest/binary>>) ->
{K, Rest2} = deserialize_type(Rest),
{V, Rest3} = deserialize_type(Rest2),
{{map, K, V}, Rest3};
deserialize_type(<<?TYPE_STRING, Rest/binary>>) ->
{string, Rest};
deserialize_type(<<?TYPE_VARIANT, Size, Rest/binary>>) ->
{Variants, Rest2} = deserialize_variants(Size, Rest, []),
{{variant, Variants}, Rest2}.
deserialize_variants(0, Rest, Variants) ->
{lists:reverse(Variants), Rest};
deserialize_variants(N, Rest, Variants) ->
{T, Rest2} = deserialize_type(Rest),
deserialize_variants(N-1, Rest2, [T|Variants]).
deserialize_types(0, Binary, Acc) ->
{lists:reverse(Acc), Binary};
deserialize_types(N, Binary, Acc) ->
{T, Rest} = deserialize_type(Binary),
deserialize_types(N-1, Rest, [T | Acc]).
%% -----------------------------------------------------
rlp_integer(S) when S >= 0 ->
aeb_rlp:encode(binary:encode_unsigned(S)).
aeser_rlp:encode(binary:encode_unsigned(S)).
serialize_integer(I) when ?IS_FATE_INTEGER(I) ->
V = ?FATE_INTEGER_VALUE(I),
@@ -187,29 +307,39 @@ deserialize2(<<?POS_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) ->
deserialize2(<<?NEG_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) ->
{?MAKE_FATE_INTEGER(-I), Rest};
deserialize2(<<?NEG_BIG_INT, Rest/binary>>) ->
{Bint, Rest2} = aeb_rlp:decode_one(Rest),
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
{?MAKE_FATE_INTEGER(-binary:decode_unsigned(Bint) - ?SMALL_INT_SIZE),
Rest2};
deserialize2(<<?POS_BIG_INT, Rest/binary>>) ->
{Bint, Rest2} = aeb_rlp:decode_one(Rest),
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
{?MAKE_FATE_INTEGER(binary:decode_unsigned(Bint) + ?SMALL_INT_SIZE),
Rest2};
deserialize2(<<?NEG_BITS, Rest/binary>>) ->
{Bint, Rest2} = aeb_rlp:decode_one(Rest),
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
{?FATE_BITS(-binary:decode_unsigned(Bint)), Rest2};
deserialize2(<<?POS_BITS, Rest/binary>>) ->
{Bint, Rest2} = aeb_rlp:decode_one(Rest),
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
{?FATE_BITS(binary:decode_unsigned(Bint)), Rest2};
deserialize2(<<?LONG_STRING, Rest/binary>>) ->
{String, Rest2} = aeb_rlp:decode_one(Rest),
{String, Rest2} = aeser_rlp:decode_one(Rest),
{?MAKE_FATE_STRING(String), Rest2};
deserialize2(<<S:6, ?SHORT_STRING:2, Rest/binary>>) ->
String = binary:part(Rest, 0, S),
Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)),
{?MAKE_FATE_STRING(String), Rest2};
deserialize2(<<?ADDRESS, Rest/binary>>) ->
{A, Rest2} = aeb_rlp:decode_one(Rest),
{?FATE_ADDRESS(A), Rest2};
deserialize2(<<?OBJECT, ObjectType, Rest/binary>>) ->
{A, Rest2} = aeser_rlp:decode_one(Rest),
Val =
case ObjectType of
?OTYPE_ADDRESS -> ?FATE_ADDRESS(A);
?OTYPE_HASH -> ?FATE_HASH(A);
?OTYPE_SIGNATURE -> ?FATE_SIGNATURE(A);
?OTYPE_CONTRACT -> ?FATE_CONTRACT(A);
?OTYPE_ORACLE -> ?FATE_ORACLE(A);
?OTYPE_NAME -> ?FATE_NAME(A);
?OTYPE_CHANNEL -> ?FATE_CHANNEL(A)
end,
{Val, Rest2};
deserialize2(<<?TRUE, Rest/binary>>) ->
{?FATE_TRUE, Rest};
deserialize2(<<?FALSE, Rest/binary>>) ->
@@ -223,7 +353,7 @@ deserialize2(<<?EMPTY_MAP, Rest/binary>>) ->
deserialize2(<<?EMPTY_STRING, Rest/binary>>) ->
{?FATE_EMPTY_STRING, Rest};
deserialize2(<<?LONG_TUPLE, Rest/binary>>) ->
{BSize, Rest1} = aeb_rlp:decode_one(Rest),
{BSize, Rest1} = aeser_rlp:decode_one(Rest),
N = binary:decode_unsigned(BSize) + ?SHORT_TUPLE_SIZE,
{List, Rest2} = deserialize_elements(N, Rest1),
{?FATE_TUPLE(list_to_tuple(List)), Rest2};
@@ -231,7 +361,7 @@ deserialize2(<<S:4, ?SHORT_TUPLE:4, Rest/binary>>) ->
{List, Rest1} = deserialize_elements(S, Rest),
{?FATE_TUPLE(list_to_tuple(List)), Rest1};
deserialize2(<<?LONG_LIST, Rest/binary>>) ->
{BLength, Rest1} = aeb_rlp:decode_one(Rest),
{BLength, Rest1} = aeser_rlp:decode_one(Rest),
Length = binary:decode_unsigned(BLength) + ?SHORT_LIST_SIZE,
{List, Rest2} = deserialize_elements(Length, Rest1),
{?MAKE_FATE_LIST(List), Rest2};
@@ -239,16 +369,25 @@ deserialize2(<<S:4, ?SHORT_LIST:4, Rest/binary>>) ->
{List, Rest1} = deserialize_elements(S, Rest),
{?MAKE_FATE_LIST(List), Rest1};
deserialize2(<<?MAP, Rest/binary>>) ->
{BSize, Rest1} = aeb_rlp:decode_one(Rest),
{BSize, Rest1} = aeser_rlp:decode_one(Rest),
Size = binary:decode_unsigned(BSize),
{List, Rest2} = deserialize_elements(2*Size, Rest1),
Map = insert_kv(List, #{}),
{?MAKE_FATE_MAP(Map), Rest2};
deserialize2(<<?VARIANT, Size:8, Tag:8, Rest/binary>>) ->
deserialize2(<<?VARIANT, Rest/binary>>) ->
{AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest),
Arities = binary_to_list(AritiesBin),
Size = length(Arities),
if Tag > Size -> exit({too_large_tag_in_variant, Tag, Size});
true ->
{?FATE_TUPLE(T), Rest2} = deserialize2(Rest),
{?FATE_VARIANT(Size, Tag, T), Rest2}
{?FATE_TUPLE(T), Rest3} = deserialize2(Rest2),
Arity = lists:nth(Tag+1, Arities),
NumElements = size(T),
if NumElements =/= Arity ->
exit({tag_does_not_match_type_in_variant, Tag, Arity});
true ->
{?FATE_VARIANT(Arities, Tag, T), Rest3}
end
end.
insert_kv([], M) -> M;
+689
View File
@@ -0,0 +1,689 @@
-module(aeb_fate_generate_ops).
-export([ gen_and_halt/1
, generate/0
, generate_documentation/1
, get_ops/0
, test_asm_generator/1]).
gen_and_halt([SrcDirArg, IncludeDirArg]) ->
generate(atom_to_list(SrcDirArg),
atom_to_list(IncludeDirArg)),
halt().
generate() -> generate("src/", "include/").
get_ops() -> gen(ops_defs()).
generate(Src, Include) ->
Ops = get_ops(),
%% io:format("ops: ~p\n", [Ops]),
HrlFile = Include ++ "aeb_fate_opcodes.hrl",
generate_header_file(HrlFile, Ops),
generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops),
generate_code_ops(aeb_fate_code, Src, Ops),
generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops),
gen_asm_pp(aeb_fate_pp, Src, Ops).
%% TODO: Some real gas numbers...
ops_defs() ->
%% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation
[ { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0. The type of the retun value has to match the return type of the function."}
, { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call copy Arg0 to arg0. The type of the retun value has to match the return type of the function."}
, { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack. The types of the arguments has to match the argument typs of the function."}
, { 'CALL_R', 16#03, 2, true, 8, [a,is], call_r, "Remote call to given contract and function. The types of the arguments has to match the argument typs of the function."}
, { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
, { 'CALL_TR', 16#05, 2, true, 8, [a,is], call_tr, "Remote tail call to given contract and function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
, { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block. The basic block has to exist in the current function."}
, { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block. If Arg0 then jump to Arg1."}
, { 'SWITCH_V2', 16#08, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_V3', 16#09, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_VN', 16#0a, 2, true, 4, [a, li], switch, "Conditional jump to a basic block on variant tag."}
, { 'PUSH', 16#0b, 1, false, 2, [a], push, "Push argument to stack."}
, { 'DUPA', 16#0c, 0, false, 3, atomic, dup, "push copy of accumulator on stack."}
, { 'DUP', 16#0d, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."}
, { 'POP', 16#0e, 1, false, 3, [a], pop, "Arg0 := top of stack."}
, { 'STORE', 16#0f, 2, false, 3, [a,a], store, "Arg0 := Arg1."}
, { 'INCA', 16#10, 0, false, 2, atomic, inc, "Increment accumulator."}
, { 'INC', 16#11, 1, false, 2, [a], inc, "Increment argument."}
, { 'DECA', 16#12, 0, false, 2, atomic, dec, "Decrement accumulator."}
, { 'DEC', 16#13, 1, false, 2, [a], dec, "Decrement argument."}
, { 'ADD', 16#14, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."}
, { 'SUB', 16#15, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."}
, { 'MUL', 16#16, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."}
, { 'DIV', 16#17, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."}
, { 'MOD', 16#18, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."}
, { 'POW', 16#19, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."}
, { 'LT', 16#20, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."}
, { 'GT', 16#21, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."}
, { 'EQ', 16#22, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."}
, { 'ELT', 16#23, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."}
, { 'EGT', 16#24, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."}
, { 'NEQ', 16#25, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."}
, { 'AND', 16#26, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."}
, { 'OR', 16#27, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."}
, { 'NOT', 16#28, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."}
, { 'TUPLE', 16#29, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."}
, { 'ELEMENT', 16#2a, 3, false, 3, [a,a,a], element_op, "Arg1 := element(Arg2, Arg3)."}
, { 'MAP_EMPTY', 16#2b, 1, false, 3, [a], map_empty, "Arg0 := #{}."}
, { 'MAP_LOOKUP', 16#2c, 3, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."}
, { 'MAP_LOOKUPD', 16#2d, 4, false, 3, [a,a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."}
, { 'MAP_UPDATE', 16#2e, 4, false, 3, [a,a,a,a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."}
, { 'MAP_DELETE', 16#2f, 3, false, 3, [a,a,a], map_delete, "Arg0 := delete key Arg2 from map Arg1."}
, { 'MAP_MEMBER', 16#30, 3, false, 3, [a,a,a], map_member, "Arg0 := true if key Arg2 is in map Arg1."}
, { 'MAP_FROM_LIST',16#31, 2, false, 3, [a,a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."}
, { 'NIL', 16#32, 1, false, 3, [a], nil, "Arg0 := []."}
, { 'IS_NIL', 16#33, 2, false, 3, [a,a], is_nil, "Arg0 := true if Arg1 == []."}
, { 'CONS', 16#34, 3, false, 3, [a,a,a], cons, "Arg0 := [Arg1|Arg2]."}
, { 'HD', 16#35, 2, false, 3, [a,a], hd, "Arg0 := head of list Arg1."}
, { 'TL', 16#36, 2, false, 3, [a,a], tl, "Arg0 := tail of list Arg1."}
, { 'LENGTH', 16#37, 2, false, 3, [a,a], length, "Arg0 := length of list Arg1."}
, { 'STR_EQ', 16#38, 3, false, 3, [a,a,a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."}
, { 'STR_JOIN', 16#39, 3, false, 3, [a,a,a], str_join, "Arg0 := string Arg1 followed by string Arg2."}
, { 'INT_TO_STR', 16#40, 2, false, 3, [a,a], int_to_str, "Arg0 := turn integer Arg1 into a string."}
, { 'ADDR_TO_STR', 16#41, 2, false, 3, [a,a], addr_to_str, "Arg0 := turn address Arg1 into a string."}
, { 'STR_REVERSE', 16#42, 2, false, 3, [a,a], str_reverse, "Arg0 := the reverse of string Arg1."}
, { 'INT_TO_ADDR', 16#43, 2, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."}
, { 'VARIANT', 16#44, 4, false, 3, [a,a,a,a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."}
, { 'VARIANT_TEST', 16#45, 3, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."}
, { 'VARIANT_ELEMENT',16#46, 3, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."}
, { 'BITS_NONEA', 16#47, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."}
, { 'BITS_NONE', 16#48, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."}
, { 'BITS_ALLA', 16#49, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."}
, { 'BITS_ALL', 16#50, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."}
, { 'BITS_ALL_N', 16#51, 2, false, 3, [a,a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."}
, { 'BITS_SET', 16#52, 3, false, 3, [a,a,a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."}
, { 'BITS_CLEAR', 16#53, 3, false, 3, [a,a,a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."}
, { 'BITS_TEST', 16#54, 3, false, 3, [a,a,a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."}
, { 'BITS_SUM', 16#55, 2, false, 3, [a,a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."}
, { 'BITS_OR', 16#56, 3, false, 3, [a,a,a], bits_or, "Arg0 := Arg1 v Arg2."}
, { 'BITS_AND', 16#57, 3, false, 3, [a,a,a], bits_and, "Arg0 := Arg1 ^ Arg2."}
, { 'BITS_DIFF', 16#58, 3, false, 3, [a,a,a], bits_diff, "Arg0 := Arg1 - Arg2."}
, { 'ADDRESS', 16#59, 1, false, 3, [a], address, "Arg0 := The current contract address."}
, { 'BALANCE', 16#5a, 1, false, 3, [a], balance, "Arg0 := The current contract balance."}
, { 'ORIGIN', 16#5b, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."}
, { 'CALLER', 16#5c, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."}
, { 'GASPRICE', 16#5d, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."}
, { 'BLOCKHASH', 16#5e, 2, false, 3, [a, a], blockhash, "Arg0 := The blockhash at height."}
, { 'BENEFICIARY', 16#5f, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."}
, { 'TIMESTAMP', 16#60, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
, { 'GENERATION', 16#61, 1, false, 3, [a], generation, "Arg0 := The block height of the cureent generation."}
, { 'MICROBLOCK', 16#62, 1, false, 3, [a], microblock, "Arg0 := The current micro block number."}
, { 'DIFFICULTY', 16#63, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."}
, { 'GASLIMIT', 16#64, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."}
, { 'GAS', 16#65, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."}
, { 'LOG0', 16#66, 2, false, 3, [a,a], log, "Create a log message in the call object."}
, { 'LOG1', 16#67, 3, false, 3, [a,a,a], log, "Create a log message with one topic in the call object."}
, { 'LOG2', 16#68, 4, false, 3, [a,a,a,a], log, "Create a log message with two topics in the call object."}
, { 'LOG3', 16#69, 5, false, 3, [a,a,a,a,a], log, "Create a log message with three topics in the call object."}
, { 'LOG4', 16#6a, 6, false, 3, [a,a,a,a,a,a], log, "Create a log message with four topics in the call object."}
, { 'DEACTIVATE', 16#6b, 0, false, 3, atomic, deactivate, "Mark the current contract for deactication."}
%% Transaction ops
, { 'SPEND', 16#6c, 2, false,3, [a,a], spend, "Transfer Arg0 tokens to account Arg1. (If the contract account has at least that many tokens."}
, { 'ORACLE_REGISTER', 16#6d, 6, false,3, [a,a,a,a,a,a], oracle_register, "Mark the current contract for deactication."}
%% TODO:
, { 'ORACLE_QUERY', 16#6e, 0, false,3, atomic, oracle_query, ""}
, { 'ORACLE_RESPOND', 16#6f, 0, false,3, atomic, oracle_respond, ""}
, { 'ORACLE_EXTEND', 16#70, 0, false,3, atomic, oracle_extend, ""}
, { 'ORACLE_GET_ANSWER', 16#71, 0, false,3, atomic, oracle_get_answer, ""}
, { 'ORACLE_GET_QUESTION', 16#72, 0, false,3, atomic,oracle_get_question, ""}
, { 'ORACLE_QUERY_FEE', 16#73, 0, false,3, atomic, oracle_query_fee, ""}
, { 'AENS_RESOLVE', 16#74, 0, false,3, atomic, aens_resolve, ""}
, { 'AENS_PRECLAIM', 16#75, 0, false,3, atomic, aens_preclaim, ""}
, { 'AENS_CLAIM', 16#76, 0, false,3, atomic, aens_claim, ""}
, { 'AENS_UPDATE', 16#77, 0, false,3, atomic, aend_update, ""}
, { 'AENS_TRANSFER', 16#78, 0, false,3, atomic, aens_transfer, ""}
, { 'AENS_REVOKE', 16#79, 0, false,3, atomic, aens_revoke, ""}
, { 'ECVERIFY', 16#7a, 0, false,3, atomic, ecverify, ""}
, { 'SHA3', 16#7b, 0, false,3, atomic, sha3, ""}
, { 'SHA256', 16#7c, 0, false,3, atomic, sha256, ""}
, { 'BLAKE2B', 16#7d, 0, false,3, atomic, blake2b, ""}
, { 'BALANCE_OTHER', 16#7e, 2, false,3, [a,a], balance_other, "Arg0 := The balance of address Arg1."}
, { 'SETELEMENT', 16#7f, 4, false,3, [a,a,a,a], setelement, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
, { 'DUMMY7ARG', 16#f9, 7, false,3, [a,a,a,a,a,a,a], dummyarg, "Temporary dummy instruction to test 7 args."}
, { 'DUMMY8ARG', 16#fa, 8, false,3, [a,a,a,a,a,a,a,a],dummyarg, "Temporary dummy instruction to test 8 args."}
, { 'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."}
, { 'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."}
, { 'NOP', 16#fd, 0, false, 1, atomic, nop, "The no op. does nothing."}
%% FUNCTION 16#fe "Function declaration and entrypoint."
%% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes."
].
generate_header_file(Filename, Ops) ->
{ok, File} = file:open(Filename, [write]),
Defines = lists:flatten([gen_defines(Op) || Op <- Ops]),
io:format(File, "~s", [prelude("Provides opcode defines.\n")]),
io:format(File, "%% FATE opcodes\n~s", [Defines]),
io:format(File, "~s",
["-define('FUNCTION' , 16#fe).\n"
"-define('EXTEND' , 16#ff).\n\n"]),
file:close(File).
generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) ->
Filename = SrcDir ++ atom_to_list(Modulename) ++ ".erl",
{ok, File} = file:open(Filename, [write]),
Mnemonic = lists:flatten([gen_mnemonic(Op) || Op <- Ops]),
ToOp = lists:flatten([gen_m_to_op(Op) || Op <- Ops]),
Args = lists:flatten([gen_args(Op) || Op <- Ops]),
EndBB = lists:flatten([gen_bb(Op) || Op <- Ops]),
io:format(File, "~s", [prelude("Provides opcode primitives.\n")]),
io:format(File, "~s", [ops_exports(Modulename, HrlFile,
["args/1\n"
" , end_bb/1\n"
" , mnemonic/1\n"
" , m_to_op/1\n"
])]),
io:format(File, "%% FATE mnemonics\n~s", [Mnemonic]),
io:format(File, "mnemonic(Op) -> exit({bad_opcode, Op}).\n\n", []),
io:format(File, "%% FATE opcodes\n~s", [ToOp]),
io:format(File, "m_to_op(M) -> exit({bad_mnemonic, M}).\n\n", []),
io:format(File, "%% FATE numbers of args to op.\n~s", [Args]),
io:format(File, "args(Op) -> exit({bad_opcode, Op}).\n\n", []),
io:format(File, "%% Does FATE Op end a Basic Block?\n~s", [EndBB]),
io:format(File, "end_bb(_) -> false.\n\n", []),
file:close(File).
generate_code_ops(Modulename, SrcDir, Ops) ->
Filename = SrcDir ++ atom_to_list(Modulename) ++ ".erl",
{ok, File} = file:open(Filename, [write]),
Types = lists:flatten([gen_type(Op) || Op <- Ops]),
TypeExports = lists:flatten([gen_type_exports(Op) || Op <- Ops]),
[#{type_name := FirstType} | RestOfOps] = Ops,
FateTypes = lists:flatten([gen_fate_code_type(Op) || Op <- RestOfOps]),
ConstructorExports = lists:flatten([gen_constructor_exports(Op) || Op <- Ops]),
Constructors = lists:flatten([gen_constructors(Op) || Op <- Ops]),
io:format(File, "~s", [prelude(" Provide constructor functuions for "
"Fate instructions.\n%%% Provide types"
" and documentation for Fate "
"instructions.\n")]),
io:format(File, "-module(~w).\n\n", [Modulename]),
io:format(File, "-include_lib(\"aebytecode/include/aeb_fate_data.hrl\").\n\n"
"-define(i(__X__), {immediate, __X__ }).\n\n"
"-type fate_arg_immediate(T) :: {immediate, T}.\n"
"-type fate_arg_var() :: {var, integer()}.\n"
"-type fate_arg_arg() :: {arg, integer()}.\n"
"-type fate_arg_stack() :: {stack, integer()}.\n"
"-type fate_arg() :: fate_arg_immediate()\n"
" | fate_arg_var()\n"
" | fate_arg_arg()\n"
" | fate_arg_stack().\n\n"
"-type fate_arg_immediate() :: {immediate, aeb_fate_data:fate_type()}.\n"
, []),
io:format(File, "~s", [Types]),
io:format(File, "-type fate_code() :: ~s\n~s .\n\n",
[FirstType, FateTypes]),
io:format(File, "-export_type([ fate_code/0\n~s ]).\n\n", [TypeExports]),
io:format(File, "-export([ foo/0\n~s ]).\n\n", [ConstructorExports]),
io:format(File, "~s\n", [Constructors]),
io:format(File, "foo() -> \"A temp hack.\".\n", []),
file:close(File).
gen_type(#{type_name := TypeName, type := Type}) ->
lists:flatten(io_lib:format("-type ~-26s :: ~s.\n",
[TypeName, Type])).
gen_fate_code_type(#{type_name := TypeName}) ->
lists:flatten(io_lib:format(" | ~s\n", [TypeName])).
gen_type_exports(#{type_name := TypeName}) ->
lists:flatten(io_lib:format(" , ~s/0\n", [TypeName--"()"])).
gen_constructor_exports(#{constructor_type := Function}) ->
lists:flatten(io_lib:format(" , ~s\n", [Function])).
gen_constructors(#{constructor := Function, format := atomic,
type_name := Type, opname := Name}) ->
lists:flatten(io_lib:format("-spec ~s() -> ~s.\n"
"~s() ->\n"
" ~w.\n\n",
[Function, Type, Function, Name]));
gen_constructors(#{constructor := Function, format := ArgSpec,
type_name := Type, opname := Name}) ->
ArgTypeSpecs = gen_arg_type_specs(ArgSpec),
Args = gen_arg_names(0, ArgSpec),
UseArgs = gen_arg_uses(0, ArgSpec),
lists:flatten(io_lib:format("-spec ~s(~s) -> ~s.\n"
"~s(~s) ->\n"
" {~w, ~s}.\n\n",
[Function, ArgTypeSpecs, Type,
Function, Args, Name, UseArgs])).
gen_arg_type_specs([]) -> [];
gen_arg_type_specs([a]) -> "fate_arg()";
gen_arg_type_specs([is]) -> "aeb_fate_data:fate_string()";
gen_arg_type_specs([ii]) -> "aeb_fate_data:fate_integer()";
gen_arg_type_specs([li]) -> "[aeb_fate_data:fate_integer()]";
gen_arg_type_specs([t]) -> "aeb_fate_data:fate_type_type()";
gen_arg_type_specs([a | Args]) -> "fate_arg(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([is | Args]) -> "aeb_fate_data:fate_string(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([ii | Args]) -> "aeb_fate_data:fate_integer(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([li | Args]) -> "[aeb_fate_data:fate_integer()], " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([t | Args]) -> "aeb_fate_data:fate_type_type(), " ++ gen_arg_type_specs(Args).
gen_arg_names(_, []) ->
[];
gen_arg_names(N, [_]) -> io_lib:format("Arg~w", [N]);
gen_arg_names(N, [_|Args]) ->
io_lib:format("Arg~w, ", [N]) ++ gen_arg_names(N+1, Args).
gen_arg_uses(_, []) ->
[];
gen_arg_uses(N, [a]) -> io_lib:format("Arg~w", [N]);
gen_arg_uses(N, [is]) -> io_lib:format("{immediate, Arg~w}", [N]);
gen_arg_uses(N, [ii]) -> io_lib:format("{immediate, Arg~w}", [N]);
gen_arg_uses(N, [li]) -> io_lib:format("[{immediate, I} || I <- Arg~w]", [N]);
gen_arg_uses(N, [t]) -> io_lib:format("Arg~w", [N]);
gen_arg_uses(N, [a | Args]) ->
io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args);
gen_arg_uses(N, [is | Args]) ->
io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args);
gen_arg_uses(N, [ii | Args]) ->
io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args);
gen_arg_uses(N, [li | Args]) ->
io_lib:format("[{immediate, I} || I <- Arg~w], ", [N]) ++ gen_arg_uses(N+1, Args);
gen_arg_uses(N, [t | Args]) ->
io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args).
ops_exports(Module, HrlFile, Exports) ->
lists:flatten(io_lib:format(
"-module(~w).\n\n"
"-export([ ~s ]).\n\n"
"-include_lib(\"aebytecode/" ++ HrlFile ++"\").\n\n"
"%%====================================================================\n"
"%% API\n"
"%%====================================================================\n",
[Module, Exports])).
gen_mnemonic(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("mnemonic(~21s) -> ~21w ;\n",
[Macro, Name])).
gen_m_to_op(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n",
[Name, Macro])).
gen_args(#{macro := Macro, args := Args}) ->
lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n",
[Macro, Args])).
gen_bb(#{macro := Macro, end_bb := EndBB}) ->
lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n",
[Macro, EndBB])).
prelude(Doc) ->
"%%%-------------------------------------------------------------------\n"
"%%% @copyright (C) 2019, Aeternity Anstalt\n"
"%%%\n"
"%%% === === N O T E : This file is generated do not edit. === ===\n"
"%%%\n"
"%%% Source is in aeb_fate_generate_ops.erl\n"
"%%% @doc\n"
"%%% "++Doc++
"%%% @end\n"
"%%%-------------------------------------------------------------------\n\n".
gen_defines(#{opname := Name, opcode := OpCode}) ->
lists:flatten(io_lib:format("-define(~-26w, 16#~2.16.0b).\n", [Name, OpCode])).
gen([]) ->
[];
gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) ->
Name = atom_to_list(OpName),
LowerName = string:to_lower(Name),
TypeName = "fate_" ++ LowerName ++ "()",
Macro = "?" ++ Name,
Type = case FateFormat of
atomic -> io_lib:format("~w", [OpName]);
ArgTypes ->
io_lib:format("{~w, ~s}", [OpName, expand_types(ArgTypes)])
end,
ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Args]),
[#{ opname => OpName
, opcode => OpCode
, args => Args
, end_bb => EndBB
, format => FateFormat
, macro => Macro
, type_name => TypeName
, doc => Doc
, gas => Gas
, type => Type
, constructor => Constructor
, constructor_type => ConstructorType
}| gen(Rest)].
expand_types([]) -> "";
expand_types([T]) -> expand_type(T);
expand_types([T|Ts]) ->expand_type(T) ++ ", " ++ expand_types(Ts).
expand_type(a) -> "fate_arg()";
expand_type(is) -> "fate_arg_immediate(aeb_fate_data:fate_string())";
expand_type(ii) -> "fate_arg_immediate(aeb_fate_data:fate_integer())";
expand_type(li) -> "[fate_arg_immediate(aeb_fate_data:fate_integer())]";
expand_type(t) -> "aeb_fate_data:fate_type_type()".
generate_scanner(TemplateFile, Outfile, Path, Ops) ->
{ok, Template} = file:read_file(filename:join(Path,TemplateFile)),
Tokens = lists:flatten([gen_token(Op) || Op <- Ops]),
NewFile = insert_tokens_in_template(Template, Tokens),
file:write_file(filename:join(Path, Outfile), NewFile).
gen_token(#{opname := OpName}) ->
Name = atom_to_list(OpName),
io_lib:format("~-28s: {token, {mnemonic, TokenLine, ~w}}.\n",
[Name, OpName]).
insert_tokens_in_template(<<"%% ###REPLACEWITHOPTOKENS###", Rest/binary >>, Tokens) ->
[Tokens, Rest];
insert_tokens_in_template(<<"%%% ###REPLACEWITHNOTE###", Rest/binary >>, Tokens) ->
[
"%%%\n"
"%%% === === N O T E : This file is generated do not edit. === ===\n"
"%%%\n"
"%%% Source is in aeb_fate_generate_ops.erl\n"
"%%% and aeb_fate_asm_scan.template"
| insert_tokens_in_template(Rest, Tokens)];
insert_tokens_in_template(<<B,Rest/binary>>, Tokens) ->
[B|insert_tokens_in_template(Rest, Tokens)].
gen_asm_pp(Module, Path, Ops) ->
Filename = filename:join(Path, atom_to_list(Module)) ++ ".erl",
{ok, File} = file:open(Filename, [write]),
Formats = lists:flatten([gen_format(Op)++"\n" || Op <- Ops]),
io:format(File, "~s", [prelude(" Provide pretty printing functuions for "
"Fate instructions.\n")]),
io:format(File, "-module(~w).\n\n", [Module]),
io:format(File,
"-export([format_op/2]).\n\n"
"format_arg(li, {immediate, LI}) ->\n"
" aeb_fate_data:format(LI);\n"
"format_arg(_, {immediate, I}) ->\n"
" aeb_fate_data:format(I);\n"
"format_arg(a, {arg, N}) -> io_lib:format(\"arg~~p\", [N]);\n"
"format_arg(a, {var, N}) -> io_lib:format(\"var~~p\", [N]);\n"
"format_arg(a, {stack, 0}) -> \"a\";\n"
"format_arg(a, {stack, N}) -> io_lib:format(\"a~~p\", [N]).\n\n"
"lookup(Name, Symbols) ->\n"
" maps:get(Name, Symbols, io_lib:format(\"~~w\",[Name])).\n\n"
"~s"
, [Formats]),
io:format(File, "format_op(Op, _Symbols) -> io_lib:format(\";; Bad Op: ~~w\\n\", [Op]).\n", []),
file:close(File).
gen_format(#{opname := Name}) when ('CALL' =:= Name) or (Name =:= 'CALL_T') ->
io_lib:format("format_op({~w, {immediate, Function}}, Symbols) ->\n"
"[\"~s \", lookup(Function, Symbols)];",
[Name, atom_to_list(Name)]);
gen_format(#{opname := Name}) when (Name =:= 'CALL_R') or (Name =:= 'CALL_TR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}}, Symbols) ->\n"
"[\"~s \", lookup(Contract, Symbols), \".\", lookup(Function, Symbols)];\n"
"format_op({~w, Contract, {immediate, Function}}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", lookup(Function, Symbols)];",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name, format := atomic}) ->
io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]);
gen_format(#{opname := Name, format := Args}) ->
NameAsString = atom_to_list(Name),
case Args of
[T0] ->
io_lib:format(
"format_op({~w, Arg0}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0)];",
[Name, NameAsString, T0]);
[T0, T1] ->
io_lib:format(
"format_op({~w, Arg0, Arg1}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1)];",
[Name, NameAsString, T0, T1]);
[T0, T1, T2] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2)];",
[Name, NameAsString, T0, T1, T2]);
[T0, T1, T2, T3] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3)];",
[Name, NameAsString, T0, T1, T2, T3]);
[T0, T1, T2, T3, T4] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4)];",
[Name, NameAsString, T0, T1, T2, T3, T4]);
[T0, T1, T2, T3, T4, T5] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5]);
[T0, T1, T2, T3, T4, T5, T6] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5),"
"\" \", format_arg(~w, Arg6)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5, T6]);
[T0, T1, T2, T3, T4, T5, T6, T7] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5),"
"\" \", format_arg(~w, Arg6),"
"\" \", format_arg(~w, Arg7)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5, T6, T7])
end.
test_asm_generator(Filename) ->
{ok, File} = file:open(Filename, [write]),
Instructions = lists:flatten([gen_instruction(Op)++"\n" || Op <- get_ops()]),
io:format(File,
";; CONTRACT all_instructions\n\n"
";; Dont expect this contract to typecheck or run.\n"
";; Just used to check assembler rountrip of all instruction.\n\n"
"FUNCTION foo () : {tuple, []}\n"
"~s"
, [Instructions]),
io:format(File, " RETURNR ()\n", []),
file:close(File).
gen_instruction(#{opname := Name, format := atomic}) ->
io_lib:format(" ~s\n", [Name]);
gen_instruction(#{opname := Name, format := ArgTypes}) ->
Args = lists:flatten(lists:join(" ", [gen_arg(A) || A <- ArgTypes])),
I = io_lib:format(" ~s ~s\n", [Name, Args]),
I.
%% This should be done with a Quick Check generator...
gen_arg(a) -> any_arg();
gen_arg(is) -> "foo";
gen_arg(ii) -> gen_int();
gen_arg(li) -> "[1, 2, 3]";
gen_arg(t) -> "integer".
any_arg() ->
element(rand:uniform(5), {"a", stack_arg(), var_arg(), arg_arg(), imm_arg()}).
stack_arg() -> "a" ++ integer_to_list(rand:uniform(255)-1).
arg_arg() -> "arg" ++ integer_to_list(rand:uniform(256)-1).
var_arg() -> "var" ++ integer_to_list(rand:uniform(256)-1).
imm_arg() ->
case rand:uniform(15) of
1 -> gen_int();
2 -> gen_int();
3 -> gen_int();
4 -> gen_int();
5 -> gen_int();
6 -> gen_int();
7 -> gen_int();
8 -> gen_address();
9 -> gen_boolean();
10 -> gen_string();
11 -> gen_map();
12 -> gen_list();
13 -> gen_bits();
14 -> gen_tuple();
15 -> gen_variant()
end.
gen_key() ->
case rand:uniform(15) of
1 -> gen_int();
2 -> gen_int();
3 -> gen_int();
4 -> gen_int();
5 -> gen_int();
6 -> gen_int();
7 -> gen_int();
8 -> gen_address();
9 -> gen_boolean();
10 -> gen_string();
11 -> gen_string();
12 -> gen_list();
13 -> gen_bits();
14 -> gen_tuple();
15 -> gen_variant()
end.
gen_boolean() ->
element(rand:uniform(2), {"true", "false"}).
gen_int() ->
element(rand:uniform(4),
{ integer_to_list(rand:uniform(round(math:pow(10,40))))
, integer_to_list(rand:uniform(10))
, integer_to_list(rand:uniform(100))
, io_lib:format("0x~.16b",[rand:uniform(round(math:pow(10,10)))])}).
gen_address() -> "#nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv".
gen_string() -> "\"foo\"".
gen_map() -> "{ " ++ gen_key() ++ " => " ++ imm_arg() ++ "}".
gen_list() ->
case rand:uniform(4) of
1 -> "[]";
2 -> "[" ++ lists:join(", ", gen_list_elements()) ++ " ]";
3 -> "[ " ++ imm_arg() ++ " ]";
4 -> "[ " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ]"
end.
%% Not type correct.
gen_list_elements() ->
case rand:uniform(3) of
1 -> [imm_arg() | gen_list_elements()];
2 -> [];
3 -> [imm_arg()]
end.
gen_bits() ->
element(rand:uniform(3),
{"<>"
,"!<>"
, "101010"}).
gen_tuple() ->
case rand:uniform(3) of
1 -> "()";
2 -> "(42)";
3 -> "(" ++ imm_arg() ++ ")"
end.
gen_variant() ->
case rand:uniform(3) of
1 -> "(| 5 | 2 | (1, \"foo\", ()) |)";
2 -> "(| 2 | 1 | ( " ++ imm_arg() ++ " ) |)";
3 -> "(| 2 | 0 | ( " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ) |)"
end.
%% TODO: add gas cost.
generate_documentation(Filename) ->
{ok, File} = file:open(Filename, [write]),
Instructions = lists:flatten([gen_doc(Op)++"\n" || Op <- get_ops()]),
io:format(File,
"### Operations\n\n"
"| OpCode | Name | Args | Description |\n"
"| --- | --- | --- | --- |\n"
"~s"
, [Instructions]),
io:format(File, "\n", []),
file:close(File).
gen_doc(#{ opname := Name
, opcode := OpCode
, args := _Args
, end_bb := _EndBB
, format := FateFormat
, macro := _Macro
, type_name := _TypeName
, doc := Doc
, gas := _Gas
, type := _Type
, constructor := _Constructor
, constructor_type := _ConstructorType
}) ->
Arguments =
case FateFormat of
atomic -> "";
_ -> lists:join(" ",
[format_arg_doc(A) ||
A <-
lists:zip(FateFormat,
lists:seq(0,length(FateFormat)-1))])
end,
io_lib:format("| 0x~.16b | ~w | ~s | ~s |\n",
[ OpCode
, Name
, Arguments
, Doc]).
format_arg_doc({a, N}) -> io_lib:format("Arg~w", [N]);
format_arg_doc({is,_N}) -> "Identifier";
format_arg_doc({ii,_N}) -> "Integer";
format_arg_doc({li,_N}) -> "[Integers]";
format_arg_doc({t,_N}) -> "Type".
-363
View File
@@ -1,363 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Opcodes
%%% @end
%%%-------------------------------------------------------------------
-module(aeb_fate_opcodes).
-export([ args/1
, end_bb/1
, mnemonic/1
, m_to_op/1
, opcode/1
]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
%%====================================================================
%% API
%%====================================================================
opcode(X) when X >= 0, X =< 255 -> X;
opcode({comment,X}) -> ?COMMENT(X).
mnemonic(?NOP) -> 'NOP' ;
mnemonic(?RETURN) -> 'RETURN' ;
mnemonic(?CALL) -> 'CALL' ;
mnemonic(?CALL_R) -> 'CALL_R' ;
mnemonic(?CALL_T) -> 'CALL_T' ;
mnemonic(?CALL_TR) -> 'CALL_TR' ;
mnemonic(?JUMP) -> 'JUMP' ;
mnemonic(?JUMPIF) -> 'JUMPIF' ;
mnemonic(?PUSH) -> 'PUSH' ;
mnemonic(?DUP) -> 'DUP' ;
mnemonic(?DUPA) -> 'DUPA' ;
mnemonic(?POP) -> 'POP' ;
mnemonic(?STORE) -> 'STORE' ;
mnemonic(?ADD) -> 'ADD' ;
mnemonic(?MUL) -> 'MUL' ;
mnemonic(?SUB) -> 'SUB' ;
mnemonic(?DIV) -> 'DIV' ;
mnemonic(?MOD) -> 'MOD' ;
mnemonic(?POW) -> 'POW' ;
mnemonic(?LT) -> 'LT' ;
mnemonic(?GT) -> 'GT' ;
mnemonic(?EQ) -> 'EQ' ;
mnemonic(?ELT) -> 'ELT' ;
mnemonic(?EGT) -> 'EGT' ;
mnemonic(?NEQ) -> 'NEQ' ;
mnemonic(?AND) -> 'AND' ;
mnemonic(?OR) -> 'OR' ;
mnemonic(?NOT) -> 'NOT' ;
mnemonic(?TUPLE) -> 'TUPLE' ;
mnemonic(?ELEMENT) -> 'ELEMENT' ;
mnemonic(?MAP_EMPTY) -> 'MAP_EMPTY' ;
mnemonic(?MAP_LOOKUP) -> 'MAP_LOOKUP' ;
mnemonic(?MAP_UPDATE) -> 'MAP_UPDATE' ;
mnemonic(?MAP_DELETE) -> 'MAP_DELETE' ;
mnemonic(?MAP_MEMBER) -> 'MAP_MEMBER' ;
mnemonic(?MAP_FROM_LIST) -> 'MAP_FROM_LIST' ;
mnemonic(?NIL) -> 'NIL' ;
mnemonic(?IS_NIL) -> 'IS_NIL' ;
mnemonic(?CONS) -> 'CONS' ;
mnemonic(?HD) -> 'HD' ;
mnemonic(?TL) -> 'TL' ;
mnemonic(?LENGTH) -> 'LENGTH' ;
mnemonic(?STR_EQ) -> 'STR_EQ' ;
mnemonic(?STR_JOIN) -> 'STR_JOIN' ;
mnemonic(?ADDR_TO_STR) -> 'ADDR_TO_STR' ;
mnemonic(?STR_REVERSE) -> 'STR_REVERSE' ;
mnemonic(?INT_TO_ADDR) -> 'INT_TO_ADDR' ;
mnemonic(?VARIANT) -> 'VARIANT' ;
mnemonic(?VARIANT_TEST) -> 'VARIANT_TEST' ;
mnemonic(?VARIANT_ELEMENT) -> 'VARIANT_ELEMENT' ;
mnemonic(?BITS_NONE) -> 'BITS_NONE' ;
mnemonic(?BITS_NONEA) -> 'BITS_NONEA' ;
mnemonic(?BITS_ALL) -> 'BITS_ALL' ;
mnemonic(?BITS_ALLA) -> 'BITS_ALLA' ;
mnemonic(?BITS_SET) -> 'BITS_SET' ;
mnemonic(?BITS_CLEAR) -> 'BITS_CLEAR' ;
mnemonic(?BITS_TEST) -> 'BITS_TEST' ;
mnemonic(?BITS_SUM) -> 'BITS_SUM' ;
mnemonic(?BITS_OR) -> 'BITS_OR' ;
mnemonic(?BITS_AND) -> 'BITS_AND' ;
mnemonic(?BITS_DIFF) -> 'BITS_DIFF' ;
mnemonic(?ADDRESS) -> 'ADDRESS' ;
mnemonic(?BALANCE) -> 'BALANCE' ;
mnemonic(?ORIGIN) -> 'ORIGIN' ;
mnemonic(?CALLER) -> 'CALLER' ;
mnemonic(?GASPRICE) -> 'GASPRICE' ;
mnemonic(?BLOCKHASH) -> 'BLOCKHASH' ;
mnemonic(?BENEFICIARY) -> 'BENEFICIARY' ;
mnemonic(?TIMESTAMP) -> 'TIMESTAMP' ;
mnemonic(?NUMBER) -> 'NUMBER' ;
mnemonic(?DIFFICULTY) -> 'DIFFICULTY' ;
mnemonic(?GASLIMIT) -> 'GASLIMIT' ;
mnemonic(?GAS) -> 'GAS' ;
mnemonic(?LOG0) -> 'LOG0' ;
mnemonic(?LOG1) -> 'LOG1' ;
mnemonic(?LOG2) -> 'LOG2' ;
mnemonic(?LOG3) -> 'LOG3' ;
mnemonic(?LOG4) -> 'LOG4' ;
mnemonic(?ABORT) -> 'ABORT' ;
mnemonic(?EXIT) -> 'EXIT' ;
mnemonic(?DEACTIVATE) -> 'DEACTIVATE' ;
mnemonic(?INC) -> 'INC' ;
mnemonic(?DEC) -> 'DEC' ;
mnemonic(?INCA) -> 'INCA' ;
mnemonic(?DECA) -> 'DECA' ;
mnemonic(?INT_TO_STR) -> 'INT_TO_STR' ;
mnemonic(?SPEND) -> 'SPEND' ;
mnemonic(?ORACLE_REGISTER) -> 'ORACLE_REGISTER' ;
mnemonic(?ORACLE_QUERY) -> 'ORACLE_QUERY' ;
mnemonic(?ORACLE_RESPOND) -> 'ORACLE_RESPOND' ;
mnemonic(?ORACLE_EXTEND) -> 'ORACLE_EXTEND' ;
mnemonic(?ORACLE_GET_ANSWER) -> 'ORACLE_GET_ANSWER' ;
mnemonic(?ORACLE_GET_QUESTION) -> 'ORACLE_GET_QUESTION' ;
mnemonic(?ORACLE_QUERY_FEE) -> 'ORACLE_QUERY_FEE' ;
mnemonic(?AENS_RESOLVE) -> 'AENS_RESOLVE' ;
mnemonic(?AENS_PRECLAIM) -> 'AENS_PRECLAIM' ;
mnemonic(?AENS_CLAIM) -> 'AENS_CLAIM' ;
mnemonic(?AENS_UPDATE) -> 'AENS_UPDATE' ;
mnemonic(?AENS_TRANSFER) -> 'AENS_TRANSFER' ;
mnemonic(?AENS_REVOKE) -> 'AENS_REVOKE' ;
mnemonic(?ECVERIFY) -> 'ECVERIFY' ;
mnemonic(?SHA3) -> 'SHA3' ;
mnemonic(?SHA256) -> 'SHA256' ;
mnemonic(?BLAKE2B) -> 'BLAKE2B' ;
mnemonic(?RETURNR) -> 'RETURNR' ;
mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ;
mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ;
mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ;
mnemonic(?SWITCH_VN) -> 'SWITCH_VN' ;
mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ;
mnemonic(?FUNCTION) -> 'FUNCTION' ;
mnemonic(?EXTEND) -> 'EXTEND'.
m_to_op('NOP') -> ?NOP ;
m_to_op('RETURN') -> ?RETURN ;
m_to_op('CALL') -> ?CALL ;
m_to_op('CALL_R') -> ?CALL_R ;
m_to_op('CALL_T') -> ?CALL_T ;
m_to_op('CALL_TR') -> ?CALL_TR ;
m_to_op('JUMP') -> ?JUMP ;
m_to_op('JUMPIF') -> ?JUMPIF ;
m_to_op('PUSH') -> ?PUSH ;
m_to_op('DUP') -> ?DUP ;
m_to_op('DUPA') -> ?DUPA ;
m_to_op('POP') -> ?POP ;
m_to_op('STORE') -> ?STORE ;
m_to_op('ADD') -> ?ADD ;
m_to_op('MUL') -> ?MUL ;
m_to_op('SUB') -> ?SUB ;
m_to_op('DIV') -> ?DIV ;
m_to_op('MOD') -> ?MOD ;
m_to_op('POW') -> ?POW ;
m_to_op('LT') -> ?LT ;
m_to_op('GT') -> ?GT ;
m_to_op('EQ') -> ?EQ ;
m_to_op('ELT') -> ?ELT ;
m_to_op('EGT') -> ?EGT ;
m_to_op('NEQ') -> ?NEQ ;
m_to_op('AND') -> ?AND ;
m_to_op('OR') -> ?OR ;
m_to_op('NOT') -> ?NOT ;
m_to_op('TUPLE') -> ?TUPLE ;
m_to_op('ELEMENT') -> ?ELEMENT ;
m_to_op('MAP_EMPTY') -> ?MAP_EMPTY ;
m_to_op('MAP_LOOKUP') -> ?MAP_LOOKUP ;
m_to_op('MAP_UPDATE') -> ?MAP_UPDATE ;
m_to_op('MAP_DELETE') -> ?MAP_DELETE ;
m_to_op('MAP_MEMBER') -> ?MAP_MEMBER ;
m_to_op('MAP_FROM_LIST') -> ?MAP_FROM_LIST ;
m_to_op('NIL') -> ?NIL ;
m_to_op('IS_NIL') -> ?IS_NIL ;
m_to_op('CONS') -> ?CONS ;
m_to_op('HD') -> ?HD ;
m_to_op('TL') -> ?TL ;
m_to_op('LENGTH') -> ?LENGTH ;
m_to_op('STR_EQ') -> ?STR_EQ ;
m_to_op('STR_JOIN') -> ?STR_JOIN ;
m_to_op('ADDR_TO_STR') -> ?ADDR_TO_STR ;
m_to_op('STR_REVERSE') -> ?STR_REVERSE ;
m_to_op('INT_TO_ADDR') -> ?INT_TO_ADDR ;
m_to_op('VARIANT') -> ?VARIANT ;
m_to_op('VARIANT_TEST') -> ?VARIANT_TEST ;
m_to_op('VARIANT_ELEMENT') -> ?VARIANT_ELEMENT ;
m_to_op('BITS_NONEA') -> ?BITS_NONEA ;
m_to_op('BITS_ALL') -> ?BITS_ALL ;
m_to_op('BITS_ALLA') -> ?BITS_ALLA ;
m_to_op('BITS_SET') -> ?BITS_SET ;
m_to_op('BITS_CLEAR') -> ?BITS_CLEAR ;
m_to_op('BITS_TEST') -> ?BITS_TEST ;
m_to_op('BITS_SUM') -> ?BITS_SUM ;
m_to_op('BITS_OR') -> ?BITS_OR ;
m_to_op('BITS_AND') -> ?BITS_AND ;
m_to_op('BITS_DIFF') -> ?BITS_DIFF ;
m_to_op('ADDRESS') -> ?ADDRESS ;
m_to_op('BALANCE') -> ?BALANCE ;
m_to_op('ORIGIN') -> ?ORIGIN ;
m_to_op('CALLER') -> ?CALLER ;
m_to_op('GASPRICE') -> ?GASPRICE ;
m_to_op('BLOCKHASH') -> ?BLOCKHASH ;
m_to_op('BENEFICIARY') -> ?BENEFICIARY ;
m_to_op('TIMESTAMP') -> ?TIMESTAMP ;
m_to_op('NUMBER') -> ?NUMBER ;
m_to_op('DIFFICULTY') -> ?DIFFICULTY ;
m_to_op('GASLIMIT') -> ?GASLIMIT ;
m_to_op('GAS') -> ?GAS ;
m_to_op('LOG0') -> ?LOG0 ;
m_to_op('LOG1') -> ?LOG1 ;
m_to_op('LOG2') -> ?LOG2 ;
m_to_op('LOG3') -> ?LOG3 ;
m_to_op('LOG4') -> ?LOG4 ;
m_to_op('ABORT') -> ?ABORT ;
m_to_op('EXIT') -> ?EXIT ;
m_to_op('DEACTIVATE') -> ?DEACTIVATE ;
m_to_op('INC') -> ?INC ;
m_to_op('DEC') -> ?DEC ;
m_to_op('INCA') -> ?INCA ;
m_to_op('DECA') -> ?DECA ;
m_to_op('INT_TO_STR') -> ?INT_TO_STR ;
m_to_op('SPEND') -> ?SPEND ;
m_to_op('ORACLE_REGISTER') -> ?ORACLE_REGISTER ;
m_to_op('ORACLE_QUERY') -> ?ORACLE_QUERY ;
m_to_op('ORACLE_RESPOND') -> ?ORACLE_RESPOND ;
m_to_op('ORACLE_EXTEND') -> ?ORACLE_EXTEND ;
m_to_op('ORACLE_GET_ANSWER') -> ?ORACLE_GET_ANSWER ;
m_to_op('ORACLE_GET_QUESTION') -> ?ORACLE_GET_QUESTION ;
m_to_op('ORACLE_QUERY_FEE') -> ?ORACLE_QUERY_FEE ;
m_to_op('AENS_RESOLVE') -> ?AENS_RESOLVE ;
m_to_op('AENS_PRECLAIM') -> ?AENS_PRECLAIM ;
m_to_op('AENS_CLAIM') -> ?AENS_CLAIM ;
m_to_op('AENS_UPDATE') -> ?AENS_UPDATE ;
m_to_op('AENS_TRANSFER') -> ?AENS_TRANSFER ;
m_to_op('AENS_REVOKE') -> ?AENS_REVOKE ;
m_to_op('ECVERIFY') -> ?ECVERIFY ;
m_to_op('SHA3') -> ?SHA3 ;
m_to_op('SHA256') -> ?SHA256 ;
m_to_op('BLAKE2B') -> ?BLAKE2B ;
m_to_op('RETURNR') -> ?RETURNR ;
m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ;
m_to_op('SWITCH_V2') -> ?SWITCH_V2 ;
m_to_op('SWITCH_V3') -> ?SWITCH_V3 ;
m_to_op('SWITCH_VN') -> ?SWITCH_VN ;
m_to_op('FUNCTION') -> ?FUNCTION ;
m_to_op('EXTEND') -> ?EXTEND.
args(?NOP) -> 0;
args(?RETURN) -> 0;
args(?INCA) -> 0;
args(?DECA) -> 0;
args(?DUPA) -> 0;
args(?BITS_NONEA) -> 0;
args(?BITS_ALLA) -> 0;
args(?INC) -> 1;
args(?DEC) -> 1;
args(?RETURNR) -> 1;
args(?PUSH) -> 1;
args(?JUMP) -> 1;
args(?CALL) -> 1;
args(?CALL_T) -> 1;
args(?TUPLE) -> 1;
args(?MAP_EMPTY) -> 1;
args(?DUP) -> 1;
args(?POP) -> 1;
args(?NIL) -> 1;
args(?BITS_NONE) -> 1;
args(?BITS_ALL) -> 1;
args(?ADDRESS) -> 1;
args(?BALANCE) -> 1;
args(?ORIGIN) -> 1;
args(?CALLER) -> 1;
args(?GASPRICE) -> 1;
args(?BLOCKHASH) -> 1;
args(?BENEFICIARY) -> 1;
args(?TIMESTAMP) -> 1;
args(?NUMBER) -> 1;
args(?DIFFICULTY)-> 1;
args(?GASLIMIT) -> 1;
args(?GAS) -> 1;
args(?ABORT) -> 1;
args(?EXIT) -> 1;
args(?JUMPIF) -> 2;
args(?CALL_R) -> 2;
args(?CALL_TR) -> 2;
args(?HD) -> 2;
args(?TL) -> 2;
args(?NOT) -> 2;
args(?STORE) -> 2;
args(?LENGTH) -> 2;
args(?IS_NIL) -> 2;
args(?BITS_SUM) -> 2;
args(?BITS_ALL_N) -> 2;
args(?ADDR_TO_STR) -> 2;
args(?STR_REVERSE) -> 2;
args(?INT_TO_ADDR) -> 2;
args(?MAP_FROM_LIST) -> 2;
args(?ADD) -> 3;
args(?SUB) -> 3;
args(?MUL) -> 3;
args(?DIV) -> 3;
args(?MOD) -> 3;
args(?POW) -> 3;
args(?AND) -> 3;
args(?OR) -> 3;
args(?LT) -> 3;
args(?GT) -> 3;
args(?EGT) -> 3;
args(?ELT) -> 3;
args(?EQ) -> 3;
args(?NEQ) -> 3;
args(?CONS) -> 3;
args(?STR_EQ) -> 3;
args(?STR_JOIN) -> 3;
args(?MAP_MEMBER) -> 3;
args(?MAP_LOOKUP) -> 3;
args(?MAP_DELETE) -> 3;
args(?BITS_OR) -> 3;
args(?BITS_AND) -> 3;
args(?BITS_SET) -> 3;
args(?BITS_DIFF) -> 3;
args(?BITS_TEST) -> 3;
args(?BITS_CLEAR) -> 3;
args(?VARIANT_TEST) -> 3;
args(?VARIANT_ELEMENT) -> 3;
args(?INT_TO_STR) -> 3;
args(?SWITCH_V2) -> 3;
args(?SWITCH_V3) -> 4;
args(?ELEMENT) -> 4;
args(?VARIANT) -> 4;
args(?MAP_UPDATE) -> 4;
args(?MAP_LOOKUPD) -> 4;
args(?SWITCH_VN) -> 2;
args(_) -> 0. %% TODO do not allow this
end_bb(?RETURN) -> true;
end_bb(?RETURNR) -> true;
end_bb(?JUMP) -> true;
end_bb(?JUMPIF) -> true;
end_bb(?CALL) -> true;
end_bb(?CALL_T) -> true;
end_bb(?CALL_R) -> true;
end_bb(?CALL_TR) -> true;
end_bb(?SWITCH_V2) -> true;
end_bb(?SWITCH_V3) -> true;
end_bb(?SWITCH_VN) -> true;
end_bb(?ABORT) -> true;
end_bb(?EXIT) -> true;
end_bb(_) -> false.
+301
View File
@@ -0,0 +1,301 @@
-module(aeb_heap).
-export([ to_binary/1
, to_binary/2
, from_heap/3
, from_binary/2
, from_binary/3
, maps_with_next_id/1
, set_next_id/2
, heap_fragment/3
, heap_value/3
, heap_value/4
, heap_value_pointer/1
, heap_value_maps/1
, heap_value_offset/1
, heap_value_heap/1
, heap_fragment_maps/1
, heap_fragment_offset/1
, heap_fragment_heap/1
]).
-export_type([binary_value/0, heap_value/0, offset/0, heap_fragment/0]).
-include_lib("aebytecode/include/aeb_typerep_def.hrl").
-include_lib("aebytecode/include/aeb_heap.hrl").
-type word() :: non_neg_integer().
-type pointer() :: word().
-opaque heap_fragment() :: #heap{}.
-type offset() :: non_neg_integer().
-type binary_value() :: binary().
-type heap_value() :: {pointer(), heap_fragment()}.
-spec maps_with_next_id(heap_fragment()) -> #maps{}.
%% Create just a maps value, don't keep rest of Heap
maps_with_next_id(#heap{maps = #maps{next_id = N}}) ->
#maps{ next_id = N }.
-spec set_next_id(heap_fragment(), non_neg_integer()) -> heap_fragment().
set_next_id(Heap, N) ->
Heap#heap{ maps = Heap#heap.maps#maps{ next_id = N } }.
%% -- data type heap_fragment
-spec heap_fragment(binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment().
heap_fragment(Heap) ->
heap_fragment(#maps{ next_id = 0 }, 0, Heap).
-spec heap_fragment(#maps{}, offset(),
binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment().
heap_fragment(Maps, Offset, Heap) ->
#heap{maps = Maps, offset = Offset, heap = Heap}.
-spec heap_fragment_maps(heap_fragment()) -> #maps{}.
heap_fragment_maps(#heap{maps = Maps}) ->
Maps.
-spec heap_fragment_offset(heap_fragment()) -> offset().
heap_fragment_offset(#heap{offset = Offs}) ->
Offs.
-spec heap_fragment_heap(heap_fragment()) -> binary() | #{non_neg_integer() => non_neg_integer()}.
heap_fragment_heap(#heap{heap = Heap}) ->
Heap.
%% -- data type heap_value
-spec heap_value(#maps{}, pointer(),
binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_value().
heap_value(Maps, Ptr, Heap) ->
heap_value(Maps, Ptr, Heap, 0).
-spec heap_value(#maps{}, pointer(),
binary() | #{non_neg_integer() => non_neg_integer()}, offset()) -> heap_value().
heap_value(Maps, Ptr, Heap, Offs) ->
{Ptr, heap_fragment(Maps, Offs, Heap)}.
-spec heap_value_pointer(heap_value()) -> pointer().
heap_value_pointer({Ptr, _}) -> Ptr.
-spec heap_value_maps(heap_value()) -> #maps{}.
heap_value_maps({_, Heap}) -> Heap#heap.maps.
-spec heap_value_offset(heap_value()) -> offset().
heap_value_offset({_, Heap}) -> Heap#heap.offset.
-spec heap_value_heap(heap_value()) ->
binary() | #{non_neg_integer() => non_neg_integer()}.
heap_value_heap({_, Heap}) -> Heap#heap.heap.
%% -- Value to binary --------------------------------------------------------
-spec to_binary(aeb_aevm_data:data()) -> aeb_aevm_data:heap().
%% Encode the data as a heap where the first word is the value (for unboxed
%% types) or a pointer to the value (for boxed types).
to_binary(Data) ->
to_binary(Data, 0).
to_binary(Data, BaseAddress) ->
{Address, Memory} = to_binary1(Data, BaseAddress + 32),
R = <<Address:256, Memory/binary>>,
R.
%% Allocate the data in memory, from the given address. Return a pair
%% of memory contents from that address and the value representing the
%% data.
to_binary1(Data,_Address) when is_integer(Data) ->
{Data,<<>>};
to_binary1(Data, Address) when is_binary(Data) ->
%% a string
Words = aeb_memory:binary_to_words(Data),
{Address,<<(size(Data)):256, << <<W:256>> || W <- Words>>/binary>>};
to_binary1(none, Address) -> to_binary1({variant, 0, []}, Address);
to_binary1({some, Value}, Address) -> to_binary1({variant, 1, [Value]}, Address);
to_binary1(word, Address) -> to_binary1({?TYPEREP_WORD_TAG}, Address);
to_binary1(string, Address) -> to_binary1({?TYPEREP_STRING_TAG}, Address);
to_binary1(typerep, Address) -> to_binary1({?TYPEREP_TYPEREP_TAG}, Address);
to_binary1(function, Address) -> to_binary1({?TYPEREP_FUN_TAG}, Address);
to_binary1({list, T}, Address) -> to_binary1({?TYPEREP_LIST_TAG, T}, Address);
to_binary1({option, T}, Address) -> to_binary1({variant, [[], [T]]}, Address);
to_binary1({tuple, Ts}, Address) -> to_binary1({?TYPEREP_TUPLE_TAG, Ts}, Address);
to_binary1({variant, Cons}, Address) -> to_binary1({?TYPEREP_VARIANT_TAG, Cons}, Address);
to_binary1({map, K, V}, Address) -> to_binary1({?TYPEREP_MAP_TAG, K, V}, Address);
to_binary1({variant, Tag, Args}, Address) ->
to_binary1(list_to_tuple([Tag | Args]), Address);
to_binary1(Map, Address) when is_map(Map) ->
Size = maps:size(Map),
%% Sort according to binary ordering
KVs = lists:sort([ {to_binary(K), to_binary(V)} || {K, V} <- maps:to_list(Map) ]),
{Address, <<Size:256, << <<(byte_size(K)):256, K/binary,
(byte_size(V)):256, V/binary>> || {K, V} <- KVs >>/binary >>};
to_binary1({}, _Address) ->
{0, <<>>};
to_binary1(Data, Address) when is_tuple(Data) ->
{Elems,Memory} = to_binaries(tuple_to_list(Data),Address+32*size(Data)),
ElemsBin = << <<W:256>> || W <- Elems>>,
{Address,<< ElemsBin/binary, Memory/binary >>};
to_binary1([],_Address) ->
<<Nil:256>> = <<(-1):256>>,
{Nil,<<>>};
to_binary1([H|T],Address) ->
to_binary1({H,T},Address).
to_binaries([],_Address) ->
{[],<<>>};
to_binaries([H|T],Address) ->
{HRep,HMem} = to_binary1(H,Address),
{TRep,TMem} = to_binaries(T,Address+size(HMem)),
{[HRep|TRep],<<HMem/binary, TMem/binary>>}.
%% Interpret a return value (a binary) using a type rep.
-spec from_heap(Type :: ?Type(), Heap :: binary(), Ptr :: integer()) ->
{ok, term()} | {error, term()}.
from_heap(Type, Heap, Ptr) ->
try {ok, from_binary(#{}, Type, Heap, Ptr)}
catch _:Err ->
%% io:format("** Error: from_heap failed with ~p\n ~p\n", [Err, erlang:get_stacktrace()]),
{error, Err}
end.
%% Base address is the address of the first word of the given heap.
-spec from_binary(T :: ?Type(),
Heap :: binary(),
BaseAddr :: non_neg_integer()) ->
{ok, term()} | {error, term()}.
from_binary(T, Heap = <<V:256, _/binary>>, BaseAddr) ->
from_heap(T, <<0:BaseAddr/unit:8, Heap/binary>>, V);
from_binary(_, Bin, _BaseAddr) ->
{error, {binary_too_short, Bin}}.
-spec from_binary(?Type(), binary()) -> {ok, term()} | {error, term()}.
from_binary(T, Heap) ->
from_binary(T, Heap, 0).
from_binary(_, word, _, V) ->
V;
from_binary(_, signed_word, _, V) ->
<<N:256/signed>> = <<V:256>>,
N;
from_binary(_, bool, _, V) ->
case V of
0 -> false;
1 -> true
end;
from_binary(_, string, Heap, V) ->
StringSize = heap_word(Heap,V),
BitAddr = 8*(V+32),
<<_:BitAddr,Bytes:StringSize/binary,_/binary>> = Heap,
Bytes;
from_binary(_, {tuple, []}, _, _) ->
{};
from_binary(Visited, {tuple,Cpts}, Heap, V) ->
check_circular_refs(Visited, V),
NewVisited = Visited#{V => true},
ElementNums = lists:seq(0, length(Cpts)-1),
TypesAndPointers = lists:zip(Cpts, ElementNums),
ElementAddress = fun(Index) -> V + 32 * Index end,
Element = fun(Index) ->
heap_word(Heap, ElementAddress(Index))
end,
Convert = fun(Type, Index) ->
from_binary(NewVisited, Type, Heap, Element(Index))
end,
Elements = [Convert(T, I) || {T,I} <- TypesAndPointers],
list_to_tuple(Elements);
from_binary(Visited, {list, Elem}, Heap, V) ->
<<Nil:256>> = <<(-1):256>>,
if V==Nil ->
[];
true ->
{H,T} = from_binary(Visited, {tuple,[Elem,{list,Elem}]},Heap,V),
[H|T]
end;
from_binary(Visited, {option, A}, Heap, V) ->
from_binary(Visited, {variant_t, [{none, []}, {some, [A]}]}, Heap, V);
from_binary(Visited, {variant, Cons}, Heap, V) ->
Tag = heap_word(Heap, V),
Args = lists:nth(Tag + 1, Cons),
Visited1 = Visited#{V => true},
{variant, Tag, tuple_to_list(from_binary(Visited1, {tuple, Args}, Heap, V + 32))};
from_binary(Visited, {variant_t, TCons}, Heap, V) -> %% Tagged variants
{Tags, Cons} = lists:unzip(TCons),
{variant, I, Args} = from_binary(Visited, {variant, Cons}, Heap, V),
Tag = lists:nth(I + 1, Tags),
case Args of
[] -> Tag;
_ -> list_to_tuple([Tag | Args])
end;
from_binary(_Visited, {map, A, B}, Heap, Ptr) ->
%% FORMAT: [Size] [KeySize] Key [ValSize] Val .. [KeySize] Key [ValSize] Val
Size = heap_word(Heap, Ptr),
map_binary_to_value(A, B, Size, Heap, Ptr + 32);
from_binary(Visited, typerep, Heap, V) ->
check_circular_refs(Visited, V),
Tag = heap_word(Heap, V),
Arg1 = fun(T, I) -> from_binary(Visited#{V => true}, T, Heap, heap_word(Heap, V + 32 * I)) end,
Arg = fun(T) -> Arg1(T, 1) end,
case Tag of
?TYPEREP_WORD_TAG -> word;
?TYPEREP_STRING_TAG -> string;
?TYPEREP_TYPEREP_TAG -> typerep;
?TYPEREP_LIST_TAG -> {list, Arg(typerep)};
?TYPEREP_TUPLE_TAG -> {tuple, Arg({list, typerep})};
?TYPEREP_VARIANT_TAG -> {variant, Arg({list, {list, typerep}})};
?TYPEREP_MAP_TAG -> {map, Arg(typerep), Arg1(typerep, 2)};
?TYPEREP_FUN_TAG -> function
end.
map_binary_to_value(KeyType, ValType, N, Bin, Ptr) ->
%% Avoid looping on bogus sizes
MaxN = byte_size(Bin) div 64,
Heap = heap_fragment(Bin),
map_from_binary({value, KeyType, ValType}, min(N, MaxN), Heap, Ptr, #{}).
map_from_binary(_, 0, _, _, Map) -> Map;
map_from_binary({value, KeyType, ValType} = Output, I, Heap, Ptr, Map) ->
KeySize = get_word(Heap, Ptr),
KeyPtr = Ptr + 32,
KeyBin = get_chunk(Heap, KeyPtr, KeySize),
ValSize = get_word(Heap, KeyPtr + KeySize),
ValPtr = KeyPtr + KeySize + 32,
ValBin = get_chunk(Heap, ValPtr, ValSize),
%% Keys and values are self contained binaries
{ok, Key} = from_binary(KeyType, KeyBin),
{ok, Val} = from_binary(ValType, ValBin),
map_from_binary(Output, I - 1, Heap, ValPtr + ValSize, Map#{Key => Val}).
check_circular_refs(Visited, V) ->
case maps:is_key(V, Visited) of
true -> exit(circular_references);
false -> ok
end.
heap_word(Heap, Addr) when is_binary(Heap) ->
BitSize = 8*Addr,
<<_:BitSize,W:256,_/binary>> = Heap,
W;
heap_word(Heap, Addr) when is_map(Heap) ->
0 = Addr rem 32, %% Check that it's word aligned.
maps:get(Addr, Heap, 0).
get_word(#heap{offset = Offs, heap = Mem}, Addr) when Addr >= Offs ->
get_word(Mem, Addr - Offs);
get_word(Mem, Addr) when is_binary(Mem) ->
<<_:Addr/unit:8, Word:256, _/binary>> = Mem,
Word.
get_chunk(#heap{offset = Offs, heap = Mem}, Addr, Bytes) when Addr >= Offs ->
get_chunk(Mem, Addr - Offs, Bytes);
get_chunk(Mem, Addr, Bytes) when is_binary(Mem) ->
<<_:Addr/unit:8, Chunk:Bytes/binary, _/binary>> = Mem,
Chunk.
+19
View File
@@ -0,0 +1,19 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc
%%% Memory speifics that compiler and VM need to agree upon
%%% @end
%%% Created : 19 Dec 2018
%%%-------------------------------------------------------------------
-module(aeb_memory).
-export([binary_to_words/1]).
binary_to_words(<<>>) ->
[];
binary_to_words(<<N:256,Bin/binary>>) ->
[N|binary_to_words(Bin)];
binary_to_words(Bin) ->
binary_to_words(<<Bin/binary,0>>).
-91
View File
@@ -1,91 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc
%%% Implementation of the Recursive Length Prefix.
%%%
%%% https://github.com/ethereum/wiki/wiki/RLP
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(aeb_rlp).
-export([ decode/1
, decode_one/1
, encode/1
]).
-export_type([ encodable/0
, encoded/0
]).
-type encodable() :: [encodable()] | binary().
-type encoded() :: <<_:8, _:_*8>>.
-define(UNTAGGED_SIZE_LIMIT , 55).
-define(UNTAGGED_LIMIT , 127).
-define(BYTE_ARRAY_OFFSET , 128).
-define(LIST_OFFSET , 192).
-spec encode(encodable()) -> encoded().
encode(X) ->
encode(X, []).
encode(<<B>> = X,_Opts) when B =< ?UNTAGGED_LIMIT ->
%% An untagged value
X;
encode(X,_Opts) when is_binary(X) ->
%% Byte array
add_size(?BYTE_ARRAY_OFFSET, X);
encode(L, Opts) when is_list(L) ->
%% Lists items are encoded and concatenated
ByteArray = << << (encode(X, Opts))/binary >> || X <- L >>,
add_size(?LIST_OFFSET, ByteArray).
add_size(Offset, X) when byte_size(X) =< ?UNTAGGED_SIZE_LIMIT ->
%% The size fits in one tagged byte
<<(Offset + byte_size(X)), X/binary>>;
add_size(Offset, X) when is_binary(X) ->
%% The size itself needs to be encoded as a byte array
%% Add the tagged size of the size byte array
SizeBin = binary:encode_unsigned(byte_size(X)),
TaggedSize = ?UNTAGGED_SIZE_LIMIT + Offset + byte_size(SizeBin),
true = (TaggedSize < 256 ), %% Assert
<<TaggedSize, SizeBin/binary, X/binary>>.
-spec decode(encoded()) -> encodable().
decode(Bin) when is_binary(Bin), byte_size(Bin) > 0 ->
case decode_one(Bin) of
{X, <<>>} -> X;
{X, Left} -> error({trailing, X, Bin, Left})
end.
decode_one(<<X, B/binary>>) when X =< ?UNTAGGED_LIMIT ->
%% Untagged value
{<<X>>, B};
decode_one(<<L, _/binary>> = B) when L < ?LIST_OFFSET ->
%% Byte array
{Size, Rest} = decode_size(B, ?BYTE_ARRAY_OFFSET),
<<X:Size/binary, Tail/binary>> = Rest,
{X, Tail};
decode_one(<<_/binary>> = B) ->
%% List
{Size, Rest} = decode_size(B, ?LIST_OFFSET),
<<X:Size/binary, Tail/binary>> = Rest,
{decode_list(X), Tail}.
decode_size(<<L, B/binary>>, Offset) when L =< Offset + ?UNTAGGED_SIZE_LIMIT->
%% One byte tagged size.
{L - Offset, B};
decode_size(<<_, 0, _/binary>>,_Offset) ->
error(leading_zeroes_in_size);
decode_size(<<L, B/binary>>, Offset) ->
%% Actual size is in a byte array.
BinSize = L - Offset - ?UNTAGGED_SIZE_LIMIT,
<<Size:BinSize/unit:8, Rest/binary>> = B,
{Size, Rest}.
decode_list(<<>>) -> [];
decode_list(B) ->
{Element, Rest} = decode_one(B),
[Element|decode_list(Rest)].
+3 -1
View File
@@ -1,10 +1,12 @@
{application, aebytecode,
[{description, "Bytecode definitions, serialization and deserialization for aeternity."},
{vsn, "2.0.0"},
{vsn, "2.1.0"},
{registered, []},
{applications,
[kernel,
stdlib,
eblake2,
aeserialization,
getopt
]},
{env,[]},
+6 -2
View File
@@ -50,16 +50,20 @@ sources() ->
, "remote"
, "test"
, "tuple"
, "mapofmap"
, "immediates"
, "all_instructions"
].
check_roundtrip(File) ->
AssemblerCode = read_file(File),
{Env, ByteCode} = assemble(AssemblerCode),
{_Env, ByteCode} = assemble(AssemblerCode),
FateCode = disassemble(ByteCode),
DissasmCode = aeb_fate_asm:to_asm(FateCode),
io:format("~s~n", [AssemblerCode]),
io:format("~s~n", [DissasmCode]),
{Env2, ByteCode2} = assemble(DissasmCode),
{_Env2, ByteCode2} = assemble(DissasmCode),
Code1 = aeb_fate_asm:strip(ByteCode),
Code2 = aeb_fate_asm:strip(ByteCode2),
io:format("~s~n", [aeb_fate_asm:to_asm(disassemble(ByteCode2))]),
?assertEqual(Code1, Code2).
+14 -7
View File
@@ -47,6 +47,11 @@ sources() ->
[aeb_fate_data:make_integer(0),
aeb_fate_data:make_integer(1),
True, False, Unit, Nil, EmptyString, EmptyMap,
aeb_fate_data:make_hash(<<1,2,3,4,5>>),
aeb_fate_data:make_signature(<<1,2,3,4,5>>),
aeb_fate_data:make_contract(<<1,2,3,4,5>>),
aeb_fate_data:make_name(<<1,2,3,4,5>>),
aeb_fate_data:make_channel(<<1,2,3,4,5>>),
aeb_fate_data:make_list([True]),
aeb_fate_data:make_address(
<<0,1,2,3,4,5,6,7,8,9,
@@ -61,6 +66,7 @@ sources() ->
"0123456789012345678901234567890123456789">>), %% Magic concat 80 char string.
aeb_fate_data:make_tuple({True, FortyTwo}),
aeb_fate_data:make_tuple(list_to_tuple(make_int_list(65))),
aeb_fate_data:make_tuple(list_to_tuple(make_int_list(16))),
aeb_fate_data:make_map(#{ aeb_fate_data:make_integer(1) => True, aeb_fate_data:make_integer(2) => False}),
aeb_fate_data:make_map(#{ aeb_fate_data:make_string(<<"foo">>) => aeb_fate_data:make_tuple({FortyTwo, True})}),
aeb_fate_data:make_list(make_int_list(3)),
@@ -71,12 +77,13 @@ sources() ->
aeb_fate_data:make_bits(1),
aeb_fate_data:make_bits(-1),
aeb_fate_data:make_list(make_int_list(65)),
aeb_fate_data:make_variant(2, 0, {FortyTwo}),
aeb_fate_data:make_variant(2, 1, {}),
aeb_fate_data:make_list([aeb_fate_data:make_variant(3, 0, {})]),
aeb_fate_data:make_variant(255, 254, {}),
aeb_fate_data:make_variant(5, 3, {aeb_fate_data:make_boolean(true),
aeb_fate_data:make_list(make_int_list(3)),
aeb_fate_data:make_string(<<"foo">>)})
aeb_fate_data:make_variant([1,2,3], 0, {FortyTwo}),
aeb_fate_data:make_variant([2,0], 1, {}),
aeb_fate_data:make_list([aeb_fate_data:make_variant([0,0,0], 0, {})]),
aeb_fate_data:make_variant([0|| _<-lists:seq(1,255)], 254, {}),
aeb_fate_data:make_variant([0,1,2,3,4,5],
3, {aeb_fate_data:make_boolean(true),
aeb_fate_data:make_list(make_int_list(3)),
aeb_fate_data:make_string(<<"foo">>)})
].
+237
View File
@@ -0,0 +1,237 @@
;; CONTRACT all_instructions
;; Dont expect this contract to typecheck or run.
;; Just used to check assembler rountrip of all instruction.
FUNCTION foo () : {tuple, []}
RETURN
RETURNR a13
CALL foo
CALL_R arg125 foo
CALL_T foo
CALL_TR arg245 foo
JUMP 5514251025295783441695716053282666408426
JUMPIF arg196 0x12c651665
SWITCH_V2 a27 63 33
SWITCH_V3 var4 0x1d61723dd 79 7
SWITCH_VN arg0 [1, 2, 3]
PUSH var80
DUPA
DUP a
POP a107
STORE arg183 var225
INCA
INC a25
DECA
DEC a
ADD a217 a a
SUB arg35 arg165 var74
MUL 44 35 "foo"
DIV 263838340369912686645632650718169038811 a24 a
MOD var113 arg80 arg207
POW a176 a a123
LT a 78 var81
GT arg19 4729414120208894485838100532547810615352 var175
EQ 85 a arg164
ELT a161 arg226 a168
EGT a131 1 var250
NEQ a85 a a83
AND var255 0x294a24f6 var189
OR (| [2,0] | 0 | ( (), (42) ) |) arg168 var107
NOT arg124 a
TUPLE 5019186157739257888756115213149493826410
ELEMENT arg148 var25 a219
MAP_EMPTY a135
MAP_LOOKUP a82 a a143
MAP_LOOKUPD var112 arg35 a163 var112
MAP_UPDATE false a0 a56 a
MAP_DELETE arg180 a var1
MAP_MEMBER a { true => 4} 94
MAP_FROM_LIST () a159
NIL arg91
IS_NIL a121 var6
CONS arg185 "foo" a114
HD a150 var124
TL arg223 a
LENGTH var216 a143
STR_EQ { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a177
STR_JOIN a a 7144184027126178769820155907121270843348
INT_TO_STR var238 a
ADDR_TO_STR a arg216
STR_REVERSE a174 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
INT_TO_ADDR arg127 var207
VARIANT a a 0x1f7b72200 a
VARIANT_TEST a26 arg217 a
VARIANT_ELEMENT a86 arg103 arg108
BITS_NONEA
BITS_NONE a
BITS_ALLA
BITS_ALL a164
BITS_ALL_N a221 arg135
BITS_SET arg150 a48 { 0x1a715e2a6 => 3}
BITS_CLEAR arg98 a arg164
BITS_TEST a a242 (| [0,0,3] | 2 | (1, "foo", ()) |)
BITS_SUM a244 a71
BITS_OR var20 var186 a
BITS_AND a187 4 arg203
BITS_DIFF var200 arg247 var20
ADDRESS a237
BALANCE a231
ORIGIN arg216
CALLER a27
GASPRICE arg119
BLOCKHASH a arg110
BENEFICIARY var163
TIMESTAMP a
GENERATION 242795038229506961431398379342231049652
MICROBLOCK arg43
DIFFICULTY var24
GASLIMIT arg220
GAS var35
LOG0 a a85
LOG1 arg94 arg86 arg208
LOG2 a113 (| [0,1,3] | 2 | (1, "foo", ()) |) arg238 var108
LOG3 arg255 arg15 arg211 var139 arg44
LOG4 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv a247 a 9 a38 a
DEACTIVATE
SPEND @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv var136
ORACLE_REGISTER arg29 48 ((| [0,1,3] | 2 | (1, "foo", ()) |)) arg65 { <> => false} <>
ORACLE_QUERY
ORACLE_RESPOND
ORACLE_EXTEND
ORACLE_GET_ANSWER
ORACLE_GET_QUESTION
ORACLE_QUERY_FEE
AENS_RESOLVE
AENS_PRECLAIM
AENS_CLAIM
AENS_UPDATE
AENS_TRANSFER
AENS_REVOKE
ECVERIFY
SHA3
SHA256
BLAKE2B
DUMMY7ARG a a 7607708484837907159893701471377343595877 (| [2,1] | 0 | ( [], [ 45, { 1 => 3441201581501946066216994494994943246334} ] ) |) a0 var56 "foo"
DUMMY8ARG 3673679924816289365509492271980889822579 a69 arg242 var237 a175 arg106 () var255
ABORT a
EXIT var120
NOP
RETURNR ()
BALANCE_OTHER a arg0
SETELEMENT a 2 (1, "two", 3) 2
+77
View File
@@ -0,0 +1,77 @@
;; CONTRACT immediates
FUNCTION integer() : integer
RETURNR 42
FUNCTION neg_integer() : integer
RETURNR -2374683271468723648732648736498712634876147
FUNCTION hex_integer() : integer
RETURNR 0x0deadbeef0
FUNCTION bool() : boolean
RETURNR true
FUNCTION bool_f() : boolean
RETURNR false
FUNCTION string() : string
RETURNR "Hello"
FUNCTION map() : {map, integer, boolean}
RETURNR {}
FUNCTION map2() : {map, integer, boolean}
RETURNR {1 => true}
FUNCTION map3() : {map, integer, boolean}
RETURNR {1 => true,
2 => false}
FUNCTION map4() : {map, integer, {map, string, boolean}}
RETURNR {1 => { "foo" => true, "bar" => false},
2 => {},
3 => { "foo" => false}}
FUNCTION nil() : {list, integer}
RETURNR []
FUNCTION list1() : {list, integer}
RETURNR [1]
FUNCTION list2() : {list, integer}
RETURNR [1, 2]
FUNCTION no_bits() : bits
RETURNR <>
FUNCTION all_bits() : bits
RETURNR !<>
FUNCTION some_bits() : bits
RETURNR <101010>
FUNCTION many_bits() : bits
RETURNR !<010101>
FUNCTION group_bits() : bits
RETURNR <1010 1010 0011 1001>
FUNCTION unit() : {tuple, []}
RETURNR ()
FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]}]}
RETURNR (42, true, "FooBar", (1, 2))
FUNCTION address() : address
RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
;; Option(integer) = NONE | SOME(integer)
FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR (| [0,1] | 0 | () |)
;; Option(integer) = NONE | SOME(integer)
FUNCTION variant_some() : {variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR (| [0,1] | 1 | (42) |)
+7
View File
@@ -0,0 +1,7 @@
;; CONTRACT mapofmap
FUNCTION map() : {map, integer, {map, string, boolean}}
RETURNR {1 => { "foo" => true, "bar" => false},
2 => {},
3 => { "foo" => false}}
+2 -2
View File
@@ -26,10 +26,10 @@ FUNCTION element1(integer, integer): integer
PUSH arg0
PUSH arg1
TUPLE 2
ELEMENT integer a 1 a
ELEMENT a 1 a
RETURN
FUNCTION element({tuple, [integer, integer]}, integer): integer
;; BB : 0
ELEMENT integer a arg1 arg0
ELEMENT a arg1 arg0
RETURN