Compare commits

...

107 Commits

Author SHA1 Message Date
sennui adf3664dd0 Add argument to claim tx to enable bidding 2019-08-14 12:08:04 +02:00
Thomas Arts 2a9035d5ef Merge pull request #63 from aeternity/PT-167126818
Pt 167126818
2019-08-06 13:15:39 +02:00
Thomas Arts 8a50d20a67 Bring tests up-to-date 2019-08-06 13:04:25 +02:00
Thomas Arts 0b0cc38444 Provide API for querying implemented abi version 2019-08-06 12:21:51 +02:00
Tobias Lindahl 17c2a93e72 Merge pull request #62 from aeternity/PT-167164508-auto-generate-offchain-capabilities
Add information on offchain capabilities of operation
2019-07-10 11:10:49 +02:00
Tobias Lindahl e62cedb22c Add information on offchain capabilities of operation 2019-07-10 10:09:17 +02:00
Tobias Lindahl 76ae61b66c Merge pull request #61 from aeternity/PT-166927306-names-as-strings
Pt 166927306 names as strings
2019-06-28 13:48:42 +02:00
Tobias Lindahl edea526f38 Renum ordinals 2019-06-28 11:22:33 +02:00
Tobias Lindahl 487e087287 Use string instead of name hash for transfer and revoke 2019-06-26 15:33:24 +02:00
Tobias Lindahl c63ac888dd Pt 166233700 fate nameservice (#60)
* Introduce AENS instructions in FATE

* Remove name object and fixup some documentation
2019-06-26 13:19:44 +02:00
Hans Svensson 4d12b124f3 Merge pull request #59 from aeternity/PT-164629640-limit_fate_in_auth_context
Add in_auth field to aeb_fate_generate_ops
2019-06-26 12:29:17 +02:00
Hans Svensson 35ce283736 Add in_auth field to aeb_fate_generate_ops 2019-06-26 11:31:10 +02:00
Ulf Norell 677712b0b8 Merge pull request #58 from aeternity/PT-166233670-fate-events
Add FATE oracle check instructions (and others)
2019-06-25 19:56:56 +02:00
Ulf Norell 5171b800cc Add FATE oracle check instructions (and others) 2019-06-25 10:01:22 +02:00
Ulf Norell bf05e14661 Merge pull request #57 from aeternity/bytes-to-x
Add bytes_to_int and bytes_to_str instructions to FATE
2019-06-24 14:24:28 +02:00
Ulf Norell e3a00905de Add bytes_to_int and bytes_to_str instructions to FATE 2019-06-24 10:55:12 +02:00
Hans Svensson 59af12bf34 Merge pull request #56 from aeternity/PT-166788837-bytes
PT-166788837 bytes
2019-06-20 15:37:12 +02:00
Ulf Norell f7f0dfde51 Update and fix quickcheck fuzz test 2019-06-20 15:27:16 +02:00
Ulf Norell 33a1d5f4fb Perform sanity checks both in serialize and deserialize 2019-06-20 14:30:04 +02:00
Ulf Norell eeaf646a86 Allow variants as map keys 2019-06-20 14:30:04 +02:00
Ulf Norell 7fdc7a6cee Update asm tests 2019-06-20 14:30:04 +02:00
Ulf Norell f13ba67a2c Update and clean up quickcheck tests 2019-06-20 14:29:34 +02:00
Ulf Norell f421c1e361 Add bytes type and values
and remove hash and signature
2019-06-20 14:27:18 +02:00
Thomas Arts f91c8fabdd Merge pull request #51 from aeternity/PT-166696064-decode-calldata-fate
Pt 166696064 decode calldata fate
2019-06-20 13:05:44 +02:00
Tobias Lindahl 9dfc5f4f1d Merge pull request #55 from aeternity/PT-166786424-check-oracle-types
Add oracle types to some oracle instructions
2019-06-20 09:21:23 +02:00
Thomas Arts 1fda6912da Fix error 2019-06-19 17:41:47 +02:00
Thomas Arts cb83224c60 Add query to generate QuickCheck data 2019-06-19 17:41:47 +02:00
Thomas Arts 9840b22546 Add decoding function 2019-06-19 17:41:47 +02:00
Thomas Arts e3f843fd91 Do not return types, create_calldata does not need those 2019-06-19 17:41:47 +02:00
Thomas Arts 803ebc0854 Three new opcodes had been added 2019-06-19 17:41:47 +02:00
Hans Svensson 7e96e3baef Merge pull request #54 from aeternity/fix-bad-spec
Fix incorrect type spec
2019-06-19 13:26:54 +02:00
Hans Svensson 768e0d4fbb Fix incorrect type spec 2019-06-19 13:25:05 +02:00
Tobias Lindahl f92e23c955 Add oracle types to some oracle instructions 2019-06-19 12:11:22 +02:00
Tobias Lindahl e321882b98 Pt 166233685 fate oracles (#52)
* Introduce typereps

* Use typereps for registering oracles

* Add TTL to oracle register

* Introduce oracle query object and ORACLE_QUERY operation

* Stub the remaining oracle instructions

* Adapt oracle respond and extend

* Document oracle ops

* Add unit tests for all oracle instructions
2019-06-18 13:56:48 +02:00
Hans Svensson b45509962e Merge pull request #53 from aeternity/fate_abi_fun
Auth.tx_hash + new functions in aeb_fate_abi
2019-06-18 13:54:21 +02:00
Hans Svensson c1fb3a47c7 More functions in aeb_fate_abi 2019-06-18 13:51:06 +02:00
Hans Svensson a0c3a990ed Add function_name_from_function_hash to aeb_fate_abi 2019-06-18 11:59:54 +02:00
Hans Svensson 506f9ca72e Add AUTH_TX_HASH operation 2019-06-11 15:31:16 +02:00
Thomas Arts 7dd9c29cc0 Merge pull request #50 from aeternity/PT-166602172-calldat-for-fate
Add creation of fate calldata
2019-06-11 14:58:40 +02:00
Thomas Arts 242700e084 Add creation of fate calldata 2019-06-11 14:47:38 +02:00
Hans Svensson 29b5ee3e68 Merge pull request #49 from aeternity/fate-crypto-ops
Fate crypto ops + contract_to_address
2019-06-11 11:57:57 +02:00
Hans Svensson 896290ad3b contract_to_address instruction 2019-06-11 09:20:13 +02:00
Ulf Norell 876e8504c8 crypto instructions 2019-06-05 14:21:47 +02:00
Ulf Norell 53a055b90a Merge pull request #48 from aeternity/PT-166407568-polymoprhic-functions
Add serialization of any and type variables
2019-06-05 12:10:50 +02:00
Ulf Norell 409d761b18 Add serialization of any and type variables 2019-06-05 11:13:52 +02:00
Thomas Arts f15315adb7 Merge pull request #44 from aeternity/PT-165173962-fuzz-testing-aefate
Pt 165173962 fuzz testing aefate
2019-06-03 18:37:49 +02:00
Thomas Arts b8b316aae0 Make sorting idempotent 2019-06-03 14:49:13 +02:00
Thomas Arts 985e5358c9 Sorting should be idempotent 2019-06-03 14:49:02 +02:00
Thomas Arts ffebc13d08 Make sort testable 2019-06-03 14:45:08 +02:00
Thomas Arts 3ff4df42ff Make sure arguments not provided are maskes 2#00. 2019-06-03 13:43:49 +02:00
Thomas Arts d6fbc73450 Quicker testing with smaller terms 2019-06-03 13:34:37 +02:00
Thomas Arts 3d6ac9df92 Slightly different property 2019-06-03 11:08:08 +02:00
Thomas Arts e8390e52d1 Bug fix and little different way of writing logic 2019-06-03 11:07:00 +02:00
Erik Stenman 58daf1bb5c Add quick check test for order and handle variants. 2019-05-31 14:42:00 +02:00
Erik Stenman cb8e2b07a4 Use FATE order to sort maps iterator. 2019-05-31 13:52:07 +02:00
Erik Stenman 46f9d34447 Total order for FATE types. 2019-05-31 13:17:50 +02:00
Erik Stenman 942c7fb069 Add first draft of total order for FATE terms. 2019-05-30 12:31:40 +02:00
Erik Stenman 53130fc638 Add FATE_BOOLEAN_VALUE 2019-05-30 12:31:01 +02:00
Thomas Arts 8bf19dc060 Do not generate maps and variants as keys in fate maps 2019-05-29 16:23:45 +02:00
Thomas Arts a5bfdf63d5 Define a specific sorting for key-Value pairs
Sort on key and check: no duplicates and no maps and variants as key
2019-05-29 16:23:43 +02:00
Thomas Arts 41860b041e We should not deserialize function blocks only containing opcodes, but not a function 2019-05-29 16:23:43 +02:00
Thomas Arts 25ef7e7fe3 Remove special cases for NIL and empty MAP 2019-05-29 11:18:41 +02:00
Thomas Arts bcc409f302 serialize and deserialize integers with validity check 2019-05-29 11:17:13 +02:00
Thomas Arts 42719e7000 Fail on deserializing negative zero 2019-05-29 08:52:24 +02:00
Thomas Arts 0d6322c0aa Fix property 2019-05-29 08:31:04 +02:00
Thomas Arts f7a4c40c50 Only decode correctly encoded negative binaries 2019-05-29 08:21:25 +02:00
Thomas Arts ec0af8046a Safer serialization of opcode arguments 2019-05-28 19:09:42 +02:00
Thomas Arts 73c80e1168 Extend model to find {stack, N} errors as argument 2019-05-28 19:09:25 +02:00
Thomas Arts 45ff418699 More dsitintc fault injection 2019-05-28 18:23:39 +02:00
Thomas Arts 032277ae8b model so far
improved model
2019-05-28 18:13:27 +02:00
Thomas Arts 0bb4ac0fea Rough model to fuzz test aeb_fate_code 2019-05-28 18:13:24 +02:00
Thomas Arts 4a90e3b2b4 Add two more opcodes 2019-05-28 18:12:43 +02:00
Thomas Arts 8b7fefc8a9 Extend tests to encoded opcodes 2019-05-28 18:12:41 +02:00
Thomas Arts 6f59ef7a7c Fuzz test aeb_fate_encoding 2019-05-28 18:11:42 +02:00
Thomas Arts 302c1c211d Erik's fix to LONG strings 2019-05-28 18:11:42 +02:00
Thomas Arts 74791cfe52 typo 2019-05-28 18:11:42 +02:00
Thomas Arts 453f68fa39 Serialize only code blocks that have operators in right order 2019-05-28 18:11:42 +02:00
Thomas Arts aa9d2bf893 Do not allow empty code blocks 2019-05-28 18:11:42 +02:00
Thomas Arts 23b98f7d65 Add unit test to trigger eqc property 2019-05-28 18:11:42 +02:00
Thomas Arts 5d7bd73bcb Serialization of functions only succeeds for 4 byte iudentifiers 2019-05-28 18:11:42 +02:00
Thomas Arts 34b9684b6b Deserialize code without functions 2019-05-28 18:11:42 +02:00
Thomas Arts ccbb0ed6c7 Make code easier to test 2019-05-28 18:11:42 +02:00
Ulf Norell f1298870e5 Merge pull request #47 from aeternity/add-target-register-to-tuple
Add target register to TUPLE
2019-05-28 14:17:16 +02:00
Ulf Norell 9cfd369c5d Update tests 2019-05-28 12:54:10 +02:00
Ulf Norell f115feb16d Print state variables as storeN instead of var-N 2019-05-28 11:53:13 +02:00
Tobias Lindahl 241a96ebaa Change the correct function name to not shadow builting guard (#46) 2019-05-28 11:32:52 +02:00
Ulf Norell 880cf573aa Add a target register to TUPLE instruction 2019-05-28 11:24:38 +02:00
Tobias Lindahl 89f5ebc84b Add missing basic instructions (#45)
* Add missing basic instructions

* MAP_SIZE
* MAP_TO_LIST
* STR_LENGTH

* Change name of erlang function to not shadow builtin guard
2019-05-28 11:15:04 +02:00
Erik Stenman e98298cce4 Add accepted types to operator declarations. Check opcode numbering. (#43)
* Add accepted types to operator declarations. 
* Check opcode numbering.
2019-05-24 13:21:49 +02:00
Erik Stenman 0d1899b32a Pt 165352420 dissallow stack n (#42)
* Get rid of redundant arity field from op defs. Reorder and renumber ops. Fix bb_end for abort and exit.

* FATE does not accept arbitrary stack positions, only the accumulator aka stack 0.
2019-05-23 13:40:49 +02:00
Erik Stenman 3e0e289f2f Get rid of redundant arity field from op defs. Reorder and renumber ops. Fix bb_end for abort and exit. (#41) 2019-05-23 13:37:32 +02:00
Tobias Lindahl 11a8997ac7 Pt 166148534 refactor fate code (#40)
* Change names of generated aeb_fate_code -> aeb_fate_ops

* Break out fate code to separate adt module

* Fix documentation of the SPEND op

* More compact implementation of serialization/deserialization

* Changed argument specification order
2019-05-23 08:15:18 +02:00
Tobias Lindahl 2f4e1888c2 Merge pull request #38 from aeternity/PT-165857097-add-gas-and-value-to-calls
Pt 165857097 add gas and value to calls
2019-05-09 14:18:15 +02:00
Tobias Lindahl a9389e4e69 Add the CALL_VALUE instruction 2019-05-09 10:57:18 +02:00
Tobias Lindahl 2d3cede235 Add value to remote calls and the new ops CALL_GR and CALL_GTR 2019-05-09 10:57:18 +02:00
Hans Svensson 08a09b065b Merge pull request #39 from aeternity/PT-165440601-165713319-sophia_addons
Add CREATOR opcode and address check primops
2019-05-09 09:51:40 +02:00
Hans Svensson 5fd076f043 Add CREATOR opcode and address check primops 2019-05-09 09:35:48 +02:00
Ulf Norell 2555868990 Merge pull request #37 from aeternity/fate-compiler
Fate compiler
2019-05-07 13:04:53 +02:00
Tobias Lindahl 7eafbc22ae Merge pull request #36 from aeternity/PT-165760129-fix-object-parsing
Fix object parsing
2019-05-07 12:40:20 +02:00
Tobias Lindahl 3ed0fcbe05 Add test for parsing immediate objects 2019-05-07 11:28:54 +02:00
Tobias Lindahl b6019eb81b Fix object parsing 2019-05-07 11:16:05 +02:00
Ulf Norell 6eab9a32c9 Bump aeserialization dependency 2019-05-07 10:14:23 +02:00
Ulf Norell 91fc56c322 Change local calls to allow dynamic function name 2019-05-07 09:04:24 +02:00
Ulf Norell 1887486d36 Replace STR_EQ by APPEND
STR_EQ is not needed, the regular EQ instruction can handle strings as well. Having
an instruction for list append is quite handy though (would need two passes to do it
tail recursive in FATE assembly)
2019-05-07 09:04:24 +02:00
Ulf Norell bf6741eac4 Format of li should be {immediate, [integer()]} 2019-05-07 08:38:23 +02:00
Ulf Norell 491489ca7d Change ~w to ~p 2019-05-07 08:38:23 +02:00
Thomas Arts 91c4ab5bea Merge pull request #35 from aeternity/PT-property-based-tests
Pt property based tests
2019-05-06 14:51:04 +02:00
31 changed files with 1645 additions and 1025 deletions
+1 -1
View File
@@ -14,8 +14,8 @@ aeb_fate_asm_scan.xrl
_build/ _build/
aefateasm aefateasm
include/aeb_fate_opcodes.hrl include/aeb_fate_opcodes.hrl
src/aeb_fate_code.erl
src/aeb_fate_opcodes.erl src/aeb_fate_opcodes.erl
src/aeb_fate_ops.erl
src/aeb_fate_pp.erl src/aeb_fate_pp.erl
*.erl~ *.erl~
*.hrl~ *.hrl~
+1 -1
View File
@@ -1,4 +1,4 @@
GENERATED_SRC = src/aeb_fate_opcodes.erl src/aeb_fate_code.erl include/aeb_fate_opcodes.hrl src/aeb_fate_asm_scan.xrl src/aeb_fate_pp.erl 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
GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template
REBAR ?= rebar3 REBAR ?= rebar3
+4 -6
View File
@@ -49,7 +49,7 @@ or start with stack followed by an integer
`stack1` `stack1`
`a` `a`
Immediate values can be of 11 types: Immediate values can be of 10 types:
1. Integers as decimals: {Digits} or -{Digits} 1. Integers as decimals: {Digits} or -{Digits}
`42` `42`
@@ -70,8 +70,8 @@ Immediate values can be of 11 types:
2c. Oracle address: @ok_{base58char}+ 2c. Oracle address: @ok_{base58char}+
`@ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` `@ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2d. Name address: @nm_{base58char}+ 2d. Oracle query: @oq_{base58char}+
`@nm_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` `@oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2e. Channel address: @ch_{base58char}+ 2e. Channel address: @ch_{base58char}+
`@ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv` `@ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
@@ -104,11 +104,9 @@ Immediate values can be of 11 types:
9. Variants: (| [Arities] | Tag | ( Elements ) |) 9. Variants: (| [Arities] | Tag | ( Elements ) |)
`(| [1,3,5,2] | 3 | ( "foo", 12) |)` `(| [1,3,5,2] | 3 | ( "foo", 12) |)`
10. Hashes: #{base64char}+ 10. Bytes: #{base64char}+
`#AQIDCioLFQ==` `#AQIDCioLFQ==`
11. Signatures: $sg_{base58char}+
Where Where
+12 -13
View File
@@ -7,11 +7,10 @@
-define(FATE_MAP_T, #{ fate_type() => fate_type() }). -define(FATE_MAP_T, #{ fate_type() => fate_type() }).
-define(FATE_STRING_T, binary()). -define(FATE_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}). -define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_HASH_T, {hash, binary()}). -define(FATE_BYTES_T(N), {bytes, binary()}).
-define(FATE_SIGNATURE_T, {signature, binary()}).
-define(FATE_CONTRACT_T, {contract, <<_:256>>}). -define(FATE_CONTRACT_T, {contract, <<_:256>>}).
-define(FATE_ORACLE_T, {oracle, <<_:256>>}). -define(FATE_ORACLE_T, {oracle, <<_:256>>}).
-define(FATE_NAME_T, {name, <<_:256>>}). -define(FATE_ORACLE_Q_T, {oracle_query, <<_:256>>}).
-define(FATE_CHANNEL_T, {channel, <<_:256>>}). -define(FATE_CHANNEL_T, {channel, <<_:256>>}).
-define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}). -define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}).
-define(FATE_VOID_T, void). -define(FATE_VOID_T, void).
@@ -24,11 +23,11 @@
-define(IS_FATE_MAP(X), (is_map(X))). -define(IS_FATE_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))). -define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_HASH(X), (is_tuple(X) andalso (hash == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_BYTES(X), (is_tuple(X) andalso (bytes == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_SIGNATURE(X), (is_tuple(X) andalso (signature == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_BYTES(N, X), (?IS_FATE_BYTES(X) andalso byte_size(element(2, X)) == (N))).
-define(IS_FATE_CONTRACT(X), (is_tuple(X) andalso (contract == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_CONTRACT(X), (is_tuple(X) andalso (contract == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_ORACLE(X), (is_tuple(X) andalso (oracle == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_ORACLE(X), (is_tuple(X) andalso (oracle == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_NAME(X), (is_tuple(X) andalso (name == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_ORACLE_Q(X), (is_tuple(X) andalso (oracle_query == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_CHANNEL(X), (is_tuple(X) andalso (channel == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_CHANNEL(X), (is_tuple(X) andalso (channel == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_BITS(X), (is_tuple(X) andalso (bits == element(1, X) andalso is_integer(element(2, X))))). -define(IS_FATE_BITS(X), (is_tuple(X) andalso (bits == element(1, X) andalso is_integer(element(2, X))))).
-define(IS_FATE_VARIANT(X), (is_tuple(X) -define(IS_FATE_VARIANT(X), (is_tuple(X)
@@ -39,30 +38,30 @@
andalso is_tuple(element(4, X)) andalso is_tuple(element(4, X))
))). ))).
-define(IS_FATE_BOOLEAN(X), is_boolean(X)). -define(IS_FATE_BOOLEAN(X), is_boolean(X)).
-define(IS_FATE_TYPEREP(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= typerep)).
-define(FATE_UNIT, {tuple, {}}). -define(FATE_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}). -define(FATE_TUPLE(T), {tuple, T}).
-define(FATE_ADDRESS(A), {address, A}). -define(FATE_ADDRESS(A), {address, A}).
-define(FATE_HASH(X), {hash, X}). -define(FATE_BYTES(X), {bytes, X}).
-define(FATE_SIGNATURE(S), {signature, S}).
-define(FATE_CONTRACT(X), {contract, X}). -define(FATE_CONTRACT(X), {contract, X}).
-define(FATE_ORACLE(X), {oracle, X}). -define(FATE_ORACLE(X), {oracle, X}).
-define(FATE_NAME(X), {name, X}). -define(FATE_ORACLE_Q(X), {oracle_query, X}).
-define(FATE_CHANNEL(X), {channel, X}). -define(FATE_CHANNEL(X), {channel, X}).
-define(FATE_BITS(B), {bits, B}). -define(FATE_BITS(B), {bits, B}).
-define(FATE_TYPEREP(T), {typerep, T}).
-define(FATE_INTEGER_VALUE(X), (X)). -define(FATE_INTEGER_VALUE(X), (X)).
-define(FATE_BOOLEAN_VALUE(X), (X)).
-define(FATE_LIST_VALUE(X), (X)). -define(FATE_LIST_VALUE(X), (X)).
-define(FATE_TUPLE_ELEMENTS(X), (tuple_to_list(element(2, X)))). -define(FATE_TUPLE_ELEMENTS(X), (tuple_to_list(element(2, X)))).
-define(FATE_STRING_VALUE(X), (X)). -define(FATE_STRING_VALUE(X), (X)).
-define(FATE_ADDRESS_VALUE(X), (element(2, X))). -define(FATE_ADDRESS_VALUE(X), (element(2, X))).
-define(FATE_HASH_VALUE(X), (element(2, X))). -define(FATE_BYTES_VALUE(X), (element(2, X))).
-define(FATE_SIGNATURE_VALUE(X), (element(2, X))).
-define(FATE_CONTRACT_VALUE(X), (element(2, X))). -define(FATE_CONTRACT_VALUE(X), (element(2, X))).
-define(FATE_ORACLE_VALUE(X), (element(2, X))). -define(FATE_ORACLE_VALUE(X), (element(2, X))).
-define(FATE_NAME_VALUE(X), (element(2, X))).
-define(FATE_CHANNEL_VALUE(X), (element(2, X))). -define(FATE_CHANNEL_VALUE(X), (element(2, X))).
-define(FATE_BITS_VALUE(X), (element(2, X))).
-define(FATE_MAP_VALUE(X), (X)). -define(FATE_MAP_VALUE(X), (X)).
-define(FATE_MAP_SIZE(X), (map_size(X))). -define(FATE_MAP_SIZE(X), (map_size(X))).
-define(FATE_STRING_SIZE(X), (byte_size(X))). -define(FATE_STRING_SIZE(X), (byte_size(X))).
+7
View File
@@ -30,6 +30,7 @@
-define( 'SHA3', 16#20). -define( 'SHA3', 16#20).
-define( 'CREATOR', 16#2f).
-define( 'ADDRESS', 16#30). -define( 'ADDRESS', 16#30).
-define( 'BALANCE', 16#31). -define( 'BALANCE', 16#31).
-define( 'ORIGIN', 16#32). -define( 'ORIGIN', 16#32).
@@ -165,6 +166,8 @@
-define(PRIM_CALL_ORACLE_GET_ANSWER, 104). -define(PRIM_CALL_ORACLE_GET_ANSWER, 104).
-define(PRIM_CALL_ORACLE_GET_QUESTION, 105). -define(PRIM_CALL_ORACLE_GET_QUESTION, 105).
-define(PRIM_CALL_ORACLE_QUERY_FEE, 106). -define(PRIM_CALL_ORACLE_QUERY_FEE, 106).
-define(PRIM_CALL_ORACLE_CHECK, 110).
-define(PRIM_CALL_ORACLE_CHECK_QUERY, 111).
-define(PRIM_CALL_IN_AENS_RANGE(__TTYPE__), (((__TTYPE__) > 199) andalso ((__TTYPE__) < 300))). -define(PRIM_CALL_IN_AENS_RANGE(__TTYPE__), (((__TTYPE__) > 199) andalso ((__TTYPE__) < 300))).
-define(PRIM_CALL_AENS_RESOLVE, 200). -define(PRIM_CALL_AENS_RESOLVE, 200).
@@ -193,3 +196,7 @@
-define(PRIM_CALL_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))). -define(PRIM_CALL_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))).
-define(PRIM_CALL_AUTH_TX_HASH, 500). -define(PRIM_CALL_AUTH_TX_HASH, 500).
-define(PRIM_CALL_IN_ADDRESS_RANGE(__TTYPE__), (((__TTYPE__) > 599) andalso ((__TTYPE__) < 700))).
-define(PRIM_CALL_ADDR_IS_ORACLE, 600).
-define(PRIM_CALL_ADDR_IS_CONTRACT, 601).
+27
View File
@@ -0,0 +1,27 @@
%%% @author Thomas Arts
%%% @doc Allow to run QuickCheck tests as eunit tests
%%% `rebar3 as eqc eunit --cover`
%%% or `rebar3 as eqc eunit --module=aeb_fate_code`
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
%%%
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aeb_fate_code_tests).
-include_lib("eunit/include/eunit.hrl").
-compile([export_all, nowarn_export_all]).
-define(EQC_EUNIT(Module, PropName, Ms),
{ atom_to_list(PropName),
{timeout, (Ms * 10) div 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
quickcheck_test_() ->
{setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_code_eqc, prop_opcodes, 200),
?EQC_EUNIT(aefate_code_eqc, prop_serializes, 3000),
?EQC_EUNIT(aefate_code_eqc, prop_fail_serializes, 3000),
?EQC_EUNIT(aefate_code_eqc, prop_fuzz, 3000)
]}.
+3 -1
View File
@@ -21,5 +21,7 @@
quickcheck_test_() -> quickcheck_test_() ->
{setup, fun() -> eqc:start() end, {setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500), [ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500),
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000) ?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000),
?EQC_EUNIT(aefate_eqc, prop_order, 2000),
?EQC_EUNIT(aefate_eqc, prop_fuzz, 2000)
]}. ]}.
+2 -1
View File
@@ -21,5 +21,6 @@
quickcheck_test_() -> quickcheck_test_() ->
{setup, fun() -> eqc:start() end, {setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000), [ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000),
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000) ?EQC_EUNIT(aefate_eqc, prop_serializes, 1000),
?EQC_EUNIT(aefate_eqc, prop_idempotent, 1000)
]}. ]}.
+166
View File
@@ -0,0 +1,166 @@
%%% @author Thomas Arts
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
%%%
%%% We want to be sure that we can deserialize all FATE assembler that is accepted on chain.
%%%
%%% We test something slightly weaker here,
%%% viz. All FATE assembler we serialize, we can deserialize
%%%
%%% Negative testing modelled:
%%% Failure 1: function names differ from 4 bytes
%%% Failure 2: pointer to empty code block
%%% Failure 3: end_BB operation as not ending block or not at end of block
%%% - empty code blocks
%%% - blocks that are not of the form (not end_bb)* end_bb.
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_code_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile([export_all, nowarn_export_all]).
%%-define(Failure(Failures, Number), case lists:member(Number, Failures) of true -> 1; false -> 0 end)
prop_serializes() ->
in_parallel(
?FORALL(FateCode, fate_code(0),
begin
{T0, Binary} = timer:tc(fun() -> aeb_fate_code:serialize(FateCode) end),
?WHENFAIL(eqc:format("serialized:\n ~120p~n", [Binary]),
begin
{T1, Decoded} = timer:tc(fun() -> aeb_fate_code:deserialize(Binary) end),
measure(binary_size, size(Binary),
measure(serialize, T0 / 1000,
measure(deserialize, T1 / 1000,
conjunction([{equal, equals(Decoded, FateCode)},
{serialize_time, T0 / 1000 < 500},
{deserialize_time, T1 / 1000 < 500}]))))
end)
end)).
prop_fail_serializes() ->
conjunction([{Failure, eqc:counterexample(
?FORALL(FateCode, fate_code(Failure),
?FORALL(Binary, catch aeb_fate_code:serialize(FateCode),
is_binary(Binary))))
=/= true} || Failure <- [1, 2, 3, 4, 5] ]).
prop_fuzz() ->
in_parallel(
?FORALL(Binary, ?LET(FateCode, fate_code(0), aeb_fate_code:serialize(FateCode)),
?FORALL(FuzzedBin, fuzz(Binary),
try aeb_fate_code:deserialize(FuzzedBin) of
Code ->
?WHENFAIL(eqc:format("Code:\n ~p\n", [Code]),
begin
Bin1 = aeb_fate_code:serialize(Code),
Code1 = aeb_fate_code:deserialize(Bin1),
?WHENFAIL(eqc:format("Reserialized\n ~120p\n", [Bin1]),
equals(Code, Code1))
end)
catch _:_ -> true
end))).
prop_opcodes() ->
?FORALL(Opcode, choose(0, 16#ff),
try M = aeb_fate_opcodes:mnemonic(Opcode),
?WHENFAIL(eqc:format("opcode ~p -> ~p", [Opcode, M]),
conjunction([{valid, lists:member(Opcode, valid_opcodes())},
{eq, equals(aeb_fate_opcodes:m_to_op(M), Opcode)}]))
catch
_:_ ->
not lists:member(Opcode, valid_opcodes())
end).
valid_opcodes() ->
lists:seq(0, 16#7c) ++ lists:seq(16#fa, 16#fd).
fate_code(Failure) ->
?SIZED(Size,
?LET({FMap, SMap, AMap},
{non_empty(map(if Failure == 1 -> binary(1);
true -> binary(4) end,
{{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})),
small_map(small_fate_data_key(5), small_fate_data(4)),
small_map(small_fate_data_key(5), small_fate_data(4))},
aeb_fate_code:update_annotations(
aeb_fate_code:update_symbols(
aeb_fate_code:update_functions(
aeb_fate_code:new(), FMap), SMap), AMap))).
short_list(Max, Gen) ->
?LET(N, choose(0, Max), eqc_gen:list(N, Gen)).
small_map(KeyGen, ValGen) ->
?LET(KeyVals, short_list(6, {KeyGen, ValGen}),
return(maps:from_list(KeyVals))).
bbs_code(Failure) ->
frequency([{if Failure == 2 -> 5; true -> 0 end, #{0 => []}},
{10, ?LET(BBs, short_list(6, bb_code(Failure)),
maps:from_list(
lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]).
bb_code(Failure) ->
EndBB = [ Op || Op <- valid_opcodes(), aeb_fate_opcodes:end_bb(Op) ],
NonEndBB = valid_opcodes() -- EndBB,
frequency(
[{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(short_list(6, elements(NonEndBB))), bblock(Failure, Ops))},
{if Failure == 4 -> 5; true -> 0 end, ?LET({Ops, Op}, {short_list(6, elements(valid_opcodes())), elements(EndBB)}, bblock(Failure, Ops ++ [Op]))},
{10, ?LET({Ops, Op}, {short_list(6, elements(NonEndBB)), elements(EndBB)},
bblock(Failure, Ops ++ [Op]))}]).
bblock(Failure, Ops) ->
[ begin
Mnemonic = aeb_fate_opcodes:mnemonic(Op),
Arity = aeb_fate_opcodes:args(Op),
case Arity of
0 -> Mnemonic;
_ -> list_to_tuple([Mnemonic |
[ frequency([{if Failure == 5 -> 5; true -> 0 end, {stack, nat()}},
{5, {stack, 0}},
{5, {arg, nat()}},
{5, {var, nat()}},
{5, {immediate, small_fate_data(4)}}]) ||
_ <- lists:seq(1, Arity) ]])
end
end || Op <- Ops ].
fuzz(Binary) ->
?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0, 255)},
begin
M = N * 8,
<<X:M, _:8, Z/binary>> = Binary,
<<X:M, Inj:8, Z/binary>>
end).
prop_small() ->
?FORALL(Value, small_fate_data(4),
begin
Bin = aeb_fate_encoding:serialize(Value),
Size = byte_size(Bin),
measure(size, Size,
?WHENFAIL(eqc:format("Size: ~p\n", [Size]),
Size < 1000))
end).
prop_small_type() ->
?FORALL(Type, ?SIZED(Size, aefate_type_eqc:fate_type(Size div 3)),
begin
Bin = iolist_to_binary(aeb_fate_encoding:serialize_type(Type)),
Size = byte_size(Bin),
measure(size, Size,
?WHENFAIL(eqc:format("Size: ~p\n", [Size]),
Size < 1000))
end).
small_fate_data(N) ->
?SIZED(Size, resize(Size div N, aefate_eqc:fate_data())).
small_fate_data_key(N) ->
?SIZED(Size, ?LET(Data, aefate_eqc:fate_data(Size div N, []), eqc_symbolic:eval(Data))).
+113 -66
View File
@@ -32,91 +32,138 @@ prop_format_scan() ->
end)). end)).
prop_serializes() -> prop_serializes() ->
?FORALL(FateDatas, non_empty(?SIZED(Size, resize(Size div 2, list(fate_data())))), ?FORALL({Data, Garbage}, {fate_data(), binary()},
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [FateDatas]), ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]),
begin begin
{T1, Binary} = Binary = <<(aeb_fate_encoding:serialize(Data))/binary, Garbage/binary>>,
timer:tc( fun() -> {FateData, Rest} = aeb_fate_encoding:deserialize_one(Binary),
<< begin B = aeb_fate_encoding:serialize(Data),
<<B/binary>> end || Data <- FateDatas >>
end),
{T2, {FateData, _}} =
timer:tc(fun() -> aeb_fate_encoding:deserialize_one(Binary) end),
measure(binary_size, size(Binary), measure(binary_size, size(Binary),
measure(encode, T1, conjunction([{equal, equals(Data, FateData)},
measure(decode, T2, {rest, equals(Garbage, Rest)},
conjunction([{equal, equals(hd(FateDatas), FateData)}, {size, size(Binary) < 500000}]))
{size, size(Binary) < 500000}]))))
end)). end)).
prop_fuzz() ->
in_parallel(
?FORALL(Binary, ?LET(FateData, ?SIZED(Size, resize(Size div 4, fate_data())), aeb_fate_encoding:serialize(FateData)),
?FORALL(InjectedBin, injection(Binary),
try Org = aeb_fate_encoding:deserialize(InjectedBin),
NewBin = aeb_fate_encoding:serialize(Org),
NewOrg = aeb_fate_encoding:deserialize(NewBin),
measure(success, 1,
?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]),
equals(NewBin, InjectedBin)))
catch _:_ ->
true
end))).
prop_order() ->
?FORALL(Items, vector(3, fate_data()),
begin
%% Use lt to take minimum
Min = lt_min(Items),
Max = lt_max(Items),
conjunction([ {minimum, is_empty([ {Min, '>', I} || I<-Items, aeb_fate_data:lt(I, Min)])},
{maximum, is_empty([ {Max, '<', I} || I<-Items, aeb_fate_data:lt(Max, I)])}])
end).
lt_min([X, Y | Rest]) ->
case aeb_fate_data:lt(X, Y) of
true -> lt_min([X | Rest]);
false -> lt_min([Y| Rest])
end;
lt_min([X]) -> X.
lt_max([X, Y | Rest]) ->
case aeb_fate_data:lt(X, Y) of
true -> lt_max([Y | Rest]);
false -> lt_max([X| Rest])
end;
lt_max([X]) -> X.
prop_idempotent() ->
?FORALL(Items, list({fate_data_key(), fate_data()}),
equals(aeb_fate_encoding:sort(Items),
aeb_fate_encoding:sort(aeb_fate_encoding:sort(Items)))).
fate_data() -> fate_data() ->
?SIZED(Size, ?LET(Data, fate_data(Size, [map]), eqc_symbolic:eval(Data))). ?SIZED(Size, ?LET(Data, fate_data(Size, [map, variant]), eqc_symbolic:eval(Data))).
fate_data_key() ->
?SIZED(Size, ?LET(Data, fate_data(Size div 4, [variant]), eqc_symbolic:eval(Data))).
fate_data(0, _Options) -> fate_data(0, _Options) ->
?LAZY( ?LAZY(
oneof([fate_integer(), frequency(
fate_boolean(), [{5, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])},
fate_nil(), {1, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(),
fate_unit(), fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}]));
fate_string(),
fate_address(),
fate_hash(),
fate_signature(),
fate_contract(),
fate_oracle(),
fate_name(),
fate_bits(),
fate_channel()]));
fate_data(Size, Options) -> fate_data(Size, Options) ->
oneof([?LAZY(fate_data(Size - 1, Options)), ?LAZY(
?LAZY(fate_list( fate_data(Size div 5, Options) )), oneof([fate_data(0, Options),
?LAZY(fate_tuple( list(fate_data(Size div 5, Options)) )), fate_list(Size, Options),
?LAZY(fate_variant( list(fate_data(Size div 5, Options)))) ] ++ fate_tuple(Size, Options)] ++
[ [fate_variant(Size, Options)
?LAZY(fate_map( fate_data(Size div 8, Options -- [map]), || lists:member(variant, Options)] ++
fate_data(Size div 5, Options))) [fate_map(Size, Options)
|| lists:member(map, Options) || lists:member(map, Options)])).
]).
fate_integer() -> {call, aeb_fate_data, make_integer, [oneof([int(), largeint()])]}. fate_integer() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_integer(X))).
fate_bits() -> {call, aeb_fate_data, make_bits, [oneof([int(), largeint()])]}. fate_bits() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_bits(X))).
fate_boolean() -> {call, aeb_fate_data, make_boolean, [elements([true, false])]}. fate_boolean() -> ?LET(X, elements([true, false]), return(aeb_fate_data:make_boolean(X))).
fate_nil() -> {call, aeb_fate_data, make_list, [[]]}. fate_nil() -> aeb_fate_data:make_list([]).
fate_unit() -> {call, aeb_fate_data, make_unit, []}. fate_unit() -> aeb_fate_data:make_unit().
fate_string() -> {call, aeb_fate_data, make_string, fate_string() -> ?LET(X, frequency([{10, non_quote_string()}, {2, list(non_quote_string())},
[frequency([{10, non_quote_string()}, {2, list(non_quote_string())}, {1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}]),
{1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}])]}. return(aeb_fate_data:make_string(X))).
fate_address() -> {call, aeb_fate_data, make_address, [non_zero_binary(256 div 8)]}. fate_address() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_address(X))).
fate_hash() -> {call, aeb_fate_data, make_hash, [non_zero_binary(32)]}. fate_bytes() -> ?LET(X, non_empty(binary()), return(aeb_fate_data:make_bytes(X))).
fate_signature() -> {call, aeb_fate_data, make_signature, [non_zero_binary(64)]}. fate_contract() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_contract(X))).
fate_contract() -> {call, aeb_fate_data, make_contract, [non_zero_binary(256 div 8)]}. fate_oracle() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle(X))).
fate_oracle() -> {call, aeb_fate_data, make_oracle, [non_zero_binary(256 div 8)]}. fate_oracle_q() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle_query(X))).
fate_name() -> {call, aeb_fate_data, make_name, [non_zero_binary(256 div 8)]}. fate_channel() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_channel(X))).
fate_channel() -> {call, aeb_fate_data, make_channel, [non_zero_binary(256 div 8)]}.
fate_values(Size, N, Options) ->
eqc_gen:list(N, fate_data(Size div max(1, N), Options)).
%% May shrink to fate_unit %% May shrink to fate_unit
fate_tuple(ListGen) -> fate_tuple(Size, Options) ->
{call, aeb_fate_data, make_tuple, [?LET(Elements, ListGen, list_to_tuple(Elements))]}. ?LET(N, choose(0, 6),
?LETSHRINK(Elements, fate_values(Size, N, Options),
return(aeb_fate_data:make_tuple(list_to_tuple(Elements))))).
fate_variant(ListGen) -> fate_variant(Size, Options) ->
?LET({L1, L2, TupleAsList}, {list(choose(0, 255)), list(choose(0,255)), ListGen}, ?LET({L1, L2, {tuple, Args}}, {list(choose(0, 255)), list(choose(0,255)), fate_tuple(Size, Options)},
{call, aeb_fate_data, make_variant, return(aeb_fate_data:make_variant(L1 ++ [tuple_size(Args)] ++ L2,
[L1 ++ [length(TupleAsList)] ++ L2, length(L1), list_to_tuple(TupleAsList)]}). length(L1), Args))).
fate_list(Gen) -> fate_list(Size, Options) ->
{call, aeb_fate_data, make_list, [frequency([{20, list(Gen)}, {1, ?LET(N, choose(64-3, 64+3), vector(N, Gen))}])]}. ?LET(N, frequency([{20, choose(0, 6)}, {1, choose(64 - 3, 64 + 3)}]),
?LETSHRINK(Vs, fate_values(Size, N, Options),
return(aeb_fate_data:make_list(Vs)))).
fate_map(KeyGen, ValGen) -> fate_map(Size, Options) ->
{call, aeb_fate_data, make_map, [map(KeyGen, ValGen)]}. ?LET(N, choose(0, 6),
?LETSHRINK(Values, fate_values(Size, N, Options),
?LET(Keys, vector(length(Values), fate_data(Size div max(1, N * 2), Options -- [map])),
non_zero_binary(N) -> return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
Bits = N*8,
?SUCHTHAT(Bin, binary(N), begin <<V:Bits>> = Bin, V =/= 0 end).
non_quote_string() -> non_quote_string() ->
?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []). ?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []).
char() -> char() ->
choose(1, 255). choose(1, 255).
injection(Binary) ->
?LET({N, Inj}, {choose(0, byte_size(Binary) - 1), choose(0,255)},
begin
M = N * 8,
<<X:M, _:8, Z/binary>> = Binary,
<<X:M, Inj:8, Z/binary>>
end).
is_empty(L) ->
?WHENFAIL(eqc:format("~p\n", [L]), L == []).
+17 -10
View File
@@ -11,9 +11,12 @@
-compile([export_all, nowarn_export_all]). -compile([export_all, nowarn_export_all]).
kind(X) when is_atom(X) -> X;
kind(T) when is_tuple(T) -> element(1, T).
prop_roundtrip() -> prop_roundtrip() ->
?FORALL(FateType, fate_type(), ?FORALL(FateType, fate_type(),
collect(FateType, collect(kind(FateType),
begin begin
Serialized = aeb_fate_encoding:serialize_type(FateType), Serialized = aeb_fate_encoding:serialize_type(FateType),
BinSerialized = list_to_binary(Serialized), BinSerialized = list_to_binary(Serialized),
@@ -32,18 +35,22 @@ fate_type(0) ->
oneof([integer, oneof([integer,
boolean, boolean,
address, address,
hash, {bytes, nat()},
signature,
contract, contract,
oracle, oracle,
name,
channel, channel,
bits, bits,
string]); string]);
fate_type(Size) -> fate_type(Size) ->
oneof([?LAZY(fate_type(Size div 2)), ?LAZY(
{list, ?LAZY(fate_type(Size div 2))}, oneof([fate_type(0),
{tuple, list(?LAZY(fate_type(Size div 2)))}, {list, fate_type(Size div 2)},
{variant, list(?LAZY(fate_type(Size div 2)))}, ?LETSHRINK(Ts, fate_types(Size), {tuple, Ts}),
?LETSHRINK([T1, T2], [?LAZY(fate_type(Size div 2)), ?LAZY(fate_type(Size div 2))], ?LETSHRINK(Ts, fate_types(Size), {variant, Ts}),
{map, T1, T2})]). ?LETSHRINK([T1, T2], vector(2, fate_type(Size div 2)),
{map, T1, T2})])).
fate_types(Size) ->
?LET(N, choose(0, 6),
eqc_gen:list(N, fate_type(Size div max(2, N)))).
+2 -2
View File
@@ -6,7 +6,7 @@
{deps, [ {eblake2, "1.0.0"} {deps, [ {eblake2, "1.0.0"}
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git", , {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
{ref, "6dce265"}}} {ref, "816bf99"}}}
, {getopt, "1.0.1"} , {getopt, "1.0.1"}
]}. ]}.
@@ -53,7 +53,7 @@
"/njs /njh /nfl /ndl & exit /b 0"} % silence things "/njs /njh /nfl /ndl & exit /b 0"} % silence things
]} ]}
]}, ]},
{eqc, [{erl_opts, [{parse_transform, eqc_cover}]}, {eqc, [{erl_opts, [{parse_transform, eqc_cover}, {d, 'EQC'}]},
{extra_src_dirs, ["quickcheck"]} %% May not be called eqc! {extra_src_dirs, ["quickcheck"]} %% May not be called eqc!
]} ]}
]}. ]}.
+1 -1
View File
@@ -1,7 +1,7 @@
{"1.1.0", {"1.1.0",
[{<<"aeserialization">>, [{<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://github.com/aeternity/aeserialization.git",
{ref,"6dce265753af4e651f77746e77ea125145c85dd3"}}, {ref,"816bf994ffb5cee218c3f22dc5fea296c9e0882e"}},
0}, 0},
{<<"base58">>, {<<"base58">>,
{git,"https://github.com/aeternity/erl-base58.git", {git,"https://github.com/aeternity/erl-base58.git",
+9 -3
View File
@@ -2,12 +2,12 @@
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc %%% @doc
%%% Encode and decode data and function calls according to %%% Encode and decode data and function calls according to
%%% Sophia-AEVM-ABI. %%% Sophia-AEVM-ABI
%%% @end %%% @end
%%% Created : 25 Jan 2018 %%% Created : 25 Jan 2018
%%% %%%
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_abi). -module(aeb_aevm_abi).
-define(HASH_SIZE, 32). -define(HASH_SIZE, 32).
-export([ create_calldata/4 -export([ create_calldata/4
@@ -19,6 +19,7 @@
, typereps_from_type_hash/2 , typereps_from_type_hash/2
, function_name_from_type_hash/2 , function_name_from_type_hash/2
, get_function_hash_from_calldata/1 , get_function_hash_from_calldata/1
, abi_version/0
]). ]).
-type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8. -type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8.
@@ -35,6 +36,11 @@
%%% API %%% API
%%%=================================================================== %%%===================================================================
%% Shall match ?ABI_AEVM_SOPHIA_1
-spec abi_version() -> integer().
abi_version() ->
1.
%%%=================================================================== %%%===================================================================
%%% Handle calldata %%% Handle calldata
@@ -43,7 +49,7 @@ create_calldata(FunName, Args, ArgTypes0, RetType) ->
<<TypeHashInt:?HASH_SIZE/unit:8>> = <<TypeHashInt:?HASH_SIZE/unit:8>> =
function_type_hash(list_to_binary(FunName), ArgTypes, RetType), function_type_hash(list_to_binary(FunName), ArgTypes, RetType),
Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}), Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}),
{ok, Data, {tuple, [word, ArgTypes]}, RetType}. {ok, Data}.
-spec check_calldata(binary(), type_info()) -> -spec check_calldata(binary(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', atom()}. {'ok', typerep(), typerep()} | {'error', atom()}.
+77
View File
@@ -0,0 +1,77 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Encode and decode data and function calls according to
%%% Sophia-FATE-ABI
%%% @end
%%% Created : 11 Jun 2019
%%%
%%%-------------------------------------------------------------------
-module(aeb_fate_abi).
-export([ create_calldata/2
, decode_calldata/2
, get_function_hash_from_calldata/1
, get_function_name_from_function_hash/2
, get_function_type_from_function_hash/2
, abi_version/0 ]).
-include("../include/aeb_fate_data.hrl").
%%%===================================================================
%%% API
%%%===================================================================
%% Shall match ?ABI_FATE_SOPHIA_1
-spec abi_version() -> integer().
abi_version() ->
3.
-spec create_calldata(list(), [term()]) -> {ok, binary()}.
create_calldata(FunName, Args) ->
FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)),
{ok, aeb_fate_encoding:serialize(
aeb_fate_data:make_tuple({FunctionId,
aeb_fate_data:make_tuple(list_to_tuple(Args))}))}.
-spec decode_calldata(list(), binary()) -> {ok, term()} | {error, term()}.
decode_calldata(FunName, Calldata) ->
FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)),
case ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(Calldata)) of
[FunctionId, FateArgs] -> {ok, ?FATE_TUPLE_ELEMENTS(FateArgs)};
_ -> {error, decode_error}
end.
-spec get_function_name_from_function_hash(binary(), aeb_fate_code:fcode()) ->
{ok, term()} | {error, term()}.
get_function_name_from_function_hash(<<SymbolHash:4/binary, _:28/binary>>, FateCode) ->
get_function_name_from_function_hash(SymbolHash, FateCode);
get_function_name_from_function_hash(SymbolHash = <<_:4/binary>>, FateCode) ->
Symbols = aeb_fate_code:symbols(FateCode),
case maps:get(SymbolHash, Symbols, undefined) of
undefined -> {error, no_function_matching_function_hash};
Function -> {ok, Function}
end.
-spec get_function_hash_from_calldata(binary()) ->
{ok, binary()} | {error, term()}.
get_function_hash_from_calldata(CallData) ->
try ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(CallData)) of
[FunHash, _Args] -> {ok, FunHash};
_ -> {error, bad_calldata}
catch _:_ ->
{error, bad_calldata}
end.
-spec get_function_type_from_function_hash(binary(), aeb_fate_code:fcode()) ->
{ok, term(), term()} | {error, term()}.
get_function_type_from_function_hash(<<SymbolHash:4/binary, _:28/binary>>, FateCode) ->
get_function_type_from_function_hash(SymbolHash, FateCode);
get_function_type_from_function_hash(SymbolHash, FateCode) ->
Functions = aeb_fate_code:functions(FateCode),
case maps:get(SymbolHash, Functions, undefined) of
undefined ->
{error, no_function_matching_function_hash};
{{ArgTypes, RetType}, _Code} ->
{ok, ArgTypes, RetType}
end.
+64 -585
View File
@@ -30,12 +30,10 @@
%%% arg0 %%% arg0
%%% References to variables/registers start with var followed by an integer %%% References to variables/registers start with var followed by an integer
%%% var0 %%% var0
%%% References to stack postions is either a (for stack 0) %%% References to the top of the stack is the letter a (for accumulator)
%%% or start with stack followed by an integer
%%% stack1
%%% a %%% a
%%% %%%
%%% Immediate values can be of 11 types: %%% Immediate values can be of 10 types:
%%% 1a. Integers as decimals: {Digits} or -{Digits} %%% 1a. Integers as decimals: {Digits} or -{Digits}
%%% 42 %%% 42
%%% -2374683271468723648732648736498712634876147 %%% -2374683271468723648732648736498712634876147
@@ -44,7 +42,7 @@
%%% 2a. account addresses, a base58c encoded string prefixed with @ak_ %%% 2a. account addresses, a base58c encoded string prefixed with @ak_
%%% 2b. contract address: @ct_{base58char}+ %%% 2b. contract address: @ct_{base58char}+
%%% 2c. oracle address: @ok_{base58char}+ %%% 2c. oracle address: @ok_{base58char}+
%%% 2d. name address: @nm_{base58char}+ %%% 2d. oracle query id: @oq_{base58char}+
%%% 2e. channel address: @ch_{base58char}+ %%% 2e. channel address: @ch_{base58char}+
%%% 3. Boolean true or false %%% 3. Boolean true or false
%%% true %%% true
@@ -67,15 +65,15 @@
%%% (1, "foo") %%% (1, "foo")
%%% 9. Variants: (| [Arities] | Tag | ( Elements ) |) %%% 9. Variants: (| [Arities] | Tag | ( Elements ) |)
%%% (| [0,1,2] | 2 | ( "foo", 12) |) %%% (| [0,1,2] | 2 | ( "foo", 12) |)
%%% 10. Hashes: #{base64char}+ %%% 10. Bytes: #{base64char}+
%%% #AQIDCioLFQ== %%% #AQIDCioLFQ==
%%% 11. Signatures: $sg_{base58char}+
%%% %%%
%%% Where Digits: [0123456789] %%% Where Digits: [0123456789]
%%% Hexdigits: [0123456789abcdef] %%% Hexdigits: [0123456789abcdef]
%%% base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz] %%% base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
%%% base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=] %%% base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=]
%%% Characters any printable ascii character 0..255 (except " no quoting yet) %%% Characters: as a code literal - any printable ascii character 0..255 (except " no quoting yet)
%%% the type supports an array of bytes (all values 0..255).
%%% Key: any value except for a map %%% Key: any value except for a map
%%% Bits: 01 or space %%% Bits: 01 or space
%%% Elements: Nothing or Value , Elements %%% Elements: Nothing or Value , Elements
@@ -89,13 +87,11 @@
-export([ assemble_file/3 -export([ assemble_file/3
, asm_to_bytecode/2 , asm_to_bytecode/2
, bytecode_to_fate_code/2
, function_call/1 , function_call/1
, pp/1 , pp/1
, read_file/1 , read_file/1
, strip/1 , strip/1
, to_asm/1 , to_asm/1
, to_hexstring/1
]). ]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). -include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
@@ -132,9 +128,10 @@ pp(FateCode) ->
io_lib:format("~ts~n",[Listing]). io_lib:format("~ts~n",[Listing]).
to_asm(#{ functions := Functions to_asm(FateCode) ->
, symbols := Symbols Functions = aeb_fate_code:functions(FateCode),
, annotations := Annotations} = _FateCode) -> Symbols = aeb_fate_code:symbols(FateCode),
Annotations = aeb_fate_code:annotations(FateCode),
insert_comments(get_comments(Annotations), 1, insert_comments(get_comments(Annotations), 1,
lists:flatten( lists:flatten(
io_lib:format("~s", io_lib:format("~s",
@@ -150,12 +147,6 @@ insert_comments([],_,[]) -> [];
insert_comments([{L,C}|Rest], _, []) -> insert_comments([{L,C}|Rest], _, []) ->
";; " ++ C ++ "\n" ++ insert_comments(Rest, L + 1, []). ";; " ++ C ++ "\n" ++ insert_comments(Rest, L + 1, []).
format_functions(Functions, Symbols) -> format_functions(Functions, Symbols) ->
[format(lookup(Name, Symbols), [format(lookup(Name, Symbols),
Sig, Sig,
@@ -218,521 +209,22 @@ asm_to_bytecode(AssemblerCode, Options) ->
none -> none ->
ok ok
end, end,
Env = #{ fate_code => aeb_fate_code:new()
, functions => #{}
},
Env = to_bytecode(Tokens, none, #{ functions => #{} Env1 = to_bytecode(Tokens, none, Env, [], Options),
, symbols => #{} FateCode = maps:get(fate_code, Env1),
, annotations => #{} FunctionsMap = maps:get(functions, Env1),
}, [], Options), Functions = [X || {_, X} <- lists:sort(maps:to_list(FunctionsMap))],
FunctionsBin = iolist_to_binary(Functions),
ByteList = serialize(Env), ByteCode = aeb_fate_code:serialize(FateCode, FunctionsBin, Options),
Signatures = serialize_sigs(Env),
SymbolTable = serialize_symbol_table(Env),
Annotatations = serialize_annotations(Env),
ByteCode = << (aeser_rlp:encode(list_to_binary(ByteList)))/binary,
(aeser_rlp:encode(list_to_binary(Signatures)))/binary,
(aeser_rlp:encode(SymbolTable))/binary,
(aeser_rlp:encode(Annotatations))/binary
>>,
case proplists:lookup(pp_hex_string, Options) of
{pp_hex_string, true} ->
io:format("Code: ~s~n",[to_hexstring(ByteList)]);
none ->
ok
end,
{Env, ByteCode}. {Env, ByteCode}.
strip(ByteCode) -> strip(ByteCode) ->
{Code, _Rest} = aeser_rlp:decode_one(ByteCode), {Code, _Rest} = aeser_rlp:decode_one(ByteCode),
Code. Code.
bytecode_to_fate_code(Bytes, _Options) ->
{ByteCode, Rest1} = aeser_rlp:decode_one(Bytes),
{Signatures, Rest2} = aeser_rlp:decode_one(Rest1),
{SymbolTable, Rest3} = aeser_rlp:decode_one(Rest2),
{Annotations, <<>>} = aeser_rlp:decode_one(Rest3),
Env1 = deserialize(ByteCode, #{ function => none
, bb => 0
, current_bb_code => []
, functions => #{}
, code => #{}
}),
Env2 = deserialize_signatures(Signatures, Env1),
Env3 = deserialize_symbols(SymbolTable, Env2),
Env4 = deserialize_annotations(Annotations, Env3),
Env4.
deserialize(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
#{ function := none
, bb := 0
, current_bb_code := []
} = Env) ->
{Sig, Rest2} = deserialize_signature(Rest),
Env2 = Env#{function => {<<A,B,C,D>>, Sig}},
deserialize(Rest2, Env2);
deserialize(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
#{ function := {F, Sig}
, bb := BB
, current_bb_code := Code
, code := Program
, functions := Funs} = Env) ->
{NewSig, Rest2} = deserialize_signature(Rest),
case Code of
[] ->
Env2 = Env#{ bb => 0
, current_bb_code => []
, function => {<<A,B,C,D>>, NewSig}
, code => #{}
, functions => Funs#{F => {Sig, Program}}},
deserialize(Rest2, Env2);
_ ->
Env2 = Env#{ bb => 0
, current_bb_code => []
, function => {<<A,B,C,D>>, NewSig}
, code => #{}
, functions =>
Funs#{F => {Sig,
Program#{ BB => lists:reverse(Code)}}}},
deserialize(Rest2, Env2)
end;
deserialize(<<Op:8, Rest/binary>>,
#{ bb := BB
, current_bb_code := Code
, code := Program} = Env) ->
{Rest2, OpCode} = deserialize_op(Op, Rest, Code),
case aeb_fate_opcodes:end_bb(Op) of
true ->
deserialize(Rest2, Env#{ bb => BB+1
, current_bb_code => []
, code => Program#{BB =>
lists:reverse(OpCode)}});
false ->
deserialize(Rest2, Env#{ current_bb_code => OpCode})
end;
deserialize(<<>>, #{ function := {F, Sig}
, bb := BB
, current_bb_code := Code
, code := Program
, functions := Funs} = Env) ->
FunctionCode =
case Code of
[] -> Program;
_ -> Program#{ BB => lists:reverse(Code)}
end,
Env#{ bb => 0
, current_bb_code => []
, function => none
, code => #{}
, functions => Funs#{F => {Sig, FunctionCode}}}.
deserialize_op(?SWITCH_VN, Rest, Code) ->
<<ArgType:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
case aeb_fate_encoding:deserialize_one(Rest3) of
{L, Rest4} when is_list(L) ->
Modifier0 = bits_to_modifier(ArgType band 2#11),
immediate = bits_to_modifier((ArgType bsr 2) band 2#11),
{Rest4, [{aeb_fate_opcodes:mnemonic(?SWITCH_VN)
, {Modifier0, Arg0}
, {immediate, L}
}
| Code]};
_ -> exit(bad_argument_to_switch_vn)
end;
deserialize_op(Op, Rest, Code) ->
OpName = aeb_fate_opcodes:mnemonic(Op),
case aeb_fate_opcodes:args(Op) of
0 -> {Rest, [OpName | Code]};
1 ->
<<ArgType:8, Rest2/binary>> = Rest,
{Arg, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
Modifier = bits_to_modifier(ArgType),
{Rest3, [{OpName, {Modifier, Arg}} | Code]};
2 ->
<<ArgType:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
{Rest4, [{OpName, {Modifier0, Arg0},
{Modifier1, Arg1}} | Code]};
3 ->
<<ArgType:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
{Rest5, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}}
| Code]};
4 ->
<<ArgType:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
{Rest6, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}}
| Code]};
5 ->
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
{Rest7, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}
, {Modifier4, Arg4}
}
| Code]};
6 ->
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
{Rest8, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}
, {Modifier4, Arg4}
, {Modifier5, Arg5}
}
| Code]};
7 ->
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
{Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11),
{Rest9, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}
, {Modifier4, Arg4}
, {Modifier5, Arg5}
, {Modifier6, Arg6}
}
| Code]};
8 ->
<<ArgType:8, ArgType2:8, Rest2/binary>> = Rest,
{Arg0, Rest3} = aeb_fate_encoding:deserialize_one(Rest2),
{Arg1, Rest4} = aeb_fate_encoding:deserialize_one(Rest3),
{Arg2, Rest5} = aeb_fate_encoding:deserialize_one(Rest4),
{Arg3, Rest6} = aeb_fate_encoding:deserialize_one(Rest5),
{Arg4, Rest7} = aeb_fate_encoding:deserialize_one(Rest6),
{Arg5, Rest8} = aeb_fate_encoding:deserialize_one(Rest7),
{Arg6, Rest9} = aeb_fate_encoding:deserialize_one(Rest8),
{Arg7, Rest10} = aeb_fate_encoding:deserialize_one(Rest9),
Modifier0 = bits_to_modifier(ArgType band 2#11),
Modifier1 = bits_to_modifier((ArgType bsr 2) band 2#11),
Modifier2 = bits_to_modifier((ArgType bsr 4) band 2#11),
Modifier3 = bits_to_modifier((ArgType bsr 6) band 2#11),
Modifier4 = bits_to_modifier(ArgType2 band 2#11),
Modifier5 = bits_to_modifier((ArgType2 bsr 2) band 2#11),
Modifier6 = bits_to_modifier((ArgType2 bsr 4) band 2#11),
Modifier7 = bits_to_modifier((ArgType2 bsr 6) band 2#11),
{Rest10, [{ OpName
, {Modifier0, Arg0}
, {Modifier1, Arg1}
, {Modifier2, Arg2}
, {Modifier3, Arg3}
, {Modifier4, Arg4}
, {Modifier5, Arg5}
, {Modifier6, Arg6}
, {Modifier7, Arg7}
}
| Code]}
end.
deserialize_signatures(_Signatures, Env) -> Env.
deserialize_symbols(Table, Env) ->
?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table),
Env#{symbols => SymbolTable}.
deserialize_annotations(AnnotationsBin, Env) ->
?FATE_MAP_VALUE(Annotations) = aeb_fate_encoding:deserialize(AnnotationsBin),
Env#{annotations => Annotations}.
serialize_sigs(_Env) -> [].
serialize_symbol_table(#{ symbols := Symbols }) ->
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)).
serialize_annotations(#{ annotations := Annotations}) ->
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)).
serialize(#{functions := Functions} =_Env) ->
%% Sort the functions oon name to get a canonical serialisation.
Code = [[?FUNCTION, Name, serialize_signature(Sig), C] ||
{Name, {Sig, C}} <- lists:sort(maps:to_list(Functions))],
serialize_code(lists:flatten(Code)).
%% Argument encoding
%% Agument Specification Byte
%% bitpos: 6 4 2 0
%% xx xx xx xx
%% Arg3 Arg2 Arg1 Arg0
%% For 5-8 args another Argument Spec Byte is used
%% Bit pattern
%% 00 : stack/unused (depending on instruction)
%% 01 : argN
%% 10 : varN
%% 11 : immediate
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}
, {Arg4Type, Arg4}
, {Arg5Type, Arg5}
, {Arg6Type, Arg6}
, {Arg7Type, Arg7}
| Rest]) ->
ArgSpec1 =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
ArgSpec2 =
modifier_bits(Arg4Type) bor
(modifier_bits(Arg5Type) bsl 2) bor
(modifier_bits(Arg6Type) bsl 4) bor
(modifier_bits(Arg7Type) bsl 6),
[ ArgSpec1
, ArgSpec2
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
, serialize_data(Arg4Type, Arg4)
, serialize_data(Arg5Type, Arg5)
, serialize_data(Arg6Type, Arg6)
, serialize_data(Arg7Type, Arg7)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}
, {Arg4Type, Arg4}
, {Arg5Type, Arg5}
, {Arg6Type, Arg6}
| Rest]) ->
ArgSpec1 =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
ArgSpec2 =
modifier_bits(Arg4Type) bor
(modifier_bits(Arg5Type) bsl 2) bor
(modifier_bits(Arg6Type) bsl 4),
[ ArgSpec1
, ArgSpec2
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
, serialize_data(Arg4Type, Arg4)
, serialize_data(Arg5Type, Arg5)
, serialize_data(Arg6Type, Arg6)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}
, {Arg4Type, Arg4}
, {Arg5Type, Arg5}
| Rest]) ->
ArgSpec1 =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
ArgSpec2 =
modifier_bits(Arg4Type) bor
(modifier_bits(Arg5Type) bsl 2),
[ ArgSpec1
, ArgSpec2
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
, serialize_data(Arg4Type, Arg4)
, serialize_data(Arg5Type, Arg5)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}
, {Arg4Type, Arg4}
| Rest]) ->
ArgSpec1 =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
ArgSpec2 =
modifier_bits(Arg4Type),
[ ArgSpec1
, ArgSpec2
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
, serialize_data(Arg4Type, Arg4)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
, {Arg3Type, Arg3}| Rest]) ->
ArgSpec =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4) bor
(modifier_bits(Arg3Type) bsl 6),
[ ArgSpec
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
, serialize_data(Arg3Type, Arg3)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
, {Arg2Type, Arg2}
| Rest]) ->
ArgSpec =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2) bor
(modifier_bits(Arg2Type) bsl 4),
[ArgSpec
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
, serialize_data(Arg2Type, Arg2)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0}
, {Arg1Type, Arg1}
| Rest]) ->
ArgSpec =
modifier_bits(Arg0Type) bor
(modifier_bits(Arg1Type) bsl 2),
[ArgSpec
, serialize_data(Arg0Type, Arg0)
, serialize_data(Arg1Type, Arg1)
| serialize_code(Rest)];
serialize_code([ {Arg0Type, Arg0} | Rest]) ->
ArgSpec =
modifier_bits(Arg0Type),
[ArgSpec
, serialize_data(Arg0Type, Arg0)
| serialize_code(Rest)];
serialize_code([ ?SWITCH_VN
, {Arg0Type, Arg0}
, {immediate, L}
| Rest]) ->
ArgSpec =
modifier_bits(Arg0Type) bor
(modifier_bits(immediate) bsl 2),
[?SWITCH_VN
, ArgSpec
, serialize_data(Arg0Type, Arg0)
, serialize_data(immediate, L)] ++ serialize_code(Rest);
serialize_code([B|Rest]) ->
[B | serialize_code(Rest)];
serialize_code([]) -> [].
%% 00 : stack/unused (depending on instruction)
%% 01 : argN
%% 10 : varN
%% 11 : immediate
modifier_bits(immediate) -> 2#11;
modifier_bits(var) -> 2#10;
modifier_bits(arg) -> 2#01;
modifier_bits(stack) -> 2#00.
bits_to_modifier(2#11) -> immediate;
bits_to_modifier(2#10) -> var;
bits_to_modifier(2#01) -> arg;
bits_to_modifier(2#00) -> stack.
serialize_data(_, Data) ->
aeb_fate_encoding:serialize(Data).
serialize_signature({Args, RetType}) ->
[aeb_fate_encoding:serialize_type({tuple, Args}) |
aeb_fate_encoding:serialize_type(RetType)].
deserialize_signature(Binary) ->
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary),
{RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest),
{{Args, RetType}, Rest2}.
to_hexstring(ByteList) ->
"0x" ++ lists:flatten(
[io_lib:format("~2.16.0b", [X])
|| X <- ByteList]).
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
%% Parser %% Parser
%% Asm tokens -> Fate code env %% Asm tokens -> Fate code env
@@ -749,8 +241,8 @@ to_bytecode([{arg,_line, N}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts); to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts);
to_bytecode([{var,_line, N}|Rest], Address, Env, Code, Opts) -> to_bytecode([{var,_line, N}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{var, N}|Code], Opts); to_bytecode(Rest, Address, Env, [{var, N}|Code], Opts);
to_bytecode([{stack,_line, N}|Rest], Address, Env, Code, Opts) -> to_bytecode([{stack,_line}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{stack, N}|Code], Opts); to_bytecode(Rest, Address, Env, [{stack, 0}|Code], Opts);
to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) -> to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{immediate, Int}|Code], Opts); to_bytecode(Rest, Address, Env, [{immediate, Int}|Code], Opts);
to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) -> to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) ->
@@ -774,29 +266,24 @@ to_bytecode([{object,_line, {oracle, Value}}|Rest],
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_oracle(Value)}|Code], [{immediate, aeb_fate_data:make_oracle(Value)}|Code],
Opts); Opts);
to_bytecode([{object,_line, {name, Value}}|Rest], to_bytecode([{object,_line, {oracle_query, Value}}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_name(Value)}|Code], [{immediate, aeb_fate_data:make_oracle_query(Value)}|Code],
Opts); Opts);
to_bytecode([{object,_line, {channel, Value}}|Rest], to_bytecode([{object,_line, {channel, Value}}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_contract(Value)}|Code], [{immediate, aeb_fate_data:make_contract(Value)}|Code],
Opts); Opts);
to_bytecode([{hash,_line, Value}|Rest], to_bytecode([{bytes,_line, Value}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_hash(Value)}|Code], [{immediate, aeb_fate_data:make_bytes(Value)}|Code],
Opts);
to_bytecode([{signature,_line, {signature, Value}}|Rest],
Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_signature(Value)}|Code],
Opts); Opts);
to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) ->
{Hash, Env2} = insert_symbol(ID, Env), {Env2, Id} = insert_symbol(list_to_binary(ID), Env),
to_bytecode(Rest, Address, Env2, [{immediate, Hash}|Code], Opts); to_bytecode(Rest, Address, Env2, [{immediate, Id}|Code], Opts);
to_bytecode([{'{',_line}|Rest], Address, Env, Code, Opts) -> to_bytecode([{'{',_line}|Rest], Address, Env, Code, Opts) ->
{Map, Rest2} = parse_map(Rest), {Map, Rest2} = parse_map(Rest),
to_bytecode(Rest2, Address, Env, [{immediate, Map}|Code], Opts); to_bytecode(Rest2, Address, Env, [{immediate, Map}|Code], Opts);
@@ -811,6 +298,10 @@ to_bytecode([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) ->
{Arities, Tag, Values, Rest} = parse_variant(Tokens), {Arities, Tag, Values, Rest} = parse_variant(Tokens),
Variant = aeb_fate_data:make_variant(Arities, Tag, Values), Variant = aeb_fate_data:make_variant(Arities, Tag, Values),
to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts); to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts);
to_bytecode([{typerep,_line}|Rest], Address, Env, Code, Opts) ->
{Type, Rest1} = to_type(Rest),
TypeRep = aeb_fate_data:make_typerep(Type),
to_bytecode(Rest1, Address, Env, [{immediate, TypeRep}|Code], Opts);
to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) -> to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts); [{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts);
@@ -819,17 +310,8 @@ to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) ->
Env2 = insert_annotation(comment, Line, Comment, Env), Env2 = insert_annotation(comment, Line, Comment, Env),
to_bytecode(Rest, Address, Env2, Code, Opts); to_bytecode(Rest, Address, Env2, Code, Opts);
to_bytecode([], Address, Env, Code, Opts) -> to_bytecode([], Address, Env, Code,_Opts) ->
Env2 = insert_fun(Address, Code, Env), insert_fun(Address, Code, Env).
#{functions := Funs} = Env2,
case proplists:lookup(pp_opcodes, Opts) of
{pp_opcodes, true} ->
Ops = [C || {_Name, {_Sig, C}} <- maps:to_list(Funs)],
io:format("opcodes ~p~n", [Ops]);
none ->
ok
end,
Env2.
parse_map([{'}',_line}|Rest]) -> parse_map([{'}',_line}|Rest]) ->
{#{}, Rest}; {#{}, Rest};
@@ -906,20 +388,22 @@ parse_value([{start_variant,_line}|_] = Tokens) ->
{Variant, Rest}; {Variant, Rest};
parse_value([{string,_line, String} | Rest]) -> parse_value([{string,_line, String} | Rest]) ->
{aeb_fate_data:make_string(String), Rest}; {aeb_fate_data:make_string(String), Rest};
parse_value([{address,_line, {address, Address}} | Rest]) -> parse_value([{object,_line, {address, Address}} | Rest]) ->
{aeb_fate_data:make_address(Address), Rest}; {aeb_fate_data:make_address(Address), Rest};
parse_value([{address,_line, {contract, Address}} | Rest]) -> parse_value([{object,_line, {contract, Address}} | Rest]) ->
{aeb_fate_data:make_contract(Address), Rest}; {aeb_fate_data:make_contract(Address), Rest};
parse_value([{address,_line, {oracle, Address}} | Rest]) -> parse_value([{object,_line, {oracle, Address}} | Rest]) ->
{aeb_fate_data:make_oracle(Address), Rest}; {aeb_fate_data:make_oracle(Address), Rest};
parse_value([{address,_line, {name, Address}} | Rest]) -> parse_value([{object,_line, {oracle_query, Address}} | Rest]) ->
{aeb_fate_data:make_name(Address), Rest}; {aeb_fate_data:make_oracle_query(Address), Rest};
parse_value([{address,_line, {channel, Address}} | Rest]) -> parse_value([{object,_line, {channel, Address}} | Rest]) ->
{aeb_fate_data:make_channel(Address), Rest}; {aeb_fate_data:make_channel(Address), Rest};
parse_value([{hash,_line, Hash} | Rest]) -> parse_value([{hash,_line, Hash} | Rest]) ->
{aeb_fate_data:make_hash(Hash), Rest}; {aeb_fate_data:make_hash(Hash), Rest};
parse_value([{signature,_line, Hash} | Rest]) -> parse_value([{signature,_line, Hash} | Rest]) ->
{aeb_fate_data:make_signature(Hash), Rest}. {aeb_fate_data:make_signature(Hash), Rest};
parse_value([{typerep,_line} | Rest]) ->
to_type(Rest).
to_fun_def([{id, _, Name}, {'(', _} | Rest]) -> to_fun_def([{id, _, Name}, {'(', _} | Rest]) ->
{ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest), {ArgsType, [{'to', _} | Rest2]} = to_arg_types(Rest),
@@ -945,6 +429,7 @@ to_type([{id, _, "string"} | Rest]) -> {string, Rest};
to_type([{id, _, "address"} | Rest]) -> {address, Rest}; to_type([{id, _, "address"} | Rest]) -> {address, Rest};
to_type([{id, _, "contract"} | Rest]) -> {contract, Rest}; to_type([{id, _, "contract"} | Rest]) -> {contract, Rest};
to_type([{id, _, "oracle"} | Rest]) -> {oracle, Rest}; to_type([{id, _, "oracle"} | Rest]) -> {oracle, Rest};
to_type([{id, _, "oracle_query"} | Rest]) -> {oracle_query, Rest};
to_type([{id, _, "name"} | Rest]) -> {name, Rest}; to_type([{id, _, "name"} | Rest]) -> {name, Rest};
to_type([{id, _, "channel"} | Rest]) -> {channel, Rest}; to_type([{id, _, "channel"} | Rest]) -> {channel, Rest};
to_type([{id, _, "hash"} | Rest]) -> {hash, Rest}; to_type([{id, _, "hash"} | Rest]) -> {hash, Rest};
@@ -963,6 +448,9 @@ to_type([{'{', _}, {id, _, "map"}, {',', _} | Rest]) ->
{KeyType, [{',', _}| Rest2]} = to_type(Rest), {KeyType, [{',', _}| Rest2]} = to_type(Rest),
{ValueType, [{'}', _}| Rest3]} = to_type(Rest2), {ValueType, [{'}', _}| Rest3]} = to_type(Rest2),
{{map, KeyType, ValueType}, Rest3}; {{map, KeyType, ValueType}, Rest3};
to_type([{'{', _}, {id, _, "bytes"}, {',', _}, {int, _, Size}, {'}', _} | Rest]) ->
%% TODO: Error handling
{{bytes, Size}, Rest};
to_type([{'{', _} to_type([{'{', _}
, {id, _, "variant"} , {id, _, "variant"}
, {',', _} , {',', _}
@@ -990,11 +478,24 @@ to_list_of_types(Tokens) ->
%% State handling %% State handling
insert_fun(none, [], Env) -> Env; insert_fun(none, [], Env) -> Env;
insert_fun({Name, Type, RetType}, Code, #{functions := Functions} = Env) -> insert_fun({NameString, ArgType, RetType}, Code, #{ fate_code := FateCode
{Hash, Env2} = insert_symbol(Name, Env), , functions := Funs} = Env) ->
Env2#{ Name = list_to_binary(NameString),
functions => Functions#{Hash => {{Type, RetType}, lists:reverse(Code)}} {FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode),
}. BodyByteCode = aeb_fate_code:serialize_code(lists:reverse(Code)),
SigByteCode = aeb_fate_code:serialize_signature({ArgType, RetType}),
FunByteCode = [?FUNCTION, Id, SigByteCode, BodyByteCode],
Env#{ functions => Funs#{ Id => FunByteCode }
, fate_code => FateCode1}.
insert_symbol(Name, #{ fate_code := FateCode } = Env) ->
{FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode),
{ Env#{ fate_code => FateCode1 }
, Id}.
insert_annotation(comment, Line, Comment, #{ fate_code := FateCode } = Env) ->
FateCode1 = aeb_fate_code:insert_annotation(comment, Line, Comment, FateCode),
Env#{ fate_code => FateCode1}.
mk_hash(Id) -> mk_hash(Id) ->
%% Use first 4 bytes of blake hash %% Use first 4 bytes of blake hash
@@ -1003,33 +504,11 @@ mk_hash(Id) ->
%% Handle annotations %% Handle annotations
insert_annotation(comment, Line, Comment, #{annotations := A} = Env) ->
Key = aeb_fate_data:make_tuple({aeb_fate_data:make_string("comment"), Line}),
Value = aeb_fate_data:make_string(Comment),
Env#{annotations => A#{ Key => Value}}.
get_comments(Annotations) -> get_comments(Annotations) ->
[ {Line, Comment} || [ {Line, Comment} ||
{?FATE_TUPLE({?FATE_STRING_VALUE("comment"), Line}), {?FATE_TUPLE({?FATE_STRING_VALUE("comment"), Line}),
?FATE_STRING_VALUE(Comment)} <- maps:to_list(Annotations)]. ?FATE_STRING_VALUE(Comment)} <- maps:to_list(Annotations)].
%% Symbols handling
insert_symbol(Id, Env) ->
Hash = mk_hash(Id),
insert_symbol(Id, Hash, Env).
insert_symbol(Id, Hash, #{symbols := Symbols} = Env) ->
case maps:find(Hash, Symbols) of
{ok, Id} -> {Hash, Env};
{ok, Id2} ->
%% Very unlikely...
exit({two_symbols_with_same_hash, Id, Id2});
error ->
{Hash, Env#{symbols => Symbols#{ Id => Hash
, Hash => Id}}}
end.
%% Symbol table handling %% Symbol table handling
lookup(Name, Symbols) -> lookup(Name, Symbols) ->
+7 -10
View File
@@ -17,8 +17,7 @@ BASE64 = [A-Za-z0-9+/=]
INT = {DIGIT}+ INT = {DIGIT}+
HEX = 0x{HEXDIGIT}+ HEX = 0x{HEXDIGIT}+
OBJECT = @[a-z][a-z]_{BASE58}+ OBJECT = @[a-z][a-z]_{BASE58}+
HASH = #{BASE64}+ BYTES = #{BASE64}+
SIG = \$sg_{BASE58}+
WS = [\000-\s] WS = [\000-\s]
ID = {LOWER}[a-zA-Z0-9_]* ID = {LOWER}[a-zA-Z0-9_]*
STRING = "[^"]*" STRING = "[^"]*"
@@ -27,8 +26,7 @@ BITS = (\!)?\<[\s01]*\>
Rules. Rules.
arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}. arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}.
var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}. var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}.
a : {token, {stack, TokenLine, 0}}. a : {token, {stack, TokenLine}}.
a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}.
true : {token, {boolean, TokenLine, true}}. true : {token, {boolean, TokenLine, true}}.
false : {token, {boolean, TokenLine, false}}. false : {token, {boolean, TokenLine, false}}.
@@ -37,10 +35,8 @@ false : {token, {boolean, TokenLine, false}}.
FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}. FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
{HASH} : {BYTES} :
{token, {hash, TokenLine, parse_hash(TokenChars)}}. {token, {bytes, TokenLine, parse_hash(TokenChars)}}.
{SIG} :
{token, {signature, TokenLine, parse_object(TokenChars)}}.
{OBJECT} : {OBJECT} :
{token, {object, TokenLine, parse_object(TokenChars)}}. {token, {object, TokenLine, parse_object(TokenChars)}}.
{ID} : {ID} :
@@ -76,6 +72,7 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
\{ : {token, {'{', TokenLine}}. \{ : {token, {'{', TokenLine}}.
\} : {token, {'}', TokenLine}}. \} : {token, {'}', TokenLine}}.
\| : {token, {'|', TokenLine}}. \| : {token, {'|', TokenLine}}.
\' : {token, {typerep, TokenLine}}.
;;.* : ;;.* :
{token, {comment, TokenLine, drop_prefix($;, TokenChars)}}. {token, {comment, TokenLine, drop_prefix($;, TokenChars)}}.
@@ -108,7 +105,7 @@ parse_int(Chars) -> list_to_integer(Chars).
parse_arg("arg" ++ N) -> list_to_integer(N). parse_arg("arg" ++ N) -> list_to_integer(N).
parse_var("var" ++ N) -> list_to_integer(N). parse_var("var" ++ N) -> list_to_integer(N).
parse_acc("a" ++ N) -> list_to_integer(N).
parse_hash("#" ++ Chars) -> parse_hash("#" ++ Chars) ->
@@ -119,7 +116,7 @@ parse_object([_|Chars]) ->
{account_pubkey, Bin} -> {address, Bin}; {account_pubkey, Bin} -> {address, Bin};
{contract_pubkey, Bin} -> {contract, Bin}; {contract_pubkey, Bin} -> {contract, Bin};
{oracle_pubkey, Bin} -> {oracle, Bin}; {oracle_pubkey, Bin} -> {oracle, Bin};
{name, Bin} -> {name, Bin}; {oracle_query_id, Bin} -> {oracle_query, Bin};
{channel, Bin} -> {channel, Bin}; {channel, Bin} -> {channel, Bin};
{signature, Bin} -> {signature, Bin} {signature, Bin} -> {signature, Bin}
end. end.
+420
View File
@@ -0,0 +1,420 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% ADT for fate byte code/fate code
%%% @end
%%%
%%%-------------------------------------------------------------------
-module(aeb_fate_code).
-export([ annotations/1
, deserialize/1
, functions/1
, insert_annotation/4
, insert_fun/4
, insert_symbol/2
, new/0
, serialize/1
, serialize/2
, serialize/3
, serialize_code/1
, serialize_signature/1
, symbol_identifier/1
, symbols/1
]).
-include("../include/aeb_fate_opcodes.hrl").
-include("../include/aeb_fate_data.hrl").
-ifdef(EQC).
-export([ update_annotations/2
, update_functions/2
, update_symbols/2]).
-endif.
-record(fcode, { functions = #{} :: map()
, symbols = #{} :: map()
, annotations = #{} :: map()
}).
-define(HASH_BYTES, 32).
-type fcode() :: #fcode{}.
-export_type([fcode/0]).
%%%===================================================================
%%% API
%%%===================================================================
new() ->
#fcode{}.
annotations(#fcode{ annotations = As }) ->
As.
functions(#fcode{ functions = Fs }) ->
Fs.
symbols(#fcode{ symbols = Ss}) ->
Ss.
update_annotations(#fcode{ annotations = As } = FCode, Anns) ->
FCode#fcode{ annotations = maps:merge(As, Anns) }.
update_functions(#fcode{ functions = Fs } = FCode, Funs) ->
FCode#fcode{ functions = maps:merge(Fs, Funs) }.
update_symbols(#fcode{ symbols = Ss } = FCode, Symbs) ->
FCode#fcode{ symbols = maps:merge(Ss, Symbs) }.
symbol_identifier(Bin) ->
%% First 4 bytes of blake hash
{ok, <<X:4/binary,_/binary>> } = eblake2:blake2b(?HASH_BYTES, Bin),
X.
insert_fun(Name, {ArgType, RetType}, #{} = BBs, FCode) ->
{F1, ID} = insert_symbol(Name, FCode),
update_functions(F1, #{ID => {{ArgType, RetType}, BBs}}).
insert_symbol(Name, #fcode{ symbols = Syms } = F) ->
ID = symbol_identifier(Name),
case maps:find(ID, Syms) of
{ok, Name} ->
{F, ID};
{ok, X} ->
error({two_symbols_with_same_hash, Name, X});
error ->
{update_symbols(F, #{ID => Name}), ID}
end.
insert_annotation(comment =_Type, Line, Comment, FCode) ->
Key = aeb_fate_data:make_tuple({aeb_fate_data:make_string("comment"), Line}),
Value = aeb_fate_data:make_string(Comment),
update_annotations(FCode, #{ Key => Value }).
%%%===================================================================
%%% Serialization
%%%===================================================================
serialize(#fcode{} = F) ->
serialize(F, []).
serialize(#fcode{} = F, Options) ->
sanity_check(F),
serialize(F, serialize_functions(F), Options).
serialize(#fcode{} = F, Functions, Options) ->
SymbolTable = serialize_symbol_table(F),
Annotatations = serialize_annotations(F),
ByteCode = << (aeser_rlp:encode(Functions))/binary,
(aeser_rlp:encode(SymbolTable))/binary,
(aeser_rlp:encode(Annotatations))/binary
>>,
case proplists:lookup(pp_hex_string, Options) of
{pp_hex_string, true} ->
io:format("Code: ~s~n",[to_hexstring(Functions)]);
none ->
ok
end,
ByteCode.
to_hexstring(ByteList) ->
"0x" ++ lists:flatten(
[io_lib:format("~2.16.0b", [X])
|| X <- ByteList]).
serialize_functions(#fcode{ functions = Functions }) ->
%% Sort the functions on name to get a canonical serialisation.
iolist_to_binary(
lists:foldr(fun({Id, {Sig, C}}, Acc) ->
[[?FUNCTION, Id, serialize_signature(Sig), serialize_bbs(C)] | Acc]
end, [], lists:sort(maps:to_list(Functions)))).
serialize_signature({Args, RetType}) ->
[aeb_fate_encoding:serialize_type({tuple, Args}) |
aeb_fate_encoding:serialize_type(RetType)].
serialize_symbol_table(#fcode{ symbols = Symbols }) ->
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)).
serialize_annotations(#fcode{ annotations = Annotations}) ->
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)).
serialize_bbs(#{} = BBs) ->
serialize_bbs(BBs, 0, []).
serialize_bbs(BBs, N, Acc) ->
case maps:get(N, BBs, none) of
none -> lists:reverse(Acc);
BB -> serialize_bbs(BBs, N + 1, [serialize_bb(BB, [])|Acc])
end.
serialize_bb([Op], Acc) ->
lists:reverse([serialize_op(Op)|Acc]);
serialize_bb([Op|Rest], Acc) ->
serialize_bb(Rest, [serialize_op(Op)|Acc]).
serialize_op(Op) ->
[Mnemonic|Args] =
case is_tuple(Op) of
true -> tuple_to_list(Op);
false -> [Op]
end,
[aeb_fate_opcodes:m_to_op(Mnemonic) | serialize_code(Args)].
sanity_check(#fcode{ functions = Funs }) ->
_ = [ case Def of
{_, BBs} when byte_size(Id) == 4 -> sanity_check_bbs(BBs);
_ -> error({illegal_function_id, Id})
end || {Id, Def} <- maps:to_list(Funs) ],
ok.
sanity_check_bbs(#{} = BBs) ->
sanity_check_bbs(BBs, 0).
sanity_check_bbs(BBs, N) ->
case maps:get(N, BBs, none) of
none ->
%% Assert that the BBs were contiguous
case maps:size(BBs) =:= N of
true -> ok;
false -> error({not_contiguous_labels, lists:sort(maps:keys(BBs))})
end;
[] ->
error({empty_code_block, N});
BB ->
sanity_check_bb(BB),
sanity_check_bbs(BBs, N + 1)
end.
sanity_check_bb([Op]) ->
sanity_check_op(true, Op);
sanity_check_bb([Op|Rest]) ->
sanity_check_op(false, Op),
sanity_check_bb(Rest).
sanity_check_op(IsLast, Op) ->
[Mnemonic|Args] =
case is_tuple(Op) of
true -> tuple_to_list(Op);
false -> [Op]
end,
safe_sanity_check(IsLast, aeb_fate_opcodes:m_to_op(Mnemonic), Args).
safe_sanity_check(IsLast, Op, Args) ->
case length(Args) == aeb_fate_opcodes:args(Op) of
true ->
case IsLast == aeb_fate_opcodes:end_bb(Op) of
true -> ok;
false -> error({wrong_opcode_in_bb, Op})
end;
false -> error({wrong_nr_args_opcode, Op})
end.
%% Argument encoding
%% Argument Specification Byte
%% bitpos: 6 4 2 0
%% xx xx xx xx
%% Arg3 Arg2 Arg1 Arg0
%% For 5-8 args another Argument Spec Byte is used
%% bitpos: 6 4 2 0 | 6 4 2 0
%% xx xx xx xx | xx xx xx xx
%% Arg7 Arg6 Arg5 Arg4 | Arg3 Arg2 Arg1 Arg0
%% Bit pattern
%% 00 : stack/unused (depending on instruction)
%% 01 : argN
%% 10 : varN
%% 11 : immediate
serialize_code([{_,_}|_] = List ) ->
%% Take out the full argument list.
{Args, Rest} = lists:splitwith(fun({_, _}) -> true; (_) -> false end, List),
%% Create the appropriate number of modifier bytes.
Mods = << <<(modifier_bits(Type, X)):2>> || {Type, X} <- pad_args(lists:reverse(Args)) >>,
case Mods of
<<M1:8, M2:8>> ->
[M1, M2 | [serialize_data(Type, Arg) || {Type, Arg} <- Args, Type =/= stack]] ++
serialize_code(Rest);
<<M1:8>> ->
[M1 | [serialize_data(Type, Arg) || {Type, Arg} <- Args, Type =/= stack]] ++
serialize_code(Rest)
end;
serialize_code([Op|Rest]) ->
[Op|serialize_code(Rest)];
serialize_code([]) ->
[].
pad_args(List) ->
case length(List) of
0 -> List;
N when N =< 4 ->
lists:duplicate(4 - N, {stack, 0}) ++ List;
N when N =< 8 ->
lists:duplicate(8 - N, {stack, 0}) ++ List
end.
serialize_data(_, Data) ->
aeb_fate_encoding:serialize(Data).
%% 00 : stack/unused (depending on instruction)
%% 01 : argN
%% 10 : varN
%% 11 : immediate
modifier_bits(immediate, _) -> 2#11;
modifier_bits(var, _) -> 2#10;
modifier_bits(arg, _) -> 2#01;
modifier_bits(stack, 0) -> 2#00;
modifier_bits(Type, X) -> error({illegal_argument, Type, X}).
bits_to_modifier(2#11) -> immediate;
bits_to_modifier(2#10) -> var;
bits_to_modifier(2#01) -> arg;
bits_to_modifier(2#00) -> stack.
%%%===================================================================
%%% Deserialization
%%%===================================================================
deserialize(Bytes) ->
{ByteCode, Rest1} = aeser_rlp:decode_one(Bytes),
{SymbolTable, Rest2} = aeser_rlp:decode_one(Rest1),
{Annotations, <<>>} = aeser_rlp:decode_one(Rest2),
Env = #{ function => none
, bb => 0
, current_bb_code => []
, functions => #{}
, code => #{}
},
Fcode =
#fcode{ functions = deserialize_functions(ByteCode, Env)
, annotations = deserialize_annotations(Annotations)
, symbols = deserialize_symbols(SymbolTable)
},
sanity_check(Fcode),
Fcode.
deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
#{ function := none
, bb := 0
, current_bb_code := []
} = Env) ->
{Sig, Rest2} = deserialize_signature(Rest),
Env2 = Env#{function => {<<A,B,C,D>>, Sig}},
deserialize_functions(Rest2, Env2);
deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
#{ function := {F, Sig}
, bb := BB
, current_bb_code := Code
, code := Program
, functions := Funs} = Env) ->
{NewSig, Rest2} = deserialize_signature(Rest),
case Code of
[] ->
Env2 = Env#{ bb => 0
, current_bb_code => []
, function => {<<A,B,C,D>>, NewSig}
, code => #{}
, functions => Funs#{F => {Sig, Program}}},
deserialize_functions(Rest2, Env2);
_ ->
Env2 = Env#{ bb => 0
, current_bb_code => []
, function => {<<A,B,C,D>>, NewSig}
, code => #{}
, functions =>
Funs#{F => {Sig,
Program#{ BB => lists:reverse(Code)}}}},
deserialize_functions(Rest2, Env2)
end;
deserialize_functions(<<_Op:8, _Rest/binary>>,
#{ function := none }) ->
error({code_without_function});
deserialize_functions(<<Op:8, Rest/binary>>,
#{ bb := BB
, current_bb_code := Code
, code := Program} = Env) ->
{Rest2, OpCode} = deserialize_op(Op, Rest, Code),
case aeb_fate_opcodes:end_bb(Op) of
true ->
deserialize_functions(Rest2, Env#{ bb => BB+1
, current_bb_code => []
, code => Program#{BB =>
lists:reverse(OpCode)}});
false ->
deserialize_functions(Rest2, Env#{ current_bb_code => OpCode})
end;
deserialize_functions(<<>>, #{ function := none
, functions := Funs}) ->
Funs;
deserialize_functions(<<>>, #{ function := {F, Sig}
, bb := BB
, current_bb_code := Code
, code := Program
, functions := Funs}) ->
FunctionCode =
case Code of
[] -> Program;
_ -> Program#{ BB => lists:reverse(Code)}
end,
Funs#{F => {Sig, FunctionCode}}.
deserialize_op(Op, Rest, Code) ->
OpName = aeb_fate_opcodes:mnemonic(Op),
case aeb_fate_opcodes:args(Op) of
0 ->
{Rest, [OpName | Code]};
N ->
{Args, Rest1} = deserialize_n_args(N, Rest),
{Rest1, [list_to_tuple([OpName|Args])|Code]}
end.
deserialize_n_args(N, <<M3:2, M2:2, M1:2, M0:2, Rest/binary>>) when N =< 4 ->
{ArgMods, Zeros} = lists:split(N, [M0, M1, M2, M3]),
assert_zero(Zeros),
lists:mapfoldl(fun(M, Acc) ->
case bits_to_modifier(M) of
stack ->
{{stack, 0}, Acc};
Modifier ->
{Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc),
{{Modifier, Arg}, Acc2}
end
end, Rest, ArgMods);
deserialize_n_args(N, <<M7:2, M6:2, M5:2, M4:2, M3:2, M2:2, M1:2, M0:2,
Rest/binary>>) when N =< 8 ->
{ArgMods, Zeros} = lists:split(N, [M0, M1, M2, M3, M4, M5, M6, M7]),
assert_zero(Zeros),
lists:mapfoldl(fun(M, Acc) ->
case bits_to_modifier(M) of
stack ->
{{stack, 0}, Acc};
Modifier ->
{Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc),
{{Modifier, Arg}, Acc2}
end
end, Rest, ArgMods).
deserialize_signature(Binary) ->
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary),
{RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest),
{{Args, RetType}, Rest2}.
deserialize_symbols(Table) ->
?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table),
SymbolTable.
deserialize_annotations(AnnotationsBin) ->
?FATE_MAP_VALUE(Annotations) = aeb_fate_encoding:deserialize(AnnotationsBin),
Annotations.
assert_zero([]) ->
true;
assert_zero([0|Rest]) ->
assert_zero(Rest);
assert_zero([_|_]) ->
error(argument_defined_outside_range).
+182 -16
View File
@@ -12,12 +12,11 @@
-type fate_map() :: ?FATE_MAP_T. -type fate_map() :: ?FATE_MAP_T.
-type fate_string() :: ?FATE_STRING_T. -type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T. -type fate_address() :: ?FATE_ADDRESS_T.
-type fate_hash() :: ?FATE_HASH_T. -type fate_hash() :: ?FATE_BYTES_T(32).
-type fate_signature() :: ?FATE_BYTES_T(64).
-type fate_contract() :: ?FATE_CONTRACT_T. -type fate_contract() :: ?FATE_CONTRACT_T.
-type fate_oracle() :: ?FATE_ORACLE_T. -type fate_oracle() :: ?FATE_ORACLE_T.
-type fate_name() :: ?FATE_NAME_T.
-type fate_channel() :: ?FATE_CHANNEL_T. -type fate_channel() :: ?FATE_CHANNEL_T.
-type fate_signature() :: ?FATE_SIGNATURE_T.
-type fate_variant() :: ?FATE_VARIANT_T. -type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T. -type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_bits() :: ?FATE_BITS_T. -type fate_bits() :: ?FATE_BITS_T.
@@ -32,7 +31,6 @@
| signature | signature
| contract | contract
| oracle | oracle
| name
| channel | channel
| bits | bits
| string | string
@@ -52,7 +50,6 @@
| fate_signature() | fate_signature()
| fate_contract() | fate_contract()
| fate_oracle() | fate_oracle()
| fate_name()
| fate_channel() | fate_channel()
| fate_variant() | fate_variant()
| fate_map() | fate_map()
@@ -71,7 +68,6 @@
, fate_signature/0 , fate_signature/0
, fate_contract/0 , fate_contract/0
, fate_oracle/0 , fate_oracle/0
, fate_name/0
, fate_channel/0 , fate_channel/0
, fate_variant/0 , fate_variant/0
, fate_map/0 , fate_map/0
@@ -87,16 +83,22 @@
, make_string/1 , make_string/1
, make_map/1 , make_map/1
, make_address/1 , make_address/1
, make_bytes/1
, make_hash/1 , make_hash/1
, make_signature/1 , make_signature/1
, make_contract/1 , make_contract/1
, make_oracle/1 , make_oracle/1
, make_name/1 , make_oracle_query/1
, make_channel/1 , make_channel/1
, make_bits/1 , make_bits/1
, make_unit/0 , make_unit/0
, make_typerep/1
]). ]).
-export([format/1]). -export([
elt/2
, lt/2
, format/1
, ordinal/1]).
make_boolean(true) -> ?FATE_TRUE; make_boolean(true) -> ?FATE_TRUE;
@@ -107,17 +109,19 @@ make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T). make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M). make_map(M) -> ?MAKE_FATE_MAP(M).
make_address(X) -> ?FATE_ADDRESS(X). make_address(X) -> ?FATE_ADDRESS(X).
make_hash(X) -> ?FATE_HASH(X). make_bytes(X) -> ?FATE_BYTES(X).
make_signature(X) -> ?FATE_SIGNATURE(X). make_hash(X) -> make_bytes(X).
make_signature(X) -> make_bytes(X).
make_contract(X) -> ?FATE_CONTRACT(X). make_contract(X) -> ?FATE_CONTRACT(X).
make_oracle(X) -> ?FATE_ORACLE(X). make_oracle(X) -> ?FATE_ORACLE(X).
make_name(X) -> ?FATE_NAME(X). make_oracle_query(X) -> ?FATE_ORACLE_Q(X).
make_channel(X) -> ?FATE_CHANNEL(X). make_channel(X) -> ?FATE_CHANNEL(X).
make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I). make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I).
make_bits(I) when is_integer(I) -> ?FATE_BITS(I). make_bits(I) when is_integer(I) -> ?FATE_BITS(I).
make_string(S) when is_list(S) -> make_string(S) when is_list(S) ->
?FATE_STRING(iolist_to_binary(S)); ?FATE_STRING(iolist_to_binary(S));
make_string(S) when is_binary(S) -> ?FATE_STRING(S). make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_typerep(T) -> ?FATE_TYPEREP(T).
%% Tag points to the selected variant (zero based) %% Tag points to the selected variant (zero based)
%% The arity of this variant is read from the list of provided arities %% The arity of this variant is read from the list of provided arities
@@ -160,19 +164,19 @@ format(?FATE_VARIANT(Arities, Tag, T)) ->
" |)"]; " |)"];
format(M) when ?IS_FATE_MAP(M) -> format(M) when ?IS_FATE_MAP(M) ->
["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"]; ["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
format(?FATE_HASH(X)) -> ["#", base64:encode(X)]; format(?FATE_BYTES(X)) -> ["#", base64:encode(X)];
format(?FATE_ADDRESS(X)) -> format(?FATE_ADDRESS(X)) ->
["@", aeser_api_encoder:encode(account_pubkey, X)]; ["@", aeser_api_encoder:encode(account_pubkey, X)];
format(?FATE_SIGNATURE(X)) ->
["$", aeser_api_encoder:encode(signature, X)];
format(?FATE_CONTRACT(X)) -> format(?FATE_CONTRACT(X)) ->
["@", aeser_api_encoder:encode(contract_pubkey, X)]; ["@", aeser_api_encoder:encode(contract_pubkey, X)];
format(?FATE_ORACLE(X)) -> format(?FATE_ORACLE(X)) ->
["@", aeser_api_encoder:encode(oracle_pubkey, X)]; ["@", aeser_api_encoder:encode(oracle_pubkey, X)];
format(?FATE_NAME(X)) -> format(?FATE_ORACLE_Q(X)) ->
["@", aeser_api_encoder:encode(name, X)]; ["@", aeser_api_encoder:encode(oracle_query_id, X)];
format(?FATE_CHANNEL(X)) -> format(?FATE_CHANNEL(X)) ->
["@", aeser_api_encoder:encode(channel, X)]; ["@", aeser_api_encoder:encode(channel, X)];
format(?FATE_TYPEREP(X)) ->
["'", io_lib:format("~p", [X])];
format(V) -> exit({not_a_fate_type, V}). format(V) -> exit({not_a_fate_type, V}).
format_bits(0, Acc) -> Acc; format_bits(0, Acc) -> Acc;
@@ -193,3 +197,165 @@ format_list(List) ->
format_kvs(List) -> format_kvs(List) ->
lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]). lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]).
%% Total order of FATE terms.
%% Integers < Booleans < Address < Channel < Contract < Oracle
%% < Hash < Signature < Bits < String < Tuple < Map < List < Variant
-define(ORD_INTEGER , 0).
-define(ORD_BOOLEAN , 1).
-define(ORD_ADDRESS , 2).
-define(ORD_CHANNEL , 3).
-define(ORD_CONTRACT , 4).
-define(ORD_ORACLE , 5).
-define(ORD_BYTES , 6).
-define(ORD_BITS , 7).
-define(ORD_STRING , 8).
-define(ORD_TUPLE , 9).
-define(ORD_MAP , 10).
-define(ORD_LIST , 11).
-define(ORD_VARIANT , 12).
-define(ORD_ORACLE_Q , 13).
-spec ordinal(fate_type()) -> integer().
ordinal(T) when ?IS_FATE_INTEGER(T) -> ?ORD_INTEGER;
ordinal(T) when ?IS_FATE_BOOLEAN(T) -> ?ORD_BOOLEAN;
ordinal(T) when ?IS_FATE_ADDRESS(T) -> ?ORD_ADDRESS;
ordinal(T) when ?IS_FATE_CHANNEL(T) -> ?ORD_CHANNEL;
ordinal(T) when ?IS_FATE_CONTRACT(T) -> ?ORD_CONTRACT;
ordinal(T) when ?IS_FATE_ORACLE(T) -> ?ORD_ORACLE;
ordinal(T) when ?IS_FATE_BYTES(T) -> ?ORD_BYTES;
ordinal(T) when ?IS_FATE_BITS(T) -> ?ORD_BITS;
ordinal(T) when ?IS_FATE_STRING(T) -> ?ORD_STRING;
ordinal(T) when ?IS_FATE_TUPLE(T) -> ?ORD_TUPLE;
ordinal(T) when ?IS_FATE_MAP(T) -> ?ORD_MAP;
ordinal(T) when ?IS_FATE_LIST(T) -> ?ORD_LIST;
ordinal(T) when ?IS_FATE_VARIANT(T) -> ?ORD_VARIANT;
ordinal(T) when ?IS_FATE_ORACLE_Q(T) -> ?ORD_ORACLE_Q.
-spec lt(fate_type(), fate_type()) -> boolean().
lt(A, B) ->
O1 = ordinal(A),
O2 = ordinal(B),
if O1 == O2 -> lt(O1, A, B);
true -> O1 < O2
end.
%% Integers are ordered as usual.
lt(?ORD_INTEGER, A, B) when ?IS_FATE_INTEGER(A), ?IS_FATE_INTEGER(B) ->
?FATE_INTEGER_VALUE(A) < ?FATE_INTEGER_VALUE(B);
%% false is smaller than true (true also for erlang booleans).
lt(?ORD_BOOLEAN, A, B) when ?IS_FATE_BOOLEAN(A), ?IS_FATE_BOOLEAN(B) ->
?FATE_BOOLEAN_VALUE(A) < ?FATE_BOOLEAN_VALUE(B);
lt(?ORD_BITS, A, B) when ?IS_FATE_BITS(A), ?IS_FATE_BITS(B) ->
BitsA = ?FATE_BITS_VALUE(A),
BitsB = ?FATE_BITS_VALUE(B),
if BitsA < 0 ->
if BitsB < 0 -> BitsA < BitsB;
true -> false
end;
BitsB < 0 ->
true;
true -> BitsA < BitsB
end;
lt(?ORD_STRING,?FATE_STRING(A), ?FATE_STRING(B)) ->
SizeA = size(A),
SizeB = size(B),
case SizeA - SizeB of
0 -> A < B;
N -> N < 0
end;
lt(?ORD_TUPLE,?FATE_TUPLE(A), ?FATE_TUPLE(B)) ->
SizeA = size(A),
SizeB = size(B),
case SizeA - SizeB of
0 -> tuple_elements_lt(0, A, B, SizeA);
N -> N < 0
end;
lt(?ORD_MAP, ?FATE_MAP_VALUE(A), ?FATE_MAP_VALUE(B)) ->
SizeA = maps:size(A),
SizeB = maps:size(B),
case SizeA - SizeB of
0 -> maps_lt(A, B);
N -> N < 0
end;
lt(?ORD_LIST, ?FATE_LIST_VALUE(_), ?FATE_LIST_VALUE([])) -> false;
lt(?ORD_LIST, ?FATE_LIST_VALUE([]), ?FATE_LIST_VALUE(_)) -> true;
lt(?ORD_LIST, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) ->
O1 = ordinal(A),
O2 = ordinal(B),
if O1 == O2 ->
if A == B -> lt(RA, RB);
true -> A < B
end;
true -> O1 < O2
end;
lt(?ORD_VARIANT, ?FATE_VARIANT(AritiesA, TagA, TA),
?FATE_VARIANT(AritiesB, TagB, TB)) ->
if length(AritiesA) < length(AritiesB) -> true;
length(AritiesA) > length(AritiesB) -> false;
true ->
if AritiesA < AritiesB -> true;
AritiesA > AritiesB -> false;
true ->
if TagA < TagB -> true;
TagA > TagB -> false;
true -> lt(make_tuple(TA), make_tuple(TB))
end
end
end;
lt(_, A, B) -> A < B.
tuple_elements_lt(N,_A,_B, N) ->
false;
tuple_elements_lt(N, A, B, Size) ->
E = N + 1,
EA = element(E, A),
EB = element(E, B),
if EA =:= EB -> tuple_elements_lt(E, A, B, Size);
true -> lt(EA, EB)
end.
maps_lt(A, B) ->
IA = maps_iterator(A),
IB = maps_iterator(B),
maps_i_lt(IA, IB).
maps_i_lt(IA, IB) ->
case {maps_next(IA), maps_next(IB)} of
{none, none} -> false;
{_, none} -> false;
{none, _} -> true;
{{KA1, VA1, IA2}, {KB1, VB1, IB2}} ->
case lt(KA1, KB1) of
true -> true;
false ->
case lt(KB1, KA1) of
true -> false;
false ->
case lt(VA1, VB1) of
true -> true;
false ->
case lt(VB1, VA1) of
true -> false;
false ->
maps_i_lt(IA2, IB2)
end
end
end
end
end.
maps_iterator(M) -> lists:sort(fun ({K1,_}, {K2,_}) -> lt(K1, K2) end, maps:to_list(M)).
maps_next([]) -> none;
maps_next([{K,V}|Rest]) -> {K, V, Rest}.
-spec elt(fate_type(), fate_type()) -> boolean().
elt(A, A) -> true;
elt(A, B) ->
R = lt(A, B),
R.
+167 -84
View File
@@ -1,21 +1,29 @@
%% Fate data (and instruction) serialization. %% Fate data (and instruction) serialization.
%% %%
%% The FATE serialization has to fullfill the following properties: %% Assuming
%% * There has to be 1 and only 1 byte sequence %% S is seralize/1 (fate_type() -> binary())
%% representing each unique value in FATE. %% D is deserialize/1 (binary) -> fate_type())
%% * A valid byte sequence has to be deserializable to a FATE value. %% V, V1, V2 are of the type fate_type()
%% * A valid byte sequence must not contain any trailing bytes. %% B is of the type binary()
%% * A serialization is a sequence of 8-bit bytes. %% Then
%% %% The FATE serialization has to fullfill the following properties:
%% The serialization function should fullfill the following: %% * For each value (V) in FATE there has to be a bytecode sequence (B)
%% * A valid FATE value should be serialized to a byte sequence. %% representing that value.
%% * Any other argument, not representing a valid FATE value should %% * A valid byte sequence has to be deserializable to a FATE value.
%% * A valid byte sequence must not contain any trailing bytes.
%% * A serialization is a sequence of 8-bit bytes.
%% The serialization function (S) should fullfill the following:
%% * A valid FATE value should be serialized to a byte sequence.
%% * Any other argument, not representing a valid FATE value should
%% throw an exception %% throw an exception
%% %% The deserialization function (D) should fullfill the following:
%% The deserialization function should fullfill the following: %% * A valid byte sequence should be deserialized to a valid FATE value.
%% * A valid byte sequence should be deserialized to a valid FATE value. %% * Any other argument, not representing a valid byte sequence should
%% * Any other argument, not representing a valid byte sequence should
%% throw an exception %% throw an exception
%% The following equalities should hold:
%% * D(S(V)) == V
%% * if V1 == V2 then S(V1) == S(V2)
%%
%% %%
%% History %% History
%% * First draft of FATE serialization encoding/decoding. %% * First draft of FATE serialization encoding/decoding.
@@ -40,6 +48,10 @@
, serialize_type/1 , serialize_type/1
]). ]).
-ifdef(EQC).
-export([sort/1]).
-endif.
-include("aeb_fate_data.hrl"). -include("aeb_fate_data.hrl").
%% Definition of tag scheme. %% Definition of tag scheme.
@@ -61,13 +73,13 @@
-define(TYPE_MAP , 2#01100111). %% 0110 0111 | Type | Type -define(TYPE_MAP , 2#01100111). %% 0110 0111 | Type | Type
-define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef -define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef
-define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type] -define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type]
%% 1001 0111 -define(TYPE_BYTES , 2#10010111). %% 1001 0111 - Bytes typedef
%% 1010 0111 %% 1010 0111
%% 1011 0111 %% 1011 0111
%% 1100 0111 %% 1100 0111
%% 1101 0111 %% 1101 0111
%% 1110 0111 -define(TYPE_VAR , 2#11100111). %% 1110 0111 | Id when 0 =< Id < 256 (type variable)
%% 1111 0111 -define(TYPE_ANY , 2#11110111). %% 1111 0111 - Any typedef
-define(LONG_TUPLE , 2#00001011). %% 0000 1011 | RLP encoded (size - 16) | [encoded elements], -define(LONG_TUPLE , 2#00001011). %% 0000 1011 | RLP encoded (size - 16) | [encoded elements],
-define(SHORT_TUPLE , 2#1011). %% xxxx 1011 | [encoded elements] when 0 < size < 16 -define(SHORT_TUPLE , 2#1011). %% xxxx 1011 | [encoded elements] when 0 < size < 16
%% 1111 Set below %% 1111 Set below
@@ -81,7 +93,6 @@
%% %% 1000 1111 - FREE (Possibly for bytecode in the future.) %% %% 1000 1111 - FREE (Possibly for bytecode in the future.)
-define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array -define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array
-define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values] -define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values]
-define(NIL , 2#10111111). %% 1011 1111 - Empty list
-define(NEG_BITS , 2#11001111). %% 1100 1111 | RLP encoded integer (infinite 1:s bitfield) -define(NEG_BITS , 2#11001111). %% 1100 1111 | RLP encoded integer (infinite 1:s bitfield)
-define(EMPTY_MAP , 2#11011111). %% 1101 1111 -define(EMPTY_MAP , 2#11011111). %% 1101 1111
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 | RLP encoded (integer - 64) -define(NEG_BIG_INT , 2#11101111). %% 1110 1111 | RLP encoded (integer - 64)
@@ -97,12 +108,23 @@
%% Object types %% Object types
-define(OTYPE_ADDRESS, 0). -define(OTYPE_ADDRESS, 0).
-define(OTYPE_HASH, 1). -define(OTYPE_BYTES, 1).
-define(OTYPE_SIGNATURE, 2). -define(OTYPE_CONTRACT, 2).
-define(OTYPE_CONTRACT, 3). -define(OTYPE_ORACLE, 3).
-define(OTYPE_ORACLE, 4). -define(OTYPE_ORACLE_Q, 4).
-define(OTYPE_NAME, 5). -define(OTYPE_CHANNEL, 5).
-define(OTYPE_CHANNEL, 6).
-define(IS_TYPE_TAG(X), (X =:= ?TYPE_INTEGER orelse
X =:= ?TYPE_BOOLEAN orelse
X =:= ?TYPE_ANY orelse
X =:= ?TYPE_VAR orelse
X =:= ?TYPE_LIST orelse
X =:= ?TYPE_TUPLE orelse
X =:= ?TYPE_OBJECT orelse
X =:= ?TYPE_BITS orelse
X =:= ?TYPE_MAP orelse
X =:= ?TYPE_STRING orelse
X =:= ?TYPE_VARIANT)).
%% -------------------------------------------------- %% --------------------------------------------------
%% Serialize %% Serialize
@@ -112,9 +134,7 @@
-spec serialize(aeb_fate_data:fate_type()) -> binary(). -spec serialize(aeb_fate_data:fate_type()) -> binary().
serialize(?FATE_TRUE) -> <<?TRUE>>; serialize(?FATE_TRUE) -> <<?TRUE>>;
serialize(?FATE_FALSE) -> <<?FALSE>>; serialize(?FATE_FALSE) -> <<?FALSE>>;
serialize(?FATE_NIL) -> <<?NIL>>; %% ! Untyped
serialize(?FATE_UNIT) -> <<?EMPTY_TUPLE>>; %% ! Untyped serialize(?FATE_UNIT) -> <<?EMPTY_TUPLE>>; %% ! Untyped
serialize(M) when ?IS_FATE_MAP(M), ?FATE_MAP_SIZE(M) =:= 0 -> <<?EMPTY_MAP>>; %% ! Untyped
serialize(?FATE_EMPTY_STRING) -> <<?EMPTY_STRING>>; serialize(?FATE_EMPTY_STRING) -> <<?EMPTY_STRING>>;
serialize(I) when ?IS_FATE_INTEGER(I) -> serialize_integer(I); serialize(I) when ?IS_FATE_INTEGER(I) -> serialize_integer(I);
serialize(?FATE_BITS(Bits)) when is_integer(Bits) -> serialize_bits(Bits); serialize(?FATE_BITS(Bits)) when is_integer(Bits) -> serialize_bits(Bits);
@@ -128,19 +148,19 @@ serialize(String) when ?IS_FATE_STRING(String),
?FATE_STRING_SIZE(String) > 0, ?FATE_STRING_SIZE(String) > 0,
?FATE_STRING_SIZE(String) >= ?SHORT_STRING_SIZE -> ?FATE_STRING_SIZE(String) >= ?SHORT_STRING_SIZE ->
Bytes = ?FATE_STRING_VALUE(String), Bytes = ?FATE_STRING_VALUE(String),
<<?LONG_STRING, (aeser_rlp:encode(Bytes))/binary>>; <<?LONG_STRING,
(serialize_integer(?FATE_STRING_SIZE(String) - ?SHORT_STRING_SIZE))/binary
, Bytes/binary>>;
serialize(?FATE_BYTES(Bytes)) when is_binary(Bytes) ->
<<?OBJECT, ?OTYPE_BYTES, (serialize(?FATE_STRING(Bytes)))/binary>>;
serialize(?FATE_ADDRESS(Address)) when is_binary(Address) -> serialize(?FATE_ADDRESS(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ADDRESS, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_ADDRESS, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_HASH(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_HASH, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_SIGNATURE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_SIGNATURE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CONTRACT(Address)) when is_binary(Address) -> serialize(?FATE_CONTRACT(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CONTRACT, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_CONTRACT, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE(Address)) when is_binary(Address) -> serialize(?FATE_ORACLE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_ORACLE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_NAME(Address)) when is_binary(Address) -> serialize(?FATE_ORACLE_Q(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_NAME, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_ORACLE_Q, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CHANNEL(Address)) when is_binary(Address) -> serialize(?FATE_CHANNEL(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CHANNEL, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_CHANNEL, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_TUPLE(T)) when size(T) > 0 -> serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
@@ -150,27 +170,28 @@ serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
if S < ?SHORT_TUPLE_SIZE -> if S < ?SHORT_TUPLE_SIZE ->
<<S:4, ?SHORT_TUPLE:4, Rest/binary>>; <<S:4, ?SHORT_TUPLE:4, Rest/binary>>;
true -> true ->
Size = rlp_integer(S - ?SHORT_TUPLE_SIZE), Size = rlp_encode_int(S - ?SHORT_TUPLE_SIZE),
<<?LONG_TUPLE:8, Size/binary, Rest/binary>> <<?LONG_TUPLE:8, Size/binary, Rest/binary>>
end; end;
serialize(L) when ?IS_FATE_LIST(L) -> serialize(L) when ?IS_FATE_LIST(L) ->
[_E|_] = List = ?FATE_LIST_VALUE(L), List = ?FATE_LIST_VALUE(L),
S = length(List), S = length(List),
Rest = << <<(serialize(El))/binary>> || El <- List >>, Rest = << <<(serialize(El))/binary>> || El <- List >>,
if S < ?SHORT_LIST_SIZE -> if S < ?SHORT_LIST_SIZE ->
<<S:4, ?SHORT_LIST:4, Rest/binary>>; <<S:4, ?SHORT_LIST:4, Rest/binary>>;
true -> true ->
Val = rlp_integer(S - ?SHORT_LIST_SIZE), Val = rlp_encode_int(S - ?SHORT_LIST_SIZE),
<<?LONG_LIST, Val/binary, Rest/binary>> <<?LONG_LIST, Val/binary, Rest/binary>>
end; end;
serialize(Map) when ?IS_FATE_MAP(Map) -> serialize(Map) when ?IS_FATE_MAP(Map) ->
L = [{_K,_V}|_] = lists:sort(maps:to_list(?FATE_MAP_VALUE(Map))), L = maps:to_list(?FATE_MAP_VALUE(Map)),
Size = length(L), Size = length(L),
%% TODO: check all K same type, and all V same type %% TODO: check all K same type, and all V same type
%% check K =/= map %% check K =/= map
Elements = << <<(serialize(K1))/binary, (serialize(V1))/binary>> || {K1,V1} <- L >>, Elements =
list_to_binary([ <<(serialize(K))/binary, (serialize(V))/binary>> || {K, V} <- sort_and_check(L) ]),
<<?MAP, <<?MAP,
(rlp_integer(Size))/binary, (rlp_encode_int(Size))/binary,
(Elements)/binary>>; (Elements)/binary>>;
serialize(?FATE_VARIANT(Arities, Tag, Values)) -> serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
Arities = [A || A <- Arities, is_integer(A), A < 256], Arities = [A || A <- Arities, is_integer(A), A < 256],
@@ -188,7 +209,9 @@ serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
(serialize(?FATE_TUPLE(Values)))/binary (serialize(?FATE_TUPLE(Values)))/binary
>> >>
end end
end. end;
serialize(?FATE_TYPEREP(T)) ->
iolist_to_binary(serialize_type(T)).
%% ----------------------------------------------------- %% -----------------------------------------------------
@@ -196,18 +219,20 @@ serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()]. -spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()].
serialize_type(integer) -> [?TYPE_INTEGER]; serialize_type(integer) -> [?TYPE_INTEGER];
serialize_type(boolean) -> [?TYPE_BOOLEAN]; serialize_type(boolean) -> [?TYPE_BOOLEAN];
serialize_type(any) -> [?TYPE_ANY];
serialize_type({tvar, N}) when 0 =< N, N =< 255 -> [?TYPE_VAR, N];
serialize_type({list, T}) -> [?TYPE_LIST | serialize_type(T)]; serialize_type({list, T}) -> [?TYPE_LIST | serialize_type(T)];
serialize_type({tuple, Ts}) -> serialize_type({tuple, Ts}) ->
case length(Ts) of case length(Ts) of
N when N =< 255 -> N when N =< 255 ->
[?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]] [?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]]
end; end;
serialize_type({bytes, N}) when 0 =< N ->
[?TYPE_BYTES | binary_to_list(serialize_integer(N))];
serialize_type(address) -> [?TYPE_OBJECT, ?OTYPE_ADDRESS]; serialize_type(address) -> [?TYPE_OBJECT, ?OTYPE_ADDRESS];
serialize_type(hash) -> [?TYPE_OBJECT, ?OTYPE_HASH];
serialize_type(signature) -> [?TYPE_OBJECT, ?OTYPE_SIGNATURE];
serialize_type(contract) -> [?TYPE_OBJECT, ?OTYPE_CONTRACT]; serialize_type(contract) -> [?TYPE_OBJECT, ?OTYPE_CONTRACT];
serialize_type(oracle) -> [?TYPE_OBJECT, ?OTYPE_ORACLE]; serialize_type(oracle) -> [?TYPE_OBJECT, ?OTYPE_ORACLE];
serialize_type(name) -> [?TYPE_OBJECT, ?OTYPE_NAME]; serialize_type(oracle_query)-> [?TYPE_OBJECT, ?OTYPE_ORACLE_Q];
serialize_type(channel) -> [?TYPE_OBJECT, ?OTYPE_CHANNEL]; serialize_type(channel) -> [?TYPE_OBJECT, ?OTYPE_CHANNEL];
serialize_type(bits) -> [?TYPE_BITS]; serialize_type(bits) -> [?TYPE_BITS];
serialize_type({map, K, V}) -> [?TYPE_MAP serialize_type({map, K, V}) -> [?TYPE_MAP
@@ -223,20 +248,24 @@ serialize_type({variant, ListOfVariants}) ->
-spec deserialize_type(binary()) -> {aeb_fate_data:fate_type_type(), binary()}. -spec deserialize_type(binary()) -> {aeb_fate_data:fate_type_type(), binary()}.
deserialize_type(<<?TYPE_INTEGER, Rest/binary>>) -> {integer, Rest}; deserialize_type(<<?TYPE_INTEGER, Rest/binary>>) -> {integer, Rest};
deserialize_type(<<?TYPE_BOOLEAN, Rest/binary>>) -> {boolean, Rest}; deserialize_type(<<?TYPE_BOOLEAN, Rest/binary>>) -> {boolean, Rest};
deserialize_type(<<?TYPE_ANY, Rest/binary>>) -> {any, Rest};
deserialize_type(<<?TYPE_VAR, Id, Rest/binary>>) -> {{tvar, Id}, Rest};
deserialize_type(<<?TYPE_LIST, Rest/binary>>) -> deserialize_type(<<?TYPE_LIST, Rest/binary>>) ->
{T, Rest2} = deserialize_type(Rest), {T, Rest2} = deserialize_type(Rest),
{{list, T}, Rest2}; {{list, T}, Rest2};
deserialize_type(<<?TYPE_TUPLE, N, Rest/binary>>) -> deserialize_type(<<?TYPE_TUPLE, N, Rest/binary>>) ->
{Ts, Rest2} = deserialize_types(N, Rest, []), {Ts, Rest2} = deserialize_types(N, Rest, []),
{{tuple, Ts}, Rest2}; {{tuple, Ts}, Rest2};
deserialize_type(<<?TYPE_BYTES, Rest/binary>>) ->
{N, Rest2} = deserialize_one(Rest),
true = is_integer(N) andalso N >= 0,
{{bytes, N}, Rest2};
deserialize_type(<<?TYPE_OBJECT, ObjectType, Rest/binary>>) -> deserialize_type(<<?TYPE_OBJECT, ObjectType, Rest/binary>>) ->
case ObjectType of case ObjectType of
?OTYPE_ADDRESS -> {address, Rest}; ?OTYPE_ADDRESS -> {address, Rest};
?OTYPE_HASH -> {hash, Rest};
?OTYPE_SIGNATURE -> {signature, Rest};
?OTYPE_CONTRACT -> {contract, Rest}; ?OTYPE_CONTRACT -> {contract, Rest};
?OTYPE_ORACLE -> {oracle, Rest}; ?OTYPE_ORACLE -> {oracle, Rest};
?OTYPE_NAME -> {name, Rest}; ?OTYPE_ORACLE_Q -> {oracle_query, Rest};
?OTYPE_CHANNEL -> {channel, Rest} ?OTYPE_CHANNEL -> {channel, Rest}
end; end;
deserialize_type(<<?TYPE_BITS, Rest/binary>>) -> {bits, Rest}; deserialize_type(<<?TYPE_BITS, Rest/binary>>) -> {bits, Rest};
@@ -267,9 +296,23 @@ deserialize_types(N, Binary, Acc) ->
%% ----------------------------------------------------- %% -----------------------------------------------------
rlp_integer(S) when S >= 0 -> rlp_encode_int(S) when S >= 0 ->
aeser_rlp:encode(binary:encode_unsigned(S)). aeser_rlp:encode(binary:encode_unsigned(S)).
%% first byte of the binary gives the number of bytes we need <<129>> is 1, <<130>> = 2,
%% so <<129, 0>> is <<0>> and <<130, 0, 0>> is <<0, 0>>
rlp_decode_int(Binary) ->
{Bin1, Rest} = aeser_rlp:decode_one(Binary),
Int = binary:decode_unsigned(Bin1),
ReEncode = rlp_encode_int(Int),
case <<ReEncode/binary, Rest/binary>> == Binary of
true ->
{Int, Rest};
false ->
error({none_unique_encoding, Bin1, ReEncode})
end.
serialize_integer(I) when ?IS_FATE_INTEGER(I) -> serialize_integer(I) when ?IS_FATE_INTEGER(I) ->
V = ?FATE_INTEGER_VALUE(I), V = ?FATE_INTEGER_VALUE(I),
Abs = abs(V), Abs = abs(V),
@@ -279,20 +322,16 @@ serialize_integer(I) when ?IS_FATE_INTEGER(I) ->
end, end,
if Abs < ?SMALL_INT_SIZE -> <<Sign:1, Abs:6, ?SMALL_INT:1>>; if Abs < ?SMALL_INT_SIZE -> <<Sign:1, Abs:6, ?SMALL_INT:1>>;
Sign =:= ?NEG_SIGN -> <<?NEG_BIG_INT, Sign =:= ?NEG_SIGN -> <<?NEG_BIG_INT,
(rlp_integer(Abs - ?SMALL_INT_SIZE))/binary>>; (rlp_encode_int(Abs - ?SMALL_INT_SIZE))/binary>>;
Sign =:= ?POS_SIGN -> <<?POS_BIG_INT, Sign =:= ?POS_SIGN -> <<?POS_BIG_INT,
(rlp_integer(Abs - ?SMALL_INT_SIZE))/binary>> (rlp_encode_int(Abs - ?SMALL_INT_SIZE))/binary>>
end. end.
serialize_bits(B) when is_integer(B) -> serialize_bits(B) when is_integer(B) ->
Abs = abs(B), Abs = abs(B),
Sign = case B < 0 of
true -> ?NEG_SIGN;
false -> ?POS_SIGN
end,
if if
Sign =:= ?NEG_SIGN -> <<?NEG_BITS, (rlp_integer(Abs))/binary>>; B < 0 -> <<?NEG_BITS, (rlp_encode_int(Abs))/binary>>;
Sign =:= ?POS_SIGN -> <<?POS_BITS, (rlp_integer(Abs))/binary>> B >= 0 -> <<?POS_BITS, (rlp_encode_int(Abs))/binary>>
end. end.
-spec deserialize(binary()) -> aeb_fate_data:fate_type(). -spec deserialize(binary()) -> aeb_fate_data:fate_type().
@@ -305,38 +344,50 @@ deserialize_one(B) -> deserialize2(B).
deserialize2(<<?POS_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) -> deserialize2(<<?POS_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) ->
{?MAKE_FATE_INTEGER(I), Rest}; {?MAKE_FATE_INTEGER(I), Rest};
deserialize2(<<?NEG_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) -> deserialize2(<<?NEG_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) ->
{?MAKE_FATE_INTEGER(-I), Rest}; if I =/= 0 -> {?MAKE_FATE_INTEGER(-I), Rest};
I == 0 -> error({illegal_sign, I})
end;
deserialize2(<<?NEG_BIG_INT, Rest/binary>>) -> deserialize2(<<?NEG_BIG_INT, Rest/binary>>) ->
{Bint, Rest2} = aeser_rlp:decode_one(Rest), {Bint, Rest2} = rlp_decode_int(Rest),
{?MAKE_FATE_INTEGER(-binary:decode_unsigned(Bint) - ?SMALL_INT_SIZE), {?MAKE_FATE_INTEGER(-Bint - ?SMALL_INT_SIZE),
Rest2}; Rest2};
deserialize2(<<?POS_BIG_INT, Rest/binary>>) -> deserialize2(<<?POS_BIG_INT, Rest/binary>>) ->
{Bint, Rest2} = aeser_rlp:decode_one(Rest), {Bint, Rest2} = rlp_decode_int(Rest),
{?MAKE_FATE_INTEGER(binary:decode_unsigned(Bint) + ?SMALL_INT_SIZE), {?MAKE_FATE_INTEGER(Bint + ?SMALL_INT_SIZE),
Rest2}; Rest2};
deserialize2(<<?NEG_BITS, Rest/binary>>) -> deserialize2(<<?NEG_BITS, Rest/binary>>) ->
{Bint, Rest2} = aeser_rlp:decode_one(Rest), case rlp_decode_int(Rest) of
{?FATE_BITS(-binary:decode_unsigned(Bint)), Rest2}; {Pos, Rest2} when Pos > 0 ->
{?FATE_BITS(-Pos), Rest2};
{N, _} ->
error({illegal_parameter, neg_bits, N})
end;
deserialize2(<<?POS_BITS, Rest/binary>>) -> deserialize2(<<?POS_BITS, Rest/binary>>) ->
{Bint, Rest2} = aeser_rlp:decode_one(Rest), {Bint, Rest2} = rlp_decode_int(Rest),
{?FATE_BITS(binary:decode_unsigned(Bint)), Rest2}; {?FATE_BITS(Bint), Rest2};
deserialize2(<<?LONG_STRING, Rest/binary>>) -> deserialize2(<<?LONG_STRING, Rest/binary>>) ->
{String, Rest2} = aeser_rlp:decode_one(Rest), {S, Rest2} = deserialize_one(Rest),
{?MAKE_FATE_STRING(String), Rest2}; true = is_integer(S) andalso S >= 0,
Size = S + ?SHORT_STRING_SIZE,
String = binary:part(Rest2, 0, Size),
Rest3 = binary:part(Rest2, byte_size(Rest2), - (byte_size(Rest2) - Size)),
{?MAKE_FATE_STRING(String), Rest3};
deserialize2(<<S:6, ?SHORT_STRING:2, Rest/binary>>) -> deserialize2(<<S:6, ?SHORT_STRING:2, Rest/binary>>) ->
String = binary:part(Rest, 0, S), String = binary:part(Rest, 0, S),
Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)), Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)),
{?MAKE_FATE_STRING(String), Rest2}; {?MAKE_FATE_STRING(String), Rest2};
deserialize2(<<?OBJECT, ?OTYPE_BYTES, Rest/binary>>) ->
{String, Rest2} = deserialize_one(Rest),
true = ?IS_FATE_STRING(String),
{?FATE_BYTES(?FATE_STRING_VALUE(String)), Rest2};
deserialize2(<<?OBJECT, ObjectType, Rest/binary>>) -> deserialize2(<<?OBJECT, ObjectType, Rest/binary>>) ->
{A, Rest2} = aeser_rlp:decode_one(Rest), {A, Rest2} = aeser_rlp:decode_one(Rest),
Val = Val =
case ObjectType of case ObjectType of
?OTYPE_ADDRESS -> ?FATE_ADDRESS(A); ?OTYPE_ADDRESS -> ?FATE_ADDRESS(A);
?OTYPE_HASH -> ?FATE_HASH(A);
?OTYPE_SIGNATURE -> ?FATE_SIGNATURE(A);
?OTYPE_CONTRACT -> ?FATE_CONTRACT(A); ?OTYPE_CONTRACT -> ?FATE_CONTRACT(A);
?OTYPE_ORACLE -> ?FATE_ORACLE(A); ?OTYPE_ORACLE -> ?FATE_ORACLE(A);
?OTYPE_NAME -> ?FATE_NAME(A); ?OTYPE_ORACLE_Q -> ?FATE_ORACLE_Q(A);
?OTYPE_CHANNEL -> ?FATE_CHANNEL(A) ?OTYPE_CHANNEL -> ?FATE_CHANNEL(A)
end, end,
{Val, Rest2}; {Val, Rest2};
@@ -344,36 +395,37 @@ deserialize2(<<?TRUE, Rest/binary>>) ->
{?FATE_TRUE, Rest}; {?FATE_TRUE, Rest};
deserialize2(<<?FALSE, Rest/binary>>) -> deserialize2(<<?FALSE, Rest/binary>>) ->
{?FATE_FALSE, Rest}; {?FATE_FALSE, Rest};
deserialize2(<<?NIL, Rest/binary>>) ->
{?FATE_NIL, Rest};
deserialize2(<<?EMPTY_TUPLE, Rest/binary>>) -> deserialize2(<<?EMPTY_TUPLE, Rest/binary>>) ->
{?FATE_UNIT, Rest}; {?FATE_UNIT, Rest};
deserialize2(<<?EMPTY_MAP, Rest/binary>>) ->
{?MAKE_FATE_MAP(#{}), Rest};
deserialize2(<<?EMPTY_STRING, Rest/binary>>) -> deserialize2(<<?EMPTY_STRING, Rest/binary>>) ->
{?FATE_EMPTY_STRING, Rest}; {?FATE_EMPTY_STRING, Rest};
deserialize2(<<?LONG_TUPLE, Rest/binary>>) -> deserialize2(<<?LONG_TUPLE, Rest/binary>>) ->
{BSize, Rest1} = aeser_rlp:decode_one(Rest), {Size, Rest1} = rlp_decode_int(Rest),
N = binary:decode_unsigned(BSize) + ?SHORT_TUPLE_SIZE, N = Size + ?SHORT_TUPLE_SIZE,
{List, Rest2} = deserialize_elements(N, Rest1), {List, Rest2} = deserialize_elements(N, Rest1),
{?FATE_TUPLE(list_to_tuple(List)), Rest2}; {?FATE_TUPLE(list_to_tuple(List)), Rest2};
deserialize2(<<S:4, ?SHORT_TUPLE:4, Rest/binary>>) -> deserialize2(<<S:4, ?SHORT_TUPLE:4, Rest/binary>>) ->
{List, Rest1} = deserialize_elements(S, Rest), {List, Rest1} = deserialize_elements(S, Rest),
{?FATE_TUPLE(list_to_tuple(List)), Rest1}; {?FATE_TUPLE(list_to_tuple(List)), Rest1};
deserialize2(<<?LONG_LIST, Rest/binary>>) -> deserialize2(<<?LONG_LIST, Rest/binary>>) ->
{BLength, Rest1} = aeser_rlp:decode_one(Rest), {Size, Rest1} = rlp_decode_int(Rest),
Length = binary:decode_unsigned(BLength) + ?SHORT_LIST_SIZE, Length = Size + ?SHORT_LIST_SIZE,
{List, Rest2} = deserialize_elements(Length, Rest1), {List, Rest2} = deserialize_elements(Length, Rest1),
{?MAKE_FATE_LIST(List), Rest2}; {?MAKE_FATE_LIST(List), Rest2};
deserialize2(<<S:4, ?SHORT_LIST:4, Rest/binary>>) -> deserialize2(<<S:4, ?SHORT_LIST:4, Rest/binary>>) ->
{List, Rest1} = deserialize_elements(S, Rest), {List, Rest1} = deserialize_elements(S, Rest),
{?MAKE_FATE_LIST(List), Rest1}; {?MAKE_FATE_LIST(List), Rest1};
deserialize2(<<?MAP, Rest/binary>>) -> deserialize2(<<?MAP, Rest/binary>>) ->
{BSize, Rest1} = aeser_rlp:decode_one(Rest), {Size, Rest1} = rlp_decode_int(Rest),
Size = binary:decode_unsigned(BSize),
{List, Rest2} = deserialize_elements(2*Size, Rest1), {List, Rest2} = deserialize_elements(2*Size, Rest1),
Map = insert_kv(List, #{}), KVList = insert_kv(List),
{?MAKE_FATE_MAP(Map), Rest2}; case sort_and_check(KVList) == KVList of
true ->
Map = maps:from_list(KVList),
{?MAKE_FATE_MAP(Map), Rest2};
false ->
error({unknown_map_serialization_format, KVList})
end;
deserialize2(<<?VARIANT, Rest/binary>>) -> deserialize2(<<?VARIANT, Rest/binary>>) ->
{AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest), {AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest),
Arities = binary_to_list(AritiesBin), Arities = binary_to_list(AritiesBin),
@@ -388,10 +440,13 @@ deserialize2(<<?VARIANT, Rest/binary>>) ->
true -> true ->
{?FATE_VARIANT(Arities, Tag, T), Rest3} {?FATE_VARIANT(Arities, Tag, T), Rest3}
end end
end. end;
deserialize2(<<TypeTag, _/binary>> = Bin) when ?IS_TYPE_TAG(TypeTag) ->
{Type, Rest} = deserialize_type(Bin),
{?FATE_TYPEREP(Type), Rest}.
insert_kv([], M) -> M; insert_kv([]) -> [];
insert_kv([K,V|R], M) -> insert_kv(R, maps:put(K, V, M)). insert_kv([K, V | R]) -> [{K, V} | insert_kv(R)].
deserialize_elements(0, Rest) -> deserialize_elements(0, Rest) ->
{[], Rest}; {[], Rest};
@@ -399,3 +454,31 @@ deserialize_elements(N, Es) ->
{E, Rest} = deserialize2(Es), {E, Rest} = deserialize2(Es),
{Tail, Rest2} = deserialize_elements(N-1, Rest), {Tail, Rest2} = deserialize_elements(N-1, Rest),
{[E|Tail], Rest2}. {[E|Tail], Rest2}.
%% It is important to remove duplicated keys.
%% For deserialize this check is needed to observe illegal duplicates.
sort_and_check(List) ->
UniqKeyList =
lists:foldr(fun({K, V}, Acc) ->
case valid_key_type(K) andalso not lists:keymember(K, 1, Acc) of
true -> [{K,V}|Acc];
false -> Acc
end
end, [], List),
sort(UniqKeyList).
%% Sorting is used to get a unique result.
%% Deserialization is checking whether the provided key-value pairs are sorted
%% and raises an exception if not.
sort(KVList) ->
SortFun = fun({K1, _}, {K2, _}) ->
aeb_fate_data:elt(K1, K2)
end,
lists:sort(SortFun, KVList).
valid_key_type(K) when ?IS_FATE_MAP(K) ->
error({map_as_key_in_map, K});
valid_key_type(_K) ->
true.
+216 -151
View File
@@ -16,140 +16,173 @@ generate() -> generate("src/", "include/").
get_ops() -> gen(ops_defs()). get_ops() -> gen(ops_defs()).
generate(Src, Include) -> generate(Src, Include) ->
check_defs(ops_defs()),
Ops = get_ops(), Ops = get_ops(),
%% io:format("ops: ~p\n", [Ops]), %% io:format("ops: ~p\n", [Ops]),
HrlFile = Include ++ "aeb_fate_opcodes.hrl", HrlFile = Include ++ "aeb_fate_opcodes.hrl",
generate_header_file(HrlFile, Ops), generate_header_file(HrlFile, Ops),
generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops), generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops),
generate_code_ops(aeb_fate_code, Src, Ops), generate_code_ops(aeb_fate_ops, Src, Ops),
generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops), generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", Src, Ops),
gen_asm_pp(aeb_fate_pp, Src, Ops). gen_asm_pp(aeb_fate_pp, Src, Ops).
check_defs(List) ->
true = check_numbering(0, lists:keysort(2, List)).
check_numbering(N, [T|Rest]) ->
OpCode = element(2, T),
case OpCode of
N -> check_numbering(N+1, Rest);
16#fa -> check_numbering(16#fa+1, Rest);
_ when OpCode < N -> {duplicate_opcode, OpCode};
_ when OpCode > N -> {missing_opcode, N}
end;
check_numbering(_, []) -> true.
%% TODO: Some real gas numbers... %% TODO: Some real gas numbers...
ops_defs() -> ops_defs() ->
%% Opname, Opcode, args, end_bb, gas, format, Constructor, Documentation %% Opname, Opcode, end_bb, in_auth offchain, gas, format, Constructor, ArgType, ResType, Documentation
[ { 'RETURN', 16#00, 0, true, 2, atomic, return, "Return from function call pop stack to arg0. The type of the retun value has to match the return type of the function."} [ { 'RETURN', 16#00, true, true, true, 2, [], return, {}, any, "Return from function call, top of stack is return value . The type of the retun value has to match the return type of the function."}
, { 'RETURNR', 16#01, 1, true, 2, [a], returnr, "Return from function call copy Arg0 to arg0. The type of the retun value has to match the return type of the function."} , { 'RETURNR', 16#01, true, true, true, 2, [a], returnr, {any}, any, "Push Arg0 and return from function. The type of the retun value has to match the return type of the function."}
, { 'CALL', 16#02, 1, true, 4, [is], call, "Call given function with args on stack. The types of the arguments has to match the argument typs of the function."} , { 'CALL', 16#02, true, true, true, 4, [a], call, {string}, any, "Call the function Arg0 with args on stack. The types of the arguments has to match the argument typs of the function."}
, { 'CALL_R', 16#03, 2, true, 8, [a,is], call_r, "Remote call to given contract and function. The types of the arguments has to match the argument typs of the function."} , { 'CALL_R', 16#03, true, false, true, 8, [a,is,a], call_r, {contract, string, integer}, any, "Remote call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function."}
, { 'CALL_T', 16#04, 1, true, 4, [is], call_t, "Tail call to given function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."} , { 'CALL_T', 16#04, true, true, true, 4, [a], call_t, {string}, any, "Tail call to function Arg0. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
, { 'CALL_TR', 16#05, 2, true, 8, [a,is], call_tr, "Remote tail call to given contract and function. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."} , { 'CALL_TR', 16#05, true, false, true, 8, [a,is,a], call_tr, {contract, string, integer}, any, "Remote tail call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
, { 'JUMP', 16#06, 1, true, 3, [ii], jump, "Jump to a basic block. The basic block has to exist in the current function."} , { 'CALL_GR', 16#06, true, false, true, 8, [a,is,a,a], call_gr, {contract, string, integer, integer}, any, "Remote call with gas cap in Arg3. Otherwise as CALL_R."}
, { 'JUMPIF', 16#07, 2, true, 4, [a,ii], jumpif, "Conditional jump to a basic block. If Arg0 then jump to Arg1."} , { 'CALL_GTR', 16#07, true, false, true, 8, [a,is,a,a], call_gtr, {contract, string, integer, integer}, any, "Remote tail call with gas cap in Arg3. Otherwise as CALL_TR."}
, { 'SWITCH_V2', 16#08, 3, true, 4, [a,ii,ii], switch, "Conditional jump to a basic block on variant tag."} , { 'JUMP', 16#08, true, true, true, 3, [ii], jump, {integer}, none, "Jump to a basic block. The basic block has to exist in the current function."}
, { 'SWITCH_V3', 16#09, 4, true, 4, [a,ii,ii,ii], switch, "Conditional jump to a basic block on variant tag."} , { 'JUMPIF', 16#09, true, true, true, 4, [a,ii], jumpif, {boolean, integer}, none, "Conditional jump to a basic block. If Arg0 then jump to Arg1."}
, { 'SWITCH_VN', 16#0a, 2, true, 4, [a, li], switch, "Conditional jump to a basic block on variant tag."} , { 'SWITCH_V2', 16#0a, true, true, true, 4, [a,ii,ii], switch, {variant, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'PUSH', 16#0b, 1, false, 2, [a], push, "Push argument to stack."} , { 'SWITCH_V3', 16#0b, true, true, true, 4, [a,ii,ii,ii], switch, {variant, integer, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'DUPA', 16#0c, 0, false, 3, atomic, dup, "push copy of accumulator on stack."} , { 'SWITCH_VN', 16#0c, true, true, true, 4, [a, li], switch, {variant, {list, integer}}, none, "Conditional jump to a basic block on variant tag."}
, { 'DUP', 16#0d, 1, false, 3, [a], dup, "push Arg0 stack pos on top of stack."} , { 'CALL_VALUE', 16#0d, false, true, true, 3, [a], call_value, {}, integer, "The value sent in the current remote call."}
, { 'POP', 16#0e, 1, false, 3, [a], pop, "Arg0 := top of stack."} , { 'PUSH', 16#0e, false, true, true, 2, [a], push, {any}, any, "Push argument to stack."}
, { 'STORE', 16#0f, 2, false, 3, [a,a], store, "Arg0 := Arg1."} , { 'DUPA', 16#0f, false, true, true, 3, [], dup, {any}, any, "Duplicate top of stack."}
, { 'INCA', 16#10, 0, false, 2, atomic, inc, "Increment accumulator."} , { 'DUP', 16#10, false, true, true, 3, [a], dup, {any}, any, "push Arg0 stack pos on top of stack."}
, { 'INC', 16#11, 1, false, 2, [a], inc, "Increment argument."} , { 'POP', 16#11, false, true, true, 3, [a], pop, {integer}, integer, "Arg0 := top of stack."}
, { 'DECA', 16#12, 0, false, 2, atomic, dec, "Decrement accumulator."} , { 'INCA', 16#12, false, true, true, 2, [], inc, {integer}, integer, "Increment accumulator."}
, { 'DEC', 16#13, 1, false, 2, [a], dec, "Decrement argument."} , { 'INC', 16#13, false, true, true, 2, [a], inc, {integer}, integer, "Increment argument."}
, { 'ADD', 16#14, 3, false, 3, [a,a,a], add, "Arg0 := Arg1 + Arg2."} , { 'DECA', 16#14, false, true, true, 2, [], dec, {integer}, integer, "Decrement accumulator."}
, { 'SUB', 16#15, 3, false, 3, [a,a,a], sub, "Arg0 := Arg1 - Arg2."} , { 'DEC', 16#15, false, true, true, 2, [a], dec, {integer}, integer, "Decrement argument."}
, { 'MUL', 16#16, 3, false, 3, [a,a,a], mul, "Arg0 := Arg1 * Arg2."} , { 'ADD', 16#16, false, true, true, 3, [a,a,a], add, {integer, integer}, integer, "Arg0 := Arg1 + Arg2."}
, { 'DIV', 16#17, 3, false, 3, [a,a,a], divide, "Arg0 := Arg1 / Arg2."} , { 'SUB', 16#17, false, true, true, 3, [a,a,a], sub, {integer, integer}, integer, "Arg0 := Arg1 - Arg2."}
, { 'MOD', 16#18, 3, false, 3, [a,a,a], modulo, "Arg0 := Arg1 mod Arg2."} , { 'MUL', 16#18, false, true, true, 3, [a,a,a], mul, {integer, integer}, integer, "Arg0 := Arg1 * Arg2."}
, { 'POW', 16#19, 3, false, 3, [a,a,a], pow, "Arg0 := Arg1 ^ Arg2."} , { 'DIV', 16#19, false, true, true, 3, [a,a,a], divide, {integer, integer}, integer, "Arg0 := Arg1 / Arg2."}
, { 'LT', 16#20, 3, false, 3, [a,a,a], lt, "Arg0 := Arg1 < Arg2."} , { 'MOD', 16#1a, false, true, true, 3, [a,a,a], modulo, {integer, integer}, integer, "Arg0 := Arg1 mod Arg2."}
, { 'GT', 16#21, 3, false, 3, [a,a,a], gt, "Arg0 := Arg1 > Arg2."} , { 'POW', 16#1b, false, true, true, 3, [a,a,a], pow, {integer, integer}, integer, "Arg0 := Arg1 ^ Arg2."}
, { 'EQ', 16#22, 3, false, 3, [a,a,a], eq, "Arg0 := Arg1 = Arg2."} , { 'STORE', 16#1c, false, true, true, 3, [a,a], store, {any}, any, "Arg0 := Arg1."}
, { 'ELT', 16#23, 3, false, 3, [a,a,a], elt, "Arg0 := Arg1 =< Arg2."} , { 'SHA3', 16#1d, false, true, true, 30, [a,a], sha3, {any}, hash, "Arg0 := sha3(Arg1)."}
, { 'EGT', 16#24, 3, false, 3, [a,a,a], egt, "Arg0 := Arg1 >= Arg2."} , { 'SHA256', 16#1e, false, true, true, 30, [a,a], sha256, {any}, hash, "Arg0 := sha256(Arg1)."}
, { 'NEQ', 16#25, 3, false, 3, [a,a,a], neq, "Arg0 := Arg1 /= Arg2."} , { 'BLAKE2B', 16#1f, false, true, true, 30, [a,a], blake2b, {any}, hash, "Arg0 := blake2b(Arg1)."}
, { 'AND', 16#26, 3, false, 3, [a,a,a], and_op, "Arg0 := Arg1 and Arg2."} , { 'LT', 16#20, false, true, true, 3, [a,a,a], lt, {integer, integer}, boolean, "Arg0 := Arg1 < Arg2."}
, { 'OR', 16#27, 3, false, 3, [a,a,a], or_op, "Arg0 := Arg1 or Arg2."} , { 'GT', 16#21, false, true, true, 3, [a,a,a], gt, {integer, integer}, boolean, "Arg0 := Arg1 > Arg2."}
, { 'NOT', 16#28, 2, false, 3, [a,a], not_op, "Arg0 := not Arg1."} , { 'EQ', 16#22, false, true, true, 3, [a,a,a], eq, {integer, integer}, boolean, "Arg0 := Arg1 = Arg2."}
, { 'TUPLE', 16#29, 1, false, 3, [ii], tuple, "Create a tuple of size = Arg0. Elements on stack."} , { 'ELT', 16#23, false, true, true, 3, [a,a,a], elt, {integer, integer}, boolean, "Arg0 := Arg1 =< Arg2."}
, { 'ELEMENT', 16#2a, 3, false, 3, [a,a,a], element_op, "Arg1 := element(Arg2, Arg3)."} , { 'EGT', 16#24, false, true, true, 3, [a,a,a], egt, {integer, integer}, boolean, "Arg0 := Arg1 >= Arg2."}
, { 'MAP_EMPTY', 16#2b, 1, false, 3, [a], map_empty, "Arg0 := #{}."} , { 'NEQ', 16#25, false, true, true, 3, [a,a,a], neq, {integer, integer}, boolean, "Arg0 := Arg1 /= Arg2."}
, { 'MAP_LOOKUP', 16#2c, 3, false, 3, [a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1."} , { 'AND', 16#26, false, true, true, 3, [a,a,a], and_op, {boolean, boolean}, boolean, "Arg0 := Arg1 and Arg2."}
, { 'MAP_LOOKUPD', 16#2d, 4, false, 3, [a,a,a,a], map_lookup, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."} , { 'OR', 16#27, false, true, true, 3, [a,a,a], or_op, {boolean, boolean}, boolean, "Arg0 := Arg1 or Arg2."}
, { 'MAP_UPDATE', 16#2e, 4, false, 3, [a,a,a,a], map_update, "Arg0 := update key Arg2 in map Arg1 with value Arg3."} , { 'NOT', 16#28, false, true, true, 3, [a,a], not_op, {boolean}, boolean, "Arg0 := not Arg1."}
, { 'MAP_DELETE', 16#2f, 3, false, 3, [a,a,a], map_delete, "Arg0 := delete key Arg2 from map Arg1."} , { 'TUPLE', 16#29, false, true, true, 3, [a,ii], tuple, {integer}, tuple, "Arg0 := tuple of size = Arg1. Elements on stack."}
, { 'MAP_MEMBER', 16#30, 3, false, 3, [a,a,a], map_member, "Arg0 := true if key Arg2 is in map Arg1."} , { 'ELEMENT', 16#2a, false, true, true, 3, [a,a,a], element_op, {integer, tuple}, any, "Arg1 := element(Arg2, Arg3)."}
, { 'MAP_FROM_LIST',16#31, 2, false, 3, [a,a], map_from_list, "Arg0 := make a map from (key, value) list in Arg1."} , { 'SETELEMENT', 16#2b, false, true, true, 3, [a,a,a,a], setelement, {integer, tuple, any}, tuple, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
, { 'NIL', 16#32, 1, false, 3, [a], nil, "Arg0 := []."} , { 'MAP_EMPTY', 16#2c, false, true, true, 3, [a], map_empty, {}, map, "Arg0 := #{}."}
, { 'IS_NIL', 16#33, 2, false, 3, [a,a], is_nil, "Arg0 := true if Arg1 == []."} , { 'MAP_LOOKUP', 16#2d, false, true, true, 3, [a,a,a], map_lookup, {map, any}, any, "Arg0 := lookup key Arg2 in map Arg1."}
, { 'CONS', 16#34, 3, false, 3, [a,a,a], cons, "Arg0 := [Arg1|Arg2]."} , { 'MAP_LOOKUPD', 16#2e, false, true, true, 3, [a,a,a,a], map_lookup, {map, any, any}, any, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."}
, { 'HD', 16#35, 2, false, 3, [a,a], hd, "Arg0 := head of list Arg1."} , { 'MAP_UPDATE', 16#2f, false, true, true, 3, [a,a,a,a], map_update, {map, any, any}, map, "Arg0 := update key Arg2 in map Arg1 with value Arg3."}
, { 'TL', 16#36, 2, false, 3, [a,a], tl, "Arg0 := tail of list Arg1."} , { 'MAP_DELETE', 16#30, false, true, true, 3, [a,a,a], map_delete, {map, any}, map, "Arg0 := delete key Arg2 from map Arg1."}
, { 'LENGTH', 16#37, 2, false, 3, [a,a], length, "Arg0 := length of list Arg1."} , { 'MAP_MEMBER', 16#31, false, true, true, 3, [a,a,a], map_member, {map, any}, boolean, "Arg0 := true if key Arg2 is in map Arg1."}
, { 'STR_EQ', 16#38, 3, false, 3, [a,a,a], str_eq, "Arg0 := true iff the strings Arg1 and Arg2 are the same."} , { 'MAP_FROM_LIST', 16#32, false, true, true, 3, [a,a], map_from_list, {{list, {tuple, [any, any]}}}, map, "Arg0 := make a map from (key, value) list in Arg1."}
, { 'STR_JOIN', 16#39, 3, false, 3, [a,a,a], str_join, "Arg0 := string Arg1 followed by string Arg2."} , { 'IS_NIL', 16#33, false, true, true, 3, [a,a], is_nil, {list}, boolean, "Arg0 := true if Arg1 == []."}
, { 'INT_TO_STR', 16#40, 2, false, 3, [a,a], int_to_str, "Arg0 := turn integer Arg1 into a string."} , { 'CONS', 16#34, false, true, true, 3, [a,a,a], cons, {any, list}, list, "Arg0 := [Arg1|Arg2]."}
, { 'ADDR_TO_STR', 16#41, 2, false, 3, [a,a], addr_to_str, "Arg0 := turn address Arg1 into a string."} , { 'HD', 16#35, false, true, true, 3, [a,a], hd, {list}, any, "Arg0 := head of list Arg1."}
, { 'STR_REVERSE', 16#42, 2, false, 3, [a,a], str_reverse, "Arg0 := the reverse of string Arg1."} , { 'TL', 16#36, false, true, true, 3, [a,a], tl, {list}, list, "Arg0 := tail of list Arg1."}
, { 'INT_TO_ADDR', 16#43, 2, false, 3, [a,a], int_to_addr, "Arg0 := turn integer Arg1 into an address."} , { 'LENGTH', 16#37, false, true, true, 3, [a,a], length, {list}, integer, "Arg0 := length of list Arg1."}
, { 'VARIANT', 16#44, 4, false, 3, [a,a,a,a], variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."} , { 'NIL', 16#38, false, true, true, 3, [a], nil, {}, list, "Arg0 := []."}
, { 'VARIANT_TEST', 16#45, 3, false, 3, [a,a,a], variant_test, "Arg0 := true if variant Arg1 has the tag Arg2."} , { 'STR_JOIN', 16#39, false, true, true, 3, [a,a,a], str_join, {string, string}, string, "Arg0 := string Arg1 followed by string Arg2."}
, { 'VARIANT_ELEMENT',16#46, 3, false, 3, [a,a,a], variant_element, "Arg0 := element number Arg2 from variant Arg1."} , { 'INT_TO_STR', 16#3a, false, true, true, 3, [a,a], int_to_str, {integer}, string, "Arg0 := turn integer Arg1 into a string."}
, { 'BITS_NONEA', 16#47, 0, false, 3, atomic, bits_none, "accumulator := empty bitmap."} , { 'ADDR_TO_STR', 16#3b, false, true, true, 3, [a,a], addr_to_str, {address}, string, "Arg0 := turn address Arg1 into a string."}
, { 'BITS_NONE', 16#48, 1, false, 3, [a], bits_none, "Arg0 := empty bitmap."} , { 'STR_REVERSE', 16#3c, false, true, true, 3, [a,a], str_reverse, {string}, string, "Arg0 := the reverse of string Arg1."}
, { 'BITS_ALLA', 16#49, 0, false, 3, atomic, bits_all, "accumulator := full bitmap."} , { 'APPEND', 16#3d, false, true, true, 3, [a,a,a], append, {list, list}, list, "Arg0 := Arg1 ++ Arg2."}
, { 'BITS_ALL', 16#50, 1, false, 3, [a], bits_all, "Arg0 := full bitmap."} , { 'INT_TO_ADDR', 16#3e, false, true, true, 3, [a,a], int_to_addr, {integer}, address, "Arg0 := turn integer Arg1 into an address."}
, { 'BITS_ALL_N', 16#51, 2, false, 3, [a,a], bits_all_n, "Arg0 := bitmap with Arg1 bits set."} , { 'VARIANT', 16#3f, false, true, true, 3, [a,a,a,a], variant, {integer, integer, integer}, variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."}
, { 'BITS_SET', 16#52, 3, false, 3, [a,a,a], bits_set, "Arg0 := set bit Arg2 of bitmap Arg1."} , { 'VARIANT_TEST', 16#40, false, true, true, 3, [a,a,a], variant_test, {variant, integer}, boolean, "Arg0 := true if variant Arg1 has the tag Arg2."}
, { 'BITS_CLEAR', 16#53, 3, false, 3, [a,a,a], bits_clear, "Arg0 := clear bit Arg2 of bitmap Arg1."} , { 'VARIANT_ELEMENT', 16#41, false, true, true, 3, [a,a,a], variant_element, {variant, integer}, any, "Arg0 := element number Arg2 from variant Arg1."}
, { 'BITS_TEST', 16#54, 3, false, 3, [a,a,a], bits_test, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."} , { 'BITS_NONEA', 16#42, false, true, true, 3, [], bits_none, {}, bits, "push an empty bitmap on the stack."}
, { 'BITS_SUM', 16#55, 2, false, 3, [a,a], bits_sum, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."} , { 'BITS_NONE', 16#43, false, true, true, 3, [a], bits_none, {}, bits, "Arg0 := empty bitmap."}
, { 'BITS_OR', 16#56, 3, false, 3, [a,a,a], bits_or, "Arg0 := Arg1 v Arg2."} , { 'BITS_ALLA', 16#44, false, true, true, 3, [], bits_all, {}, bits, "push a full bitmap on the stack."}
, { 'BITS_AND', 16#57, 3, false, 3, [a,a,a], bits_and, "Arg0 := Arg1 ^ Arg2."} , { 'BITS_ALL', 16#45, false, true, true, 3, [a], bits_all, {}, bits, "Arg0 := full bitmap."}
, { 'BITS_DIFF', 16#58, 3, false, 3, [a,a,a], bits_diff, "Arg0 := Arg1 - Arg2."} , { 'BITS_ALL_N', 16#46, false, true, true, 3, [a,a], bits_all_n, {integer}, bits, "Arg0 := bitmap with Arg1 bits set."}
, { 'ADDRESS', 16#59, 1, false, 3, [a], address, "Arg0 := The current contract address."} , { 'BITS_SET', 16#47, false, true, true, 3, [a,a,a], bits_set, {bits, integer}, bits, "Arg0 := set bit Arg2 of bitmap Arg1."}
, { 'BALANCE', 16#5a, 1, false, 3, [a], balance, "Arg0 := The current contract balance."} , { 'BITS_CLEAR', 16#48, false, true, true, 3, [a,a,a], bits_clear, {bits, integer}, bits, "Arg0 := clear bit Arg2 of bitmap Arg1."}
, { 'ORIGIN', 16#5b, 1, false, 3, [a], origin, "Arg0 := Address of contract called by the call transaction."} , { 'BITS_TEST', 16#49, false, true, true, 3, [a,a,a], bits_test, {bits, integer}, boolean, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."}
, { 'CALLER', 16#5c, 1, false, 3, [a], caller, "Arg0 := The address that signed the call transaction."} , { 'BITS_SUM', 16#4a, false, true, true, 3, [a,a], bits_sum, {bits}, integer, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."}
, { 'GASPRICE', 16#5d, 1, false, 3, [a], gasprice, "Arg0 := The current gas price."} , { 'BITS_OR', 16#4b, false, true, true, 3, [a,a,a], bits_or, {bits, bits}, bits, "Arg0 := Arg1 v Arg2."}
, { 'BLOCKHASH', 16#5e, 2, false, 3, [a, a], blockhash, "Arg0 := The blockhash at height."} , { 'BITS_AND', 16#4c, false, true, true, 3, [a,a,a], bits_and, {bits, bits}, bits, "Arg0 := Arg1 ^ Arg2."}
, { 'BENEFICIARY', 16#5f, 1, false, 3, [a], beneficiary, "Arg0 := The address of the current beneficiary."} , { 'BITS_DIFF', 16#4d, false, true, true, 3, [a,a,a], bits_diff, {bits, bits}, bits, "Arg0 := Arg1 - Arg2."}
, { 'TIMESTAMP', 16#60, 1, false, 3, [a], timestamp, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."} , { 'BALANCE', 16#4e, false, true, true, 3, [a], balance, {}, integer, "Arg0 := The current contract balance."}
, { 'GENERATION', 16#61, 1, false, 3, [a], generation, "Arg0 := The block height of the cureent generation."} , { 'ORIGIN', 16#4f, false, true, true, 3, [a], origin, {}, address, "Arg0 := Address of contract called by the call transaction."}
, { 'MICROBLOCK', 16#62, 1, false, 3, [a], microblock, "Arg0 := The current micro block number."} , { 'CALLER', 16#50, false, true, true, 3, [a], caller, {}, address, "Arg0 := The address that signed the call transaction."}
, { 'DIFFICULTY', 16#63, 1, false, 3, [a], difficulty, "Arg0 := The current difficulty."} , { 'GASPRICE', 16#51, false, true, true, 3, [a], gasprice, {}, integer, "Arg0 := The current gas price."}
, { 'GASLIMIT', 16#64, 1, false, 3, [a], gaslimit, "Arg0 := The current gaslimit."} , { 'BLOCKHASH', 16#52, false, true, true, 3, [a,a], blockhash, {integer}, hash, "Arg0 := The blockhash at height."}
, { 'GAS', 16#65, 1, false, 3, [a], gas, "Arg0 := The amount of gas left."} , { 'BENEFICIARY', 16#53, false, true, true, 3, [a], beneficiary, {}, address, "Arg0 := The address of the current beneficiary."}
, { 'TIMESTAMP', 16#54, false, true, true, 3, [a], timestamp, {}, integer, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
, { 'GENERATION', 16#55, false, true, true, 3, [a], generation, {}, integer, "Arg0 := The block height of the cureent generation."}
, { 'MICROBLOCK', 16#56, false, true, true, 3, [a], microblock, {}, integer, "Arg0 := The current micro block number."}
, { 'DIFFICULTY', 16#57, false, true, true, 3, [a], difficulty, {}, integer, "Arg0 := The current difficulty."}
, { 'GASLIMIT', 16#58, false, true, true, 3, [a], gaslimit, {}, integer, "Arg0 := The current gaslimit."}
, { 'GAS', 16#59, false, true, true, 3, [a], gas, {}, integer, "Arg0 := The amount of gas left."}
, { 'ADDRESS', 16#5a, false, true, true, 3, [a], address, {}, address, "Arg0 := The current contract address."}
, { 'LOG0', 16#66, 2, false, 3, [a,a], log, "Create a log message in the call object."} , { 'LOG0', 16#5b, false, true, true, 3, [a], log, {string}, none, "Create a log message in the call object."}
, { 'LOG1', 16#67, 3, false, 3, [a,a,a], log, "Create a log message with one topic in the call object."} , { 'LOG1', 16#5c, false, true, true, 3, [a,a], log, {integer, string}, none, "Create a log message with one topic in the call object."}
, { 'LOG2', 16#68, 4, false, 3, [a,a,a,a], log, "Create a log message with two topics in the call object."} , { 'LOG2', 16#5d, false, true, true, 3, [a,a,a], log, {integer, integer, string}, none, "Create a log message with two topics in the call object."}
, { 'LOG3', 16#69, 5, false, 3, [a,a,a,a,a], log, "Create a log message with three topics in the call object."} , { 'LOG3', 16#5e, false, true, true, 3, [a,a,a,a], log, {integer, integer, integer, string}, none, "Create a log message with three topics in the call object."}
, { 'LOG4', 16#6a, 6, false, 3, [a,a,a,a,a,a], log, "Create a log message with four topics in the call object."} , { 'LOG4', 16#5f, false, true, true, 3, [a,a,a,a,a], log, {integer, integer, integer, integer, string}, none, "Create a log message with four topics in the call object."}
, { 'DEACTIVATE', 16#6b, 0, false, 3, atomic, deactivate, "Mark the current contract for deactication."}
%% Transaction ops %% Transaction ops
, { 'SPEND', 16#6c, 2, false,3, [a,a], spend, "Transfer Arg0 tokens to account Arg1. (If the contract account has at least that many tokens."} , { 'SPEND', 16#60, false, false, true, 3, [a,a], spend, {address, integer}, none, "Transfer Arg1 tokens to account Arg0. (If the contract account has at least that many tokens."}
, { 'ORACLE_REGISTER', 16#6d, 6, false,3, [a,a,a,a,a,a], oracle_register, "Mark the current contract for deactication."} , { 'ORACLE_REGISTER', 16#61, false, false, false, 3, [a,a,a,a,a,a,a], oracle_register, {signature, address, integer, variant, typerep, typerep}, oracle, "Arg0 := New oracle with address Arg2, query fee Arg3, TTL Arg4, query type Arg5 and response type Arg6. Arg0 contains delegation signature."}
%% TODO: , { 'ORACLE_QUERY', 16#62, false, false, false, 3, [a,a,a,a,a,a,a,a], oracle_query, {oracle, any, integer, variant, variant, typerep, typerep}, oracle_query, "Arg0 := New oracle query for oracle Arg1, question in Arg2, query fee in Arg3, query TTL in Arg4, response TTL in Arg5. Typereps for checking oracle type is in Arg6 and Arg7."}
, { 'ORACLE_QUERY', 16#6e, 0, false,3, atomic, oracle_query, ""} , { 'ORACLE_RESPOND', 16#63, false, false, false, 3, [a,a,a,a,a,a], oracle_respond, {signature, oracle, oracle_query,any, typerep, typerep}, none, "Respond as oracle Arg1 to query in Arg2 with response Arg3. Arg0 contains delegation signature. Typereps for checking oracle type is in Arg4 and Arg5."}
, { 'ORACLE_RESPOND', 16#6f, 0, false,3, atomic, oracle_respond, ""} , { 'ORACLE_EXTEND', 16#64, false, false, false, 3, [a,a,a], oracle_extend, {signature, oracle, variant}, none, "Extend oracle in Arg1 with TTL in Arg2. Arg0 contains delegation signature."}
, { 'ORACLE_EXTEND', 16#70, 0, false,3, atomic, oracle_extend, ""} , { 'ORACLE_GET_ANSWER', 16#65, false, false, true, 3, [a,a,a,a,a], oracle_get_answer, {oracle, oracle_query, typerep, typerep}, any, "Arg0 := option variant with answer (if any) from oracle query in Arg1 given by oracle Arg0. Typereps for checking oracle type is in Arg3 and Arg4."}
, { 'ORACLE_GET_ANSWER', 16#71, 0, false,3, atomic, oracle_get_answer, ""} , { 'ORACLE_GET_QUESTION', 16#66, false, false, true, 3, [a,a,a,a,a], oracle_get_question, {oracle, oracle_query, typerep, typerep}, any, "Arg0 := question in oracle query Arg2 given to oracle Arg1. Typereps for checking oracle type is in Arg3 and Arg4."}
, { 'ORACLE_GET_QUESTION', 16#72, 0, false,3, atomic,oracle_get_question, ""} , { 'ORACLE_QUERY_FEE', 16#67, false, false, true, 3, [a,a], oracle_query_fee, {oracle}, integer, "Arg0 := query fee for oracle Arg1"}
, { 'ORACLE_QUERY_FEE', 16#73, 0, false,3, atomic, oracle_query_fee, ""} , { 'AENS_RESOLVE', 16#68, false, false, true, 3, [a,a,a,a], aens_resolve, {string, string, typerep}, variant, "Resolve name in Arg0 with tag Arg1. Arg2 describes the type parameter of the resolved name."}
, { 'AENS_RESOLVE', 16#74, 0, false,3, atomic, aens_resolve, ""} , { 'AENS_PRECLAIM', 16#69, false, false, false, 3, [a,a,a], aens_preclaim, {signature, address, hash}, none, "Preclaim the hash in Arg2 for address in Arg1. Arg0 contains delegation signature."}
, { 'AENS_PRECLAIM', 16#75, 0, false,3, atomic, aens_preclaim, ""} , { 'AENS_CLAIM', 16#6a, false, false, false, 3, [a,a,a,a,a], aens_claim, {signature, address, string, integer, integer}, none, "Attempt to claim the name in Arg2 for address in Arg1 at a price in Arg4. Arg3 contains the salt used to hash the preclaim. Arg0 contains delegation signature."}
, { 'AENS_CLAIM', 16#76, 0, false,3, atomic, aens_claim, ""} , { 'AENS_UPDATE', 16#6b, false, false, false, 3, [], aens_update, {}, none, "NYI"}
, { 'AENS_UPDATE', 16#77, 0, false,3, atomic, aend_update, ""} , { 'AENS_TRANSFER', 16#6c, false, false, false, 3, [a,a,a,a], aens_transfer,{signature, address, address, string}, none, "Transfer ownership of name Arg3 from account Arg1 to Arg2. Arg0 contains delegation signature."}
, { 'AENS_TRANSFER', 16#78, 0, false,3, atomic, aens_transfer, ""} , { 'AENS_REVOKE', 16#6d, false, false, false, 3, [a,a,a], aens_revoke, {signature, address, string}, none, "Revoke the name in Arg2 from owner Arg1. Arg0 contains delegation signature."}
, { 'AENS_REVOKE', 16#79, 0, false,3, atomic, aens_revoke, ""} , { 'BALANCE_OTHER', 16#6e, false, true, true, 3, [a,a], balance_other, {address}, integer, "Arg0 := The balance of address Arg1."}
, { 'ECVERIFY', 16#7a, 0, false,3, atomic, ecverify, ""} %% TODO: Reorder these before documenting the specification
, { 'SHA3', 16#7b, 0, false,3, atomic, sha3, ""} , { 'MAP_SIZE', 16#6f, false, true, true, 3, [a,a], map_size_, {map}, integer, "Arg0 := The size of the map Arg1."}
, { 'SHA256', 16#7c, 0, false,3, atomic, sha256, ""} , { 'MAP_TO_LIST', 16#70, false, true, true, 3, [a,a], map_to_list, {map}, list, "Arg0 := The tuple list representation of the map Arg1."}
, { 'BLAKE2B', 16#7d, 0, false,3, atomic, blake2b, ""} , { 'STR_LENGTH', 16#71, false, true, true, 3, [a,a], str_length, {string}, integer, "Arg0 := The length of the string Arg1."}
, { 'BALANCE_OTHER', 16#7e, 2, false,3, [a,a], balance_other, "Arg0 := The balance of address Arg1."}
, { 'SETELEMENT', 16#7f, 4, false,3, [a,a,a,a], setelement, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
, { 'ECVERIFY', 16#72, false, true, true, 1300, [a,a,a,a], ecverify, {bytes, address, bytes}, boolean, "Arg0 := ecverify(Hash, PubKey, Signature)"}
, { 'ECVERIFY_SECP256K1', 16#73, false, true, true, 1300, [a,a,a,a], ecverify_secp256k1, {bytes, bytes, bytes}, boolean, "Arg0 := ecverify_secp256k1(Hash, PubKey, Signature)"}
, { 'DUMMY7ARG', 16#f9, 7, false,3, [a,a,a,a,a,a,a], dummyarg, "Temporary dummy instruction to test 7 args."} , { 'CONTRACT_TO_ADDRESS', 16#74, false, true, true, 3, [a,a], contract_to_address, {contract}, address, "Arg0 := Arg1 - A no-op type conversion"}
, { 'DUMMY8ARG', 16#fa, 8, false,3, [a,a,a,a,a,a,a,a],dummyarg, "Temporary dummy instruction to test 8 args."} , { 'AUTH_TX_HASH', 16#75, false, true, true, 3, [a], auth_tx_hash, {}, variant, "If in GA authentication context return Some(TxHash) otherwise None."}
, { 'ABORT', 16#fb, 1, false, 3, [a], abort, "Abort execution (dont use all gas) with error message in Arg0."}
, { 'EXIT', 16#fc, 1, false, 3, [a], exit, "Abort execution (use upp all gas) with error message in Arg0."} , { 'BYTES_TO_INT', 16#76, false, true, true, 3, [a,a], bytes_to_int, {bytes}, integer, "Arg0 := bytes_to_int(Arg1)"}
, { 'NOP', 16#fd, 0, false, 1, atomic, nop, "The no op. does nothing."} , { 'BYTES_TO_STR', 16#77, false, true, true, 3, [a,a], bytes_to_str, {bytes}, string, "Arg0 := bytes_to_str(Arg1)"}
%% FUNCTION 16#fe "Function declaration and entrypoint."
%% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes." , { 'ORACLE_CHECK', 16#78, false, false, true, 3, [a,a,a,a], oracle_check, {oracle, typerep, typerep}, bool, "Arg0 := is Arg1 an oracle with the given query (Arg2) and response (Arg3) types"}
, { 'ORACLE_CHECK_QUERY', 16#79, false, false, true, 3, [a,a,a,a,a], oracle_check_query, {oracle, oracle_query, typerep, typerep}, bool, "Arg0 := is Arg2 a query for the oracle Arg1 with the given types (Arg3, Arg4)"}
, { 'IS_ORACLE', 16#7a, false, false, true, 3, [a,a], is_oracle, {address}, bool, "Arg0 := is Arg1 an oracle"}
, { 'IS_CONTRACT', 16#7b, false, false, true, 3, [a,a], is_contract, {address}, bool, "Arg0 := is Arg1 a contract"}
, { 'CREATOR', 16#7c, false, true, true, 3, [a], contract_creator, {}, address, "Arg0 := contract creator"}
, { 'DEACTIVATE', 16#fa, false, true, true, 3, [], deactivate, {}, none, "Mark the current contract for deactivation."}
, { 'ABORT', 16#fb, true, true, true, 3, [a], abort, {string}, none, "Abort execution (dont use all gas) with error message in Arg0."}
, { 'EXIT', 16#fc, true, true, true, 3, [a], exit, {string}, none, "Abort execution (use upp all gas) with error message in Arg0."}
, { 'NOP', 16#fd, false, true, true, 1, [], nop, {}, none, "The no op. does nothing."}
%% FUNCTION 16#fe "Function declaration and entrypoint."
%% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes."
]. ].
@@ -171,11 +204,15 @@ generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) ->
ToOp = lists:flatten([gen_m_to_op(Op) || Op <- Ops]), ToOp = lists:flatten([gen_m_to_op(Op) || Op <- Ops]),
Args = lists:flatten([gen_args(Op) || Op <- Ops]), Args = lists:flatten([gen_args(Op) || Op <- Ops]),
EndBB = lists:flatten([gen_bb(Op) || Op <- Ops]), EndBB = lists:flatten([gen_bb(Op) || Op <- Ops]),
InAuth = lists:flatten([gen_in_auth(Op) || Op <- Ops]),
Offchain = lists:flatten([gen_allowed_offchain(Op) || Op <- Ops]),
io:format(File, "~s", [prelude("Provides opcode primitives.\n")]), io:format(File, "~s", [prelude("Provides opcode primitives.\n")]),
io:format(File, "~s", [ops_exports(Modulename, HrlFile, io:format(File, "~s", [ops_exports(Modulename, HrlFile,
["args/1\n" ["args/1\n"
" , end_bb/1\n" " , end_bb/1\n"
" , in_auth/1\n"
" , allowed_offchain/1\n"
" , mnemonic/1\n" " , mnemonic/1\n"
" , m_to_op/1\n" " , m_to_op/1\n"
])]), ])]),
@@ -192,6 +229,12 @@ generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) ->
io:format(File, "%% Does FATE Op end a Basic Block?\n~s", [EndBB]), io:format(File, "%% Does FATE Op end a Basic Block?\n~s", [EndBB]),
io:format(File, "end_bb(_) -> false.\n\n", []), io:format(File, "end_bb(_) -> false.\n\n", []),
io:format(File, "%% Is FATE Op allowed in GA Authentication context?\n~s", [InAuth]),
io:format(File, "in_auth(_) -> false.\n\n", []),
io:format(File, "%% Is FATE Op allowed in a state channel offchain context?\n~s", [Offchain]),
io:format(File, "allowed_offchain(_) -> false.\n\n", []),
file:close(File). file:close(File).
generate_code_ops(Modulename, SrcDir, Ops) -> generate_code_ops(Modulename, SrcDir, Ops) ->
@@ -215,7 +258,7 @@ generate_code_ops(Modulename, SrcDir, Ops) ->
"-type fate_arg_immediate(T) :: {immediate, T}.\n" "-type fate_arg_immediate(T) :: {immediate, T}.\n"
"-type fate_arg_var() :: {var, integer()}.\n" "-type fate_arg_var() :: {var, integer()}.\n"
"-type fate_arg_arg() :: {arg, integer()}.\n" "-type fate_arg_arg() :: {arg, integer()}.\n"
"-type fate_arg_stack() :: {stack, integer()}.\n" "-type fate_arg_stack() :: {stack, 0}.\n"
"-type fate_arg() :: fate_arg_immediate()\n" "-type fate_arg() :: fate_arg_immediate()\n"
" | fate_arg_var()\n" " | fate_arg_var()\n"
" | fate_arg_arg()\n" " | fate_arg_arg()\n"
@@ -246,7 +289,7 @@ gen_type_exports(#{type_name := TypeName}) ->
gen_constructor_exports(#{constructor_type := Function}) -> gen_constructor_exports(#{constructor_type := Function}) ->
lists:flatten(io_lib:format(" , ~s\n", [Function])). lists:flatten(io_lib:format(" , ~s\n", [Function])).
gen_constructors(#{constructor := Function, format := atomic, gen_constructors(#{constructor := Function, format := [],
type_name := Type, opname := Name}) -> type_name := Type, opname := Name}) ->
lists:flatten(io_lib:format("-spec ~s() -> ~s.\n" lists:flatten(io_lib:format("-spec ~s() -> ~s.\n"
"~s() ->\n" "~s() ->\n"
@@ -287,7 +330,7 @@ gen_arg_uses(_, []) ->
gen_arg_uses(N, [a]) -> io_lib:format("Arg~w", [N]); gen_arg_uses(N, [a]) -> io_lib:format("Arg~w", [N]);
gen_arg_uses(N, [is]) -> io_lib:format("{immediate, Arg~w}", [N]); gen_arg_uses(N, [is]) -> io_lib:format("{immediate, Arg~w}", [N]);
gen_arg_uses(N, [ii]) -> io_lib:format("{immediate, Arg~w}", [N]); gen_arg_uses(N, [ii]) -> io_lib:format("{immediate, Arg~w}", [N]);
gen_arg_uses(N, [li]) -> io_lib:format("[{immediate, I} || I <- Arg~w]", [N]); gen_arg_uses(N, [li]) -> io_lib:format("{immediate, Arg~w}", [N]);
gen_arg_uses(N, [t]) -> io_lib:format("Arg~w", [N]); gen_arg_uses(N, [t]) -> io_lib:format("Arg~w", [N]);
gen_arg_uses(N, [a | Args]) -> gen_arg_uses(N, [a | Args]) ->
io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args); io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args);
@@ -315,19 +358,25 @@ gen_mnemonic(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("mnemonic(~21s) -> ~21w ;\n", lists:flatten(io_lib:format("mnemonic(~21s) -> ~21w ;\n",
[Macro, Name])). [Macro, Name])).
gen_m_to_op(#{opname := Name, macro := Macro}) -> gen_m_to_op(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n", lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n",
[Name, Macro])). [Name, Macro])).
gen_args(#{macro := Macro, args := Args}) -> gen_args(#{macro := Macro, arity := Arity}) ->
lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n", lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n",
[Macro, Args])). [Macro, Arity])).
gen_bb(#{macro := Macro, end_bb := EndBB}) -> gen_bb(#{macro := Macro, end_bb := EndBB}) ->
lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n", lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n",
[Macro, EndBB])). [Macro, EndBB])).
gen_in_auth(#{macro := Macro, in_auth := InAuth}) ->
lists:flatten(io_lib:format("in_auth(~21s) -> ~w ;\n",
[Macro, InAuth])).
gen_allowed_offchain(#{macro := Macro, offchain := Offchain}) ->
lists:flatten(io_lib:format("allowed_offchain(~21s) -> ~w ;\n",
[Macro, Offchain])).
prelude(Doc) -> prelude(Doc) ->
"%%%-------------------------------------------------------------------\n" "%%%-------------------------------------------------------------------\n"
@@ -347,22 +396,25 @@ gen_defines(#{opname := Name, opcode := OpCode}) ->
gen([]) -> gen([]) ->
[]; [];
gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) -> gen([{OpName, OpCode, EndBB, InAuth, AllowedOffchain, Gas, FateFormat, Constructor, ArgTypes, ResType, Doc} | Rest]) ->
Arity = length(FateFormat),
Name = atom_to_list(OpName), Name = atom_to_list(OpName),
LowerName = string:to_lower(Name), LowerName = string:to_lower(Name),
TypeName = "fate_" ++ LowerName ++ "()", TypeName = "fate_" ++ LowerName ++ "()",
Macro = "?" ++ Name, Macro = "?" ++ Name,
Type = case FateFormat of Type = case FateFormat of
atomic -> io_lib:format("~w", [OpName]); [] -> io_lib:format("~w", [OpName]);
ArgTypes -> Args ->
io_lib:format("{~w, ~s}", [OpName, expand_types(ArgTypes)]) io_lib:format("{~w, ~s}", [OpName, expand_types(Args)])
end, end,
ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Args]), ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Arity]),
[#{ opname => OpName [#{ opname => OpName
, opcode => OpCode , opcode => OpCode
, args => Args , arity => Arity
, end_bb => EndBB , end_bb => EndBB
, in_auth => InAuth
, offchain => AllowedOffchain
, format => FateFormat , format => FateFormat
, macro => Macro , macro => Macro
, type_name => TypeName , type_name => TypeName
@@ -371,6 +423,8 @@ gen([{OpName, OpCode, Args, EndBB, Gas, FateFormat, Constructor, Doc} | Rest]) -
, type => Type , type => Type
, constructor => Constructor , constructor => Constructor
, constructor_type => ConstructorType , constructor_type => ConstructorType
, arg_types => ArgTypes
, res_type => ResType
}| gen(Rest)]. }| gen(Rest)].
@@ -381,7 +435,7 @@ expand_types([T|Ts]) ->expand_type(T) ++ ", " ++ expand_types(Ts).
expand_type(a) -> "fate_arg()"; expand_type(a) -> "fate_arg()";
expand_type(is) -> "fate_arg_immediate(aeb_fate_data:fate_string())"; expand_type(is) -> "fate_arg_immediate(aeb_fate_data:fate_string())";
expand_type(ii) -> "fate_arg_immediate(aeb_fate_data:fate_integer())"; expand_type(ii) -> "fate_arg_immediate(aeb_fate_data:fate_integer())";
expand_type(li) -> "[fate_arg_immediate(aeb_fate_data:fate_integer())]"; expand_type(li) -> "fate_arg_immediate([aeb_fate_data:fate_integer()])";
expand_type(t) -> "aeb_fate_data:fate_type_type()". expand_type(t) -> "aeb_fate_data:fate_type_type()".
generate_scanner(TemplateFile, Outfile, Path, Ops) -> generate_scanner(TemplateFile, Outfile, Path, Ops) ->
@@ -423,28 +477,40 @@ gen_asm_pp(Module, Path, Ops) ->
"format_arg(_, {immediate, I}) ->\n" "format_arg(_, {immediate, I}) ->\n"
" aeb_fate_data:format(I);\n" " aeb_fate_data:format(I);\n"
"format_arg(a, {arg, N}) -> io_lib:format(\"arg~~p\", [N]);\n" "format_arg(a, {arg, N}) -> io_lib:format(\"arg~~p\", [N]);\n"
"format_arg(a, {var, N}) when N < 0 -> io_lib:format(\"store~~p\", [-N]);\n"
"format_arg(a, {var, N}) -> io_lib:format(\"var~~p\", [N]);\n" "format_arg(a, {var, N}) -> io_lib:format(\"var~~p\", [N]);\n"
"format_arg(a, {stack, 0}) -> \"a\";\n" "format_arg(a, {stack, 0}) -> \"a\".\n\n"
"format_arg(a, {stack, N}) -> io_lib:format(\"a~~p\", [N]).\n\n"
"lookup(Name, Symbols) ->\n" "lookup(Name, Symbols) ->\n"
" maps:get(Name, Symbols, io_lib:format(\"~~w\",[Name])).\n\n" " maps:get(Name, Symbols, io_lib:format(\"~~p\",[Name])).\n\n"
"~s" "~s"
, [Formats]), , [Formats]),
io:format(File, "format_op(Op, _Symbols) -> io_lib:format(\";; Bad Op: ~~w\\n\", [Op]).\n", []), io:format(File, "format_op(Op, _Symbols) -> io_lib:format(\";; Bad Op: ~~w\\n\", [Op]).\n", []),
file:close(File). file:close(File).
gen_format(#{opname := Name}) when ('CALL' =:= Name) or (Name =:= 'CALL_T') ->
io_lib:format("format_op({~w, {immediate, Function}}, Symbols) ->\n"
"[\"~s \", lookup(Function, Symbols)];",
[Name, atom_to_list(Name)]);
gen_format(#{opname := Name}) when (Name =:= 'CALL_R') or (Name =:= 'CALL_TR') -> gen_format(#{opname := Name}) when (Name =:= 'CALL_R') or (Name =:= 'CALL_TR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}}, Symbols) ->\n" io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Value}, Symbols) ->\n"
"[\"~s \", lookup(Contract, Symbols), \".\", lookup(Function, Symbols)];\n" " [\"~s \", lookup(Contract, Symbols), \".\", "
"format_op({~w, Contract, {immediate, Function}}, Symbols) ->\n" "lookup(Function, Symbols), \" \", "
"[\"~s \", format_arg(a, Contract), \".\", lookup(Function, Symbols)];", "format_arg(a, Value)];\n"
"format_op({~w, Contract, {immediate, Function}, Value}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Value)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]); [Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name, format := atomic}) -> gen_format(#{opname := Name}) when (Name =:= 'CALL_GR') or (Name =:= 'CALL_GTR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Value, Gas}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n"
"format_op({~w, Contract, {immediate, Function}, Value, Gas}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name, format := []}) ->
io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]); io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]);
gen_format(#{opname := Name, format := Args}) -> gen_format(#{opname := Name, format := Args}) ->
NameAsString = atom_to_list(Name), NameAsString = atom_to_list(Name),
@@ -533,7 +599,7 @@ test_asm_generator(Filename) ->
file:close(File). file:close(File).
gen_instruction(#{opname := Name, format := atomic}) -> gen_instruction(#{opname := Name, format := []}) ->
io_lib:format(" ~s\n", [Name]); io_lib:format(" ~s\n", [Name]);
gen_instruction(#{opname := Name, format := ArgTypes}) -> gen_instruction(#{opname := Name, format := ArgTypes}) ->
Args = lists:flatten(lists:join(" ", [gen_arg(A) || A <- ArgTypes])), Args = lists:flatten(lists:join(" ", [gen_arg(A) || A <- ArgTypes])),
@@ -549,7 +615,7 @@ gen_arg(t) -> "integer".
any_arg() -> any_arg() ->
element(rand:uniform(5), {"a", stack_arg(), var_arg(), arg_arg(), imm_arg()}). element(rand:uniform(5), {"a", stack_arg(), var_arg(), arg_arg(), imm_arg()}).
stack_arg() -> "a" ++ integer_to_list(rand:uniform(255)-1). stack_arg() -> "a".
arg_arg() -> "arg" ++ integer_to_list(rand:uniform(256)-1). arg_arg() -> "arg" ++ integer_to_list(rand:uniform(256)-1).
var_arg() -> "var" ++ integer_to_list(rand:uniform(256)-1). var_arg() -> "var" ++ integer_to_list(rand:uniform(256)-1).
imm_arg() -> imm_arg() ->
@@ -640,7 +706,7 @@ gen_variant() ->
end. end.
%% TODO: add gas cost. %% TODO: add gas cost and end_bb/in_auth?
generate_documentation(Filename) -> generate_documentation(Filename) ->
{ok, File} = file:open(Filename, [write]), {ok, File} = file:open(Filename, [write]),
Instructions = lists:flatten([gen_doc(Op)++"\n" || Op <- get_ops()]), Instructions = lists:flatten([gen_doc(Op)++"\n" || Op <- get_ops()]),
@@ -655,7 +721,7 @@ generate_documentation(Filename) ->
gen_doc(#{ opname := Name gen_doc(#{ opname := Name
, opcode := OpCode , opcode := OpCode
, args := _Args , arity := _Arity
, end_bb := _EndBB , end_bb := _EndBB
, format := FateFormat , format := FateFormat
, macro := _Macro , macro := _Macro
@@ -668,7 +734,7 @@ gen_doc(#{ opname := Name
}) -> }) ->
Arguments = Arguments =
case FateFormat of case FateFormat of
atomic -> ""; [] -> "";
_ -> lists:join(" ", _ -> lists:join(" ",
[format_arg_doc(A) || [format_arg_doc(A) ||
A <- A <-
@@ -686,4 +752,3 @@ format_arg_doc({is,_N}) -> "Identifier";
format_arg_doc({ii,_N}) -> "Integer"; format_arg_doc({ii,_N}) -> "Integer";
format_arg_doc({li,_N}) -> "[Integers]"; format_arg_doc({li,_N}) -> "[Integers]";
format_arg_doc({t,_N}) -> "Type". format_arg_doc({t,_N}) -> "Type".
+3
View File
@@ -51,6 +51,7 @@ opcode(?SHL) -> ?SHL;
opcode(?SHR) -> ?SHR; opcode(?SHR) -> ?SHR;
opcode(?SAR) -> ?SAR; opcode(?SAR) -> ?SAR;
opcode(?SHA3) -> ?SHA3; opcode(?SHA3) -> ?SHA3;
opcode(?CREATOR) -> ?CREATOR;
opcode(?ADDRESS) -> ?ADDRESS; opcode(?ADDRESS) -> ?ADDRESS;
opcode(?BALANCE) -> ?BALANCE; opcode(?BALANCE) -> ?BALANCE;
opcode(?ORIGIN) -> ?ORIGIN; opcode(?ORIGIN) -> ?ORIGIN;
@@ -191,6 +192,7 @@ mnemonic(?SHL) -> 'SHL' ;
mnemonic(?SHR) -> 'SHR' ; mnemonic(?SHR) -> 'SHR' ;
mnemonic(?SAR) -> 'SAR' ; mnemonic(?SAR) -> 'SAR' ;
mnemonic(?SHA3) -> 'SHA3' ; mnemonic(?SHA3) -> 'SHA3' ;
mnemonic(?CREATOR) -> 'CREATOR' ;
mnemonic(?ADDRESS) -> 'ADDRESS' ; mnemonic(?ADDRESS) -> 'ADDRESS' ;
mnemonic(?BALANCE) -> 'BALANCE' ; mnemonic(?BALANCE) -> 'BALANCE' ;
mnemonic(?ORIGIN) -> 'ORIGIN' ; mnemonic(?ORIGIN) -> 'ORIGIN' ;
@@ -332,6 +334,7 @@ m_to_op('SHL') -> ?SHL ;
m_to_op('SHR') -> ?SHR ; m_to_op('SHR') -> ?SHR ;
m_to_op('SAR') -> ?SAR ; m_to_op('SAR') -> ?SAR ;
m_to_op('SHA3') -> ?SHA3 ; m_to_op('SHA3') -> ?SHA3 ;
m_to_op('CREATOR') -> ?CREATOR ;
m_to_op('ADDRESS') -> ?ADDRESS ; m_to_op('ADDRESS') -> ?ADDRESS ;
m_to_op('BALANCE') -> ?BALANCE ; m_to_op('BALANCE') -> ?BALANCE ;
m_to_op('ORIGIN') -> ?ORIGIN ; m_to_op('ORIGIN') -> ?ORIGIN ;
+9 -12
View File
@@ -25,12 +25,7 @@ read_file(File) ->
Asm. Asm.
assemble(Asm) -> assemble(Asm) ->
{Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, []), aeb_fate_asm:asm_to_bytecode(Asm, []).
{Env, BC}.
disassemble(BC) ->
aeb_fate_asm:bytecode_to_fate_code(BC, []).
asm_disasm_idenity_test() -> asm_disasm_idenity_test() ->
check_roundtrip(identity). check_roundtrip(identity).
@@ -52,18 +47,20 @@ sources() ->
, "tuple" , "tuple"
, "mapofmap" , "mapofmap"
, "immediates" , "immediates"
, "all_instructions" , "names"
, "oracles"
%% , "all_instructions"
]. ].
check_roundtrip(File) -> check_roundtrip(File) ->
AssemblerCode = read_file(File), AssemblerCode = read_file(File),
{_Env, ByteCode} = assemble(AssemblerCode), {_Env, ByteCode} = assemble(AssemblerCode),
FateCode = disassemble(ByteCode), FateCode = aeb_fate_code:deserialize(ByteCode),
DissasmCode = aeb_fate_asm:to_asm(FateCode), DissasmCode = aeb_fate_asm:to_asm(FateCode),
io:format("~s~n", [AssemblerCode]),
io:format("~s~n", [DissasmCode]),
{_Env2, ByteCode2} = assemble(DissasmCode), {_Env2, ByteCode2} = assemble(DissasmCode),
ByteCode3 = aeb_fate_code:serialize(FateCode),
Code1 = aeb_fate_asm:strip(ByteCode), Code1 = aeb_fate_asm:strip(ByteCode),
Code2 = aeb_fate_asm:strip(ByteCode2), Code2 = aeb_fate_asm:strip(ByteCode2),
io:format("~s~n", [aeb_fate_asm:to_asm(disassemble(ByteCode2))]), Code3 = aeb_fate_asm:strip(ByteCode3),
?assertEqual(Code1, Code2). ?assertEqual(Code1, Code2),
?assertEqual(Code1, Code3).
-1
View File
@@ -50,7 +50,6 @@ sources() ->
aeb_fate_data:make_hash(<<1,2,3,4,5>>), aeb_fate_data:make_hash(<<1,2,3,4,5>>),
aeb_fate_data:make_signature(<<1,2,3,4,5>>), aeb_fate_data:make_signature(<<1,2,3,4,5>>),
aeb_fate_data:make_contract(<<1,2,3,4,5>>), aeb_fate_data:make_contract(<<1,2,3,4,5>>),
aeb_fate_data:make_name(<<1,2,3,4,5>>),
aeb_fate_data:make_channel(<<1,2,3,4,5>>), aeb_fate_data:make_channel(<<1,2,3,4,5>>),
aeb_fate_data:make_list([True]), aeb_fate_data:make_list([True]),
aeb_fate_data:make_address( aeb_fate_data:make_address(
+63 -51
View File
@@ -1,26 +1,30 @@
;; CONTRACT all_instructions ;; CONTRACT all_instructions
;; Dont expect this contract to typecheck or run. ;; Dont expect this contract to typecheck or run.
;; Just used to check assembler rountrip of all instruction. ;; Just used to check assembler rountrip of all instructions.
FUNCTION foo () : {tuple, []} FUNCTION foo () : {tuple, []}
RETURN RETURN
RETURNR a13 RETURNR a
CALL foo CALL "foo"
CALL_R arg125 foo CALL_R arg125 foo 0
CALL_T foo CALL_T "foo"
CALL_TR arg245 foo CALL_TR arg245 foo 4711
CALL_GTR arg245 foo 0 100
CALL_GR arg245 foo 0 4711
JUMP 5514251025295783441695716053282666408426 JUMP 5514251025295783441695716053282666408426
JUMPIF arg196 0x12c651665 JUMPIF arg196 0x12c651665
SWITCH_V2 a27 63 33 SWITCH_V2 a 63 33
SWITCH_V3 var4 0x1d61723dd 79 7 SWITCH_V3 var4 0x1d61723dd 79 7
@@ -32,29 +36,29 @@ FUNCTION foo () : {tuple, []}
DUP a DUP a
POP a107 POP a
STORE arg183 var225 STORE arg183 var225
INCA INCA
INC a25 INC a
DECA DECA
DEC a DEC a
ADD a217 a a ADD a a a
SUB arg35 arg165 var74 SUB arg35 arg165 var74
MUL 44 35 "foo" MUL 44 35 "foo"
DIV 263838340369912686645632650718169038811 a24 a DIV 263838340369912686645632650718169038811 a a
MOD var113 arg80 arg207 MOD var113 arg80 arg207
POW a176 a a123 POW a a a
LT a 78 var81 LT a 78 var81
@@ -62,11 +66,11 @@ FUNCTION foo () : {tuple, []}
EQ 85 a arg164 EQ 85 a arg164
ELT a161 arg226 a168 ELT a arg226 a
EGT a131 1 var250 EGT a 1 var250
NEQ a85 a a83 NEQ a a a
AND var255 0x294a24f6 var189 AND var255 0x294a24f6 var189
@@ -74,37 +78,41 @@ FUNCTION foo () : {tuple, []}
NOT arg124 a NOT arg124 a
TUPLE 5019186157739257888756115213149493826410 TUPLE var999 5019186157739257888756115213149493826410
ELEMENT arg148 var25 a219 ELEMENT arg148 var25 a
MAP_EMPTY a135 MAP_EMPTY a
MAP_LOOKUP a82 a a143 MAP_LOOKUP a a a
MAP_LOOKUPD var112 arg35 a163 var112 MAP_LOOKUPD var112 arg35 a var112
MAP_UPDATE false a0 a56 a MAP_UPDATE false a a a
MAP_DELETE arg180 a var1 MAP_DELETE arg180 a var1
MAP_MEMBER a { true => 4} 94 MAP_MEMBER a { true => 4} 94
MAP_FROM_LIST () a159 MAP_FROM_LIST () a
MAP_TO_LIST a { true => 4 }
MAP_SIZE a { true => 42 }
NIL arg91 NIL arg91
IS_NIL a121 var6 IS_NIL a var6
CONS arg185 "foo" a114 CONS arg185 "foo" a
HD a150 var124 HD a var124
TL arg223 a TL arg223 a
LENGTH var216 a143 LENGTH var216 a
STR_EQ { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a177 APPEND { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a
STR_JOIN a a 7144184027126178769820155907121270843348 STR_JOIN a a 7144184027126178769820155907121270843348
@@ -112,15 +120,17 @@ FUNCTION foo () : {tuple, []}
ADDR_TO_STR a arg216 ADDR_TO_STR a arg216
STR_REVERSE a174 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv STR_REVERSE a @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
STR_LENGTH a "foo"
INT_TO_ADDR arg127 var207 INT_TO_ADDR arg127 var207
VARIANT a a 0x1f7b72200 a VARIANT a a 0x1f7b72200 a
VARIANT_TEST a26 arg217 a VARIANT_TEST a arg217 a
VARIANT_ELEMENT a86 arg103 arg108 VARIANT_ELEMENT a arg103 arg108
BITS_NONEA BITS_NONEA
@@ -128,36 +138,38 @@ FUNCTION foo () : {tuple, []}
BITS_ALLA BITS_ALLA
BITS_ALL a164 BITS_ALL a
BITS_ALL_N a221 arg135 BITS_ALL_N a arg135
BITS_SET arg150 a48 { 0x1a715e2a6 => 3} BITS_SET arg150 a { 0x1a715e2a6 => 3}
BITS_CLEAR arg98 a arg164 BITS_CLEAR arg98 a arg164
BITS_TEST a a242 (| [0,0,3] | 2 | (1, "foo", ()) |) BITS_TEST a a (| [0,0,3] | 2 | (1, "foo", ()) |)
BITS_SUM a244 a71 BITS_SUM a a
BITS_OR var20 var186 a BITS_OR var20 var186 a
BITS_AND a187 4 arg203 BITS_AND a 4 arg203
BITS_DIFF var200 arg247 var20 BITS_DIFF var200 arg247 var20
ADDRESS a237 ADDRESS a
BALANCE a231 BALANCE a
ORIGIN arg216 ORIGIN arg216
CALLER a27 CALLER a
GASPRICE arg119 GASPRICE arg119
BLOCKHASH a arg110 BLOCKHASH a arg110
CALL_VALUE a
BENEFICIARY var163 BENEFICIARY var163
TIMESTAMP a TIMESTAMP a
@@ -172,15 +184,15 @@ FUNCTION foo () : {tuple, []}
GAS var35 GAS var35
LOG0 a a85 LOG0 a
LOG1 arg94 arg86 arg208 LOG1 arg86 arg208
LOG2 a113 (| [0,1,3] | 2 | (1, "foo", ()) |) arg238 var108 LOG2 a a (| [0,1,3] | 2 | (1, "foo", ()) |)
LOG3 arg255 arg15 arg211 var139 arg44 LOG3 arg15 arg211 var139 arg44
LOG4 @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv a247 a 9 a38 a LOG4 a a 9 a a
DEACTIVATE DEACTIVATE
@@ -214,15 +226,11 @@ FUNCTION foo () : {tuple, []}
ECVERIFY ECVERIFY
SHA3 SHA3 a
SHA256 SHA256 a
BLAKE2B BLAKE2B a
DUMMY7ARG a a 7607708484837907159893701471377343595877 (| [2,1] | 0 | ( [], [ 45, { 1 => 3441201581501946066216994494994943246334} ] ) |) a0 var56 "foo"
DUMMY8ARG 3673679924816289365509492271980889822579 a69 arg242 var237 a175 arg106 () var255
ABORT a ABORT a
@@ -235,3 +243,7 @@ FUNCTION foo () : {tuple, []}
BALANCE_OTHER a arg0 BALANCE_OTHER a arg0
SETELEMENT a 2 (1, "two", 3) 2 SETELEMENT a 2 (1, "two", 3) 2
AUTH_TX_HASH
CONTRACT_TO_ADDRESS @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
+9
View File
@@ -68,6 +68,15 @@ FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]
FUNCTION address() : address FUNCTION address() : address
RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION oracle() : oracle
RETURNR @ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION contract() : contract
RETURNR @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION channel() : channel
RETURNR @ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
;; Option(integer) = NONE | SOME(integer) ;; Option(integer) = NONE | SOME(integer)
FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]} FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR (| [0,1] | 0 | () |) RETURNR (| [0,1] | 0 | () |)
+1 -1
View File
@@ -2,7 +2,7 @@
FUNCTION call(integer):integer FUNCTION call(integer):integer
STORE var1 arg0 STORE var1 arg0
PUSH 0 PUSH 0
CALL write CALL "write"
PUSH var1 PUSH var1
RETURN RETURN
+21
View File
@@ -0,0 +1,21 @@
;; CONTRACT names
FUNCTION preclaim(address, {bytes, 32}) : {tuple, []}
AENS_PRECLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1
RETURNR {}
FUNCTION claim(address, string, integer, integer) : {tuple, []}
AENS_CLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 arg3
RETURNR {}
FUNCTION transfer(address, address, {bytes, 32}) : {tuple, []}
AENS_TRANSFER #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2
RETURNR {}
FUNCTION revoke(address, {bytes, 32}) : {tuple, []}
AENS_REVOKE #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1
RETURNR {}
FUNCTION resolve(string, string) : {variant, [{tuple, []}, {tuple, [address]}]}
AENS_RESOLVE a arg0 arg1 'address
RETURN
+32
View File
@@ -0,0 +1,32 @@
;; CONTRACT oracles
FUNCTION register (address, integer, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : oracle
ORACLE_REGISTER a #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURN
FUNCTION query (oracle, integer, string) : oracle_query
ORACLE_QUERY a arg0 arg1 arg2 (| [1,1] | 0 | (100) |) (| [1,1] | 0 | (100) |) 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURN
FUNCTION bogus_query () : oracle_query
RETURNR @oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION respond (oracle, integer, string) : {tuple, []}
ORACLE_RESPOND #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR {}
FUNCTION extend (oracle, {variant, [{tuple, [integer]}, {tuple, [integer]}]}) : {tuple, []}
ORACLE_EXTEND #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1
RETURNR {}
FUNCTION get_question (oracle, oracle_query) : string
ORACLE_GET_QUESTION a arg0 arg1 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURN
FUNCTION get_answer (oracle, oracle_query) : {variant, [{tuple, []}, {tuple, [string]}]}
ORACLE_GET_ANSWER a arg1 arg2 'string '{variant, [{tuple, []}, {tuple, [integer]}]}
RETURN
FUNCTION query_fee (oracle) : integer
ORACLE_QUERY_FEE a arg0
RETURN
+5 -5
View File
@@ -19,27 +19,27 @@ FUNCTION inc(integer) -> integer
FUNCTION call(integer) -> integer FUNCTION call(integer) -> integer
INCA INCA
CALL inc CALL "inc"
INCA INCA
RETURN RETURN
FUNCTION tailcall(integer) -> integer FUNCTION tailcall(integer) -> integer
INCA INCA
CALL_T inc CALL_T "inc"
FUNCTION remote_call(integer) : integer FUNCTION remote_call(integer) : integer
PUSH arg0 PUSH arg0
CALL_R remote.add_five CALL_R remote.add_five 0
INCA INCA
RETURN RETURN
FUNCTION remote_tailcall(integer) : integer FUNCTION remote_tailcall(integer) : integer
PUSH arg0 PUSH arg0
CALL_TR remote add_five CALL_TR remote add_five 0
;; Test the code from the shell ;; Test the code from the shell
;; _build/default/rel/aessembler/bin/aessembler console ;; _build/default/rel/aessembler/bin/aessembler console
;; aeb_aefa:file("../../../../test/asm_code/test.fate", []). ;; aeb_aefa:file("../../../../test/asm_code/test.fate", []).
;; 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, []). ;; 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, []).
+4 -4
View File
@@ -1,13 +1,13 @@
FUNCTION make_0tuple():{tuple, []} FUNCTION make_0tuple():{tuple, []}
;; BB : 0 ;; BB : 0
TUPLE 0 TUPLE a 0
RETURN RETURN
FUNCTION make_2tuple(integer, integer):{tuple, [integer, integer]} FUNCTION make_2tuple(integer, integer):{tuple, [integer, integer]}
;; BB : 0 ;; BB : 0
PUSH arg0 PUSH arg0
PUSH arg1 PUSH arg1
TUPLE 2 TUPLE a 2
RETURN RETURN
FUNCTION make_5tuple(integer, integer, integer, integer, integer): FUNCTION make_5tuple(integer, integer, integer, integer, integer):
@@ -18,14 +18,14 @@ FUNCTION make_5tuple(integer, integer, integer, integer, integer):
PUSH arg2 PUSH arg2
PUSH arg3 PUSH arg3
PUSH arg4 PUSH arg4
TUPLE 5 TUPLE a 5
RETURN RETURN
FUNCTION element1(integer, integer): integer FUNCTION element1(integer, integer): integer
;; BB : 0 ;; BB : 0
PUSH arg0 PUSH arg0
PUSH arg1 PUSH arg1
TUPLE 2 TUPLE a 2
ELEMENT a 1 a ELEMENT a 1 a
RETURN RETURN