├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── ebin └── .gitkeep ├── include ├── aeb_fate_data.hrl ├── aeb_heap.hrl ├── aeb_opcodes.hrl └── aeb_typerep_def.hrl ├── quickcheck ├── aeb_fate_code_tests.erl ├── aeb_fate_data_tests.erl ├── aeb_fate_encoding_tests.erl ├── aefate_code_eqc.erl ├── aefate_eqc.erl └── aefate_type_eqc.erl ├── rebar.config ├── rebar.lock ├── rebar3 ├── src ├── aeb_aevm_abi.erl ├── aeb_aevm_data.erl ├── aeb_asm.erl ├── aeb_asm_scan.xrl ├── aeb_disassemble.erl ├── aeb_fate_abi.erl ├── aeb_fate_asm.erl ├── aeb_fate_asm_scan.template ├── aeb_fate_code.erl ├── aeb_fate_data.erl ├── aeb_fate_encoding.erl ├── aeb_fate_generate_docs.erl ├── aeb_fate_generate_ops.erl ├── aeb_fate_maps.erl ├── aeb_heap.erl ├── aeb_memory.erl ├── aeb_opcodes.erl ├── aeb_primops.erl ├── aebytecode.app.src └── aefateasm.erl └── test ├── aeb_data_test.erl ├── aeb_fate_asm_test.erl ├── aeb_serialize_test.erl ├── aebytecode_SUITE.erl └── asm_code ├── arith.fate ├── bool.fate ├── comp.fate ├── identity.aesm ├── identity.fate ├── immediates.fate ├── jumpif.fate ├── map.fate ├── mapofmap.fate ├── memory.fate ├── meta.fate ├── names.fate ├── oracles.fate ├── remote.fate ├── test.fate └── tuple.fate /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | 3 | executors: 4 | aebuilder: 5 | docker: 6 | - image: aeternity/builder:bionic-otp23 7 | user: builder 8 | working_directory: ~/aebytecode 9 | 10 | jobs: 11 | build: 12 | executor: aebuilder 13 | steps: 14 | - checkout 15 | - restore_cache: 16 | keys: 17 | - dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }} 18 | - dialyzer-cache-v1-{{ .Branch }}- 19 | - dialyzer-cache-v1- 20 | - run: 21 | name: Build 22 | command: rebar3 compile 23 | - run: 24 | name: Static Analysis 25 | command: make dialyzer 26 | - run: 27 | name: Eunit 28 | command: make eunit 29 | - run: 30 | name: Common Tests 31 | command: make test 32 | - save_cache: 33 | key: dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }} 34 | paths: 35 | - _build/default/rebar3_20.3.8_plt 36 | - store_artifacts: 37 | path: _build/test/logs 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | deps 3 | *.o 4 | *.beam 5 | *.plt 6 | erl_crash.dump 7 | ebin/*.beam 8 | rel/example_project 9 | .concrete/DEV_MODE 10 | .rebar 11 | aeb_asm_scan.erl 12 | aeb_fate_asm_scan.erl 13 | aeb_fate_asm_scan.xrl 14 | _build/ 15 | aefateasm 16 | include/aeb_fate_opcodes.hrl 17 | src/aeb_fate_opcodes.erl 18 | src/aeb_fate_ops.erl 19 | src/aeb_fate_pp.erl 20 | *.erl~ 21 | *.hrl~ 22 | *.aes~ 23 | doc 24 | cover 25 | aefate 26 | current_counterexample.eqc 27 | .rebar3 28 | ebin 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2017, aeternity developers 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GENERATED_SRC = src/aeb_fate_opcodes.erl src/aeb_fate_ops.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl 2 | GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template 3 | REBAR ?= ./rebar3 4 | 5 | all: local 6 | 7 | sources: $(GENERATED_SRC) 8 | 9 | local: $(GENERATED_SRC) 10 | @$(REBAR) as local release 11 | 12 | console: local 13 | @$(REBAR) as local shell 14 | 15 | clean: 16 | @$(REBAR) clean 17 | rm -f $(GENERATED_SRC) 18 | rm -f ebin/* 19 | 20 | dialyzer: local 21 | @$(REBAR) as local dialyzer 22 | 23 | distclean: clean 24 | @rm -rf _build/ 25 | 26 | eunit: local 27 | @$(REBAR) as local eunit 28 | 29 | test: local 30 | @$(REBAR) as local eunit 31 | 32 | ebin/%.beam: src/%.erl 33 | erlc +debug_info -o $(dir $@) $< 34 | 35 | $(GENERATED_SRC): $(GENERATOR_DEPS) 36 | erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/ 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aebytecode 2 | A library and stand alone assembler for aeternity bytecode. 3 | 4 | This version supports AEVM bytecode and FATE bytecode. 5 | 6 | ## Build 7 | 8 | $ make 9 | 10 | ## Fate Code 11 | 12 | Fate code exists in 3 formats: 13 | 14 | 1. Fate byte code. This format is under consensus. 15 | 2. Fate assembler. This is a text represenation of fate code. 16 | This is not under consensus and other 17 | implemenation and toolchains could have 18 | their own format. 19 | 3. Internal. This is an Erlang representation of fate code 20 | Used by this particular engin implementation. 21 | 22 | This library handles all three representations. 23 | The byte code format is described in a separate document. 24 | The internal format is described in a separate document. 25 | The text representation is described below. 26 | 27 | ### Fate Assembler Code 28 | 29 | Assembler code can be read from a file. 30 | The assembler has the following format: 31 | 32 | Comments start with 2 semicolons and runs till end of line 33 | `;; This is a comment` 34 | 35 | Opcode mnemonics start with an upper case letter. 36 | `DUP` 37 | 38 | Identifiers start with a lower case letter 39 | `an_identifier` 40 | 41 | References to function arguments start with arg followed by an integer 42 | `arg0` 43 | 44 | References to variables/registers start with var followed by an integer 45 | `var0` 46 | 47 | References to stack postions is either a (for stack 0) 48 | or start with stack followed by an integer 49 | `stack1` 50 | `a` 51 | 52 | Immediate values can be of 10 types: 53 | 54 | 1. Integers as decimals: {Digits} or -{Digits} 55 | `42` 56 | `-2374683271468723648732648736498712634876147` 57 | And integers as Hexadecimals:: 0x{Hexdigits} 58 | `0x0deadbeef0` 59 | 60 | 2. Chain Objects. These are all addresses to different types of chain objects. 61 | Each address is a 256 bits number encoded in base58 with checksum 62 | with a prefix of "@" plus a type prefix followed by "_". 63 | 64 | 2a. Account Address: a base58c encoded number starting with @ak_ followed by a number of base58chars 65 | '@ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` 66 | 67 | 2b. Contract address: @ct_{base58char}+ 68 | `@ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` 69 | 70 | 2c. Oracle address: @ok_{base58char}+ 71 | `@ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` 72 | 73 | 2d. Oracle query: @oq_{base58char}+ 74 | `@oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` 75 | 76 | 2e. Channel address: @ch_{base58char}+ 77 | `@ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` 78 | 79 | 3. Boolean true or false 80 | `true` 81 | `false` 82 | 83 | 4. Strings "{Characters}" 84 | `"Hello"` 85 | 86 | 5. Map { Key => Value } 87 | `{}` 88 | `{ 1 => { "foo" => true, "bar" => false}` 89 | 90 | 6. Lists [ Elements ] 91 | `[]` 92 | `[1, 2]` 93 | 94 | 7. Bit field < Bits > or !< Bits > 95 | `<000>` 96 | `<1010 1010>` 97 | `<>` 98 | `!<>` 99 | 100 | 8. Tuples ( Elements ) 101 | `()` 102 | `(1, "foo")` 103 | 104 | 9. Variants: (| [Arities] | Tag | ( Elements ) |) 105 | `(| [1,3,5,2] | 3 | ( "foo", 12) |)` 106 | 107 | 10. Bytes: #{base64char}+ 108 | `#AQIDCioLFQ==` 109 | 110 | 11. Contract bytearray (code of another smart contract) 111 | `@cb_+PJGA6A4Fz4T2LHV5knITCldR3rqO7HrXO2zhOAR9JWNbhf8Q8C4xbhx/gx8JckANwAXfQBVACAAAP4vhlvZADcABwECgv5E1kQfADcBBzcACwAWMBReAHMAFjBvJFMAFjBvggOoFAAUABQSggABAz/+tIwWhAA3AAdTAAD+1jB5kAQ3AAcLAAD+6MRetgA3AQc3ABoGggABAz+4TS8GEQx8JclFY2FsbGVyX2lzX2NyZWF0b3IRL4Zb2Q1nZXQRRNZEHxFpbml0EbSMFoQdYmFsYW5jZRHWMHmQFXZhbHVlEejEXrYNc2V0gi8AhTQuMy4wAUqQ8s4=` 112 | 113 | 114 | Where 115 | 116 | Digits: [0123456789] 117 | 118 | Hexdigits: [0123456789abcdef] 119 | 120 | base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz] 121 | 122 | base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=] 123 | 124 | Characters: any printable ascii character 0..255 (except " no quoting yet) 125 | 126 | Key: any value except for a map 127 | 128 | Bits: 01 or space 129 | 130 | Elements: Nothing or Value , Elements 131 | 132 | Size: Digits (0 < Size < 256) 133 | 134 | Tag: Digits (0 =< Tag < Size) 135 | 136 | -------------------------------------------------------------------------------- /ebin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aeternity/aebytecode/6bd6f82c70d800950ea1a2c70c364a4181ff5291/ebin/.gitkeep -------------------------------------------------------------------------------- /include/aeb_fate_data.hrl: -------------------------------------------------------------------------------- 1 | -define(FATE_INTEGER_T, integer()). 2 | -define(FATE_BYTE_T, 0..255). 3 | -define(FATE_BOOLEAN_T, true | false). 4 | -define(FATE_NIL_T, []). 5 | -define(FATE_LIST_T, list()). 6 | -define(FATE_UNIT_T, {tuple, {}}). 7 | -define(FATE_MAP_T, #{ fate_type() => fate_type() }). 8 | -define(FATE_STORE_MAP_T, {store_map, #{ fate_type() => fate_type() | ?FATE_MAP_TOMBSTONE }, integer()}). 9 | -define(FATE_STRING_T, binary()). 10 | -define(FATE_ADDRESS_T, {address, <<_:256>>}). 11 | -define(FATE_BYTES_T(N), {bytes, binary()}). 12 | -define(FATE_CONTRACT_T, {contract, <<_:256>>}). 13 | -define(FATE_ORACLE_T, {oracle, <<_:256>>}). 14 | -define(FATE_ORACLE_Q_T, {oracle_query, <<_:256>>}). 15 | -define(FATE_CHANNEL_T, {channel, <<_:256>>}). 16 | -define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}). 17 | -define(FATE_VOID_T, void). 18 | -define(FATE_TUPLE_T, {tuple, tuple()}). 19 | -define(FATE_BITS_T, {bits, integer()}). 20 | -define(FATE_TYPEREP_T, {typerep, fate_type_type()}). 21 | -define(FATE_CONTRACT_BYTEARRAY_T, {contract_bytearray, binary()}). 22 | 23 | -define(IS_FATE_INTEGER(X), (is_integer(X))). 24 | -define(IS_FATE_LIST(X), (is_list(X))). 25 | -define(IS_FATE_STRING(X), (is_binary(X))). 26 | -define(IS_FATE_STORE_MAP(X), (is_tuple(X) andalso tuple_size(X) == 3 27 | andalso store_map == element(1, X) 28 | andalso is_map(element(2, X)) 29 | andalso is_integer(element(3, X)))). 30 | -define(IS_FATE_MAP(X), (is_map(X))). 31 | -define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))). 32 | -define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))). 33 | -define(IS_FATE_BYTES(X), (is_tuple(X) andalso (bytes == element(1, X) andalso is_binary(element(2, X))))). 34 | -define(IS_FATE_BYTES(N, X), (?IS_FATE_BYTES(X) andalso byte_size(element(2, X)) == (N))). 35 | -define(IS_FATE_CONTRACT(X), (is_tuple(X) andalso (contract == element(1, X) andalso is_binary(element(2, X))))). 36 | -define(IS_FATE_ORACLE(X), (is_tuple(X) andalso (oracle == element(1, X) andalso is_binary(element(2, X))))). 37 | -define(IS_FATE_ORACLE_Q(X), (is_tuple(X) andalso (oracle_query == element(1, X) andalso is_binary(element(2, X))))). 38 | -define(IS_FATE_CHANNEL(X), (is_tuple(X) andalso (channel == element(1, X) andalso is_binary(element(2, X))))). 39 | -define(IS_FATE_BITS(X), (is_tuple(X) andalso (bits == element(1, X) andalso is_integer(element(2, X))))). 40 | -define(IS_FATE_VARIANT(X), (is_tuple(X) 41 | andalso 42 | (variant == element(1, X) 43 | andalso is_list(element(2, X)) 44 | andalso is_integer(element(3, X)) 45 | andalso is_tuple(element(4, X)) 46 | ))). 47 | -define(IS_FATE_BOOLEAN(X), is_boolean(X)). 48 | -define(IS_FATE_TYPEREP(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= typerep)). 49 | -define(IS_FATE_CONTRACT_BYTEARRAY(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= contract_bytearray 50 | andalso is_binary(element(2, X)))). 51 | 52 | -define(FATE_UNIT, {tuple, {}}). 53 | -define(FATE_TUPLE(T), {tuple, T}). 54 | -define(FATE_ADDRESS(A), {address, A}). 55 | -define(FATE_BYTES(X), {bytes, X}). 56 | -define(FATE_CONTRACT(X), {contract, X}). 57 | -define(FATE_ORACLE(X), {oracle, X}). 58 | -define(FATE_ORACLE_Q(X), {oracle_query, X}). 59 | -define(FATE_CHANNEL(X), {channel, X}). 60 | -define(FATE_BITS(B), {bits, B}). 61 | -define(FATE_TYPEREP(T), {typerep, T}). 62 | -define(FATE_STORE_MAP(Cache, Id), {store_map, Cache, Id}). 63 | -define(FATE_MAP_TOMBSTONE, '__DELETED__'). 64 | 65 | -define(FATE_INTEGER_VALUE(X), (X)). 66 | -define(FATE_BOOLEAN_VALUE(X), (X)). 67 | -define(FATE_LIST_VALUE(X), (X)). 68 | -define(FATE_TUPLE_ELEMENTS(X), (tuple_to_list(element(2, X)))). 69 | -define(FATE_STRING_VALUE(X), (X)). 70 | -define(FATE_ADDRESS_VALUE(X), (element(2, X))). 71 | -define(FATE_BYTES_VALUE(X), (element(2, X))). 72 | -define(FATE_CONTRACT_VALUE(X), (element(2, X))). 73 | -define(FATE_ORACLE_VALUE(X), (element(2, X))). 74 | -define(FATE_CHANNEL_VALUE(X), (element(2, X))). 75 | -define(FATE_BITS_VALUE(X), (element(2, X))). 76 | -define(FATE_MAP_VALUE(X), (X)). 77 | -define(FATE_STORE_MAP_CACHE(X), (element(2, X))). 78 | -define(FATE_STORE_MAP_ID(X), (element(3, X))). 79 | -define(FATE_MAP_SIZE(X), (map_size(X))). 80 | -define(FATE_STRING_SIZE(X), (byte_size(X))). 81 | -define(FATE_CONTRACT_BYTEARRAY_SIZE(X), (byte_size(X))). 82 | -define(FATE_TRUE, true). 83 | -define(FATE_FALSE, false). 84 | -define(FATE_NIL, []). 85 | -define(FATE_VOID, void). 86 | -define(FATE_EMPTY_STRING, <<>>). 87 | -define(FATE_STRING(S), S). 88 | -define(FATE_VARIANT(Arity, Tag,T), {variant, Arity, Tag, T}). 89 | -define(FATE_CONTRACT_BYTEARRAY(B), {contract_bytearray, B}). 90 | 91 | % Result of aeb_fate_code:symbol_identifier(<<"init">>). 92 | % Stored here to avoid repeated calls to eblake2 93 | -define(FATE_INIT_ID, <<68,214,68,31>>). 94 | 95 | -define(MAKE_FATE_INTEGER(X), X). 96 | -define(MAKE_FATE_LIST(X), X). 97 | -define(MAKE_FATE_MAP(X), X). 98 | -define(MAKE_FATE_STRING(X), X). 99 | -define(MAKE_FATE_CONTRACT_BYTEARRAY(X), {contract_bytearray, X}). 100 | -------------------------------------------------------------------------------- /include/aeb_heap.hrl: -------------------------------------------------------------------------------- 1 | 2 | -record(pmap, {key_t :: aeb_aevm_data:type(), 3 | val_t :: aeb_aevm_data:type(), 4 | parent :: none | non_neg_integer(), 5 | size = 0 :: non_neg_integer(), 6 | data :: #{aeb_heap:binary_value() => aeb_heap:binary_value() | tombstone} 7 | | stored}). 8 | 9 | -record(maps, { maps = #{} :: #{ non_neg_integer() => #pmap{} } 10 | , next_id = 0 :: non_neg_integer() }). 11 | 12 | -record(heap, { maps :: #maps{}, 13 | offset :: aeb_heap:offset(), 14 | heap :: binary() | #{non_neg_integer() => non_neg_integer()} }). 15 | 16 | -------------------------------------------------------------------------------- /include/aeb_opcodes.hrl: -------------------------------------------------------------------------------- 1 | 2 | %% AEVM opcodes 3 | -define( 'STOP', 16#00). 4 | -define( 'ADD', 16#01). 5 | -define( 'MUL', 16#02). 6 | -define( 'SUB', 16#03). 7 | -define( 'DIV', 16#04). 8 | -define( 'SDIV', 16#05). 9 | -define( 'MOD', 16#06). 10 | -define( 'SMOD', 16#07). 11 | -define( 'ADDMOD', 16#08). 12 | -define( 'MULMOD', 16#09). 13 | -define( 'EXP', 16#0a). 14 | -define( 'SIGNEXTEND', 16#0b). 15 | 16 | -define( 'LT', 16#10). 17 | -define( 'GT', 16#11). 18 | -define( 'SLT', 16#12). 19 | -define( 'SGT', 16#13). 20 | -define( 'EQ', 16#14). 21 | -define( 'ISZERO', 16#15). 22 | -define( 'AND', 16#16). 23 | -define( 'OR', 16#17). 24 | -define( 'XOR', 16#18). 25 | -define( 'NOT', 16#19). 26 | -define( 'BYTE', 16#1a). 27 | -define( 'SHL', 16#1b). 28 | -define( 'SHR', 16#1c). 29 | -define( 'SAR', 16#1d). 30 | 31 | -define( 'SHA3', 16#20). 32 | 33 | -define( 'CREATOR', 16#2f). 34 | -define( 'ADDRESS', 16#30). 35 | -define( 'BALANCE', 16#31). 36 | -define( 'ORIGIN', 16#32). 37 | -define( 'CALLER', 16#33). 38 | -define( 'CALLVALUE', 16#34). 39 | -define( 'CALLDATALOAD', 16#35). 40 | -define( 'CALLDATASIZE', 16#36). 41 | -define( 'CALLDATACOPY', 16#37). 42 | -define( 'CODESIZE', 16#38). 43 | -define( 'CODECOPY', 16#39). 44 | -define( 'GASPRICE', 16#3a). 45 | -define( 'EXTCODESIZE', 16#3b). 46 | -define( 'EXTCODECOPY', 16#3c). 47 | -define( 'RETURNDATASIZE', 16#3d). 48 | -define( 'RETURNDATACOPY', 16#3e). 49 | 50 | -define( 'BLOCKHASH', 16#40). 51 | -define( 'COINBASE', 16#41). 52 | -define( 'TIMESTAMP', 16#42). 53 | -define( 'NUMBER', 16#43). 54 | -define( 'DIFFICULTY', 16#44). 55 | -define( 'GASLIMIT', 16#45). 56 | 57 | -define( 'POP', 16#50). 58 | -define( 'MLOAD', 16#51). 59 | -define( 'MSTORE', 16#52). 60 | -define( 'MSTORE8', 16#53). 61 | -define( 'SLOAD', 16#54). 62 | -define( 'SSTORE', 16#55). 63 | -define( 'JUMP', 16#56). 64 | -define( 'JUMPI', 16#57). 65 | -define( 'PC', 16#58). 66 | -define( 'MSIZE', 16#59). 67 | -define( 'GAS', 16#5a). 68 | -define( 'JUMPDEST', 16#5b). 69 | 70 | -define( 'PUSH1', 16#60). 71 | -define( 'PUSH2', 16#61). 72 | -define( 'PUSH3', 16#62). 73 | -define( 'PUSH4', 16#63). 74 | -define( 'PUSH5', 16#64). 75 | -define( 'PUSH6', 16#65). 76 | -define( 'PUSH7', 16#66). 77 | -define( 'PUSH8', 16#67). 78 | -define( 'PUSH9', 16#68). 79 | -define( 'PUSH10', 16#69). 80 | -define( 'PUSH11', 16#6a). 81 | -define( 'PUSH12', 16#6b). 82 | -define( 'PUSH13', 16#6c). 83 | -define( 'PUSH14', 16#6d). 84 | -define( 'PUSH15', 16#6e). 85 | -define( 'PUSH16', 16#6f). 86 | -define( 'PUSH17', 16#70). 87 | -define( 'PUSH18', 16#71). 88 | -define( 'PUSH19', 16#72). 89 | -define( 'PUSH20', 16#73). 90 | -define( 'PUSH21', 16#74). 91 | -define( 'PUSH22', 16#75). 92 | -define( 'PUSH23', 16#76). 93 | -define( 'PUSH24', 16#77). 94 | -define( 'PUSH25', 16#78). 95 | -define( 'PUSH26', 16#79). 96 | -define( 'PUSH27', 16#7a). 97 | -define( 'PUSH28', 16#7b). 98 | -define( 'PUSH29', 16#7c). 99 | -define( 'PUSH30', 16#7d). 100 | -define( 'PUSH31', 16#7e). 101 | -define( 'PUSH32', 16#7f). 102 | -define( 'DUP1', 16#80). 103 | -define( 'DUP2', 16#81). 104 | -define( 'DUP3', 16#82). 105 | -define( 'DUP4', 16#83). 106 | -define( 'DUP5', 16#84). 107 | -define( 'DUP6', 16#85). 108 | -define( 'DUP7', 16#86). 109 | -define( 'DUP8', 16#87). 110 | -define( 'DUP9', 16#88). 111 | -define( 'DUP10', 16#89). 112 | -define( 'DUP11', 16#8a). 113 | -define( 'DUP12', 16#8b). 114 | -define( 'DUP13', 16#8c). 115 | -define( 'DUP14', 16#8d). 116 | -define( 'DUP15', 16#8e). 117 | -define( 'DUP16', 16#8f). 118 | -define( 'SWAP1', 16#90). 119 | -define( 'SWAP2', 16#91). 120 | -define( 'SWAP3', 16#92). 121 | -define( 'SWAP4', 16#93). 122 | -define( 'SWAP5', 16#94). 123 | -define( 'SWAP6', 16#95). 124 | -define( 'SWAP7', 16#96). 125 | -define( 'SWAP8', 16#97). 126 | -define( 'SWAP9', 16#98). 127 | -define( 'SWAP10', 16#99). 128 | -define( 'SWAP11', 16#9a). 129 | -define( 'SWAP12', 16#9b). 130 | -define( 'SWAP13', 16#9c). 131 | -define( 'SWAP14', 16#9d). 132 | -define( 'SWAP15', 16#9e). 133 | -define( 'SWAP16', 16#9f). 134 | -define( 'LOG0', 16#a0). 135 | -define( 'LOG1', 16#a1). 136 | -define( 'LOG2', 16#a2). 137 | -define( 'LOG3', 16#a3). 138 | -define( 'LOG4', 16#a4). 139 | 140 | -define( 'CREATE', 16#f0). 141 | -define( 'CALL', 16#f1). 142 | -define( 'CALLCODE', 16#f2). 143 | -define( 'RETURN', 16#f3). 144 | -define( 'DELEGATECALL', 16#f4). 145 | 146 | -define( 'STATICCALL', 16#fa). 147 | 148 | -define( 'REVERT', 16#fd). 149 | -define( 'INVALID', 16#fe). 150 | -define( 'SUICIDE', 16#ff). 151 | 152 | -define( COMMENT(X), {comment, X}). 153 | 154 | %% Transactions are implemented as contract calls to address zero, with the 155 | %% first argument encoding the transaction type according to the below. 156 | 157 | -define(PRIM_CALLS_CONTRACT, 0). 158 | 159 | -define(PRIM_CALL_SPEND, 1). 160 | 161 | -define(PRIM_CALL_IN_ORACLE_RANGE(__TTYPE__), (((__TTYPE__) > 99) andalso ((__TTYPE__) < 200))). 162 | -define(PRIM_CALL_ORACLE_REGISTER, 100). 163 | -define(PRIM_CALL_ORACLE_QUERY, 101). 164 | -define(PRIM_CALL_ORACLE_RESPOND, 102). 165 | -define(PRIM_CALL_ORACLE_EXTEND, 103). 166 | -define(PRIM_CALL_ORACLE_GET_ANSWER, 104). 167 | -define(PRIM_CALL_ORACLE_GET_QUESTION, 105). 168 | -define(PRIM_CALL_ORACLE_QUERY_FEE, 106). 169 | -define(PRIM_CALL_ORACLE_CHECK, 110). 170 | -define(PRIM_CALL_ORACLE_CHECK_QUERY, 111). 171 | 172 | -define(PRIM_CALL_IN_AENS_RANGE(__TTYPE__), (((__TTYPE__) > 199) andalso ((__TTYPE__) < 300))). 173 | -define(PRIM_CALL_AENS_RESOLVE, 200). 174 | -define(PRIM_CALL_AENS_PRECLAIM, 201). 175 | -define(PRIM_CALL_AENS_CLAIM, 202). 176 | -define(PRIM_CALL_AENS_UPDATE, 203). 177 | -define(PRIM_CALL_AENS_TRANSFER, 204). 178 | -define(PRIM_CALL_AENS_REVOKE, 205). 179 | 180 | -define(PRIM_CALL_IN_MAP_RANGE(__TTYPE__), (((__TTYPE__) > 299) andalso ((__TTYPE__) < 400))). 181 | -define(PRIM_CALL_MAP_EMPTY, 300). 182 | -define(PRIM_CALL_MAP_GET, 301). 183 | -define(PRIM_CALL_MAP_PUT, 302). 184 | -define(PRIM_CALL_MAP_DELETE, 303). 185 | -define(PRIM_CALL_MAP_SIZE, 304). 186 | -define(PRIM_CALL_MAP_TOLIST, 305). 187 | 188 | -define(PRIM_CALL_IN_CRYPTO_RANGE(__TTYPE__), (((__TTYPE__) > 399) andalso ((__TTYPE__) < 500))). 189 | -define(PRIM_CALL_CRYPTO_VERIFY_SIG, 400). 190 | -define(PRIM_CALL_CRYPTO_SHA3, 401). 191 | -define(PRIM_CALL_CRYPTO_SHA256, 402). 192 | -define(PRIM_CALL_CRYPTO_BLAKE2B, 403). 193 | -define(PRIM_CALL_CRYPTO_SHA256_STRING, 404). 194 | -define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405). 195 | -define(PRIM_CALL_CRYPTO_VERIFY_SIG_SECP256K1, 410). 196 | -define(PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, 420). 197 | -define(PRIM_CALL_CRYPTO_ECRECOVER_SECP256K1, 421). 198 | 199 | -define(PRIM_CALL_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))). 200 | -define(PRIM_CALL_AUTH_TX_HASH, 500). 201 | 202 | -define(PRIM_CALL_IN_ADDRESS_RANGE(__TTYPE__), (((__TTYPE__) > 599) andalso ((__TTYPE__) < 700))). 203 | -define(PRIM_CALL_ADDR_IS_ORACLE, 600). 204 | -define(PRIM_CALL_ADDR_IS_CONTRACT, 601). 205 | -define(PRIM_CALL_ADDR_IS_PAYABLE, 610). 206 | -------------------------------------------------------------------------------- /include/aeb_typerep_def.hrl: -------------------------------------------------------------------------------- 1 | 2 | -define(Type(), aeb_aevm_data:type()). 3 | 4 | -define(TYPEREP_WORD_TAG, 0). 5 | -define(TYPEREP_STRING_TAG, 1). 6 | -define(TYPEREP_LIST_TAG, 2). 7 | -define(TYPEREP_TUPLE_TAG, 3). 8 | -define(TYPEREP_VARIANT_TAG, 4). 9 | -define(TYPEREP_TYPEREP_TAG, 5). 10 | -define(TYPEREP_MAP_TAG, 6). 11 | -define(TYPEREP_FUN_TAG, 7). 12 | -define(TYPEREP_CONTRACT_BYTEARRAY_TAG,8). 13 | -------------------------------------------------------------------------------- /quickcheck/aeb_fate_code_tests.erl: -------------------------------------------------------------------------------- 1 | %%% @author Thomas Arts 2 | %%% @doc Allow to run QuickCheck tests as eunit tests 3 | %%% `rebar3 as eqc eunit --cover` 4 | %%% or `rebar3 as eqc eunit --module=aeb_fate_code` 5 | %%% Note that for obtainign cover file, one needs `rebar3 as eqc cover 6 | %%% 7 | %%% 8 | %%% @end 9 | %%% Created : 13 Dec 2018 by Thomas Arts 10 | 11 | -module(aeb_fate_code_tests). 12 | 13 | -include_lib("eunit/include/eunit.hrl"). 14 | 15 | -compile([export_all, nowarn_export_all]). 16 | 17 | -define(EQC_EUNIT(Module, PropName, Ms), 18 | { atom_to_list(PropName), 19 | {timeout, (Ms * 10) div 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}). 20 | 21 | quickcheck_test_() -> 22 | {setup, fun() -> eqc:start() end, 23 | [ ?EQC_EUNIT(aefate_code_eqc, prop_opcodes, 200), 24 | ?EQC_EUNIT(aefate_code_eqc, prop_serializes, 3000), 25 | ?EQC_EUNIT(aefate_code_eqc, prop_fail_serializes, 3000), 26 | ?EQC_EUNIT(aefate_code_eqc, prop_fuzz, 3000) 27 | ]}. 28 | -------------------------------------------------------------------------------- /quickcheck/aeb_fate_data_tests.erl: -------------------------------------------------------------------------------- 1 | %%% @author Thomas Arts 2 | %%% @doc Allow to run QuickCheck tests as eunit tests 3 | %%% `rebar3 as eqc eunit --cover` 4 | %%% or `rebar3 as eqc eunit --module=aeb_fate_data` 5 | %%% Note that for obtainign cover file, one needs `rebar3 as eqc cover 6 | %%% 7 | %%% 8 | %%% @end 9 | %%% Created : 13 Dec 2018 by Thomas Arts 10 | 11 | -module(aeb_fate_data_tests). 12 | 13 | -include_lib("eunit/include/eunit.hrl"). 14 | 15 | -compile([export_all, nowarn_export_all]). 16 | 17 | -define(EQC_EUNIT(Module, PropName, Ms), 18 | { atom_to_list(PropName), 19 | {timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}). 20 | 21 | quickcheck_test_() -> 22 | {setup, fun() -> eqc:start() end, 23 | [ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500), 24 | ?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000), 25 | ?EQC_EUNIT(aefate_eqc, prop_order, 2000), 26 | ?EQC_EUNIT(aefate_eqc, prop_fuzz, 2000) 27 | ]}. 28 | -------------------------------------------------------------------------------- /quickcheck/aeb_fate_encoding_tests.erl: -------------------------------------------------------------------------------- 1 | %%% @author Thomas Arts 2 | %%% @doc Allow to run QuickCheck tests as eunit tests 3 | %%% `rebar3 as eqc eunit --cover` 4 | %%% or `rebar3 as eqc eunit --module=aeb_fate_encoding` 5 | %%% Note that for obtaining cover file, one needs `rebar3 as eqc cover 6 | %%% 7 | %%% 8 | %%% @end 9 | %%% Created : 13 Dec 2018 by Thomas Arts 10 | 11 | -module(aeb_fate_encoding_tests). 12 | 13 | -include_lib("eunit/include/eunit.hrl"). 14 | 15 | -compile([export_all, nowarn_export_all]). 16 | 17 | -define(EQC_EUNIT(Module, PropName, Ms), 18 | { atom_to_list(PropName), 19 | {timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}). 20 | 21 | quickcheck_test_() -> 22 | {setup, fun() -> eqc:start() end, 23 | [ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000), 24 | ?EQC_EUNIT(aefate_eqc, prop_serializes, 1000), 25 | ?EQC_EUNIT(aefate_eqc, prop_no_maps_in_keys, 1000), 26 | ?EQC_EUNIT(aefate_eqc, prop_idempotent, 1000) 27 | ]}. 28 | -------------------------------------------------------------------------------- /quickcheck/aefate_code_eqc.erl: -------------------------------------------------------------------------------- 1 | %%% @author Thomas Arts 2 | %%% @doc Use `rebar3 as eqc shell` to run properties in the shell 3 | %%% 4 | %%% We want to be sure that we can deserialize all FATE assembler that is accepted on chain. 5 | %%% 6 | %%% We test something slightly weaker here, 7 | %%% viz. All FATE assembler we serialize, we can deserialize 8 | %%% 9 | %%% Negative testing modelled: 10 | %%% Failure 1: function names differ from 4 bytes 11 | %%% Failure 2: pointer to empty code block 12 | %%% Failure 3: end_BB operation as not ending block or not at end of block 13 | %%% - empty code blocks 14 | %%% - blocks that are not of the form (not end_bb)* end_bb. 15 | %%% 16 | %%% @end 17 | %%% Created : 13 Dec 2018 by Thomas Arts 18 | 19 | -module(aefate_code_eqc). 20 | 21 | -include_lib("eqc/include/eqc.hrl"). 22 | 23 | -compile([export_all, nowarn_export_all]). 24 | %%-define(Failure(Failures, Number), case lists:member(Number, Failures) of true -> 1; false -> 0 end) 25 | 26 | 27 | prop_serializes() -> 28 | in_parallel( 29 | ?FORALL(FateCode, fate_code(0), 30 | begin 31 | {T0, Binary} = timer:tc(fun() -> aeb_fate_code:serialize(FateCode) end), 32 | ?WHENFAIL(eqc:format("serialized:\n ~120p~n", [Binary]), 33 | begin 34 | {T1, Decoded} = timer:tc(fun() -> aeb_fate_code:deserialize(Binary) end), 35 | measure(binary_size, size(Binary), 36 | measure(serialize, T0 / 1000, 37 | measure(deserialize, T1 / 1000, 38 | conjunction([{equal, equals(Decoded, FateCode)}, 39 | {serialize_time, T0 / 1000 < 500}, 40 | {deserialize_time, T1 / 1000 < 500}])))) 41 | end) 42 | end)). 43 | 44 | prop_fail_serializes() -> 45 | conjunction([{Failure, eqc:counterexample( 46 | ?FORALL(FateCode, fate_code(Failure), 47 | ?FORALL(Binary, catch aeb_fate_code:serialize(FateCode), 48 | is_binary(Binary)))) 49 | =/= true} || Failure <- [1, 2, 3, 4, 5] ]). 50 | 51 | prop_fuzz() -> 52 | in_parallel( 53 | ?FORALL(Binary, ?LET(FateCode, fate_code(0), aeb_fate_code:serialize(FateCode)), 54 | ?FORALL(FuzzedBin, fuzz(Binary), 55 | try aeb_fate_code:deserialize(FuzzedBin) of 56 | Code -> 57 | ?WHENFAIL(eqc:format("Code:\n ~p\n", [Code]), 58 | begin 59 | Bin1 = aeb_fate_code:serialize(Code), 60 | Code1 = aeb_fate_code:deserialize(Bin1), 61 | ?WHENFAIL(eqc:format("Reserialized\n ~120p\n", [Bin1]), 62 | equals(Code, Code1)) 63 | end) 64 | catch _:_ -> true 65 | end))). 66 | 67 | prop_opcodes() -> 68 | ?FORALL(Opcode, choose(0, 16#ff), 69 | try M = aeb_fate_opcodes:mnemonic(Opcode), 70 | ?WHENFAIL(eqc:format("opcode ~p -> ~p", [Opcode, M]), 71 | conjunction([{valid, lists:member(Opcode, valid_opcodes())}, 72 | {eq, equals(aeb_fate_opcodes:m_to_op(M), Opcode)}])) 73 | catch 74 | _:_ -> 75 | not lists:member(Opcode, valid_opcodes()) 76 | end). 77 | 78 | 79 | valid_opcodes() -> 80 | [ Op || #{opcode := Op} <- aeb_fate_generate_ops:get_ops() ]. 81 | 82 | 83 | fate_code(Failure) -> 84 | ?SIZED(Size, 85 | ?LET({FMap, SMap, AMap}, 86 | {non_empty(map(if Failure == 1 -> binary(1); 87 | true -> binary(4) end, 88 | {sublist(lists:sort([private, payable])), %% deserialize sorts them 89 | {list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})), 90 | small_map(small_fate_data_key(5), small_fate_data(4)), 91 | small_map(small_fate_data_key(5), small_fate_data(4))}, 92 | aeb_fate_code:update_annotations( 93 | aeb_fate_code:update_symbols( 94 | aeb_fate_code:update_functions( 95 | aeb_fate_code:new(), FMap), SMap), AMap))). 96 | 97 | short_list(Max, Gen) -> 98 | ?LET(N, choose(0, Max), eqc_gen:list(N, Gen)). 99 | 100 | small_map(KeyGen, ValGen) -> 101 | ?LET(KeyVals, short_list(6, {KeyGen, ValGen}), 102 | return(maps:from_list(KeyVals))). 103 | 104 | bbs_code(Failure) -> 105 | frequency([{if Failure == 2 -> 5; true -> 0 end, #{0 => []}}, 106 | {10, ?LET(BBs, short_list(6, bb_code(Failure)), 107 | maps:from_list( 108 | lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]). 109 | 110 | bb_code(Failure) -> 111 | EndBB = [ Op || Op <- valid_opcodes(), aeb_fate_opcodes:end_bb(Op) ], 112 | NonEndBB = valid_opcodes() -- EndBB, 113 | frequency( 114 | [{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(short_list(6, elements(NonEndBB))), bblock(Failure, Ops))}, 115 | {if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {short_list(6, elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))}, 116 | {10, ?LET({Ops, Op}, {short_list(6, elements(NonEndBB)), elements(EndBB)}, 117 | bblock(Failure, Ops ++ [Op]))}]). 118 | 119 | bblock(Failure, Ops) -> 120 | [ begin 121 | Mnemonic = aeb_fate_opcodes:mnemonic(Op), 122 | Arity = aeb_fate_opcodes:args(Op), 123 | case Arity of 124 | 0 -> Mnemonic; 125 | _ -> list_to_tuple([Mnemonic | 126 | [ frequency([{if Failure == 5 -> 5; true -> 0 end, {stack, nat()}}, 127 | {5, {stack, 0}}, 128 | {5, {arg, nat()}}, 129 | {5, {var, nat()}}, 130 | {5, {immediate, small_fate_data(4)}}]) || 131 | _ <- lists:seq(1, Arity) ]]) 132 | end 133 | end || Op <- Ops ]. 134 | 135 | fuzz(Binary) -> 136 | ?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0, 255)}, 137 | begin 138 | M = N * 8, 139 | <> = Binary, 140 | <> 141 | end). 142 | 143 | prop_small() -> 144 | ?FORALL(Value, small_fate_data(4), 145 | begin 146 | Bin = aeb_fate_encoding:serialize(Value), 147 | Size = byte_size(Bin), 148 | measure(size, Size, 149 | ?WHENFAIL(eqc:format("Size: ~p\n", [Size]), 150 | Size < 1000)) 151 | end). 152 | 153 | prop_small_type() -> 154 | ?FORALL(Type, ?SIZED(Size, aefate_type_eqc:fate_type(Size div 3)), 155 | begin 156 | Bin = iolist_to_binary(aeb_fate_encoding:serialize_type(Type)), 157 | Size = byte_size(Bin), 158 | measure(size, Size, 159 | ?WHENFAIL(eqc:format("Size: ~p\n", [Size]), 160 | Size < 1000)) 161 | end). 162 | 163 | small_fate_data(N) -> 164 | ?SIZED(Size, resize(Size div N, aefate_eqc:fate_data())). 165 | 166 | small_fate_data_key(N) -> 167 | ?SIZED(Size, ?LET(Data, aefate_eqc:fate_data(Size div N, []), eqc_symbolic:eval(Data))). 168 | -------------------------------------------------------------------------------- /quickcheck/aefate_eqc.erl: -------------------------------------------------------------------------------- 1 | %%% @author Thomas Arts 2 | %%% @doc Use `rebar3 as eqc shell` to run properties in the shell 3 | %%% 4 | %%% We need to be able to generate data that serializes with ?LONG_LIST, ?LONG_TUPLE etc. 5 | %%% In other words make some rather broad terms as well as some deep terms 6 | %%% 7 | %%% @end 8 | %%% Created : 13 Dec 2018 by Thomas Arts 9 | 10 | -module(aefate_eqc). 11 | 12 | -include_lib("eqc/include/eqc.hrl"). 13 | -include("../include/aeb_fate_data.hrl"). 14 | 15 | -compile([export_all, nowarn_export_all]). 16 | 17 | prop_roundtrip() -> 18 | ?FORALL(FateData, fate_data(), 19 | measure(bytes, size(term_to_binary(FateData)), 20 | begin 21 | Serialized = aeb_fate_encoding:serialize(FateData), 22 | ?WHENFAIL(eqc:format("Serialized ~p to ~p~n", [FateData, Serialized]), 23 | equals(aeb_fate_encoding:deserialize(Serialized), FateData)) 24 | end)). 25 | 26 | prop_format_scan() -> 27 | ?FORALL(FateData, fate_data([variant, map]), 28 | ?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]), 29 | begin 30 | String = aeb_fate_data:format(FateData), 31 | {ok, _Scanned, _} = aeb_fate_asm_scan:scan(unicode:characters_to_list(String)), 32 | true 33 | end)). 34 | 35 | prop_serializes() -> 36 | ?FORALL({Data, Garbage}, {fate_data(), binary()}, 37 | ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]), 38 | begin 39 | Binary = <<(aeb_fate_encoding:serialize(Data))/binary, Garbage/binary>>, 40 | {FateData, Rest} = aeb_fate_encoding:deserialize_one(Binary), 41 | measure(binary_size, size(Binary), 42 | conjunction([{equal, equals(Data, FateData)}, 43 | {rest, equals(Garbage, Rest)}, 44 | {size, size(Binary) < 500000}])) 45 | end)). 46 | 47 | prop_no_maps_in_keys() -> 48 | ?FORALL(FateData, fate_bad_map(), %% may contain a map in its keys 49 | begin 50 | HasMapInKeys = lists:any(fun(K) -> has_map(K) end, maps:keys(FateData)), 51 | try aeb_fate_encoding:serialize(FateData), 52 | ?WHENFAIL(eqc:format("Should not serialize, contains a map in key\n", []), 53 | not HasMapInKeys) 54 | catch error:Reason -> 55 | ?WHENFAIL(eqc:format("(~p) Should serialize\n", [Reason]), HasMapInKeys) 56 | end 57 | end). 58 | 59 | prop_fuzz() -> 60 | in_parallel( 61 | ?FORALL(Binary, ?LET(FateData, ?SIZED(Size, resize(Size div 4, fate_data())), aeb_fate_encoding:serialize(FateData)), 62 | ?FORALL(InjectedBin, injection(Binary), 63 | try Org = aeb_fate_encoding:deserialize(InjectedBin), 64 | NewBin = aeb_fate_encoding:serialize(Org), 65 | NewOrg = aeb_fate_encoding:deserialize(NewBin), 66 | measure(success, 1, 67 | ?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]), 68 | equals(NewBin, InjectedBin))) 69 | catch _:_ -> 70 | true 71 | end))). 72 | 73 | 74 | prop_order() -> 75 | ?FORALL(Items, vector(3, fate_data([variant, map])), 76 | begin 77 | %% Use lt to take minimum 78 | Min = lt_min(Items), 79 | Max = lt_max(Items), 80 | conjunction([ {minimum, is_empty([ {Min, '>', I} || I<-Items, aeb_fate_data:lt(I, Min)])}, 81 | {maximum, is_empty([ {Max, '<', I} || I<-Items, aeb_fate_data:lt(Max, I)])}, 82 | {asym, aeb_fate_data:lt(Min, Max) orelse Min == Max}]) 83 | end). 84 | 85 | lt_min([X, Y | Rest]) -> 86 | case aeb_fate_data:lt(X, Y) of 87 | true -> lt_min([X | Rest]); 88 | false -> lt_min([Y| Rest]) 89 | end; 90 | lt_min([X]) -> X. 91 | 92 | lt_max([X, Y | Rest]) -> 93 | case aeb_fate_data:lt(X, Y) of 94 | true -> lt_max([Y | Rest]); 95 | false -> lt_max([X| Rest]) 96 | end; 97 | lt_max([X]) -> X. 98 | 99 | prop_idempotent() -> 100 | ?FORALL(Items, list({fate_data_key(), fate_data()}), 101 | equals(aeb_fate_encoding:sort(Items), 102 | aeb_fate_encoding:sort(aeb_fate_encoding:sort(Items)))). 103 | 104 | 105 | 106 | fate_data(Kind) -> 107 | ?SIZED(Size, ?LET(Data, fate_data(Size, Kind), eqc_symbolic:eval(Data))). 108 | 109 | fate_data() -> 110 | fate_data([map, variant, store_map]). 111 | 112 | %% keys may contain variants but no maps 113 | fate_data_key() -> 114 | fate_data([variant]). 115 | 116 | fate_data(0, Options) -> 117 | ?LAZY( 118 | frequency( 119 | [{50, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])}, 120 | {10, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(), 121 | fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}] ++ 122 | [{1, fate_store_map()} || lists:member(store_map, Options)])); 123 | fate_data(Size, Options) -> 124 | ?LAZY( 125 | oneof([fate_data(0, Options), 126 | fate_list(Size, Options), 127 | fate_tuple(Size, Options)] ++ 128 | [fate_variant(Size, Options) 129 | || lists:member(variant, Options)] ++ 130 | [fate_map(Size, Options) 131 | || lists:member(map, Options)])). 132 | 133 | 134 | fate_integer() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_integer(X))). 135 | fate_bits() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_bits(X))). 136 | fate_boolean() -> ?LET(X, elements([true, false]), return(aeb_fate_data:make_boolean(X))). 137 | fate_nil() -> aeb_fate_data:make_list([]). 138 | fate_unit() -> aeb_fate_data:make_unit(). 139 | fate_string() -> ?LET(X, frequency([{10, non_quote_string()}, {2, list(non_quote_string())}, 140 | {1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}]), 141 | return(aeb_fate_data:make_string(X))). 142 | fate_address() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_address(X))). 143 | fate_bytes() -> ?LET(X, non_empty(binary()), return(aeb_fate_data:make_bytes(X))). 144 | fate_contract() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_contract(X))). 145 | fate_oracle() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle(X))). 146 | fate_oracle_q() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle_query(X))). 147 | fate_channel() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_channel(X))). 148 | 149 | fate_values(Size, N, Options) -> 150 | eqc_gen:list(N, fate_data(Size div max(1, N), Options)). 151 | 152 | %% May shrink to fate_unit 153 | fate_tuple(Size, Options) -> 154 | ?LET(N, choose(0, 6), 155 | ?LETSHRINK(Elements, fate_values(Size, N, Options), 156 | return(aeb_fate_data:make_tuple(list_to_tuple(Elements))))). 157 | 158 | fate_variant(Size, Options) -> 159 | ?LET({L1, L2, {tuple, Args}}, {list(choose(0, 255)), list(choose(0,255)), fate_tuple(Size, Options)}, 160 | return(aeb_fate_data:make_variant(L1 ++ [tuple_size(Args)] ++ L2, 161 | length(L1), Args))). 162 | 163 | fate_list(Size, Options) -> 164 | ?LET(N, frequency([{20, choose(0, 6)}, {1, choose(64 - 3, 64 + 3)}]), 165 | ?LETSHRINK(Vs, fate_values(Size, N, Options), 166 | return(aeb_fate_data:make_list(Vs)))). 167 | 168 | fate_map(Size, Options) -> 169 | ?LET(N, choose(0, 6), 170 | ?LETSHRINK(Values, fate_values(Size, N, Options), 171 | ?LET(Keys, vector(length(Values), fate_data(Size div max(1, N * 2), Options -- [map, store_map])), 172 | return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))). 173 | 174 | fate_store_map() -> 175 | %% only #{} is allowed as cache in serialization 176 | ?LET(X, oneof([int(), largeint()]), 177 | return(aeb_fate_data:make_store_map(abs(X)))). 178 | 179 | fate_bad_map() -> 180 | ?LET(N, choose(0, 6), 181 | ?LET(Values, vector(N, ?SIZED(Size, resize(Size div 8, fate_data()))), 182 | ?LET(Keys, vector(N, ?SIZED(Size, resize(Size div 4, fate_data()))), 183 | return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))). 184 | 185 | non_quote_string() -> 186 | ?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []). 187 | 188 | char() -> 189 | choose(1, 255). 190 | 191 | injection(Binary) -> 192 | ?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0,255)}, 193 | begin 194 | M = N * 8, 195 | <> = Binary, 196 | <> 197 | end). 198 | 199 | is_empty(L) -> 200 | ?WHENFAIL(eqc:format("~p\n", [L]), L == []). 201 | 202 | has_map(L) when is_list(L) -> 203 | lists:any(fun(V) -> has_map(V) end, L); 204 | has_map(T) when is_tuple(T) -> 205 | has_map(tuple_to_list(T)); 206 | has_map(M) when is_map(M) -> 207 | true; 208 | has_map(?FATE_STORE_MAP(_, _)) -> 209 | true; 210 | has_map(_) -> 211 | false. 212 | -------------------------------------------------------------------------------- /quickcheck/aefate_type_eqc.erl: -------------------------------------------------------------------------------- 1 | %%% @author Thomas Arts 2 | %%% @doc Use `rebar3 as eqc shell` to run properties in the shell 3 | %%% Properties for testing Fate type representations 4 | %%% 5 | %%% @end 6 | %%% Created : 13 Dec 2018 by Thomas Arts 7 | 8 | -module(aefate_type_eqc). 9 | 10 | -include_lib("eqc/include/eqc.hrl"). 11 | 12 | -compile([export_all, nowarn_export_all]). 13 | 14 | kind(X) when is_atom(X) -> X; 15 | kind(T) when is_tuple(T) -> element(1, T). 16 | 17 | prop_roundtrip() -> 18 | ?FORALL(FateType, fate_type(), 19 | collect(kind(FateType), 20 | begin 21 | Serialized = aeb_fate_encoding:serialize_type(FateType), 22 | BinSerialized = list_to_binary(Serialized), 23 | ?WHENFAIL(eqc:format("Serialized ~p to ~p (~p)~n", [FateType, Serialized, BinSerialized]), 24 | begin 25 | {Type, <<>>} = aeb_fate_encoding:deserialize_type(BinSerialized), 26 | equals(Type, FateType) 27 | end) 28 | end)). 29 | 30 | 31 | fate_type() -> 32 | ?SIZED(Size, fate_type(Size)). 33 | 34 | fate_type(0) -> 35 | oneof([integer, 36 | boolean, 37 | address, 38 | {bytes, nat()}, 39 | contract, 40 | oracle, 41 | channel, 42 | bits, 43 | string]); 44 | fate_type(Size) -> 45 | ?LAZY( 46 | oneof([fate_type(0), 47 | {list, fate_type(Size div 2)}, 48 | ?LETSHRINK(Ts, fate_types(Size), {tuple, Ts}), 49 | ?LETSHRINK(Ts, fate_types(Size), {variant, Ts}), 50 | ?LETSHRINK([T1, T2], vector(2, fate_type(Size div 2)), 51 | {map, T1, T2})])). 52 | 53 | fate_types(Size) -> 54 | ?LET(N, choose(0, 6), 55 | eqc_gen:list(N, fate_type(Size div max(2, N)))). 56 | 57 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang; indent-tabs-mode: nil -*- 2 | 3 | {minimum_otp_vsn, "20.1"}. 4 | 5 | {erl_opts, [debug_info]}. 6 | 7 | {deps, [ {eblake2, "1.0.0"} 8 | , {aeserialization, {git, "https://github.com/aeternity/aeserialization.git", 9 | {tag, "v1.1.1"}}} 10 | , {getopt, "1.0.1"} 11 | ]}. 12 | 13 | {escript_incl_apps, [aebytecode, eblake2, aeserialization, getopt]}. 14 | {escript_main_app, aebytecode}. 15 | {escript_name, aefateasm}. 16 | {escript_emu_args, "%%!"}. 17 | 18 | {pre_hooks, 19 | [{"(linux|darwin|solaris|win32)", compile, "make sources"}, 20 | {"(freebsd)", compile, "gmake sources"}]}. 21 | 22 | {provider_hooks, [{post, [{compile, escriptize}]}]}. 23 | 24 | 25 | {dialyzer, [ 26 | {warnings, [unknown]}, 27 | {plt_apps, all_deps}, 28 | {base_plt_apps, [erts, kernel, stdlib, crypto, getopt]} 29 | ]}. 30 | 31 | 32 | {relx, [{release, {aebytecode, "3.4.0"}, 33 | [aebytecode, eblake2, getopt]}, 34 | 35 | {dev_mode, true}, 36 | {include_erts, false}, 37 | 38 | {extended_start_script, true}]}. 39 | 40 | {profiles, [{binary, [ 41 | {deps, [ {eblake2, "1.0.0"} 42 | , {aeserialization, {git, "https://github.com/aeternity/aeserialization.git", 43 | {tag, "v1.0.0"}}} 44 | , {getopt, "1.0.1"} 45 | ]}, 46 | 47 | {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", 48 | escriptize, 49 | "cp \"$REBAR_BUILD_DIR/bin/aefateasm\" ./aefateasm"}, 50 | {"win32", 51 | escriptize, 52 | "robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* " 53 | "/njs /njh /nfl /ndl & exit /b 0"} % silence things 54 | ]} 55 | ]}, 56 | {eqc, [{erl_opts, [{parse_transform, eqc_cover}, {d, 'EQC'}]}, 57 | {extra_src_dirs, ["quickcheck"]} %% May not be called eqc! 58 | ]} 59 | ]}. 60 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | {"1.2.0", 2 | [{<<"aeserialization">>, 3 | {git,"https://github.com/aeternity/aeserialization.git", 4 | {ref,"b26e6d105424748ba1c27917267b7cff07f37802"}}, 5 | 0}, 6 | {<<"base58">>, 7 | {git,"https://github.com/aeternity/erl-base58.git", 8 | {ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}}, 9 | 1}, 10 | {<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0}, 11 | {<<"enacl">>, 12 | {git,"https://github.com/aeternity/enacl.git", 13 | {ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}}, 14 | 1}, 15 | {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}. 16 | [ 17 | {pkg_hash,[ 18 | {<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>}, 19 | {<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}, 20 | {pkg_hash_ext,[ 21 | {<<"eblake2">>, <<"3C4D300A91845B25D501929A26AC2E6F7157480846FAB2347A4C11AE52E08A99">>}, 22 | {<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>}]} 23 | ]. 24 | -------------------------------------------------------------------------------- /rebar3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aeternity/aebytecode/6bd6f82c70d800950ea1a2c70c364a4181ff5291/rebar3 -------------------------------------------------------------------------------- /src/aeb_aevm_abi.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2017, Aeternity Anstalt 3 | %%% @doc 4 | %%% Encode and decode data and function calls according to 5 | %%% Sophia-AEVM-ABI 6 | %%% @end 7 | %%% Created : 25 Jan 2018 8 | %%% 9 | %%%------------------------------------------------------------------- 10 | -module(aeb_aevm_abi). 11 | -define(HASH_SIZE, 32). 12 | 13 | -export([ create_calldata/4 14 | , check_calldata/3 15 | , function_type_info/4 16 | , function_type_hash/3 17 | , arg_typerep_from_function/2 18 | , type_hash_from_function_name/2 19 | , typereps_from_type_hash/2 20 | , function_name_from_type_hash/2 21 | , get_function_hash_from_calldata/1 22 | , is_payable/2 23 | , abi_version/0 24 | ]). 25 | 26 | -type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8. 27 | -type function_name() :: binary(). %% String 28 | -type typerep() :: aeb_aevm_data:type(). 29 | -type function_type_info() :: { FunctionHash :: hash() 30 | , FunctionName :: function_name() 31 | , Payable :: boolean() 32 | , ArgType :: binary() %% binary typerep 33 | , OutType :: binary() %% binary typerep 34 | }. 35 | -type type_info() :: [function_type_info()]. 36 | 37 | %%%=================================================================== 38 | %%% API 39 | %%%=================================================================== 40 | 41 | %% Shall match ?ABI_AEVM_SOPHIA_1 42 | -spec abi_version() -> integer(). 43 | abi_version() -> 44 | 1. 45 | 46 | %%%=================================================================== 47 | %%% Handle calldata 48 | 49 | create_calldata(FunName, Args, ArgTypes0, RetType) -> 50 | ArgTypes = {tuple, ArgTypes0}, 51 | <> = 52 | function_type_hash(list_to_binary(FunName), ArgTypes, RetType), 53 | Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}), 54 | {ok, Data}. 55 | 56 | -spec check_calldata(binary(), type_info(), boolean()) -> 57 | {'ok', typerep(), typerep()} | {'error', atom()}. 58 | check_calldata(CallData, TypeInfo, CheckPayable) -> 59 | %% The first element of the CallData should be the function name 60 | case get_function_hash_from_calldata(CallData) of 61 | {ok, Hash} -> 62 | check_calldata(Hash, CallData, TypeInfo, CheckPayable); 63 | {error, _What} -> 64 | {error, bad_call_data} 65 | end. 66 | 67 | check_calldata(Hash, CallData, TypeInfo, true) -> 68 | case is_payable(Hash, TypeInfo) of 69 | {ok, true} -> check_calldata(Hash, CallData, TypeInfo, false); 70 | {ok, false} -> {error, function_is_not_payable}; 71 | Err = {error, _} -> Err 72 | end; 73 | check_calldata(Hash, CallData, TypeInfo, false) -> 74 | case typereps_from_type_hash(Hash, TypeInfo) of 75 | {ok, ArgType, OutType} -> 76 | try aeb_heap:from_binary({tuple, [word, ArgType]}, CallData) of 77 | {ok, _Something} -> 78 | {ok, {tuple, [word, ArgType]}, OutType}; 79 | {error, _} -> 80 | {error, bad_call_data} 81 | catch 82 | _T:_E -> 83 | {error, bad_call_data} 84 | end; 85 | {error, _} -> 86 | {error, unknown_function} 87 | end. 88 | 89 | 90 | -spec get_function_hash_from_calldata(CallData::binary()) -> 91 | {ok, binary()} | {error, term()}. 92 | get_function_hash_from_calldata(CallData) -> 93 | case aeb_heap:from_binary({tuple, [word]}, CallData) of 94 | {ok, {HashInt}} -> {ok, <>}; 95 | {error, _} = Error -> Error 96 | end. 97 | 98 | %%%=================================================================== 99 | %%% Handle type info from contract meta data 100 | 101 | -spec function_type_info(function_name(), boolean(), [typerep()], typerep()) -> 102 | function_type_info(). 103 | function_type_info(Name, Payable, ArgTypes, OutType) -> 104 | ArgType = {tuple, ArgTypes}, 105 | { function_type_hash(Name, ArgType, OutType) 106 | , Name 107 | , Payable 108 | , aeb_heap:to_binary(ArgType) 109 | , aeb_heap:to_binary(OutType) 110 | }. 111 | 112 | -spec function_type_hash(function_name(), typerep(), typerep()) -> hash(). 113 | function_type_hash(Name, ArgType, OutType) when is_binary(Name) -> 114 | Bin = iolist_to_binary([ Name 115 | , aeb_heap:to_binary(ArgType) 116 | , aeb_heap:to_binary(OutType) 117 | ]), 118 | %% Calculate a 256 bit digest BLAKE2b hash value of a binary 119 | {ok, Hash} = eblake2:blake2b(?HASH_SIZE, Bin), 120 | Hash. 121 | 122 | -spec arg_typerep_from_function(function_name(), type_info()) -> 123 | {'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}. 124 | arg_typerep_from_function(Function, TypeInfo) -> 125 | case lists:keyfind(Function, 2, TypeInfo) of 126 | {_TypeHash, Function, ArgTypeBin, _OutTypeBin} -> 127 | arg_typerep_from_type_binary(ArgTypeBin); 128 | {_TypeHash, Function, _Payable, ArgTypeBin, _OutTypeBin} -> 129 | arg_typerep_from_type_binary(ArgTypeBin); 130 | false -> 131 | {error, unknown_function} 132 | end. 133 | 134 | arg_typerep_from_type_binary(ArgTBin) -> 135 | case aeb_heap:from_binary(typerep, ArgTBin) of 136 | {ok, ArgT} -> {ok, ArgT}; 137 | {error,_} -> {error, bad_type_data} 138 | end. 139 | 140 | -spec typereps_from_type_hash(hash(), type_info()) -> 141 | {'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}. 142 | typereps_from_type_hash(TypeHash, TypeInfo) -> 143 | case lists:keyfind(TypeHash, 1, TypeInfo) of 144 | {TypeHash, _Function, ArgTypeBin, OutTypeBin} -> 145 | typereps_from_type_binaries(ArgTypeBin, OutTypeBin); 146 | {TypeHash, _Function, _Payable, ArgTypeBin, OutTypeBin} -> 147 | typereps_from_type_binaries(ArgTypeBin, OutTypeBin); 148 | false -> 149 | {error, unknown_function} 150 | end. 151 | 152 | typereps_from_type_binaries(ArgTBin, OutTBin) -> 153 | case {aeb_heap:from_binary(typerep, ArgTBin), aeb_heap:from_binary(typerep, OutTBin)} of 154 | {{ok, ArgT}, {ok, OutT}} -> {ok, ArgT, OutT}; 155 | {_, _} -> {error, bad_type_data} 156 | end. 157 | 158 | -spec function_name_from_type_hash(hash(), type_info()) -> 159 | {'ok', function_name()} 160 | | {'error', 'unknown_function'}. 161 | function_name_from_type_hash(TypeHash, TypeInfo) -> 162 | case lists:keyfind(TypeHash, 1, TypeInfo) of 163 | {TypeHash, Function, _ArgTypeBin, _OutTypeBin} -> 164 | {ok, Function}; 165 | {TypeHash, Function, _Payable, _ArgTypeBin, _OutTypeBin} -> 166 | {ok, Function}; 167 | false -> 168 | {error, unknown_function} 169 | end. 170 | 171 | -spec type_hash_from_function_name(function_name(), type_info()) -> 172 | {'ok', hash()} 173 | | {'error', 'unknown_function'}. 174 | type_hash_from_function_name(Name, TypeInfo) -> 175 | case lists:keyfind(Name, 2, TypeInfo) of 176 | {TypeHash, Name, _ArgTypeBin, _OutTypeBin} -> 177 | {ok, TypeHash}; 178 | {TypeHash, Name, _Payable, _ArgTypeBin, _OutTypeBin} -> 179 | {ok, TypeHash}; 180 | false -> 181 | {error, unknown_function} 182 | end. 183 | 184 | -spec is_payable(hash(), type_info()) -> {ok, boolean()} | {error, 'unknown_function'}. 185 | is_payable(TypeHash, TypeInfo) -> 186 | case lists:keyfind(TypeHash, 1, TypeInfo) of 187 | {TypeHash, _Function, _ArgTypeBin, _OutTypeBin} -> 188 | {ok, true}; 189 | {TypeHash, _Function, Payable, _ArgTypeBin, _OutTypeBin} -> 190 | {ok, Payable}; 191 | false -> 192 | {error, unknown_function} 193 | end. 194 | 195 | -------------------------------------------------------------------------------- /src/aeb_aevm_data.erl: -------------------------------------------------------------------------------- 1 | -module(aeb_aevm_data). 2 | 3 | -export_type([data/0, 4 | type/0, 5 | heap/0]). 6 | 7 | -type type() :: word | signed_word | string | typerep | function 8 | | {list, type()} 9 | | {option, type()} 10 | | {tuple, [type()]} 11 | | {variant, [[type()]]}. 12 | 13 | 14 | -type data() :: none 15 | | {some, data()} 16 | | {option, data()} 17 | | word 18 | | string 19 | | {list, data()} 20 | | {tuple, [data()]} 21 | | {variant, integer(), [data()]} 22 | | integer() 23 | | binary() 24 | | [data()] 25 | | {} 26 | | {data()} 27 | | {data(), data()}. 28 | 29 | -type heap() :: binary(). 30 | 31 | -------------------------------------------------------------------------------- /src/aeb_asm.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2017, Aeternity Anstalt 3 | %%% @doc Assembler for aevm machine code. 4 | %%% 5 | %%% Assembler code can be read from a file. 6 | %%% The assembler has the following format 7 | %%% Comments start with 2 semicolons and runs till end of line 8 | %%% ;; This is a comment 9 | %%% Opcode mnemonics start with an upper case letter. 10 | %%% DUP1 11 | %%% Identifiers start with a lower case letter 12 | %%% an_identifier 13 | %%% All identifiers has to be decleard with a label. 14 | %%% A label is an identifier that ends with a colon. 15 | %%% The label is the byte code address of the next instruction. 16 | %%% a_label: 17 | %%% Immediates can be of four types: 18 | %%% 1. integers 19 | %%% 42 20 | %%% 2. hexadecimal integers starting with 0x 21 | %%% 0x0deadbeef0 22 | %%% 3. 256-bit hash strings starting with # 23 | %%% followed by up to 64 hex chars 24 | %%% #00000deadbeef 25 | %%% 4. labels as descibed above. 26 | %%% 27 | %%% @end 28 | %%% Created : 21 Dec 2017 29 | %%%------------------------------------------------------------------- 30 | 31 | -module(aeb_asm). 32 | 33 | -export([ file/2 34 | , pp/1 35 | , to_hexstring/1 36 | ]). 37 | 38 | -include_lib("aebytecode/include/aeb_opcodes.hrl"). 39 | 40 | 41 | pp(Asm) -> 42 | Listing = format(Asm), 43 | io:format("~s~n", [Listing]). 44 | 45 | format(Asm) -> format(Asm, 0). 46 | 47 | format([{comment, Comment} | Rest], Address) -> 48 | ";; " ++ Comment ++ "\n" ++ format(Rest, Address); 49 | format([Mnemonic | Rest], Address) -> 50 | Op = aeb_opcodes:m_to_op(Mnemonic), 51 | case (Op >= ?PUSH1) andalso (Op =< ?PUSH32) of 52 | true -> 53 | Arity = aeb_opcodes:op_size(Op) - 1, 54 | {Args, Code} = get_args(Arity, Rest), 55 | " " ++ atom_to_list(Mnemonic) 56 | ++ " " ++ Args ++ "\n" 57 | ++ format(Code, Address + Arity + 1); 58 | false -> 59 | " " ++ atom_to_list(Mnemonic) ++ "\n" 60 | ++ format(Rest, Address + 1) 61 | end; 62 | format([],_) -> []. 63 | 64 | %% TODO: Are args encoded as one list element or as a number of bytes... 65 | get_args(1, [Arg|Code]) -> 66 | {integer_to_list(Arg), Code}; 67 | get_args(N, [Arg|Code]) -> 68 | {Args, Rest} = get_args(N-1, Code), 69 | {integer_to_list(Arg) ++ ", " ++ Args, Rest}. 70 | 71 | 72 | 73 | file(Filename, Options) -> 74 | {ok, File} = file:read_file(Filename), 75 | {ok, Tokens, _} = aeb_asm_scan:scan(binary_to_list(File)), 76 | 77 | case proplists:lookup(pp_tokens, Options) of 78 | {pp_tokens, true} -> 79 | io:format("Tokens ~p~n",[Tokens]); 80 | none -> 81 | ok 82 | end, 83 | 84 | ByteList = to_bytecode(Tokens, 0, #{}, [], Options), 85 | 86 | case proplists:lookup(pp_hex_string, Options) of 87 | {pp_hex_string, true} -> 88 | io:format("Code: ~s~n",[to_hexstring(ByteList)]); 89 | none -> 90 | ok 91 | end, 92 | 93 | 94 | list_to_binary(ByteList). 95 | 96 | to_hexstring(ByteList) -> 97 | "0x" ++ lists:flatten( 98 | [io_lib:format("~2.16.0b", [X]) 99 | || X <- ByteList]). 100 | 101 | 102 | to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) -> 103 | OpCode = aeb_opcodes:m_to_op(Op), 104 | OpSize = aeb_opcodes:op_size(OpCode), 105 | to_bytecode(Rest, Address + OpSize, Env, [OpCode|Code], Opts); 106 | to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) -> 107 | to_bytecode(Rest, Address, Env, [Int|Code], Opts); 108 | to_bytecode([{hash,_line, Hash}|Rest], Address, Env, Code, Opts) -> 109 | to_bytecode(Rest, Address, Env, [Hash|Code], Opts); 110 | to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> 111 | to_bytecode(Rest, Address, Env, [{ref, ID}|Code], Opts); 112 | to_bytecode([{label,_line, Label}|Rest], Address, Env, Code, Opts) -> 113 | to_bytecode(Rest, Address, Env#{Label => Address}, Code, Opts); 114 | to_bytecode([], _Address, Env, Code, Opts) -> 115 | case proplists:lookup(pp_opcodes, Opts) of 116 | {pp_opcodes, true} -> 117 | io:format("opcodes ~p~n", [lists:reverse(Code)]); 118 | none -> 119 | ok 120 | end, 121 | 122 | PatchedCode = resolve_refs(Code, Env, []), 123 | case proplists:lookup(pp_patched_code, Opts) of 124 | {pp_patched_code, true} -> 125 | io:format("Patched Code: ~p~n", [PatchedCode]); 126 | none -> 127 | ok 128 | end, 129 | 130 | expand_args(PatchedCode). 131 | 132 | %% Also reverses the code (back to unreversed state). 133 | resolve_refs([{ref, ID} | Rest], Env, Code) -> 134 | Address = maps:get(ID, Env), 135 | resolve_refs(Rest, Env, [Address | Code]); 136 | resolve_refs([Op | Rest], Env, Code) -> 137 | resolve_refs(Rest, Env, [Op | Code]); 138 | resolve_refs([],_Env, Code) -> Code. 139 | 140 | expand_args([OP, Arg | Rest]) when OP >= ?PUSH1 andalso OP =< ?PUSH32 -> 141 | BitSize = (aeb_opcodes:op_size(OP) - 1) * 8, 142 | Bin = << << X:BitSize>> || X <- [Arg] >>, 143 | ArgByteList = binary_to_list(Bin), 144 | [OP | ArgByteList] ++ expand_args(Rest); 145 | expand_args([OP | Rest]) -> 146 | [OP | expand_args(Rest)]; 147 | expand_args([]) -> []. 148 | -------------------------------------------------------------------------------- /src/aeb_asm_scan.xrl: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- 2 | %%%------------------------------------------------------------------- 3 | %%% @copyright (C) 2017, Aeternity Anstalt 4 | %%% @doc Assembler lexer. 5 | %%% 6 | %%% @end 7 | %%%------------------------------------------------------------------- 8 | 9 | Definitions. 10 | DIGIT = [0-9] 11 | HEXDIGIT = [0-9a-fA-F] 12 | LOWER = [a-z_] 13 | UPPER = [A-Z] 14 | INT = {DIGIT}+ 15 | HEX = 0x{HEXDIGIT}+ 16 | HASH = #{HEXDIGIT}+ 17 | WS = [\000-\s] 18 | ID = {LOWER}[a-zA-Z0-9_]* 19 | LABEL = {ID}: 20 | 21 | 22 | 23 | Rules. 24 | {LABEL} : {token, {label, TokenLine, TokenChars -- ":"}}. 25 | STOP : {token, {mnemonic, TokenLine, 'STOP'}}. 26 | ADD : {token, {mnemonic, TokenLine, 'ADD'}}. 27 | MUL : {token, {mnemonic, TokenLine, 'MUL'}}. 28 | SUB : {token, {mnemonic, TokenLine, 'SUB'}}. 29 | DIV : {token, {mnemonic, TokenLine, 'DIV'}}. 30 | SDIV : {token, {mnemonic, TokenLine, 'SDIV'}}. 31 | MOD : {token, {mnemonic, TokenLine, 'MOD'}}. 32 | SMOD : {token, {mnemonic, TokenLine, 'SMOD'}}. 33 | ADDMOD : {token, {mnemonic, TokenLine, 'ADDMOD'}}. 34 | MULMOD : {token, {mnemonic, TokenLine, 'MULMOD'}}. 35 | EXP : {token, {mnemonic, TokenLine, 'EXP'}}. 36 | SIGNEXTEND : {token, {mnemonic, TokenLine, 'SIGNEXTEND'}}. 37 | LT : {token, {mnemonic, TokenLine, 'LT'}}. 38 | GT : {token, {mnemonic, TokenLine, 'GT'}}. 39 | SLT : {token, {mnemonic, TokenLine, 'SLT'}}. 40 | SGT : {token, {mnemonic, TokenLine, 'SGT'}}. 41 | EQ : {token, {mnemonic, TokenLine, 'EQ'}}. 42 | ISZERO : {token, {mnemonic, TokenLine, 'ISZERO'}}. 43 | AND : {token, {mnemonic, TokenLine, 'AND'}}. 44 | OR : {token, {mnemonic, TokenLine, 'OR'}}. 45 | XOR : {token, {mnemonic, TokenLine, 'XOR'}}. 46 | NOT : {token, {mnemonic, TokenLine, 'NOT'}}. 47 | BYTE : {token, {mnemonic, TokenLine, 'BYTE'}}. 48 | SHA3 : {token, {mnemonic, TokenLine, 'SHA3'}}. 49 | ADDRESS : {token, {mnemonic, TokenLine, 'ADDRESS'}}. 50 | BALANCE : {token, {mnemonic, TokenLine, 'BALANCE'}}. 51 | ORIGIN : {token, {mnemonic, TokenLine, 'ORIGIN'}}. 52 | CALLER : {token, {mnemonic, TokenLine, 'CALLER'}}. 53 | CALLVALUE : {token, {mnemonic, TokenLine, 'CALLVALUE'}}. 54 | CALLDATALOAD : {token, {mnemonic, TokenLine, 'CALLDATALOAD'}}. 55 | CALLDATASIZE : {token, {mnemonic, TokenLine, 'CALLDATASIZE'}}. 56 | CALLDATACOPY : {token, {mnemonic, TokenLine, 'CALLDATACOPY'}}. 57 | CODESIZE : {token, {mnemonic, TokenLine, 'CODESIZE'}}. 58 | CODECOPY : {token, {mnemonic, TokenLine, 'CODECOPY'}}. 59 | GASPRICE : {token, {mnemonic, TokenLine, 'GASPRICE'}}. 60 | EXTCODESIZE : {token, {mnemonic, TokenLine, 'EXTCODESIZE'}}. 61 | EXTCODECOPY : {token, {mnemonic, TokenLine, 'EXTCODECOPY'}}. 62 | RETURNDATASIZE : {token, {mnemonic, TokenLine, 'RETURNDATASIZE'}}. 63 | RETURNDATACOPY : {token, {mnemonic, TokenLine, 'RETURNDATACOPY'}}. 64 | BLOCKHASH : {token, {mnemonic, TokenLine, 'BLOCKHASH'}}. 65 | COINBASE : {token, {mnemonic, TokenLine, 'COINBASE'}}. 66 | TIMESTAMP : {token, {mnemonic, TokenLine, 'TIMESTAMP'}}. 67 | NUMBER : {token, {mnemonic, TokenLine, 'NUMBER'}}. 68 | DIFFICULTY : {token, {mnemonic, TokenLine, 'DIFFICULTY'}}. 69 | GASLIMIT : {token, {mnemonic, TokenLine, 'GASLIMIT'}}. 70 | POP : {token, {mnemonic, TokenLine, 'POP'}}. 71 | MLOAD : {token, {mnemonic, TokenLine, 'MLOAD'}}. 72 | MSTORE : {token, {mnemonic, TokenLine, 'MSTORE'}}. 73 | MSTORE8 : {token, {mnemonic, TokenLine, 'MSTORE8'}}. 74 | SLOAD : {token, {mnemonic, TokenLine, 'SLOAD'}}. 75 | SSTORE : {token, {mnemonic, TokenLine, 'SSTORE'}}. 76 | JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}. 77 | JUMPI : {token, {mnemonic, TokenLine, 'JUMPI'}}. 78 | PC : {token, {mnemonic, TokenLine, 'PC'}}. 79 | MSIZE : {token, {mnemonic, TokenLine, 'MSIZE'}}. 80 | GAS : {token, {mnemonic, TokenLine, 'GAS'}}. 81 | JUMPDEST : {token, {mnemonic, TokenLine, 'JUMPDEST'}}. 82 | PUSH1 : {token, {mnemonic, TokenLine, 'PUSH1'}}. 83 | PUSH2 : {token, {mnemonic, TokenLine, 'PUSH2'}}. 84 | PUSH3 : {token, {mnemonic, TokenLine, 'PUSH3'}}. 85 | PUSH4 : {token, {mnemonic, TokenLine, 'PUSH4'}}. 86 | PUSH5 : {token, {mnemonic, TokenLine, 'PUSH5'}}. 87 | PUSH6 : {token, {mnemonic, TokenLine, 'PUSH6'}}. 88 | PUSH7 : {token, {mnemonic, TokenLine, 'PUSH7'}}. 89 | PUSH8 : {token, {mnemonic, TokenLine, 'PUSH8'}}. 90 | PUSH9 : {token, {mnemonic, TokenLine, 'PUSH9'}}. 91 | PUSH10 : {token, {mnemonic, TokenLine, 'PUSH10'}}. 92 | PUSH11 : {token, {mnemonic, TokenLine, 'PUSH11'}}. 93 | PUSH12 : {token, {mnemonic, TokenLine, 'PUSH12'}}. 94 | PUSH13 : {token, {mnemonic, TokenLine, 'PUSH13'}}. 95 | PUSH14 : {token, {mnemonic, TokenLine, 'PUSH14'}}. 96 | PUSH15 : {token, {mnemonic, TokenLine, 'PUSH15'}}. 97 | PUSH16 : {token, {mnemonic, TokenLine, 'PUSH16'}}. 98 | PUSH17 : {token, {mnemonic, TokenLine, 'PUSH17'}}. 99 | PUSH18 : {token, {mnemonic, TokenLine, 'PUSH18'}}. 100 | PUSH19 : {token, {mnemonic, TokenLine, 'PUSH19'}}. 101 | PUSH20 : {token, {mnemonic, TokenLine, 'PUSH20'}}. 102 | PUSH21 : {token, {mnemonic, TokenLine, 'PUSH21'}}. 103 | PUSH22 : {token, {mnemonic, TokenLine, 'PUSH22'}}. 104 | PUSH23 : {token, {mnemonic, TokenLine, 'PUSH23'}}. 105 | PUSH24 : {token, {mnemonic, TokenLine, 'PUSH24'}}. 106 | PUSH25 : {token, {mnemonic, TokenLine, 'PUSH25'}}. 107 | PUSH26 : {token, {mnemonic, TokenLine, 'PUSH26'}}. 108 | PUSH27 : {token, {mnemonic, TokenLine, 'PUSH27'}}. 109 | PUSH28 : {token, {mnemonic, TokenLine, 'PUSH28'}}. 110 | PUSH29 : {token, {mnemonic, TokenLine, 'PUSH29'}}. 111 | PUSH30 : {token, {mnemonic, TokenLine, 'PUSH30'}}. 112 | PUSH31 : {token, {mnemonic, TokenLine, 'PUSH31'}}. 113 | PUSH32 : {token, {mnemonic, TokenLine, 'PUSH32'}}. 114 | DUP1 : {token, {mnemonic, TokenLine, 'DUP1'}}. 115 | DUP2 : {token, {mnemonic, TokenLine, 'DUP2'}}. 116 | DUP3 : {token, {mnemonic, TokenLine, 'DUP3'}}. 117 | DUP4 : {token, {mnemonic, TokenLine, 'DUP4'}}. 118 | DUP5 : {token, {mnemonic, TokenLine, 'DUP5'}}. 119 | DUP6 : {token, {mnemonic, TokenLine, 'DUP6'}}. 120 | DUP7 : {token, {mnemonic, TokenLine, 'DUP7'}}. 121 | DUP8 : {token, {mnemonic, TokenLine, 'DUP8'}}. 122 | DUP9 : {token, {mnemonic, TokenLine, 'DUP9'}}. 123 | DUP10 : {token, {mnemonic, TokenLine, 'DUP10'}}. 124 | DUP11 : {token, {mnemonic, TokenLine, 'DUP11'}}. 125 | DUP12 : {token, {mnemonic, TokenLine, 'DUP12'}}. 126 | DUP13 : {token, {mnemonic, TokenLine, 'DUP13'}}. 127 | DUP14 : {token, {mnemonic, TokenLine, 'DUP14'}}. 128 | DUP15 : {token, {mnemonic, TokenLine, 'DUP15'}}. 129 | DUP16 : {token, {mnemonic, TokenLine, 'DUP16'}}. 130 | SWAP1 : {token, {mnemonic, TokenLine, 'SWAP1'}}. 131 | SWAP2 : {token, {mnemonic, TokenLine, 'SWAP2'}}. 132 | SWAP3 : {token, {mnemonic, TokenLine, 'SWAP3'}}. 133 | SWAP4 : {token, {mnemonic, TokenLine, 'SWAP4'}}. 134 | SWAP5 : {token, {mnemonic, TokenLine, 'SWAP5'}}. 135 | SWAP6 : {token, {mnemonic, TokenLine, 'SWAP6'}}. 136 | SWAP7 : {token, {mnemonic, TokenLine, 'SWAP7'}}. 137 | SWAP8 : {token, {mnemonic, TokenLine, 'SWAP8'}}. 138 | SWAP9 : {token, {mnemonic, TokenLine, 'SWAP9'}}. 139 | SWAP10 : {token, {mnemonic, TokenLine, 'SWAP10'}}. 140 | SWAP11 : {token, {mnemonic, TokenLine, 'SWAP11'}}. 141 | SWAP12 : {token, {mnemonic, TokenLine, 'SWAP12'}}. 142 | SWAP13 : {token, {mnemonic, TokenLine, 'SWAP13'}}. 143 | SWAP14 : {token, {mnemonic, TokenLine, 'SWAP14'}}. 144 | SWAP15 : {token, {mnemonic, TokenLine, 'SWAP15'}}. 145 | SWAP16 : {token, {mnemonic, TokenLine, 'SWAP16'}}. 146 | LOG0 : {token, {mnemonic, TokenLine, 'LOG0'}}. 147 | LOG1 : {token, {mnemonic, TokenLine, 'LOG1'}}. 148 | LOG2 : {token, {mnemonic, TokenLine, 'LOG2'}}. 149 | LOG3 : {token, {mnemonic, TokenLine, 'LOG3'}}. 150 | LOG4 : {token, {mnemonic, TokenLine, 'LOG4'}}. 151 | CREATE : {token, {mnemonic, TokenLine, 'CREATE'}}. 152 | CALL : {token, {mnemonic, TokenLine, 'CALL'}}. 153 | CALLCODE : {token, {mnemonic, TokenLine, 'CALLCODE'}}. 154 | RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}. 155 | DELEGATECALL : {token, {mnemonic, TokenLine, 'DELEGATECALL'}}. 156 | STATICCALL : {token, {mnemonic, TokenLine, 'STATICCALL'}}. 157 | REVERT : {token, {mnemonic, TokenLine, 'REVERT'}}. 158 | INVALID : {token, {mnemonic, TokenLine, 'INVALID'}}. 159 | SUICIDE : {token, {mnemonic, TokenLine, 'SUICIDE'}}. 160 | COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}. 161 | {ID} : 162 | {token, {id, TokenLine, TokenChars}}. 163 | {HEX} : 164 | {token, {int, TokenLine, parse_hex(TokenChars)}}. 165 | {INT} : 166 | {token, {int, TokenLine, parse_int(TokenChars)}}. 167 | {HASH} : 168 | {token, {hash, TokenLine, parse_hash(TokenChars)}}. 169 | 170 | 171 | %% Symbols 172 | , : {token, {',', TokenLine}}. 173 | \. : {token, {'.', TokenLine}}. 174 | \( : {token, {'(', TokenLine}}. 175 | \) : {token, {')', TokenLine}}. 176 | \[ : {token, {'[', TokenLine}}. 177 | \] : {token, {']', TokenLine}}. 178 | { : {token, {'{', TokenLine}}. 179 | } : {token, {'}', TokenLine}}. 180 | 181 | 182 | %% Whitespace ignore 183 | {WS} : skip_token. 184 | 185 | %% Comments (TODO: nested comments) 186 | ;;.* : skip_token. 187 | 188 | . : {error, "Unexpected token: " ++ TokenChars}. 189 | 190 | Erlang code. 191 | 192 | -export([scan/1]). 193 | 194 | -dialyzer({nowarn_function, yyrev/2}). 195 | 196 | -ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). 197 | 198 | -include_lib("aebytecode/include/aeb_opcodes.hrl"). 199 | 200 | 201 | parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). 202 | 203 | parse_int(Chars) -> list_to_integer(Chars). 204 | 205 | parse_hash("#" ++ Chars) -> 206 | N = list_to_integer(Chars, 16), 207 | <>. 208 | 209 | scan(S) -> 210 | string(S). 211 | 212 | -------------------------------------------------------------------------------- /src/aeb_disassemble.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2017, Aeternity Anstalt 3 | %%% @doc 4 | %%% Prettyprint aevm machine code 5 | %%% @end 6 | %%% Created : 2 Oct 2017 7 | %%%------------------------------------------------------------------- 8 | 9 | -module(aeb_disassemble). 10 | 11 | -export([ pp/1, 12 | format/2, 13 | format_address/1 14 | ]). 15 | 16 | -include_lib("aebytecode/include/aeb_opcodes.hrl"). 17 | 18 | 19 | pp(Binary) -> 20 | Listing = format(Binary, fun io:format/2), 21 | io:format("~s~n", [Listing]). 22 | 23 | format(Binary, ErrFormatFun) -> 24 | pp(0, binary:bin_to_list(Binary), [], ErrFormatFun). 25 | 26 | pp(Address, [Op|Ops], Assembly, ErrFormatFun) -> 27 | case Op of 28 | X when (X >= ?STOP) andalso (X =< ?SIGNEXTEND) -> 29 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 30 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 31 | X when (X >= ?LT) andalso (X =< ?BYTE) -> 32 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 33 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 34 | X when (X >= ?SHA3) andalso (X =< ?SHA3) -> 35 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 36 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 37 | X when (X >= ?ADDRESS) andalso (X =< ?EXTCODECOPY) -> 38 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 39 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 40 | X when (X >= ?BLOCKHASH) andalso (X =< ?GASLIMIT) -> 41 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 42 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 43 | X when (X >= ?POP) andalso (X =< ?JUMPDEST) -> 44 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 45 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 46 | X when (X >= ?PUSH1) andalso (X =< ?PUSH32) -> 47 | Bytes = X-?PUSH1+1, 48 | {ArgList, NextOps} = lists:split(Bytes, Ops), 49 | Arg = arglist_to_arg(ArgList), 50 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), [{Arg,8*Bytes}]), 51 | next(Address+Bytes, NextOps, Instr, Assembly, ErrFormatFun); 52 | X when (X >= ?DUP1) andalso (X =< ?LOG4) -> 53 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 54 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 55 | X when (X >= ?CREATE) andalso (X =< ?DELEGATECALL) -> 56 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 57 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 58 | X when (X >= ?INVALID) andalso (X =< ?SUICIDE) -> 59 | Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), 60 | next(Address, Ops, Instr, Assembly, ErrFormatFun); 61 | _ -> 62 | ErrFormatFun("unhandled op ~p at ~p",[Op, Address]), 63 | next(Address, Ops, "", Assembly, ErrFormatFun) 64 | end; 65 | pp(_, [], Assembly, _) -> lists:reverse(Assembly). 66 | 67 | 68 | arglist_to_arg([B|Bs]) -> 69 | arglist_to_arg(Bs, B). 70 | 71 | arglist_to_arg([B|Bs], Acc) -> 72 | arglist_to_arg(Bs, Acc*256 + B); 73 | arglist_to_arg([], Acc) -> Acc. 74 | 75 | pp_instruction(Address, Op, Args) -> 76 | [format_address(Address), " ", 77 | pad_op(atom_to_list(Op)), 78 | pp_args(Args), 79 | "\n"]. 80 | 81 | format_address(Address) -> 82 | io_lib:format("0x~8.16.0B",[Address]). 83 | 84 | pad_op(Op) -> 85 | N = length(Op), 86 | Pad = 17 - N, 87 | [Op,lists:duplicate(Pad, 32)]. 88 | 89 | pp_args([]) -> []; 90 | pp_args([{Arg, Size}]) -> 91 | case Size of 92 | 8 -> io_lib:format("0x~2.16.0B",[Arg]); 93 | 160 -> io_lib:format("0x~64.16.0B",[Arg]); 94 | 232 -> io_lib:format("0x~64.16.0B",[Arg]); 95 | 256 -> io_lib:format("0x~64.16.0B",[Arg]); 96 | _ -> io_lib:format("0x~64.16.0B",[Arg]) 97 | end; 98 | pp_args([{Arg, Size}|Args]) -> 99 | [pp_args([{Arg, Size}]), " ", pp_args(Args)]. 100 | 101 | next(Address, Ops, Instr, Assembly, ErrFormatFun) -> 102 | pp(Address+1, Ops, [Instr|Assembly], ErrFormatFun). 103 | -------------------------------------------------------------------------------- /src/aeb_fate_abi.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2019, Aeternity Anstalt 3 | %%% @doc 4 | %%% Encode and decode data and function calls according to 5 | %%% Sophia-FATE-ABI 6 | %%% @end 7 | %%% Created : 11 Jun 2019 8 | %%% 9 | %%%------------------------------------------------------------------- 10 | -module(aeb_fate_abi). 11 | 12 | -export([ create_calldata/2 13 | , decode_calldata/2 14 | , get_function_hash_from_calldata/1 15 | , get_function_name_from_function_hash/2 16 | , get_function_type_from_function_hash/2 17 | , abi_version/0 ]). 18 | 19 | -include("../include/aeb_fate_data.hrl"). 20 | 21 | %%%=================================================================== 22 | %%% API 23 | %%%=================================================================== 24 | 25 | %% Shall match ?ABI_FATE_SOPHIA_1 26 | -spec abi_version() -> integer(). 27 | abi_version() -> 28 | 3. 29 | 30 | -spec create_calldata(list(), [term()]) -> {ok, binary()}. 31 | create_calldata(FunName, Args) -> 32 | FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)), 33 | {ok, aeb_fate_encoding:serialize( 34 | aeb_fate_data:make_tuple({FunctionId, 35 | aeb_fate_data:make_tuple(list_to_tuple(Args))}))}. 36 | 37 | -spec decode_calldata(list(), binary()) -> {ok, term()} | {error, term()}. 38 | decode_calldata(FunName, Calldata) -> 39 | FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)), 40 | try ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(Calldata)) of 41 | [FunctionId, FateArgs] -> {ok, ?FATE_TUPLE_ELEMENTS(FateArgs)}; 42 | _ -> {error, decode_error} 43 | catch _:_ -> 44 | {error, decode_error} 45 | end. 46 | 47 | -spec get_function_name_from_function_hash(binary(), aeb_fate_code:fcode()) -> 48 | {ok, term()} | {error, term()}. 49 | get_function_name_from_function_hash(<>, FateCode) -> 50 | get_function_name_from_function_hash(SymbolHash, FateCode); 51 | get_function_name_from_function_hash(SymbolHash = <<_:4/binary>>, FateCode) -> 52 | Symbols = aeb_fate_code:symbols(FateCode), 53 | case maps:get(SymbolHash, Symbols, undefined) of 54 | undefined -> {error, no_function_matching_function_hash}; 55 | Function -> {ok, Function} 56 | end. 57 | 58 | -spec get_function_hash_from_calldata(binary()) -> 59 | {ok, binary()} | {error, term()}. 60 | get_function_hash_from_calldata(CallData) -> 61 | try ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(CallData)) of 62 | [FunHash, _Args] -> {ok, FunHash}; 63 | _ -> {error, bad_calldata} 64 | catch _:_ -> 65 | {error, bad_calldata} 66 | end. 67 | 68 | -spec get_function_type_from_function_hash(binary(), aeb_fate_code:fcode()) -> 69 | {ok, term(), term()} | {error, term()}. 70 | get_function_type_from_function_hash(<>, FateCode) -> 71 | get_function_type_from_function_hash(SymbolHash, FateCode); 72 | get_function_type_from_function_hash(SymbolHash, FateCode) -> 73 | Functions = aeb_fate_code:functions(FateCode), 74 | case maps:get(SymbolHash, Functions, undefined) of 75 | undefined -> 76 | {error, no_function_matching_function_hash}; 77 | {_Attrs, {ArgTypes, RetType}, _Code} -> 78 | {ok, ArgTypes, RetType} 79 | end. 80 | -------------------------------------------------------------------------------- /src/aeb_fate_asm.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2019, Aeternity Anstalt 3 | %%% @doc Assembler for Fate machine code. 4 | %%% @end 5 | %%% 6 | %%% Fate code exists in 3 formats: 7 | %%% 8 | %%% 1. Fate byte code. This format is under consensus. 9 | %%% 2. Fate assembler. This is a text represenation of fate code. 10 | %%% This is not under consensus and other 11 | %%% implemenation and toolchains could have 12 | %%% their own format. 13 | %%% 3. Internal. This is an Erlang representation of fate code 14 | %%% Used by this particular engin implementation. 15 | %%% 16 | %%% This library handles all tree representations. 17 | %%% The byte code format is described in a separate document. 18 | %%% The internal format is described in a separate document. 19 | %%% The text representation is described here: 20 | %%% 21 | %%% Assembler code can be read from a file. 22 | %%% The assembler has the following format 23 | %%% Comments start with 2 semicolons and runs till end of line 24 | %%% ;; This is a comment 25 | %%% Opcode mnemonics start with an upper case letter. 26 | %%% DUP 27 | %%% Identifiers start with a lower case letter 28 | %%% an_identifier 29 | %%% References to function arguments start with arg followed by an integer 30 | %%% arg0 31 | %%% References to variables/registers start with var followed by an integer 32 | %%% var0 33 | %%% References to the top of the stack is the letter a (for accumulator) 34 | %%% a 35 | %%% 36 | %%% Immediate values can be of 10 types: 37 | %%% 1a. Integers as decimals: {Digits} or -{Digits} 38 | %%% 42 39 | %%% -2374683271468723648732648736498712634876147 40 | %%% 1b. Integers as Hexadecimals:: 0x{Hexdigits} 41 | %%% 0x0deadbeef0 42 | %%% 2a. account addresses, a base58c encoded string prefixed with @ak_ 43 | %%% 2b. contract address: @ct_{base58char}+ 44 | %%% 2c. oracle address: @ok_{base58char}+ 45 | %%% 2d. oracle query id: @oq_{base58char}+ 46 | %%% 2e. channel address: @ch_{base58char}+ 47 | %%% 3. Boolean true or false 48 | %%% true 49 | %%% false 50 | %%% 4. Strings "{Characters}" 51 | %%% "Hello" 52 | %%% 5. Map { Key => Value } 53 | %%% {} 54 | %%% { 1 => { "foo" => true, "bar" => false} 55 | %%% 6. Lists [ Elements ] 56 | %%% [] 57 | %%% [1, 2] 58 | %%% 7. Bit field < Bits > or !< Bits > 59 | %%% <000> 60 | %%% <1010 1010> 61 | %%% <> 62 | %%% !<> 63 | %%% 8. Tuples ( Elements ) 64 | %%% () 65 | %%% (1, "foo") 66 | %%% 9. Variants: (| [Arities] | Tag | ( Elements ) |) 67 | %%% (| [0,1,2] | 2 | ( "foo", 12) |) 68 | %%% 10. Bytes: #{base64char}+ 69 | %%% #AQIDCioLFQ== 70 | %%% 71 | %%% Where Digits: [0123456789] 72 | %%% Hexdigits: [0123456789abcdef] 73 | %%% base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz] 74 | %%% base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=] 75 | %%% Characters: as a code literal - any printable ascii character 0..255 (except " no quoting yet) 76 | %%% the type supports an array of bytes (all values 0..255). 77 | %%% Key: any value except for a map 78 | %%% Bits: 01 or space 79 | %%% Elements: Nothing or Value , Elements 80 | %%% Size: Digits 81 | %%% Tag: Digits 82 | %%% 83 | %%% Created : 21 Dec 2017 84 | %%%------------------------------------------------------------------- 85 | 86 | -module(aeb_fate_asm). 87 | 88 | -export([ assemble_file/3 89 | , asm_to_bytecode/2 90 | , function_call/1 91 | , pp/1 92 | , read_file/1 93 | , strip/1 94 | , to_asm/1 95 | ]). 96 | 97 | -include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). 98 | -include_lib("aebytecode/include/aeb_fate_data.hrl"). 99 | -define(HASH_BYTES, 32). 100 | 101 | assemble_file(InFile, OutFile, Options) -> 102 | Asm = read_file(InFile), 103 | {_Env, BC} = asm_to_bytecode(Asm, Options), 104 | ok = file:write_file(OutFile, BC). 105 | 106 | function_call(String) -> 107 | {ok, Tokens, _} = aeb_fate_asm_scan:scan(String), 108 | parse_function_call(Tokens). 109 | 110 | parse_function_call([{id,_,Name}, {'(',_}| Rest]) -> 111 | {Args, []} = to_args(Rest), 112 | aeb_fate_encoding:serialize( 113 | {tuple, {mk_hash(Name), {tuple, list_to_tuple(Args)}}}). 114 | 115 | 116 | to_args([{')', _}]) -> {[], []}; 117 | to_args(Tokens) -> 118 | case parse_value(Tokens) of 119 | {Arg, [{',', _} | Rest]} -> 120 | {More, Rest2} = to_args(Rest), 121 | {[Arg|More], Rest2}; 122 | {Arg, [{')', _} | Rest]} -> 123 | {[Arg], Rest} 124 | end. 125 | 126 | pp(FateCode) -> 127 | Listing = to_asm(FateCode), 128 | io_lib:format("~ts~n",[Listing]). 129 | 130 | 131 | to_asm(FateCode) -> 132 | Functions = aeb_fate_code:functions(FateCode), 133 | Symbols = aeb_fate_code:symbols(FateCode), 134 | Annotations = aeb_fate_code:annotations(FateCode), 135 | insert_comments(get_comments(Annotations), 1, 136 | lists:flatten( 137 | io_lib:format("~s", 138 | [format_functions(Functions, Symbols)]))). 139 | 140 | insert_comments([{L,C}|Comments], L, String) -> 141 | ";; " ++ C ++ "\n" ++ insert_comments(Comments, L + 1, String); 142 | insert_comments(Comments, L, [$\n|String]) -> 143 | "\n" ++ insert_comments(Comments, L+1, String); 144 | insert_comments(Comments, L, [C|Rest]) -> 145 | [C|insert_comments(Comments, L, Rest)]; 146 | insert_comments([],_,[]) -> []; 147 | insert_comments([{L,C}|Rest], _, []) -> 148 | ";; " ++ C ++ "\n" ++ insert_comments(Rest, L + 1, []). 149 | 150 | format_functions(Functions, Symbols) -> 151 | [format(lookup(Name, Symbols), 152 | Sig, 153 | lists:sort(maps:to_list(CodeMap)), 154 | Symbols) 155 | || 156 | {Name, {_Attrs, Sig, CodeMap}} <- maps:to_list(Functions)]. 157 | 158 | 159 | format(Name, Sig, BBs, Symbols) -> 160 | [ "FUNCTION " 161 | , Name 162 | , format_sig(Sig) 163 | , "\n" 164 | , format_bbs(BBs, Symbols)]. 165 | 166 | format_sig({Args, RetType}) -> 167 | [ "( " 168 | , format_arg_types(Args) 169 | , ") : " 170 | , format_type(RetType)]. 171 | 172 | format_arg_types([]) -> ""; 173 | format_arg_types([T]) -> format_type(T); 174 | format_arg_types([T|Ts]) -> 175 | [format_type(T) 176 | , ", " 177 | , format_arg_types(Ts)]. 178 | 179 | format_type(T) -> 180 | %% TODO: Limit to ok types. 181 | io_lib:format("~p", [T]). 182 | 183 | format_bbs([], _) -> 184 | []; 185 | format_bbs([{BB, Code}|Rest], Symbols) -> 186 | [ io_lib:format(" ;; BB : ~p~n", [BB]) 187 | , format_code(Code, Symbols) 188 | | format_bbs(Rest, Symbols)]. 189 | 190 | format_code([], _) -> 191 | ""; 192 | format_code([Op|Rest], Symbols) -> 193 | [" ", 194 | aeb_fate_pp:format_op(Op, Symbols), 195 | "\n", 196 | format_code(Rest, Symbols)]. 197 | 198 | 199 | read_file(Filename) -> 200 | {ok, File} = file:read_file(Filename), 201 | binary_to_list(File). 202 | 203 | asm_to_bytecode(AssemblerCode, Options) -> 204 | {ok, Tokens, _} = aeb_fate_asm_scan:scan(AssemblerCode), 205 | 206 | case proplists:lookup(pp_tokens, Options) of 207 | {pp_tokens, true} -> 208 | io:format("Tokens ~p~n",[Tokens]); 209 | none -> 210 | ok 211 | end, 212 | Env = #{ fate_code => aeb_fate_code:new() 213 | , functions => #{} 214 | }, 215 | 216 | Env1 = to_bytecode(Tokens, none, Env, [], Options), 217 | FateCode = maps:get(fate_code, Env1), 218 | FunctionsMap = maps:get(functions, Env1), 219 | Functions = [X || {_, X} <- lists:sort(maps:to_list(FunctionsMap))], 220 | FunctionsBin = iolist_to_binary(Functions), 221 | ByteCode = aeb_fate_code:serialize(FateCode, FunctionsBin, Options), 222 | {Env, ByteCode}. 223 | 224 | strip(ByteCode) -> 225 | {Code, _Rest} = aeser_rlp:decode_one(ByteCode), 226 | Code. 227 | 228 | %% ------------------------------------------------------------------- 229 | %% Parser 230 | %% Asm tokens -> Fate code env 231 | %% ------------------------------------------------------------------- 232 | 233 | to_bytecode([{function,_line, 'FUNCTION'}|Rest], Address, Env, Code, Opts) -> 234 | Env2 = insert_fun(Address, Code, Env), 235 | {Fun, Rest2} = to_fun_def(Rest), 236 | to_bytecode(Rest2, Fun, Env2, [], Opts); 237 | to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) -> 238 | OpCode = aeb_fate_opcodes:m_to_op(Op), 239 | to_bytecode(Rest, Address, Env, [OpCode|Code], Opts); 240 | to_bytecode([{arg,_line, N}|Rest], Address, Env, Code, Opts) -> 241 | to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts); 242 | to_bytecode([{var,_line, N}|Rest], Address, Env, Code, Opts) -> 243 | to_bytecode(Rest, Address, Env, [{var, N}|Code], Opts); 244 | to_bytecode([{stack,_line}|Rest], Address, Env, Code, Opts) -> 245 | to_bytecode(Rest, Address, Env, [{stack, 0}|Code], Opts); 246 | to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) -> 247 | to_bytecode(Rest, Address, Env, [{immediate, Int}|Code], Opts); 248 | to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) -> 249 | to_bytecode(Rest, Address, Env, [{immediate, Bool}|Code], Opts); 250 | to_bytecode([{string,_line, String}|Rest], Address, Env, Code, Opts) -> 251 | to_bytecode(Rest, Address, Env, 252 | [{immediate, aeb_fate_data:make_string(String)}|Code], 253 | Opts); 254 | to_bytecode([{object,_line, {address, Value}}|Rest], 255 | Address, Env, Code, Opts) -> 256 | to_bytecode(Rest, Address, Env, 257 | [{immediate, aeb_fate_data:make_address(Value)}|Code], 258 | Opts); 259 | to_bytecode([{object,_line, {contract, Value}}|Rest], 260 | Address, Env, Code, Opts) -> 261 | to_bytecode(Rest, Address, Env, 262 | [{immediate, aeb_fate_data:make_contract(Value)}|Code], 263 | Opts); 264 | to_bytecode([{object,_line, {oracle, Value}}|Rest], 265 | Address, Env, Code, Opts) -> 266 | to_bytecode(Rest, Address, Env, 267 | [{immediate, aeb_fate_data:make_oracle(Value)}|Code], 268 | Opts); 269 | to_bytecode([{object,_line, {oracle_query, Value}}|Rest], 270 | Address, Env, Code, Opts) -> 271 | to_bytecode(Rest, Address, Env, 272 | [{immediate, aeb_fate_data:make_oracle_query(Value)}|Code], 273 | Opts); 274 | to_bytecode([{object,_line, {channel, Value}}|Rest], 275 | Address, Env, Code, Opts) -> 276 | to_bytecode(Rest, Address, Env, 277 | [{immediate, aeb_fate_data:make_contract(Value)}|Code], 278 | Opts); 279 | to_bytecode([{bytes,_line, Value}|Rest], 280 | Address, Env, Code, Opts) -> 281 | to_bytecode(Rest, Address, Env, 282 | [{immediate, aeb_fate_data:make_bytes(Value)}|Code], 283 | Opts); 284 | to_bytecode([{contract_bytearray,_line, FateCode}|Rest], Address, Env, Code, Opts) -> 285 | to_bytecode(Rest, Address, Env, 286 | [{immediate, aeb_fate_data:make_contract_bytearray(FateCode)}|Code], 287 | Opts); 288 | to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> 289 | {Env2, Id} = insert_symbol(list_to_binary(ID), Env), 290 | to_bytecode(Rest, Address, Env2, [{immediate, Id}|Code], Opts); 291 | to_bytecode([{'{',_line}|Rest], Address, Env, Code, Opts) -> 292 | {Map, Rest2} = parse_map(Rest), 293 | to_bytecode(Rest2, Address, Env, [{immediate, Map}|Code], Opts); 294 | to_bytecode([{'[',_line}|Rest], Address, Env, Code, Opts) -> 295 | {List, Rest2} = parse_list(Rest), 296 | to_bytecode(Rest2, Address, Env, [{immediate, List}|Code], Opts); 297 | to_bytecode([{'(',_line}|Rest], Address, Env, Code, Opts) -> 298 | {Elements, Rest2} = parse_tuple(Rest), 299 | Tuple = aeb_fate_data:make_tuple(list_to_tuple(Elements)), 300 | to_bytecode(Rest2, Address, Env, [{immediate, Tuple}|Code], Opts); 301 | to_bytecode([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) -> 302 | {Arities, Tag, Values, Rest} = parse_variant(Tokens), 303 | Variant = aeb_fate_data:make_variant(Arities, Tag, Values), 304 | to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts); 305 | to_bytecode([{typerep,_line}|Rest], Address, Env, Code, Opts) -> 306 | {Type, Rest1} = to_type(Rest), 307 | TypeRep = aeb_fate_data:make_typerep(Type), 308 | to_bytecode(Rest1, Address, Env, [{immediate, TypeRep}|Code], Opts); 309 | to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) -> 310 | to_bytecode(Rest, Address, Env, 311 | [{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts); 312 | 313 | to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) -> 314 | Env2 = insert_annotation(comment, Line, Comment, Env), 315 | to_bytecode(Rest, Address, Env2, Code, Opts); 316 | 317 | to_bytecode([], Address, Env, Code,_Opts) -> 318 | insert_fun(Address, Code, Env). 319 | 320 | parse_map([{'}',_line}|Rest]) -> 321 | {#{}, Rest}; 322 | parse_map(Tokens) -> 323 | {Key, [{arrow, _} | Rest]} = parse_value(Tokens), 324 | {Value, Rest2} = parse_value(Rest), 325 | case Rest2 of 326 | [{',',_} | Rest3] -> 327 | {Map, Rest4} = parse_map(Rest3), 328 | {Map#{Key => Value}, Rest4}; 329 | [{'}',_} | Rest3] -> 330 | {#{Key => Value}, Rest3} 331 | end. 332 | 333 | parse_list([{']',_line}|Rest]) -> 334 | {[], Rest}; 335 | parse_list(Tokens) -> 336 | {Head , Rest} = parse_value(Tokens), 337 | case Rest of 338 | [{',',_} | Rest2] -> 339 | {Tail, Rest3} = parse_list(Rest2), 340 | {[Head | Tail], Rest3}; 341 | [{']',_} | Rest3] -> 342 | {[Head], Rest3} 343 | end. 344 | 345 | parse_tuple([{')',_line}|Rest]) -> 346 | {[], Rest}; 347 | parse_tuple(Tokens) -> 348 | {Head , Rest} = parse_value(Tokens), 349 | case Rest of 350 | [{',',_} | Rest2] -> 351 | {Tail, Rest3} = parse_tuple(Rest2), 352 | {[Head | Tail], Rest3}; 353 | [{')',_} | Rest3] -> 354 | {[Head], Rest3} 355 | end. 356 | 357 | 358 | parse_variant([{start_variant,_line} 359 | , {'[', _} 360 | | Rest]) -> 361 | {Arities, Rest2} = parse_list(Rest), 362 | %% Make sure Arities is a list of bytes. 363 | Arities = [A || A <- Arities, 364 | is_integer(A), A < 256], 365 | 366 | [{'|',_} 367 | , {int,_, Tag} 368 | , {'|',_} 369 | , {'(',_} | Rest3] = Rest2, 370 | {Elements , [{end_variant, _} | Rest4]} = parse_tuple(Rest3), 371 | Size = length(Arities), 372 | if 0 =< Tag, Tag < Size -> 373 | Arity = lists:nth(Tag+1, Arities), 374 | if length(Elements) =:= Arity -> 375 | {Arities, Tag, list_to_tuple(Elements), Rest4} 376 | end 377 | end. 378 | 379 | 380 | parse_value([{int,_line, Int} | Rest]) -> {Int, Rest}; 381 | parse_value([{boolean,_line, Bool} | Rest]) -> {Bool, Rest}; 382 | parse_value([{'{',_line} | Rest]) -> parse_map(Rest); 383 | parse_value([{'[',_line} | Rest]) -> parse_list(Rest); 384 | parse_value([{'(',_line} | Rest]) -> 385 | {T, Rest2} = parse_tuple(Rest), 386 | {aeb_fate_data:make_tuple(list_to_tuple(T)), Rest2}; 387 | parse_value([{bits,_line, Bits} | Rest]) -> 388 | {aeb_fate_data:make_bits(Bits), Rest}; 389 | parse_value([{start_variant,_line}|_] = Tokens) -> 390 | {Arities, Tag, Values, Rest} = parse_variant(Tokens), 391 | Variant = aeb_fate_data:make_variant(Arities, Tag, Values), 392 | {Variant, Rest}; 393 | parse_value([{string,_line, String} | Rest]) -> 394 | {aeb_fate_data:make_string(String), Rest}; 395 | parse_value([{object,_line, {address, Address}} | Rest]) -> 396 | {aeb_fate_data:make_address(Address), Rest}; 397 | parse_value([{object,_line, {contract, Address}} | Rest]) -> 398 | {aeb_fate_data:make_contract(Address), Rest}; 399 | parse_value([{object,_line, {oracle, Address}} | Rest]) -> 400 | {aeb_fate_data:make_oracle(Address), Rest}; 401 | parse_value([{object,_line, {oracle_query, Address}} | Rest]) -> 402 | {aeb_fate_data:make_oracle_query(Address), Rest}; 403 | parse_value([{object,_line, {channel, Address}} | Rest]) -> 404 | {aeb_fate_data:make_channel(Address), Rest}; 405 | parse_value([{hash,_line, Hash} | Rest]) -> 406 | {aeb_fate_data:make_hash(Hash), Rest}; 407 | parse_value([{signature,_line, Hash} | Rest]) -> 408 | {aeb_fate_data:make_signature(Hash), Rest}; 409 | parse_value([{typerep,_line} | Rest]) -> 410 | to_type(Rest). 411 | 412 | to_fun_def([{id, _, Name}, {'(', _} | Rest]) -> 413 | {ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest), 414 | {RetType, Rest3} = to_type(Rest2), 415 | {{Name, ArgsType, RetType}, Rest3}. 416 | 417 | to_arg_types([{')', _} | Rest]) -> {[], Rest}; 418 | to_arg_types(Tokens) -> 419 | case to_type(Tokens) of 420 | {Type, [{',', _} | Rest]} -> 421 | {MoreTypes, Rest2} = to_arg_types(Rest), 422 | {[Type|MoreTypes], Rest2}; 423 | {Type, [{')', _} | Rest]} -> 424 | {[Type], Rest} 425 | end. 426 | 427 | 428 | %% Type handling 429 | 430 | to_type([{id, _, "integer"} | Rest]) -> {integer, Rest}; 431 | to_type([{id, _, "boolean"} | Rest]) -> {boolean, Rest}; 432 | to_type([{id, _, "string"} | Rest]) -> {string, Rest}; 433 | to_type([{id, _, "address"} | Rest]) -> {address, Rest}; 434 | to_type([{id, _, "contract"} | Rest]) -> {contract, Rest}; 435 | to_type([{id, _, "oracle"} | Rest]) -> {oracle, Rest}; 436 | to_type([{id, _, "oracle_query"} | Rest]) -> {oracle_query, Rest}; 437 | to_type([{id, _, "name"} | Rest]) -> {name, Rest}; 438 | to_type([{id, _, "channel"} | Rest]) -> {channel, Rest}; 439 | to_type([{id, _, "hash"} | Rest]) -> {hash, Rest}; 440 | to_type([{id, _, "signature"} | Rest]) -> {signature, Rest}; 441 | to_type([{id, _, "bits"} | Rest]) -> {bits, Rest}; 442 | to_type([{'{', _}, {id, _, "list"}, {',', _} | Rest]) -> 443 | %% TODO: Error handling 444 | {ListType, [{'}', _}| Rest2]} = to_type(Rest), 445 | {{list, ListType}, Rest2}; 446 | to_type([{'{', _}, {id, _, "tuple"}, {',', _}, {'[', _} | Rest]) -> 447 | %% TODO: Error handling 448 | {ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest), 449 | {{tuple, ElementTypes}, Rest2}; 450 | to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) -> 451 | %% TODO: Error handling 452 | {KeyType, [{',', _}| Rest2]} = to_type(Rest), 453 | {ValueType, [{'}', _}| Rest3]} = to_type(Rest2), 454 | {{map, KeyType, ValueType}, Rest3}; 455 | to_type([{'{', _}, {id, _, "bytes"}, {',', _}, {int, _, Size}, {'}', _} | Rest]) -> 456 | %% TODO: Error handling 457 | {{bytes, Size}, Rest}; 458 | to_type([{'{', _} 459 | , {id, _, "variant"} 460 | , {',', _} 461 | , {'[', _} 462 | | Rest]) -> 463 | {ElementTypes, [{'}', _}| Rest2]} = to_list_of_types(Rest), 464 | {{variant, ElementTypes}, Rest2}. 465 | 466 | 467 | to_list_of_types([{']', _} | Rest]) -> {[], Rest}; 468 | to_list_of_types(Tokens) -> 469 | case to_type(Tokens) of 470 | {Type, [{',', _} | Rest]} -> 471 | {MoreTypes, Rest2} = to_list_of_types(Rest), 472 | {[Type|MoreTypes], Rest2}; 473 | {Type, [{']', _} | Rest]} -> 474 | {[Type], Rest} 475 | end. 476 | 477 | 478 | %% ------------------------------------------------------------------- 479 | %% Helper functions 480 | %% ------------------------------------------------------------------- 481 | 482 | %% State handling 483 | 484 | insert_fun(none, [], Env) -> Env; 485 | insert_fun({NameString, ArgType, RetType}, Code, #{ fate_code := FateCode 486 | , functions := Funs} = Env) -> 487 | Name = list_to_binary(NameString), 488 | {FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode), 489 | BodyByteCode = aeb_fate_code:serialize_code(lists:reverse(Code)), 490 | SigByteCode = aeb_fate_code:serialize_signature({ArgType, RetType}), 491 | FunByteCode = [?FUNCTION, Id, aeb_fate_encoding:serialize(0), SigByteCode, BodyByteCode], 492 | Env#{ functions => Funs#{ Id => FunByteCode } 493 | , fate_code => FateCode1}. 494 | 495 | insert_symbol(Name, #{ fate_code := FateCode } = Env) -> 496 | {FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode), 497 | { Env#{ fate_code => FateCode1 } 498 | , Id}. 499 | 500 | insert_annotation(comment, Line, Comment, #{ fate_code := FateCode } = Env) -> 501 | FateCode1 = aeb_fate_code:insert_annotation(comment, Line, Comment, FateCode), 502 | Env#{ fate_code => FateCode1}. 503 | 504 | mk_hash(Id) -> 505 | %% Use first 4 bytes of blake hash 506 | {ok, <> } = eblake2:blake2b(?HASH_BYTES, list_to_binary(Id)), 507 | <>. 508 | 509 | %% Handle annotations 510 | 511 | get_comments(Annotations) -> 512 | [ {Line, Comment} || 513 | {?FATE_TUPLE({?FATE_STRING_VALUE("comment"), Line}), 514 | ?FATE_STRING_VALUE(Comment)} <- maps:to_list(Annotations)]. 515 | 516 | %% Symbol table handling 517 | 518 | lookup(Name, Symbols) -> 519 | maps:get(Name, Symbols, Name). 520 | -------------------------------------------------------------------------------- /src/aeb_fate_asm_scan.template: -------------------------------------------------------------------------------- 1 | %%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- 2 | %%%------------------------------------------------------------------- 3 | %%% @copyright (C) 2019, aeternity Anstalt 4 | %%% @doc 5 | %%% Handling FATE code. 6 | %%% @end 7 | %%% ###REPLACEWITHNOTE### 8 | %%%------------------------------------------------------------------- 9 | 10 | Definitions. 11 | DIGIT = [0-9] 12 | HEXDIGIT = [0-9a-fA-F] 13 | LOWER = [a-z_] 14 | UPPER = [A-Z] 15 | BASE58 = [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz] 16 | BASE64 = [A-Za-z0-9+/=] 17 | INT = {DIGIT}+ 18 | HEX = 0x{HEXDIGIT}+ 19 | OBJ_PFX = (ak|ct|ok|oq|ch|sg) 20 | OBJECT = @{OBJ_PFX}_{BASE58}+ 21 | CODE = @cb_{BASE64}+ 22 | BYTES = #{BASE64}+ 23 | WS = [\000-\s] 24 | ID = {LOWER}[a-zA-Z0-9_]* 25 | STRING = "[^"]*" 26 | BITS = (\!)?\<[\s01]*\> 27 | 28 | Rules. 29 | arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. 30 | var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}. 31 | a : {token, {stack, TokenLine}}. 32 | 33 | true : {token, {boolean, TokenLine, true}}. 34 | false : {token, {boolean, TokenLine, false}}. 35 | 36 | %% ###REPLACEWITHOPTOKENS### 37 | 38 | FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. 39 | 40 | {BYTES} : 41 | {token, {bytes, TokenLine, parse_hash(TokenChars)}}. 42 | {CODE} : 43 | {token, {contract_bytearray, TokenLine, parse_contract_bytearray(TokenChars)}}. 44 | {OBJECT} : 45 | {token, {object, TokenLine, parse_object(TokenChars)}}. 46 | {ID} : 47 | {token, {id, TokenLine, TokenChars}}. 48 | {HEX} : 49 | {token, {int, TokenLine, parse_hex(TokenChars)}}. 50 | {INT} : 51 | {token, {int, TokenLine, parse_int(TokenChars)}}. 52 | -{INT} : 53 | {token, {int, TokenLine, parse_int(TokenChars)}}. 54 | 55 | %% Due to the definition of STRING the tokens start and end with a quote ". 56 | {STRING} : 57 | {token, {string, TokenLine, unicode:characters_to_binary( 58 | lists:sublist(TokenChars, 2, length(TokenChars) - 2))}}. 59 | {BITS} : 60 | {token, {bits, TokenLine, bits(TokenChars)}}. 61 | 62 | 63 | %% Symbols 64 | \-\> : {token, {to, TokenLine}}. 65 | \: : {token, {to, TokenLine}}. 66 | 67 | \=\> : {token, {arrow, TokenLine}}. 68 | \(\| : {token, {start_variant, TokenLine}}. 69 | \|\) : {token, {end_variant, TokenLine}}. 70 | 71 | , : {token, {',', TokenLine}}. 72 | \( : {token, {'(', TokenLine}}. 73 | \) : {token, {')', TokenLine}}. 74 | \[ : {token, {'[', TokenLine}}. 75 | \] : {token, {']', TokenLine}}. 76 | \{ : {token, {'{', TokenLine}}. 77 | \} : {token, {'}', TokenLine}}. 78 | \| : {token, {'|', TokenLine}}. 79 | \' : {token, {typerep, TokenLine}}. 80 | 81 | ;;.* : 82 | {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. 83 | 84 | \. : skip_token. 85 | 86 | 87 | %% Whitespace ignore 88 | {WS} : skip_token. 89 | 90 | %% Comments (TODO: nested comments) 91 | 92 | 93 | . : {error, "Unexpected token: " ++ TokenChars}. 94 | 95 | Erlang code. 96 | 97 | -export([scan/1]). 98 | 99 | -dialyzer({nowarn_function, yyrev/2}). 100 | 101 | -ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). 102 | 103 | -include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). 104 | 105 | 106 | parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). 107 | 108 | parse_int(Chars) -> list_to_integer(Chars). 109 | 110 | parse_arg("arg" ++ N) -> list_to_integer(N). 111 | parse_var("var" ++ N) -> list_to_integer(N). 112 | 113 | 114 | parse_hash("#" ++ Chars) -> 115 | base64:decode(Chars). 116 | 117 | parse_contract_bytearray("@" ++ Chars) -> 118 | case aeser_api_encoder:decode(unicode:characters_to_binary(Chars)) of 119 | {contract_bytearray, Bin} -> Bin 120 | end. 121 | 122 | parse_object([_|Chars]) -> 123 | case aeser_api_encoder:decode(unicode:characters_to_binary(Chars)) of 124 | {account_pubkey, Bin} -> {address, Bin}; 125 | {contract_pubkey, Bin} -> {contract, Bin}; 126 | {oracle_pubkey, Bin} -> {oracle, Bin}; 127 | {oracle_query_id, Bin} -> {oracle_query, Bin}; 128 | {channel, Bin} -> {channel, Bin}; 129 | {signature, Bin} -> {signature, Bin} 130 | end. 131 | 132 | scan(S) -> 133 | string(S). 134 | 135 | drop_prefix(C, [C|Rest]) -> 136 | drop_prefix(C, Rest); 137 | drop_prefix(_, Tail) -> Tail. 138 | 139 | bits([$!, $< | Rest]) -> 140 | bits(Rest, -1); 141 | bits([$< | Rest]) -> 142 | bits(Rest, 0). 143 | 144 | bits([$> |_Rest], Acc) -> Acc; 145 | bits([$0 | Rest], Acc) -> bits(Rest, Acc bsl 1); 146 | bits([$1 | Rest], Acc) -> bits(Rest, (Acc bsl 1) bor 1); 147 | bits([$ | Rest], Acc) -> bits(Rest, Acc). 148 | -------------------------------------------------------------------------------- /src/aeb_fate_code.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2019, Aeternity Anstalt 3 | %%% @doc 4 | %%% ADT for fate byte code/fate code 5 | %%% @end 6 | %%% 7 | %%%------------------------------------------------------------------- 8 | -module(aeb_fate_code). 9 | 10 | -export([ annotations/1 11 | , deserialize/1 12 | , functions/1 13 | , insert_annotation/4 14 | , insert_fun/5 15 | , insert_symbol/2 16 | , new/0 17 | , serialize/1 18 | , serialize/2 19 | , serialize/3 20 | , serialize_code/1 21 | , serialize_signature/1 22 | , strip_init_function/1 23 | , symbol_identifier/1 24 | , symbols/1 25 | ]). 26 | 27 | -include("../include/aeb_fate_opcodes.hrl"). 28 | -include("../include/aeb_fate_data.hrl"). 29 | 30 | -export([ update_annotations/2 31 | , update_functions/2 32 | , update_symbols/2]). 33 | 34 | -record(fcode, { functions = #{} :: map() 35 | , symbols = #{} :: map() 36 | , annotations = #{} :: map() 37 | }). 38 | 39 | -define(HASH_BYTES, 32). 40 | 41 | -type fcode() :: #fcode{}. 42 | -export_type([fcode/0]). 43 | 44 | %%%=================================================================== 45 | %%% API 46 | %%%=================================================================== 47 | 48 | new() -> 49 | #fcode{}. 50 | 51 | annotations(#fcode{ annotations = As }) -> 52 | As. 53 | 54 | functions(#fcode{ functions = Fs }) -> 55 | Fs. 56 | 57 | symbols(#fcode{ symbols = Ss}) -> 58 | Ss. 59 | 60 | update_annotations(#fcode{ annotations = As } = FCode, Anns) -> 61 | FCode#fcode{ annotations = maps:merge(As, Anns) }. 62 | 63 | update_functions(#fcode{ functions = Fs } = FCode, Funs) -> 64 | FCode#fcode{ functions = maps:merge(Fs, Funs) }. 65 | 66 | update_symbols(#fcode{ symbols = Ss } = FCode, Symbs) -> 67 | FCode#fcode{ symbols = maps:merge(Ss, Symbs) }. 68 | 69 | symbol_identifier(Bin) -> 70 | %% First 4 bytes of blake hash 71 | {ok, <> } = eblake2:blake2b(?HASH_BYTES, Bin), 72 | X. 73 | 74 | insert_fun(Name, Attrs, {ArgType, RetType}, #{} = BBs, FCode) -> 75 | {F1, ID} = insert_symbol(Name, FCode), 76 | update_functions(F1, #{ID => {Attrs, {ArgType, RetType}, BBs}}). 77 | 78 | insert_symbol(Name, #fcode{ symbols = Syms } = F) -> 79 | ID = symbol_identifier(Name), 80 | case maps:find(ID, Syms) of 81 | {ok, Name} -> 82 | {F, ID}; 83 | {ok, X} -> 84 | error({two_symbols_with_same_hash, Name, X}); 85 | error -> 86 | {update_symbols(F, #{ID => Name}), ID} 87 | end. 88 | 89 | insert_annotation(comment =_Type, Line, Comment, FCode) -> 90 | Key = aeb_fate_data:make_tuple({aeb_fate_data:make_string("comment"), Line}), 91 | Value = aeb_fate_data:make_string(Comment), 92 | update_annotations(FCode, #{ Key => Value }). 93 | 94 | strip_init_function(#fcode{ functions = Funs, 95 | symbols = Syms } = FCode) -> 96 | Funs1 = maps:remove(?FATE_INIT_ID, Funs), 97 | Syms1 = maps:remove(?FATE_INIT_ID, Syms), 98 | FCode#fcode{ functions = Funs1, symbols = Syms1 }. 99 | 100 | %%%=================================================================== 101 | %%% Serialization 102 | %%%=================================================================== 103 | 104 | serialize(#fcode{} = F) -> 105 | serialize(F, []). 106 | 107 | serialize(#fcode{} = F, Options) -> 108 | sanity_check(F), 109 | serialize(F, serialize_functions(F), Options). 110 | 111 | serialize(#fcode{} = F, Functions, Options) -> 112 | SymbolTable = serialize_symbol_table(F), 113 | Annotatations = serialize_annotations(F), 114 | ByteCode = << (aeser_rlp:encode(Functions))/binary, 115 | (aeser_rlp:encode(SymbolTable))/binary, 116 | (aeser_rlp:encode(Annotatations))/binary 117 | >>, 118 | 119 | case proplists:lookup(pp_hex_string, Options) of 120 | {pp_hex_string, true} -> 121 | io:format("Code: ~s~n",[to_hexstring(Functions)]); 122 | none -> 123 | ok 124 | end, 125 | ByteCode. 126 | 127 | to_hexstring(ByteList) -> 128 | "0x" ++ lists:flatten( 129 | [io_lib:format("~2.16.0b", [X]) 130 | || X <- ByteList]). 131 | 132 | 133 | serialize_functions(#fcode{ functions = Functions }) -> 134 | %% Sort the functions on name to get a canonical serialisation. 135 | iolist_to_binary( 136 | lists:foldr(fun({Id, {Attrs, Sig, C}}, Acc) -> 137 | [[?FUNCTION, Id, serialize_attributes(Attrs), serialize_signature(Sig), serialize_bbs(C)] | Acc] 138 | end, [], lists:sort(maps:to_list(Functions)))). 139 | 140 | serialize_attributes(Attrs) -> 141 | AttrVal = lists:sum([ attr_value(Attr) || Attr <- Attrs ]), 142 | aeb_fate_encoding:serialize(?MAKE_FATE_INTEGER(AttrVal)). 143 | 144 | attr_value(private) -> 1; 145 | attr_value(payable) -> 2. 146 | 147 | serialize_signature({Args, RetType}) -> 148 | [aeb_fate_encoding:serialize_type({tuple, Args}) | 149 | aeb_fate_encoding:serialize_type(RetType)]. 150 | 151 | serialize_symbol_table(#fcode{ symbols = Symbols }) -> 152 | aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)). 153 | 154 | serialize_annotations(#fcode{ annotations = Annotations }) -> 155 | aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)). 156 | 157 | serialize_bbs(#{} = BBs) -> 158 | serialize_bbs(BBs, 0, []). 159 | 160 | serialize_bbs(BBs, N, Acc) -> 161 | case maps:get(N, BBs, none) of 162 | none -> lists:reverse(Acc); 163 | BB -> serialize_bbs(BBs, N + 1, [serialize_bb(BB, [])|Acc]) 164 | end. 165 | 166 | serialize_bb([Op], Acc) -> 167 | lists:reverse([serialize_op(Op)|Acc]); 168 | serialize_bb([Op|Rest], Acc) -> 169 | serialize_bb(Rest, [serialize_op(Op)|Acc]). 170 | 171 | serialize_op(Op) -> 172 | [Mnemonic|Args] = 173 | case is_tuple(Op) of 174 | true -> tuple_to_list(Op); 175 | false -> [Op] 176 | end, 177 | [aeb_fate_opcodes:m_to_op(Mnemonic) | serialize_code(Args)]. 178 | 179 | sanity_check(#fcode{ functions = Funs }) -> 180 | _ = [ case Def of 181 | {_, _, BBs} when byte_size(Id) == 4 -> sanity_check_bbs(BBs); 182 | _ -> error({illegal_function_id, Id}) 183 | end || {Id, Def} <- maps:to_list(Funs) ], 184 | ok. 185 | 186 | sanity_check_bbs(#{} = BBs) -> 187 | sanity_check_bbs(BBs, 0). 188 | 189 | sanity_check_bbs(BBs, N) -> 190 | case maps:get(N, BBs, none) of 191 | none -> 192 | %% Assert that the BBs were contiguous 193 | case maps:size(BBs) =:= N of 194 | true -> ok; 195 | false -> error({not_contiguous_labels, lists:sort(maps:keys(BBs))}) 196 | end; 197 | [] -> 198 | error({empty_code_block, N}); 199 | BB -> 200 | sanity_check_bb(BB), 201 | sanity_check_bbs(BBs, N + 1) 202 | end. 203 | 204 | sanity_check_bb([Op]) -> 205 | sanity_check_op(true, Op); 206 | sanity_check_bb([Op|Rest]) -> 207 | sanity_check_op(false, Op), 208 | sanity_check_bb(Rest). 209 | 210 | sanity_check_op(IsLast, Op) -> 211 | [Mnemonic|Args] = 212 | case is_tuple(Op) of 213 | true -> tuple_to_list(Op); 214 | false -> [Op] 215 | end, 216 | safe_sanity_check(IsLast, aeb_fate_opcodes:m_to_op(Mnemonic), Args). 217 | 218 | safe_sanity_check(IsLast, Op, Args) -> 219 | case length(Args) == aeb_fate_opcodes:args(Op) of 220 | true -> 221 | case IsLast == aeb_fate_opcodes:end_bb(Op) of 222 | true -> ok; 223 | false -> error({wrong_opcode_in_bb, Op}) 224 | end; 225 | false -> error({wrong_nr_args_opcode, Op}) 226 | end. 227 | 228 | 229 | %% Argument encoding 230 | %% Argument Specification Byte 231 | %% bitpos: 6 4 2 0 232 | %% xx xx xx xx 233 | %% Arg3 Arg2 Arg1 Arg0 234 | %% For 5-8 args another Argument Spec Byte is used 235 | %% bitpos: 6 4 2 0 | 6 4 2 0 236 | %% xx xx xx xx | xx xx xx xx 237 | %% Arg7 Arg6 Arg5 Arg4 | Arg3 Arg2 Arg1 Arg0 238 | %% Bit pattern 239 | %% 00 : stack/unused (depending on instruction) 240 | %% 01 : argN 241 | %% 10 : varN 242 | %% 11 : immediate 243 | 244 | serialize_code([{_,_}|_] = List ) -> 245 | %% Take out the full argument list. 246 | {Args, Rest} = lists:splitwith(fun({_, _}) -> true; (_) -> false end, List), 247 | %% Create the appropriate number of modifier bytes. 248 | Mods = << <<(modifier_bits(Type, X)):2>> || {Type, X} <- pad_args(lists:reverse(Args)) >>, 249 | case Mods of 250 | <> -> 251 | [M1, M2 | [serialize_data(Type, Arg) || {Type, Arg} <- Args, Type =/= stack]] ++ 252 | serialize_code(Rest); 253 | <> -> 254 | [M1 | [serialize_data(Type, Arg) || {Type, Arg} <- Args, Type =/= stack]] ++ 255 | serialize_code(Rest) 256 | end; 257 | serialize_code([Op|Rest]) -> 258 | [Op|serialize_code(Rest)]; 259 | serialize_code([]) -> 260 | []. 261 | 262 | pad_args(List) -> 263 | case length(List) of 264 | 0 -> List; 265 | N when N =< 4 -> 266 | lists:duplicate(4 - N, {stack, 0}) ++ List; 267 | N when N =< 8 -> 268 | lists:duplicate(8 - N, {stack, 0}) ++ List 269 | end. 270 | 271 | serialize_data(_, Data) -> 272 | aeb_fate_encoding:serialize(Data). 273 | 274 | %% 00 : stack/unused (depending on instruction) 275 | %% 01 : argN 276 | %% 10 : varN 277 | %% 11 : immediate 278 | modifier_bits(immediate, _) -> 2#11; 279 | modifier_bits(var, _) -> 2#10; 280 | modifier_bits(arg, _) -> 2#01; 281 | modifier_bits(stack, 0) -> 2#00; 282 | modifier_bits(Type, X) -> error({illegal_argument, Type, X}). 283 | 284 | bits_to_modifier(2#11) -> immediate; 285 | bits_to_modifier(2#10) -> var; 286 | bits_to_modifier(2#01) -> arg; 287 | bits_to_modifier(2#00) -> stack. 288 | 289 | %%%=================================================================== 290 | %%% Deserialization 291 | %%%=================================================================== 292 | 293 | deserialize(Bytes) -> 294 | {ByteCode, Rest1} = aeser_rlp:decode_one(Bytes), 295 | {SymbolTable, Rest2} = aeser_rlp:decode_one(Rest1), 296 | {Annotations, <<>>} = aeser_rlp:decode_one(Rest2), 297 | 298 | Env = #{ function => none 299 | , bb => 0 300 | , current_bb_code => [] 301 | , functions => #{} 302 | , code => #{} 303 | }, 304 | Fcode = 305 | #fcode{ functions = deserialize_functions(ByteCode, Env) 306 | , annotations = deserialize_annotations(Annotations) 307 | , symbols = deserialize_symbols(SymbolTable) 308 | }, 309 | sanity_check(Fcode), 310 | Fcode. 311 | 312 | 313 | deserialize_functions(<>, 314 | #{ function := none 315 | , bb := 0 316 | , current_bb_code := [] 317 | } = Env) -> 318 | {Attrs, Rest2} = deserialize_attributes(Rest), 319 | {Sig, Rest3} = deserialize_signature(Rest2), 320 | Env2 = Env#{function => {<>, Attrs, Sig}}, 321 | deserialize_functions(Rest3, Env2); 322 | deserialize_functions(<>, 323 | #{ function := {F, Attrs, Sig} 324 | , bb := BB 325 | , current_bb_code := Code 326 | , code := Program 327 | , functions := Funs} = Env) -> 328 | {NewAttrs, Rest2} = deserialize_attributes(Rest), 329 | {NewSig, Rest3} = deserialize_signature(Rest2), 330 | case Code of 331 | [] -> 332 | Env2 = Env#{ bb => 0 333 | , current_bb_code => [] 334 | , function => {<>, NewAttrs, NewSig} 335 | , code => #{} 336 | , functions => Funs#{F => {Attrs, Sig, Program}}}, 337 | deserialize_functions(Rest3, Env2); 338 | _ -> 339 | Env2 = Env#{ bb => 0 340 | , current_bb_code => [] 341 | , function => {<>, NewAttrs, NewSig} 342 | , code => #{} 343 | , functions => 344 | Funs#{F => {Attrs, Sig, 345 | Program#{ BB => lists:reverse(Code)}}}}, 346 | deserialize_functions(Rest3, Env2) 347 | end; 348 | deserialize_functions(<<_Op:8, _Rest/binary>>, 349 | #{ function := none }) -> 350 | error({code_without_function}); 351 | deserialize_functions(<>, 352 | #{ bb := BB 353 | , current_bb_code := Code 354 | , code := Program} = Env) -> 355 | {Rest2, OpCode} = deserialize_op(Op, Rest, Code), 356 | case aeb_fate_opcodes:end_bb(Op) of 357 | true -> 358 | deserialize_functions(Rest2, Env#{ bb => BB+1 359 | , current_bb_code => [] 360 | , code => Program#{BB => 361 | lists:reverse(OpCode)}}); 362 | false -> 363 | deserialize_functions(Rest2, Env#{ current_bb_code => OpCode}) 364 | end; 365 | deserialize_functions(<<>>, #{ function := none 366 | , functions := Funs}) -> 367 | Funs; 368 | deserialize_functions(<<>>, #{ function := {F, Attrs, Sig} 369 | , bb := BB 370 | , current_bb_code := Code 371 | , code := Program 372 | , functions := Funs}) -> 373 | FunctionCode = 374 | case Code of 375 | [] -> Program; 376 | _ -> Program#{ BB => lists:reverse(Code)} 377 | end, 378 | Funs#{F => {Attrs, Sig, FunctionCode}}. 379 | 380 | deserialize_op(Op, Rest, Code) -> 381 | OpName = aeb_fate_opcodes:mnemonic(Op), 382 | case aeb_fate_opcodes:args(Op) of 383 | 0 -> 384 | {Rest, [OpName | Code]}; 385 | N -> 386 | {Args, Rest1} = deserialize_n_args(N, Rest), 387 | {Rest1, [list_to_tuple([OpName|Args])|Code]} 388 | end. 389 | 390 | deserialize_n_args(N, <>) when N =< 4 -> 391 | {ArgMods, Zeros} = lists:split(N, [M0, M1, M2, M3]), 392 | assert_zero(Zeros), 393 | lists:mapfoldl(fun(M, Acc) -> 394 | case bits_to_modifier(M) of 395 | stack -> 396 | {{stack, 0}, Acc}; 397 | Modifier -> 398 | {Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc), 399 | {{Modifier, Arg}, Acc2} 400 | end 401 | end, Rest, ArgMods); 402 | deserialize_n_args(N, <>) when N =< 8 -> 404 | {ArgMods, Zeros} = lists:split(N, [M0, M1, M2, M3, M4, M5, M6, M7]), 405 | assert_zero(Zeros), 406 | lists:mapfoldl(fun(M, Acc) -> 407 | case bits_to_modifier(M) of 408 | stack -> 409 | {{stack, 0}, Acc}; 410 | Modifier -> 411 | {Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc), 412 | {{Modifier, Arg}, Acc2} 413 | end 414 | end, Rest, ArgMods). 415 | 416 | deserialize_attributes(Binary) -> 417 | {AttrVal, Rest} = aeb_fate_encoding:deserialize_one(Binary), 418 | Attrs = [ attr(AVal) || AVal <- attr_vals(1, AttrVal) ], 419 | {lists:sort(Attrs), Rest}. 420 | 421 | attr_vals(_, 0) -> []; 422 | attr_vals(X, N) when N rem 2 == 0 -> attr_vals(X + 1, N div 2); 423 | attr_vals(X, N) -> [X | attr_vals(X + 1, N div 2)]. 424 | 425 | attr(1) -> private; 426 | attr(2) -> payable. 427 | 428 | deserialize_signature(Binary) -> 429 | {{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary), 430 | {RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest), 431 | {{Args, RetType}, Rest2}. 432 | 433 | deserialize_symbols(Table) -> 434 | ?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table), 435 | SymbolTable. 436 | 437 | deserialize_annotations(AnnotationsBin) -> 438 | ?FATE_MAP_VALUE(Annotations) = aeb_fate_encoding:deserialize(AnnotationsBin), 439 | Annotations. 440 | 441 | assert_zero([]) -> 442 | true; 443 | assert_zero([0|Rest]) -> 444 | assert_zero(Rest); 445 | assert_zero([_|_]) -> 446 | error(argument_defined_outside_range). 447 | -------------------------------------------------------------------------------- /src/aeb_fate_data.erl: -------------------------------------------------------------------------------- 1 | %% FATE data representation. 2 | %% 3 | -include("aeb_fate_data.hrl"). 4 | 5 | -module(aeb_fate_data). 6 | 7 | -type fate_integer() :: ?FATE_INTEGER_T. 8 | -type fate_boolean() :: ?FATE_BOOLEAN_T. 9 | -type fate_nil() :: ?FATE_NIL_T. 10 | -type fate_list() :: ?FATE_LIST_T. 11 | -type fate_unit() :: ?FATE_UNIT_T. 12 | -type fate_map() :: ?FATE_MAP_T. 13 | -type fate_store_map() :: ?FATE_STORE_MAP_T. 14 | -type fate_string() :: ?FATE_STRING_T. 15 | -type fate_address() :: ?FATE_ADDRESS_T. 16 | -type fate_hash() :: ?FATE_BYTES_T(32). 17 | -type fate_signature() :: ?FATE_BYTES_T(64). 18 | -type fate_contract() :: ?FATE_CONTRACT_T. 19 | -type fate_oracle() :: ?FATE_ORACLE_T. 20 | -type fate_oracle_q() :: ?FATE_ORACLE_Q_T. 21 | -type fate_channel() :: ?FATE_CHANNEL_T. 22 | -type fate_variant() :: ?FATE_VARIANT_T. 23 | -type fate_tuple() :: ?FATE_TUPLE_T. 24 | -type fate_bits() :: ?FATE_BITS_T. 25 | -type fate_typerep() :: ?FATE_TYPEREP_T. 26 | -type fate_contract_bytearray() :: ?FATE_CONTRACT_BYTEARRAY_T. 27 | 28 | -type fate_type_type() :: integer 29 | | boolean 30 | | {list, fate_type_type()} 31 | | {map, fate_type_type(), fate_type_type()} 32 | | {tuple, [fate_type_type()]} 33 | | address 34 | | hash 35 | | signature 36 | | contract 37 | | oracle 38 | | oracle_query 39 | | channel 40 | | bits 41 | | string 42 | | {variant, [fate_type_type()]} 43 | | contract_bytearray. 44 | 45 | 46 | -type fate_type() :: 47 | fate_boolean() 48 | | fate_integer() 49 | | fate_nil() 50 | | fate_list() 51 | | fate_unit() 52 | | fate_tuple() 53 | | fate_string() 54 | | fate_address() 55 | | fate_hash() 56 | | fate_signature() 57 | | fate_contract() 58 | | fate_oracle() 59 | | fate_oracle_q() 60 | | fate_channel() 61 | | fate_variant() 62 | | fate_map() 63 | | fate_bits() 64 | | fate_typerep() 65 | | fate_contract_bytearray(). 66 | 67 | -export_type([fate_type/0 68 | , fate_boolean/0 69 | , fate_integer/0 70 | , fate_nil/0 71 | , fate_list/0 72 | , fate_unit/0 73 | , fate_tuple/0 74 | , fate_string/0 75 | , fate_address/0 76 | , fate_hash/0 77 | , fate_signature/0 78 | , fate_contract/0 79 | , fate_oracle/0 80 | , fate_channel/0 81 | , fate_variant/0 82 | , fate_map/0 83 | , fate_store_map/0 84 | , fate_bits/0 85 | , fate_type_type/0 86 | ]). 87 | 88 | -export([ make_integer/1 89 | , make_boolean/1 90 | , make_list/1 91 | , make_variant/3 92 | , make_tuple/1 93 | , make_string/1 94 | , make_map/1 95 | , make_store_map/1 96 | , make_store_map/2 97 | , make_address/1 98 | , make_bytes/1 99 | , make_hash/1 100 | , make_signature/1 101 | , make_contract/1 102 | , make_oracle/1 103 | , make_oracle_query/1 104 | , make_channel/1 105 | , make_bits/1 106 | , make_unit/0 107 | , make_typerep/1 108 | , make_contract_bytearray/1 109 | ]). 110 | -export([ 111 | elt/2 112 | , lt/2 113 | , format/1 114 | , ordinal/1]). 115 | 116 | 117 | make_boolean(true) -> ?FATE_TRUE; 118 | make_boolean(false) -> ?FATE_FALSE. 119 | make_list([]) -> ?FATE_NIL; 120 | make_list(L) -> ?MAKE_FATE_LIST(L). 121 | make_unit() -> ?FATE_UNIT. 122 | make_tuple(T) -> ?FATE_TUPLE(T). 123 | make_map(M) -> ?MAKE_FATE_MAP(M). 124 | make_store_map(Id) -> make_store_map(#{}, Id). 125 | make_store_map(Cache, Id) -> ?FATE_STORE_MAP(Cache, Id). 126 | make_address(X) -> ?FATE_ADDRESS(X). 127 | make_bytes(X) -> ?FATE_BYTES(X). 128 | make_hash(X) -> make_bytes(X). 129 | make_signature(X) -> make_bytes(X). 130 | make_contract(X) -> ?FATE_CONTRACT(X). 131 | make_oracle(X) -> ?FATE_ORACLE(X). 132 | make_oracle_query(X) -> ?FATE_ORACLE_Q(X). 133 | make_channel(X) -> ?FATE_CHANNEL(X). 134 | make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I). 135 | make_bits(I) when is_integer(I) -> ?FATE_BITS(I). 136 | make_string(S) when is_list(S) -> 137 | ?FATE_STRING(iolist_to_binary(S)); 138 | make_string(S) when is_binary(S) -> ?FATE_STRING(S). 139 | make_typerep(T) -> ?FATE_TYPEREP(T). 140 | make_contract_bytearray(B) -> ?FATE_CONTRACT_BYTEARRAY(B). 141 | 142 | %% Tag points to the selected variant (zero based) 143 | %% The arity of this variant is read from the list of provided arities 144 | %% and should match the size of the given tuple. 145 | make_variant(Arities, Tag, Values) -> 146 | Arities = [A || A <- Arities, is_integer(A), A < 256], 147 | Size = length(Arities), 148 | if is_integer(Tag) 149 | , 0 =< Tag 150 | , Tag < Size 151 | , is_tuple(Values) -> 152 | Arity = lists:nth(Tag+1, Arities), 153 | if size(Values) =:= Arity -> 154 | ?FATE_VARIANT(Arities, Tag, Values) 155 | end 156 | end. 157 | 158 | 159 | 160 | -spec format(fate_type()) -> iolist(). 161 | format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I)); 162 | format(?FATE_TRUE) -> "true"; 163 | format(?FATE_FALSE) -> "false"; 164 | format(?FATE_NIL) -> "[]"; 165 | format(L) when ?IS_FATE_LIST(L) -> format_list(?FATE_LIST_VALUE(L)); 166 | format(?FATE_UNIT) -> "()"; 167 | format(?FATE_TUPLE(T)) -> 168 | ["( ", lists:join(", ", [ format(E) || E <- erlang:tuple_to_list(T)]), " )"]; 169 | format(S) when ?IS_FATE_STRING(S) -> ["\"", S, "\""]; 170 | format(?FATE_BITS(B)) when B >= 0 -> 171 | ["<", format_bits(B, "") , ">"]; 172 | format(?FATE_BITS(B)) when B < 0 -> 173 | ["!< ", format_nbits(-B-1, "") , " >"]; 174 | format(?FATE_VARIANT(Arities, Tag, T)) -> 175 | ["(| ", 176 | lists:join("| ", 177 | [format_arities(Arities), 178 | integer_to_list(Tag) | 179 | [format(make_tuple(T))]]), 180 | " |)"]; 181 | format(M) when ?IS_FATE_MAP(M) -> 182 | ["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"]; 183 | format(?FATE_BYTES(X)) -> ["#", base64:encode(X)]; 184 | format(?FATE_ADDRESS(X)) -> 185 | ["@", aeser_api_encoder:encode(account_pubkey, X)]; 186 | format(?FATE_CONTRACT(X)) -> 187 | ["@", aeser_api_encoder:encode(contract_pubkey, X)]; 188 | format(?FATE_ORACLE(X)) -> 189 | ["@", aeser_api_encoder:encode(oracle_pubkey, X)]; 190 | format(?FATE_ORACLE_Q(X)) -> 191 | ["@", aeser_api_encoder:encode(oracle_query_id, X)]; 192 | format(?FATE_CHANNEL(X)) -> 193 | ["@", aeser_api_encoder:encode(channel, X)]; 194 | format(?FATE_TYPEREP(X)) -> 195 | ["'", io_lib:format("~p", [X])]; 196 | format(?FATE_CONTRACT_BYTEARRAY(B)) -> 197 | ["@", aeser_api_encoder:encode(contract_bytearray, B)]; 198 | format(V) -> exit({not_a_fate_type, V}). 199 | 200 | format_bits(0, Acc) -> Acc; 201 | format_bits(N, Acc) -> 202 | Bit = $0 + (N band 1), 203 | format_bits(N bsr 1, [Bit|Acc]). 204 | 205 | format_nbits(0, Acc) -> Acc; 206 | format_nbits(N, Acc) -> 207 | Bit = $1 - (N band 1), 208 | format_nbits(N bsr 1, [Bit|Acc]). 209 | 210 | format_arities(Arities) -> 211 | ["[ ", lists:join(", ", [integer_to_list(E) || E <- Arities]), " ]"]. 212 | 213 | format_list(List) -> 214 | ["[ ", lists:join(", ", [format(E) || E <- List]), " ]"]. 215 | 216 | format_kvs(List) -> 217 | lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]). 218 | 219 | 220 | %% Total order of FATE terms. 221 | %% Integers < Booleans < Address < Channel < Contract < Oracle 222 | %% < Hash < Signature < Bits < String < Tuple < Map < List < Variant 223 | %% < Oracle query < FATE code 224 | -define(ORD_INTEGER , 0). 225 | -define(ORD_BOOLEAN , 1). 226 | -define(ORD_ADDRESS , 2). 227 | -define(ORD_CHANNEL , 3). 228 | -define(ORD_CONTRACT , 4). 229 | -define(ORD_ORACLE , 5). 230 | -define(ORD_BYTES , 6). 231 | -define(ORD_BITS , 7). 232 | -define(ORD_STRING , 8). 233 | -define(ORD_TUPLE , 9). 234 | -define(ORD_MAP , 10). 235 | -define(ORD_LIST , 11). 236 | -define(ORD_VARIANT , 12). 237 | -define(ORD_ORACLE_Q , 13). 238 | -define(ORD_CONTRACT_BYTEARRAY , 14). 239 | 240 | -spec ordinal(fate_type()) -> integer(). 241 | ordinal(T) when ?IS_FATE_INTEGER(T) -> ?ORD_INTEGER; 242 | ordinal(T) when ?IS_FATE_BOOLEAN(T) -> ?ORD_BOOLEAN; 243 | ordinal(T) when ?IS_FATE_ADDRESS(T) -> ?ORD_ADDRESS; 244 | ordinal(T) when ?IS_FATE_CHANNEL(T) -> ?ORD_CHANNEL; 245 | ordinal(T) when ?IS_FATE_CONTRACT(T) -> ?ORD_CONTRACT; 246 | ordinal(T) when ?IS_FATE_ORACLE(T) -> ?ORD_ORACLE; 247 | ordinal(T) when ?IS_FATE_BYTES(T) -> ?ORD_BYTES; 248 | ordinal(T) when ?IS_FATE_BITS(T) -> ?ORD_BITS; 249 | ordinal(T) when ?IS_FATE_STRING(T) -> ?ORD_STRING; 250 | ordinal(T) when ?IS_FATE_TUPLE(T) -> ?ORD_TUPLE; 251 | ordinal(T) when ?IS_FATE_MAP(T) -> ?ORD_MAP; 252 | ordinal(T) when ?IS_FATE_LIST(T) -> ?ORD_LIST; 253 | ordinal(T) when ?IS_FATE_VARIANT(T) -> ?ORD_VARIANT; 254 | ordinal(T) when ?IS_FATE_ORACLE_Q(T) -> ?ORD_ORACLE_Q; 255 | ordinal(T) when ?IS_FATE_CONTRACT_BYTEARRAY(T) -> ?ORD_CONTRACT_BYTEARRAY. 256 | 257 | 258 | -spec lt(fate_type(), fate_type()) -> boolean(). 259 | lt(A, B) -> 260 | O1 = ordinal(A), 261 | O2 = ordinal(B), 262 | if O1 == O2 -> lt(O1, A, B); 263 | true -> O1 < O2 264 | end. 265 | 266 | %% Integers are ordered as usual. 267 | lt(?ORD_INTEGER, A, B) when ?IS_FATE_INTEGER(A), ?IS_FATE_INTEGER(B) -> 268 | ?FATE_INTEGER_VALUE(A) < ?FATE_INTEGER_VALUE(B); 269 | %% false is smaller than true (true also for erlang booleans). 270 | lt(?ORD_BOOLEAN, A, B) when ?IS_FATE_BOOLEAN(A), ?IS_FATE_BOOLEAN(B) -> 271 | ?FATE_BOOLEAN_VALUE(A) < ?FATE_BOOLEAN_VALUE(B); 272 | lt(?ORD_BITS, A, B) when ?IS_FATE_BITS(A), ?IS_FATE_BITS(B) -> 273 | BitsA = ?FATE_BITS_VALUE(A), 274 | BitsB = ?FATE_BITS_VALUE(B), 275 | if BitsA < 0 -> 276 | if BitsB < 0 -> BitsA < BitsB; 277 | true -> false 278 | end; 279 | BitsB < 0 -> 280 | true; 281 | true -> BitsA < BitsB 282 | end; 283 | lt(?ORD_TUPLE, ?FATE_TUPLE(A), ?FATE_TUPLE(B)) -> 284 | SizeA = tuple_size(A), 285 | SizeB = tuple_size(B), 286 | case SizeA - SizeB of 287 | 0 -> tuple_elements_lt(0, A, B, SizeA); 288 | N -> N < 0 289 | end; 290 | lt(?ORD_MAP, ?FATE_MAP_VALUE(A), ?FATE_MAP_VALUE(B)) -> 291 | SizeA = maps:size(A), 292 | SizeB = maps:size(B), 293 | case SizeA - SizeB of 294 | 0 -> maps_lt(A, B); 295 | N -> N < 0 296 | end; 297 | lt(?ORD_LIST, ?FATE_LIST_VALUE(_), ?FATE_LIST_VALUE([])) -> false; 298 | lt(?ORD_LIST, ?FATE_LIST_VALUE([]), ?FATE_LIST_VALUE(_)) -> true; 299 | lt(?ORD_LIST, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) -> 300 | if A == B -> lt(RA, RB); 301 | true -> lt(A, B) 302 | end; 303 | lt(?ORD_VARIANT, ?FATE_VARIANT(AritiesA, TagA, TA), 304 | ?FATE_VARIANT(AritiesB, TagB, TB)) -> 305 | if length(AritiesA) < length(AritiesB) -> true; 306 | length(AritiesA) > length(AritiesB) -> false; 307 | true -> 308 | % Compare element by element consistent with Erlang compare 309 | if AritiesA < AritiesB -> true; 310 | AritiesA > AritiesB -> false; 311 | true -> 312 | if TagA < TagB -> true; 313 | TagA > TagB -> false; 314 | true -> lt(make_tuple(TA), make_tuple(TB)) 315 | end 316 | end 317 | end; 318 | lt(?ORD_ADDRESS, ?FATE_ADDRESS(A), ?FATE_ADDRESS(B)) -> 319 | A < B; 320 | lt(?ORD_CHANNEL, ?FATE_CHANNEL(A), ?FATE_CHANNEL(B)) -> 321 | A < B; 322 | lt(?ORD_CONTRACT, ?FATE_CONTRACT(A), ?FATE_CONTRACT(B)) -> 323 | A < B; 324 | lt(?ORD_ORACLE, ?FATE_ORACLE(A), ?FATE_ORACLE(B)) -> 325 | A < B; 326 | lt(?ORD_ORACLE_Q, ?FATE_ORACLE_Q(A), ?FATE_ORACLE_Q(B)) -> 327 | A < B; 328 | lt(?ORD_STRING, ?FATE_STRING(A), ?FATE_STRING(B)) -> 329 | compare_bytes(A, B); 330 | lt(?ORD_BYTES, ?FATE_BYTES(A), ?FATE_BYTES(B)) -> 331 | compare_bytes(A, B); 332 | lt(?ORD_CONTRACT_BYTEARRAY, ?FATE_CONTRACT_BYTEARRAY(A), ?FATE_CONTRACT_BYTEARRAY(B)) -> 333 | compare_bytes(A, B). 334 | 335 | % Shorter comes first 336 | % On same length compare by first different bit 337 | compare_bytes(A, B) -> 338 | SizeA = byte_size(A), 339 | SizeB = byte_size(B), 340 | case SizeA - SizeB of 341 | 0 -> A < B; 342 | N -> N < 0 343 | end. 344 | 345 | tuple_elements_lt(N,_A,_B, N) -> 346 | false; 347 | tuple_elements_lt(N, A, B, Size) -> 348 | E = N + 1, 349 | EA = element(E, A), 350 | EB = element(E, B), 351 | if EA =:= EB -> tuple_elements_lt(E, A, B, Size); 352 | true -> lt(EA, EB) 353 | end. 354 | 355 | maps_lt(A, B) -> 356 | IA = maps_iterator(A), 357 | IB = maps_iterator(B), 358 | maps_i_lt(IA, IB). 359 | 360 | maps_i_lt(IA, IB) -> 361 | case {maps_next(IA), maps_next(IB)} of 362 | {none, none} -> false; 363 | {_, none} -> false; 364 | {none, _} -> true; 365 | {{KA1, VA1, IA2}, {KB1, VB1, IB2}} -> 366 | case lt(KA1, KB1) of 367 | true -> true; 368 | false -> 369 | case lt(KB1, KA1) of 370 | true -> false; 371 | false -> 372 | case lt(VA1, VB1) of 373 | true -> true; 374 | false -> 375 | case lt(VB1, VA1) of 376 | true -> false; 377 | false -> 378 | maps_i_lt(IA2, IB2) 379 | end 380 | end 381 | end 382 | end 383 | end. 384 | 385 | maps_iterator(M) -> lists:sort(fun ({K1,_}, {K2,_}) -> lt(K1, K2) end, maps:to_list(M)). 386 | maps_next([]) -> none; 387 | maps_next([{K,V}|Rest]) -> {K, V, Rest}. 388 | 389 | 390 | -spec elt(fate_type(), fate_type()) -> boolean(). 391 | elt(A, A) -> true; 392 | elt(A, B) -> 393 | R = lt(A, B), 394 | R. 395 | 396 | -------------------------------------------------------------------------------- /src/aeb_fate_generate_docs.erl: -------------------------------------------------------------------------------- 1 | -module(aeb_fate_generate_docs). 2 | 3 | -export([generate_documentation/2, generate_documentation/3]). 4 | 5 | -export( 6 | [ gen_protocol_opcodes_flags_and_gas/1 7 | , gen_protocol_description_of_operations/1 8 | , gen_protocol_opcodes/1 9 | ]). 10 | 11 | -define(LIMA_PROTOCOL_VSN, 4). 12 | -define(IRIS_PROTOCOL_VSN, 5). 13 | 14 | generate_documentation(Filename, Fields) -> 15 | generate_documentation(Filename, Fields, fun(_) -> true end). 16 | generate_documentation(Filename, Fields, Filter) when is_function(Filter, 1) -> 17 | {ok, File} = file:open(Filename, [write, {encoding, utf8}]), 18 | Header = 19 | lists:flatten( 20 | "|" ++ [" " ++ header_name(F) ++ " |" || F <- Fields] ++ "\n" 21 | ), 22 | Separator = 23 | lists:flatten( 24 | "|" ++ [" " ++ ["-" || _ <- header_name(F)] ++ " |" || F <- Fields] ++ "\n" 25 | ), 26 | Instructions = 27 | lists:flatten( 28 | [gen_doc_for_op(Op, Fields) 29 | ++ "\n" || Op <- aeb_fate_generate_ops:get_ops(), Filter(Op)]), 30 | io:format(File, "~ts~ts~ts\n", [Header, Separator, Instructions]), 31 | file:close(File). 32 | 33 | header_name(opname) -> 34 | "Name"; 35 | header_name(opcode) -> 36 | "Opcode"; 37 | header_name(arity) -> 38 | "Arity"; 39 | header_name(end_bb) -> 40 | "Ends basic block"; 41 | header_name(in_auth) -> 42 | "Allowed in auth"; 43 | header_name(offchain) -> 44 | "Allowed offchain"; 45 | header_name(format) -> 46 | "Args"; 47 | header_name(doc) -> 48 | "Description"; 49 | header_name(gas) -> 50 | "Gas cost"; 51 | header_name(arg_types) -> 52 | "Arg types"; 53 | header_name(res_type) -> 54 | "Res type". 55 | 56 | gen_doc_for_op(#{ opname := OpName 57 | , opcode := OpCode 58 | , arity := Arity 59 | , end_bb := EndBB 60 | , in_auth := InAuth 61 | , offchain := AllowedOffchain 62 | , format := FateFormat 63 | , doc := Doc 64 | , gas := Gas 65 | , arg_types := ArgTypes 66 | , res_type := ResType 67 | }, Fields) -> 68 | "| " ++ 69 | string:join( 70 | [ case Field of 71 | opname -> io_lib:format("`~s`", [OpName]); 72 | opcode -> io_lib:format("0x~.16b", [OpCode]); 73 | arity -> io_lib:format("~p", [Arity]); 74 | end_bb -> io_lib:format("~p", [EndBB]); 75 | in_auth -> io_lib:format("~p", [InAuth]); 76 | offchain -> io_lib:format("~p", [AllowedOffchain]); 77 | format -> 78 | case FateFormat of 79 | [] -> ""; 80 | _ -> lists:join( 81 | " ", 82 | [format_arg_doc(A) || 83 | A <- 84 | lists:zip(FateFormat, 85 | lists:seq(0,length(FateFormat)-1))]) 86 | end; 87 | doc -> Doc; 88 | gas when is_integer(Gas) -> io_lib:format("~p", [Gas]); 89 | gas when is_list(Gas) -> 90 | lists:flatten( 91 | string:join( 92 | [ io_lib:format( 93 | "~p (~s)", 94 | [GasVal, protocol_name(Prot)] 95 | ) 96 | || {Prot, GasVal} <- Gas 97 | ], ", ")); 98 | arg_types -> io_lib:format("~p", [ArgTypes]); 99 | res_type -> io_lib:format("~p", [ResType]) 100 | end 101 | || Field <- Fields 102 | ], 103 | " | ") ++ " |". 104 | 105 | protocol_name(?LIMA_PROTOCOL_VSN) -> 106 | "lima"; 107 | protocol_name(?IRIS_PROTOCOL_VSN) -> 108 | "iris". 109 | 110 | format_arg_doc({a, N}) -> io_lib:format("Arg~w", [N]); 111 | format_arg_doc({is,_N}) -> "Identifier"; 112 | format_arg_doc({ii,_N}) -> "Integer"; 113 | format_arg_doc({li,_N}) -> "[Integers]"; 114 | format_arg_doc({t,_N}) -> "Type". 115 | 116 | 117 | %% --- protocol documentation --- 118 | 119 | gen_protocol_description_of_operations(Filename) -> 120 | generate_documentation( 121 | Filename, [opname, format, doc, arg_types, res_type] 122 | ). 123 | 124 | gen_protocol_opcodes_flags_and_gas(Filename) -> 125 | generate_documentation( 126 | Filename, [opcode, opname, end_bb, in_auth, offchain, gas] 127 | ). 128 | 129 | gen_protocol_opcodes(Filename) -> 130 | generate_documentation( 131 | Filename, [opcode, opname] 132 | ). 133 | -------------------------------------------------------------------------------- /src/aeb_fate_maps.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2019, Aeternity Anstalt 3 | %%% @doc 4 | %%% Functions for manipulating FATE maps. In particular for mediating 5 | %%% between plain map values (represented by Erlang maps) and maps that are 6 | %%% fully or partially saved in the contract store. 7 | %%% @end 8 | %%% ------------------------------------------------------------------- 9 | -module(aeb_fate_maps). 10 | 11 | -include("aeb_fate_data.hrl"). 12 | 13 | -export([ allocate_store_maps/2 14 | , has_store_maps/1 15 | , unfold_store_maps/2 16 | , refcount/1 17 | , refcount_zero/0 18 | , refcount_diff/2 19 | , refcount_union/1 20 | , refcount_union/2 21 | , no_used_ids/0 ]). 22 | 23 | -export_type([used_ids/0, maps/0, refcount/0]). 24 | 25 | %% Size in bytes of serialization of a map for which we turn it into a store 26 | %% map. It's not worth turning small maps into store maps. 27 | %% Under consensus! 28 | -ifdef(TEST). 29 | -define(STORE_MAP_THRESHOLD, 0). 30 | -else. 31 | -define(STORE_MAP_THRESHOLD, 100). 32 | -endif. 33 | 34 | -type fate_value() :: aeb_fate_data:fate_type(). 35 | -type fate_value_or_tombstone() :: fate_value() | ?FATE_MAP_TOMBSTONE. 36 | -type id() :: integer(). 37 | -type used_ids() :: list(id()). 38 | -type maps() :: #{ id() => aeb_fate_data:fate_map() | aeb_fate_data:fate_store_map() }. 39 | 40 | %% -- Allocating store maps -------------------------------------------------- 41 | 42 | -spec allocate_store_maps(used_ids(), [fate_value_or_tombstone()]) -> {[fate_value_or_tombstone()], maps()}. 43 | allocate_store_maps(Used, Vals) -> 44 | {_Used, Vals1, Maps} = allocate_store_maps_l(Used, Vals, #{}), 45 | {Vals1, Maps}. 46 | 47 | allocate_store_maps(Used, ?FATE_MAP_TOMBSTONE = Val, Maps) -> {Used, Val, Maps}; 48 | allocate_store_maps(Used, ?FATE_TRUE = Val, Maps) -> {Used, Val, Maps}; 49 | allocate_store_maps(Used, ?FATE_FALSE = Val, Maps) -> {Used, Val, Maps}; 50 | allocate_store_maps(Used, ?FATE_UNIT = Val, Maps) -> {Used, Val, Maps}; 51 | allocate_store_maps(Used, ?FATE_BITS(_) = Val, Maps) -> {Used, Val, Maps}; 52 | allocate_store_maps(Used, ?FATE_BYTES(_) = Val, Maps) -> {Used, Val, Maps}; 53 | allocate_store_maps(Used, ?FATE_ADDRESS(_) = Val, Maps) -> {Used, Val, Maps}; 54 | allocate_store_maps(Used, ?FATE_CONTRACT(_) = Val, Maps) -> {Used, Val, Maps}; 55 | allocate_store_maps(Used, ?FATE_ORACLE(_) = Val, Maps) -> {Used, Val, Maps}; 56 | allocate_store_maps(Used, ?FATE_ORACLE_Q(_) = Val, Maps) -> {Used, Val, Maps}; 57 | allocate_store_maps(Used, ?FATE_CHANNEL(_) = Val, Maps) -> {Used, Val, Maps}; 58 | allocate_store_maps(Used, ?FATE_TYPEREP(_) = Val, Maps) -> {Used, Val, Maps}; 59 | allocate_store_maps(Used, Val, Maps) when ?IS_FATE_INTEGER(Val) -> {Used, Val, Maps}; 60 | allocate_store_maps(Used, Val, Maps) when ?IS_FATE_STRING(Val) -> {Used, Val, Maps}; 61 | allocate_store_maps(Used, ?FATE_TUPLE(Val), Maps) -> 62 | {Used1, Vals, Maps1} = allocate_store_maps_l(Used, tuple_to_list(Val), Maps), 63 | {Used1, ?FATE_TUPLE(list_to_tuple(Vals)), Maps1}; 64 | allocate_store_maps(Used, Val, Maps) when ?IS_FATE_LIST(Val) -> 65 | {Used1, Vals, Maps1} = allocate_store_maps_l(Used, ?FATE_LIST_VALUE(Val), Maps), 66 | {Used1, ?MAKE_FATE_LIST(Vals), Maps1}; 67 | allocate_store_maps(Used, ?FATE_VARIANT(Arities, Tag, Vals), Maps) -> 68 | {Used1, Vals1, Maps1} = allocate_store_maps_l(Used, tuple_to_list(Vals), Maps), 69 | {Used1, ?FATE_VARIANT(Arities, Tag, list_to_tuple(Vals1)), Maps1}; 70 | allocate_store_maps(Used, Val, Maps) when ?IS_FATE_MAP(Val) -> 71 | {Used1, KVs, Maps1} = allocate_store_maps_m(Used, ?FATE_MAP_VALUE(Val), Maps), 72 | Val1 = ?MAKE_FATE_MAP(KVs), 73 | case byte_size(aeb_fate_encoding:serialize(Val1)) < ?STORE_MAP_THRESHOLD of 74 | true -> {Used1, Val1, Maps1}; 75 | false -> 76 | {Id, Used2} = next_id(Used1), 77 | {Used2, ?FATE_STORE_MAP(#{}, Id), Maps1#{Id => Val1}} 78 | end; 79 | allocate_store_maps(Used, ?FATE_STORE_MAP(Cache, _Id) = Val, Maps) when Cache =:= #{} -> 80 | {Used, Val, Maps}; 81 | allocate_store_maps(Used, ?FATE_STORE_MAP(Cache, Id), Maps) -> 82 | {NewId, Used1} = next_id(Used), 83 | {Used2, Cache1, Maps1} = allocate_store_maps_m(Used1, Cache, Maps), 84 | {Used2, ?FATE_STORE_MAP(#{}, NewId), Maps1#{NewId => ?FATE_STORE_MAP(Cache1, Id)}}. 85 | 86 | allocate_store_maps_l(Used, [], Maps) -> {Used, [], Maps}; 87 | allocate_store_maps_l(Used, [H | T], Maps) -> 88 | {Used1, H1, Maps1} = allocate_store_maps(Used, H, Maps), 89 | {Used2, T1, Maps2} = allocate_store_maps(Used1, T, Maps1), 90 | {Used2, [H1 | T1], Maps2}. 91 | 92 | allocate_store_maps_m(Used, Val, Maps) -> 93 | maps:fold(fun(K, V, {Us, M, Ms}) -> 94 | {Us1, V1, Ms1} = allocate_store_maps(Us, V, Ms), 95 | {Us1, M#{ K => V1 }, Ms1} 96 | end, {Used, #{}, Maps}, Val). 97 | 98 | %% -- Unfolding store maps --------------------------------------------------- 99 | 100 | -type unfold_fun() :: fun((id()) -> aeb_fate_data:fate_map()). 101 | 102 | -spec unfold_store_maps(unfold_fun(), fate_value_or_tombstone()) -> fate_value_or_tombstone(). 103 | unfold_store_maps(_Unfold, ?FATE_MAP_TOMBSTONE = Val) -> Val; 104 | unfold_store_maps(_Unfold, ?FATE_TRUE = Val) -> Val; 105 | unfold_store_maps(_Unfold, ?FATE_FALSE = Val) -> Val; 106 | unfold_store_maps(_Unfold, ?FATE_UNIT = Val) -> Val; 107 | unfold_store_maps(_Unfold, ?FATE_BITS(_) = Val) -> Val; 108 | unfold_store_maps(_Unfold, ?FATE_BYTES(_) = Val) -> Val; 109 | unfold_store_maps(_Unfold, ?FATE_ADDRESS(_) = Val) -> Val; 110 | unfold_store_maps(_Unfold, ?FATE_CONTRACT(_) = Val) -> Val; 111 | unfold_store_maps(_Unfold, ?FATE_ORACLE(_) = Val) -> Val; 112 | unfold_store_maps(_Unfold, ?FATE_ORACLE_Q(_) = Val) -> Val; 113 | unfold_store_maps(_Unfold, ?FATE_CHANNEL(_) = Val) -> Val; 114 | unfold_store_maps(_Unfold, ?FATE_TYPEREP(_) = Val) -> Val; 115 | unfold_store_maps(_Unfold, Val) when ?IS_FATE_INTEGER(Val) -> Val; 116 | unfold_store_maps(_Unfold, Val) when ?IS_FATE_STRING(Val) -> Val; 117 | unfold_store_maps(Unfold, ?FATE_TUPLE(Val)) -> 118 | Vals = unfold_store_maps_l(Unfold, tuple_to_list(Val)), 119 | ?FATE_TUPLE(list_to_tuple(Vals)); 120 | unfold_store_maps(Unfold, Val) when ?IS_FATE_LIST(Val) -> 121 | ?MAKE_FATE_LIST(unfold_store_maps_l(Unfold, ?FATE_LIST_VALUE(Val))); 122 | unfold_store_maps(Unfold, ?FATE_VARIANT(Arities, Tag, Vals)) -> 123 | Vals1 = unfold_store_maps_l(Unfold, tuple_to_list(Vals)), 124 | ?FATE_VARIANT(Arities, Tag, list_to_tuple(Vals1)); 125 | unfold_store_maps(Unfold, Val) when ?IS_FATE_MAP(Val) -> 126 | ?MAKE_FATE_MAP(unfold_store_maps_m(Unfold, ?FATE_MAP_VALUE(Val))); 127 | unfold_store_maps(Unfold, ?FATE_STORE_MAP(Cache, Id)) -> 128 | StoreMap = Unfold(Id), 129 | maps:fold(fun write_cache/3, unfold_store_maps(Unfold, StoreMap), 130 | unfold_store_maps_m(Unfold, Cache)). 131 | 132 | unfold_store_maps_l(Unfold, Vals) -> 133 | [ unfold_store_maps(Unfold, Val) || Val <- Vals ]. 134 | 135 | unfold_store_maps_m(Unfold, Val) -> 136 | maps:map(fun(_, V) -> unfold_store_maps(Unfold, V) end, Val). 137 | 138 | write_cache(Key, ?FATE_MAP_TOMBSTONE, Map) -> 139 | maps:remove(Key, Map); 140 | write_cache(Key, Val, Map) -> 141 | Map#{ Key => Val }. 142 | 143 | %% -- Reference counting ----------------------------------------------------- 144 | 145 | -type refcount() :: #{id() => integer()}. 146 | 147 | -spec refcount_zero() -> refcount(). 148 | refcount_zero() -> #{}. 149 | 150 | -spec refcount_diff(refcount(), refcount()) -> refcount(). 151 | refcount_diff(New, Old) -> 152 | maps:fold(fun(K, N, C) -> maps:update_with(K, fun(M) -> M - N end, -N, C) end, 153 | New, Old). 154 | 155 | -spec refcount_union([refcount()]) -> refcount(). 156 | refcount_union(Counts) -> lists:foldl(fun refcount_union/2, #{}, Counts). 157 | 158 | -spec refcount_union(refcount(), refcount()) -> refcount(). 159 | refcount_union(A, B) -> 160 | maps:fold(fun(K, N, C) -> maps:update_with(K, fun(M) -> M + N end, N, C) end, 161 | B, A). 162 | 163 | -spec has_store_maps(fate_value()) -> boolean(). 164 | has_store_maps(Val) -> 165 | refcount_zero() /= refcount(Val). 166 | 167 | -spec refcount(fate_value()) -> refcount(). 168 | refcount(Val) -> refcount(Val, #{}). 169 | 170 | -spec refcount(fate_value_or_tombstone(), refcount()) -> refcount(). 171 | refcount(?FATE_MAP_TOMBSTONE, Count) -> Count; 172 | refcount(?FATE_TRUE, Count) -> Count; 173 | refcount(?FATE_FALSE, Count) -> Count; 174 | refcount(?FATE_UNIT, Count) -> Count; 175 | refcount(?FATE_BITS(_), Count) -> Count; 176 | refcount(?FATE_BYTES(_), Count) -> Count; 177 | refcount(?FATE_ADDRESS(_), Count) -> Count; 178 | refcount(?FATE_CONTRACT(_), Count) -> Count; 179 | refcount(?FATE_ORACLE(_), Count) -> Count; 180 | refcount(?FATE_ORACLE_Q(_), Count) -> Count; 181 | refcount(?FATE_CHANNEL(_), Count) -> Count; 182 | refcount(?FATE_TYPEREP(_), Count) -> Count; 183 | refcount(Val, Count) when ?IS_FATE_INTEGER(Val) -> Count; 184 | refcount(Val, Count) when ?IS_FATE_STRING(Val) -> Count; 185 | refcount(?FATE_TUPLE(Val), Count) -> 186 | refcount_l(tuple_to_list(Val), Count); 187 | refcount(Val, Count) when ?IS_FATE_LIST(Val) -> 188 | refcount_l(?FATE_LIST_VALUE(Val), Count); 189 | refcount(?FATE_VARIANT(_Arities, _Tag, Vals), Count) -> 190 | refcount_l(tuple_to_list(Vals), Count); 191 | refcount(Val, Count) when ?IS_FATE_MAP(Val) -> 192 | refcount_m(?FATE_MAP_VALUE(Val), Count); 193 | refcount(?FATE_STORE_MAP(Cache, Id), Count) -> 194 | refcount_m(Cache, maps:update_with(Id, fun(N) -> N + 1 end, 1, Count)). 195 | 196 | refcount_l(Vals, Count) -> 197 | lists:foldl(fun refcount/2, Count, Vals). 198 | 199 | refcount_m(Val, Count) -> 200 | %% No maps in map keys 201 | maps:fold(fun(_, ?FATE_MAP_TOMBSTONE, C) -> C; 202 | (_, V, C) -> refcount(V, C) end, Count, Val). 203 | 204 | %% -- Map id allocation ------------------------------------------------------ 205 | 206 | -spec no_used_ids() -> used_ids(). 207 | no_used_ids() -> []. 208 | 209 | -spec next_id(used_ids()) -> {id(), used_ids()}. 210 | next_id(UsedIds) -> 211 | next_id(UsedIds, 0, []). 212 | 213 | next_id(Used, J, Acc) when Used == []; J < hd(Used) -> 214 | {J, lists:reverse(Acc) ++ [J | Used]}; 215 | next_id([I | Used], I, Acc) -> 216 | next_id(Used, I + 1, [I | Acc]); 217 | next_id([I | Used], J, Acc) when J > I -> 218 | next_id(Used, J, [I | Acc]). 219 | -------------------------------------------------------------------------------- /src/aeb_heap.erl: -------------------------------------------------------------------------------- 1 | -module(aeb_heap). 2 | 3 | -export([ to_binary/1 4 | , to_binary/2 5 | , from_heap/3 6 | , from_binary/2 7 | , from_binary/3 8 | , maps_with_next_id/1 9 | , set_next_id/2 10 | , heap_fragment/3 11 | , heap_value/3 12 | , heap_value/4 13 | , heap_value_pointer/1 14 | , heap_value_maps/1 15 | , heap_value_offset/1 16 | , heap_value_heap/1 17 | , heap_value_byte_size/1 18 | , heap_fragment_maps/1 19 | , heap_fragment_offset/1 20 | , heap_fragment_heap/1 21 | ]). 22 | 23 | -export_type([binary_value/0, heap_value/0, offset/0, heap_fragment/0]). 24 | 25 | -include_lib("aebytecode/include/aeb_typerep_def.hrl"). 26 | -include_lib("aebytecode/include/aeb_heap.hrl"). 27 | 28 | -type word() :: non_neg_integer(). 29 | -type pointer() :: word(). 30 | -opaque heap_fragment() :: #heap{}. 31 | -type offset() :: non_neg_integer(). 32 | -type binary_value() :: binary(). 33 | -type heap_value() :: {pointer(), heap_fragment()}. 34 | 35 | 36 | -spec maps_with_next_id(heap_fragment()) -> #maps{}. 37 | %% Create just a maps value, don't keep rest of Heap 38 | maps_with_next_id(#heap{maps = #maps{next_id = N}}) -> 39 | #maps{ next_id = N }. 40 | 41 | -spec set_next_id(heap_fragment(), non_neg_integer()) -> heap_fragment(). 42 | set_next_id(Heap, N) -> 43 | Heap#heap{ maps = Heap#heap.maps#maps{ next_id = N } }. 44 | 45 | %% -- data type heap_fragment 46 | 47 | -spec heap_fragment(binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment(). 48 | heap_fragment(Heap) -> 49 | heap_fragment(#maps{ next_id = 0 }, 0, Heap). 50 | 51 | -spec heap_fragment(#maps{}, offset(), 52 | binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment(). 53 | heap_fragment(Maps, Offset, Heap) -> 54 | #heap{maps = Maps, offset = Offset, heap = Heap}. 55 | 56 | -spec heap_fragment_maps(heap_fragment()) -> #maps{}. 57 | heap_fragment_maps(#heap{maps = Maps}) -> 58 | Maps. 59 | 60 | -spec heap_fragment_offset(heap_fragment()) -> offset(). 61 | heap_fragment_offset(#heap{offset = Offs}) -> 62 | Offs. 63 | 64 | -spec heap_fragment_heap(heap_fragment()) -> binary() | #{non_neg_integer() => non_neg_integer()}. 65 | heap_fragment_heap(#heap{heap = Heap}) -> 66 | Heap. 67 | 68 | 69 | %% -- data type heap_value 70 | 71 | -spec heap_value(#maps{}, pointer(), 72 | binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_value(). 73 | heap_value(Maps, Ptr, Heap) -> 74 | heap_value(Maps, Ptr, Heap, 0). 75 | 76 | -spec heap_value(#maps{}, pointer(), 77 | binary() | #{non_neg_integer() => non_neg_integer()}, offset()) -> heap_value(). 78 | heap_value(Maps, Ptr, Heap, Offs) -> 79 | {Ptr, heap_fragment(Maps, Offs, Heap)}. 80 | 81 | -spec heap_value_pointer(heap_value()) -> pointer(). 82 | heap_value_pointer({Ptr, _}) -> Ptr. 83 | 84 | -spec heap_value_maps(heap_value()) -> #maps{}. 85 | heap_value_maps({_, Heap}) -> Heap#heap.maps. 86 | 87 | -spec heap_value_offset(heap_value()) -> offset(). 88 | heap_value_offset({_, Heap}) -> Heap#heap.offset. 89 | 90 | -spec heap_value_heap(heap_value()) -> 91 | binary() | #{non_neg_integer() => non_neg_integer()}. 92 | heap_value_heap({_, Heap}) -> Heap#heap.heap. 93 | 94 | %% -- Byte size of a heap value ---------------------------------------------- 95 | 96 | -spec heap_value_byte_size(heap_value()) -> non_neg_integer(). 97 | heap_value_byte_size({_, Heap}) -> 98 | Value = Heap#heap.heap, 99 | Maps = Heap#heap.maps, 100 | ValueSize = 101 | if is_binary(Value) -> byte_size(Value); 102 | true -> 0 end, 103 | MapsSize = 104 | lists:sum([ pmap_size(Map) || Map <- maps:values(Maps#maps.maps) ]), 105 | ValueSize + MapsSize. 106 | 107 | pmap_size(#pmap{data = stored}) -> 0; 108 | pmap_size(#pmap{data = Data}) when is_map(Data) -> 109 | lists:sum([ byte_size(Key) + byte_size(Val) 110 | || {Key, Val} <- maps:to_list(Data), 111 | Val /= tombstone ]). 112 | 113 | %% -- Value to binary -------------------------------------------------------- 114 | 115 | -spec to_binary(aeb_aevm_data:data()) -> aeb_aevm_data:heap(). 116 | %% Encode the data as a heap where the first word is the value (for unboxed 117 | %% types) or a pointer to the value (for boxed types). 118 | to_binary(Data) -> 119 | to_binary(Data, 0). 120 | 121 | to_binary(Data, BaseAddress) -> 122 | {Address, Memory} = to_binary1(Data, BaseAddress + 32), 123 | R = <>, 124 | R. 125 | 126 | 127 | %% Allocate the data in memory, from the given address. Return a pair 128 | %% of memory contents from that address and the value representing the 129 | %% data. 130 | to_binary1(Data,_Address) when is_integer(Data) -> 131 | {Data,<<>>}; 132 | to_binary1(Data, Address) when is_binary(Data) -> 133 | %% a string 134 | Words = aeb_memory:binary_to_words(Data), 135 | {Address,<<(size(Data)):256, << <> || W <- Words>>/binary>>}; 136 | to_binary1({contract_bytearray, FateCode}, Address) when is_binary(FateCode) -> 137 | Words = aeb_memory:binary_to_words(FateCode), 138 | {Address,<<(size(FateCode)):256, << <> || W <- Words>>/binary>>}; 139 | to_binary1(none, Address) -> to_binary1({variant, 0, []}, Address); 140 | to_binary1({some, Value}, Address) -> to_binary1({variant, 1, [Value]}, Address); 141 | to_binary1(word, Address) -> to_binary1({?TYPEREP_WORD_TAG}, Address); 142 | to_binary1(string, Address) -> to_binary1({?TYPEREP_STRING_TAG}, Address); 143 | to_binary1(typerep, Address) -> to_binary1({?TYPEREP_TYPEREP_TAG}, Address); 144 | to_binary1(contract_bytearray, Address) -> to_binary1({?TYPEREP_CONTRACT_BYTEARRAY_TAG}, Address); 145 | to_binary1(function, Address) -> to_binary1({?TYPEREP_FUN_TAG}, Address); 146 | to_binary1({list, T}, Address) -> to_binary1({?TYPEREP_LIST_TAG, T}, Address); 147 | to_binary1({option, T}, Address) -> to_binary1({variant, [[], [T]]}, Address); 148 | to_binary1({tuple, Ts}, Address) -> to_binary1({?TYPEREP_TUPLE_TAG, Ts}, Address); 149 | to_binary1({variant, Cons}, Address) -> to_binary1({?TYPEREP_VARIANT_TAG, Cons}, Address); 150 | to_binary1({map, K, V}, Address) -> to_binary1({?TYPEREP_MAP_TAG, K, V}, Address); 151 | to_binary1({variant, Tag, Args}, Address) -> 152 | to_binary1(list_to_tuple([Tag | Args]), Address); 153 | to_binary1(Map, Address) when is_map(Map) -> 154 | Size = maps:size(Map), 155 | %% Sort according to binary ordering 156 | KVs = lists:sort([ {to_binary(K), to_binary(V)} || {K, V} <- maps:to_list(Map) ]), 157 | {Address, <> || {K, V} <- KVs >>/binary >>}; 159 | to_binary1({}, _Address) -> 160 | {0, <<>>}; 161 | to_binary1(Data, Address) when is_tuple(Data) -> 162 | {Elems,Memory} = to_binaries(tuple_to_list(Data),Address+32*size(Data)), 163 | ElemsBin = << <> || W <- Elems>>, 164 | {Address,<< ElemsBin/binary, Memory/binary >>}; 165 | to_binary1([],_Address) -> 166 | <> = <<(-1):256>>, 167 | {Nil,<<>>}; 168 | to_binary1([H|T],Address) -> 169 | to_binary1({H,T},Address). 170 | 171 | 172 | to_binaries([],_Address) -> 173 | {[],<<>>}; 174 | to_binaries([H|T],Address) -> 175 | {HRep,HMem} = to_binary1(H,Address), 176 | {TRep,TMem} = to_binaries(T,Address+size(HMem)), 177 | {[HRep|TRep],<>}. 178 | 179 | %% Interpret a return value (a binary) using a type rep. 180 | 181 | -spec from_heap(Type :: ?Type(), Heap :: binary(), Ptr :: integer()) -> 182 | {ok, term()} | {error, term()}. 183 | from_heap(Type, Heap, Ptr) -> 184 | try {ok, from_binary(#{}, Type, Heap, Ptr)} 185 | catch _:Err -> 186 | %% io:format("** Error: from_heap failed with ~p\n ~p\n", [Err, erlang:get_stacktrace()]), 187 | {error, Err} 188 | end. 189 | 190 | %% Base address is the address of the first word of the given heap. 191 | -spec from_binary(T :: ?Type(), 192 | Heap :: binary(), 193 | BaseAddr :: non_neg_integer()) -> 194 | {ok, term()} | {error, term()}. 195 | from_binary(T, Heap = <>, BaseAddr) -> 196 | from_heap(T, <<0:BaseAddr/unit:8, Heap/binary>>, V); 197 | from_binary(_, Bin, _BaseAddr) -> 198 | {error, {binary_too_short, Bin}}. 199 | 200 | -spec from_binary(?Type(), binary()) -> {ok, term()} | {error, term()}. 201 | from_binary(T, Heap) -> 202 | from_binary(T, Heap, 0). 203 | 204 | from_binary(_, word, _, V) -> 205 | V; 206 | from_binary(_, signed_word, _, V) -> 207 | <> = <>, 208 | N; 209 | from_binary(_, bool, _, V) -> 210 | case V of 211 | 0 -> false; 212 | 1 -> true 213 | end; 214 | from_binary(_, string, Heap, V) -> 215 | StringSize = heap_word(Heap,V), 216 | BitAddr = 8*(V+32), 217 | <<_:BitAddr,Bytes:StringSize/binary,_/binary>> = Heap, 218 | Bytes; 219 | from_binary(_, {tuple, []}, _, _) -> 220 | {}; 221 | from_binary(Visited, {tuple,Cpts}, Heap, V) -> 222 | check_circular_refs(Visited, V), 223 | NewVisited = Visited#{V => true}, 224 | ElementNums = lists:seq(0, length(Cpts)-1), 225 | TypesAndPointers = lists:zip(Cpts, ElementNums), 226 | ElementAddress = fun(Index) -> V + 32 * Index end, 227 | Element = fun(Index) -> 228 | heap_word(Heap, ElementAddress(Index)) 229 | end, 230 | Convert = fun(Type, Index) -> 231 | from_binary(NewVisited, Type, Heap, Element(Index)) 232 | end, 233 | Elements = [Convert(T, I) || {T,I} <- TypesAndPointers], 234 | list_to_tuple(Elements); 235 | from_binary(Visited, {list, Elem}, Heap, V) -> 236 | <> = <<(-1):256>>, 237 | if V==Nil -> 238 | []; 239 | true -> 240 | {H,T} = from_binary(Visited, {tuple,[Elem,{list,Elem}]},Heap,V), 241 | [H|T] 242 | end; 243 | from_binary(Visited, {option, A}, Heap, V) -> 244 | from_binary(Visited, {variant_t, [{none, []}, {some, [A]}]}, Heap, V); 245 | from_binary(Visited, {variant, Cons}, Heap, V) -> 246 | Tag = heap_word(Heap, V), 247 | Args = lists:nth(Tag + 1, Cons), 248 | Visited1 = Visited#{V => true}, 249 | {variant, Tag, tuple_to_list(from_binary(Visited1, {tuple, Args}, Heap, V + 32))}; 250 | from_binary(Visited, {variant_t, TCons}, Heap, V) -> %% Tagged variants 251 | {Tags, Cons} = lists:unzip(TCons), 252 | {variant, I, Args} = from_binary(Visited, {variant, Cons}, Heap, V), 253 | Tag = lists:nth(I + 1, Tags), 254 | case Args of 255 | [] -> Tag; 256 | _ -> list_to_tuple([Tag | Args]) 257 | end; 258 | from_binary(_Visited, {map, A, B}, Heap, Ptr) -> 259 | %% FORMAT: [Size] [KeySize] Key [ValSize] Val .. [KeySize] Key [ValSize] Val 260 | Size = heap_word(Heap, Ptr), 261 | map_binary_to_value(A, B, Size, Heap, Ptr + 32); 262 | from_binary(Visited, typerep, Heap, V) -> 263 | check_circular_refs(Visited, V), 264 | Tag = heap_word(Heap, V), 265 | Arg1 = fun(T, I) -> from_binary(Visited#{V => true}, T, Heap, heap_word(Heap, V + 32 * I)) end, 266 | Arg = fun(T) -> Arg1(T, 1) end, 267 | case Tag of 268 | ?TYPEREP_WORD_TAG -> word; 269 | ?TYPEREP_STRING_TAG -> string; 270 | ?TYPEREP_TYPEREP_TAG -> typerep; 271 | ?TYPEREP_LIST_TAG -> {list, Arg(typerep)}; 272 | ?TYPEREP_TUPLE_TAG -> {tuple, Arg({list, typerep})}; 273 | ?TYPEREP_VARIANT_TAG -> {variant, Arg({list, {list, typerep}})}; 274 | ?TYPEREP_MAP_TAG -> {map, Arg(typerep), Arg1(typerep, 2)}; 275 | ?TYPEREP_FUN_TAG -> function; 276 | ?TYPEREP_CONTRACT_BYTEARRAY_TAG -> contract_bytearray 277 | end; 278 | from_binary(_, contract_bytearray, Heap, V) -> 279 | FateCodeSize = heap_word(Heap, V), 280 | BitAddr = 8*(V+32), 281 | <<_:BitAddr,Bytes:FateCodeSize/binary,_/binary>> = Heap, 282 | {contract_bytearray, Bytes}. 283 | 284 | map_binary_to_value(KeyType, ValType, N, Bin, Ptr) -> 285 | %% Avoid looping on bogus sizes 286 | MaxN = byte_size(Bin) div 64, 287 | Heap = heap_fragment(Bin), 288 | map_from_binary({value, KeyType, ValType}, min(N, MaxN), Heap, Ptr, #{}). 289 | 290 | map_from_binary(_, 0, _, _, Map) -> Map; 291 | map_from_binary({value, KeyType, ValType} = Output, I, Heap, Ptr, Map) -> 292 | KeySize = get_word(Heap, Ptr), 293 | KeyPtr = Ptr + 32, 294 | KeyBin = get_chunk(Heap, KeyPtr, KeySize), 295 | ValSize = get_word(Heap, KeyPtr + KeySize), 296 | ValPtr = KeyPtr + KeySize + 32, 297 | ValBin = get_chunk(Heap, ValPtr, ValSize), 298 | %% Keys and values are self contained binaries 299 | {ok, Key} = from_binary(KeyType, KeyBin), 300 | {ok, Val} = from_binary(ValType, ValBin), 301 | map_from_binary(Output, I - 1, Heap, ValPtr + ValSize, Map#{Key => Val}). 302 | 303 | check_circular_refs(Visited, V) -> 304 | case maps:is_key(V, Visited) of 305 | true -> exit(circular_references); 306 | false -> ok 307 | end. 308 | 309 | heap_word(Heap, Addr) when is_binary(Heap) -> 310 | BitSize = 8*Addr, 311 | <<_:BitSize,W:256,_/binary>> = Heap, 312 | W; 313 | heap_word(Heap, Addr) when is_map(Heap) -> 314 | 0 = Addr rem 32, %% Check that it's word aligned. 315 | maps:get(Addr, Heap, 0). 316 | 317 | get_word(#heap{offset = Offs, heap = Mem}, Addr) when Addr >= Offs -> 318 | get_word(Mem, Addr - Offs); 319 | get_word(Mem, Addr) when is_binary(Mem) -> 320 | <<_:Addr/unit:8, Word:256, _/binary>> = Mem, 321 | Word. 322 | 323 | get_chunk(#heap{offset = Offs, heap = Mem}, Addr, Bytes) when Addr >= Offs -> 324 | get_chunk(Mem, Addr - Offs, Bytes); 325 | get_chunk(Mem, Addr, Bytes) when is_binary(Mem) -> 326 | <<_:Addr/unit:8, Chunk:Bytes/binary, _/binary>> = Mem, 327 | Chunk. 328 | 329 | 330 | 331 | 332 | -------------------------------------------------------------------------------- /src/aeb_memory.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2018, Aeternity Anstalt 3 | %%% @doc 4 | %%% Memory speifics that compiler and VM need to agree upon 5 | %%% @end 6 | %%% Created : 19 Dec 2018 7 | %%%------------------------------------------------------------------- 8 | 9 | -module(aeb_memory). 10 | 11 | -export([binary_to_words/1]). 12 | 13 | binary_to_words(<<>>) -> 14 | []; 15 | binary_to_words(<>) -> 16 | [N|binary_to_words(Bin)]; 17 | binary_to_words(Bin) -> 18 | binary_to_words(<>). 19 | 20 | -------------------------------------------------------------------------------- /src/aeb_opcodes.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2017, Aeternity Anstalt 3 | %%% @doc 4 | %%% Opcodes 5 | %%% @end 6 | %%% Created : 2 Oct 2017 7 | %%%------------------------------------------------------------------- 8 | 9 | -module(aeb_opcodes). 10 | 11 | -export([ dup/1 12 | , mnemonic/1 13 | , m_to_op/1 14 | , opcode/1 15 | , op_size/1 16 | , push/1 17 | , swap/1 18 | ]). 19 | 20 | -include_lib("aebytecode/include/aeb_opcodes.hrl"). 21 | 22 | 23 | %%==================================================================== 24 | %% API 25 | %%==================================================================== 26 | 27 | opcode(?STOP) -> ?STOP; 28 | opcode(?ADD) -> ?ADD; 29 | opcode(?MUL) -> ?MUL; 30 | opcode(?SUB) -> ?SUB; 31 | opcode(?DIV) -> ?DIV; 32 | opcode(?SDIV) -> ?SDIV; 33 | opcode(?MOD) -> ?MOD; 34 | opcode(?SMOD) -> ?SMOD; 35 | opcode(?ADDMOD) -> ?ADDMOD; 36 | opcode(?MULMOD) -> ?MULMOD; 37 | opcode(?EXP) -> ?EXP; 38 | opcode(?SIGNEXTEND) -> ?SIGNEXTEND; 39 | opcode(?LT) -> ?LT; 40 | opcode(?GT) -> ?GT; 41 | opcode(?SLT) -> ?SLT; 42 | opcode(?SGT) -> ?SGT; 43 | opcode(?EQ) -> ?EQ; 44 | opcode(?ISZERO) -> ?ISZERO; 45 | opcode(?AND) -> ?AND; 46 | opcode(?OR) -> ?OR; 47 | opcode(?XOR) -> ?XOR; 48 | opcode(?NOT) -> ?NOT; 49 | opcode(?BYTE) -> ?BYTE; 50 | opcode(?SHL) -> ?SHL; 51 | opcode(?SHR) -> ?SHR; 52 | opcode(?SAR) -> ?SAR; 53 | opcode(?SHA3) -> ?SHA3; 54 | opcode(?CREATOR) -> ?CREATOR; 55 | opcode(?ADDRESS) -> ?ADDRESS; 56 | opcode(?BALANCE) -> ?BALANCE; 57 | opcode(?ORIGIN) -> ?ORIGIN; 58 | opcode(?CALLER) -> ?CALLER; 59 | opcode(?CALLVALUE) -> ?CALLVALUE; 60 | opcode(?CALLDATALOAD) -> ?CALLDATALOAD; 61 | opcode(?CALLDATASIZE) -> ?CALLDATASIZE; 62 | opcode(?CALLDATACOPY) -> ?CALLDATACOPY; 63 | opcode(?CODESIZE) -> ?CODESIZE; 64 | opcode(?CODECOPY) -> ?CODECOPY; 65 | opcode(?GASPRICE) -> ?GASPRICE; 66 | opcode(?EXTCODESIZE) -> ?EXTCODESIZE; 67 | opcode(?EXTCODECOPY) -> ?EXTCODECOPY; 68 | opcode(?RETURNDATASIZE) -> ?RETURNDATASIZE; %% TODO 69 | opcode(?RETURNDATACOPY) -> ?RETURNDATACOPY; %% TODO 70 | opcode(?BLOCKHASH) -> ?BLOCKHASH; 71 | opcode(?COINBASE) -> ?COINBASE; 72 | opcode(?TIMESTAMP) -> ?TIMESTAMP; 73 | opcode(?NUMBER) -> ?NUMBER; 74 | opcode(?DIFFICULTY) -> ?DIFFICULTY; 75 | opcode(?GASLIMIT) -> ?GASLIMIT; 76 | opcode(?POP) -> ?POP; 77 | opcode(?MLOAD) -> ?MLOAD; 78 | opcode(?MSTORE) -> ?MSTORE; 79 | opcode(?MSTORE8) -> ?MSTORE8; 80 | opcode(?SLOAD) -> ?SLOAD; 81 | opcode(?SSTORE) -> ?SSTORE; 82 | opcode(?JUMP) -> ?JUMP; 83 | opcode(?JUMPI) -> ?JUMPI; 84 | opcode(?PC) -> ?PC; 85 | opcode(?MSIZE) -> ?MSIZE; 86 | opcode(?GAS) -> ?GAS; 87 | opcode(?JUMPDEST) -> ?JUMPDEST; 88 | opcode(?PUSH1) -> ?PUSH1; 89 | opcode(?PUSH2) -> ?PUSH2; 90 | opcode(?PUSH3) -> ?PUSH3; 91 | opcode(?PUSH4) -> ?PUSH4; 92 | opcode(?PUSH5) -> ?PUSH5; 93 | opcode(?PUSH6) -> ?PUSH6; 94 | opcode(?PUSH7) -> ?PUSH7; 95 | opcode(?PUSH8) -> ?PUSH8; 96 | opcode(?PUSH9) -> ?PUSH9; 97 | opcode(?PUSH10) -> ?PUSH10; 98 | opcode(?PUSH11) -> ?PUSH11; 99 | opcode(?PUSH12) -> ?PUSH12; 100 | opcode(?PUSH13) -> ?PUSH13; 101 | opcode(?PUSH14) -> ?PUSH14; 102 | opcode(?PUSH15) -> ?PUSH15; 103 | opcode(?PUSH16) -> ?PUSH16; 104 | opcode(?PUSH17) -> ?PUSH17; 105 | opcode(?PUSH18) -> ?PUSH18; 106 | opcode(?PUSH19) -> ?PUSH19; 107 | opcode(?PUSH20) -> ?PUSH20; 108 | opcode(?PUSH21) -> ?PUSH21; 109 | opcode(?PUSH22) -> ?PUSH22; 110 | opcode(?PUSH23) -> ?PUSH23; 111 | opcode(?PUSH24) -> ?PUSH24; 112 | opcode(?PUSH25) -> ?PUSH25; 113 | opcode(?PUSH26) -> ?PUSH26; 114 | opcode(?PUSH27) -> ?PUSH27; 115 | opcode(?PUSH28) -> ?PUSH28; 116 | opcode(?PUSH29) -> ?PUSH29; 117 | opcode(?PUSH30) -> ?PUSH30; 118 | opcode(?PUSH31) -> ?PUSH31; 119 | opcode(?PUSH32) -> ?PUSH32; 120 | opcode(?DUP1) -> ?DUP1; 121 | opcode(?DUP2) -> ?DUP2; 122 | opcode(?DUP3) -> ?DUP3; 123 | opcode(?DUP4) -> ?DUP4; 124 | opcode(?DUP5) -> ?DUP5; 125 | opcode(?DUP6) -> ?DUP6; 126 | opcode(?DUP7) -> ?DUP7; 127 | opcode(?DUP8) -> ?DUP8; 128 | opcode(?DUP9) -> ?DUP9; 129 | opcode(?DUP10) -> ?DUP10; 130 | opcode(?DUP11) -> ?DUP11; 131 | opcode(?DUP12) -> ?DUP12; 132 | opcode(?DUP13) -> ?DUP13; 133 | opcode(?DUP14) -> ?DUP14; 134 | opcode(?DUP15) -> ?DUP15; 135 | opcode(?DUP16) -> ?DUP16; 136 | opcode(?SWAP1) -> ?SWAP1; 137 | opcode(?SWAP2) -> ?SWAP2; 138 | opcode(?SWAP3) -> ?SWAP3; 139 | opcode(?SWAP4) -> ?SWAP4; 140 | opcode(?SWAP5) -> ?SWAP5; 141 | opcode(?SWAP6) -> ?SWAP6; 142 | opcode(?SWAP7) -> ?SWAP7; 143 | opcode(?SWAP8) -> ?SWAP8; 144 | opcode(?SWAP9) -> ?SWAP9; 145 | opcode(?SWAP10) -> ?SWAP10; 146 | opcode(?SWAP11) -> ?SWAP11; 147 | opcode(?SWAP12) -> ?SWAP12; 148 | opcode(?SWAP13) -> ?SWAP13; 149 | opcode(?SWAP14) -> ?SWAP14; 150 | opcode(?SWAP15) -> ?SWAP15; 151 | opcode(?SWAP16) -> ?SWAP16; 152 | opcode(?LOG0) -> ?LOG0; 153 | opcode(?LOG1) -> ?LOG1; 154 | opcode(?LOG2) -> ?LOG2; 155 | opcode(?LOG3) -> ?LOG3; 156 | opcode(?LOG4) -> ?LOG4; 157 | opcode(?CREATE) -> ?CREATE; 158 | opcode(?CALL) -> ?CALL; 159 | opcode(?CALLCODE) -> ?CALLCODE; 160 | opcode(?RETURN) -> ?RETURN; 161 | opcode(?DELEGATECALL) -> ?DELEGATECALL; 162 | opcode(?STATICCALL) -> ?STATICCALL; %% TODO 163 | opcode(?REVERT) -> ?REVERT; 164 | opcode({comment,X}) -> ?COMMENT(X); 165 | opcode(?SUICIDE) -> ?SUICIDE. 166 | 167 | 168 | mnemonic(?STOP) -> 'STOP' ; 169 | mnemonic(?ADD) -> 'ADD' ; 170 | mnemonic(?MUL) -> 'MUL' ; 171 | mnemonic(?SUB) -> 'SUB' ; 172 | mnemonic(?DIV) -> 'DIV' ; 173 | mnemonic(?SDIV) -> 'SDIV' ; 174 | mnemonic(?MOD) -> 'MOD' ; 175 | mnemonic(?SMOD) -> 'SMOD' ; 176 | mnemonic(?ADDMOD) -> 'ADDMOD' ; 177 | mnemonic(?MULMOD) -> 'MULMOD' ; 178 | mnemonic(?EXP) -> 'EXP' ; 179 | mnemonic(?SIGNEXTEND) -> 'SIGNEXTEND' ; 180 | mnemonic(?LT) -> 'LT' ; 181 | mnemonic(?GT) -> 'GT' ; 182 | mnemonic(?SLT) -> 'SLT' ; 183 | mnemonic(?SGT) -> 'SGT' ; 184 | mnemonic(?EQ) -> 'EQ' ; 185 | mnemonic(?ISZERO) -> 'ISZERO' ; 186 | mnemonic(?AND) -> 'AND' ; 187 | mnemonic(?OR) -> 'OR' ; 188 | mnemonic(?XOR) -> 'XOR' ; 189 | mnemonic(?NOT) -> 'NOT' ; 190 | mnemonic(?BYTE) -> 'BYTE' ; 191 | mnemonic(?SHL) -> 'SHL' ; 192 | mnemonic(?SHR) -> 'SHR' ; 193 | mnemonic(?SAR) -> 'SAR' ; 194 | mnemonic(?SHA3) -> 'SHA3' ; 195 | mnemonic(?CREATOR) -> 'CREATOR' ; 196 | mnemonic(?ADDRESS) -> 'ADDRESS' ; 197 | mnemonic(?BALANCE) -> 'BALANCE' ; 198 | mnemonic(?ORIGIN) -> 'ORIGIN' ; 199 | mnemonic(?CALLER) -> 'CALLER' ; 200 | mnemonic(?CALLVALUE) -> 'CALLVALUE' ; 201 | mnemonic(?CALLDATALOAD) -> 'CALLDATALOAD' ; 202 | mnemonic(?CALLDATASIZE) -> 'CALLDATASIZE' ; 203 | mnemonic(?CALLDATACOPY) -> 'CALLDATACOPY' ; 204 | mnemonic(?CODESIZE) -> 'CODESIZE' ; 205 | mnemonic(?CODECOPY) -> 'CODECOPY' ; 206 | mnemonic(?GASPRICE) -> 'GASPRICE' ; 207 | mnemonic(?EXTCODESIZE) -> 'EXTCODESIZE' ; 208 | mnemonic(?EXTCODECOPY) -> 'EXTCODECOPY' ; 209 | mnemonic(?RETURNDATASIZE) -> 'RETURNDATASIZE' ; 210 | mnemonic(?RETURNDATACOPY) -> 'RETURNDATACOPY' ; 211 | mnemonic(?BLOCKHASH) -> 'BLOCKHASH' ; 212 | mnemonic(?COINBASE) -> 'COINBASE' ; 213 | mnemonic(?TIMESTAMP) -> 'TIMESTAMP' ; 214 | mnemonic(?NUMBER) -> 'NUMBER' ; 215 | mnemonic(?DIFFICULTY) -> 'DIFFICULTY' ; 216 | mnemonic(?GASLIMIT) -> 'GASLIMIT' ; 217 | mnemonic(?POP) -> 'POP' ; 218 | mnemonic(?MLOAD) -> 'MLOAD' ; 219 | mnemonic(?MSTORE) -> 'MSTORE' ; 220 | mnemonic(?MSTORE8) -> 'MSTORE8' ; 221 | mnemonic(?SLOAD) -> 'SLOAD' ; 222 | mnemonic(?SSTORE) -> 'SSTORE' ; 223 | mnemonic(?JUMP) -> 'JUMP' ; 224 | mnemonic(?JUMPI) -> 'JUMPI' ; 225 | mnemonic(?PC) -> 'PC' ; 226 | mnemonic(?MSIZE) -> 'MSIZE' ; 227 | mnemonic(?GAS) -> 'GAS' ; 228 | mnemonic(?JUMPDEST) -> 'JUMPDEST' ; 229 | mnemonic(?PUSH1) -> 'PUSH1' ; 230 | mnemonic(?PUSH2) -> 'PUSH2' ; 231 | mnemonic(?PUSH3) -> 'PUSH3' ; 232 | mnemonic(?PUSH4) -> 'PUSH4' ; 233 | mnemonic(?PUSH5) -> 'PUSH5' ; 234 | mnemonic(?PUSH6) -> 'PUSH6' ; 235 | mnemonic(?PUSH7) -> 'PUSH7' ; 236 | mnemonic(?PUSH8) -> 'PUSH8' ; 237 | mnemonic(?PUSH9) -> 'PUSH9' ; 238 | mnemonic(?PUSH10) -> 'PUSH10' ; 239 | mnemonic(?PUSH11) -> 'PUSH11' ; 240 | mnemonic(?PUSH12) -> 'PUSH12' ; 241 | mnemonic(?PUSH13) -> 'PUSH13' ; 242 | mnemonic(?PUSH14) -> 'PUSH14' ; 243 | mnemonic(?PUSH15) -> 'PUSH15' ; 244 | mnemonic(?PUSH16) -> 'PUSH16' ; 245 | mnemonic(?PUSH17) -> 'PUSH17' ; 246 | mnemonic(?PUSH18) -> 'PUSH18' ; 247 | mnemonic(?PUSH19) -> 'PUSH19' ; 248 | mnemonic(?PUSH20) -> 'PUSH20' ; 249 | mnemonic(?PUSH21) -> 'PUSH21' ; 250 | mnemonic(?PUSH22) -> 'PUSH22' ; 251 | mnemonic(?PUSH23) -> 'PUSH23' ; 252 | mnemonic(?PUSH24) -> 'PUSH24' ; 253 | mnemonic(?PUSH25) -> 'PUSH25' ; 254 | mnemonic(?PUSH26) -> 'PUSH26' ; 255 | mnemonic(?PUSH27) -> 'PUSH27' ; 256 | mnemonic(?PUSH28) -> 'PUSH28' ; 257 | mnemonic(?PUSH29) -> 'PUSH29' ; 258 | mnemonic(?PUSH30) -> 'PUSH30' ; 259 | mnemonic(?PUSH31) -> 'PUSH31' ; 260 | mnemonic(?PUSH32) -> 'PUSH32' ; 261 | mnemonic(?DUP1) -> 'DUP1' ; 262 | mnemonic(?DUP2) -> 'DUP2' ; 263 | mnemonic(?DUP3) -> 'DUP3' ; 264 | mnemonic(?DUP4) -> 'DUP4' ; 265 | mnemonic(?DUP5) -> 'DUP5' ; 266 | mnemonic(?DUP6) -> 'DUP6' ; 267 | mnemonic(?DUP7) -> 'DUP7' ; 268 | mnemonic(?DUP8) -> 'DUP8' ; 269 | mnemonic(?DUP9) -> 'DUP9' ; 270 | mnemonic(?DUP10) -> 'DUP10' ; 271 | mnemonic(?DUP11) -> 'DUP11' ; 272 | mnemonic(?DUP12) -> 'DUP12' ; 273 | mnemonic(?DUP13) -> 'DUP13' ; 274 | mnemonic(?DUP14) -> 'DUP14' ; 275 | mnemonic(?DUP15) -> 'DUP15' ; 276 | mnemonic(?DUP16) -> 'DUP16' ; 277 | mnemonic(?SWAP1) -> 'SWAP1' ; 278 | mnemonic(?SWAP2) -> 'SWAP2' ; 279 | mnemonic(?SWAP3) -> 'SWAP3' ; 280 | mnemonic(?SWAP4) -> 'SWAP4' ; 281 | mnemonic(?SWAP5) -> 'SWAP5' ; 282 | mnemonic(?SWAP6) -> 'SWAP6' ; 283 | mnemonic(?SWAP7) -> 'SWAP7' ; 284 | mnemonic(?SWAP8) -> 'SWAP8' ; 285 | mnemonic(?SWAP9) -> 'SWAP9' ; 286 | mnemonic(?SWAP10) -> 'SWAP10' ; 287 | mnemonic(?SWAP11) -> 'SWAP11' ; 288 | mnemonic(?SWAP12) -> 'SWAP12' ; 289 | mnemonic(?SWAP13) -> 'SWAP13' ; 290 | mnemonic(?SWAP14) -> 'SWAP14' ; 291 | mnemonic(?SWAP15) -> 'SWAP15' ; 292 | mnemonic(?SWAP16) -> 'SWAP16' ; 293 | mnemonic(?LOG0) -> 'LOG0' ; 294 | mnemonic(?LOG1) -> 'LOG1' ; 295 | mnemonic(?LOG2) -> 'LOG2' ; 296 | mnemonic(?LOG3) -> 'LOG3' ; 297 | mnemonic(?LOG4) -> 'LOG4' ; 298 | mnemonic(?CREATE) -> 'CREATE' ; 299 | mnemonic(?CALL) -> 'CALL' ; 300 | mnemonic(?CALLCODE) -> 'CALLCODE' ; 301 | mnemonic(?RETURN) -> 'RETURN' ; 302 | mnemonic(?DELEGATECALL) -> 'DELEGATECALL' ; 303 | mnemonic(?STATICCALL) -> 'STATICCALL' ; 304 | mnemonic(?REVERT) -> 'REVERT' ; 305 | mnemonic({comment,_}) -> 'COMMENT' ; 306 | mnemonic(?SUICIDE) -> 'SUICIDE' . 307 | 308 | 309 | 310 | m_to_op('STOP') -> ?STOP ; 311 | m_to_op('ADD') -> ?ADD ; 312 | m_to_op('MUL') -> ?MUL ; 313 | m_to_op('SUB') -> ?SUB ; 314 | m_to_op('DIV') -> ?DIV ; 315 | m_to_op('SDIV') -> ?SDIV ; 316 | m_to_op('MOD') -> ?MOD ; 317 | m_to_op('SMOD') -> ?SMOD ; 318 | m_to_op('ADDMOD') -> ?ADDMOD ; 319 | m_to_op('MULMOD') -> ?MULMOD ; 320 | m_to_op('EXP') -> ?EXP ; 321 | m_to_op('SIGNEXTEND') -> ?SIGNEXTEND ; 322 | m_to_op('LT') -> ?LT ; 323 | m_to_op('GT') -> ?GT ; 324 | m_to_op('SLT') -> ?SLT ; 325 | m_to_op('SGT') -> ?SGT ; 326 | m_to_op('EQ') -> ?EQ ; 327 | m_to_op('ISZERO') -> ?ISZERO ; 328 | m_to_op('AND') -> ?AND ; 329 | m_to_op('OR') -> ?OR ; 330 | m_to_op('XOR') -> ?XOR ; 331 | m_to_op('NOT') -> ?NOT ; 332 | m_to_op('BYTE') -> ?BYTE ; 333 | m_to_op('SHL') -> ?SHL ; 334 | m_to_op('SHR') -> ?SHR ; 335 | m_to_op('SAR') -> ?SAR ; 336 | m_to_op('SHA3') -> ?SHA3 ; 337 | m_to_op('CREATOR') -> ?CREATOR ; 338 | m_to_op('ADDRESS') -> ?ADDRESS ; 339 | m_to_op('BALANCE') -> ?BALANCE ; 340 | m_to_op('ORIGIN') -> ?ORIGIN ; 341 | m_to_op('CALLER') -> ?CALLER ; 342 | m_to_op('CALLVALUE') -> ?CALLVALUE ; 343 | m_to_op('CALLDATALOAD') -> ?CALLDATALOAD ; 344 | m_to_op('CALLDATASIZE') -> ?CALLDATASIZE ; 345 | m_to_op('CALLDATACOPY') -> ?CALLDATACOPY ; 346 | m_to_op('CODESIZE') -> ?CODESIZE ; 347 | m_to_op('CODECOPY') -> ?CODECOPY ; 348 | m_to_op('GASPRICE') -> ?GASPRICE ; 349 | m_to_op('EXTCODESIZE') -> ?EXTCODESIZE ; 350 | m_to_op('EXTCODECOPY') -> ?EXTCODECOPY ; 351 | m_to_op('RETURNDATASIZE') -> ?RETURNDATASIZE ; 352 | m_to_op('RETURNDATACOPY') -> ?RETURNDATACOPY ; 353 | m_to_op('BLOCKHASH') -> ?BLOCKHASH ; 354 | m_to_op('COINBASE') -> ?COINBASE ; 355 | m_to_op('TIMESTAMP') -> ?TIMESTAMP ; 356 | m_to_op('NUMBER') -> ?NUMBER ; 357 | m_to_op('DIFFICULTY') -> ?DIFFICULTY ; 358 | m_to_op('GASLIMIT') -> ?GASLIMIT ; 359 | m_to_op('POP') -> ?POP ; 360 | m_to_op('MLOAD') -> ?MLOAD ; 361 | m_to_op('MSTORE') -> ?MSTORE ; 362 | m_to_op('MSTORE8') -> ?MSTORE8 ; 363 | m_to_op('SLOAD') -> ?SLOAD ; 364 | m_to_op('SSTORE') -> ?SSTORE ; 365 | m_to_op('JUMP') -> ?JUMP ; 366 | m_to_op('JUMPI') -> ?JUMPI ; 367 | m_to_op('PC') -> ?PC ; 368 | m_to_op('MSIZE') -> ?MSIZE ; 369 | m_to_op('GAS') -> ?GAS ; 370 | m_to_op('JUMPDEST') -> ?JUMPDEST ; 371 | m_to_op('PUSH1') -> ?PUSH1 ; 372 | m_to_op('PUSH2') -> ?PUSH2 ; 373 | m_to_op('PUSH3') -> ?PUSH3 ; 374 | m_to_op('PUSH4') -> ?PUSH4 ; 375 | m_to_op('PUSH5') -> ?PUSH5 ; 376 | m_to_op('PUSH6') -> ?PUSH6 ; 377 | m_to_op('PUSH7') -> ?PUSH7 ; 378 | m_to_op('PUSH8') -> ?PUSH8 ; 379 | m_to_op('PUSH9') -> ?PUSH9 ; 380 | m_to_op('PUSH10') -> ?PUSH10 ; 381 | m_to_op('PUSH11') -> ?PUSH11 ; 382 | m_to_op('PUSH12') -> ?PUSH12 ; 383 | m_to_op('PUSH13') -> ?PUSH13 ; 384 | m_to_op('PUSH14') -> ?PUSH14 ; 385 | m_to_op('PUSH15') -> ?PUSH15 ; 386 | m_to_op('PUSH16') -> ?PUSH16 ; 387 | m_to_op('PUSH17') -> ?PUSH17 ; 388 | m_to_op('PUSH18') -> ?PUSH18 ; 389 | m_to_op('PUSH19') -> ?PUSH19 ; 390 | m_to_op('PUSH20') -> ?PUSH20 ; 391 | m_to_op('PUSH21') -> ?PUSH21 ; 392 | m_to_op('PUSH22') -> ?PUSH22 ; 393 | m_to_op('PUSH23') -> ?PUSH23 ; 394 | m_to_op('PUSH24') -> ?PUSH24 ; 395 | m_to_op('PUSH25') -> ?PUSH25 ; 396 | m_to_op('PUSH26') -> ?PUSH26 ; 397 | m_to_op('PUSH27') -> ?PUSH27 ; 398 | m_to_op('PUSH28') -> ?PUSH28 ; 399 | m_to_op('PUSH29') -> ?PUSH29 ; 400 | m_to_op('PUSH30') -> ?PUSH30 ; 401 | m_to_op('PUSH31') -> ?PUSH31 ; 402 | m_to_op('PUSH32') -> ?PUSH32 ; 403 | m_to_op('DUP1') -> ?DUP1 ; 404 | m_to_op('DUP2') -> ?DUP2 ; 405 | m_to_op('DUP3') -> ?DUP3 ; 406 | m_to_op('DUP4') -> ?DUP4 ; 407 | m_to_op('DUP5') -> ?DUP5 ; 408 | m_to_op('DUP6') -> ?DUP6 ; 409 | m_to_op('DUP7') -> ?DUP7 ; 410 | m_to_op('DUP8') -> ?DUP8 ; 411 | m_to_op('DUP9') -> ?DUP9 ; 412 | m_to_op('DUP10') -> ?DUP10 ; 413 | m_to_op('DUP11') -> ?DUP11 ; 414 | m_to_op('DUP12') -> ?DUP12 ; 415 | m_to_op('DUP13') -> ?DUP13 ; 416 | m_to_op('DUP14') -> ?DUP14 ; 417 | m_to_op('DUP15') -> ?DUP15 ; 418 | m_to_op('DUP16') -> ?DUP16 ; 419 | m_to_op('SWAP1') -> ?SWAP1 ; 420 | m_to_op('SWAP2') -> ?SWAP2 ; 421 | m_to_op('SWAP3') -> ?SWAP3 ; 422 | m_to_op('SWAP4') -> ?SWAP4 ; 423 | m_to_op('SWAP5') -> ?SWAP5 ; 424 | m_to_op('SWAP6') -> ?SWAP6 ; 425 | m_to_op('SWAP7') -> ?SWAP7 ; 426 | m_to_op('SWAP8') -> ?SWAP8 ; 427 | m_to_op('SWAP9') -> ?SWAP9 ; 428 | m_to_op('SWAP10') -> ?SWAP10 ; 429 | m_to_op('SWAP11') -> ?SWAP11 ; 430 | m_to_op('SWAP12') -> ?SWAP12 ; 431 | m_to_op('SWAP13') -> ?SWAP13 ; 432 | m_to_op('SWAP14') -> ?SWAP14 ; 433 | m_to_op('SWAP15') -> ?SWAP15 ; 434 | m_to_op('SWAP16') -> ?SWAP16 ; 435 | m_to_op('LOG0') -> ?LOG0 ; 436 | m_to_op('LOG1') -> ?LOG1 ; 437 | m_to_op('LOG2') -> ?LOG2 ; 438 | m_to_op('LOG3') -> ?LOG3 ; 439 | m_to_op('LOG4') -> ?LOG4 ; 440 | m_to_op('CREATE') -> ?CREATE ; 441 | m_to_op('CALL') -> ?CALL ; 442 | m_to_op('CALLCODE') -> ?CALLCODE ; 443 | m_to_op('RETURN') -> ?RETURN ; 444 | m_to_op('DELEGATECALL') -> ?DELEGATECALL ; 445 | m_to_op('STATICCALL') -> ?STATICCALL ; 446 | m_to_op('REVERT') -> ?REVERT ; 447 | m_to_op('COMMENT') -> ?COMMENT("") ; 448 | m_to_op('SUICIDE') -> ?SUICIDE ; 449 | m_to_op(Data) when 0= Data . 451 | 452 | push(N) when N >= 1, N =< 32 -> ?PUSH1 + N - 1. 453 | dup(N) when N >= 1, N =< 16 -> ?DUP1 + N - 1. 454 | swap(N) when N >= 1, N =< 16 -> ?SWAP1 + N - 1. 455 | 456 | op_size(OP) when OP >= ?PUSH1 andalso OP =< ?PUSH32 -> 457 | (OP - ?PUSH1) + 2; 458 | op_size({comment, _}) -> 0; 459 | op_size(_) -> 1. 460 | 461 | -------------------------------------------------------------------------------- /src/aeb_primops.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2018, Aeternity Anstalt 3 | %%% @doc 4 | %%% Handle interaction with the aeternity chain 5 | %%% through calls to AEternity primitive operations at address 0. 6 | %%% @end 7 | %%% Created : 18 Dec 2018 8 | %%%------------------------------------------------------------------- 9 | 10 | -module(aeb_primops). 11 | -export([ is_local_primop_op/1 12 | , op_needs_type_check/1 13 | ]). 14 | 15 | -include("aeb_opcodes.hrl"). 16 | 17 | is_local_primop_op(Op) when ?PRIM_CALL_IN_MAP_RANGE(Op) -> true; 18 | is_local_primop_op(Op) when ?PRIM_CALL_IN_CRYPTO_RANGE(Op) -> true; 19 | is_local_primop_op(Op) when is_integer(Op) -> false. 20 | 21 | op_needs_type_check(Op) -> 22 | (not is_local_primop_op(Op)) andalso op_has_dynamic_type(Op). 23 | 24 | op_has_dynamic_type(?PRIM_CALL_ORACLE_QUERY) -> true; 25 | op_has_dynamic_type(?PRIM_CALL_ORACLE_RESPOND) -> true; 26 | op_has_dynamic_type(?PRIM_CALL_ORACLE_GET_QUESTION) -> true; 27 | op_has_dynamic_type(?PRIM_CALL_ORACLE_GET_ANSWER) -> true; 28 | op_has_dynamic_type(?PRIM_CALL_MAP_GET) -> true; 29 | op_has_dynamic_type(?PRIM_CALL_MAP_PUT) -> true; 30 | op_has_dynamic_type(?PRIM_CALL_MAP_TOLIST) -> true; 31 | op_has_dynamic_type(?PRIM_CALL_AENS_RESOLVE) -> true; 32 | op_has_dynamic_type(_) -> false. 33 | -------------------------------------------------------------------------------- /src/aebytecode.app.src: -------------------------------------------------------------------------------- 1 | {application, aebytecode, 2 | [{description, "Bytecode definitions, serialization and deserialization for aeternity."}, 3 | {vsn, "3.4.0"}, 4 | {registered, []}, 5 | {applications, 6 | [kernel, 7 | stdlib, 8 | eblake2, 9 | aeserialization, 10 | getopt 11 | ]}, 12 | {env,[]}, 13 | {modules, []}, 14 | {maintainers, []}, 15 | {licenses, []}, 16 | {links, []} 17 | ]}. 18 | -------------------------------------------------------------------------------- /src/aefateasm.erl: -------------------------------------------------------------------------------- 1 | -module(aefateasm). 2 | 3 | -export([main/1]). 4 | 5 | -define(OPT_SPEC, 6 | [ {src_file, undefined, undefined, string, "Fate assembler code file"} 7 | , {verbose, $v, "verbose", undefined, "Verbose output"} 8 | , {help, $h, "help", undefined, "Show this message"} 9 | , {outfile, $o, "out", string, "Output file (experimental)"} ]). 10 | 11 | usage() -> 12 | getopt:usage(?OPT_SPEC, "aefateasm"). 13 | 14 | main(Args) -> 15 | case getopt:parse(?OPT_SPEC, Args) of 16 | {ok, {Opts, []}} -> 17 | case proplists:get_value(help, Opts, false) of 18 | false -> 19 | assemble(Opts); 20 | true -> 21 | usage() 22 | end; 23 | 24 | {ok, {_, NonOpts}} -> 25 | io:format("Can't understand ~p\n\n", [NonOpts]), 26 | usage(); 27 | 28 | {error, {Reason, Data}} -> 29 | io:format("Error: ~s ~p\n\n", [Reason, Data]), 30 | usage() 31 | end. 32 | 33 | assemble(Opts) -> 34 | case proplists:get_value(src_file, Opts, undefined) of 35 | undefined -> 36 | io:format("Error: no input source file\n\n"), 37 | usage(); 38 | File -> 39 | assemble(File, Opts) 40 | end. 41 | 42 | assemble(File, Opts) -> 43 | Verbose = proplists:get_value(verbose, Opts, false), 44 | case proplists:get_value(outfile, Opts, undefined) of 45 | undefined -> 46 | Asm = aeb_fate_asm:read_file(File), 47 | {Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, Opts), 48 | case Verbose of 49 | true -> 50 | io:format("Env: ~0p~n", [Env]); 51 | false -> ok 52 | end, 53 | io:format("Code: ~0p~n", [BC]); 54 | OutFile -> 55 | aeb_fate_asm:assemble_file(File, OutFile, Opts) 56 | end. 57 | 58 | -------------------------------------------------------------------------------- /test/aeb_data_test.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2018, Aeternity Anstalt 3 | %%% @doc Basic tests for Fate data 4 | %%% @end 5 | %%%------------------------------------------------------------------- 6 | 7 | -module(aeb_data_test). 8 | 9 | -include_lib("eunit/include/eunit.hrl"). 10 | 11 | format_integer_test() -> 12 | "0" = aeb_fate_data:format(0). 13 | -------------------------------------------------------------------------------- /test/aeb_fate_asm_test.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2018, Aeternity Anstalt 3 | %%% @doc Basic tests for Fate serialization 4 | %%% 5 | %%% To run: 6 | %%% TEST=aeb_fate_asm_test rebar3 eunit 7 | %%% 8 | %%% @end 9 | %%%------------------------------------------------------------------- 10 | 11 | -module(aeb_fate_asm_test). 12 | 13 | -include_lib("eunit/include/eunit.hrl"). 14 | 15 | asm_path() -> 16 | filename:join(code:lib_dir(aebytecode, test), "asm_code"). 17 | 18 | 19 | file_path(File) -> 20 | filename:join(asm_path(), File) ++ ".fate". 21 | 22 | read_file(File) -> 23 | FilePath = file_path(File), 24 | Asm = aeb_fate_asm:read_file(FilePath), 25 | Asm. 26 | 27 | assemble(Asm) -> 28 | aeb_fate_asm:asm_to_bytecode(Asm, []). 29 | 30 | asm_disasm_idenity_test() -> 31 | check_roundtrip(identity). 32 | 33 | asm_disasm_files_test_() -> 34 | [{lists:flatten(io_lib:format("~p", [X])), 35 | fun() -> check_roundtrip(X) end} 36 | || X <- sources()]. 37 | 38 | sources() -> 39 | [ "arith" 40 | , "bool" 41 | , "comp" 42 | , "jumpif" 43 | , "map" 44 | , "memory" 45 | , "remote" 46 | , "test" 47 | , "tuple" 48 | , "mapofmap" 49 | , "immediates" 50 | , "names" 51 | , "oracles" 52 | , "meta" 53 | ]. 54 | 55 | check_roundtrip(File) -> 56 | AssemblerCode = read_file(File), 57 | {_Env, ByteCode} = assemble(AssemblerCode), 58 | FateCode = aeb_fate_code:deserialize(ByteCode), 59 | DissasmCode = aeb_fate_asm:to_asm(FateCode), 60 | {_Env2, ByteCode2} = assemble(DissasmCode), 61 | ByteCode3 = aeb_fate_code:serialize(FateCode), 62 | Code1 = aeb_fate_asm:strip(ByteCode), 63 | Code2 = aeb_fate_asm:strip(ByteCode2), 64 | Code3 = aeb_fate_asm:strip(ByteCode3), 65 | ?assertEqual(Code1, Code2), 66 | ?assertEqual(Code1, Code3). 67 | -------------------------------------------------------------------------------- /test/aeb_serialize_test.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% @copyright (C) 2018, Aeternity Anstalt 3 | %%% @doc Basic tests for Fate serialization 4 | %%% 5 | %%% To run: 6 | %%% TEST=aeb_serialize_test rebar3 eunit 7 | %%% 8 | %%% @end 9 | %%%------------------------------------------------------------------- 10 | 11 | -module(aeb_serialize_test). 12 | 13 | -include_lib("eunit/include/eunit.hrl"). 14 | 15 | serialize_integer_test() -> 16 | <<0>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(0)), 17 | <<2>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(1)), 18 | <<126>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(63)), 19 | <<111, 0>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(64)), 20 | <<111,130,255,255>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(65535 + 64)), 21 | <<111,184,129,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 22 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 24 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 25 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>> = 27 | aeb_fate_encoding:serialize(aeb_fate_data:make_integer(1 bsl 1024 + 64)). 28 | 29 | serialize_deserialize_test_() -> 30 | [{lists:flatten(io_lib:format("~p", [X])), 31 | fun() -> 32 | ?assertEqual(X, 33 | aeb_fate_encoding:deserialize(aeb_fate_encoding:serialize(X))) 34 | end} 35 | || X <- sources()]. 36 | 37 | make_int_list(N) -> [aeb_fate_data:make_integer(I) || I <- lists:seq(1, N)]. 38 | 39 | sources() -> 40 | FortyTwo = aeb_fate_data:make_integer(42), 41 | Unit = aeb_fate_data:make_unit(), 42 | True = aeb_fate_data:make_boolean(true), 43 | False = aeb_fate_data:make_boolean(false), 44 | Nil = aeb_fate_data:make_list([]), 45 | EmptyString = aeb_fate_data:make_string(""), 46 | EmptyMap = aeb_fate_data:make_map(#{}), 47 | [aeb_fate_data:make_integer(0), 48 | aeb_fate_data:make_integer(1), 49 | True, False, Unit, Nil, EmptyString, EmptyMap, 50 | aeb_fate_data:make_hash(<<1,2,3,4,5>>), 51 | aeb_fate_data:make_signature(<<1,2,3,4,5>>), 52 | aeb_fate_data:make_contract(<<1,2,3,4,5>>), 53 | aeb_fate_data:make_channel(<<1,2,3,4,5>>), 54 | aeb_fate_data:make_list([True]), 55 | aeb_fate_data:make_address( 56 | <<0,1,2,3,4,5,6,7,8,9, 57 | 0,1,2,3,4,5,6,7,8,9, 58 | 0,1,2,3,4,5,6,7,8,9, 59 | 1,2>>), 60 | aeb_fate_data:make_string(<<"Hello">>), 61 | aeb_fate_data:make_string( 62 | <<"0123456789012345678901234567890123456789" 63 | "0123456789012345678901234567890123456789" 64 | "0123456789012345678901234567890123456789" 65 | "0123456789012345678901234567890123456789">>), %% Magic concat 80 char string. 66 | aeb_fate_data:make_tuple({True, FortyTwo}), 67 | aeb_fate_data:make_tuple(list_to_tuple(make_int_list(65))), 68 | aeb_fate_data:make_tuple(list_to_tuple(make_int_list(16))), 69 | aeb_fate_data:make_map(#{ aeb_fate_data:make_integer(1) => True, aeb_fate_data:make_integer(2) => False}), 70 | aeb_fate_data:make_map(#{ aeb_fate_data:make_string(<<"foo">>) => aeb_fate_data:make_tuple({FortyTwo, True})}), 71 | aeb_fate_data:make_list(make_int_list(3)), 72 | aeb_fate_data:make_integer(-65), 73 | aeb_fate_data:make_integer(65), 74 | aeb_fate_data:make_integer(-32432847932847928374983), 75 | aeb_fate_data:make_bits(0), 76 | aeb_fate_data:make_bits(1), 77 | aeb_fate_data:make_bits(-1), 78 | aeb_fate_data:make_list(make_int_list(65)), 79 | aeb_fate_data:make_variant([1,2,3], 0, {FortyTwo}), 80 | aeb_fate_data:make_variant([2,0], 1, {}), 81 | aeb_fate_data:make_list([aeb_fate_data:make_variant([0,0,0], 0, {})]), 82 | aeb_fate_data:make_variant([0|| _<-lists:seq(1,255)], 254, {}), 83 | aeb_fate_data:make_variant([0,1,2,3,4,5], 84 | 3, {aeb_fate_data:make_boolean(true), 85 | aeb_fate_data:make_list(make_int_list(3)), 86 | aeb_fate_data:make_string(<<"foo">>)}), 87 | %% contract C = 88 | %% type state = int 89 | %% entrypoint init() = 2137 90 | 91 | %% cb_+FFGA6Af6sHTrctrcNGwEa8MPei7iEHIjnxcsBzlA5IK0Yn11sCllP5E1kQfADcANwAaDoJvgggZAQM/jC8BEUTWRB8RaW5pdIIvAIU0LjMuMAD7u 92 | aeb_fate_data:make_contract_bytearray( 93 | <<248,81,70,3,160,31,234,193,211,173,203,107,112,209,176,17,175,12,61,232,187, 94 | 136,65,200,142,124,92,176,28,229,3,146,10,209,137,245,214,192,165,148,254,68, 95 | 214,68,31,0,55,0,55,0,26,14,130,111,130,8,25,1,3,63,140,47,1,17,68,214,68,31, 96 | 17,105,110,105,116,130,47,0,133,52,46,51,46,48,0>>) 97 | ]. 98 | -------------------------------------------------------------------------------- /test/aebytecode_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(aebytecode_SUITE). 2 | 3 | %% common_test exports 4 | -export([ all/0 ]). 5 | 6 | %% test case exports 7 | -export([ roundtrip_identy/1 ]). 8 | 9 | -include_lib("common_test/include/ct.hrl"). 10 | 11 | all() -> 12 | [ roundtrip_identy ]. 13 | 14 | roundtrip_identy(_Cfg) -> 15 | CodeDir = code:lib_dir(aebytecode, test), 16 | FileName = filename:join(CodeDir, "asm_code/identity.aesm"), 17 | Code = aeb_asm:file(FileName, []), 18 | ct:log("Code ~p:~n~s~n", [FileName, aeb_disassemble:format(Code, fun io:format/2)]), 19 | ok. 20 | -------------------------------------------------------------------------------- /test/asm_code/arith.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT arith 2 | 3 | FUNCTION add (integer, integer) : integer 4 | ADD a arg0 arg1 5 | RETURN 6 | 7 | FUNCTION sub (integer, integer) : integer 8 | SUB a arg0 arg1 9 | RETURN 10 | 11 | FUNCTION mul (integer, integer) : integer 12 | MUL a arg0 arg1 13 | RETURN 14 | 15 | FUNCTION div (integer, integer) : integer 16 | DIV a arg0 arg1 17 | RETURN 18 | 19 | FUNCTION mod (integer, integer) : integer 20 | MOD a arg0 arg1 21 | RETURN 22 | 23 | FUNCTION pow (integer, integer) : integer 24 | POW a arg0 arg1 25 | RETURN 26 | 27 | -------------------------------------------------------------------------------- /test/asm_code/bool.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT bool 2 | 3 | FUNCTION and(boolean, boolean) : boolean 4 | AND a arg0 arg1 5 | RETURN 6 | 7 | FUNCTION or(boolean, boolean) : boolean 8 | OR a arg0 arg1 9 | RETURN 10 | 11 | FUNCTION not(boolean) : boolean 12 | NOT a arg0 13 | RETURN 14 | 15 | -------------------------------------------------------------------------------- /test/asm_code/comp.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT comp 2 | 3 | FUNCTION lt(integer, integer) : boolean 4 | LT a arg0 arg1 5 | RETURN 6 | 7 | FUNCTION gt(integer, integer) : boolean 8 | GT a arg0 arg1 9 | RETURN 10 | 11 | FUNCTION egt(integer, integer) : boolean 12 | EGT a arg0 arg1 13 | RETURN 14 | 15 | FUNCTION elt(integer, integer) : boolean 16 | ELT a arg0 arg1 17 | RETURN 18 | 19 | FUNCTION eq(integer, integer) : boolean 20 | EQ a arg0 arg1 21 | RETURN 22 | 23 | FUNCTION neq(integer, integer) : boolean 24 | NEQ a arg0 arg1 25 | RETURN 26 | 27 | -------------------------------------------------------------------------------- /test/asm_code/identity.aesm: -------------------------------------------------------------------------------- 1 | ;; CONTRACT: Identity 2 | 3 | PUSH1 0 4 | CALLDATALOAD 5 | 6 | DUP1 7 | ;; Should be the hash of 8 | ;; the signature of the 9 | ;; first function (use 0 as placeholder) 10 | PUSH32 0x0000000000000000000000000000 11 | EQ 12 | PUSH32 id_entry 13 | JUMPI 14 | 15 | STOP 16 | 17 | id_entry: JUMPDEST 18 | ;; Skip the function name in the calldata 19 | PUSH1 32 20 | ;; Load argument on stack 21 | CALLDATALOAD 22 | ;; This function only takes one immidiate as argument. 23 | 24 | ;; Call the local version of the function 25 | ;; Get return address 26 | PC 27 | PUSH1 39 28 | ADD 29 | ;; Get Argument 30 | SWAP1 31 | PUSH32 id_local 32 | JUMP 33 | 34 | 35 | ;; return here from local call 36 | ;; Store top of stack at mem[0] 37 | JUMPDEST 38 | PUSH1 0 39 | MSTORE 40 | ;; Return mem[0]-mem[32] 41 | PUSH1 32 42 | PUSH1 0 43 | RETURN 44 | 45 | ;; for local calls 46 | ;; Stack: 47 | ;; | | 48 | ;; | Arg | <- SP 49 | ;; | RetAdr | 50 | ;; ... 51 | 52 | id_local: JUMPDEST 53 | 54 | ;; Just return the argument 55 | SWAP1 56 | ;; Stack: 57 | ;; | | 58 | ;; | RetAdr | <- SP 59 | ;; | RetVal | (Arg in this case) 60 | ;; ... 61 | 62 | JUMP 63 | 64 | ;; Test the code from the shell 65 | ;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => list_to_binary(aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", [])), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0 }, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{})). 66 | 67 | ;; Test the code from the shell with tracing. 68 | ;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", []), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0 }, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{ trace => true})). 69 | 70 | 71 | ;; Test the code from the shell with tracing. 72 | ;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", [pp_tokens, pp_opcodes, pp_patched_code, pp_hex_string]), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0}, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{ trace => true})). 73 | 74 | ;; aec_conductor:stop_mining(). 75 | -------------------------------------------------------------------------------- /test/asm_code/identity.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT: Identity 2 | FUNCTION id(integer) -> integer 3 | RETURN 4 | 5 | ;; Test the code from the shell 6 | ;; _build/default/rel/aessembler/bin/aessembler console 7 | 8 | ;; aeb_aefa:file("../../../../test/asm_code/identity.fate", []). 9 | -------------------------------------------------------------------------------- /test/asm_code/immediates.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT immediates 2 | 3 | FUNCTION integer() : integer 4 | RETURNR 42 5 | 6 | FUNCTION neg_integer() : integer 7 | RETURNR -2374683271468723648732648736498712634876147 8 | 9 | FUNCTION hex_integer() : integer 10 | RETURNR 0x0deadbeef0 11 | 12 | FUNCTION bool() : boolean 13 | RETURNR true 14 | 15 | FUNCTION bool_f() : boolean 16 | RETURNR false 17 | 18 | FUNCTION string() : string 19 | RETURNR "Hello" 20 | 21 | FUNCTION map() : {map, integer, boolean} 22 | RETURNR {} 23 | 24 | FUNCTION map2() : {map, integer, boolean} 25 | RETURNR {1 => true} 26 | 27 | FUNCTION map3() : {map, integer, boolean} 28 | RETURNR {1 => true, 29 | 2 => false} 30 | 31 | FUNCTION map4() : {map, integer, {map, string, boolean}} 32 | RETURNR {1 => { "foo" => true, "bar" => false}, 33 | 2 => {}, 34 | 3 => { "foo" => false}} 35 | 36 | FUNCTION nil() : {list, integer} 37 | RETURNR [] 38 | 39 | FUNCTION list1() : {list, integer} 40 | RETURNR [1] 41 | 42 | FUNCTION list2() : {list, integer} 43 | RETURNR [1, 2] 44 | 45 | 46 | FUNCTION no_bits() : bits 47 | RETURNR <> 48 | 49 | FUNCTION all_bits() : bits 50 | RETURNR !<> 51 | 52 | FUNCTION some_bits() : bits 53 | RETURNR <101010> 54 | 55 | FUNCTION many_bits() : bits 56 | RETURNR !<010101> 57 | 58 | FUNCTION group_bits() : bits 59 | RETURNR <1010 1010 0011 1001> 60 | 61 | FUNCTION unit() : {tuple, []} 62 | RETURNR () 63 | 64 | FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]}]} 65 | RETURNR (42, true, "FooBar", (1, 2)) 66 | 67 | 68 | FUNCTION address() : address 69 | RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv 70 | 71 | FUNCTION oracle() : oracle 72 | RETURNR @ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv 73 | 74 | FUNCTION contract() : contract 75 | RETURNR @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv 76 | 77 | FUNCTION channel() : channel 78 | RETURNR @ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv 79 | 80 | ;; Option(integer) = NONE | SOME(integer) 81 | FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]} 82 | RETURNR (| [0,1] | 0 | () |) 83 | 84 | ;; Option(integer) = NONE | SOME(integer) 85 | FUNCTION variant_some() : {variant, [{tuple, []}, {tuple, [integer]}]} 86 | RETURNR (| [0,1] | 1 | (42) |) 87 | -------------------------------------------------------------------------------- /test/asm_code/jumpif.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT jumpif 2 | FUNCTION skip(integer, integer) : integer 3 | ;; BB : 0 4 | PUSH arg1 5 | PUSH 0 6 | EQ a a arg0 7 | JUMPIF a 2 8 | ;; BB : 1 9 | INCA 10 | JUMP 2 11 | RETURN 12 | -------------------------------------------------------------------------------- /test/asm_code/map.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT map 2 | FUNCTION make_empty_map():{map, integer, boolean} 3 | MAP_EMPTY a 4 | RETURN 5 | 6 | FUNCTION map_update({map, integer, boolean}, integer, boolean):{map, integer, boolean} 7 | MAP_UPDATE a arg0 arg1 arg2 8 | RETURN 9 | 10 | FUNCTION map_lookup({map, integer, boolean}, integer):boolean 11 | MAP_LOOKUP a arg0 arg1 12 | RETURN 13 | 14 | FUNCTION map_lookup_default({map, integer, boolean}, integer): boolean 15 | MAP_LOOKUPD a arg0 arg1 false 16 | RETURN 17 | 18 | FUNCTION map_member({map, integer, boolean}, integer):boolean 19 | MAP_MEMBER a arg0 arg1 20 | RETURN 21 | 22 | FUNCTION map_delete({map, integer, boolean}, integer):{map, integer, boolean} 23 | MAP_DELETE a arg0 arg1 24 | RETURN 25 | 26 | FUNCTION map_member({map, integer, boolean}, integer) : boolean 27 | MAP_MEMBER a arg0 arg1 28 | RETURN 29 | 30 | FUNCTION map_from_list({list, {tuple, [integer, boolean]}}) : {map, integer, boolean} 31 | MAP_FROM_LIST a arg0 32 | RETURN 33 | 34 | -------------------------------------------------------------------------------- /test/asm_code/mapofmap.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT mapofmap 2 | FUNCTION map() : {map, integer, {map, string, boolean}} 3 | RETURNR {1 => { "foo" => true, "bar" => false}, 4 | 2 => {}, 5 | 3 => { "foo" => false}} 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/asm_code/memory.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT memory 2 | FUNCTION call(integer):integer 3 | STORE var1 arg0 4 | PUSH 0 5 | CALL "write" 6 | PUSH var1 7 | RETURN 8 | 9 | FUNCTION write(integer):integer 10 | STORE var1 arg0 11 | RETURNR var1 12 | 13 | FUNCTION dest_add(integer, integer): integer 14 | STORE var1 arg0 15 | STORE var2 arg1 16 | ADD var3 var1 var2 17 | PUSH var3 18 | RETURN 19 | 20 | FUNCTION dest_add_imm(integer):integer 21 | STORE var1 arg0 22 | ADD var3 var1 2 23 | PUSH var3 24 | RETURN 25 | 26 | FUNCTION dest_add_stack(integer, integer): integer 27 | STORE var1 arg0 28 | PUSH arg1 29 | ADD var3 var1 a 30 | PUSH var3 31 | RETURN 32 | -------------------------------------------------------------------------------- /test/asm_code/meta.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT meta 2 | 3 | FUNCTION meta() : boolean 4 | CREATE @cb_+PJGA6A4Fz4T2LHV5knITCldR3rqO7HrXO2zhOAR9JWNbhf8Q8C4xbhx/gx8JckANwAXfQBVACAAAP4vhlvZADcABwECgv5E1kQfADcBBzcACwAWMBReAHMAFjBvJFMAFjBvggOoFAAUABQSggABAz/+tIwWhAA3AAdTAAD+1jB5kAQ3AAcLAAD+6MRetgA3AQc3ABoGggABAz+4TS8GEQx8JclFY2FsbGVyX2lzX2NyZWF0b3IRL4Zb2Q1nZXQRRNZEHxFpbml0EbSMFoQdYmFsYW5jZRHWMHmQFXZhbHVlEejEXrYNc2V0gi8AhTQuMy4wAUqQ8s4= a 2137 5 | 6 | CLONE a arg0 2137 false 7 | CLONE_G a arg0 2137 10000 false 8 | 9 | BYTECODE_HASH a a 10 | BYTECODE_HASH a a 11 | EQ a a a 12 | RETURNR a -------------------------------------------------------------------------------- /test/asm_code/names.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT names 2 | 3 | FUNCTION preclaim(address, {bytes, 32}) : {tuple, []} 4 | AENS_PRECLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 5 | RETURNR {} 6 | 7 | FUNCTION claim(address, string, integer, integer) : {tuple, []} 8 | AENS_CLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 arg3 9 | RETURNR {} 10 | 11 | FUNCTION transfer(address, address, {bytes, 32}) : {tuple, []} 12 | AENS_TRANSFER #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 13 | RETURNR {} 14 | 15 | FUNCTION revoke(address, {bytes, 32}) : {tuple, []} 16 | AENS_REVOKE #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 17 | RETURNR {} 18 | 19 | FUNCTION resolve(string, string) : {variant, [{tuple, []}, {tuple, [address]}]} 20 | AENS_RESOLVE a arg0 arg1 'address 21 | RETURN 22 | -------------------------------------------------------------------------------- /test/asm_code/oracles.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT oracles 2 | 3 | FUNCTION register (address, integer, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : oracle 4 | ORACLE_REGISTER a #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]} 5 | RETURN 6 | 7 | FUNCTION query (oracle, integer, string) : oracle_query 8 | ORACLE_QUERY a arg0 arg1 arg2 (| [1,1] | 0 | (100) |) (| [1,1] | 0 | (100) |) 'string '{variant, [{tuple, []}, {tuple, [integer]}]} 9 | RETURN 10 | 11 | FUNCTION bogus_query () : oracle_query 12 | RETURNR @oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv 13 | 14 | FUNCTION respond (oracle, integer, string) : {tuple, []} 15 | ORACLE_RESPOND #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]} 16 | RETURNR {} 17 | 18 | FUNCTION extend (oracle, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : {tuple, []} 19 | ORACLE_EXTEND #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 20 | RETURNR {} 21 | 22 | FUNCTION get_question (oracle, oracle_query) : string 23 | ORACLE_GET_QUESTION a arg0 arg1 'string '{variant, [{tuple, []}, {tuple, [integer]}]} 24 | RETURN 25 | 26 | FUNCTION get_answer (oracle, oracle_query) : {variant, [{tuple, []}, {tuple, [string]}]} 27 | ORACLE_GET_ANSWER a arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]} 28 | RETURN 29 | 30 | FUNCTION query_fee (oracle) : integer 31 | ORACLE_QUERY_FEE a arg0 32 | RETURN 33 | -------------------------------------------------------------------------------- /test/asm_code/remote.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT remote 2 | FUNCTION add_five(integer):integer 3 | ADD a 5 arg0 4 | RETURN 5 | -------------------------------------------------------------------------------- /test/asm_code/test.fate: -------------------------------------------------------------------------------- 1 | ;; CONTRACT: Test 2 | FUNCTION id(integer) -> integer 3 | RETURN 4 | 5 | FUNCTION jumps() -> integer 6 | PUSH 0 7 | JUMP 3 8 | NOP 9 | JUMP 2 10 | NOP 11 | RETURN 12 | NOP 13 | JUMP 1 14 | 15 | FUNCTION inc(integer) -> integer 16 | INCA 17 | INCA 18 | RETURN 19 | 20 | FUNCTION call(integer) -> integer 21 | INCA 22 | CALL "inc" 23 | INCA 24 | RETURN 25 | 26 | 27 | FUNCTION tailcall(integer) -> integer 28 | INCA 29 | CALL_T "inc" 30 | 31 | ;; FUNCTION remote_call(integer) : integer 32 | ;; PUSH arg0 33 | ;; CALL_R remote.add_five {tuple, [integer]} integer 0 ;; typereps don't parse 34 | ;; INCA 35 | ;; RETURN 36 | 37 | ;; Test the code from the shell 38 | ;; _build/default/rel/aessembler/bin/aessembler console 39 | 40 | ;; aeb_aefa:file("../../../../test/asm_code/test.fate", []). 41 | ;; f(Asm), f(Env), f(BC), Asm = aefa_asm:read_file("../../../../test/asm_code/test.fate"), {Env, BC} = aefa_asm:asm_to_bytecode(Asm, []), aefa_asm:bytecode_to_fate_code(BC, []). 42 | -------------------------------------------------------------------------------- /test/asm_code/tuple.fate: -------------------------------------------------------------------------------- 1 | FUNCTION make_0tuple():{tuple, []} 2 | ;; BB : 0 3 | TUPLE a 0 4 | RETURN 5 | 6 | FUNCTION make_2tuple(integer, integer):{tuple, [integer, integer]} 7 | ;; BB : 0 8 | PUSH arg0 9 | PUSH arg1 10 | TUPLE a 2 11 | RETURN 12 | 13 | FUNCTION make_5tuple(integer, integer, integer, integer, integer): 14 | {tuple, [integer, integer, integer, integer, integer]} 15 | ;; BB : 0 16 | PUSH arg0 17 | PUSH arg1 18 | PUSH arg2 19 | PUSH arg3 20 | PUSH arg4 21 | TUPLE a 5 22 | RETURN 23 | 24 | FUNCTION element1(integer, integer): integer 25 | ;; BB : 0 26 | PUSH arg0 27 | PUSH arg1 28 | TUPLE a 2 29 | ELEMENT a 1 a 30 | RETURN 31 | 32 | FUNCTION element({tuple, [integer, integer]}, integer): integer 33 | ;; BB : 0 34 | ELEMENT a arg1 arg0 35 | RETURN 36 | --------------------------------------------------------------------------------