Compare commits

...

284 Commits

Author SHA1 Message Date
Radosław Rowicki 8269dbd71e Update rebar3 and aeserialization (#109)
* Update rebar3 and aeserialization

* Version
2022-07-27 17:22:42 +04:00
Hans Svensson 08cc0a9fcd Merge pull request #108 from aeternity/fix_doc_generation
Fix document generation script
2021-12-10 14:00:20 +01:00
Hans Svensson 84f20ab683 Fix document generation script 2021-12-10 11:53:54 +01:00
Hans Svensson 7497345928 Merge pull request #106 from aeternity/ceres_extension
Add OPCODES for bitwise operations, addr_to_byte and poseidon hash
2021-11-25 08:33:33 +01:00
Hans Svensson 822a269f75 Add OPCODES for bitwise operations, addr_to_byte and poseidon hash 2021-11-18 16:31:07 +01:00
seanhinde 0699f35b03 Merge pull request #107 from aeternity/otp-24-support
Fix OTP-24 warnings and add OTP-24 compatible rebar3
2021-11-09 16:27:23 +01:00
Sean Hinde 3829e29a63 Fix dialyzer complaint about reading core erlang 2021-11-08 17:56:57 +01:00
Sean Hinde 52e9d30f76 Fix OTP-24 warnings and add OTP-24 compatible rebar3 2021-11-08 17:35:17 +01:00
Hans Svensson da7f00ae5d Merge pull request #105 from aeternity/doc-gen
Ease the process of doc generation
2021-11-04 08:45:26 +01:00
radrow 326fca709f Add opcodes gen 2021-06-08 13:33:33 +02:00
radrow e860e217a0 Better clone&create docs 2021-06-08 13:09:39 +02:00
radrow bc48b5d62f Styling in doc gen 2021-06-08 11:43:36 +02:00
radrow 7b9c1b856b Upgrade doc generation 2021-06-08 11:26:18 +02:00
Hans Svensson 05dfd7ffc7 Merge pull request #104 from aeternity/iris_gas_prices_adjustment
BYTECODE_HASH should be cheaper
2021-04-30 12:16:57 +02:00
Hans Svensson 7d4f3fed10 BYTECODE_HASH should be cheaper 2021-04-30 12:05:17 +02:00
Hans Svensson 4c0906398e Merge pull request #103 from aeternity/iris_gas_prices_adjustment
Revert CALL_R/GR/PGR gas changes
2021-04-30 09:45:18 +02:00
Hans Svensson 1bb24a485b Revert CALL_R/GR/PGR gas changes 2021-04-30 09:14:03 +02:00
Hans Svensson abd12b599f Merge pull request #102 from aeternity/iris_gas_prices_adjustment
Adjust FATE OPs gas price for Iris
2021-04-29 20:31:58 +02:00
Hans Svensson e4d46bd17a Adjust FATE OPs gas price for Iris 2021-04-29 15:16:48 +02:00
Hans Svensson 142502179c Merge pull request #101 from aeternity/protocol_bound_gas_price
Add mechanism for pricing FATE ops differently in IRIS
2021-04-28 15:10:43 +02:00
Hans Svensson 0318c60caf Add mechanism for pricing FATE ops differently in IRIS 2021-04-28 13:18:56 +02:00
Radosław Rowicki eb8810a783 Merge pull request #100 from aeternity/call-fee
Call fee
2021-04-27 09:50:31 +02:00
radrow 69110b2ee8 Rename TX_FEE to just FEE 2021-04-26 15:19:59 +02:00
radrow b0263319e3 Add TX_FEE 2021-04-26 14:23:20 +02:00
Artur Puzio 951db9f384 Merge pull request #99 from aeternity/revert-string-compare
Restore old binary and string comparison
2021-04-14 11:25:35 +00:00
Artur Puzio fe11ee31ab Restore old binary and string comparison
Binary and string comparison is under consensus

This partially reverts commit d16f9a9579.
2021-04-14 13:07:24 +02:00
Artur Puzio 0c010cfd0d Merge pull request #98 from aeternity/bring-sanity-to-compare
Bring sanity to compare
2021-04-07 13:27:01 +00:00
Artur Puzio d16f9a9579 Compare binaries and strings in standard manner
By first different byte, if one is prefix of another than prefix is
smaller
2021-04-07 11:31:02 +02:00
Artur Puzio f53f61e554 Comparison cleanup
- more verbose comparison function
- adjusted bytes and bytecode comparison for consistency
- fixed oracle query type
2021-04-07 11:30:53 +02:00
Radosław Rowicki 84e246d9da Merge pull request #94 from aeternity/factories
Add CREATE, CLONE and BYTECODE_HASH opcodes. Add bytecode typerep and datatype
2021-03-31 09:45:57 +02:00
radrow 1498e1cdc2 Remove CREATE_G 2021-03-24 14:41:44 +01:00
radrow 41b80b5d44 Gas versions 2021-03-24 14:22:09 +01:00
radrow fd9f15789c Fix test 2021-03-24 13:13:05 +01:00
radrow 8d447e803b CREATE and CLONE now push to the stack 2021-03-23 20:37:36 +01:00
radrow 83616392e1 Add CREATE, CLONE and BYTECODE_HASH opcodes. Add bytecode typerep and datatype.
Format fixes. Changed type of BLOCKHASH to variant.

Fixed number of parameters to include target

Changed op args

Protected create

Make new type store a SERIALIZED CODE (instead of RAW BYTECODE)

Fix test

Format

Make create not protected

format

Fix serialization of fate_code type

Align

Add rebar3

Use shipped rebar3

Fix serialization, test

Fix tests

Rename fate_code to contract_bytearray

Update README
2021-03-23 12:27:35 +01:00
Artur Puzio 135a27c992 Merge pull request #95 from aeternity/precalculate-init-id
Avoid unnecessery calls to eblake2
2021-03-23 08:57:11 +00:00
Artur Puzio b30f8a2bfd Avoid unnecessery calls to eblake2
We frequently need identifier of init function. Store it precalculated
in headers.
2021-03-23 09:27:25 +01:00
Artur Puzio 9d81358daf Merge pull request #96 from aeternity/rebar
Use aeternity rebar
2021-03-23 08:24:40 +00:00
radrow 5493bece71 Include rebar 2021-03-22 17:28:21 +01:00
radrow c90bb0c6db Use aeternity rebar 2021-03-22 17:27:42 +01:00
Hans Svensson 7f0d3090d4 Merge pull request #93 from aeternity/GH-203-protected-calls
Add protected call FATE instruction
2020-03-02 12:07:50 +01:00
Ulf Norell e5164330b0 Get valid opcodes from instruction list 2020-03-02 08:48:29 +01:00
Ulf Norell e5bdf583f9 Add protected remote call instruction 2020-03-02 08:40:04 +01:00
Ulf Norell 719bf26a38 Set store map size theshold to 0 ifdef TEST 2020-03-02 08:37:30 +01:00
Hans Svensson 5ef5d455b4 Merge pull request #92 from aeternity/aesophia/GH-176-more_string_functions
Char.to_upper/lower isn't feasible do String.to_upper/lower
2020-02-26 18:22:03 +01:00
Hans Svensson 36559842ae Char.to_upper/lower isn't feasible do String.to_upper/lower 2020-02-26 14:06:50 +01:00
Hans Svensson 94d0b984e9 Merge pull request #91 from aeternity/aesophia/GH-176-more_string_functions
Aesophia/gh 176 more string functions
2020-02-25 09:01:17 +01:00
Hans Svensson 167bfcc0e6 Add String.to/from_list Char.to/from_int and Char.to_upper/lower as operations 2020-02-24 10:09:23 +01:00
Hans Svensson 2f11143e5a Align operators (missing spaces) 2020-02-24 10:08:48 +01:00
Hans Svensson ff5a4c7dd5 Merge pull request #90 from aeternity/GH-202-Auth_tx_introspection
Add AUTH_TX to FATE operations
2020-02-11 14:18:39 +01:00
Hans Svensson 370c7387a9 Add AUTH_TX to FATE operations 2020-02-11 09:34:48 +01:00
Hans Svensson 8a9c9dec95 Merge pull request #89 from aeternity/GH-188-AENS_lookup
Add AENS_LOOKUP and ORACLE_EXPIRY as FATE operations
2020-02-03 14:24:44 +01:00
Hans Svensson 4565188c6a Add AENS_LOOKUP and ORACLE_EXPIRY as FATE operations 2020-02-03 13:19:08 +01:00
Hans Svensson 059d114a06 Merge pull request #88 from aeternity/GH-2850-add_fancy_crypto_bls12_381
Add FATE-ops for BLS12-381
2019-11-29 15:27:13 +01:00
Hans Svensson e4b09d7c5c Add FATE-ops for BLS12-381 2019-11-27 09:06:20 +01:00
skkw b040dccdef Merge pull request #87 from aeternity/PT-158904718-aens-update-sophia
fixed documenting of AENS_UPDATE type
2019-11-18 16:20:16 +01:00
skkw 211ee74df2 fixed documenting of AENS_UPDATE type 2019-11-18 16:00:36 +01:00
skkw 38f4f0ecd7 Merge pull request #81 from aeternity/PT-158904718-aens-update-sophia
add support for AENS.update call
2019-11-12 14:17:11 +01:00
Hans Svensson 281e9b210c Merge pull request #86 from aeternity/GH-2942-bytes_is_also_a_type
TYPE_BYTES was missing in IS_TYPE_TAG
2019-10-25 11:39:33 +02:00
Hans Svensson 3e7a8e4a69 TYPE_BYTES was missing in IS_TYPE_TAG 2019-10-25 11:30:47 +02:00
Ulf Norell ea5548be6c Merge pull request #84 from aeternity/store-gas
Store gas
2019-10-01 13:11:39 +02:00
Ulf Norell 48cfbd03b0 Recalibrate the store map threshold
Now that we charge gas for store writes the math is changed.
2019-10-01 10:52:54 +02:00
Ulf Norell 04878c5ed9 Add function to compute the byte size of a heap_value
Used to compute store gas cost in AEVM
2019-10-01 10:52:09 +02:00
Ulf Norell 4f4d6d30cd Merge pull request #83 from aeternity/address-to-contract-instruction
Add ADDRESS_TO_CONTRACT
2019-09-30 13:50:22 +02:00
Ulf Norell 176df87bb0 Add ADDRESS_TO_CONTRACT 2019-09-30 13:14:11 +02:00
Ulf Norell 2239bfb6f6 Merge pull request #82 from aeternity/fate-gas-cost-tweaks
Bump gas cost for non-trivial instructions
2019-09-30 12:27:55 +02:00
Ulf Norell 872766260b Bump gas cost for non-trivial instructions 2019-09-30 11:39:38 +02:00
skkw f184abeb87 add support for AENS.update call 2019-09-10 15:46:04 +02:00
Ulf Norell a66dc0a97f Merge pull request #80 from aeternity/bytes_concat
Add FATE instructions for Bytes.concat and Bytes.split
2019-09-09 19:08:06 +02:00
Ulf Norell 17c9656f5c Add FATE instructions for Bytes.concat and Bytes.split 2019-09-09 14:45:23 +02:00
Thomas Arts 3106ca1306 Merge pull request #79 from aeternity/fix-opcodees-in-QuickCheck-tests
New opcodes after renumbering
2019-09-06 15:00:16 +02:00
Thomas Arts 3565719c7a New opcodes after renumbering 2019-09-06 14:48:37 +02:00
Erik Stenman b036531dc2 Renumber ops and set base gas. (#78) 2019-09-06 14:13:51 +02:00
Tobias Lindahl 7f0593fbf2 Merge pull request #77 from aeternity/PT-168310969-expose-gas-costs
Make api function for exposing gas costs of fate ops
2019-09-05 16:20:45 +02:00
Hans Svensson a533fd5fcb Merge pull request #76 from aeternity/dont_crash_on_deserialize
Don't crash in decode_calldata for FATE
2019-09-05 14:26:00 +02:00
Hans Svensson 02a3462cf4 Don't crash in decode_calldata for FATE 2019-09-05 14:12:13 +02:00
Tobias Lindahl 69912db2b6 Make api function for exposing gas costs of ops 2019-09-05 13:27:33 +02:00
Hans Svensson 5e16b85ae2 Merge pull request #75 from aeternity/PT-168256282-disallow_calls_to_init
Implement aeb_fate_code:strip_init_function/1
2019-09-03 13:12:59 +02:00
Hans Svensson d272e821b2 Implement aeb_fate_code:strip_init_function/1 2019-09-03 11:00:32 +02:00
Thomas Arts 72b2a581d5 Merge pull request #73 from aeternity/aens-at-full-node-ver-ta
Add argument to claim tx to enable bidding
2019-09-02 08:51:09 +02:00
Thomas Arts 72d61471e0 Depend on newer aeserialization 2019-09-02 08:47:21 +02:00
sennui e21abb875e Add argument to claim tx to enable bidding 2019-08-28 11:02:21 +02:00
Ulf Norell e7f2be7ce8 Merge pull request #74 from aeternity/fate-map-fixes
Fate map fixes
2019-08-27 10:55:37 +02:00
Ulf Norell c6475fe1c2 Fix typo 2019-08-26 11:57:21 +02:00
Ulf Norell 4e4c20c387 Remember to unfold store map caches too! 2019-08-26 09:06:43 +02:00
Thomas Arts 1d5e5be252 Merge pull request #72 from aeternity/PT-166330348-check-map-keys-fate
Pt 166330348 check map keys fate
2019-08-26 08:37:52 +02:00
Ulf Norell 6efa4a0cb8 Merge pull request #71 from aeternity/missing-case-in-allocate-maps
Add missing case for map tombstones
2019-08-23 17:13:24 +02:00
Thomas Arts 59b7b786ac Update quickcheck/aefate_code_eqc.erl
Co-Authored-By: Hans Svensson <hanssv@gmail.com>
2019-08-23 15:50:39 +02:00
Thomas Arts f31887c2ed Update properties 2019-08-23 15:00:12 +02:00
Thomas Arts d794566363 Fix check for no maps in keys 2019-08-23 14:45:15 +02:00
Ulf Norell 850a5e2c35 Add missing case for map tombstones 2019-08-23 14:30:07 +02:00
Thomas Arts c270c794c3 Fix tests to new datastructure containing attributes 2019-08-23 11:31:54 +02:00
Hans Svensson 10cc127883 Merge pull request #70 from aeternity/PT-167996886-a_proper_ecverify
PT-167996886 a proper ecverify
2019-08-21 11:07:59 +02:00
Hans Svensson 50df849709 VERIFY_SIG_SECP256K1 was too long for code generation 2019-08-21 09:26:01 +02:00
Hans Svensson dfa9b80a3c Change ECVERIFY to VERIFY_SIG and add proper ECVERIFY 2019-08-21 09:25:28 +02:00
Hans Svensson befa1e3ff9 Merge pull request #69 from aeternity/PT-162578406-payable_modifier
PT-162578406 Add payable modifier
2019-08-19 08:54:31 +02:00
Hans Svensson efb4afeafa Add IS_PAYABLE opcode 2019-08-16 09:31:27 +02:00
Hans Svensson e75336486e Track payable (and private) in FATE/AEVM type info
Privateness is only tracked for FATE.
2019-08-16 09:31:27 +02:00
Ulf Norell fdd660a219 Merge pull request #67 from aeternity/PT-167221635-remote-type-check
PT-167221635 remote type check
2019-08-16 09:10:47 +02:00
Tino Breddin 3954bd22da Merge pull request #64 from aeternity/newby/ecrecover
[PT-167805291] Add opcode for ecrecover
2019-08-14 16:08:45 +02:00
Ulf Norell 13211887a3 Update src/aeb_fate_generate_ops.erl
Co-Authored-By: Hans Svensson <hanssv@gmail.com>
2019-08-14 09:32:23 +02:00
Ulf Norell 834ab298d1 typereps are values 2019-08-14 09:27:51 +02:00
Ulf Norell 52781060b2 fix type spec 2019-08-14 09:01:53 +02:00
Ulf Norell 3721fde7e8 Add typereps to remote call instructions 2019-08-14 09:01:53 +02:00
Tino Breddin 23ee7e0ca4 Add missing crypto entries to all_instructions 2019-08-13 16:24:17 +02:00
Ulf Norell af6224cb3b Merge pull request #66 from aeternity/PT-166788647-fate-efficient-maps
PT-166788647 FATE efficient maps
2019-08-13 15:51:13 +02:00
Tino Breddin 197dfd5da1 Let ecrecover only require two parameters 2019-08-13 15:15:03 +02:00
Ulf Norell 087ec31698 Fix bad type spec 2019-08-13 13:17:41 +02:00
Ulf Norell c49140fd5d Fix type and some code cleanup 2019-08-13 11:33:21 +02:00
Ulf Norell 414c45fbf7 Export refcount type 2019-08-13 11:25:54 +02:00
Ulf Norell f5a9be67d9 Remove CALL_TR and CALL_GTR 2019-08-13 09:20:29 +02:00
Ulf Norell 15095a74ab Add has_store_maps function 2019-08-12 14:40:34 +02:00
Ulf Norell 5aee70b8ff Add arity to CALL_R and CALL_GR
and deprecate CALL_TR and CALL_GTR
2019-08-12 14:38:10 +02:00
John Newby 44ec31d958 fixed ordering 2019-08-12 13:52:27 +02:00
John Newby 8fde1e5e24 Added FATE opcode for ecrecover 2019-08-12 13:36:49 +02:00
Ulf Norell 54dcf364e5 Store map reference counting 2019-08-12 10:59:30 +02:00
johnsnewby 7c6a80fef7 Update include/aeb_opcodes.hrl
Co-Authored-By: Hans Svensson <hanssv@gmail.com>
2019-08-12 10:52:47 +02:00
John Newby c0bc71b0b7 Added opcode for ecrecover 2019-08-09 16:32:00 +02:00
Ulf Norell c30bfd7b1c Unfolding store maps 2019-08-09 12:52:31 +02:00
Ulf Norell e184028261 Code for allocating store maps 2019-08-08 12:42:03 +02:00
Ulf Norell 986a7e6734 Add a store map fate value.
A store map is a (contract local) unique id pointing to a map saved in the contract store, plus a cache of updates.
2019-08-08 12:41:42 +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
Thomas Arts 163e805f55 Enable running properties as Eunit tests
Update model to address serialization

Update eunit test wrapper

Update tests

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

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

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

* Test lists of length 16.

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

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

* Add ops to test 7 and 8 arguments.

* Make sure rebar builds sources before trying to build.

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

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

* Format Readme.

* Format Readme step 2.

* Format Readme step 3.

* Format Readme step 4.

* Format Readme step 5.

* Update src/aeb_fate_asm.erl

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

* Update README.md

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

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

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

* Test targets and cleanup.

* Spell eunit the right way.

* Use test target for ci.

* Renumber opcodes. Add primops.

* Generate tokens in scanner from definitions.

* Rename NUMBER op to GENERATION and add MICROBLOCK instruction.

* Since Tag < Size, Size cannot be zero

* unit is printed `()`

* Formatting differently

* Add eqc profile

* Generate code for fate ops from spec.

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

* Test targets and cleanup.

* Generate op pretty printer.

* Removed unused function.

* Polish Makefile file references (#11)

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

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

* Test targets and cleanup.

* Spell eunit the right way.

* Use test target for ci.

* Renumber opcodes. Add primops.

* Generate tokens in scanner from definitions.

* Rename NUMBER op to GENERATION and add MICROBLOCK instruction.
2019-02-28 11:24:13 +01:00
46 changed files with 4364 additions and 1925 deletions
+3 -3
View File
@@ -22,13 +22,13 @@ jobs:
command: rebar3 compile
- run:
name: Static Analysis
command: rebar3 dialyzer
command: make dialyzer
- run:
name: Eunit
command: rebar3 eunit
command: make eunit
- run:
name: Common Tests
command: rebar3 ct
command: make test
- save_cache:
key: dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }}
paths:
+14
View File
@@ -10,5 +10,19 @@ rel/example_project
.rebar
aeb_asm_scan.erl
aeb_fate_asm_scan.erl
aeb_fate_asm_scan.xrl
_build/
aefateasm
include/aeb_fate_opcodes.hrl
src/aeb_fate_opcodes.erl
src/aeb_fate_ops.erl
src/aeb_fate_pp.erl
*.erl~
*.hrl~
*.aes~
doc
cover
aefate
current_counterexample.eqc
.rebar3
ebin
+21 -5
View File
@@ -1,20 +1,36 @@
REBAR ?= rebar3
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
REBAR ?= ./rebar3
all: local
local:
sources: $(GENERATED_SRC)
local: $(GENERATED_SRC)
@$(REBAR) as local release
console:
console: local
@$(REBAR) as local shell
clean:
@$(REBAR) clean
rm -f $(GENERATED_SRC)
rm -f ebin/*
dialyzer: local
@$(REBAR) as local dialyzer
distclean: clean
@rm -rf _build/
eunit: local
@$(REBAR) as local eunit
test: local
@$(REBAR) as local eunit
ebin/%.beam: src/%.erl
erlc +debug_info -o $(dir $@) $<
$(GENERATED_SRC): $(GENERATOR_DEPS)
erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/
+132 -6
View File
@@ -1,9 +1,135 @@
aebytecode
=====
# aebytecode
A library and stand alone assembler for aeternity bytecode.
An OTP library
This version supports AEVM bytecode and FATE bytecode.
Build
-----
## Build
$ make
## Fate Code
Fate code exists in 3 formats:
1. Fate byte code. This format is under consensus.
2. Fate assembler. This is a text represenation of fate code.
This is not under consensus and other
implemenation and toolchains could have
their own format.
3. Internal. This is an Erlang representation of fate code
Used by this particular engin implementation.
This library handles all three representations.
The byte code format is described in a separate document.
The internal format is described in a separate document.
The text representation is described below.
### Fate Assembler Code
Assembler code can be read from a file.
The assembler has the following format:
Comments start with 2 semicolons and runs till end of line
`;; This is a comment`
Opcode mnemonics start with an upper case letter.
`DUP`
Identifiers start with a lower case letter
`an_identifier`
References to function arguments start with arg followed by an integer
`arg0`
References to variables/registers start with var followed by an integer
`var0`
References to stack postions is either a (for stack 0)
or start with stack followed by an integer
`stack1`
`a`
Immediate values can be of 10 types:
1. Integers as decimals: {Digits} or -{Digits}
`42`
`-2374683271468723648732648736498712634876147`
And integers as Hexadecimals:: 0x{Hexdigits}
`0x0deadbeef0`
2. Chain Objects. These are all addresses to different types of chain objects.
Each address is a 256 bits number encoded in base58 with checksum
with a prefix of "@" plus a type prefix followed by "_".
2a. Account Address: a base58c encoded number starting with @ak_ followed by a number of base58chars
'@ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2b. Contract address: @ct_{base58char}+
`@ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2c. Oracle address: @ok_{base58char}+
`@ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2d. Oracle query: @oq_{base58char}+
`@oq_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
2e. Channel address: @ch_{base58char}+
`@ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv`
3. Boolean true or false
`true`
`false`
4. Strings "{Characters}"
`"Hello"`
5. Map { Key => Value }
`{}`
`{ 1 => { "foo" => true, "bar" => false}`
6. Lists [ Elements ]
`[]`
`[1, 2]`
7. Bit field < Bits > or !< Bits >
`<000>`
`<1010 1010>`
`<>`
`!<>`
8. Tuples ( Elements )
`()`
`(1, "foo")`
9. Variants: (| [Arities] | Tag | ( Elements ) |)
`(| [1,3,5,2] | 3 | ( "foo", 12) |)`
10. Bytes: #{base64char}+
`#AQIDCioLFQ==`
11. Contract bytearray (code of another smart contract)
`@cb_+PJGA6A4Fz4T2LHV5knITCldR3rqO7HrXO2zhOAR9JWNbhf8Q8C4xbhx/gx8JckANwAXfQBVACAAAP4vhlvZADcABwECgv5E1kQfADcBBzcACwAWMBReAHMAFjBvJFMAFjBvggOoFAAUABQSggABAz/+tIwWhAA3AAdTAAD+1jB5kAQ3AAcLAAD+6MRetgA3AQc3ABoGggABAz+4TS8GEQx8JclFY2FsbGVyX2lzX2NyZWF0b3IRL4Zb2Q1nZXQRRNZEHxFpbml0EbSMFoQdYmFsYW5jZRHWMHmQFXZhbHVlEejEXrYNc2V0gi8AhTQuMy4wAUqQ8s4=`
Where
Digits: [0123456789]
Hexdigits: [0123456789abcdef]
base58char: [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
base64char: [ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy0123456789+/=]
Characters: any printable ascii character 0..255 (except " no quoting yet)
Key: any value except for a map
Bits: 01 or space
Elements: Nothing or Value , Elements
Size: Digits (0 < Size < 256)
Tag: Digits (0 =< Tag < Size)
$ rebar3 compile
View File
+69 -25
View File
@@ -1,55 +1,99 @@
-define(FATE_INTEGER_T, integer()).
-define(FATE_BYTE_T, 0..255).
-define(FATE_BOOLEAN_T, true | false).
-define(FATE_NIL_T, []).
-define(FATE_LIST_T, list()).
-define(FATE_UNIT_T, {tuple, {}}).
-define(FATE_MAP_T, #{ fate_type() => fate_type() }).
-define(FATE_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_VARIANT_T, {variant, ?FATE_BYTE_T, ?FATE_BYTE_T, tuple()}).
-define(FATE_VOID_T, void).
-define(FATE_TUPLE_T, {tuple, tuple()}).
-define(FATE_BITS_T, {bits, integer()}).
-define(FATE_INTEGER_T, integer()).
-define(FATE_BYTE_T, 0..255).
-define(FATE_BOOLEAN_T, true | false).
-define(FATE_NIL_T, []).
-define(FATE_LIST_T, list()).
-define(FATE_UNIT_T, {tuple, {}}).
-define(FATE_MAP_T, #{ fate_type() => fate_type() }).
-define(FATE_STORE_MAP_T, {store_map, #{ fate_type() => fate_type() | ?FATE_MAP_TOMBSTONE }, integer()}).
-define(FATE_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_BYTES_T(N), {bytes, binary()}).
-define(FATE_CONTRACT_T, {contract, <<_:256>>}).
-define(FATE_ORACLE_T, {oracle, <<_:256>>}).
-define(FATE_ORACLE_Q_T, {oracle_query, <<_:256>>}).
-define(FATE_CHANNEL_T, {channel, <<_:256>>}).
-define(FATE_VARIANT_T, {variant, [byte()], ?FATE_BYTE_T, tuple()}).
-define(FATE_VOID_T, void).
-define(FATE_TUPLE_T, {tuple, tuple()}).
-define(FATE_BITS_T, {bits, integer()}).
-define(FATE_TYPEREP_T, {typerep, fate_type_type()}).
-define(FATE_CONTRACT_BYTEARRAY_T, {contract_bytearray, binary()}).
-define(IS_FATE_INTEGER(X), is_integer(X)).
-define(IS_FATE_LIST(X), (is_list(X))).
-define(IS_FATE_STRING(X), (is_binary(X))).
-define(IS_FATE_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_INTEGER(X), (is_integer(X))).
-define(IS_FATE_LIST(X), (is_list(X))).
-define(IS_FATE_STRING(X), (is_binary(X))).
-define(IS_FATE_STORE_MAP(X), (is_tuple(X) andalso tuple_size(X) == 3
andalso store_map == element(1, X)
andalso is_map(element(2, X))
andalso is_integer(element(3, X)))).
-define(IS_FATE_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))).
-define(IS_FATE_BYTES(X), (is_tuple(X) andalso (bytes == 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_ORACLE(X), (is_tuple(X) andalso (oracle == 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_BITS(X), (is_tuple(X) andalso (bits == element(1, X) andalso is_integer(element(2, X))))).
-define(IS_FATE_VARIANT(X), (is_tuple(X)
andalso
(variant == element(1, X)
andalso is_integer(element(2, X))
andalso is_list(element(2, X))
andalso is_integer(element(3, X))
andalso is_tuple(element(4, X))
))).
-define(IS_FATE_BOOLEAN(X), is_boolean(X)).
-define(IS_FATE_TYPEREP(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= typerep)).
-define(IS_FATE_CONTRACT_BYTEARRAY(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= contract_bytearray
andalso is_binary(element(2, X)))).
-define(FATE_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}).
-define(FATE_ADDRESS(A), {address, A}).
-define(FATE_BITS(B), {bits, B}).
-define(FATE_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}).
-define(FATE_ADDRESS(A), {address, A}).
-define(FATE_BYTES(X), {bytes, X}).
-define(FATE_CONTRACT(X), {contract, X}).
-define(FATE_ORACLE(X), {oracle, X}).
-define(FATE_ORACLE_Q(X), {oracle_query, X}).
-define(FATE_CHANNEL(X), {channel, X}).
-define(FATE_BITS(B), {bits, B}).
-define(FATE_TYPEREP(T), {typerep, T}).
-define(FATE_STORE_MAP(Cache, Id), {store_map, Cache, Id}).
-define(FATE_MAP_TOMBSTONE, '__DELETED__').
-define(FATE_INTEGER_VALUE(X), (X)).
-define(FATE_BOOLEAN_VALUE(X), (X)).
-define(FATE_LIST_VALUE(X), (X)).
-define(FATE_TUPLE_ELEMENTS(X), (tuple_to_list(element(2, X)))).
-define(FATE_STRING_VALUE(X), (X)).
-define(FATE_ADDRESS_VALUE(X), (element(2, X))).
-define(FATE_BYTES_VALUE(X), (element(2, X))).
-define(FATE_CONTRACT_VALUE(X), (element(2, X))).
-define(FATE_ORACLE_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_STORE_MAP_CACHE(X), (element(2, X))).
-define(FATE_STORE_MAP_ID(X), (element(3, X))).
-define(FATE_MAP_SIZE(X), (map_size(X))).
-define(FATE_STRING_SIZE(X), (byte_size(X))).
-define(FATE_CONTRACT_BYTEARRAY_SIZE(X), (byte_size(X))).
-define(FATE_TRUE, true).
-define(FATE_FALSE, false).
-define(FATE_NIL, []).
-define(FATE_VOID, void).
-define(FATE_EMPTY_STRING, <<>>).
-define(FATE_STRING(S), S).
-define(FATE_VARIANT(Size, Tag,T), {variant, Size, Tag, T}).
-define(FATE_VARIANT(Arity, Tag,T), {variant, Arity, Tag, T}).
-define(FATE_CONTRACT_BYTEARRAY(B), {contract_bytearray, B}).
% Result of aeb_fate_code:symbol_identifier(<<"init">>).
% Stored here to avoid repeated calls to eblake2
-define(FATE_INIT_ID, <<68,214,68,31>>).
-define(MAKE_FATE_INTEGER(X), X).
-define(MAKE_FATE_LIST(X), X).
-define(MAKE_FATE_MAP(X), X).
-define(MAKE_FATE_STRING(X), X).
-define(MAKE_FATE_CONTRACT_BYTEARRAY(X), {contract_bytearray, X}).
-121
View File
@@ -1,121 +0,0 @@
%% FATE opcodes
-define('NOP' , 16#00).
-define('RETURN' , 16#01).
-define('CALL' , 16#02).
-define('CALL_R' , 16#03).
-define('CALL_T' , 16#04).
-define('CALL_TR' , 16#05).
-define('JUMP' , 16#06).
-define('JUMPIF' , 16#07).
-define('SWITCH' , 16#08).
-define('PUSH' , 16#09).
-define('DUP' , 16#0a).
-define('POP' , 16#0b).
-define('STORE' , 16#10).
-define('ADD' , 16#11).
-define('MUL' , 16#12).
-define('SUB' , 16#13).
-define('DIV' , 16#14).
-define('MOD' , 16#15).
-define('POW' , 16#16).
-define('LT' , 16#17).
-define('GT' , 16#18).
-define('EQ' , 16#19).
-define('ELT' , 16#1a).
-define('EGT' , 16#1b).
-define('NEQ' , 16#1c).
-define('AND' , 16#1d).
-define('OR' , 16#1e).
-define('NOT' , 16#1f).
-define('TUPLE' , 16#20).
-define('ELEMENT' , 16#21).
-define('MAP_EMPTY' , 16#22).
-define('MAP_LOOKUP' , 16#23).
-define('MAP_UPDATE' , 16#24).
-define('MAP_DELETE' , 16#25).
-define('MAP_MEMBER' , 16#26).
-define('MAP_FROM_LIST' , 16#27).
-define('NIL' , 16#28).
-define('IS_NIL' , 16#29).
-define('CONS' , 16#2a).
-define('HD' , 16#2b).
-define('TL' , 16#2c).
-define('LENGTH' , 16#2d).
-define('STR_EQ' , 16#2e).
-define('STR_JOIN' , 16#2f).
-define('ADDR_TO_STR' , 16#30).
-define('STR_REVERSE' , 16#31).
-define('INT_TO_ADDR' , 16#32).
-define('VARIANT' , 16#33).
-define('VARIANT_TEST' , 16#34).
-define('VARIANT_ELEMENT', 16#35).
-define('BITS_NONE' , 16#36).
-define('BITS_ALL' , 16#37).
-define('BITS_SET' , 16#38).
-define('BITS_CLEAR' , 16#39).
-define('BITS_TEST' , 16#3a).
-define('BITS_SUM' , 16#3b).
-define('BITS_OR' , 16#3c).
-define('BITS_AND' , 16#3d).
-define('BITS_DIFF' , 16#3e).
-define('ADDRESS' , 16#3f).
-define('BALANCE' , 16#40).
-define('ORIGIN' , 16#41).
-define('CALLER' , 16#42).
-define('GASPRICE' , 16#43).
-define('BLOCKHASH' , 16#44).
-define('BENEFICIARY' , 16#45).
-define('TIMESTAMP' , 16#46).
-define('NUMBER' , 16#47).
-define('DIFFICULTY' , 16#48).
-define('GASLIMIT' , 16#49).
-define('GAS' , 16#4a).
-define('LOG0' , 16#4b).
-define('LOG1' , 16#4c).
-define('LOG2' , 16#4d).
-define('LOG3' , 16#4e).
-define('LOG4' , 16#4f).
-define('ABORT' , 16#50).
-define('EXIT' , 16#51).
-define('DEACTIVATE' , 16#52).
-define('INC' , 16#53).
-define('DEC' , 16#54).
-define('INT_TO_STR' , 16#55).
-define('SPEND' , 16#56).
-define('ORACLE_REGISTER', 16#57).
-define('ORACLE_QUERY' , 16#58).
-define('ORACLE_RESPOND' , 16#59).
-define('ORACLE_EXTEND' , 16#5a).
-define('ORACLE_GET_ANSWER', 16#5b).
-define('ORACLE_GET_QUESTION', 16#5c).
-define('ORACLE_QUERY_FEE', 16#5d).
-define('AENS_RESOLVE' , 16#5e).
-define('AENS_PRECLAIM' , 16#5f).
-define('AENS_CLAIM' , 16#60).
-define('AENS_UPDATE' , 16#61).
-define('AENS_TRANSFER' , 16#62).
-define('AENS_REVOKE' , 16#63).
-define('ECVERIFY' , 16#64).
-define('SHA3' , 16#65).
-define('SHA256' , 16#66).
-define('BLAKE2B' , 16#67).
-define('RETURNR' , 16#68).
-define('MAP_LOOKUPD' , 16#69).
-define('SWITCH_V2' , 16#6a).
-define('SWITCH_V3' , 16#6b).
-define('SWITCH_VN' , 16#6c).
-define('BITS_ALL_N' , 16#6d).
-define('BITS_NONEA' , 16#6e).
-define('BITS_ALLA' , 16#6f).
-define('DUPA' , 16#70).
-define('INCA' , 16#71).
-define('DECA' , 16#72).
-define('POPA' , 16#73).
-define('FUNCTION' , 16#fe).
-define('EXTEND' , 16#ff).
-define( COMMENT(X), {comment, X}).
+15
View File
@@ -0,0 +1,15 @@
-record(pmap, {key_t :: aeb_aevm_data:type(),
val_t :: aeb_aevm_data:type(),
parent :: none | non_neg_integer(),
size = 0 :: non_neg_integer(),
data :: #{aeb_heap:binary_value() => aeb_heap:binary_value() | tombstone}
| stored}).
-record(maps, { maps = #{} :: #{ non_neg_integer() => #pmap{} }
, next_id = 0 :: non_neg_integer() }).
-record(heap, { maps :: #maps{},
offset :: aeb_heap:offset(),
heap :: binary() | #{non_neg_integer() => non_neg_integer()} }).
+20 -6
View File
@@ -30,6 +30,7 @@
-define( 'SHA3', 16#20).
-define( 'CREATOR', 16#2f).
-define( 'ADDRESS', 16#30).
-define( 'BALANCE', 16#31).
-define( 'ORIGIN', 16#32).
@@ -165,6 +166,8 @@
-define(PRIM_CALL_ORACLE_GET_ANSWER, 104).
-define(PRIM_CALL_ORACLE_GET_QUESTION, 105).
-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_AENS_RESOLVE, 200).
@@ -183,9 +186,20 @@
-define(PRIM_CALL_MAP_TOLIST, 305).
-define(PRIM_CALL_IN_CRYPTO_RANGE(__TTYPE__), (((__TTYPE__) > 399) andalso ((__TTYPE__) < 500))).
-define(PRIM_CALL_CRYPTO_ECVERIFY, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-define(PRIM_CALL_CRYPTO_VERIFY_SIG, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-define(PRIM_CALL_CRYPTO_VERIFY_SIG_SECP256K1, 410).
-define(PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, 420).
-define(PRIM_CALL_CRYPTO_ECRECOVER_SECP256K1, 421).
-define(PRIM_CALL_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))).
-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).
-define(PRIM_CALL_ADDR_IS_PAYABLE, 610).
+12
View File
@@ -0,0 +1,12 @@
-define(Type(), aeb_aevm_data:type()).
-define(TYPEREP_WORD_TAG, 0).
-define(TYPEREP_STRING_TAG, 1).
-define(TYPEREP_LIST_TAG, 2).
-define(TYPEREP_TUPLE_TAG, 3).
-define(TYPEREP_VARIANT_TAG, 4).
-define(TYPEREP_TYPEREP_TAG, 5).
-define(TYPEREP_MAP_TAG, 6).
-define(TYPEREP_FUN_TAG, 7).
-define(TYPEREP_CONTRACT_BYTEARRAY_TAG,8).
+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)
]}.
+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_data`
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
%%%
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aeb_fate_data_tests).
-include_lib("eunit/include/eunit.hrl").
-compile([export_all, nowarn_export_all]).
-define(EQC_EUNIT(Module, PropName, Ms),
{ atom_to_list(PropName),
{timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
quickcheck_test_() ->
{setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500),
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000),
?EQC_EUNIT(aefate_eqc, prop_order, 2000),
?EQC_EUNIT(aefate_eqc, prop_fuzz, 2000)
]}.
+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_encoding`
%%% Note that for obtaining cover file, one needs `rebar3 as eqc cover
%%%
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts
-module(aeb_fate_encoding_tests).
-include_lib("eunit/include/eunit.hrl").
-compile([export_all, nowarn_export_all]).
-define(EQC_EUNIT(Module, PropName, Ms),
{ atom_to_list(PropName),
{timeout, (Ms * 3) / 1000, ?_assert(eqc:quickcheck(eqc:testing_time(Ms / 1000, Module:PropName())))}}).
quickcheck_test_() ->
{setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000),
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000),
?EQC_EUNIT(aefate_eqc, prop_no_maps_in_keys, 1000),
?EQC_EUNIT(aefate_eqc, prop_idempotent, 1000)
]}.
+167
View File
@@ -0,0 +1,167 @@
%%% @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() ->
[ Op || #{opcode := Op} <- aeb_fate_generate_ops:get_ops() ].
fate_code(Failure) ->
?SIZED(Size,
?LET({FMap, SMap, AMap},
{non_empty(map(if Failure == 1 -> binary(1);
true -> binary(4) end,
{sublist(lists:sort([private, payable])), %% deserialize sorts them
{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))).
+211
View File
@@ -0,0 +1,211 @@
%%% @author Thomas Arts
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
%%%
%%% We need to be able to generate data that serializes with ?LONG_LIST, ?LONG_TUPLE etc.
%%% In other words make some rather broad terms as well as some deep terms
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_eqc).
-include_lib("eqc/include/eqc.hrl").
-include("../include/aeb_fate_data.hrl").
-compile([export_all, nowarn_export_all]).
prop_roundtrip() ->
?FORALL(FateData, fate_data(),
measure(bytes, size(term_to_binary(FateData)),
begin
Serialized = aeb_fate_encoding:serialize(FateData),
?WHENFAIL(eqc:format("Serialized ~p to ~p~n", [FateData, Serialized]),
equals(aeb_fate_encoding:deserialize(Serialized), FateData))
end)).
prop_format_scan() ->
?FORALL(FateData, fate_data([variant, map]),
?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]),
begin
String = aeb_fate_data:format(FateData),
{ok, _Scanned, _} = aeb_fate_asm_scan:scan(unicode:characters_to_list(String)),
true
end)).
prop_serializes() ->
?FORALL({Data, Garbage}, {fate_data(), binary()},
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]),
begin
Binary = <<(aeb_fate_encoding:serialize(Data))/binary, Garbage/binary>>,
{FateData, Rest} = aeb_fate_encoding:deserialize_one(Binary),
measure(binary_size, size(Binary),
conjunction([{equal, equals(Data, FateData)},
{rest, equals(Garbage, Rest)},
{size, size(Binary) < 500000}]))
end)).
prop_no_maps_in_keys() ->
?FORALL(FateData, fate_bad_map(), %% may contain a map in its keys
begin
HasMapInKeys = lists:any(fun(K) -> has_map(K) end, maps:keys(FateData)),
try aeb_fate_encoding:serialize(FateData),
?WHENFAIL(eqc:format("Should not serialize, contains a map in key\n", []),
not HasMapInKeys)
catch error:Reason ->
?WHENFAIL(eqc:format("(~p) Should serialize\n", [Reason]), HasMapInKeys)
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([variant, map])),
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)])},
{asym, aeb_fate_data:lt(Min, Max) orelse Min == Max}])
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(Kind) ->
?SIZED(Size, ?LET(Data, fate_data(Size, Kind), eqc_symbolic:eval(Data))).
fate_data() ->
fate_data([map, variant, store_map]).
%% keys may contain variants but no maps
fate_data_key() ->
fate_data([variant]).
fate_data(0, Options) ->
?LAZY(
frequency(
[{50, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])},
{10, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(),
fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}] ++
[{1, fate_store_map()} || lists:member(store_map, Options)]));
fate_data(Size, Options) ->
?LAZY(
oneof([fate_data(0, Options),
fate_list(Size, Options),
fate_tuple(Size, Options)] ++
[fate_variant(Size, Options)
|| lists:member(variant, Options)] ++
[fate_map(Size, Options)
|| lists:member(map, Options)])).
fate_integer() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_integer(X))).
fate_bits() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_bits(X))).
fate_boolean() -> ?LET(X, elements([true, false]), return(aeb_fate_data:make_boolean(X))).
fate_nil() -> aeb_fate_data:make_list([]).
fate_unit() -> aeb_fate_data:make_unit().
fate_string() -> ?LET(X, frequency([{10, non_quote_string()}, {2, list(non_quote_string())},
{1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}]),
return(aeb_fate_data:make_string(X))).
fate_address() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_address(X))).
fate_bytes() -> ?LET(X, non_empty(binary()), return(aeb_fate_data:make_bytes(X))).
fate_contract() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_contract(X))).
fate_oracle() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle(X))).
fate_oracle_q() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle_query(X))).
fate_channel() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_channel(X))).
fate_values(Size, N, Options) ->
eqc_gen:list(N, fate_data(Size div max(1, N), Options)).
%% May shrink to fate_unit
fate_tuple(Size, Options) ->
?LET(N, choose(0, 6),
?LETSHRINK(Elements, fate_values(Size, N, Options),
return(aeb_fate_data:make_tuple(list_to_tuple(Elements))))).
fate_variant(Size, Options) ->
?LET({L1, L2, {tuple, Args}}, {list(choose(0, 255)), list(choose(0,255)), fate_tuple(Size, Options)},
return(aeb_fate_data:make_variant(L1 ++ [tuple_size(Args)] ++ L2,
length(L1), Args))).
fate_list(Size, Options) ->
?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(Size, Options) ->
?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, store_map])),
return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
fate_store_map() ->
%% only #{} is allowed as cache in serialization
?LET(X, oneof([int(), largeint()]),
return(aeb_fate_data:make_store_map(abs(X)))).
fate_bad_map() ->
?LET(N, choose(0, 6),
?LET(Values, vector(N, ?SIZED(Size, resize(Size div 8, fate_data()))),
?LET(Keys, vector(N, ?SIZED(Size, resize(Size div 4, fate_data()))),
return(aeb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
non_quote_string() ->
?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []).
char() ->
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 == []).
has_map(L) when is_list(L) ->
lists:any(fun(V) -> has_map(V) end, L);
has_map(T) when is_tuple(T) ->
has_map(tuple_to_list(T));
has_map(M) when is_map(M) ->
true;
has_map(?FATE_STORE_MAP(_, _)) ->
true;
has_map(_) ->
false.
+56
View File
@@ -0,0 +1,56 @@
%%% @author Thomas Arts
%%% @doc Use `rebar3 as eqc shell` to run properties in the shell
%%% Properties for testing Fate type representations
%%%
%%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_type_eqc).
-include_lib("eqc/include/eqc.hrl").
-compile([export_all, nowarn_export_all]).
kind(X) when is_atom(X) -> X;
kind(T) when is_tuple(T) -> element(1, T).
prop_roundtrip() ->
?FORALL(FateType, fate_type(),
collect(kind(FateType),
begin
Serialized = aeb_fate_encoding:serialize_type(FateType),
BinSerialized = list_to_binary(Serialized),
?WHENFAIL(eqc:format("Serialized ~p to ~p (~p)~n", [FateType, Serialized, BinSerialized]),
begin
{Type, <<>>} = aeb_fate_encoding:deserialize_type(BinSerialized),
equals(Type, FateType)
end)
end)).
fate_type() ->
?SIZED(Size, fate_type(Size)).
fate_type(0) ->
oneof([integer,
boolean,
address,
{bytes, nat()},
contract,
oracle,
channel,
bits,
string]);
fate_type(Size) ->
?LAZY(
oneof([fate_type(0),
{list, fate_type(Size div 2)},
?LETSHRINK(Ts, fate_types(Size), {tuple, Ts}),
?LETSHRINK(Ts, fate_types(Size), {variant, Ts}),
?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)))).
+14 -4
View File
@@ -1,10 +1,12 @@
%% -*- mode: erlang; indent-tabs-mode: nil -*-
{minimum_otp_vsn, "20.1"}.
{erl_opts, [debug_info]}.
{deps, [ {eblake2, "1.0.0"}
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
{ref, "b55c372"}}}
{ref, "eb68fe3"}}}
, {getopt, "1.0.1"}
]}.
@@ -12,6 +14,11 @@
{escript_main_app, aebytecode}.
{escript_name, aefateasm}.
{escript_emu_args, "%%!"}.
{pre_hooks,
[{"(linux|darwin|solaris|win32)", compile, "make sources"},
{"(freebsd)", compile, "gmake sources"}]}.
{provider_hooks, [{post, [{compile, escriptize}]}]}.
@@ -33,7 +40,7 @@
{profiles, [{binary, [
{deps, [ {eblake2, "1.0.0"}
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git",
{ref, "b55c372"}}}
{ref, "47aaa8f"}}}
, {getopt, "1.0.1"}
]},
@@ -45,5 +52,8 @@
"robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* "
"/njs /njh /nfl /ndl & exit /b 0"} % silence things
]}
]}]}.
]},
{eqc, [{erl_opts, [{parse_transform, eqc_cover}, {d, 'EQC'}]},
{extra_src_dirs, ["quickcheck"]} %% May not be called eqc!
]}
]}.
+10 -3
View File
@@ -1,16 +1,23 @@
{"1.1.0",
{"1.2.0",
[{<<"aeserialization">>,
{git,"https://github.com/aeternity/aeserialization.git",
{ref,"b55c3726f4a21063721c68d6fa7fda39121edf11"}},
{ref,"eb68fe331bd476910394966b7f5ede7a74d37e35"}},
0},
{<<"base58">>,
{git,"https://github.com/aeternity/erl-base58.git",
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}},
1},
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
{<<"enacl">>,
{git,"https://github.com/aeternity/enacl.git",
{ref,"793ddb502f7fe081302e1c42227dca70b09f8e17"}},
1},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
[
{pkg_hash,[
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]}
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]},
{pkg_hash_ext,[
{<<"eblake2">>, <<"3C4D300A91845B25D501929A26AC2E6F7157480846FAB2347A4C11AE52E08A99">>},
{<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>}]}
].
Executable
BIN
View File
Binary file not shown.
+194
View File
@@ -0,0 +1,194 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc
%%% Encode and decode data and function calls according to
%%% Sophia-AEVM-ABI
%%% @end
%%% Created : 25 Jan 2018
%%%
%%%-------------------------------------------------------------------
-module(aeb_aevm_abi).
-define(HASH_SIZE, 32).
-export([ create_calldata/4
, check_calldata/3
, function_type_info/4
, function_type_hash/3
, arg_typerep_from_function/2
, type_hash_from_function_name/2
, typereps_from_type_hash/2
, function_name_from_type_hash/2
, get_function_hash_from_calldata/1
, is_payable/2
, abi_version/0
]).
-type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8.
-type function_name() :: binary(). %% String
-type typerep() :: aeb_aevm_data:type().
-type function_type_info() :: { FunctionHash :: hash()
, FunctionName :: function_name()
, Payable :: boolean()
, ArgType :: binary() %% binary typerep
, OutType :: binary() %% binary typerep
}.
-type type_info() :: [function_type_info()].
%%%===================================================================
%%% API
%%%===================================================================
%% Shall match ?ABI_AEVM_SOPHIA_1
-spec abi_version() -> integer().
abi_version() ->
1.
%%%===================================================================
%%% Handle calldata
create_calldata(FunName, Args, ArgTypes0, RetType) ->
ArgTypes = {tuple, ArgTypes0},
<<TypeHashInt:?HASH_SIZE/unit:8>> =
function_type_hash(list_to_binary(FunName), ArgTypes, RetType),
Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}),
{ok, Data}.
-spec check_calldata(binary(), type_info(), boolean()) ->
{'ok', typerep(), typerep()} | {'error', atom()}.
check_calldata(CallData, TypeInfo, CheckPayable) ->
%% The first element of the CallData should be the function name
case get_function_hash_from_calldata(CallData) of
{ok, Hash} ->
check_calldata(Hash, CallData, TypeInfo, CheckPayable);
{error, _What} ->
{error, bad_call_data}
end.
check_calldata(Hash, CallData, TypeInfo, true) ->
case is_payable(Hash, TypeInfo) of
{ok, true} -> check_calldata(Hash, CallData, TypeInfo, false);
{ok, false} -> {error, function_is_not_payable};
Err = {error, _} -> Err
end;
check_calldata(Hash, CallData, TypeInfo, false) ->
case typereps_from_type_hash(Hash, TypeInfo) of
{ok, ArgType, OutType} ->
try aeb_heap:from_binary({tuple, [word, ArgType]}, CallData) of
{ok, _Something} ->
{ok, {tuple, [word, ArgType]}, OutType};
{error, _} ->
{error, bad_call_data}
catch
_T:_E ->
{error, bad_call_data}
end;
{error, _} ->
{error, unknown_function}
end.
-spec get_function_hash_from_calldata(CallData::binary()) ->
{ok, binary()} | {error, term()}.
get_function_hash_from_calldata(CallData) ->
case aeb_heap:from_binary({tuple, [word]}, CallData) of
{ok, {HashInt}} -> {ok, <<HashInt:?HASH_SIZE/unit:8>>};
{error, _} = Error -> Error
end.
%%%===================================================================
%%% Handle type info from contract meta data
-spec function_type_info(function_name(), boolean(), [typerep()], typerep()) ->
function_type_info().
function_type_info(Name, Payable, ArgTypes, OutType) ->
ArgType = {tuple, ArgTypes},
{ function_type_hash(Name, ArgType, OutType)
, Name
, Payable
, aeb_heap:to_binary(ArgType)
, aeb_heap:to_binary(OutType)
}.
-spec function_type_hash(function_name(), typerep(), typerep()) -> hash().
function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
Bin = iolist_to_binary([ Name
, aeb_heap:to_binary(ArgType)
, aeb_heap:to_binary(OutType)
]),
%% Calculate a 256 bit digest BLAKE2b hash value of a binary
{ok, Hash} = eblake2:blake2b(?HASH_SIZE, Bin),
Hash.
-spec arg_typerep_from_function(function_name(), type_info()) ->
{'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
arg_typerep_from_function(Function, TypeInfo) ->
case lists:keyfind(Function, 2, TypeInfo) of
{_TypeHash, Function, ArgTypeBin, _OutTypeBin} ->
arg_typerep_from_type_binary(ArgTypeBin);
{_TypeHash, Function, _Payable, ArgTypeBin, _OutTypeBin} ->
arg_typerep_from_type_binary(ArgTypeBin);
false ->
{error, unknown_function}
end.
arg_typerep_from_type_binary(ArgTBin) ->
case aeb_heap:from_binary(typerep, ArgTBin) of
{ok, ArgT} -> {ok, ArgT};
{error,_} -> {error, bad_type_data}
end.
-spec typereps_from_type_hash(hash(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
typereps_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash, _Function, ArgTypeBin, OutTypeBin} ->
typereps_from_type_binaries(ArgTypeBin, OutTypeBin);
{TypeHash, _Function, _Payable, ArgTypeBin, OutTypeBin} ->
typereps_from_type_binaries(ArgTypeBin, OutTypeBin);
false ->
{error, unknown_function}
end.
typereps_from_type_binaries(ArgTBin, OutTBin) ->
case {aeb_heap:from_binary(typerep, ArgTBin), aeb_heap:from_binary(typerep, OutTBin)} of
{{ok, ArgT}, {ok, OutT}} -> {ok, ArgT, OutT};
{_, _} -> {error, bad_type_data}
end.
-spec function_name_from_type_hash(hash(), type_info()) ->
{'ok', function_name()}
| {'error', 'unknown_function'}.
function_name_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash, Function, _ArgTypeBin, _OutTypeBin} ->
{ok, Function};
{TypeHash, Function, _Payable, _ArgTypeBin, _OutTypeBin} ->
{ok, Function};
false ->
{error, unknown_function}
end.
-spec type_hash_from_function_name(function_name(), type_info()) ->
{'ok', hash()}
| {'error', 'unknown_function'}.
type_hash_from_function_name(Name, TypeInfo) ->
case lists:keyfind(Name, 2, TypeInfo) of
{TypeHash, Name, _ArgTypeBin, _OutTypeBin} ->
{ok, TypeHash};
{TypeHash, Name, _Payable, _ArgTypeBin, _OutTypeBin} ->
{ok, TypeHash};
false ->
{error, unknown_function}
end.
-spec is_payable(hash(), type_info()) -> {ok, boolean()} | {error, 'unknown_function'}.
is_payable(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash, _Function, _ArgTypeBin, _OutTypeBin} ->
{ok, true};
{TypeHash, _Function, Payable, _ArgTypeBin, _OutTypeBin} ->
{ok, Payable};
false ->
{error, unknown_function}
end.
+30
View File
@@ -0,0 +1,30 @@
-module(aeb_aevm_data).
-export_type([data/0,
type/0,
heap/0]).
-type type() :: word | signed_word | string | typerep | function
| {list, type()}
| {option, type()}
| {tuple, [type()]}
| {variant, [[type()]]}.
-type data() :: none
| {some, data()}
| {option, data()}
| word
| string
| {list, data()}
| {tuple, [data()]}
| {variant, integer(), [data()]}
| integer()
| binary()
| [data()]
| {}
| {data()}
| {data(), data()}.
-type heap() :: binary().
+79
View File
@@ -0,0 +1,79 @@
%%%-------------------------------------------------------------------
%%% @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)),
try ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(Calldata)) of
[FunctionId, FateArgs] -> {ok, ?FATE_TUPLE_ELEMENTS(FateArgs)};
_ -> {error, decode_error}
catch _:_ ->
{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};
{_Attrs, {ArgTypes, RetType}, _Code} ->
{ok, ArgTypes, RetType}
end.
+269 -632
View File
File diff suppressed because it is too large Load Diff
+147
View File
@@ -0,0 +1,147 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, aeternity Anstalt
%%% @doc
%%% Handling FATE code.
%%% @end
%%% ###REPLACEWITHNOTE###
%%%-------------------------------------------------------------------
Definitions.
DIGIT = [0-9]
HEXDIGIT = [0-9a-fA-F]
LOWER = [a-z_]
UPPER = [A-Z]
BASE58 = [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
BASE64 = [A-Za-z0-9+/=]
INT = {DIGIT}+
HEX = 0x{HEXDIGIT}+
OBJ_PFX = (ak|ct|ok|oq|ch|sg)
OBJECT = @{OBJ_PFX}_{BASE58}+
CODE = @cb_{BASE64}+
BYTES = #{BASE64}+
WS = [\000-\s]
ID = {LOWER}[a-zA-Z0-9_]*
STRING = "[^"]*"
BITS = (\!)?\<[\s01]*\>
Rules.
arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}.
var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}.
a : {token, {stack, TokenLine}}.
true : {token, {boolean, TokenLine, true}}.
false : {token, {boolean, TokenLine, false}}.
%% ###REPLACEWITHOPTOKENS###
FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
{BYTES} :
{token, {bytes, TokenLine, parse_hash(TokenChars)}}.
{CODE} :
{token, {contract_bytearray, TokenLine, parse_contract_bytearray(TokenChars)}}.
{OBJECT} :
{token, {object, TokenLine, parse_object(TokenChars)}}.
{ID} :
{token, {id, TokenLine, TokenChars}}.
{HEX} :
{token, {int, TokenLine, parse_hex(TokenChars)}}.
{INT} :
{token, {int, TokenLine, parse_int(TokenChars)}}.
-{INT} :
{token, {int, TokenLine, parse_int(TokenChars)}}.
%% Due to the definition of STRING the tokens start and end with a quote ".
{STRING} :
{token, {string, TokenLine, unicode:characters_to_binary(
lists:sublist(TokenChars, 2, length(TokenChars) - 2))}}.
{BITS} :
{token, {bits, TokenLine, bits(TokenChars)}}.
%% Symbols
\-\> : {token, {to, TokenLine}}.
\: : {token, {to, TokenLine}}.
\=\> : {token, {arrow, TokenLine}}.
\(\| : {token, {start_variant, TokenLine}}.
\|\) : {token, {end_variant, TokenLine}}.
, : {token, {',', TokenLine}}.
\( : {token, {'(', TokenLine}}.
\) : {token, {')', TokenLine}}.
\[ : {token, {'[', TokenLine}}.
\] : {token, {']', TokenLine}}.
\{ : {token, {'{', TokenLine}}.
\} : {token, {'}', TokenLine}}.
\| : {token, {'|', TokenLine}}.
\' : {token, {typerep, TokenLine}}.
;;.* :
{token, {comment, TokenLine, drop_prefix($;, TokenChars)}}.
\. : skip_token.
%% Whitespace ignore
{WS} : skip_token.
%% Comments (TODO: nested comments)
. : {error, "Unexpected token: " ++ TokenChars}.
Erlang code.
-export([scan/1]).
-dialyzer({nowarn_function, yyrev/2}).
-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
parse_int(Chars) -> list_to_integer(Chars).
parse_arg("arg" ++ N) -> list_to_integer(N).
parse_var("var" ++ N) -> list_to_integer(N).
parse_hash("#" ++ Chars) ->
base64:decode(Chars).
parse_contract_bytearray("@" ++ Chars) ->
case aeser_api_encoder:decode(unicode:characters_to_binary(Chars)) of
{contract_bytearray, Bin} -> Bin
end.
parse_object([_|Chars]) ->
case aeser_api_encoder:decode(unicode:characters_to_binary(Chars)) of
{account_pubkey, Bin} -> {address, Bin};
{contract_pubkey, Bin} -> {contract, Bin};
{oracle_pubkey, Bin} -> {oracle, Bin};
{oracle_query_id, Bin} -> {oracle_query, Bin};
{channel, Bin} -> {channel, Bin};
{signature, Bin} -> {signature, Bin}
end.
scan(S) ->
string(S).
drop_prefix(C, [C|Rest]) ->
drop_prefix(C, Rest);
drop_prefix(_, Tail) -> Tail.
bits([$!, $< | Rest]) ->
bits(Rest, -1);
bits([$< | Rest]) ->
bits(Rest, 0).
bits([$> |_Rest], Acc) -> Acc;
bits([$0 | Rest], Acc) -> bits(Rest, Acc bsl 1);
bits([$1 | Rest], Acc) -> bits(Rest, (Acc bsl 1) bor 1);
bits([$ | Rest], Acc) -> bits(Rest, Acc).
-207
View File
@@ -1,207 +0,0 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, aeternity Anstalt
%%% @doc
%%% Handling FATE code.
%%% @end
%%% Created : 9 Jan 2019
%%%-------------------------------------------------------------------
Definitions.
DIGIT = [0-9]
HEXDIGIT = [0-9a-fA-F]
LOWER = [a-z_]
UPPER = [A-Z]
INT = {DIGIT}+
HEX = 0x{HEXDIGIT}+
HASH = #{HEXDIGIT}+
WS = [\000-\s]
ID = {LOWER}[a-zA-Z0-9_]*
Rules.
arg{INT} : {token, {arg, TokenLine, parse_arg(TokenChars)}}.
var{INT} : {token, {var, TokenLine, parse_var(TokenChars)}}.
a : {token, {stack, TokenLine, 0}}.
a{INT} : {token, {stack, TokenLine, parse_acc(TokenChars)}}.
true : {token, {boolean, TokenLine, true}}.
false : {token, {boolean, TokenLine, false}}.
RETURN : {token, {mnemonic, TokenLine, 'RETURN'}}.
RETURNR : {token, {mnemonic, TokenLine, 'RETURNR'}}.
CALL : {token, {mnemonic, TokenLine, 'CALL'}}.
NOP : {token, {mnemonic, TokenLine, 'NOP'}}.
CALL_R : {token, {mnemonic, TokenLine, 'CALL_R'}}.
CALL_T : {token, {mnemonic, TokenLine, 'CALL_T'}}.
CALL_TR : {token, {mnemonic, TokenLine, 'CALL_TR'}}.
JUMP : {token, {mnemonic, TokenLine, 'JUMP'}}.
JUMPIF : {token, {mnemonic, TokenLine, 'JUMPIF'}}.
SWITCH_V2 : {token, {mnemonic, TokenLine, 'SWITCH_V2'}}.
SWITCH_V3 : {token, {mnemonic, TokenLine, 'SWITCH_V3'}}.
SWITCH_VN : {token, {mnemonic, TokenLine, 'SWITCH_VN'}}.
PUSH : {token, {mnemonic, TokenLine, 'PUSH'}}.
DUP : {token, {mnemonic, TokenLine, 'DUP'}}.
DUPA : {token, {mnemonic, TokenLine, 'DUPA'}}.
POP : {token, {mnemonic, TokenLine, 'POP'}}.
STORE : {token, {mnemonic, TokenLine, 'STORE'}}.
ADD : {token, {mnemonic, TokenLine, 'ADD'}}.
MUL : {token, {mnemonic, TokenLine, 'MUL'}}.
SUB : {token, {mnemonic, TokenLine, 'SUB'}}.
DIV : {token, {mnemonic, TokenLine, 'DIV'}}.
MOD : {token, {mnemonic, TokenLine, 'MOD'}}.
POW : {token, {mnemonic, TokenLine, 'POW'}}.
INC : {token, {mnemonic, TokenLine, 'INC'}}.
DEC : {token, {mnemonic, TokenLine, 'DEC'}}.
INCA : {token, {mnemonic, TokenLine, 'INCA'}}.
DECA : {token, {mnemonic, TokenLine, 'DECA'}}.
LT : {token, {mnemonic, TokenLine, 'LT'}}.
GT : {token, {mnemonic, TokenLine, 'GT'}}.
EQ : {token, {mnemonic, TokenLine, 'EQ'}}.
ELT : {token, {mnemonic, TokenLine, 'ELT'}}.
EGT : {token, {mnemonic, TokenLine, 'EGT'}}.
NEQ : {token, {mnemonic, TokenLine, 'NEQ'}}.
AND : {token, {mnemonic, TokenLine, 'AND'}}.
OR : {token, {mnemonic, TokenLine, 'OR'}}.
NOT : {token, {mnemonic, TokenLine, 'NOT'}}.
TUPLE : {token, {mnemonic, TokenLine, 'TUPLE'}}.
ELEMENT : {token, {mnemonic, TokenLine, 'ELEMENT'}}.
MAP_EMPTY : {token, {mnemonic, TokenLine, 'MAP_EMPTY'}}.
MAP_LOOKUP : {token, {mnemonic, TokenLine, 'MAP_LOOKUP'}}.
MAP_LOOKUPD : {token, {mnemonic, TokenLine, 'MAP_LOOKUPD'}}.
MAP_UPDATE : {token, {mnemonic, TokenLine, 'MAP_UPDATE'}}.
MAP_MEMBER : {token, {mnemonic, TokenLine, 'MAP_MEMBER'}}.
MAP_DELETE : {token, {mnemonic, TokenLine, 'MAP_DELETE'}}.
MAP_FROM_LIST : {token, {mnemonic, TokenLine, 'MAP_FROM_LIST'}}.
NIL : {token, {mnemonic, TokenLine, 'NIL'}}.
IS_NIL : {token, {mnemonic, TokenLine, 'IS_NIL'}}.
CONS : {token, {mnemonic, TokenLine, 'CONS'}}.
HD : {token, {mnemonic, TokenLine, 'HD'}}.
TL : {token, {mnemonic, TokenLine, 'TL'}}.
LENGTH : {token, {mnemonic, TokenLine, 'LENGTH'}}.
STR_EQ : {token, {mnemonic, TokenLine, 'STR_EQ'}}.
STR_JOIN : {token, {mnemonic, TokenLine, 'STR_JOIN'}}.
INT_TO_STR : {token, {mnemonic, TokenLine, 'INT_TO_STR'}}.
ADDR_TO_STR : {token, {mnemonic, TokenLine, 'ADDR_TO_STR'}}.
STR_REVERSE : {token, {mnemonic, TokenLine, 'STR_REVERSE'}}.
INT_TO_ADDR : {token, {mnemonic, TokenLine, 'INT_TO_ADDR'}}.
VARIANT : {token, {mnemonic, TokenLine, 'VARIANT'}}.
VARIANT_TEST : {token, {mnemonic, TokenLine, 'VARIANT_TEST'}}.
VARIANT_ELEMENT : {token, {mnemonic, TokenLine, 'VARIANT_ELEMENT'}}.
BITS_NONE : {token, {mnemonic, TokenLine, 'BITS_NONE'}}.
BITS_NONEA : {token, {mnemonic, TokenLine, 'BITS_NONEA'}}.
BITS_ALL : {token, {mnemonic, TokenLine, 'BITS_ALL'}}.
BITS_ALLA : {token, {mnemonic, TokenLine, 'BITS_ALLA'}}.
BITS_ALL_N : {token, {mnemonic, TokenLine, 'BITS_ALL_N'}}.
BITS_SET : {token, {mnemonic, TokenLine, 'BITS_SET'}}.
BITS_CLEAR : {token, {mnemonic, TokenLine, 'BITS_CLEAR'}}.
BITS_TEST : {token, {mnemonic, TokenLine, 'BITS_TEST'}}.
BITS_SUM : {token, {mnemonic, TokenLine, 'BITS_SUM'}}.
BITS_OR : {token, {mnemonic, TokenLine, 'BITS_OR'}}.
BITS_AND : {token, {mnemonic, TokenLine, 'BITS_AND'}}.
BITS_DIFF : {token, {mnemonic, TokenLine, 'BITS_DIFF'}}.
ADDRESS : {token, {mnemonic, TokenLine, 'ADDRESS'}}.
BALANCE : {token, {mnemonic, TokenLine, 'BALANCE'}}.
ORIGIN : {token, {mnemonic, TokenLine, 'ORIGIN'}}.
CALLER : {token, {mnemonic, TokenLine, 'CALLER'}}.
GASPRICE : {token, {mnemonic, TokenLine, 'GASPRICE'}}.
BLOCKHASH : {token, {mnemonic, TokenLine, 'BLOCKHASH'}}.
BENEFICIARY : {token, {mnemonic, TokenLine, 'BENEFICIARY'}}.
TIMESTAMP : {token, {mnemonic, TokenLine, 'TIMESTAMP'}}.
NUMBER : {token, {mnemonic, TokenLine, 'NUMBER'}}.
DIFFICULTY : {token, {mnemonic, TokenLine, 'DIFFICULTY'}}.
GASLIMIT : {token, {mnemonic, TokenLine, 'GASLIMIT'}}.
GAS : {token, {mnemonic, TokenLine, 'GAS'}}.
LOG0 : {token, {mnemonic, TokenLine, 'LOG0'}}.
LOG1 : {token, {mnemonic, TokenLine, 'LOG1'}}.
LOG2 : {token, {mnemonic, TokenLine, 'LOG2'}}.
LOG3 : {token, {mnemonic, TokenLine, 'LOG3'}}.
LOG4 : {token, {mnemonic, TokenLine, 'LOG4'}}.
ABORT : {token, {mnemonic, TokenLine, 'ABORT'}}.
EXIT : {token, {mnemonic, TokenLine, 'EXIT'}}.
DEACTIVATE : {token, {mnemonic, TokenLine, 'DEACTIVATE'}}.
COMMENT : {token, {mnemonic, TokenLine, 'COMMENT'}}.
FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
{ID} :
{token, {id, TokenLine, TokenChars}}.
{HEX} :
{token, {int, TokenLine, parse_hex(TokenChars)}}.
{INT} :
{token, {int, TokenLine, parse_int(TokenChars)}}.
{HASH} :
{token, {hash, TokenLine, parse_hash(TokenChars)}}.
%% Symbols
\-\> : {token, {'to', TokenLine}}.
\: : {token, {'to', TokenLine}}.
, : {token, {',', TokenLine}}.
\( : {token, {'(', TokenLine}}.
\) : {token, {')', TokenLine}}.
\[ : {token, {'[', TokenLine}}.
\] : {token, {']', TokenLine}}.
\{ : {token, {'{', TokenLine}}.
\} : {token, {'}', TokenLine}}.
;;.* :
{token, {comment, TokenLine, drop_prefix($;, TokenChars)}}.
\. : skip_token.
%% Whitespace ignore
{WS} : skip_token.
%% Comments (TODO: nested comments)
. : {error, "Unexpected token: " ++ TokenChars}.
Erlang code.
-export([scan/1]).
-dialyzer({nowarn_function, yyrev/2}).
-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
parse_int(Chars) -> list_to_integer(Chars).
parse_arg("arg" ++ N) -> list_to_integer(N).
parse_var("var" ++ N) -> list_to_integer(N).
parse_acc("a" ++ N) -> list_to_integer(N).
parse_hash("#" ++ Chars) ->
N = list_to_integer(Chars, 16),
<<N:256>>.
scan(S) ->
string(S).
drop_prefix(C, [C|Rest]) ->
drop_prefix(C, Rest);
drop_prefix(_, Tail) -> Tail.
+444 -299
View File
@@ -1,303 +1,448 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% ADT for fate byte code/fate code
%%% @end
%%%
%%%-------------------------------------------------------------------
-module(aeb_fate_code).
-export([ return/0
, return/1
, call/1
, call_t/1
, call_r/2
, call_tr/2
, jump/1
, jumpif/2
, switch/3
, switch/4
, switch/5
, switch/6
, push/1
, inc/0
, inc/1
, dec/0
, dec/1
, add/3
, sub/3
, mul/3
, divide/3
, modulo/3
, pow/3
, lt/3
, gt/3
, elt/3
, egt/3
, eq/3
, neq/3
, and_op/3
, or_op/3
, not_op/2
, tuple/1
, element_op/4
, map_empty/1
, map_lookup/3
, map_lookup/4
, map_update/4
, map_member/3
, map_from_list/2
, nil/1
, is_nil/2
, cons/3
, hd/2
, tl/2
, length/2
, str_eq/3
, str_join/3
, int_to_str/2
, addr_to_str/2
, str_reverse/2
, int_to_addr/2
, variant_test/3
, variant_element/3
, variant/4
, bits_none/0
, bits_none/1
, bits_all/0
, bits_all/1
, bits_all_n/2
, bits_set/3
, bits_clear/3
, bits_test/3
, bits_sum/2
, bits_or/3
, bits_and/3
, bits_diff/3
, dup/0
, dup/1
, pop/0
, store/2
, nop/0
-export([ annotations/1
, deserialize/1
, functions/1
, insert_annotation/4
, insert_fun/5
, insert_symbol/2
, new/0
, serialize/1
, serialize/2
, serialize/3
, serialize_code/1
, serialize_signature/1
, strip_init_function/1
, symbol_identifier/1
, symbols/1
]).
-define(i(__X__), {immediate, __X__ }).
return() ->
'RETURN'.
return(Arg) ->
{'RETURNR', Arg}.
call(Function) when is_binary(Function)->
{'CALL', ?i(Function) }.
call_t(Function) when is_binary(Function) ->
{'CALL_T', ?i(Function)}.
call_r(Contract, Function) when is_binary(Function) ->
{'CALL_R', Contract, ?i(Function)}.
call_tr(Contract, Function) when is_binary(Function) ->
{'CALL_TR', Contract, ?i(Function)}.
jump(BB) when is_integer(BB) ->
{'JUMP', ?i(BB)}.
jumpif(Arg, BB) when is_integer(BB) ->
{'JUMPIF', Arg, ?i(BB)}.
switch(Arg, BB1, BB2) when is_integer(BB1),
is_integer(BB2) ->
{'SWITCH_V2', Arg, ?i(BB1), ?i(BB2)}.
switch(Arg, BB1, BB2, BB3) when is_integer(BB1),
is_integer(BB2),
is_integer(BB3) ->
{'SWITCH_V3', Arg, ?i(BB1), ?i(BB2), ?i(BB3)}.
switch(Arg, BB1, BB2, BB3, BB4) when is_integer(BB1),
is_integer(BB2),
is_integer(BB3),
is_integer(BB4) ->
{'SWITCH_V4', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4)}.
switch(Arg, BB1, BB2, BB3, BB4, BB5) when is_integer(BB1),
is_integer(BB2),
is_integer(BB3),
is_integer(BB4),
is_integer(BB5) ->
{'SWITCH_V5', Arg, ?i(BB1), ?i(BB2), ?i(BB3), ?i(BB4), ?i(BB5)}.
push(Arg) ->
{'PUSH', Arg}.
inc() ->
'INCA'.
inc(Arg) ->
{'INC', Arg}.
dec() ->
'DECA'.
dec(Arg) ->
{'DEC', Arg}.
add(Dest, Left, Right) ->
{'ADD', Dest, Left, Right}.
sub(Dest, Left, Right) ->
{'SUB', Dest, Left, Right}.
mul(Dest, Left, Right) ->
{'MUL', Dest, Left, Right}.
divide(Dest, Left, Right) ->
{'DIV', Dest, Left, Right}.
modulo(Dest, Left, Right) ->
{'MOD', Dest, Left, Right}.
pow(Dest, Left, Right) ->
{'POW', Dest, Left, Right}.
lt(Dest, Left, Right) ->
{'LT', Dest, Left, Right}.
gt(Dest, Left, Right) ->
{'GT', Dest, Left, Right}.
elt(Dest, Left, Right) ->
{'ELT', Dest, Left, Right}.
egt(Dest, Left, Right) ->
{'EGT', Dest, Left, Right}.
eq(Dest, Left, Right) ->
{'EQ', Dest, Left, Right}.
neq(Dest, Left, Right) ->
{'NEQ', Dest, Left, Right}.
and_op(Dest, Left, Right) ->
{'AND', Dest, Left, Right}.
or_op(Dest, Left, Right) ->
{'OR', Dest, Left, Right}.
not_op(Dest, Arg) ->
{'NOT', Dest, Arg}.
tuple(Size) when is_integer(Size) ->
{'TUPLE', ?i(Size)}.
element_op(Type, Dest, N, T) ->
{'ELEMENT', Type, Dest, N, T}.
map_empty(Dest) ->
{'MAP_EMPTY', Dest}.
map_lookup(Dest, Map, Key) ->
{'MAP_LOOKUP', Dest, Map, Key}.
map_lookup(Dest, Map, Key, Default) ->
{'MAP_LOOKUPD', Dest, Map, Key, Default}.
map_update(Dest, Map, Key, Value) ->
{'MAP_UPDATE', Dest, Map, Key, Value}.
map_member(Dest, Map, Key) ->
{'MAP_MEMBER', Dest, Map, Key}.
map_from_list(Dest, List) ->
{'MAP_MEMBER', Dest, List}.
nil(Dest) ->
{'NIL', Dest}.
is_nil(Dest, List) ->
{'IS_NIL', Dest, List}.
cons(Dest, Hd, Tl) ->
{'CONS', Dest, Hd, Tl}.
hd(Dest, List) ->
{'HD', Dest, List}.
tl(Dest, List) ->
{'TL', Dest, List}.
length(Dest, List) ->
{'LENGTH', Dest, List}.
str_eq(Dest, Str1, Str2) ->
{'STR_EQ', Dest, Str1, Str2}.
str_join(Dest, Str1, Str2) ->
{'STR_JOIN', Dest, Str1, Str2}.
int_to_str(Dest, Str) ->
{'INT_TO_STR', Dest, Str}.
addr_to_str(Dest, Str) ->
{'ADDR_TO_STR', Dest, Str}.
str_reverse(Dest, Str) ->
{'STR_REVERSE', Dest, Str}.
int_to_addr(Dest, Str) ->
{'INT_TO_ADDR', Dest, Str}.
variant_test(Dest, Variant, Tag) ->
{'VARIANT_TEST', Dest, Variant, Tag}.
variant_element( Dest, Variant, Index) ->
{'VARIANT_ELEMENT', Dest, Variant, Index}.
variant(Dest, SizeA, TagA, ElementsA) ->
{'VARIANT', Dest, SizeA, TagA, ElementsA}.
bits_none() ->
'BITS_NONEA'.
bits_none(To) ->
{'BITS_NONE', To}.
bits_all() ->
'BITS_ALLA'.
bits_all(To) ->
{'BITS_ALL', To}.
bits_all_n(To, N) ->
{'BITS_ALL_N', To, N}.
bits_set(To, Bits, Bit) ->
{'BITS_SET', To, Bits, Bit}.
bits_clear(To, Bits, Bit) ->
{'BITS_CLEAR', To, Bits, Bit}.
bits_test(To, Bits, Bit) ->
{'BITS_TEST', To, Bits, Bit}.
bits_sum(To, Bits) ->
{'BITS_SUM', To, Bits}.
bits_or(To, Bits, Bit) ->
{'BITS_OR', To, Bits, Bit}.
bits_and(To, Bits, Bit) ->
{'BITS_AND', To, Bits, Bit}.
bits_diff(To, Bits, Bit) ->
{'BITS_DIFF', To, Bits, Bit}.
dup() ->
'DUPA'.
dup(N) when is_integer(N) ->
{'DUP', ?i(N)}.
pop() ->
'POP'.
store(Var, What) ->
{'STORE', Var, What}.
nop() ->
'NOP'.
-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, Attrs, {ArgType, RetType}, #{} = BBs, FCode) ->
{F1, ID} = insert_symbol(Name, FCode),
update_functions(F1, #{ID => {Attrs, {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 }).
strip_init_function(#fcode{ functions = Funs,
symbols = Syms } = FCode) ->
Funs1 = maps:remove(?FATE_INIT_ID, Funs),
Syms1 = maps:remove(?FATE_INIT_ID, Syms),
FCode#fcode{ functions = Funs1, symbols = Syms1 }.
%%%===================================================================
%%% 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, {Attrs, Sig, C}}, Acc) ->
[[?FUNCTION, Id, serialize_attributes(Attrs), serialize_signature(Sig), serialize_bbs(C)] | Acc]
end, [], lists:sort(maps:to_list(Functions)))).
serialize_attributes(Attrs) ->
AttrVal = lists:sum([ attr_value(Attr) || Attr <- Attrs ]),
aeb_fate_encoding:serialize(?MAKE_FATE_INTEGER(AttrVal)).
attr_value(private) -> 1;
attr_value(payable) -> 2.
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) ->
{Attrs, Rest2} = deserialize_attributes(Rest),
{Sig, Rest3} = deserialize_signature(Rest2),
Env2 = Env#{function => {<<A,B,C,D>>, Attrs, Sig}},
deserialize_functions(Rest3, Env2);
deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
#{ function := {F, Attrs, Sig}
, bb := BB
, current_bb_code := Code
, code := Program
, functions := Funs} = Env) ->
{NewAttrs, Rest2} = deserialize_attributes(Rest),
{NewSig, Rest3} = deserialize_signature(Rest2),
case Code of
[] ->
Env2 = Env#{ bb => 0
, current_bb_code => []
, function => {<<A,B,C,D>>, NewAttrs, NewSig}
, code => #{}
, functions => Funs#{F => {Attrs, Sig, Program}}},
deserialize_functions(Rest3, Env2);
_ ->
Env2 = Env#{ bb => 0
, current_bb_code => []
, function => {<<A,B,C,D>>, NewAttrs, NewSig}
, code => #{}
, functions =>
Funs#{F => {Attrs, Sig,
Program#{ BB => lists:reverse(Code)}}}},
deserialize_functions(Rest3, 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, Attrs, Sig}
, bb := BB
, current_bb_code := Code
, code := Program
, functions := Funs}) ->
FunctionCode =
case Code of
[] -> Program;
_ -> Program#{ BB => lists:reverse(Code)}
end,
Funs#{F => {Attrs, 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_attributes(Binary) ->
{AttrVal, Rest} = aeb_fate_encoding:deserialize_one(Binary),
Attrs = [ attr(AVal) || AVal <- attr_vals(1, AttrVal) ],
{lists:sort(Attrs), Rest}.
attr_vals(_, 0) -> [];
attr_vals(X, N) when N rem 2 == 0 -> attr_vals(X + 1, N div 2);
attr_vals(X, N) -> [X | attr_vals(X + 1, N div 2)].
attr(1) -> private;
attr(2) -> payable.
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).
+334 -121
View File
@@ -4,20 +4,44 @@
-module(aeb_fate_data).
-type fate_integer() :: ?FATE_INTEGER_T.
-type fate_boolean() :: ?FATE_BOOLEAN_T.
-type fate_nil() :: ?FATE_NIL_T.
-type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T.
-type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T.
-type fate_integer() :: ?FATE_INTEGER_T.
-type fate_boolean() :: ?FATE_BOOLEAN_T.
-type fate_nil() :: ?FATE_NIL_T.
-type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T.
-type fate_store_map() :: ?FATE_STORE_MAP_T.
-type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T.
-type fate_hash() :: ?FATE_BYTES_T(32).
-type fate_signature() :: ?FATE_BYTES_T(64).
-type fate_contract() :: ?FATE_CONTRACT_T.
-type fate_oracle() :: ?FATE_ORACLE_T.
-type fate_oracle_q() :: ?FATE_ORACLE_Q_T.
-type fate_channel() :: ?FATE_CHANNEL_T.
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_bits() :: ?FATE_BITS_T.
-type fate_typerep() :: ?FATE_TYPEREP_T.
-type fate_contract_bytearray() :: ?FATE_CONTRACT_BYTEARRAY_T.
-type fate_variant() :: ?FATE_VARIANT_T.
-type fate_type_type() :: integer
| boolean
| {list, fate_type_type()}
| {map, fate_type_type(), fate_type_type()}
| {tuple, [fate_type_type()]}
| address
| hash
| signature
| contract
| oracle
| oracle_query
| channel
| bits
| string
| {variant, [fate_type_type()]}
| contract_bytearray.
-type fate_void() :: ?FATE_VOID_T.
-type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_type() ::
fate_boolean()
@@ -28,13 +52,38 @@
| fate_tuple()
| fate_string()
| fate_address()
| fate_hash()
| fate_signature()
| fate_contract()
| fate_oracle()
| fate_oracle_q()
| fate_channel()
| fate_variant()
| fate_map()
| fate_list()
| fate_tuple()
| fate_void(). %% Not sure we need this.
| fate_bits()
| fate_typerep()
| fate_contract_bytearray().
-export_type([fate_type/0]).
-export_type([fate_type/0
, fate_boolean/0
, fate_integer/0
, fate_nil/0
, fate_list/0
, fate_unit/0
, fate_tuple/0
, fate_string/0
, fate_address/0
, fate_hash/0
, fate_signature/0
, fate_contract/0
, fate_oracle/0
, fate_channel/0
, fate_variant/0
, fate_map/0
, fate_store_map/0
, fate_bits/0
, fate_type_type/0
]).
-export([ make_integer/1
, make_boolean/1
@@ -43,140 +92,304 @@
, make_tuple/1
, make_string/1
, make_map/1
, make_store_map/1
, make_store_map/2
, make_address/1
, make_bytes/1
, make_hash/1
, make_signature/1
, make_contract/1
, make_oracle/1
, make_oracle_query/1
, make_channel/1
, make_bits/1
, make_unit/0
, tuple_to_list/1
, decode/1
, encode/1
, make_typerep/1
, make_contract_bytearray/1
]).
-export([format/1]).
-export([
elt/2
, lt/2
, format/1
, ordinal/1]).
make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I).
make_boolean(true) -> ?FATE_TRUE;
make_boolean(false) -> ?FATE_FALSE.
make_list([]) -> ?FATE_NIL;
make_list(L) -> ?MAKE_FATE_LIST(L).
make_string(S) when is_list(S) ->
?FATE_STRING(list_to_binary(lists:flatten(S)));
make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M).
make_address(A) -> ?FATE_ADDRESS(A).
make_bits(I) when is_integer(I) -> ?FATE_BITS(I).
make_list([]) -> ?FATE_NIL;
make_list(L) -> ?MAKE_FATE_LIST(L).
make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M).
make_store_map(Id) -> make_store_map(#{}, Id).
make_store_map(Cache, Id) -> ?FATE_STORE_MAP(Cache, Id).
make_address(X) -> ?FATE_ADDRESS(X).
make_bytes(X) -> ?FATE_BYTES(X).
make_hash(X) -> make_bytes(X).
make_signature(X) -> make_bytes(X).
make_contract(X) -> ?FATE_CONTRACT(X).
make_oracle(X) -> ?FATE_ORACLE(X).
make_oracle_query(X) -> ?FATE_ORACLE_Q(X).
make_channel(X) -> ?FATE_CHANNEL(X).
make_integer(I) when is_integer(I) -> ?MAKE_FATE_INTEGER(I).
make_bits(I) when is_integer(I) -> ?FATE_BITS(I).
make_string(S) when is_list(S) ->
?FATE_STRING(iolist_to_binary(S));
make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_typerep(T) -> ?FATE_TYPEREP(T).
make_contract_bytearray(B) -> ?FATE_CONTRACT_BYTEARRAY(B).
make_variant(Size, Tag, Values) when is_integer(Size), is_integer(Tag)
, 0 =< Size
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
?FATE_VARIANT(Size, Tag, Values).
tuple_to_list(?FATE_TUPLE(T)) -> erlang:tuple_to_list(T).
%% Encode is a convinience function for testing, encoding an Erlang term
%% to a Fate term, but it can not distinguish between e.g. 32-byte strings
%% and addresses. Therfore an extra tuple layer on the erlang side for
%% addresses and bits.
encode({bits, Term}) when is_integer(Term) -> make_bits(Term);
%% TODO: check that each byte is in base58
encode({address, B}) when is_binary(B) -> make_address(B);
encode({address, I}) when is_integer(I) -> B = <<I:256>>, make_address(B);
encode({address, S}) when is_list(S) -> make_address(base58_to_address(S));
encode({variant, Size, Tag, Values}) -> make_variant(Size, Tag, Values);
encode(Term) when is_integer(Term) -> make_integer(Term);
encode(Term) when is_boolean(Term) -> make_boolean(Term);
encode(Term) when is_list(Term) -> make_list([encode(E) || E <- Term]);
encode(Term) when is_tuple(Term) ->
make_tuple(list_to_tuple([encode(E) || E <- erlang:tuple_to_list(Term)]));
encode(Term) when is_map(Term) ->
make_map(maps:from_list([{encode(K), encode(V)} || {K,V} <- maps:to_list(Term)]));
encode(Term) when is_binary(Term) -> make_string(Term).
%% Tag points to the selected variant (zero based)
%% The arity of this variant is read from the list of provided arities
%% and should match the size of the given tuple.
make_variant(Arities, Tag, Values) ->
Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities),
if is_integer(Tag)
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
Arity = lists:nth(Tag+1, Arities),
if size(Values) =:= Arity ->
?FATE_VARIANT(Arities, Tag, Values)
end
end.
decode(I) when ?IS_FATE_INTEGER(I) -> I;
decode(?FATE_TRUE) -> true;
decode(?FATE_FALSE) -> false;
decode(L) when ?IS_FATE_LIST(L) -> [decode(E) || E <- L];
decode(?FATE_ADDRESS(<<Address:256>>)) -> {address, Address};
decode(?FATE_BITS(Bits)) -> {bits, Bits};
decode(?FATE_TUPLE(T)) -> erlang:list_to_tuple([decode(E) || E <- T]);
decode(?FATE_VARIANT(Size, Tag, Values)) -> {variant, Size, Tag, Values};
decode(S) when ?IS_FATE_STRING(S) -> binary_to_list(S);
decode(M) when ?IS_FATE_MAP(M) ->
maps:from_list([{decode(K), decode(V)} || {K, V} <- maps:to_list(M)]).
-spec format(fate_type()) -> iolist().
format(I) when ?IS_FATE_INTEGER(I) -> integer_to_list(?MAKE_FATE_INTEGER(I));
format(?FATE_VOID) -> "void";
format(?FATE_TRUE) -> "true";
format(?FATE_FALSE) -> "false";
format(?FATE_NIL) -> "[]";
format(L) when ?IS_FATE_LIST(L) -> format_list(?FATE_LIST_VALUE(L));
format(?FATE_UNIT) -> "unit";
format(?FATE_UNIT) -> "()";
format(?FATE_TUPLE(T)) ->
"{ " ++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)] ++ "}";
format(S) when ?IS_FATE_STRING(S) -> [S];
format(?FATE_VARIANT(Size, Tag, T)) ->
"( " ++ integer_to_list(Size) ++ ", "
++ integer_to_list(Tag) ++ ", "
++ [format(E) ++ " " || E <- erlang:tuple_to_list(T)]
++ " )";
["( ", lists:join(", ", [ format(E) || E <- erlang:tuple_to_list(T)]), " )"];
format(S) when ?IS_FATE_STRING(S) -> ["\"", S, "\""];
format(?FATE_BITS(B)) when B >= 0 ->
["<", format_bits(B, "") , ">"];
format(?FATE_BITS(B)) when B < 0 ->
["!< ", format_nbits(-B-1, "") , " >"];
format(?FATE_VARIANT(Arities, Tag, T)) ->
["(| ",
lists:join("| ",
[format_arities(Arities),
integer_to_list(Tag) |
[format(make_tuple(T))]]),
" |)"];
format(M) when ?IS_FATE_MAP(M) ->
"#{ "
++ format_kvs(maps:to_list(?FATE_MAP_VALUE(M)))
++" }";
format(?FATE_ADDRESS(Address)) -> address_to_base58(Address);
["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
format(?FATE_BYTES(X)) -> ["#", base64:encode(X)];
format(?FATE_ADDRESS(X)) ->
["@", aeser_api_encoder:encode(account_pubkey, X)];
format(?FATE_CONTRACT(X)) ->
["@", aeser_api_encoder:encode(contract_pubkey, X)];
format(?FATE_ORACLE(X)) ->
["@", aeser_api_encoder:encode(oracle_pubkey, X)];
format(?FATE_ORACLE_Q(X)) ->
["@", aeser_api_encoder:encode(oracle_query_id, X)];
format(?FATE_CHANNEL(X)) ->
["@", aeser_api_encoder:encode(channel, X)];
format(?FATE_TYPEREP(X)) ->
["'", io_lib:format("~p", [X])];
format(?FATE_CONTRACT_BYTEARRAY(B)) ->
["@", aeser_api_encoder:encode(contract_bytearray, B)];
format(V) -> exit({not_a_fate_type, V}).
format_list([]) -> " ]";
format_list([E]) -> format(E) ++ " ]";
format_list([H|T]) -> format(H) ++ ", " ++ format_list(T).
format_bits(0, Acc) -> Acc;
format_bits(N, Acc) ->
Bit = $0 + (N band 1),
format_bits(N bsr 1, [Bit|Acc]).
format_kvs([]) -> "";
format_kvs([{K,V}]) -> "( " ++ format(K) ++ " => " ++ format(V) ++ " )";
format_kvs([{K,V} | Rest]) ->
"( " ++ format(K) ++ " => " ++ format(V) ++ " ), " ++ format_kvs(Rest).
format_nbits(0, Acc) -> Acc;
format_nbits(N, Acc) ->
Bit = $1 - (N band 1),
format_nbits(N bsr 1, [Bit|Acc]).
format_arities(Arities) ->
["[ ", lists:join(", ", [integer_to_list(E) || E <- Arities]), " ]"].
format_list(List) ->
["[ ", lists:join(", ", [format(E) || E <- List]), " ]"].
format_kvs(List) ->
lists:join(", ", [ [format(K), " => ", format(V)] || {K, V} <- List]).
%% -- Local base 58 library
%% Total order of FATE terms.
%% Integers < Booleans < Address < Channel < Contract < Oracle
%% < Hash < Signature < Bits < String < Tuple < Map < List < Variant
%% < Oracle query < FATE code
-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).
-define(ORD_CONTRACT_BYTEARRAY , 14).
base58char(Char) ->
binary:at(<<"123456789ABCDEFGHJKLMNPQRSTUVWXYZ"
"abcdefghijkmnopqrstuvwxyz">>, Char).
char_to_base58(C) ->
binary:at(<<0,1,2,3,4,5,6,7,8,0,0,0,0,0,0,0,9,10,11,12,13,14,15,16,0,17,
18,19,20,21,0,22,23,24,25,26,27,28,29,30,31,32,0,0,0,0,0,0,
33,34,35,36,37,38,39,40,41,42,43,0,44,45,46,47,48,49,50,51,
52,53,54,55,56,57>>, C-$1).
-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;
ordinal(T) when ?IS_FATE_CONTRACT_BYTEARRAY(T) -> ?ORD_CONTRACT_BYTEARRAY.
base58_to_integer(C, []) -> C;
base58_to_integer(C, [X | Xs]) ->
base58_to_integer(C * 58 + char_to_base58(X), Xs).
base58_to_integer([]) -> error;
base58_to_integer([Char]) -> char_to_base58(Char);
base58_to_integer([Char | Str]) ->
base58_to_integer(char_to_base58(Char), Str).
-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.
base58_to_address(Base58) ->
I = base58_to_integer(Base58),
Bin = <<I:256>>,
Bin.
%% 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_TUPLE, ?FATE_TUPLE(A), ?FATE_TUPLE(B)) ->
SizeA = tuple_size(A),
SizeB = tuple_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])) ->
if A == B -> lt(RA, RB);
true -> lt(A, B)
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 ->
% Compare element by element consistent with Erlang compare
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(?ORD_ADDRESS, ?FATE_ADDRESS(A), ?FATE_ADDRESS(B)) ->
A < B;
lt(?ORD_CHANNEL, ?FATE_CHANNEL(A), ?FATE_CHANNEL(B)) ->
A < B;
lt(?ORD_CONTRACT, ?FATE_CONTRACT(A), ?FATE_CONTRACT(B)) ->
A < B;
lt(?ORD_ORACLE, ?FATE_ORACLE(A), ?FATE_ORACLE(B)) ->
A < B;
lt(?ORD_ORACLE_Q, ?FATE_ORACLE_Q(A), ?FATE_ORACLE_Q(B)) ->
A < B;
lt(?ORD_STRING, ?FATE_STRING(A), ?FATE_STRING(B)) ->
compare_bytes(A, B);
lt(?ORD_BYTES, ?FATE_BYTES(A), ?FATE_BYTES(B)) ->
compare_bytes(A, B);
lt(?ORD_CONTRACT_BYTEARRAY, ?FATE_CONTRACT_BYTEARRAY(A), ?FATE_CONTRACT_BYTEARRAY(B)) ->
compare_bytes(A, B).
address_to_base58(<<A:256>>) ->
integer_to_base58(A).
% Shorter comes first
% On same length compare by first different bit
compare_bytes(A, B) ->
SizeA = byte_size(A),
SizeB = byte_size(B),
case SizeA - SizeB of
0 -> A < B;
N -> N < 0
end.
integer_to_base58(0) -> <<"1">>;
integer_to_base58(Integer) ->
Base58String = integer_to_base58(Integer, []),
list_to_binary(Base58String).
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.
integer_to_base58(0, Acc) -> Acc;
integer_to_base58(Integer, Acc) ->
Quot = Integer div 58,
Rem = Integer rem 58,
integer_to_base58(Quot, [base58char(Rem)|Acc]).
+342 -92
View File
@@ -1,21 +1,29 @@
%% Fate data (and instruction) serialization.
%%
%% The FATE serialization has to fullfill the following properties:
%% * There has to be 1 and only 1 byte sequence
%% representing each unique value in FATE.
%% * 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 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
%% Assuming
%% S is seralize/1 (fate_type() -> binary())
%% D is deserialize/1 (binary) -> fate_type())
%% V, V1, V2 are of the type fate_type()
%% B is of the type binary()
%% Then
%% The FATE serialization has to fullfill the following properties:
%% * For each value (V) in FATE there has to be a bytecode sequence (B)
%% representing that value.
%% * 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
%%
%% The deserialization function should fullfill the following:
%% * A valid byte sequence should be deserialized to a valid FATE value.
%% * Any other argument, not representing a valid byte sequence should
%% The deserialization function (D) should fullfill the following:
%% * A valid byte sequence should be deserialized to a valid FATE value.
%% * Any other argument, not representing a valid byte sequence should
%% throw an exception
%% The following equalities should hold:
%% * D(S(V)) == V
%% * if V1 == V2 then S(V1) == S(V2)
%%
%%
%% History
%% * First draft of FATE serialization encoding/decoding.
@@ -28,8 +36,6 @@
%% TODO:
%% * Make the code production ready.
%% (add tests, document exported functions).
%% * Handle Variant types better.
%% * Handle type representations.
%% * Handle instructions.
%%
%% ------------------------------------------------------------------------
@@ -37,9 +43,15 @@
-export([ deserialize/1
, deserialize_one/1
, deserialize_type/1
, serialize/1
, serialize_type/1
]).
-ifdef(EQC).
-export([sort/1]).
-endif.
-include("aeb_fate_data.hrl").
%% Definition of tag scheme.
@@ -47,38 +59,76 @@
-define(SMALL_INT , 2#0). %% sxxxxxx 0 - 6 bit integer with sign bit
%% 1 Set below
-define(LONG_STRING , 2#00000001). %% 000000 01 - RLP encoded array, size >= 64
-define(SHORT_STRING , 2#01). %% xxxxxx 01 - [bytes], 0 < xxxxxx:size < 64
-define(LONG_STRING , 2#00000001). %% 000000 01 | RLP encoded array - when size >= 64
-define(SHORT_STRING , 2#01). %% xxxxxx 01 | [bytes] - when 0 < xxxxxx:size < 64
%% 11 Set below
-define(SHORT_LIST , 2#0011). %% xxxx 0011 - [encoded elements], 0 < length < 16
%% xxxx 0111 - FREE (For typedefs in future)
-define(LONG_TUPLE , 2#00001011). %% 0000 1011 - RLP encoded (size - 16) + [encoded elements],
-define(SHORT_TUPLE , 2#1011). %% xxxx 1011 - [encoded elements], 0 < size < 16
-define(SHORT_LIST , 2#0011). %% xxxx 0011 | [encoded elements] when 0 < length < 16
%% 0111 Set below
-define(TYPE_INTEGER , 2#00000111). %% 0000 0111 - Integer typedef
-define(TYPE_BOOLEAN , 2#00010111). %% 0001 0111 - Boolean typedef
-define(TYPE_LIST , 2#00100111). %% 0010 0111 | Type
-define(TYPE_TUPLE , 2#00110111). %% 0011 0111 | Size | [Element Types]
-define(TYPE_OBJECT , 2#01000111). %% 0100 0111 | ObjectType
-define(TYPE_BITS , 2#01010111). %% 0101 0111 - Bits typedef
-define(TYPE_MAP , 2#01100111). %% 0110 0111 | Type | Type
-define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef
-define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type]
-define(TYPE_BYTES , 2#10010111). %% 1001 0111 - Bytes typedef
-define(TYPE_CONTRACT_BYTEARRAY,2#10100111). %% 1010 0111 - Fate code typedef
%% 1011 0111
%% 1100 0111
%% 1101 0111
-define(TYPE_VAR , 2#11100111). %% 1110 0111 | Id when 0 =< Id < 256 (type variable)
-define(TYPE_ANY , 2#11110111). %% 1111 0111 - Any typedef
-define(LONG_TUPLE , 2#00001011). %% 0000 1011 | RLP encoded (size - 16) | [encoded elements],
-define(SHORT_TUPLE , 2#1011). %% xxxx 1011 | [encoded elements] when 0 < size < 16
%% 1111 Set below
-define(LONG_LIST , 2#00011111). %% 0001 1111 - RLP encoded (length - 16) + [Elements]
-define(MAP , 2#00101111). %% 0010 1111 - RLP encoded size + [encoded key, encoded value]
-define(LONG_LIST , 2#00011111). %% 0001 1111 | RLP encoded (length - 16) | [encoded lements]
-define(MAP , 2#00101111). %% 0010 1111 | RLP encoded size | [encoded key, encoded value]
-define(EMPTY_TUPLE , 2#00111111). %% 0011 1111
-define(POS_BITS , 2#01001111). %% 0100 1111 - RLP encoded integer (to be interpreted as bitfield)
-define(POS_BITS , 2#01001111). %% 0100 1111 | RLP encoded integer (to be interpreted as bitfield)
-define(EMPTY_STRING , 2#01011111). %% 0101 1111
-define(POS_BIG_INT , 2#01101111). %% 0110 1111 - RLP encoded (integer - 64)
-define(POS_BIG_INT , 2#01101111). %% 0110 1111 | RLP encoded (integer - 64)
-define(FALSE , 2#01111111). %% 0111 1111
%% %% 1000 1111 - FREE (Possibly for bytecode in the future.)
-define(ADDRESS , 2#10011111). %% 1001 1111 - [32 bytes]
-define(VARIANT , 2#10101111). %% 1010 1111 - encoded size + encoded tag + encoded values
-define(NIL , 2#10111111). %% 1011 1111 - Empty list
-define(NEG_BITS , 2#11001111). %% 1100 1111 - RLP encoded integer (infinite 1:s bitfield)
-define(
CONTRACT_BYTEARRAY, 2#10001111). %% 1000 1111
-define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array
-define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values]
-define(MAP_ID , 2#10111111). %% 1011 1111 | RLP encoded integer (store map id)
-define(NEG_BITS , 2#11001111). %% 1100 1111 | RLP encoded integer (infinite 1:s bitfield)
-define(EMPTY_MAP , 2#11011111). %% 1101 1111
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 - RLP encoded (integer - 64)
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 | RLP encoded (integer - 64)
-define(TRUE , 2#11111111). %% 1111 1111
-define(SHORT_TUPLE_SIZE, 16).
-define(SHORT_LIST_SIZE , 16).
-define(SMALL_INT_SIZE , 64).
-define(SHORT_TUPLE_SIZE, 16).
-define(SHORT_LIST_SIZE, 16).
-define(SMALL_INT_SIZE, 64).
-define(SHORT_STRING_SIZE, 64).
-define(POS_SIGN, 0).
-define(NEG_SIGN, 1).
%% Object types
-define(OTYPE_ADDRESS, 0).
-define(OTYPE_BYTES, 1).
-define(OTYPE_CONTRACT, 2).
-define(OTYPE_ORACLE, 3).
-define(OTYPE_ORACLE_Q, 4).
-define(OTYPE_CHANNEL, 5).
-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_BYTES orelse
X =:= ?TYPE_MAP orelse
X =:= ?TYPE_STRING orelse
X =:= ?TYPE_VARIANT orelse
X =:= ?TYPE_CONTRACT_BYTEARRAY)).
%% --------------------------------------------------
%% Serialize
@@ -88,9 +138,7 @@
-spec serialize(aeb_fate_data:fate_type()) -> binary().
serialize(?FATE_TRUE) -> <<?TRUE>>;
serialize(?FATE_FALSE) -> <<?FALSE>>;
serialize(?FATE_NIL) -> <<?NIL>>; %% ! 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(I) when ?IS_FATE_INTEGER(I) -> serialize_integer(I);
serialize(?FATE_BITS(Bits)) when is_integer(Bits) -> serialize_bits(Bits);
@@ -104,9 +152,21 @@ serialize(String) when ?IS_FATE_STRING(String),
?FATE_STRING_SIZE(String) > 0,
?FATE_STRING_SIZE(String) >= ?SHORT_STRING_SIZE ->
Bytes = ?FATE_STRING_VALUE(String),
<<?LONG_STRING, (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) ->
<<?ADDRESS, (aeser_rlp:encode(Address))/binary>>;
<<?OBJECT, ?OTYPE_ADDRESS, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CONTRACT(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CONTRACT, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE_Q(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE_Q, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_CHANNEL(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CHANNEL, (aeser_rlp:encode(Address))/binary>>;
serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
S = size(T),
L = tuple_to_list(T),
@@ -114,42 +174,158 @@ serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
if S < ?SHORT_TUPLE_SIZE ->
<<S:4, ?SHORT_TUPLE:4, Rest/binary>>;
true ->
Size = rlp_integer(S - ?SHORT_TUPLE_SIZE),
Size = rlp_encode_int(S - ?SHORT_TUPLE_SIZE),
<<?LONG_TUPLE:8, Size/binary, Rest/binary>>
end;
serialize(L) when ?IS_FATE_LIST(L) ->
[_E|_] = List = ?FATE_LIST_VALUE(L),
List = ?FATE_LIST_VALUE(L),
S = length(List),
Rest = << <<(serialize(El))/binary>> || El <- List >>,
if S < ?SHORT_LIST_SIZE ->
<<S:4, ?SHORT_LIST:4, Rest/binary>>;
true ->
Val = rlp_integer(S - ?SHORT_LIST_SIZE),
Val = rlp_encode_int(S - ?SHORT_LIST_SIZE),
<<?LONG_LIST, Val/binary, Rest/binary>>
end;
serialize(Map) when ?IS_FATE_MAP(Map) ->
L = [{_K,_V}|_] = maps:to_list(?FATE_MAP_VALUE(Map)),
L = maps:to_list(?FATE_MAP_VALUE(Map)),
Size = length(L),
%% TODO: check all K same type, and all V same type
%% 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,
(rlp_integer(Size))/binary,
(rlp_encode_int(Size))/binary,
(Elements)/binary>>;
serialize(?FATE_VARIANT(Size, Tag, Values)) when 0 =< Size
, Size < 256
, 0 =< Tag
, Tag < Size ->
<<?VARIANT, Size:8, Tag:8,
(serialize(?FATE_TUPLE(Values)))/binary
>>.
serialize(?FATE_STORE_MAP(Cache, Id)) when Cache =:= #{} ->
%% We should never get to serialization without having flushed the caches.
<<?MAP_ID, (rlp_encode_int(Id))/binary>>;
serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities),
if is_integer(Tag)
, 0 =< Tag
, Tag < Size
, is_tuple(Values) ->
Arity = lists:nth(Tag+1, Arities),
if size(Values) =:= Arity ->
EncodedArities = aeser_rlp:encode(list_to_binary(Arities)),
<<?VARIANT,
EncodedArities/binary,
Tag:8,
(serialize(?FATE_TUPLE(Values)))/binary
>>
end
end;
serialize(?FATE_TYPEREP(T)) ->
iolist_to_binary(serialize_type(T));
serialize(?FATE_CONTRACT_BYTEARRAY(B)) ->
<<?CONTRACT_BYTEARRAY,
(serialize_integer(?FATE_CONTRACT_BYTEARRAY_SIZE(B)))/binary
, B/binary>>.
%% -----------------------------------------------------
rlp_integer(S) when S >= 0 ->
-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()].
serialize_type(integer) -> [?TYPE_INTEGER];
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({tuple, Ts}) ->
case length(Ts) of
N when N =< 255 ->
[?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]]
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(contract) -> [?TYPE_OBJECT, ?OTYPE_CONTRACT];
serialize_type(oracle) -> [?TYPE_OBJECT, ?OTYPE_ORACLE];
serialize_type(oracle_query)-> [?TYPE_OBJECT, ?OTYPE_ORACLE_Q];
serialize_type(channel) -> [?TYPE_OBJECT, ?OTYPE_CHANNEL];
serialize_type(bits) -> [?TYPE_BITS];
serialize_type({map, K, V}) -> [?TYPE_MAP
| serialize_type(K) ++ serialize_type(V)];
serialize_type(string) -> [?TYPE_STRING];
serialize_type({variant, ListOfVariants}) ->
Size = length(ListOfVariants),
if Size < 256 ->
[?TYPE_VARIANT, Size | [serialize_type(T) || T <- ListOfVariants]]
end;
serialize_type(contract_bytearray) -> [?TYPE_CONTRACT_BYTEARRAY].
-spec deserialize_type(binary()) -> {aeb_fate_data:fate_type_type(), binary()}.
deserialize_type(<<?TYPE_INTEGER, Rest/binary>>) -> {integer, Rest};
deserialize_type(<<?TYPE_BOOLEAN, Rest/binary>>) -> {boolean, Rest};
deserialize_type(<<?TYPE_ANY, Rest/binary>>) -> {any, Rest};
deserialize_type(<<?TYPE_VAR, Id, Rest/binary>>) -> {{tvar, Id}, Rest};
deserialize_type(<<?TYPE_LIST, Rest/binary>>) ->
{T, Rest2} = deserialize_type(Rest),
{{list, T}, Rest2};
deserialize_type(<<?TYPE_TUPLE, N, Rest/binary>>) ->
{Ts, Rest2} = deserialize_types(N, Rest, []),
{{tuple, Ts}, Rest2};
deserialize_type(<<?TYPE_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>>) ->
case ObjectType of
?OTYPE_ADDRESS -> {address, Rest};
?OTYPE_CONTRACT -> {contract, Rest};
?OTYPE_ORACLE -> {oracle, Rest};
?OTYPE_ORACLE_Q -> {oracle_query, Rest};
?OTYPE_CHANNEL -> {channel, Rest}
end;
deserialize_type(<<?TYPE_BITS, Rest/binary>>) -> {bits, Rest};
deserialize_type(<<?TYPE_MAP, Rest/binary>>) ->
{K, Rest2} = deserialize_type(Rest),
{V, Rest3} = deserialize_type(Rest2),
{{map, K, V}, Rest3};
deserialize_type(<<?TYPE_STRING, Rest/binary>>) ->
{string, Rest};
deserialize_type(<<?TYPE_VARIANT, Size, Rest/binary>>) ->
{Variants, Rest2} = deserialize_variants(Size, Rest, []),
{{variant, Variants}, Rest2};
deserialize_type(<<?TYPE_CONTRACT_BYTEARRAY, Rest/binary>>) -> {contract_bytearray, Rest}.
deserialize_variants(0, Rest, Variants) ->
{lists:reverse(Variants), Rest};
deserialize_variants(N, Rest, Variants) ->
{T, Rest2} = deserialize_type(Rest),
deserialize_variants(N-1, Rest2, [T|Variants]).
deserialize_types(0, Binary, Acc) ->
{lists:reverse(Acc), Binary};
deserialize_types(N, Binary, Acc) ->
{T, Rest} = deserialize_type(Binary),
deserialize_types(N-1, Rest, [T | Acc]).
%% -----------------------------------------------------
rlp_encode_int(S) when S >= 0 ->
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) ->
V = ?FATE_INTEGER_VALUE(I),
Abs = abs(V),
@@ -159,20 +335,16 @@ serialize_integer(I) when ?IS_FATE_INTEGER(I) ->
end,
if Abs < ?SMALL_INT_SIZE -> <<Sign:1, Abs:6, ?SMALL_INT:1>>;
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,
(rlp_integer(Abs - ?SMALL_INT_SIZE))/binary>>
(rlp_encode_int(Abs - ?SMALL_INT_SIZE))/binary>>
end.
serialize_bits(B) when is_integer(B) ->
Abs = abs(B),
Sign = case B < 0 of
true -> ?NEG_SIGN;
false -> ?POS_SIGN
end,
if
Sign =:= ?NEG_SIGN -> <<?NEG_BITS, (rlp_integer(Abs))/binary>>;
Sign =:= ?POS_SIGN -> <<?POS_BITS, (rlp_integer(Abs))/binary>>
B < 0 -> <<?NEG_BITS, (rlp_encode_int(Abs))/binary>>;
B >= 0 -> <<?POS_BITS, (rlp_encode_int(Abs))/binary>>
end.
-spec deserialize(binary()) -> aeb_fate_data:fate_type().
@@ -185,74 +357,118 @@ deserialize_one(B) -> deserialize2(B).
deserialize2(<<?POS_SIGN:1, I:6, ?SMALL_INT:1, Rest/binary>>) ->
{?MAKE_FATE_INTEGER(I), Rest};
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>>) ->
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
{?MAKE_FATE_INTEGER(-binary:decode_unsigned(Bint) - ?SMALL_INT_SIZE),
{Bint, Rest2} = rlp_decode_int(Rest),
{?MAKE_FATE_INTEGER(-Bint - ?SMALL_INT_SIZE),
Rest2};
deserialize2(<<?POS_BIG_INT, Rest/binary>>) ->
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
{?MAKE_FATE_INTEGER(binary:decode_unsigned(Bint) + ?SMALL_INT_SIZE),
{Bint, Rest2} = rlp_decode_int(Rest),
{?MAKE_FATE_INTEGER(Bint + ?SMALL_INT_SIZE),
Rest2};
deserialize2(<<?NEG_BITS, Rest/binary>>) ->
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
{?FATE_BITS(-binary:decode_unsigned(Bint)), Rest2};
case rlp_decode_int(Rest) of
{Pos, Rest2} when Pos > 0 ->
{?FATE_BITS(-Pos), Rest2};
{N, _} ->
error({illegal_parameter, neg_bits, N})
end;
deserialize2(<<?POS_BITS, Rest/binary>>) ->
{Bint, Rest2} = aeser_rlp:decode_one(Rest),
{?FATE_BITS(binary:decode_unsigned(Bint)), Rest2};
{Bint, Rest2} = rlp_decode_int(Rest),
{?FATE_BITS(Bint), Rest2};
deserialize2(<<?LONG_STRING, Rest/binary>>) ->
{String, Rest2} = aeser_rlp:decode_one(Rest),
{?MAKE_FATE_STRING(String), Rest2};
{S, Rest2} = deserialize_one(Rest),
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(<<?CONTRACT_BYTEARRAY, Rest/binary>>) ->
{Size, Rest2} = deserialize_one(Rest),
true = is_integer(Size) andalso Size >= 0,
FateCode = binary:part(Rest2, 0, Size),
Rest3 = binary:part(Rest2, byte_size(Rest2), - (byte_size(Rest2) - Size)),
{?MAKE_FATE_CONTRACT_BYTEARRAY(FateCode), Rest3};
deserialize2(<<S:6, ?SHORT_STRING:2, Rest/binary>>) ->
String = binary:part(Rest, 0, S),
Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)),
{?MAKE_FATE_STRING(String), Rest2};
deserialize2(<<?ADDRESS, Rest/binary>>) ->
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>>) ->
{A, Rest2} = aeser_rlp:decode_one(Rest),
{?FATE_ADDRESS(A), Rest2};
Val =
case ObjectType of
?OTYPE_ADDRESS -> ?FATE_ADDRESS(A);
?OTYPE_CONTRACT -> ?FATE_CONTRACT(A);
?OTYPE_ORACLE -> ?FATE_ORACLE(A);
?OTYPE_ORACLE_Q -> ?FATE_ORACLE_Q(A);
?OTYPE_CHANNEL -> ?FATE_CHANNEL(A)
end,
{Val, Rest2};
deserialize2(<<?TRUE, Rest/binary>>) ->
{?FATE_TRUE, Rest};
deserialize2(<<?FALSE, Rest/binary>>) ->
{?FATE_FALSE, Rest};
deserialize2(<<?NIL, Rest/binary>>) ->
{?FATE_NIL, Rest};
deserialize2(<<?EMPTY_TUPLE, Rest/binary>>) ->
{?FATE_UNIT, Rest};
deserialize2(<<?EMPTY_MAP, Rest/binary>>) ->
{?MAKE_FATE_MAP(#{}), Rest};
deserialize2(<<?EMPTY_STRING, Rest/binary>>) ->
{?FATE_EMPTY_STRING, Rest};
deserialize2(<<?LONG_TUPLE, Rest/binary>>) ->
{BSize, Rest1} = aeser_rlp:decode_one(Rest),
N = binary:decode_unsigned(BSize) + ?SHORT_TUPLE_SIZE,
{Size, Rest1} = rlp_decode_int(Rest),
N = Size + ?SHORT_TUPLE_SIZE,
{List, Rest2} = deserialize_elements(N, Rest1),
{?FATE_TUPLE(list_to_tuple(List)), Rest2};
deserialize2(<<S:4, ?SHORT_TUPLE:4, Rest/binary>>) ->
{List, Rest1} = deserialize_elements(S, Rest),
{?FATE_TUPLE(list_to_tuple(List)), Rest1};
deserialize2(<<?LONG_LIST, Rest/binary>>) ->
{BLength, Rest1} = aeser_rlp:decode_one(Rest),
Length = binary:decode_unsigned(BLength) + ?SHORT_LIST_SIZE,
{Size, Rest1} = rlp_decode_int(Rest),
Length = Size + ?SHORT_LIST_SIZE,
{List, Rest2} = deserialize_elements(Length, Rest1),
{?MAKE_FATE_LIST(List), Rest2};
deserialize2(<<S:4, ?SHORT_LIST:4, Rest/binary>>) ->
{List, Rest1} = deserialize_elements(S, Rest),
{?MAKE_FATE_LIST(List), Rest1};
deserialize2(<<?MAP, Rest/binary>>) ->
{BSize, Rest1} = aeser_rlp:decode_one(Rest),
Size = binary:decode_unsigned(BSize),
{Size, Rest1} = rlp_decode_int(Rest),
{List, Rest2} = deserialize_elements(2*Size, Rest1),
Map = insert_kv(List, #{}),
{?MAKE_FATE_MAP(Map), Rest2};
deserialize2(<<?VARIANT, Size:8, Tag:8, Rest/binary>>) ->
KVList = insert_kv(List),
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(<<?MAP_ID, Rest/binary>>) ->
{Id, Rest1} = rlp_decode_int(Rest),
{?FATE_STORE_MAP(#{}, Id), Rest1};
deserialize2(<<?VARIANT, Rest/binary>>) ->
{AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest),
Arities = binary_to_list(AritiesBin),
Size = length(Arities),
if Tag > Size -> exit({too_large_tag_in_variant, Tag, Size});
true ->
{?FATE_TUPLE(T), Rest2} = deserialize2(Rest),
{?FATE_VARIANT(Size, Tag, T), Rest2}
end.
{?FATE_TUPLE(T), Rest3} = deserialize2(Rest2),
Arity = lists:nth(Tag+1, Arities),
NumElements = size(T),
if NumElements =/= Arity ->
exit({tag_does_not_match_type_in_variant, Tag, Arity});
true ->
{?FATE_VARIANT(Arities, Tag, T), Rest3}
end
end;
deserialize2(<<TypeTag, _/binary>> = Bin) when ?IS_TYPE_TAG(TypeTag) ->
{Type, Rest} = deserialize_type(Bin),
{?FATE_TYPEREP(Type), Rest}.
insert_kv([], M) -> M;
insert_kv([K,V|R], M) -> insert_kv(R, maps:put(K, V, M)).
insert_kv([]) -> [];
insert_kv([K, V | R]) -> [{K, V} | insert_kv(R)].
deserialize_elements(0, Rest) ->
{[], Rest};
@@ -260,3 +476,37 @@ deserialize_elements(N, Es) ->
{E, Rest} = deserialize2(Es),
{Tail, Rest2} = deserialize_elements(N-1, Rest),
{[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(?FATE_STORE_MAP(_, _) = K) ->
error({map_as_key_in_map, K});
valid_key_type(K) when is_list(K) ->
lists:all(fun(E) -> valid_key_type(E) end, K);
valid_key_type(K) when is_tuple(K) ->
lists:all(fun(E) -> valid_key_type(E) end, tuple_to_list(K));
valid_key_type(_K) ->
true.
+132
View File
@@ -0,0 +1,132 @@
-module(aeb_fate_generate_docs).
-export([generate_documentation/2, generate_documentation/3]).
-export(
[ gen_protocol_opcodes_flags_and_gas/1
, gen_protocol_description_of_operations/1
, gen_protocol_opcodes/1
]).
-define(LIMA_PROTOCOL_VSN, 4).
-define(IRIS_PROTOCOL_VSN, 5).
generate_documentation(Filename, Fields) ->
generate_documentation(Filename, Fields, fun(_) -> true end).
generate_documentation(Filename, Fields, Filter) when is_function(Filter, 1) ->
{ok, File} = file:open(Filename, [write, {encoding, utf8}]),
Header =
lists:flatten(
"|" ++ [" " ++ header_name(F) ++ " |" || F <- Fields] ++ "\n"
),
Separator =
lists:flatten(
"|" ++ [" " ++ ["-" || _ <- header_name(F)] ++ " |" || F <- Fields] ++ "\n"
),
Instructions =
lists:flatten(
[gen_doc_for_op(Op, Fields)
++ "\n" || Op <- aeb_fate_generate_ops:get_ops(), Filter(Op)]),
io:format(File, "~ts~ts~ts\n", [Header, Separator, Instructions]),
file:close(File).
header_name(opname) ->
"Name";
header_name(opcode) ->
"Opcode";
header_name(arity) ->
"Arity";
header_name(end_bb) ->
"Ends basic block";
header_name(in_auth) ->
"Allowed in auth";
header_name(offchain) ->
"Allowed offchain";
header_name(format) ->
"Args";
header_name(doc) ->
"Description";
header_name(gas) ->
"Gas cost";
header_name(arg_types) ->
"Arg types";
header_name(res_type) ->
"Res type".
gen_doc_for_op(#{ opname := OpName
, opcode := OpCode
, arity := Arity
, end_bb := EndBB
, in_auth := InAuth
, offchain := AllowedOffchain
, format := FateFormat
, doc := Doc
, gas := Gas
, arg_types := ArgTypes
, res_type := ResType
}, Fields) ->
"| " ++
string:join(
[ case Field of
opname -> io_lib:format("`~s`", [OpName]);
opcode -> io_lib:format("0x~.16b", [OpCode]);
arity -> io_lib:format("~p", [Arity]);
end_bb -> io_lib:format("~p", [EndBB]);
in_auth -> io_lib:format("~p", [InAuth]);
offchain -> io_lib:format("~p", [AllowedOffchain]);
format ->
case FateFormat of
[] -> "";
_ -> lists:join(
" ",
[format_arg_doc(A) ||
A <-
lists:zip(FateFormat,
lists:seq(0,length(FateFormat)-1))])
end;
doc -> Doc;
gas when is_integer(Gas) -> io_lib:format("~p", [Gas]);
gas when is_list(Gas) ->
lists:flatten(
string:join(
[ io_lib:format(
"~p (~s)",
[GasVal, protocol_name(Prot)]
)
|| {Prot, GasVal} <- Gas
], ", "));
arg_types -> io_lib:format("~p", [ArgTypes]);
res_type -> io_lib:format("~p", [ResType])
end
|| Field <- Fields
],
" | ") ++ " |".
protocol_name(?LIMA_PROTOCOL_VSN) ->
"lima";
protocol_name(?IRIS_PROTOCOL_VSN) ->
"iris".
format_arg_doc({a, N}) -> io_lib:format("Arg~w", [N]);
format_arg_doc({is,_N}) -> "Identifier";
format_arg_doc({ii,_N}) -> "Integer";
format_arg_doc({li,_N}) -> "[Integers]";
format_arg_doc({t,_N}) -> "Type".
%% --- protocol documentation ---
gen_protocol_description_of_operations(Filename) ->
generate_documentation(
Filename, [opname, format, doc, arg_types, res_type]
).
gen_protocol_opcodes_flags_and_gas(Filename) ->
generate_documentation(
Filename, [opcode, opname, end_bb, in_auth, offchain, gas]
).
gen_protocol_opcodes(Filename) ->
generate_documentation(
Filename, [opcode, opname]
).
+789
View File
@@ -0,0 +1,789 @@
-module(aeb_fate_generate_ops).
-export([ gen_and_halt/1
, generate/0
, get_ops/0
, test_asm_generator/1 ]).
gen_and_halt([SrcDirArg, IncludeDirArg]) ->
generate(atom_to_list(SrcDirArg),
atom_to_list(IncludeDirArg)),
halt().
generate() -> generate("src/", "include/").
get_ops() -> gen(ops_defs()).
generate(Src, Include) ->
check_defs(ops_defs()),
Ops = get_ops(),
%% io:format("ops: ~p\n", [Ops]),
HrlFile = Include ++ "aeb_fate_opcodes.hrl",
generate_header_file(HrlFile, Ops),
generate_opcodes_ops(aeb_fate_opcodes, HrlFile, Src, Ops),
generate_code_ops(aeb_fate_ops, Src, Ops),
generate_scanner("aeb_fate_asm_scan.template", "aeb_fate_asm_scan.xrl", 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.
-define(LIMA_PROTOCOL_VSN, 4).
-define(IRIS_PROTOCOL_VSN, 5).
-define(GAS(A), A).
-define(GAS_IRIS(A, B), [{?IRIS_PROTOCOL_VSN, B}, {?LIMA_PROTOCOL_VSN, A}]).
ops_defs() ->
%% Opname, Opcode, end_bb, in_auth,offchain, gas, format, Constructor, ArgType, ResType, Documentation
[ { 'RETURN', 16#00, true, true, true, ?GAS(10), [], 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, true, true, true, ?GAS(10), [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, true, true, true, ?GAS(10), [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, true, false, true, ?GAS(100), [a,is,a,a,a], call_r, {contract, string, typerep, typerep, integer}, any, "Remote call to contract Arg0 and function Arg1 of type Arg2 => Arg3 with value Arg4. The types of the arguments has to match the argument types of the function."}
, { 'CALL_T', 16#04, true, true, true, ?GAS(10), [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_GR', 16#05, true, false, true, ?GAS(100), [a,is,a,a,a,a], call_gr, {contract, string, typerep, typerep, integer, integer}, any, "Remote call with gas cap in Arg4. Otherwise as CALL_R."}
, { 'JUMP', 16#06, true, true, true, ?GAS(10), [ii], jump, {integer}, none, "Jump to a basic block. The basic block has to exist in the current function."}
, { 'JUMPIF', 16#07, true, true, true, ?GAS(10), [a,ii], jumpif, {boolean, integer}, none, "Conditional jump to a basic block. If Arg0 then jump to Arg1."}
, { 'SWITCH_V2', 16#08, true, true, true, ?GAS(10), [a,ii,ii], switch, {variant, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_V3', 16#09, true, true, true, ?GAS(10), [a,ii,ii,ii], switch, {variant, integer, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_VN', 16#0a, true, true, true, ?GAS(10), [a, li], switch, {variant, {list, integer}}, none, "Conditional jump to a basic block on variant tag."}
, { 'CALL_VALUE', 16#0b, false, true, true, ?GAS(10), [a], call_value, {}, integer, "The value sent in the current remote call."}
, { 'PUSH', 16#0c, false, true, true, ?GAS(10), [a], push, {any}, any, "Push argument to stack."}
, { 'DUPA', 16#0d, false, true, true, ?GAS(10), [], dup, {any}, any, "Duplicate top of stack."}
, { 'DUP', 16#0e, false, true, true, ?GAS(10), [a], dup, {any}, any, "push Arg0 stack pos on top of stack."}
, { 'POP', 16#0f, false, true, true, ?GAS(10), [a], pop, {integer}, integer, "Arg0 := top of stack."}
, { 'INCA', 16#10, false, true, true, ?GAS(10), [], inc, {integer}, integer, "Increment accumulator."}
, { 'INC', 16#11, false, true, true, ?GAS(10), [a], inc, {integer}, integer, "Increment argument."}
, { 'DECA', 16#12, false, true, true, ?GAS(10), [], dec, {integer}, integer, "Decrement accumulator."}
, { 'DEC', 16#13, false, true, true, ?GAS(10), [a], dec, {integer}, integer, "Decrement argument."}
, { 'ADD', 16#14, false, true, true, ?GAS(10), [a,a,a], add, {integer, integer}, integer, "Arg0 := Arg1 + Arg2."}
, { 'SUB', 16#15, false, true, true, ?GAS(10), [a,a,a], sub, {integer, integer}, integer, "Arg0 := Arg1 - Arg2."}
, { 'MUL', 16#16, false, true, true, ?GAS(10), [a,a,a], mul, {integer, integer}, integer, "Arg0 := Arg1 * Arg2."}
, { 'DIV', 16#17, false, true, true, ?GAS(10), [a,a,a], divide, {integer, integer}, integer, "Arg0 := Arg1 / Arg2."}
, { 'MOD', 16#18, false, true, true, ?GAS(10), [a,a,a], modulo, {integer, integer}, integer, "Arg0 := Arg1 mod Arg2."}
, { 'POW', 16#19, false, true, true, ?GAS(10), [a,a,a], pow, {integer, integer}, integer, "Arg0 := Arg1 ^ Arg2."}
, { 'STORE', 16#1a, false, true, true, ?GAS(10), [a,a], store, {any}, any, "Arg0 := Arg1."}
, { 'SHA3', 16#1b, false, true, true, ?GAS(100), [a,a], sha3, {any}, hash, "Arg0 := sha3(Arg1)."}
, { 'SHA256', 16#1c, false, true, true, ?GAS(100), [a,a], sha256, {any}, hash, "Arg0 := sha256(Arg1)."}
, { 'BLAKE2B', 16#1d, false, true, true, ?GAS(100), [a,a], blake2b, {any}, hash, "Arg0 := blake2b(Arg1)."}
, { 'LT', 16#1e, false, true, true, ?GAS(10), [a,a,a], lt, {integer, integer}, boolean, "Arg0 := Arg1 < Arg2."}
, { 'GT', 16#1f, false, true, true, ?GAS(10), [a,a,a], gt, {integer, integer}, boolean, "Arg0 := Arg1 > Arg2."}
, { 'EQ', 16#20, false, true, true, ?GAS(10), [a,a,a], eq, {integer, integer}, boolean, "Arg0 := Arg1 = Arg2."}
, { 'ELT', 16#21, false, true, true, ?GAS(10), [a,a,a], elt, {integer, integer}, boolean, "Arg0 := Arg1 =< Arg2."}
, { 'EGT', 16#22, false, true, true, ?GAS(10), [a,a,a], egt, {integer, integer}, boolean, "Arg0 := Arg1 >= Arg2."}
, { 'NEQ', 16#23, false, true, true, ?GAS(10), [a,a,a], neq, {integer, integer}, boolean, "Arg0 := Arg1 /= Arg2."}
, { 'AND', 16#24, false, true, true, ?GAS(10), [a,a,a], and_op, {boolean, boolean}, boolean, "Arg0 := Arg1 and Arg2."}
, { 'OR', 16#25, false, true, true, ?GAS(10), [a,a,a], or_op, {boolean, boolean}, boolean, "Arg0 := Arg1 or Arg2."}
, { 'NOT', 16#26, false, true, true, ?GAS(10), [a,a], not_op, {boolean}, boolean, "Arg0 := not Arg1."}
, { 'TUPLE', 16#27, false, true, true, ?GAS(10), [a,ii], tuple, {integer}, tuple, "Arg0 := tuple of size = Arg1. Elements on stack."}
, { 'ELEMENT', 16#28, false, true, true, ?GAS(10), [a,a,a], element_op, {integer, tuple}, any, "Arg1 := element(Arg2, Arg3)."}
, { 'SETELEMENT', 16#29, false, true, true, ?GAS(10), [a,a,a,a], setelement, {integer, tuple, any}, tuple, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
, { 'MAP_EMPTY', 16#2a, false, true, true, ?GAS(10), [a], map_empty, {}, map, "Arg0 := #{}."}
, { 'MAP_LOOKUP', 16#2b, false, true, true, ?GAS(10), [a,a,a], map_lookup, {map, any}, any, "Arg0 := lookup key Arg2 in map Arg1."}
, { 'MAP_LOOKUPD', 16#2c, false, true, true, ?GAS(10), [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."}
, { 'MAP_UPDATE', 16#2d, false, true, true, ?GAS(10), [a,a,a,a], map_update, {map, any, any}, map, "Arg0 := update key Arg2 in map Arg1 with value Arg3."}
, { 'MAP_DELETE', 16#2e, false, true, true, ?GAS(10), [a,a,a], map_delete, {map, any}, map, "Arg0 := delete key Arg2 from map Arg1."}
, { 'MAP_MEMBER', 16#2f, false, true, true, ?GAS(10), [a,a,a], map_member, {map, any}, boolean, "Arg0 := true if key Arg2 is in map Arg1."}
, { 'MAP_FROM_LIST', 16#30, false, true, true, ?GAS(10), [a,a], map_from_list, {{list, {tuple, [any, any]}}}, map, "Arg0 := make a map from (key, value) list in Arg1."}
, { 'MAP_SIZE', 16#31, false, true, true, ?GAS(10), [a,a], map_size_, {map}, integer, "Arg0 := The size of the map Arg1."}
, { 'MAP_TO_LIST', 16#32, false, true, true, ?GAS(10), [a,a], map_to_list, {map}, list, "Arg0 := The tuple list representation of the map Arg1."}
, { 'IS_NIL', 16#33, false, true, true, ?GAS(10), [a,a], is_nil, {list}, boolean, "Arg0 := true if Arg1 == []."}
, { 'CONS', 16#34, false, true, true, ?GAS(10), [a,a,a], cons, {any, list}, list, "Arg0 := [Arg1|Arg2]."}
, { 'HD', 16#35, false, true, true, ?GAS(10), [a,a], hd, {list}, any, "Arg0 := head of list Arg1."}
, { 'TL', 16#36, false, true, true, ?GAS(10), [a,a], tl, {list}, list, "Arg0 := tail of list Arg1."}
, { 'LENGTH', 16#37, false, true, true, ?GAS(10), [a,a], length, {list}, integer, "Arg0 := length of list Arg1."}
, { 'NIL', 16#38, false, true, true, ?GAS(10), [a], nil, {}, list, "Arg0 := []."}
, { 'APPEND', 16#39, false, true, true, ?GAS(10), [a,a,a], append, {list, list}, list, "Arg0 := Arg1 ++ Arg2."}
, { 'STR_JOIN', 16#3a, false, true, true, ?GAS(10), [a,a,a], str_join, {string, string}, string, "Arg0 := string Arg1 followed by string Arg2."}
, { 'INT_TO_STR', 16#3b, false, true, true, ?GAS(100), [a,a], int_to_str, {integer}, string, "Arg0 := turn integer Arg1 into a string."}
, { 'ADDR_TO_STR', 16#3c, false, true, true, ?GAS(100), [a,a], addr_to_str, {address}, string, "Arg0 := turn address Arg1 into a string."}
, { 'STR_REVERSE', 16#3d, false, true, true, ?GAS(100), [a,a], str_reverse, {string}, string, "Arg0 := the reverse of string Arg1."}
, { 'STR_LENGTH', 16#3e, false, true, true, ?GAS(10), [a,a], str_length, {string}, integer, "Arg0 := The length of the string Arg1."}
, { 'BYTES_TO_INT', 16#3f, false, true, true, ?GAS(10), [a,a], bytes_to_int, {bytes}, integer, "Arg0 := bytes_to_int(Arg1)"}
, { 'BYTES_TO_STR', 16#40, false, true, true, ?GAS(100), [a,a], bytes_to_str, {bytes}, string, "Arg0 := bytes_to_str(Arg1)"}
, { 'BYTES_CONCAT', 16#41, false, true, true, ?GAS(10), [a,a,a], bytes_concat, {bytes, bytes}, bytes, "Arg0 := bytes_concat(Arg1, Arg2)"}
, { 'BYTES_SPLIT', 16#42, false, true, true, ?GAS(10), [a,a,a], bytes_split, {bytes, integer}, bytes, "Arg0 := bytes_split(Arg2, Arg1), where Arg2 is the length of the first chunk."}
, { 'INT_TO_ADDR', 16#43, false, true, true, ?GAS(10), [a,a], int_to_addr, {integer}, address, "Arg0 := turn integer Arg1 into an address."}
, { 'VARIANT', 16#44, false, true, true, ?GAS(10), [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."}
, { 'VARIANT_TEST', 16#45, false, true, true, ?GAS(10), [a,a,a], variant_test, {variant, integer}, boolean, "Arg0 := true if variant Arg1 has the tag Arg2."}
, { 'VARIANT_ELEMENT', 16#46, false, true, true, ?GAS(10), [a,a,a], variant_element, {variant, integer}, any, "Arg0 := element number Arg2 from variant Arg1."}
, { 'BITS_NONEA', 16#47, false, true, true, ?GAS(10), [], bits_none, {}, bits, "push an empty bitmap on the stack."}
, { 'BITS_NONE', 16#48, false, true, true, ?GAS(10), [a], bits_none, {}, bits, "Arg0 := empty bitmap."}
, { 'BITS_ALLA', 16#49, false, true, true, ?GAS(10), [], bits_all, {}, bits, "push a full bitmap on the stack."}
, { 'BITS_ALL', 16#4a, false, true, true, ?GAS(10), [a], bits_all, {}, bits, "Arg0 := full bitmap."}
, { 'BITS_ALL_N', 16#4b, false, true, true, ?GAS(10), [a,a], bits_all_n, {integer}, bits, "Arg0 := bitmap with Arg1 bits set."}
, { 'BITS_SET', 16#4c, false, true, true, ?GAS(10), [a,a,a], bits_set, {bits, integer}, bits, "Arg0 := set bit Arg2 of bitmap Arg1."}
, { 'BITS_CLEAR', 16#4d, false, true, true, ?GAS(10), [a,a,a], bits_clear, {bits, integer}, bits, "Arg0 := clear bit Arg2 of bitmap Arg1."}
, { 'BITS_TEST', 16#4e, false, true, true, ?GAS(10), [a,a,a], bits_test, {bits, integer}, boolean, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."}
, { 'BITS_SUM', 16#4f, false, true, true, ?GAS(10), [a,a], bits_sum, {bits}, integer, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."}
, { 'BITS_OR', 16#50, false, true, true, ?GAS(10), [a,a,a], bits_or, {bits, bits}, bits, "Arg0 := Arg1 v Arg2."}
, { 'BITS_AND', 16#51, false, true, true, ?GAS(10), [a,a,a], bits_and, {bits, bits}, bits, "Arg0 := Arg1 ^ Arg2."}
, { 'BITS_DIFF', 16#52, false, true, true, ?GAS(10), [a,a,a], bits_diff, {bits, bits}, bits, "Arg0 := Arg1 - Arg2."}
, { 'BALANCE', 16#53, false, true, true, ?GAS(10), [a], balance, {}, integer, "Arg0 := The current contract balance."}
, { 'ORIGIN', 16#54, false, true, true, ?GAS(10), [a], origin, {}, address, "Arg0 := Address of contract called by the call transaction."}
, { 'CALLER', 16#55, false, true, true, ?GAS(10), [a], caller, {}, address, "Arg0 := The address that signed the call transaction."}
, { 'BLOCKHASH', 16#56, false, true, true, ?GAS_IRIS(10, 1000), [a,a], blockhash, {integer}, variant, "Arg0 := The blockhash at height."}
, { 'BENEFICIARY', 16#57, false, true, true, ?GAS(10), [a], beneficiary, {}, address, "Arg0 := The address of the current beneficiary."}
, { 'TIMESTAMP', 16#58, false, true, true, ?GAS(10), [a], timestamp, {}, integer, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
, { 'GENERATION', 16#59, false, true, true, ?GAS(10), [a], generation, {}, integer, "Arg0 := The block height of the cureent generation."}
, { 'MICROBLOCK', 16#5a, false, true, true, ?GAS(10), [a], microblock, {}, integer, "Arg0 := The current micro block number."}
, { 'DIFFICULTY', 16#5b, false, true, true, ?GAS(10), [a], difficulty, {}, integer, "Arg0 := The current difficulty."}
, { 'GASLIMIT', 16#5c, false, true, true, ?GAS(10), [a], gaslimit, {}, integer, "Arg0 := The current gaslimit."}
, { 'GAS', 16#5d, false, true, true, ?GAS(10), [a], gas, {}, integer, "Arg0 := The amount of gas left."}
, { 'ADDRESS', 16#5e, false, true, true, ?GAS(10), [a], address, {}, address, "Arg0 := The current contract address."}
, { 'GASPRICE', 16#5f, false, true, true, ?GAS(10), [a], gasprice, {}, integer, "Arg0 := The current gas price."}
, { 'LOG0', 16#60, false, true, true, ?GAS(1000), [a], log, {string}, none, "Create a log message in the call object."}
, { 'LOG1', 16#61, false, true, true, ?GAS(1100), [a,a], log, {integer, string}, none, "Create a log message with one topic in the call object."}
, { 'LOG2', 16#62, false, true, true, ?GAS(1200), [a,a,a], log, {integer, integer, string}, none, "Create a log message with two topics in the call object."}
, { 'LOG3', 16#63, false, true, true, ?GAS(1300), [a,a,a,a], log, {integer, integer, integer, string}, none, "Create a log message with three topics in the call object."}
, { 'LOG4', 16#64, false, true, true, ?GAS(1400), [a,a,a,a,a], log, {integer, integer, integer, integer, string}, none, "Create a log message with four topics in the call object."}
%% Transaction ops
, { 'SPEND', 16#65, false, false, true, ?GAS_IRIS(100, 5000), [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#66, false, false, false, ?GAS_IRIS(100, 10000), [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."}
, { 'ORACLE_QUERY', 16#67, false, false, false, ?GAS_IRIS(100, 10000), [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_RESPOND', 16#68, false, false, false, ?GAS_IRIS(100, 10000), [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_EXTEND', 16#69, false, false, false, ?GAS_IRIS(100, 10000), [a,a,a], oracle_extend, {signature, oracle, variant}, none, "Extend oracle in Arg1 with TTL in Arg2. Arg0 contains delegation signature."}
, { 'ORACLE_GET_ANSWER', 16#6a, false, false, true, ?GAS_IRIS(100, 2000), [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_QUESTION', 16#6b, false, false, true, ?GAS_IRIS(100, 2000), [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_QUERY_FEE', 16#6c, false, false, true, ?GAS_IRIS(100, 2000), [a,a], oracle_query_fee, {oracle}, integer, "Arg0 := query fee for oracle Arg1"}
, { 'AENS_RESOLVE', 16#6d, false, false, true, ?GAS_IRIS(100, 2000), [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_PRECLAIM', 16#6e, false, false, false, ?GAS_IRIS(100, 10000), [a,a,a], aens_preclaim, {signature, address, hash}, none, "Preclaim the hash in Arg2 for address in Arg1. Arg0 contains delegation signature."}
, { 'AENS_CLAIM', 16#6f, false, false, false, ?GAS_IRIS(100, 10000), [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_UPDATE', 16#70, false, false, false, ?GAS_IRIS(100, 10000), [a,a,a,a,a,a], aens_update, {signature, address, string, variant, variant, variant}, none, "Updates name in Arg2 for address in Arg1. Arg3 contains optional ttl (of type Chain.ttl), Arg4 contains optional client_ttl (of type int), Arg5 contains optional pointers (of type map(string, pointee))"}
, { 'AENS_TRANSFER', 16#71, false, false, false, ?GAS_IRIS(100, 10000), [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_REVOKE', 16#72, false, false, false, ?GAS_IRIS(100, 10000), [a,a,a], aens_revoke, {signature, address, string}, none, "Revoke the name in Arg2 from owner Arg1. Arg0 contains delegation signature."}
, { 'BALANCE_OTHER', 16#73, false, true, true, ?GAS_IRIS( 50, 2000), [a,a], balance_other, {address}, integer, "Arg0 := The balance of address Arg1."}
, { 'VERIFY_SIG', 16#74, false, true, true, ?GAS(1300), [a,a,a,a], verify_sig, {bytes, address, bytes}, boolean, "Arg0 := verify_sig(Hash, PubKey, Signature)"}
, { 'VERIFY_SIG_SECP256K1',16#75, false, true, true, ?GAS(1300), [a,a,a,a], verify_sig_secp256k1, {bytes, bytes, bytes}, boolean, "Arg0 := verify_sig_secp256k1(Hash, PubKey, Signature)"}
, { 'CONTRACT_TO_ADDRESS', 16#76, false, true, true, ?GAS(10), [a,a], contract_to_address, {contract}, address, "Arg0 := Arg1 - A no-op type conversion"}
, { 'AUTH_TX_HASH', 16#77, false, true, true, ?GAS(10), [a], auth_tx_hash, {}, variant, "If in GA authentication context return Some(TxHash) otherwise None."}
, { 'ORACLE_CHECK', 16#78, false, false, true, ?GAS(100), [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, ?GAS(100), [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, ?GAS(100), [a,a], is_oracle, {address}, bool, "Arg0 := is Arg1 an oracle"}
, { 'IS_CONTRACT', 16#7b, false, false, true, ?GAS(100), [a,a], is_contract, {address}, bool, "Arg0 := is Arg1 a contract"}
, { 'IS_PAYABLE', 16#7c, false, false, true, ?GAS(100), [a,a], is_payable, {address}, bool, "Arg0 := is Arg1 a payable address"}
, { 'CREATOR', 16#7d, false, true, true, ?GAS(10), [a], contract_creator, {}, address, "Arg0 := contract creator"}
, { 'ECVERIFY_SECP256K1', 16#7e, false, true, true, ?GAS(1300), [a,a,a,a], ecverify_secp256k1, {bytes, bytes, bytes}, bytes, "Arg0 := ecverify_secp256k1(Hash, Addr, Signature)"}
, { 'ECRECOVER_SECP256K1', 16#7f, false, true, true, ?GAS(1300), [a,a,a], ecrecover_secp256k1, {bytes, bytes}, bytes, "Arg0 := ecrecover_secp256k1(Hash, Signature)"}
, { 'ADDRESS_TO_CONTRACT', 16#80, false, true, true, ?GAS(10), [a,a], address_to_contract, {address}, contract, "Arg0 := Arg1 - A no-op type conversion"}
, { 'BLS12_381_G1_NEG', 16#81, false, true, true, ?GAS(100), [a,a], bls12_381_g1_neg, {tuple}, tuple, "Arg0 := BLS12_381.g1_neg(Arg1) - Negate a G1-value"}
, { 'BLS12_381_G1_NORM', 16#82, false, true, true, ?GAS(100), [a,a], bls12_381_g1_norm, {tuple}, tuple, "Arg0 := BLS12_381.g1_normalize(Arg1) - Normalize a G1-value"}
, { 'BLS12_381_G1_VALID', 16#83, false, true, true, ?GAS(2000), [a,a], bls12_381_g1_valid, {tuple}, bool, "Arg0 := BLS12_381.g1_valid(Arg1) - Check if G1-value is a valid group member"}
, { 'BLS12_381_G1_IS_ZERO', 16#84, false, true, true, ?GAS(30), [a,a], bls12_381_g1_is_zero, {tuple}, bool, "Arg0 := BLS12_381.g1_is_zero(Arg1) - Check if G1-value is zero"}
, { 'BLS12_381_G1_ADD', 16#85, false, true, true, ?GAS(100), [a,a,a], bls12_381_g1_add, {tuple, tuple}, tuple, "Arg0 := BLS12_381.g1_add(Arg1, Arg2) - Add two G1-values"}
, { 'BLS12_381_G1_MUL', 16#86, false, true, true, ?GAS(1000), [a,a,a], bls12_381_g1_mul, {tuple, tuple}, tuple, "Arg0 := BLS12_381.g1_mul(Arg1, Arg2) - Scalar multiplication for a G1-value (Arg1), and an Fr-value"}
, { 'BLS12_381_G2_NEG', 16#87, false, true, true, ?GAS(100), [a,a], bls12_381_g2_neg, {tuple}, tuple, "Arg0 := BLS12_381.g2_neg(Arg1) - Negate a G2-value"}
, { 'BLS12_381_G2_NORM', 16#88, false, true, true, ?GAS(100), [a,a], bls12_381_g2_norm, {tuple}, tuple, "Arg0 := BLS12_381.g2_normalize(Arg1) - Normalize a G2-value"}
, { 'BLS12_381_G2_VALID', 16#89, false, true, true, ?GAS(2000), [a,a], bls12_381_g2_valid, {tuple}, bool, "Arg0 := BLS12_381.g2_valid(Arg1) - Check if G2-value is a valid group member"}
, { 'BLS12_381_G2_IS_ZERO', 16#8a, false, true, true, ?GAS(30), [a,a], bls12_381_g2_is_zero, {tuple}, bool, "Arg0 := BLS12_381.g2_is_zero(Arg1) - Check if G2-value is zero"}
, { 'BLS12_381_G2_ADD', 16#8b, false, true, true, ?GAS(100), [a,a,a], bls12_381_g2_add, {tuple, tuple}, tuple, "Arg0 := BLS12_381.g2_add(Arg1, Arg2) - Add two G2-values"}
, { 'BLS12_381_G2_MUL', 16#8c, false, true, true, ?GAS(1000), [a,a,a], bls12_381_g2_mul, {tuple, tuple}, tuple, "Arg0 := BLS12_381.g2_mul(Arg1, Arg2) - Scalar multiplication for a G2-value (Arg2), and an Fr-value"}
, { 'BLS12_381_GT_INV', 16#8d, false, true, true, ?GAS(100), [a,a], bls12_381_gt_inv, {tuple}, tuple, "Arg0 := BLS12_381.gt_inv(Arg1) - Invert a GT-value"}
, { 'BLS12_381_GT_ADD', 16#8e, false, true, true, ?GAS(100), [a,a,a], bls12_381_gt_add, {tuple, tuple}, tuple, "Arg0 := BLS12_381.gt_add(Arg1, Arg2) - Add two GT-values"}
, { 'BLS12_381_GT_MUL', 16#8f, false, true, true, ?GAS(100), [a,a,a], bls12_381_gt_mul, {tuple, tuple}, tuple, "Arg0 := BLS12_381.gt_mul(Arg1, Arg2) - Multiply two GT-values"}
, { 'BLS12_381_GT_POW', 16#90, false, true, true, ?GAS(2000), [a,a,a], bls12_381_gt_pow, {tuple, tuple}, tuple, "Arg0 := BLS12_381.gt_pow(Arg1, Arg2) - Scalar exponentiation for a GT-value (Arg2), and an Fr-value"}
, { 'BLS12_381_GT_IS_ONE', 16#91, false, true, true, ?GAS(30), [a,a], bls12_381_gt_is_one, {tuple}, bool, "Arg0 := BLS12_381.gt_is_one(Arg1) - Check if a GT value is \"one\""}
, { 'BLS12_381_PAIRING', 16#92, false, true, true, ?GAS(12000), [a,a,a], bls12_381_pairing, {tuple, tuple}, tuple, "Arg0 := BLS12_381.pairing(Arg1, Arg2) - Find the pairing of a G1-value (Arg1) and a G2-value (Arg2)"}
, { 'BLS12_381_MILLER_LOOP', 16#93, false, true, true, ?GAS(5000), [a,a,a], bls12_381_miller_loop, {tuple, tuple}, tuple, "Arg0 := BLS12_381.miller_loop(Arg1, Arg2) - Do the Miller-loop step of pairing for a G1-value (Arg1) and a G2-value (Arg2)"}
, { 'BLS12_381_FINAL_EXP', 16#94, false, true, true, ?GAS(7000), [a,a], bls12_381_final_exp, {tuple}, tuple, "Arg0 := BLS12_381.final_exp(Arg1) - Do the final exponentiation in pairing"}
, { 'BLS12_381_INT_TO_FR', 16#95, false, true, true, ?GAS(30), [a,a], bls12_381_int_to_fr, {tuple}, tuple, "Arg0 := to_montgomery(Arg1) - Convert (Big)integer to montgomery representation (32 bytes)"}
, { 'BLS12_381_INT_TO_FP', 16#96, false, true, true, ?GAS(30), [a,a], bls12_381_int_to_fp, {tuple}, tuple, "Arg0 := to_montgomery(Arg1) - Convert (Big)integer to montgomery representation (48 bytes)"}
, { 'BLS12_381_FR_TO_INT', 16#97, false, true, true, ?GAS(30), [a,a], bls12_381_fr_to_int, {tuple}, tuple, "Arg0 := from_montgomery(Arg1) - Convert montgomery representation (32 bytes) to integer"}
, { 'BLS12_381_FP_TO_INT', 16#98, false, true, true, ?GAS(30), [a,a], bls12_381_fp_to_int, {tuple}, tuple, "Arg0 := from_montgomery(Arg1) - Convert montgomery representation (48 bytes) to integer"}
, { 'AENS_LOOKUP', 16#99, false, false, true, ?GAS(2000), [a,a], aens_lookup, {string}, variant, "Lookup the name of Arg0. Returns option(AENS.name)"}
, { 'ORACLE_EXPIRY', 16#9a, false, false, true, ?GAS(2000), [a,a], oracle_expiry, {oracle}, int, "Arg0 := expiry block for oracle Arg1"}
, { 'AUTH_TX', 16#9b, false, true, true, ?GAS(100 ), [a], auth_tx, {}, variant, "If in GA authentication context return Some(Tx) otherwise None."}
, { 'STR_TO_LIST', 16#9c, false, true, true, ?GAS(100), [a,a], str_to_list, {string}, list, "Arg0 := string converted to list of characters"}
, { 'STR_FROM_LIST', 16#9d, false, true, true, ?GAS(100), [a,a], str_from_list, {list}, string, "Arg0 := string converted from list of characters"}
, { 'STR_TO_UPPER', 16#9e, false, true, true, ?GAS(100), [a,a], str_to_upper, {string}, string, "Arg0 := to_upper(string)"}
, { 'STR_TO_LOWER', 16#9f, false, true, true, ?GAS(100), [a,a], str_to_lower, {string}, string, "Arg0 := to_lower(string)"}
, { 'CHAR_TO_INT', 16#a0, false, true, true, ?GAS(10), [a,a], char_to_int, {char}, int, "Arg0 := integer representation of UTF-8 character"}
, { 'CHAR_FROM_INT', 16#a1, false, true, true, ?GAS(10), [a,a], char_from_int, {int}, variant, "Arg0 := Some(UTF-8 character) from integer if valid, None if not valid."}
, { 'CALL_PGR', 16#a2, true, false, true, ?GAS(100), [a,is,a,a,a,a,a], call_pgr, {contract, string, typerep, typerep, integer, integer, bool}, variant, "Potentially protected remote call. Arg5 is protected flag, otherwise as CALL_GR."}
, { 'CREATE', 16#a3, true, false, true, ?GAS(10000), [a,a,a], create, {contract_bytearray, typerep, integer}, contract, "Deploys a contract with a bytecode Arg1 and value Arg3. The `init` arguments should be placed on the stack and match the type in Arg2. Writes contract address to the top of the accumulator stack. If an account on the resulting address did exist before the call, the `payable` flag will be updated."}
, { 'CLONE', 16#a4, true, false, true, ?GAS(5000), [a,a,a,a], clone, {contract, typerep, integer, bool}, any, "Clones the contract under Arg1 and deploys it with value of Arg3. The `init` arguments should be placed on the stack and match the type in Arg2. Writes contract (or `None` on fail when protected) to the top of the accumulator stack. Does not copy the existing contract's store it will be initialized by a fresh call to the `init` function. If an account on the resulting address did exist before the call, the `payable` flag will be updated."}
, { 'CLONE_G', 16#a5, true, false, true, ?GAS(5000), [a,a,a,a,a], clone_g, {contract, typerep, integer, integer, bool}, any, "Like `CLONE` but additionally limits the gas of the `init` call by Arg3"}
, { 'BYTECODE_HASH', 16#a6, false, true, true, ?GAS(100), [a,a], bytecode_hash, {contract}, variant, "Arg0 := hash of the deserialized contract's bytecode under address given in Arg1 (or `None` on fail). Fails on AEVM contracts and contracts deployed before Iris."}
, { 'FEE', 16#a7, false, true, true, ?GAS(10), [a], fee, {}, integer, "Arg0 := The fee for the current call tx."}
, { 'ADDRESS_TO_BYTES', 16#a8, false, true, true, ?GAS(10), [a, a], addr_to_bytes, {address}, bytes, "Arg0 := the byte representation of the address"}
, { 'POSEIDON', 16#a9, false, true, true, ?GAS(6000), [a, a, a], poseidon, {integer, integer}, integer, "Arg0 := the Poseidon hash of Arg1 and Arg2 - all integers in the BLS12-381 scalar field"}
, { 'MULMOD', 16#aa, false, true, true, ?GAS(10), [a, a, a, a], mulmod, {integer, integer, integer}, integer, "Arg0 := (Arg1 * Arg2) mod Arg3"}
, { 'BAND', 16#ab, false, true, true, ?GAS(10), [a, a, a], bin_and, {integer, integer}, integer, "Arg0 := Arg1 & Arg2"}
, { 'BOR', 16#ac, false, true, true, ?GAS(10), [a, a, a], bin_or, {integer, integer}, integer, "Arg0 := Arg1 | Arg2"}
, { 'BXOR', 16#ad, false, true, true, ?GAS(10), [a, a, a], bin_xor, {integer, integer}, integer, "Arg0 := Arg1 ^ Arg2"}
, { 'BNOT', 16#ae, false, true, true, ?GAS(10), [a, a], bin_not, {integer}, integer, "Arg0 := ~Arg1"}
, { 'BSL', 16#af, false, true, true, ?GAS(10), [a, a, a], bin_sl, {integer, integer}, integer, "Arg0 := Arg1 << Arg2"}
, { 'BSR', 16#b0, false, true, true, ?GAS(10), [a, a, a], bin_sr, {integer, integer}, integer, "Arg0 := Arg1 >> Arg2"}
, { 'DEACTIVATE', 16#fa, false, true, true, ?GAS(10), [], deactivate, {}, none, "Mark the current contract for deactivation."}
, { 'ABORT', 16#fb, true, true, true, ?GAS(10), [a], abort, {string}, none, "Abort execution (dont use all gas) with error message in Arg0."}
, { 'EXIT', 16#fc, true, true, true, ?GAS(10), [a], exit, {string}, none, "Abort execution (use upp all gas) with error message in Arg0."}
, { 'NOP', 16#fd, false, true, true, ?GAS(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."
].
generate_header_file(Filename, Ops) ->
{ok, File} = file:open(Filename, [write]),
Defines = lists:flatten([gen_defines(Op) || Op <- Ops]),
io:format(File, "~s", [prelude("Provides opcode defines.\n")]),
io:format(File, "%% FATE opcodes\n~s", [Defines]),
io:format(File, "~s",
["-define('FUNCTION' , 16#fe).\n"
"-define('EXTEND' , 16#ff).\n\n"]),
file:close(File).
generate_opcodes_ops(Modulename, HrlFile, SrcDir, Ops) ->
Filename = SrcDir ++ atom_to_list(Modulename) ++ ".erl",
{ok, File} = file:open(Filename, [write]),
Mnemonic = lists:flatten([gen_mnemonic(Op) || Op <- Ops]),
ToOp = lists:flatten([gen_m_to_op(Op) || Op <- Ops]),
Args = lists:flatten([gen_args(Op) || Op <- Ops]),
EndBB = lists:flatten([gen_bb(Op) || Op <- Ops]),
InAuth = lists:flatten([gen_in_auth(Op) || Op <- Ops]),
Offchain = lists:flatten([gen_allowed_offchain(Op) || Op <- Ops]),
GasCost = lists:flatten([gen_gas_cost(Op) || Op <- Ops]),
io:format(File, "~s", [prelude("Provides opcode primitives.\n")]),
io:format(File, "~s", [ops_exports(Modulename, HrlFile,
["args/1\n"
" , end_bb/1\n"
" , in_auth/1\n"
" , allowed_offchain/1\n"
" , mnemonic/1\n"
" , m_to_op/1\n"
" , gas_cost/1\n"
])]),
io:format(File, "%% FATE mnemonics\n~s", [Mnemonic]),
io:format(File, "mnemonic(Op) -> exit({bad_opcode, Op}).\n\n", []),
io:format(File, "%% FATE opcodes\n~s", [ToOp]),
io:format(File, "m_to_op(M) -> exit({bad_mnemonic, M}).\n\n", []),
io:format(File, "%% FATE numbers of args to op.\n~s", [Args]),
io:format(File, "args(Op) -> exit({bad_opcode, Op}).\n\n", []),
io:format(File, "%% Does FATE Op end a Basic Block?\n~s", [EndBB]),
io:format(File, "end_bb(_) -> false.\n\n", []),
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", []),
io:format(File, "%% Base cost of operation\n~s", [GasCost]),
io:format(File, "gas_cost(Op) -> exit({bad_opcode, Op}).\n\n", []),
file:close(File).
generate_code_ops(Modulename, SrcDir, Ops) ->
Filename = SrcDir ++ atom_to_list(Modulename) ++ ".erl",
{ok, File} = file:open(Filename, [write]),
Types = lists:flatten([gen_type(Op) || Op <- Ops]),
TypeExports = lists:flatten([gen_type_exports(Op) || Op <- Ops]),
[#{type_name := FirstType} | RestOfOps] = Ops,
FateTypes = lists:flatten([gen_fate_code_type(Op) || Op <- RestOfOps]),
ConstructorExports = lists:flatten([gen_constructor_exports(Op) || Op <- Ops]),
Constructors = lists:flatten([gen_constructors(Op) || Op <- Ops]),
io:format(File, "~s", [prelude(" Provide constructor functuions for "
"Fate instructions.\n%%% Provide types"
" and documentation for Fate "
"instructions.\n")]),
io:format(File, "-module(~w).\n\n", [Modulename]),
io:format(File, "-include_lib(\"aebytecode/include/aeb_fate_data.hrl\").\n\n"
"-define(i(__X__), {immediate, __X__ }).\n\n"
"-type fate_arg_immediate(T) :: {immediate, T}.\n"
"-type fate_arg_var() :: {var, integer()}.\n"
"-type fate_arg_arg() :: {arg, integer()}.\n"
"-type fate_arg_stack() :: {stack, 0}.\n"
"-type fate_arg() :: fate_arg_immediate()\n"
" | fate_arg_var()\n"
" | fate_arg_arg()\n"
" | fate_arg_stack().\n\n"
"-type fate_arg_immediate() :: {immediate, aeb_fate_data:fate_type()}.\n"
, []),
io:format(File, "~s", [Types]),
io:format(File, "-type fate_code() :: ~s\n~s .\n\n",
[FirstType, FateTypes]),
io:format(File, "-export_type([ fate_code/0\n~s ]).\n\n", [TypeExports]),
io:format(File, "-export([ foo/0\n~s ]).\n\n", [ConstructorExports]),
io:format(File, "~s\n", [Constructors]),
io:format(File, "foo() -> \"A temp hack.\".\n", []),
file:close(File).
gen_type(#{type_name := TypeName, type := Type}) ->
lists:flatten(io_lib:format("-type ~-29s :: ~s.\n",
[TypeName, Type])).
gen_fate_code_type(#{type_name := TypeName}) ->
lists:flatten(io_lib:format(" | ~s\n", [TypeName])).
gen_type_exports(#{type_name := TypeName}) ->
lists:flatten(io_lib:format(" , ~s/0\n", [TypeName--"()"])).
gen_constructor_exports(#{constructor_type := Function}) ->
lists:flatten(io_lib:format(" , ~s\n", [Function])).
gen_constructors(#{constructor := Function, format := [],
type_name := Type, opname := Name}) ->
lists:flatten(io_lib:format("-spec ~s() -> ~s.\n"
"~s() ->\n"
" ~w.\n\n",
[Function, Type, Function, Name]));
gen_constructors(#{constructor := Function, format := ArgSpec,
type_name := Type, opname := Name}) ->
ArgTypeSpecs = gen_arg_type_specs(ArgSpec),
Args = gen_arg_names(0, ArgSpec),
UseArgs = gen_arg_uses(0, ArgSpec),
lists:flatten(io_lib:format("-spec ~s(~s) -> ~s.\n"
"~s(~s) ->\n"
" {~w, ~s}.\n\n",
[Function, ArgTypeSpecs, Type,
Function, Args, Name, UseArgs])).
gen_arg_type_specs([]) -> [];
gen_arg_type_specs([a]) -> "fate_arg()";
gen_arg_type_specs([is]) -> "aeb_fate_data:fate_string()";
gen_arg_type_specs([ii]) -> "aeb_fate_data:fate_integer()";
gen_arg_type_specs([li]) -> "[aeb_fate_data:fate_integer()]";
gen_arg_type_specs([t]) -> "aeb_fate_data:fate_type_type()";
gen_arg_type_specs([a | Args]) -> "fate_arg(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([is | Args]) -> "aeb_fate_data:fate_string(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([ii | Args]) -> "aeb_fate_data:fate_integer(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([li | Args]) -> "[aeb_fate_data:fate_integer()], " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([t | Args]) -> "aeb_fate_data:fate_type_type(), " ++ gen_arg_type_specs(Args).
gen_arg_names(_, []) ->
[];
gen_arg_names(N, [_]) -> io_lib:format("Arg~w", [N]);
gen_arg_names(N, [_|Args]) ->
io_lib:format("Arg~w, ", [N]) ++ gen_arg_names(N+1, Args).
gen_arg_uses(_, []) ->
[];
gen_arg_uses(N, [a]) -> io_lib:format("Arg~w", [N]);
gen_arg_uses(N, [is]) -> io_lib:format("{immediate, Arg~w}", [N]);
gen_arg_uses(N, [ii]) -> io_lib:format("{immediate, Arg~w}", [N]);
gen_arg_uses(N, [li]) -> io_lib:format("{immediate, Arg~w}", [N]);
gen_arg_uses(N, [t]) -> io_lib:format("Arg~w", [N]);
gen_arg_uses(N, [a | Args]) ->
io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args);
gen_arg_uses(N, [is | Args]) ->
io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args);
gen_arg_uses(N, [ii | Args]) ->
io_lib:format("{immediate, Arg~w}, ", [N]) ++ gen_arg_uses(N+1, Args);
gen_arg_uses(N, [li | Args]) ->
io_lib:format("[{immediate, I} || I <- Arg~w], ", [N]) ++ gen_arg_uses(N+1, Args);
gen_arg_uses(N, [t | Args]) ->
io_lib:format("Arg~w, ", [N]) ++ gen_arg_uses(N+1, Args).
ops_exports(Module, HrlFile, Exports) ->
lists:flatten(io_lib:format(
"-module(~w).\n\n"
"-export([ ~s ]).\n\n"
"-include_lib(\"aebytecode/" ++ HrlFile ++"\").\n\n"
"%%====================================================================\n"
"%% API\n"
"%%====================================================================\n",
[Module, Exports])).
gen_mnemonic(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("mnemonic(~24s) -> ~24w ;\n",
[Macro, Name])).
gen_m_to_op(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("m_to_op(~24w) -> ~24s ;\n",
[Name, Macro])).
gen_args(#{macro := Macro, arity := Arity}) ->
lists:flatten(io_lib:format("args(~24s) -> ~2w ;\n",
[Macro, Arity])).
gen_bb(#{macro := Macro, end_bb := EndBB}) ->
lists:flatten(io_lib:format("end_bb(~24s) -> ~w ;\n",
[Macro, EndBB])).
gen_in_auth(#{macro := Macro, in_auth := InAuth}) ->
lists:flatten(io_lib:format("in_auth(~24s) -> ~w ;\n",
[Macro, InAuth])).
gen_allowed_offchain(#{macro := Macro, offchain := Offchain}) ->
lists:flatten(io_lib:format("allowed_offchain(~24s) -> ~w ;\n",
[Macro, Offchain])).
gen_gas_cost(#{macro := Macro, gas := Gas}) ->
lists:flatten(io_lib:format("gas_cost(~24s) -> ~w ;\n",
[Macro, Gas])).
prelude(Doc) ->
"%%%-------------------------------------------------------------------\n"
"%%% @copyright (C) 2019, Aeternity Anstalt\n"
"%%%\n"
"%%% === === N O T E : This file is generated do not edit. === ===\n"
"%%%\n"
"%%% Source is in aeb_fate_generate_ops.erl\n"
"%%% @doc\n"
"%%% "++Doc++
"%%% @end\n"
"%%%-------------------------------------------------------------------\n\n".
gen_defines(#{opname := Name, opcode := OpCode}) ->
lists:flatten(io_lib:format("-define(~-29w, 16#~2.16.0b).\n", [Name, OpCode])).
gen([]) ->
[];
gen([{OpName, OpCode, EndBB, InAuth, AllowedOffchain, Gas, FateFormat, Constructor, ArgTypes, ResType, Doc} | Rest]) ->
Arity = length(FateFormat),
Name = atom_to_list(OpName),
LowerName = string:to_lower(Name),
TypeName = "fate_" ++ LowerName ++ "()",
Macro = "?" ++ Name,
Type = case FateFormat of
[] -> io_lib:format("~w", [OpName]);
Args ->
io_lib:format("{~w, ~s}", [OpName, expand_types(Args)])
end,
ConstructorType = atom_to_list(Constructor) ++ "/" ++ io_lib:format("~w", [Arity]),
[#{ opname => OpName
, opcode => OpCode
, arity => Arity
, end_bb => EndBB
, in_auth => InAuth
, offchain => AllowedOffchain
, format => FateFormat
, macro => Macro
, type_name => TypeName
, doc => Doc
, gas => Gas
, type => Type
, constructor => Constructor
, constructor_type => ConstructorType
, arg_types => ArgTypes
, res_type => ResType
}| gen(Rest)].
expand_types([]) -> "";
expand_types([T]) -> expand_type(T);
expand_types([T|Ts]) ->expand_type(T) ++ ", " ++ expand_types(Ts).
expand_type(a) -> "fate_arg()";
expand_type(is) -> "fate_arg_immediate(aeb_fate_data:fate_string())";
expand_type(ii) -> "fate_arg_immediate(aeb_fate_data:fate_integer())";
expand_type(li) -> "fate_arg_immediate([aeb_fate_data:fate_integer()])";
expand_type(t) -> "aeb_fate_data:fate_type_type()".
generate_scanner(TemplateFile, Outfile, Path, Ops) ->
{ok, Template} = file:read_file(filename:join(Path,TemplateFile)),
Tokens = lists:flatten([gen_token(Op) || Op <- Ops]),
NewFile = insert_tokens_in_template(Template, Tokens),
file:write_file(filename:join(Path, Outfile), NewFile).
gen_token(#{opname := OpName}) ->
Name = atom_to_list(OpName),
io_lib:format("~-28s: {token, {mnemonic, TokenLine, ~w}}.\n",
[Name, OpName]).
insert_tokens_in_template(<<"%% ###REPLACEWITHOPTOKENS###", Rest/binary >>, Tokens) ->
[Tokens, Rest];
insert_tokens_in_template(<<"%%% ###REPLACEWITHNOTE###", Rest/binary >>, Tokens) ->
[
"%%%\n"
"%%% === === N O T E : This file is generated do not edit. === ===\n"
"%%%\n"
"%%% Source is in aeb_fate_generate_ops.erl\n"
"%%% and aeb_fate_asm_scan.template"
| insert_tokens_in_template(Rest, Tokens)];
insert_tokens_in_template(<<B,Rest/binary>>, Tokens) ->
[B|insert_tokens_in_template(Rest, Tokens)].
gen_asm_pp(Module, Path, Ops) ->
Filename = filename:join(Path, atom_to_list(Module)) ++ ".erl",
{ok, File} = file:open(Filename, [write]),
Formats = lists:flatten([gen_format(Op)++"\n" || Op <- Ops]),
io:format(File, "~s", [prelude(" Provide pretty printing functuions for "
"Fate instructions.\n")]),
io:format(File, "-module(~w).\n\n", [Module]),
io:format(File,
"-export([format_op/2]).\n\n"
"format_arg(li, {immediate, LI}) ->\n"
" aeb_fate_data:format(LI);\n"
"format_arg(_, {immediate, I}) ->\n"
" aeb_fate_data:format(I);\n"
"format_arg(a, {arg, N}) -> io_lib:format(\"arg~~p\", [N]);\n"
"format_arg(a, {var, N}) 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, {stack, 0}) -> \"a\".\n\n"
"lookup(Name, Symbols) ->\n"
" maps:get(Name, Symbols, io_lib:format(\"~~p\",[Name])).\n\n"
"~s"
, [Formats]),
io:format(File, "format_op(Op, _Symbols) -> io_lib:format(\";; Bad Op: ~~w\\n\", [Op]).\n", []),
file:close(File).
gen_format(#{opname := Name}) when (Name =:= 'CALL_R') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, ArgType, RetType, Value}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, ArgType), \" \", "
"format_arg(a, RetType), \" \", "
"format_arg(a, Value)];\n"
"format_op({~w, Contract, {immediate, Function}, ArgType, RetType, Value}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, ArgType), \" \", "
"format_arg(a, RetType), \" \", "
"format_arg(a, Value)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name}) when (Name =:= 'CALL_GR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, ArgType, RetType, Value, Gas}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, ArgType), \" \", "
"format_arg(a, RetType), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n"
"format_op({~w, Contract, {immediate, Function}, ArgType, RetType, Value, Gas}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, ArgType), \" \", "
"format_arg(a, RetType), \" \", "
"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)]);
gen_format(#{opname := Name, format := Args}) ->
NameAsString = atom_to_list(Name),
case Args of
[T0] ->
io_lib:format(
"format_op({~w, Arg0}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0)];",
[Name, NameAsString, T0]);
[T0, T1] ->
io_lib:format(
"format_op({~w, Arg0, Arg1}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1)];",
[Name, NameAsString, T0, T1]);
[T0, T1, T2] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2)];",
[Name, NameAsString, T0, T1, T2]);
[T0, T1, T2, T3] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3)];",
[Name, NameAsString, T0, T1, T2, T3]);
[T0, T1, T2, T3, T4] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4)];",
[Name, NameAsString, T0, T1, T2, T3, T4]);
[T0, T1, T2, T3, T4, T5] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5]);
[T0, T1, T2, T3, T4, T5, T6] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5),"
"\" \", format_arg(~w, Arg6)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5, T6]);
[T0, T1, T2, T3, T4, T5, T6, T7] ->
io_lib:format(
"format_op({~w, Arg0, Arg1, Arg2, Arg3, Arg4, Arg5, Arg6, Arg7}, _) ->\n"
" [\"~s \", format_arg(~w, Arg0), "
"\" \", format_arg(~w, Arg1),"
"\" \", format_arg(~w, Arg2),"
"\" \", format_arg(~w, Arg3),"
"\" \", format_arg(~w, Arg4),"
"\" \", format_arg(~w, Arg5),"
"\" \", format_arg(~w, Arg6),"
"\" \", format_arg(~w, Arg7)];",
[Name, NameAsString, T0, T1, T2, T3, T4, T5, T6, T7])
end.
test_asm_generator(Filename) ->
{ok, File} = file:open(Filename, [write]),
Instructions = lists:flatten([gen_instruction(Op)++"\n" || Op <- get_ops()]),
io:format(File,
";; CONTRACT all_instructions\n\n"
";; Dont expect this contract to typecheck or run.\n"
";; Just used to check assembler rountrip of all instruction.\n\n"
"FUNCTION foo () : {tuple, []}\n"
"~s"
, [Instructions]),
io:format(File, " RETURNR ()\n", []),
file:close(File).
gen_instruction(#{opname := Name, format := []}) ->
io_lib:format(" ~s\n", [Name]);
gen_instruction(#{opname := Name, format := ArgTypes}) ->
Args = lists:flatten(lists:join(" ", [gen_arg(A) || A <- ArgTypes])),
I = io_lib:format(" ~s ~s\n", [Name, Args]),
I.
%% This should be done with a Quick Check generator...
gen_arg(a) -> any_arg();
gen_arg(is) -> "foo";
gen_arg(ii) -> gen_int();
gen_arg(li) -> "[1, 2, 3]";
gen_arg(t) -> "integer".
any_arg() ->
element(rand:uniform(5), {"a", stack_arg(), var_arg(), arg_arg(), imm_arg()}).
stack_arg() -> "a".
arg_arg() -> "arg" ++ integer_to_list(rand:uniform(256)-1).
var_arg() -> "var" ++ integer_to_list(rand:uniform(256)-1).
imm_arg() ->
case rand:uniform(15) of
1 -> gen_int();
2 -> gen_int();
3 -> gen_int();
4 -> gen_int();
5 -> gen_int();
6 -> gen_int();
7 -> gen_int();
8 -> gen_address();
9 -> gen_boolean();
10 -> gen_string();
11 -> gen_map();
12 -> gen_list();
13 -> gen_bits();
14 -> gen_tuple();
15 -> gen_variant()
end.
gen_key() ->
case rand:uniform(15) of
1 -> gen_int();
2 -> gen_int();
3 -> gen_int();
4 -> gen_int();
5 -> gen_int();
6 -> gen_int();
7 -> gen_int();
8 -> gen_address();
9 -> gen_boolean();
10 -> gen_string();
11 -> gen_string();
12 -> gen_list();
13 -> gen_bits();
14 -> gen_tuple();
15 -> gen_variant()
end.
gen_boolean() ->
element(rand:uniform(2), {"true", "false"}).
gen_int() ->
element(rand:uniform(4),
{ integer_to_list(rand:uniform(round(math:pow(10,40))))
, integer_to_list(rand:uniform(10))
, integer_to_list(rand:uniform(100))
, io_lib:format("0x~.16b",[rand:uniform(round(math:pow(10,10)))])}).
gen_address() -> "#nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv".
gen_string() -> "\"foo\"".
gen_map() -> "{ " ++ gen_key() ++ " => " ++ imm_arg() ++ "}".
gen_list() ->
case rand:uniform(4) of
1 -> "[]";
2 -> "[" ++ lists:join(", ", gen_list_elements()) ++ " ]";
3 -> "[ " ++ imm_arg() ++ " ]";
4 -> "[ " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ]"
end.
%% Not type correct.
gen_list_elements() ->
case rand:uniform(3) of
1 -> [imm_arg() | gen_list_elements()];
2 -> [];
3 -> [imm_arg()]
end.
gen_bits() ->
element(rand:uniform(3),
{"<>"
,"!<>"
, "101010"}).
gen_tuple() ->
case rand:uniform(3) of
1 -> "()";
2 -> "(42)";
3 -> "(" ++ imm_arg() ++ ")"
end.
gen_variant() ->
case rand:uniform(3) of
1 -> "(| 5 | 2 | (1, \"foo\", ()) |)";
2 -> "(| 2 | 1 | ( " ++ imm_arg() ++ " ) |)";
3 -> "(| 2 | 0 | ( " ++ imm_arg() ++ ", " ++ imm_arg() ++ " ) |)"
end.
+218
View File
@@ -0,0 +1,218 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Functions for manipulating FATE maps. In particular for mediating
%%% between plain map values (represented by Erlang maps) and maps that are
%%% fully or partially saved in the contract store.
%%% @end
%%% -------------------------------------------------------------------
-module(aeb_fate_maps).
-include("aeb_fate_data.hrl").
-export([ allocate_store_maps/2
, has_store_maps/1
, unfold_store_maps/2
, refcount/1
, refcount_zero/0
, refcount_diff/2
, refcount_union/1
, refcount_union/2
, no_used_ids/0 ]).
-export_type([used_ids/0, maps/0, refcount/0]).
%% Size in bytes of serialization of a map for which we turn it into a store
%% map. It's not worth turning small maps into store maps.
%% Under consensus!
-ifdef(TEST).
-define(STORE_MAP_THRESHOLD, 0).
-else.
-define(STORE_MAP_THRESHOLD, 100).
-endif.
-type fate_value() :: aeb_fate_data:fate_type().
-type fate_value_or_tombstone() :: fate_value() | ?FATE_MAP_TOMBSTONE.
-type id() :: integer().
-type used_ids() :: list(id()).
-type maps() :: #{ id() => aeb_fate_data:fate_map() | aeb_fate_data:fate_store_map() }.
%% -- Allocating store maps --------------------------------------------------
-spec allocate_store_maps(used_ids(), [fate_value_or_tombstone()]) -> {[fate_value_or_tombstone()], maps()}.
allocate_store_maps(Used, Vals) ->
{_Used, Vals1, Maps} = allocate_store_maps_l(Used, Vals, #{}),
{Vals1, Maps}.
allocate_store_maps(Used, ?FATE_MAP_TOMBSTONE = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_TRUE = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_FALSE = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_UNIT = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_BITS(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_BYTES(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_ADDRESS(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_CONTRACT(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_ORACLE(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_ORACLE_Q(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_CHANNEL(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_TYPEREP(_) = Val, Maps) -> {Used, Val, Maps};
allocate_store_maps(Used, Val, Maps) when ?IS_FATE_INTEGER(Val) -> {Used, Val, Maps};
allocate_store_maps(Used, Val, Maps) when ?IS_FATE_STRING(Val) -> {Used, Val, Maps};
allocate_store_maps(Used, ?FATE_TUPLE(Val), Maps) ->
{Used1, Vals, Maps1} = allocate_store_maps_l(Used, tuple_to_list(Val), Maps),
{Used1, ?FATE_TUPLE(list_to_tuple(Vals)), Maps1};
allocate_store_maps(Used, Val, Maps) when ?IS_FATE_LIST(Val) ->
{Used1, Vals, Maps1} = allocate_store_maps_l(Used, ?FATE_LIST_VALUE(Val), Maps),
{Used1, ?MAKE_FATE_LIST(Vals), Maps1};
allocate_store_maps(Used, ?FATE_VARIANT(Arities, Tag, Vals), Maps) ->
{Used1, Vals1, Maps1} = allocate_store_maps_l(Used, tuple_to_list(Vals), Maps),
{Used1, ?FATE_VARIANT(Arities, Tag, list_to_tuple(Vals1)), Maps1};
allocate_store_maps(Used, Val, Maps) when ?IS_FATE_MAP(Val) ->
{Used1, KVs, Maps1} = allocate_store_maps_m(Used, ?FATE_MAP_VALUE(Val), Maps),
Val1 = ?MAKE_FATE_MAP(KVs),
case byte_size(aeb_fate_encoding:serialize(Val1)) < ?STORE_MAP_THRESHOLD of
true -> {Used1, Val1, Maps1};
false ->
{Id, Used2} = next_id(Used1),
{Used2, ?FATE_STORE_MAP(#{}, Id), Maps1#{Id => Val1}}
end;
allocate_store_maps(Used, ?FATE_STORE_MAP(Cache, _Id) = Val, Maps) when Cache =:= #{} ->
{Used, Val, Maps};
allocate_store_maps(Used, ?FATE_STORE_MAP(Cache, Id), Maps) ->
{NewId, Used1} = next_id(Used),
{Used2, Cache1, Maps1} = allocate_store_maps_m(Used1, Cache, Maps),
{Used2, ?FATE_STORE_MAP(#{}, NewId), Maps1#{NewId => ?FATE_STORE_MAP(Cache1, Id)}}.
allocate_store_maps_l(Used, [], Maps) -> {Used, [], Maps};
allocate_store_maps_l(Used, [H | T], Maps) ->
{Used1, H1, Maps1} = allocate_store_maps(Used, H, Maps),
{Used2, T1, Maps2} = allocate_store_maps(Used1, T, Maps1),
{Used2, [H1 | T1], Maps2}.
allocate_store_maps_m(Used, Val, Maps) ->
maps:fold(fun(K, V, {Us, M, Ms}) ->
{Us1, V1, Ms1} = allocate_store_maps(Us, V, Ms),
{Us1, M#{ K => V1 }, Ms1}
end, {Used, #{}, Maps}, Val).
%% -- Unfolding store maps ---------------------------------------------------
-type unfold_fun() :: fun((id()) -> aeb_fate_data:fate_map()).
-spec unfold_store_maps(unfold_fun(), fate_value_or_tombstone()) -> fate_value_or_tombstone().
unfold_store_maps(_Unfold, ?FATE_MAP_TOMBSTONE = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_TRUE = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_FALSE = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_UNIT = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_BITS(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_BYTES(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_ADDRESS(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_CONTRACT(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_ORACLE(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_ORACLE_Q(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_CHANNEL(_) = Val) -> Val;
unfold_store_maps(_Unfold, ?FATE_TYPEREP(_) = Val) -> Val;
unfold_store_maps(_Unfold, Val) when ?IS_FATE_INTEGER(Val) -> Val;
unfold_store_maps(_Unfold, Val) when ?IS_FATE_STRING(Val) -> Val;
unfold_store_maps(Unfold, ?FATE_TUPLE(Val)) ->
Vals = unfold_store_maps_l(Unfold, tuple_to_list(Val)),
?FATE_TUPLE(list_to_tuple(Vals));
unfold_store_maps(Unfold, Val) when ?IS_FATE_LIST(Val) ->
?MAKE_FATE_LIST(unfold_store_maps_l(Unfold, ?FATE_LIST_VALUE(Val)));
unfold_store_maps(Unfold, ?FATE_VARIANT(Arities, Tag, Vals)) ->
Vals1 = unfold_store_maps_l(Unfold, tuple_to_list(Vals)),
?FATE_VARIANT(Arities, Tag, list_to_tuple(Vals1));
unfold_store_maps(Unfold, Val) when ?IS_FATE_MAP(Val) ->
?MAKE_FATE_MAP(unfold_store_maps_m(Unfold, ?FATE_MAP_VALUE(Val)));
unfold_store_maps(Unfold, ?FATE_STORE_MAP(Cache, Id)) ->
StoreMap = Unfold(Id),
maps:fold(fun write_cache/3, unfold_store_maps(Unfold, StoreMap),
unfold_store_maps_m(Unfold, Cache)).
unfold_store_maps_l(Unfold, Vals) ->
[ unfold_store_maps(Unfold, Val) || Val <- Vals ].
unfold_store_maps_m(Unfold, Val) ->
maps:map(fun(_, V) -> unfold_store_maps(Unfold, V) end, Val).
write_cache(Key, ?FATE_MAP_TOMBSTONE, Map) ->
maps:remove(Key, Map);
write_cache(Key, Val, Map) ->
Map#{ Key => Val }.
%% -- Reference counting -----------------------------------------------------
-type refcount() :: #{id() => integer()}.
-spec refcount_zero() -> refcount().
refcount_zero() -> #{}.
-spec refcount_diff(refcount(), refcount()) -> refcount().
refcount_diff(New, Old) ->
maps:fold(fun(K, N, C) -> maps:update_with(K, fun(M) -> M - N end, -N, C) end,
New, Old).
-spec refcount_union([refcount()]) -> refcount().
refcount_union(Counts) -> lists:foldl(fun refcount_union/2, #{}, Counts).
-spec refcount_union(refcount(), refcount()) -> refcount().
refcount_union(A, B) ->
maps:fold(fun(K, N, C) -> maps:update_with(K, fun(M) -> M + N end, N, C) end,
B, A).
-spec has_store_maps(fate_value()) -> boolean().
has_store_maps(Val) ->
refcount_zero() /= refcount(Val).
-spec refcount(fate_value()) -> refcount().
refcount(Val) -> refcount(Val, #{}).
-spec refcount(fate_value_or_tombstone(), refcount()) -> refcount().
refcount(?FATE_MAP_TOMBSTONE, Count) -> Count;
refcount(?FATE_TRUE, Count) -> Count;
refcount(?FATE_FALSE, Count) -> Count;
refcount(?FATE_UNIT, Count) -> Count;
refcount(?FATE_BITS(_), Count) -> Count;
refcount(?FATE_BYTES(_), Count) -> Count;
refcount(?FATE_ADDRESS(_), Count) -> Count;
refcount(?FATE_CONTRACT(_), Count) -> Count;
refcount(?FATE_ORACLE(_), Count) -> Count;
refcount(?FATE_ORACLE_Q(_), Count) -> Count;
refcount(?FATE_CHANNEL(_), Count) -> Count;
refcount(?FATE_TYPEREP(_), Count) -> Count;
refcount(Val, Count) when ?IS_FATE_INTEGER(Val) -> Count;
refcount(Val, Count) when ?IS_FATE_STRING(Val) -> Count;
refcount(?FATE_TUPLE(Val), Count) ->
refcount_l(tuple_to_list(Val), Count);
refcount(Val, Count) when ?IS_FATE_LIST(Val) ->
refcount_l(?FATE_LIST_VALUE(Val), Count);
refcount(?FATE_VARIANT(_Arities, _Tag, Vals), Count) ->
refcount_l(tuple_to_list(Vals), Count);
refcount(Val, Count) when ?IS_FATE_MAP(Val) ->
refcount_m(?FATE_MAP_VALUE(Val), Count);
refcount(?FATE_STORE_MAP(Cache, Id), Count) ->
refcount_m(Cache, maps:update_with(Id, fun(N) -> N + 1 end, 1, Count)).
refcount_l(Vals, Count) ->
lists:foldl(fun refcount/2, Count, Vals).
refcount_m(Val, Count) ->
%% No maps in map keys
maps:fold(fun(_, ?FATE_MAP_TOMBSTONE, C) -> C;
(_, V, C) -> refcount(V, C) end, Count, Val).
%% -- Map id allocation ------------------------------------------------------
-spec no_used_ids() -> used_ids().
no_used_ids() -> [].
-spec next_id(used_ids()) -> {id(), used_ids()}.
next_id(UsedIds) ->
next_id(UsedIds, 0, []).
next_id(Used, J, Acc) when Used == []; J < hd(Used) ->
{J, lists:reverse(Acc) ++ [J | Used]};
next_id([I | Used], I, Acc) ->
next_id(Used, I + 1, [I | Acc]);
next_id([I | Used], J, Acc) when J > I ->
next_id(Used, J, [I | Acc]).
-363
View File
@@ -1,363 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc
%%% Opcodes
%%% @end
%%%-------------------------------------------------------------------
-module(aeb_fate_opcodes).
-export([ args/1
, end_bb/1
, mnemonic/1
, m_to_op/1
, opcode/1
]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl").
%%====================================================================
%% API
%%====================================================================
opcode(X) when X >= 0, X =< 255 -> X;
opcode({comment,X}) -> ?COMMENT(X).
mnemonic(?NOP) -> 'NOP' ;
mnemonic(?RETURN) -> 'RETURN' ;
mnemonic(?CALL) -> 'CALL' ;
mnemonic(?CALL_R) -> 'CALL_R' ;
mnemonic(?CALL_T) -> 'CALL_T' ;
mnemonic(?CALL_TR) -> 'CALL_TR' ;
mnemonic(?JUMP) -> 'JUMP' ;
mnemonic(?JUMPIF) -> 'JUMPIF' ;
mnemonic(?PUSH) -> 'PUSH' ;
mnemonic(?DUP) -> 'DUP' ;
mnemonic(?DUPA) -> 'DUPA' ;
mnemonic(?POP) -> 'POP' ;
mnemonic(?STORE) -> 'STORE' ;
mnemonic(?ADD) -> 'ADD' ;
mnemonic(?MUL) -> 'MUL' ;
mnemonic(?SUB) -> 'SUB' ;
mnemonic(?DIV) -> 'DIV' ;
mnemonic(?MOD) -> 'MOD' ;
mnemonic(?POW) -> 'POW' ;
mnemonic(?LT) -> 'LT' ;
mnemonic(?GT) -> 'GT' ;
mnemonic(?EQ) -> 'EQ' ;
mnemonic(?ELT) -> 'ELT' ;
mnemonic(?EGT) -> 'EGT' ;
mnemonic(?NEQ) -> 'NEQ' ;
mnemonic(?AND) -> 'AND' ;
mnemonic(?OR) -> 'OR' ;
mnemonic(?NOT) -> 'NOT' ;
mnemonic(?TUPLE) -> 'TUPLE' ;
mnemonic(?ELEMENT) -> 'ELEMENT' ;
mnemonic(?MAP_EMPTY) -> 'MAP_EMPTY' ;
mnemonic(?MAP_LOOKUP) -> 'MAP_LOOKUP' ;
mnemonic(?MAP_UPDATE) -> 'MAP_UPDATE' ;
mnemonic(?MAP_DELETE) -> 'MAP_DELETE' ;
mnemonic(?MAP_MEMBER) -> 'MAP_MEMBER' ;
mnemonic(?MAP_FROM_LIST) -> 'MAP_FROM_LIST' ;
mnemonic(?NIL) -> 'NIL' ;
mnemonic(?IS_NIL) -> 'IS_NIL' ;
mnemonic(?CONS) -> 'CONS' ;
mnemonic(?HD) -> 'HD' ;
mnemonic(?TL) -> 'TL' ;
mnemonic(?LENGTH) -> 'LENGTH' ;
mnemonic(?STR_EQ) -> 'STR_EQ' ;
mnemonic(?STR_JOIN) -> 'STR_JOIN' ;
mnemonic(?ADDR_TO_STR) -> 'ADDR_TO_STR' ;
mnemonic(?STR_REVERSE) -> 'STR_REVERSE' ;
mnemonic(?INT_TO_ADDR) -> 'INT_TO_ADDR' ;
mnemonic(?VARIANT) -> 'VARIANT' ;
mnemonic(?VARIANT_TEST) -> 'VARIANT_TEST' ;
mnemonic(?VARIANT_ELEMENT) -> 'VARIANT_ELEMENT' ;
mnemonic(?BITS_NONE) -> 'BITS_NONE' ;
mnemonic(?BITS_NONEA) -> 'BITS_NONEA' ;
mnemonic(?BITS_ALL) -> 'BITS_ALL' ;
mnemonic(?BITS_ALLA) -> 'BITS_ALLA' ;
mnemonic(?BITS_SET) -> 'BITS_SET' ;
mnemonic(?BITS_CLEAR) -> 'BITS_CLEAR' ;
mnemonic(?BITS_TEST) -> 'BITS_TEST' ;
mnemonic(?BITS_SUM) -> 'BITS_SUM' ;
mnemonic(?BITS_OR) -> 'BITS_OR' ;
mnemonic(?BITS_AND) -> 'BITS_AND' ;
mnemonic(?BITS_DIFF) -> 'BITS_DIFF' ;
mnemonic(?ADDRESS) -> 'ADDRESS' ;
mnemonic(?BALANCE) -> 'BALANCE' ;
mnemonic(?ORIGIN) -> 'ORIGIN' ;
mnemonic(?CALLER) -> 'CALLER' ;
mnemonic(?GASPRICE) -> 'GASPRICE' ;
mnemonic(?BLOCKHASH) -> 'BLOCKHASH' ;
mnemonic(?BENEFICIARY) -> 'BENEFICIARY' ;
mnemonic(?TIMESTAMP) -> 'TIMESTAMP' ;
mnemonic(?NUMBER) -> 'NUMBER' ;
mnemonic(?DIFFICULTY) -> 'DIFFICULTY' ;
mnemonic(?GASLIMIT) -> 'GASLIMIT' ;
mnemonic(?GAS) -> 'GAS' ;
mnemonic(?LOG0) -> 'LOG0' ;
mnemonic(?LOG1) -> 'LOG1' ;
mnemonic(?LOG2) -> 'LOG2' ;
mnemonic(?LOG3) -> 'LOG3' ;
mnemonic(?LOG4) -> 'LOG4' ;
mnemonic(?ABORT) -> 'ABORT' ;
mnemonic(?EXIT) -> 'EXIT' ;
mnemonic(?DEACTIVATE) -> 'DEACTIVATE' ;
mnemonic(?INC) -> 'INC' ;
mnemonic(?DEC) -> 'DEC' ;
mnemonic(?INCA) -> 'INCA' ;
mnemonic(?DECA) -> 'DECA' ;
mnemonic(?INT_TO_STR) -> 'INT_TO_STR' ;
mnemonic(?SPEND) -> 'SPEND' ;
mnemonic(?ORACLE_REGISTER) -> 'ORACLE_REGISTER' ;
mnemonic(?ORACLE_QUERY) -> 'ORACLE_QUERY' ;
mnemonic(?ORACLE_RESPOND) -> 'ORACLE_RESPOND' ;
mnemonic(?ORACLE_EXTEND) -> 'ORACLE_EXTEND' ;
mnemonic(?ORACLE_GET_ANSWER) -> 'ORACLE_GET_ANSWER' ;
mnemonic(?ORACLE_GET_QUESTION) -> 'ORACLE_GET_QUESTION' ;
mnemonic(?ORACLE_QUERY_FEE) -> 'ORACLE_QUERY_FEE' ;
mnemonic(?AENS_RESOLVE) -> 'AENS_RESOLVE' ;
mnemonic(?AENS_PRECLAIM) -> 'AENS_PRECLAIM' ;
mnemonic(?AENS_CLAIM) -> 'AENS_CLAIM' ;
mnemonic(?AENS_UPDATE) -> 'AENS_UPDATE' ;
mnemonic(?AENS_TRANSFER) -> 'AENS_TRANSFER' ;
mnemonic(?AENS_REVOKE) -> 'AENS_REVOKE' ;
mnemonic(?ECVERIFY) -> 'ECVERIFY' ;
mnemonic(?SHA3) -> 'SHA3' ;
mnemonic(?SHA256) -> 'SHA256' ;
mnemonic(?BLAKE2B) -> 'BLAKE2B' ;
mnemonic(?RETURNR) -> 'RETURNR' ;
mnemonic(?MAP_LOOKUPD) -> 'MAP_LOOKUPD' ;
mnemonic(?SWITCH_V2) -> 'SWITCH_V2' ;
mnemonic(?SWITCH_V3) -> 'SWITCH_V3' ;
mnemonic(?SWITCH_VN) -> 'SWITCH_VN' ;
mnemonic(?BITS_ALL_N) -> 'BITS_ALL_N' ;
mnemonic(?FUNCTION) -> 'FUNCTION' ;
mnemonic(?EXTEND) -> 'EXTEND'.
m_to_op('NOP') -> ?NOP ;
m_to_op('RETURN') -> ?RETURN ;
m_to_op('CALL') -> ?CALL ;
m_to_op('CALL_R') -> ?CALL_R ;
m_to_op('CALL_T') -> ?CALL_T ;
m_to_op('CALL_TR') -> ?CALL_TR ;
m_to_op('JUMP') -> ?JUMP ;
m_to_op('JUMPIF') -> ?JUMPIF ;
m_to_op('PUSH') -> ?PUSH ;
m_to_op('DUP') -> ?DUP ;
m_to_op('DUPA') -> ?DUPA ;
m_to_op('POP') -> ?POP ;
m_to_op('STORE') -> ?STORE ;
m_to_op('ADD') -> ?ADD ;
m_to_op('MUL') -> ?MUL ;
m_to_op('SUB') -> ?SUB ;
m_to_op('DIV') -> ?DIV ;
m_to_op('MOD') -> ?MOD ;
m_to_op('POW') -> ?POW ;
m_to_op('LT') -> ?LT ;
m_to_op('GT') -> ?GT ;
m_to_op('EQ') -> ?EQ ;
m_to_op('ELT') -> ?ELT ;
m_to_op('EGT') -> ?EGT ;
m_to_op('NEQ') -> ?NEQ ;
m_to_op('AND') -> ?AND ;
m_to_op('OR') -> ?OR ;
m_to_op('NOT') -> ?NOT ;
m_to_op('TUPLE') -> ?TUPLE ;
m_to_op('ELEMENT') -> ?ELEMENT ;
m_to_op('MAP_EMPTY') -> ?MAP_EMPTY ;
m_to_op('MAP_LOOKUP') -> ?MAP_LOOKUP ;
m_to_op('MAP_UPDATE') -> ?MAP_UPDATE ;
m_to_op('MAP_DELETE') -> ?MAP_DELETE ;
m_to_op('MAP_MEMBER') -> ?MAP_MEMBER ;
m_to_op('MAP_FROM_LIST') -> ?MAP_FROM_LIST ;
m_to_op('NIL') -> ?NIL ;
m_to_op('IS_NIL') -> ?IS_NIL ;
m_to_op('CONS') -> ?CONS ;
m_to_op('HD') -> ?HD ;
m_to_op('TL') -> ?TL ;
m_to_op('LENGTH') -> ?LENGTH ;
m_to_op('STR_EQ') -> ?STR_EQ ;
m_to_op('STR_JOIN') -> ?STR_JOIN ;
m_to_op('ADDR_TO_STR') -> ?ADDR_TO_STR ;
m_to_op('STR_REVERSE') -> ?STR_REVERSE ;
m_to_op('INT_TO_ADDR') -> ?INT_TO_ADDR ;
m_to_op('VARIANT') -> ?VARIANT ;
m_to_op('VARIANT_TEST') -> ?VARIANT_TEST ;
m_to_op('VARIANT_ELEMENT') -> ?VARIANT_ELEMENT ;
m_to_op('BITS_NONEA') -> ?BITS_NONEA ;
m_to_op('BITS_ALL') -> ?BITS_ALL ;
m_to_op('BITS_ALLA') -> ?BITS_ALLA ;
m_to_op('BITS_SET') -> ?BITS_SET ;
m_to_op('BITS_CLEAR') -> ?BITS_CLEAR ;
m_to_op('BITS_TEST') -> ?BITS_TEST ;
m_to_op('BITS_SUM') -> ?BITS_SUM ;
m_to_op('BITS_OR') -> ?BITS_OR ;
m_to_op('BITS_AND') -> ?BITS_AND ;
m_to_op('BITS_DIFF') -> ?BITS_DIFF ;
m_to_op('ADDRESS') -> ?ADDRESS ;
m_to_op('BALANCE') -> ?BALANCE ;
m_to_op('ORIGIN') -> ?ORIGIN ;
m_to_op('CALLER') -> ?CALLER ;
m_to_op('GASPRICE') -> ?GASPRICE ;
m_to_op('BLOCKHASH') -> ?BLOCKHASH ;
m_to_op('BENEFICIARY') -> ?BENEFICIARY ;
m_to_op('TIMESTAMP') -> ?TIMESTAMP ;
m_to_op('NUMBER') -> ?NUMBER ;
m_to_op('DIFFICULTY') -> ?DIFFICULTY ;
m_to_op('GASLIMIT') -> ?GASLIMIT ;
m_to_op('GAS') -> ?GAS ;
m_to_op('LOG0') -> ?LOG0 ;
m_to_op('LOG1') -> ?LOG1 ;
m_to_op('LOG2') -> ?LOG2 ;
m_to_op('LOG3') -> ?LOG3 ;
m_to_op('LOG4') -> ?LOG4 ;
m_to_op('ABORT') -> ?ABORT ;
m_to_op('EXIT') -> ?EXIT ;
m_to_op('DEACTIVATE') -> ?DEACTIVATE ;
m_to_op('INC') -> ?INC ;
m_to_op('DEC') -> ?DEC ;
m_to_op('INCA') -> ?INCA ;
m_to_op('DECA') -> ?DECA ;
m_to_op('INT_TO_STR') -> ?INT_TO_STR ;
m_to_op('SPEND') -> ?SPEND ;
m_to_op('ORACLE_REGISTER') -> ?ORACLE_REGISTER ;
m_to_op('ORACLE_QUERY') -> ?ORACLE_QUERY ;
m_to_op('ORACLE_RESPOND') -> ?ORACLE_RESPOND ;
m_to_op('ORACLE_EXTEND') -> ?ORACLE_EXTEND ;
m_to_op('ORACLE_GET_ANSWER') -> ?ORACLE_GET_ANSWER ;
m_to_op('ORACLE_GET_QUESTION') -> ?ORACLE_GET_QUESTION ;
m_to_op('ORACLE_QUERY_FEE') -> ?ORACLE_QUERY_FEE ;
m_to_op('AENS_RESOLVE') -> ?AENS_RESOLVE ;
m_to_op('AENS_PRECLAIM') -> ?AENS_PRECLAIM ;
m_to_op('AENS_CLAIM') -> ?AENS_CLAIM ;
m_to_op('AENS_UPDATE') -> ?AENS_UPDATE ;
m_to_op('AENS_TRANSFER') -> ?AENS_TRANSFER ;
m_to_op('AENS_REVOKE') -> ?AENS_REVOKE ;
m_to_op('ECVERIFY') -> ?ECVERIFY ;
m_to_op('SHA3') -> ?SHA3 ;
m_to_op('SHA256') -> ?SHA256 ;
m_to_op('BLAKE2B') -> ?BLAKE2B ;
m_to_op('RETURNR') -> ?RETURNR ;
m_to_op('MAP_LOOKUPD') -> ?MAP_LOOKUPD ;
m_to_op('SWITCH_V2') -> ?SWITCH_V2 ;
m_to_op('SWITCH_V3') -> ?SWITCH_V3 ;
m_to_op('SWITCH_VN') -> ?SWITCH_VN ;
m_to_op('FUNCTION') -> ?FUNCTION ;
m_to_op('EXTEND') -> ?EXTEND.
args(?NOP) -> 0;
args(?RETURN) -> 0;
args(?INCA) -> 0;
args(?DECA) -> 0;
args(?DUPA) -> 0;
args(?BITS_NONEA) -> 0;
args(?BITS_ALLA) -> 0;
args(?INC) -> 1;
args(?DEC) -> 1;
args(?RETURNR) -> 1;
args(?PUSH) -> 1;
args(?JUMP) -> 1;
args(?CALL) -> 1;
args(?CALL_T) -> 1;
args(?TUPLE) -> 1;
args(?MAP_EMPTY) -> 1;
args(?DUP) -> 1;
args(?POP) -> 1;
args(?NIL) -> 1;
args(?BITS_NONE) -> 1;
args(?BITS_ALL) -> 1;
args(?ADDRESS) -> 1;
args(?BALANCE) -> 1;
args(?ORIGIN) -> 1;
args(?CALLER) -> 1;
args(?GASPRICE) -> 1;
args(?BLOCKHASH) -> 1;
args(?BENEFICIARY) -> 1;
args(?TIMESTAMP) -> 1;
args(?NUMBER) -> 1;
args(?DIFFICULTY)-> 1;
args(?GASLIMIT) -> 1;
args(?GAS) -> 1;
args(?ABORT) -> 1;
args(?EXIT) -> 1;
args(?JUMPIF) -> 2;
args(?CALL_R) -> 2;
args(?CALL_TR) -> 2;
args(?HD) -> 2;
args(?TL) -> 2;
args(?NOT) -> 2;
args(?STORE) -> 2;
args(?LENGTH) -> 2;
args(?IS_NIL) -> 2;
args(?BITS_SUM) -> 2;
args(?BITS_ALL_N) -> 2;
args(?ADDR_TO_STR) -> 2;
args(?STR_REVERSE) -> 2;
args(?INT_TO_ADDR) -> 2;
args(?MAP_FROM_LIST) -> 2;
args(?ADD) -> 3;
args(?SUB) -> 3;
args(?MUL) -> 3;
args(?DIV) -> 3;
args(?MOD) -> 3;
args(?POW) -> 3;
args(?AND) -> 3;
args(?OR) -> 3;
args(?LT) -> 3;
args(?GT) -> 3;
args(?EGT) -> 3;
args(?ELT) -> 3;
args(?EQ) -> 3;
args(?NEQ) -> 3;
args(?CONS) -> 3;
args(?STR_EQ) -> 3;
args(?STR_JOIN) -> 3;
args(?MAP_MEMBER) -> 3;
args(?MAP_LOOKUP) -> 3;
args(?MAP_DELETE) -> 3;
args(?BITS_OR) -> 3;
args(?BITS_AND) -> 3;
args(?BITS_SET) -> 3;
args(?BITS_DIFF) -> 3;
args(?BITS_TEST) -> 3;
args(?BITS_CLEAR) -> 3;
args(?VARIANT_TEST) -> 3;
args(?VARIANT_ELEMENT) -> 3;
args(?INT_TO_STR) -> 3;
args(?SWITCH_V2) -> 3;
args(?SWITCH_V3) -> 4;
args(?ELEMENT) -> 4;
args(?VARIANT) -> 4;
args(?MAP_UPDATE) -> 4;
args(?MAP_LOOKUPD) -> 4;
args(?SWITCH_VN) -> 2;
args(_) -> 0. %% TODO do not allow this
end_bb(?RETURN) -> true;
end_bb(?RETURNR) -> true;
end_bb(?JUMP) -> true;
end_bb(?JUMPIF) -> true;
end_bb(?CALL) -> true;
end_bb(?CALL_T) -> true;
end_bb(?CALL_R) -> true;
end_bb(?CALL_TR) -> true;
end_bb(?SWITCH_V2) -> true;
end_bb(?SWITCH_V3) -> true;
end_bb(?SWITCH_VN) -> true;
end_bb(?ABORT) -> true;
end_bb(?EXIT) -> true;
end_bb(_) -> false.
+331
View File
@@ -0,0 +1,331 @@
-module(aeb_heap).
-export([ to_binary/1
, to_binary/2
, from_heap/3
, from_binary/2
, from_binary/3
, maps_with_next_id/1
, set_next_id/2
, heap_fragment/3
, heap_value/3
, heap_value/4
, heap_value_pointer/1
, heap_value_maps/1
, heap_value_offset/1
, heap_value_heap/1
, heap_value_byte_size/1
, heap_fragment_maps/1
, heap_fragment_offset/1
, heap_fragment_heap/1
]).
-export_type([binary_value/0, heap_value/0, offset/0, heap_fragment/0]).
-include_lib("aebytecode/include/aeb_typerep_def.hrl").
-include_lib("aebytecode/include/aeb_heap.hrl").
-type word() :: non_neg_integer().
-type pointer() :: word().
-opaque heap_fragment() :: #heap{}.
-type offset() :: non_neg_integer().
-type binary_value() :: binary().
-type heap_value() :: {pointer(), heap_fragment()}.
-spec maps_with_next_id(heap_fragment()) -> #maps{}.
%% Create just a maps value, don't keep rest of Heap
maps_with_next_id(#heap{maps = #maps{next_id = N}}) ->
#maps{ next_id = N }.
-spec set_next_id(heap_fragment(), non_neg_integer()) -> heap_fragment().
set_next_id(Heap, N) ->
Heap#heap{ maps = Heap#heap.maps#maps{ next_id = N } }.
%% -- data type heap_fragment
-spec heap_fragment(binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment().
heap_fragment(Heap) ->
heap_fragment(#maps{ next_id = 0 }, 0, Heap).
-spec heap_fragment(#maps{}, offset(),
binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_fragment().
heap_fragment(Maps, Offset, Heap) ->
#heap{maps = Maps, offset = Offset, heap = Heap}.
-spec heap_fragment_maps(heap_fragment()) -> #maps{}.
heap_fragment_maps(#heap{maps = Maps}) ->
Maps.
-spec heap_fragment_offset(heap_fragment()) -> offset().
heap_fragment_offset(#heap{offset = Offs}) ->
Offs.
-spec heap_fragment_heap(heap_fragment()) -> binary() | #{non_neg_integer() => non_neg_integer()}.
heap_fragment_heap(#heap{heap = Heap}) ->
Heap.
%% -- data type heap_value
-spec heap_value(#maps{}, pointer(),
binary() | #{non_neg_integer() => non_neg_integer()}) -> heap_value().
heap_value(Maps, Ptr, Heap) ->
heap_value(Maps, Ptr, Heap, 0).
-spec heap_value(#maps{}, pointer(),
binary() | #{non_neg_integer() => non_neg_integer()}, offset()) -> heap_value().
heap_value(Maps, Ptr, Heap, Offs) ->
{Ptr, heap_fragment(Maps, Offs, Heap)}.
-spec heap_value_pointer(heap_value()) -> pointer().
heap_value_pointer({Ptr, _}) -> Ptr.
-spec heap_value_maps(heap_value()) -> #maps{}.
heap_value_maps({_, Heap}) -> Heap#heap.maps.
-spec heap_value_offset(heap_value()) -> offset().
heap_value_offset({_, Heap}) -> Heap#heap.offset.
-spec heap_value_heap(heap_value()) ->
binary() | #{non_neg_integer() => non_neg_integer()}.
heap_value_heap({_, Heap}) -> Heap#heap.heap.
%% -- Byte size of a heap value ----------------------------------------------
-spec heap_value_byte_size(heap_value()) -> non_neg_integer().
heap_value_byte_size({_, Heap}) ->
Value = Heap#heap.heap,
Maps = Heap#heap.maps,
ValueSize =
if is_binary(Value) -> byte_size(Value);
true -> 0 end,
MapsSize =
lists:sum([ pmap_size(Map) || Map <- maps:values(Maps#maps.maps) ]),
ValueSize + MapsSize.
pmap_size(#pmap{data = stored}) -> 0;
pmap_size(#pmap{data = Data}) when is_map(Data) ->
lists:sum([ byte_size(Key) + byte_size(Val)
|| {Key, Val} <- maps:to_list(Data),
Val /= tombstone ]).
%% -- Value to binary --------------------------------------------------------
-spec to_binary(aeb_aevm_data:data()) -> aeb_aevm_data:heap().
%% Encode the data as a heap where the first word is the value (for unboxed
%% types) or a pointer to the value (for boxed types).
to_binary(Data) ->
to_binary(Data, 0).
to_binary(Data, BaseAddress) ->
{Address, Memory} = to_binary1(Data, BaseAddress + 32),
R = <<Address:256, Memory/binary>>,
R.
%% Allocate the data in memory, from the given address. Return a pair
%% of memory contents from that address and the value representing the
%% data.
to_binary1(Data,_Address) when is_integer(Data) ->
{Data,<<>>};
to_binary1(Data, Address) when is_binary(Data) ->
%% a string
Words = aeb_memory:binary_to_words(Data),
{Address,<<(size(Data)):256, << <<W:256>> || W <- Words>>/binary>>};
to_binary1({contract_bytearray, FateCode}, Address) when is_binary(FateCode) ->
Words = aeb_memory:binary_to_words(FateCode),
{Address,<<(size(FateCode)):256, << <<W:256>> || W <- Words>>/binary>>};
to_binary1(none, Address) -> to_binary1({variant, 0, []}, Address);
to_binary1({some, Value}, Address) -> to_binary1({variant, 1, [Value]}, Address);
to_binary1(word, Address) -> to_binary1({?TYPEREP_WORD_TAG}, Address);
to_binary1(string, Address) -> to_binary1({?TYPEREP_STRING_TAG}, Address);
to_binary1(typerep, Address) -> to_binary1({?TYPEREP_TYPEREP_TAG}, Address);
to_binary1(contract_bytearray, Address) -> to_binary1({?TYPEREP_CONTRACT_BYTEARRAY_TAG}, Address);
to_binary1(function, Address) -> to_binary1({?TYPEREP_FUN_TAG}, Address);
to_binary1({list, T}, Address) -> to_binary1({?TYPEREP_LIST_TAG, T}, Address);
to_binary1({option, T}, Address) -> to_binary1({variant, [[], [T]]}, Address);
to_binary1({tuple, Ts}, Address) -> to_binary1({?TYPEREP_TUPLE_TAG, Ts}, Address);
to_binary1({variant, Cons}, Address) -> to_binary1({?TYPEREP_VARIANT_TAG, Cons}, Address);
to_binary1({map, K, V}, Address) -> to_binary1({?TYPEREP_MAP_TAG, K, V}, Address);
to_binary1({variant, Tag, Args}, Address) ->
to_binary1(list_to_tuple([Tag | Args]), Address);
to_binary1(Map, Address) when is_map(Map) ->
Size = maps:size(Map),
%% Sort according to binary ordering
KVs = lists:sort([ {to_binary(K), to_binary(V)} || {K, V} <- maps:to_list(Map) ]),
{Address, <<Size:256, << <<(byte_size(K)):256, K/binary,
(byte_size(V)):256, V/binary>> || {K, V} <- KVs >>/binary >>};
to_binary1({}, _Address) ->
{0, <<>>};
to_binary1(Data, Address) when is_tuple(Data) ->
{Elems,Memory} = to_binaries(tuple_to_list(Data),Address+32*size(Data)),
ElemsBin = << <<W:256>> || W <- Elems>>,
{Address,<< ElemsBin/binary, Memory/binary >>};
to_binary1([],_Address) ->
<<Nil:256>> = <<(-1):256>>,
{Nil,<<>>};
to_binary1([H|T],Address) ->
to_binary1({H,T},Address).
to_binaries([],_Address) ->
{[],<<>>};
to_binaries([H|T],Address) ->
{HRep,HMem} = to_binary1(H,Address),
{TRep,TMem} = to_binaries(T,Address+size(HMem)),
{[HRep|TRep],<<HMem/binary, TMem/binary>>}.
%% Interpret a return value (a binary) using a type rep.
-spec from_heap(Type :: ?Type(), Heap :: binary(), Ptr :: integer()) ->
{ok, term()} | {error, term()}.
from_heap(Type, Heap, Ptr) ->
try {ok, from_binary(#{}, Type, Heap, Ptr)}
catch _:Err ->
%% io:format("** Error: from_heap failed with ~p\n ~p\n", [Err, erlang:get_stacktrace()]),
{error, Err}
end.
%% Base address is the address of the first word of the given heap.
-spec from_binary(T :: ?Type(),
Heap :: binary(),
BaseAddr :: non_neg_integer()) ->
{ok, term()} | {error, term()}.
from_binary(T, Heap = <<V:256, _/binary>>, BaseAddr) ->
from_heap(T, <<0:BaseAddr/unit:8, Heap/binary>>, V);
from_binary(_, Bin, _BaseAddr) ->
{error, {binary_too_short, Bin}}.
-spec from_binary(?Type(), binary()) -> {ok, term()} | {error, term()}.
from_binary(T, Heap) ->
from_binary(T, Heap, 0).
from_binary(_, word, _, V) ->
V;
from_binary(_, signed_word, _, V) ->
<<N:256/signed>> = <<V:256>>,
N;
from_binary(_, bool, _, V) ->
case V of
0 -> false;
1 -> true
end;
from_binary(_, string, Heap, V) ->
StringSize = heap_word(Heap,V),
BitAddr = 8*(V+32),
<<_:BitAddr,Bytes:StringSize/binary,_/binary>> = Heap,
Bytes;
from_binary(_, {tuple, []}, _, _) ->
{};
from_binary(Visited, {tuple,Cpts}, Heap, V) ->
check_circular_refs(Visited, V),
NewVisited = Visited#{V => true},
ElementNums = lists:seq(0, length(Cpts)-1),
TypesAndPointers = lists:zip(Cpts, ElementNums),
ElementAddress = fun(Index) -> V + 32 * Index end,
Element = fun(Index) ->
heap_word(Heap, ElementAddress(Index))
end,
Convert = fun(Type, Index) ->
from_binary(NewVisited, Type, Heap, Element(Index))
end,
Elements = [Convert(T, I) || {T,I} <- TypesAndPointers],
list_to_tuple(Elements);
from_binary(Visited, {list, Elem}, Heap, V) ->
<<Nil:256>> = <<(-1):256>>,
if V==Nil ->
[];
true ->
{H,T} = from_binary(Visited, {tuple,[Elem,{list,Elem}]},Heap,V),
[H|T]
end;
from_binary(Visited, {option, A}, Heap, V) ->
from_binary(Visited, {variant_t, [{none, []}, {some, [A]}]}, Heap, V);
from_binary(Visited, {variant, Cons}, Heap, V) ->
Tag = heap_word(Heap, V),
Args = lists:nth(Tag + 1, Cons),
Visited1 = Visited#{V => true},
{variant, Tag, tuple_to_list(from_binary(Visited1, {tuple, Args}, Heap, V + 32))};
from_binary(Visited, {variant_t, TCons}, Heap, V) -> %% Tagged variants
{Tags, Cons} = lists:unzip(TCons),
{variant, I, Args} = from_binary(Visited, {variant, Cons}, Heap, V),
Tag = lists:nth(I + 1, Tags),
case Args of
[] -> Tag;
_ -> list_to_tuple([Tag | Args])
end;
from_binary(_Visited, {map, A, B}, Heap, Ptr) ->
%% FORMAT: [Size] [KeySize] Key [ValSize] Val .. [KeySize] Key [ValSize] Val
Size = heap_word(Heap, Ptr),
map_binary_to_value(A, B, Size, Heap, Ptr + 32);
from_binary(Visited, typerep, Heap, V) ->
check_circular_refs(Visited, V),
Tag = heap_word(Heap, V),
Arg1 = fun(T, I) -> from_binary(Visited#{V => true}, T, Heap, heap_word(Heap, V + 32 * I)) end,
Arg = fun(T) -> Arg1(T, 1) end,
case Tag of
?TYPEREP_WORD_TAG -> word;
?TYPEREP_STRING_TAG -> string;
?TYPEREP_TYPEREP_TAG -> typerep;
?TYPEREP_LIST_TAG -> {list, Arg(typerep)};
?TYPEREP_TUPLE_TAG -> {tuple, Arg({list, typerep})};
?TYPEREP_VARIANT_TAG -> {variant, Arg({list, {list, typerep}})};
?TYPEREP_MAP_TAG -> {map, Arg(typerep), Arg1(typerep, 2)};
?TYPEREP_FUN_TAG -> function;
?TYPEREP_CONTRACT_BYTEARRAY_TAG -> contract_bytearray
end;
from_binary(_, contract_bytearray, Heap, V) ->
FateCodeSize = heap_word(Heap, V),
BitAddr = 8*(V+32),
<<_:BitAddr,Bytes:FateCodeSize/binary,_/binary>> = Heap,
{contract_bytearray, Bytes}.
map_binary_to_value(KeyType, ValType, N, Bin, Ptr) ->
%% Avoid looping on bogus sizes
MaxN = byte_size(Bin) div 64,
Heap = heap_fragment(Bin),
map_from_binary({value, KeyType, ValType}, min(N, MaxN), Heap, Ptr, #{}).
map_from_binary(_, 0, _, _, Map) -> Map;
map_from_binary({value, KeyType, ValType} = Output, I, Heap, Ptr, Map) ->
KeySize = get_word(Heap, Ptr),
KeyPtr = Ptr + 32,
KeyBin = get_chunk(Heap, KeyPtr, KeySize),
ValSize = get_word(Heap, KeyPtr + KeySize),
ValPtr = KeyPtr + KeySize + 32,
ValBin = get_chunk(Heap, ValPtr, ValSize),
%% Keys and values are self contained binaries
{ok, Key} = from_binary(KeyType, KeyBin),
{ok, Val} = from_binary(ValType, ValBin),
map_from_binary(Output, I - 1, Heap, ValPtr + ValSize, Map#{Key => Val}).
check_circular_refs(Visited, V) ->
case maps:is_key(V, Visited) of
true -> exit(circular_references);
false -> ok
end.
heap_word(Heap, Addr) when is_binary(Heap) ->
BitSize = 8*Addr,
<<_:BitSize,W:256,_/binary>> = Heap,
W;
heap_word(Heap, Addr) when is_map(Heap) ->
0 = Addr rem 32, %% Check that it's word aligned.
maps:get(Addr, Heap, 0).
get_word(#heap{offset = Offs, heap = Mem}, Addr) when Addr >= Offs ->
get_word(Mem, Addr - Offs);
get_word(Mem, Addr) when is_binary(Mem) ->
<<_:Addr/unit:8, Word:256, _/binary>> = Mem,
Word.
get_chunk(#heap{offset = Offs, heap = Mem}, Addr, Bytes) when Addr >= Offs ->
get_chunk(Mem, Addr - Offs, Bytes);
get_chunk(Mem, Addr, Bytes) when is_binary(Mem) ->
<<_:Addr/unit:8, Chunk:Bytes/binary, _/binary>> = Mem,
Chunk.
+19
View File
@@ -0,0 +1,19 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc
%%% Memory speifics that compiler and VM need to agree upon
%%% @end
%%% Created : 19 Dec 2018
%%%-------------------------------------------------------------------
-module(aeb_memory).
-export([binary_to_words/1]).
binary_to_words(<<>>) ->
[];
binary_to_words(<<N:256,Bin/binary>>) ->
[N|binary_to_words(Bin)];
binary_to_words(Bin) ->
binary_to_words(<<Bin/binary,0>>).
+3
View File
@@ -51,6 +51,7 @@ opcode(?SHL) -> ?SHL;
opcode(?SHR) -> ?SHR;
opcode(?SAR) -> ?SAR;
opcode(?SHA3) -> ?SHA3;
opcode(?CREATOR) -> ?CREATOR;
opcode(?ADDRESS) -> ?ADDRESS;
opcode(?BALANCE) -> ?BALANCE;
opcode(?ORIGIN) -> ?ORIGIN;
@@ -191,6 +192,7 @@ mnemonic(?SHL) -> 'SHL' ;
mnemonic(?SHR) -> 'SHR' ;
mnemonic(?SAR) -> 'SAR' ;
mnemonic(?SHA3) -> 'SHA3' ;
mnemonic(?CREATOR) -> 'CREATOR' ;
mnemonic(?ADDRESS) -> 'ADDRESS' ;
mnemonic(?BALANCE) -> 'BALANCE' ;
mnemonic(?ORIGIN) -> 'ORIGIN' ;
@@ -332,6 +334,7 @@ m_to_op('SHL') -> ?SHL ;
m_to_op('SHR') -> ?SHR ;
m_to_op('SAR') -> ?SAR ;
m_to_op('SHA3') -> ?SHA3 ;
m_to_op('CREATOR') -> ?CREATOR ;
m_to_op('ADDRESS') -> ?ADDRESS ;
m_to_op('BALANCE') -> ?BALANCE ;
m_to_op('ORIGIN') -> ?ORIGIN ;
+1 -1
View File
@@ -1,6 +1,6 @@
{application, aebytecode,
[{description, "Bytecode definitions, serialization and deserialization for aeternity."},
{vsn, "2.0.1"},
{vsn, "3.1.1"},
{registered, []},
{applications,
[kernel,
+11 -10
View File
@@ -25,12 +25,7 @@ read_file(File) ->
Asm.
assemble(Asm) ->
{Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, []),
{Env, BC}.
disassemble(BC) ->
aeb_fate_asm:bytecode_to_fate_code(BC, []).
aeb_fate_asm:asm_to_bytecode(Asm, []).
asm_disasm_idenity_test() ->
check_roundtrip(identity).
@@ -50,16 +45,22 @@ sources() ->
, "remote"
, "test"
, "tuple"
, "mapofmap"
, "immediates"
, "names"
, "oracles"
, "meta"
].
check_roundtrip(File) ->
AssemblerCode = read_file(File),
{_Env, ByteCode} = assemble(AssemblerCode),
FateCode = disassemble(ByteCode),
FateCode = aeb_fate_code:deserialize(ByteCode),
DissasmCode = aeb_fate_asm:to_asm(FateCode),
io:format("~s~n", [AssemblerCode]),
io:format("~s~n", [DissasmCode]),
{_Env2, ByteCode2} = assemble(DissasmCode),
ByteCode3 = aeb_fate_code:serialize(FateCode),
Code1 = aeb_fate_asm:strip(ByteCode),
Code2 = aeb_fate_asm:strip(ByteCode2),
?assertEqual(Code1, Code2).
Code3 = aeb_fate_asm:strip(ByteCode3),
?assertEqual(Code1, Code2),
?assertEqual(Code1, Code3).
+23 -8
View File
@@ -47,6 +47,10 @@ sources() ->
[aeb_fate_data:make_integer(0),
aeb_fate_data:make_integer(1),
True, False, Unit, Nil, EmptyString, EmptyMap,
aeb_fate_data:make_hash(<<1,2,3,4,5>>),
aeb_fate_data:make_signature(<<1,2,3,4,5>>),
aeb_fate_data:make_contract(<<1,2,3,4,5>>),
aeb_fate_data:make_channel(<<1,2,3,4,5>>),
aeb_fate_data:make_list([True]),
aeb_fate_data:make_address(
<<0,1,2,3,4,5,6,7,8,9,
@@ -61,6 +65,7 @@ sources() ->
"0123456789012345678901234567890123456789">>), %% Magic concat 80 char string.
aeb_fate_data:make_tuple({True, FortyTwo}),
aeb_fate_data:make_tuple(list_to_tuple(make_int_list(65))),
aeb_fate_data:make_tuple(list_to_tuple(make_int_list(16))),
aeb_fate_data:make_map(#{ aeb_fate_data:make_integer(1) => True, aeb_fate_data:make_integer(2) => False}),
aeb_fate_data:make_map(#{ aeb_fate_data:make_string(<<"foo">>) => aeb_fate_data:make_tuple({FortyTwo, True})}),
aeb_fate_data:make_list(make_int_list(3)),
@@ -71,12 +76,22 @@ sources() ->
aeb_fate_data:make_bits(1),
aeb_fate_data:make_bits(-1),
aeb_fate_data:make_list(make_int_list(65)),
aeb_fate_data:make_variant(2, 0, {FortyTwo}),
aeb_fate_data:make_variant(2, 1, {}),
aeb_fate_data:make_list([aeb_fate_data:make_variant(3, 0, {})]),
aeb_fate_data:make_variant(255, 254, {}),
aeb_fate_data:make_variant(5, 3, {aeb_fate_data:make_boolean(true),
aeb_fate_data:make_list(make_int_list(3)),
aeb_fate_data:make_string(<<"foo">>)})
aeb_fate_data:make_variant([1,2,3], 0, {FortyTwo}),
aeb_fate_data:make_variant([2,0], 1, {}),
aeb_fate_data:make_list([aeb_fate_data:make_variant([0,0,0], 0, {})]),
aeb_fate_data:make_variant([0|| _<-lists:seq(1,255)], 254, {}),
aeb_fate_data:make_variant([0,1,2,3,4,5],
3, {aeb_fate_data:make_boolean(true),
aeb_fate_data:make_list(make_int_list(3)),
aeb_fate_data:make_string(<<"foo">>)}),
%% contract C =
%% type state = int
%% entrypoint init() = 2137
].
%% cb_+FFGA6Af6sHTrctrcNGwEa8MPei7iEHIjnxcsBzlA5IK0Yn11sCllP5E1kQfADcANwAaDoJvgggZAQM/jC8BEUTWRB8RaW5pdIIvAIU0LjMuMAD7u
aeb_fate_data:make_contract_bytearray(
<<248,81,70,3,160,31,234,193,211,173,203,107,112,209,176,17,175,12,61,232,187,
136,65,200,142,124,92,176,28,229,3,146,10,209,137,245,214,192,165,148,254,68,
214,68,31,0,55,0,55,0,26,14,130,111,130,8,25,1,3,63,140,47,1,17,68,214,68,31,
17,105,110,105,116,130,47,0,133,52,46,51,46,48,0>>)
].
+86
View File
@@ -0,0 +1,86 @@
;; CONTRACT immediates
FUNCTION integer() : integer
RETURNR 42
FUNCTION neg_integer() : integer
RETURNR -2374683271468723648732648736498712634876147
FUNCTION hex_integer() : integer
RETURNR 0x0deadbeef0
FUNCTION bool() : boolean
RETURNR true
FUNCTION bool_f() : boolean
RETURNR false
FUNCTION string() : string
RETURNR "Hello"
FUNCTION map() : {map, integer, boolean}
RETURNR {}
FUNCTION map2() : {map, integer, boolean}
RETURNR {1 => true}
FUNCTION map3() : {map, integer, boolean}
RETURNR {1 => true,
2 => false}
FUNCTION map4() : {map, integer, {map, string, boolean}}
RETURNR {1 => { "foo" => true, "bar" => false},
2 => {},
3 => { "foo" => false}}
FUNCTION nil() : {list, integer}
RETURNR []
FUNCTION list1() : {list, integer}
RETURNR [1]
FUNCTION list2() : {list, integer}
RETURNR [1, 2]
FUNCTION no_bits() : bits
RETURNR <>
FUNCTION all_bits() : bits
RETURNR !<>
FUNCTION some_bits() : bits
RETURNR <101010>
FUNCTION many_bits() : bits
RETURNR !<010101>
FUNCTION group_bits() : bits
RETURNR <1010 1010 0011 1001>
FUNCTION unit() : {tuple, []}
RETURNR ()
FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]}]}
RETURNR (42, true, "FooBar", (1, 2))
FUNCTION address() : address
RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION oracle() : oracle
RETURNR @ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION contract() : contract
RETURNR @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION channel() : channel
RETURNR @ch_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
;; Option(integer) = NONE | SOME(integer)
FUNCTION variant_none() : {variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR (| [0,1] | 0 | () |)
;; Option(integer) = NONE | SOME(integer)
FUNCTION variant_some() : {variant, [{tuple, []}, {tuple, [integer]}]}
RETURNR (| [0,1] | 1 | (42) |)
+7
View File
@@ -0,0 +1,7 @@
;; CONTRACT mapofmap
FUNCTION map() : {map, integer, {map, string, boolean}}
RETURNR {1 => { "foo" => true, "bar" => false},
2 => {},
3 => { "foo" => false}}
+1 -1
View File
@@ -2,7 +2,7 @@
FUNCTION call(integer):integer
STORE var1 arg0
PUSH 0
CALL write
CALL "write"
PUSH var1
RETURN
+12
View File
@@ -0,0 +1,12 @@
;; CONTRACT meta
FUNCTION meta() : boolean
CREATE @cb_+PJGA6A4Fz4T2LHV5knITCldR3rqO7HrXO2zhOAR9JWNbhf8Q8C4xbhx/gx8JckANwAXfQBVACAAAP4vhlvZADcABwECgv5E1kQfADcBBzcACwAWMBReAHMAFjBvJFMAFjBvggOoFAAUABQSggABAz/+tIwWhAA3AAdTAAD+1jB5kAQ3AAcLAAD+6MRetgA3AQc3ABoGggABAz+4TS8GEQx8JclFY2FsbGVyX2lzX2NyZWF0b3IRL4Zb2Q1nZXQRRNZEHxFpbml0EbSMFoQdYmFsYW5jZRHWMHmQFXZhbHVlEejEXrYNc2V0gi8AhTQuMy4wAUqQ8s4= a 2137
CLONE a arg0 2137 false
CLONE_G a arg0 2137 10000 false
BYTECODE_HASH a a
BYTECODE_HASH a a
EQ a a a
RETURNR a
+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
+8 -12
View File
@@ -19,27 +19,23 @@ FUNCTION inc(integer) -> integer
FUNCTION call(integer) -> integer
INCA
CALL inc
CALL "inc"
INCA
RETURN
FUNCTION tailcall(integer) -> integer
INCA
CALL_T inc
CALL_T "inc"
FUNCTION remote_call(integer) : integer
PUSH arg0
CALL_R remote.add_five
INCA
RETURN
FUNCTION remote_tailcall(integer) : integer
PUSH arg0
CALL_TR remote add_five
;; FUNCTION remote_call(integer) : integer
;; PUSH arg0
;; CALL_R remote.add_five {tuple, [integer]} integer 0 ;; typereps don't parse
;; INCA
;; RETURN
;; Test the code from the shell
;; _build/default/rel/aessembler/bin/aessembler console
;; 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, []).
+6 -6
View File
@@ -1,13 +1,13 @@
FUNCTION make_0tuple():{tuple, []}
;; BB : 0
TUPLE 0
TUPLE a 0
RETURN
FUNCTION make_2tuple(integer, integer):{tuple, [integer, integer]}
;; BB : 0
PUSH arg0
PUSH arg1
TUPLE 2
TUPLE a 2
RETURN
FUNCTION make_5tuple(integer, integer, integer, integer, integer):
@@ -18,18 +18,18 @@ FUNCTION make_5tuple(integer, integer, integer, integer, integer):
PUSH arg2
PUSH arg3
PUSH arg4
TUPLE 5
TUPLE a 5
RETURN
FUNCTION element1(integer, integer): integer
;; BB : 0
PUSH arg0
PUSH arg1
TUPLE 2
ELEMENT integer a 1 a
TUPLE a 2
ELEMENT a 1 a
RETURN
FUNCTION element({tuple, [integer, integer]}, integer): integer
;; BB : 0
ELEMENT integer a arg1 arg0
ELEMENT a arg1 arg0
RETURN