Compare commits

...

137 Commits

Author SHA1 Message Date
cc4fd04019 Update test path
All checks were successful
Gajumaru Bytecode Tests / tests (push) Successful in 47m48s
2025-02-24 20:59:02 +09:00
97cea33be8 Dep ref update (#237)
All checks were successful
Gajumaru Bytecode Tests / tests (push) Successful in -3m36s
Reviewed-on: #237
Co-authored-by: Craig Everett <zxq9@zxq9.com>
Co-committed-by: Craig Everett <zxq9@zxq9.com>
2025-01-23 21:41:25 +09:00
1cdcb9150b Revamp (#235)
All checks were successful
Gajumaru Bytecode Tests / tests (push) Successful in -3m34s
Add Gitea tests
Rename
Remove oracle references
Package for zx

Reviewed-on: #235
Reviewed-by: dimitar.p.ivanov <dimitarivanov@qpq.swiss>
Co-authored-by: Craig Everett <zxq9@zxq9.com>
Co-committed-by: Craig Everett <zxq9@zxq9.com>
2025-01-22 21:12:54 +09:00
Hans Svensson
b47b2fe23c Merge branch 'drop_oracles' into 'master'
A few mandatory changes to FATE ops to drop Oracles

See merge request ioecs/aebytecode!117
2024-03-07 11:28:09 +00:00
Hans Svensson
5e38d6e829 A few mandatory changes to FATE ops to drop Oracles 2024-03-07 12:18:24 +01:00
Hans Svensson
009e036192
Merge pull request #116 from aeternity/add_chain_network_id
Add NETWORK_ID FATE instruction
2023-06-30 18:25:31 +02:00
Hans Svensson
37808419a8 Bump version to 3.4.0 2023-06-22 10:12:46 +02:00
Hans Svensson
f4c3782888 Patch check_numbering to allow for DBG_ at end 2023-06-22 10:08:41 +02:00
Hans Svensson
1688f85f2b Add NETWORK_ID and push DBG_* to higher opcodes 2023-06-22 08:35:32 +02:00
Gaith Hallak
b38349274f
Bump version to 3.3.0 (#115) 2023-05-28 13:40:20 +03:00
Hans Svensson
b8d593e351
Add instructions for arbitrary sized byte arrays (#114)
* Encode/decode bytes()/{bytes, any} as bytes(-1)

* Add 5 new bytes instructions

* reorder instructions and clarify some operations
2023-05-27 17:13:20 +03:00
Gaith Hallak
0f7529b26a
Introduce debugging instructions (#113)
* Add DBGLOC fate op

* Add DBGDEF and DBGUNDEF

* Change the type of arg for dbgdef and dbgundef

* Change DBGUNDEF to end_bb = true

* No safe sanity check for dbgundef

* Rename DBGDEF and DBGUNDEF to DBG_DEF and DBG_UNDEF

* Revert "No safe sanity check for dbgundef"

This reverts commit ee4949777fd988b76b9f854a2523a64a6dcdf591.

* Rename DBGLOC to DBG_LOC

* Remove column from DBG_LOC

* Add DBG_CALL and DBG_RETURN

* Update the docs for debug opcodes

* Add a DBG_CALL_R for remote calls

* Remove is_tail_call from DBG_CALL_R

* Revert "Remove is_tail_call from DBG_CALL_R"

This reverts commit a620c9c34b1c0ace77253ec0eabe6ac0b8e77ad2.

* Revert "Add a DBG_CALL_R for remote calls"

This reverts commit a336314cfc3930b348fb73352de8c108eba72973.

* Revert "Add DBG_CALL and DBG_RETURN"

This reverts commit db9766ac746b7f2d34a6f09a2f528be07b4226ea.

* Add DBG_CONTRACT op

* Upgrade aeserialization dep

* Use aeserialization v1.0.0

* Use aeserialization tag v1.0.0

* Remove debug instructions from AEVM
2023-05-25 18:09:42 +03:00
Gaith Hallak
2a0a397afa
Bump version to 3.2.0 (#112)
* Bump version to 3.2.0

* Bump version in rebar.config
2022-10-07 13:25:57 +03:00
Radosław Rowicki
093bcd6204
Merge pull request #111 from aeternity/enable-fate-code-mods
Export FATE code setters
2022-10-03 14:33:59 +02:00
radrow
6601ad2d38 Enable FATE code modifications 2022-09-16 10:54:48 +02:00
Dincho Todorov
578ebe2a8a
Switch to OTP23 (#110) 2022-08-29 14:26:17 +03:00
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 d16f9a9579d0b901d8deb17dec87f6aa36d09744.
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
58 changed files with 2053 additions and 1599 deletions

View File

@ -1,37 +0,0 @@
version: 2.1
executors:
aebuilder:
docker:
- image: aeternity/builder
user: builder
working_directory: ~/aebytecode
jobs:
build:
executor: aebuilder
steps:
- checkout
- restore_cache:
keys:
- dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }}
- dialyzer-cache-v1-{{ .Branch }}-
- dialyzer-cache-v1-
- run:
name: Build
command: rebar3 compile
- run:
name: Static Analysis
command: make dialyzer
- run:
name: Eunit
command: make eunit
- run:
name: Common Tests
command: make test
- save_cache:
key: dialyzer-cache-v1-{{ .Branch }}-{{ .Revision }}
paths:
- _build/default/rebar3_20.3.8_plt
- store_artifacts:
path: _build/test/logs

View File

@ -0,0 +1,15 @@
name: Gajumaru Bytecode Tests
run-name: ${{ gitea.actor }} testing Gajumaru Bytecode
on: [push, workflow_dispatch]
jobs:
tests:
runs-on: linux_amd64
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: test
run: |
. /home/act_runner/.erts/27.2.1/activate
make dialyzer
make eunit

20
.gitignore vendored
View File

@ -8,21 +8,21 @@ ebin/*.beam
rel/example_project rel/example_project
.concrete/DEV_MODE .concrete/DEV_MODE
.rebar .rebar
aeb_asm_scan.erl gmb_asm_scan.erl
aeb_fate_asm_scan.erl gmb_fate_asm_scan.erl
aeb_fate_asm_scan.xrl gmb_fate_asm_scan.xrl
_build/ _build/
aefateasm gmfateasm
include/aeb_fate_opcodes.hrl include/gmb_fate_opcodes.hrl
src/aeb_fate_opcodes.erl src/gmb_fate_opcodes.erl
src/aeb_fate_ops.erl src/gmb_fate_ops.erl
src/aeb_fate_pp.erl src/gmb_fate_pp.erl
*.erl~ *.erl~
*.hrl~ *.hrl~
*.aes~ *.aes~
doc doc
cover cover
aefate gmfate
current_counterexample.eqc current_counterexample.eqc
.rebar3 .rebar3
ebin ebin/*.beam

1
Emakefile Normal file
View File

@ -0,0 +1 @@
{"src/*", [debug_info, {i, "include/"}, {outdir, "ebin/"}]}.

View File

@ -1,5 +1,6 @@
ISC License ISC License
Copyright (c) 2025, QPQ AG
Copyright (c) 2017, aeternity developers Copyright (c) 2017, aeternity developers
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any

View File

@ -1,6 +1,6 @@
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 GENERATED_SRC = src/gmb_fate_opcodes.erl src/gmb_fate_ops.erl include/gmb_fate_opcodes.hrl src/gmb_fate_asm_scan.xrl src/gmb_fate_pp.erl
GENERATOR_DEPS = ebin/aeb_fate_generate_ops.beam src/aeb_fate_asm_scan.template GENERATOR_DEPS = ebin/gmb_fate_generate_ops.beam src/gmb_fate_asm_scan.template
REBAR ?= rebar3 REBAR ?= ./rebar3
all: local all: local
@ -15,7 +15,8 @@ console: local
clean: clean:
@$(REBAR) clean @$(REBAR) clean
rm -f $(GENERATED_SRC) rm -f $(GENERATED_SRC)
rm -f ebin/* rm -f ebin/*.beam
rm -rf _build
dialyzer: local dialyzer: local
@$(REBAR) as local dialyzer @$(REBAR) as local dialyzer
@ -30,7 +31,7 @@ test: local
@$(REBAR) as local eunit @$(REBAR) as local eunit
ebin/%.beam: src/%.erl ebin/%.beam: src/%.erl
erlc -o $(dir $@) $< erlc +debug_info -o $(dir $@) $<
$(GENERATED_SRC): $(GENERATOR_DEPS) $(GENERATED_SRC): $(GENERATOR_DEPS)
erl -pa ebin/ -noshell -s aeb_fate_generate_ops gen_and_halt src/ include/ erl -pa ebin/ -noshell -s gmb_fate_generate_ops gen_and_halt src/ include/

View File

@ -1,7 +1,7 @@
# aebytecode # gmbytecode
An library and stand alone assembler for aeternity bytecode. A library and stand alone assembler for Gajumaru bytecode.
This version supports Aevm bytecode and Fate bytecode. This version supports AEVM bytecode and FATE bytecode.
## Build ## Build
@ -19,7 +19,7 @@ Fate code exists in 3 formats:
3. Internal. This is an Erlang representation of fate code 3. Internal. This is an Erlang representation of fate code
Used by this particular engin implementation. Used by this particular engin implementation.
This library handles all tree representations. This library handles all three representations.
The byte code format is described in a separate document. The byte code format is described in a separate document.
The internal format is described in a separate document. The internal format is described in a separate document.
The text representation is described below. The text representation is described below.
@ -107,6 +107,9 @@ Immediate values can be of 10 types:
10. Bytes: #{base64char}+ 10. Bytes: #{base64char}+
`#AQIDCioLFQ==` `#AQIDCioLFQ==`
11. Contract bytearray (code of another smart contract)
`@cb_+PJGA6A4Fz4T2LHV5knITCldR3rqO7HrXO2zhOAR9JWNbhf8Q8C4xbhx/gx8JckANwAXfQBVACAAAP4vhlvZADcABwECgv5E1kQfADcBBzcACwAWMBReAHMAFjBvJFMAFjBvggOoFAAUABQSggABAz/+tIwWhAA3AAdTAAD+1jB5kAQ3AAcLAAD+6MRetgA3AQc3ABoGggABAz+4TS8GEQx8JclFY2FsbGVyX2lzX2NyZWF0b3IRL4Zb2Q1nZXQRRNZEHxFpbml0EbSMFoQdYmFsYW5jZRHWMHmQFXZhbHVlEejEXrYNc2V0gi8AhTQuMy4wAUqQ8s4=`
Where Where

View File

16
ebin/gmbytecode.app Normal file
View File

@ -0,0 +1,16 @@
{application,gmbytecode,
[{description,"Bytecode definitions, serialization and deserialization for the Gajumaru."},
{vsn,"3.4.1"},
{registered,[]},
{applications,[kernel,stdlib,eblake2,gmserialization,getopt]},
{env,[]},
{modules,[gmb_aevm_abi,gmb_aevm_data,gmb_asm,gmb_asm_scan,
gmb_disassemble,gmb_fate_abi,gmb_fate_asm,
gmb_fate_asm_scan,gmb_fate_code,gmb_fate_data,
gmb_fate_encoding,gmb_fate_generate_docs,
gmb_fate_generate_ops,gmb_fate_maps,gmb_fate_opcodes,
gmb_fate_ops,gmb_fate_pp,gmb_heap,gmb_memory,
gmb_opcodes,gmb_primops,gmfateasm]},
{maintainers,[]},
{licenses,[]},
{links,[]}]}.

View File

@ -5,6 +5,7 @@
-define(FATE_LIST_T, list()). -define(FATE_LIST_T, list()).
-define(FATE_UNIT_T, {tuple, {}}). -define(FATE_UNIT_T, {tuple, {}}).
-define(FATE_MAP_T, #{ fate_type() => fate_type() }). -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_STRING_T, binary()).
-define(FATE_ADDRESS_T, {address, <<_:256>>}). -define(FATE_ADDRESS_T, {address, <<_:256>>}).
-define(FATE_BYTES_T(N), {bytes, binary()}). -define(FATE_BYTES_T(N), {bytes, binary()}).
@ -16,10 +17,16 @@
-define(FATE_VOID_T, void). -define(FATE_VOID_T, void).
-define(FATE_TUPLE_T, {tuple, tuple()}). -define(FATE_TUPLE_T, {tuple, tuple()}).
-define(FATE_BITS_T, {bits, integer()}). -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_INTEGER(X), (is_integer(X))).
-define(IS_FATE_LIST(X), (is_list(X))). -define(IS_FATE_LIST(X), (is_list(X))).
-define(IS_FATE_STRING(X), (is_binary(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_MAP(X), (is_map(X))).
-define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))). -define(IS_FATE_TUPLE(X), (is_tuple(X) andalso (tuple == element(1, X) andalso is_tuple(element(2, X))))).
-define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))). -define(IS_FATE_ADDRESS(X), (is_tuple(X) andalso (address == element(1, X) andalso is_binary(element(2, X))))).
@ -39,6 +46,8 @@
))). ))).
-define(IS_FATE_BOOLEAN(X), is_boolean(X)). -define(IS_FATE_BOOLEAN(X), is_boolean(X)).
-define(IS_FATE_TYPEREP(X), (is_tuple(X) andalso tuple_size(X) =:= 2 andalso element(1, X) =:= typerep)). -define(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_UNIT, {tuple, {}}).
-define(FATE_TUPLE(T), {tuple, T}). -define(FATE_TUPLE(T), {tuple, T}).
@ -50,6 +59,8 @@
-define(FATE_CHANNEL(X), {channel, X}). -define(FATE_CHANNEL(X), {channel, X}).
-define(FATE_BITS(B), {bits, B}). -define(FATE_BITS(B), {bits, B}).
-define(FATE_TYPEREP(T), {typerep, T}). -define(FATE_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_INTEGER_VALUE(X), (X)).
-define(FATE_BOOLEAN_VALUE(X), (X)). -define(FATE_BOOLEAN_VALUE(X), (X)).
@ -63,8 +74,11 @@
-define(FATE_CHANNEL_VALUE(X), (element(2, X))). -define(FATE_CHANNEL_VALUE(X), (element(2, X))).
-define(FATE_BITS_VALUE(X), (element(2, X))). -define(FATE_BITS_VALUE(X), (element(2, X))).
-define(FATE_MAP_VALUE(X), (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_MAP_SIZE(X), (map_size(X))).
-define(FATE_STRING_SIZE(X), (byte_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_TRUE, true).
-define(FATE_FALSE, false). -define(FATE_FALSE, false).
-define(FATE_NIL, []). -define(FATE_NIL, []).
@ -72,8 +86,14 @@
-define(FATE_EMPTY_STRING, <<>>). -define(FATE_EMPTY_STRING, <<>>).
-define(FATE_STRING(S), S). -define(FATE_STRING(S), S).
-define(FATE_VARIANT(Arity, Tag,T), {variant, Arity, Tag, T}). -define(FATE_VARIANT(Arity, Tag,T), {variant, Arity, Tag, T}).
-define(FATE_CONTRACT_BYTEARRAY(B), {contract_bytearray, B}).
% Result of gmb_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_INTEGER(X), X).
-define(MAKE_FATE_LIST(X), X). -define(MAKE_FATE_LIST(X), X).
-define(MAKE_FATE_MAP(X), X). -define(MAKE_FATE_MAP(X), X).
-define(MAKE_FATE_STRING(X), X). -define(MAKE_FATE_STRING(X), X).
-define(MAKE_FATE_CONTRACT_BYTEARRAY(X), {contract_bytearray, X}).

View File

@ -1,15 +1,15 @@
-record(pmap, {key_t :: aeb_aevm_data:type(), -record(pmap, {key_t :: gmb_aevm_data:type(),
val_t :: aeb_aevm_data:type(), val_t :: gmb_aevm_data:type(),
parent :: none | non_neg_integer(), parent :: none | non_neg_integer(),
size = 0 :: non_neg_integer(), size = 0 :: non_neg_integer(),
data :: #{aeb_heap:binary_value() => aeb_heap:binary_value() | tombstone} data :: #{gmb_heap:binary_value() => gmb_heap:binary_value() | tombstone}
| stored}). | stored}).
-record(maps, { maps = #{} :: #{ non_neg_integer() => #pmap{} } -record(maps, { maps = #{} :: #{ non_neg_integer() => #pmap{} }
, next_id = 0 :: non_neg_integer() }). , next_id = 0 :: non_neg_integer() }).
-record(heap, { maps :: #maps{}, -record(heap, { maps :: #maps{},
offset :: aeb_heap:offset(), offset :: gmb_heap:offset(),
heap :: binary() | #{non_neg_integer() => non_neg_integer()} }). heap :: binary() | #{non_neg_integer() => non_neg_integer()} }).

View File

@ -186,13 +186,15 @@
-define(PRIM_CALL_MAP_TOLIST, 305). -define(PRIM_CALL_MAP_TOLIST, 305).
-define(PRIM_CALL_IN_CRYPTO_RANGE(__TTYPE__), (((__TTYPE__) > 399) andalso ((__TTYPE__) < 500))). -define(PRIM_CALL_IN_CRYPTO_RANGE(__TTYPE__), (((__TTYPE__) > 399) andalso ((__TTYPE__) < 500))).
-define(PRIM_CALL_CRYPTO_ECVERIFY, 400). -define(PRIM_CALL_CRYPTO_VERIFY_SIG, 400).
-define(PRIM_CALL_CRYPTO_SHA3, 401). -define(PRIM_CALL_CRYPTO_SHA3, 401).
-define(PRIM_CALL_CRYPTO_SHA256, 402). -define(PRIM_CALL_CRYPTO_SHA256, 402).
-define(PRIM_CALL_CRYPTO_BLAKE2B, 403). -define(PRIM_CALL_CRYPTO_BLAKE2B, 403).
-define(PRIM_CALL_CRYPTO_SHA256_STRING, 404). -define(PRIM_CALL_CRYPTO_SHA256_STRING, 404).
-define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405). -define(PRIM_CALL_CRYPTO_BLAKE2B_STRING, 405).
-define(PRIM_CALL_CRYPTO_ECVERIFY_SECP256K1, 410). -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_IN_AUTH_RANGE(__TTYPE__), (((__TTYPE__) > 499) andalso ((__TTYPE__) < 600))).
-define(PRIM_CALL_AUTH_TX_HASH, 500). -define(PRIM_CALL_AUTH_TX_HASH, 500).
@ -200,3 +202,4 @@
-define(PRIM_CALL_IN_ADDRESS_RANGE(__TTYPE__), (((__TTYPE__) > 599) andalso ((__TTYPE__) < 700))). -define(PRIM_CALL_IN_ADDRESS_RANGE(__TTYPE__), (((__TTYPE__) > 599) andalso ((__TTYPE__) < 700))).
-define(PRIM_CALL_ADDR_IS_ORACLE, 600). -define(PRIM_CALL_ADDR_IS_ORACLE, 600).
-define(PRIM_CALL_ADDR_IS_CONTRACT, 601). -define(PRIM_CALL_ADDR_IS_CONTRACT, 601).
-define(PRIM_CALL_ADDR_IS_PAYABLE, 610).

View File

@ -1,5 +1,5 @@
-define(Type(), aeb_aevm_data:type()). -define(Type(), gmb_aevm_data:type()).
-define(TYPEREP_WORD_TAG, 0). -define(TYPEREP_WORD_TAG, 0).
-define(TYPEREP_STRING_TAG, 1). -define(TYPEREP_STRING_TAG, 1).
@ -9,3 +9,4 @@
-define(TYPEREP_TYPEREP_TAG, 5). -define(TYPEREP_TYPEREP_TAG, 5).
-define(TYPEREP_MAP_TAG, 6). -define(TYPEREP_MAP_TAG, 6).
-define(TYPEREP_FUN_TAG, 7). -define(TYPEREP_FUN_TAG, 7).
-define(TYPEREP_CONTRACT_BYTEARRAY_TAG,8).

View File

@ -1,14 +1,14 @@
%%% @author Thomas Arts %%% @author Thomas Arts
%%% @doc Allow to run QuickCheck tests as eunit tests %%% @doc Allow to run QuickCheck tests as eunit tests
%%% `rebar3 as eqc eunit --cover` %%% `rebar3 as eqc eunit --cover`
%%% or `rebar3 as eqc eunit --module=aeb_fate_code` %%% or `rebar3 as eqc eunit --module=gmb_fate_code`
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover %%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
%%% %%%
%%% %%%
%%% @end %%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan> %%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aeb_fate_code_tests). -module(gmb_fate_code_tests).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
@ -20,8 +20,8 @@
quickcheck_test_() -> quickcheck_test_() ->
{setup, fun() -> eqc:start() end, {setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_code_eqc, prop_opcodes, 200), [ ?EQC_EUNIT(gmfate_code_eqc, prop_opcodes, 200),
?EQC_EUNIT(aefate_code_eqc, prop_serializes, 3000), ?EQC_EUNIT(gmfate_code_eqc, prop_serializes, 3000),
?EQC_EUNIT(aefate_code_eqc, prop_fail_serializes, 3000), ?EQC_EUNIT(gmfate_code_eqc, prop_fail_serializes, 3000),
?EQC_EUNIT(aefate_code_eqc, prop_fuzz, 3000) ?EQC_EUNIT(gmfate_code_eqc, prop_fuzz, 3000)
]}. ]}.

View File

@ -1,14 +1,14 @@
%%% @author Thomas Arts %%% @author Thomas Arts
%%% @doc Allow to run QuickCheck tests as eunit tests %%% @doc Allow to run QuickCheck tests as eunit tests
%%% `rebar3 as eqc eunit --cover` %%% `rebar3 as eqc eunit --cover`
%%% or `rebar3 as eqc eunit --module=aeb_fate_data` %%% or `rebar3 as eqc eunit --module=gmb_fate_data`
%%% Note that for obtainign cover file, one needs `rebar3 as eqc cover %%% Note that for obtainign cover file, one needs `rebar3 as eqc cover
%%% %%%
%%% %%%
%%% @end %%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan> %%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aeb_fate_data_tests). -module(gmb_fate_data_tests).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
@ -20,8 +20,8 @@
quickcheck_test_() -> quickcheck_test_() ->
{setup, fun() -> eqc:start() end, {setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_eqc, prop_roundtrip, 500), [ ?EQC_EUNIT(gmfate_eqc, prop_roundtrip, 500),
?EQC_EUNIT(aefate_eqc, prop_format_scan, 2000), ?EQC_EUNIT(gmfate_eqc, prop_format_scan, 2000),
?EQC_EUNIT(aefate_eqc, prop_order, 2000), ?EQC_EUNIT(gmfate_eqc, prop_order, 2000),
?EQC_EUNIT(aefate_eqc, prop_fuzz, 2000) ?EQC_EUNIT(gmfate_eqc, prop_fuzz, 2000)
]}. ]}.

View File

@ -1,14 +1,14 @@
%%% @author Thomas Arts %%% @author Thomas Arts
%%% @doc Allow to run QuickCheck tests as eunit tests %%% @doc Allow to run QuickCheck tests as eunit tests
%%% `rebar3 as eqc eunit --cover` %%% `rebar3 as eqc eunit --cover`
%%% or `rebar3 as eqc eunit --module=aeb_fate_encoding` %%% or `rebar3 as eqc eunit --module=gmb_fate_encoding`
%%% Note that for obtaining cover file, one needs `rebar3 as eqc cover %%% Note that for obtaining cover file, one needs `rebar3 as eqc cover
%%% %%%
%%% %%%
%%% @end %%% @end
%%% Created : 13 Dec 2018 by Thomas Arts %%% Created : 13 Dec 2018 by Thomas Arts
-module(aeb_fate_encoding_tests). -module(gmb_fate_encoding_tests).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
@ -20,7 +20,8 @@
quickcheck_test_() -> quickcheck_test_() ->
{setup, fun() -> eqc:start() end, {setup, fun() -> eqc:start() end,
[ ?EQC_EUNIT(aefate_type_eqc, prop_roundtrip, 1000), [ ?EQC_EUNIT(gmfate_type_eqc, prop_roundtrip, 1000),
?EQC_EUNIT(aefate_eqc, prop_serializes, 1000), ?EQC_EUNIT(gmfate_eqc, prop_serializes, 1000),
?EQC_EUNIT(aefate_eqc, prop_idempotent, 1000) ?EQC_EUNIT(gmfate_eqc, prop_no_maps_in_keys, 1000),
?EQC_EUNIT(gmfate_eqc, prop_idempotent, 1000)
]}. ]}.

View File

@ -16,7 +16,7 @@
%%% @end %%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan> %%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_code_eqc). -module(gmfate_code_eqc).
-include_lib("eqc/include/eqc.hrl"). -include_lib("eqc/include/eqc.hrl").
@ -28,10 +28,10 @@ prop_serializes() ->
in_parallel( in_parallel(
?FORALL(FateCode, fate_code(0), ?FORALL(FateCode, fate_code(0),
begin begin
{T0, Binary} = timer:tc(fun() -> aeb_fate_code:serialize(FateCode) end), {T0, Binary} = timer:tc(fun() -> gmb_fate_code:serialize(FateCode) end),
?WHENFAIL(eqc:format("serialized:\n ~120p~n", [Binary]), ?WHENFAIL(eqc:format("serialized:\n ~120p~n", [Binary]),
begin begin
{T1, Decoded} = timer:tc(fun() -> aeb_fate_code:deserialize(Binary) end), {T1, Decoded} = timer:tc(fun() -> gmb_fate_code:deserialize(Binary) end),
measure(binary_size, size(Binary), measure(binary_size, size(Binary),
measure(serialize, T0 / 1000, measure(serialize, T0 / 1000,
measure(deserialize, T1 / 1000, measure(deserialize, T1 / 1000,
@ -44,20 +44,20 @@ prop_serializes() ->
prop_fail_serializes() -> prop_fail_serializes() ->
conjunction([{Failure, eqc:counterexample( conjunction([{Failure, eqc:counterexample(
?FORALL(FateCode, fate_code(Failure), ?FORALL(FateCode, fate_code(Failure),
?FORALL(Binary, catch aeb_fate_code:serialize(FateCode), ?FORALL(Binary, catch gmb_fate_code:serialize(FateCode),
is_binary(Binary)))) is_binary(Binary))))
=/= true} || Failure <- [1, 2, 3, 4, 5] ]). =/= true} || Failure <- [1, 2, 3, 4, 5] ]).
prop_fuzz() -> prop_fuzz() ->
in_parallel( in_parallel(
?FORALL(Binary, ?LET(FateCode, fate_code(0), aeb_fate_code:serialize(FateCode)), ?FORALL(Binary, ?LET(FateCode, fate_code(0), gmb_fate_code:serialize(FateCode)),
?FORALL(FuzzedBin, fuzz(Binary), ?FORALL(FuzzedBin, fuzz(Binary),
try aeb_fate_code:deserialize(FuzzedBin) of try gmb_fate_code:deserialize(FuzzedBin) of
Code -> Code ->
?WHENFAIL(eqc:format("Code:\n ~p\n", [Code]), ?WHENFAIL(eqc:format("Code:\n ~p\n", [Code]),
begin begin
Bin1 = aeb_fate_code:serialize(Code), Bin1 = gmb_fate_code:serialize(Code),
Code1 = aeb_fate_code:deserialize(Bin1), Code1 = gmb_fate_code:deserialize(Bin1),
?WHENFAIL(eqc:format("Reserialized\n ~120p\n", [Bin1]), ?WHENFAIL(eqc:format("Reserialized\n ~120p\n", [Bin1]),
equals(Code, Code1)) equals(Code, Code1))
end) end)
@ -66,10 +66,10 @@ prop_fuzz() ->
prop_opcodes() -> prop_opcodes() ->
?FORALL(Opcode, choose(0, 16#ff), ?FORALL(Opcode, choose(0, 16#ff),
try M = aeb_fate_opcodes:mnemonic(Opcode), try M = gmb_fate_opcodes:mnemonic(Opcode),
?WHENFAIL(eqc:format("opcode ~p -> ~p", [Opcode, M]), ?WHENFAIL(eqc:format("opcode ~p -> ~p", [Opcode, M]),
conjunction([{valid, lists:member(Opcode, valid_opcodes())}, conjunction([{valid, lists:member(Opcode, valid_opcodes())},
{eq, equals(aeb_fate_opcodes:m_to_op(M), Opcode)}])) {eq, equals(gmb_fate_opcodes:m_to_op(M), Opcode)}]))
catch catch
_:_ -> _:_ ->
not lists:member(Opcode, valid_opcodes()) not lists:member(Opcode, valid_opcodes())
@ -77,7 +77,7 @@ prop_opcodes() ->
valid_opcodes() -> valid_opcodes() ->
lists:seq(0, 16#7c) ++ lists:seq(16#fa, 16#fd). [ Op || #{opcode := Op} <- gmb_fate_generate_ops:get_ops() ].
fate_code(Failure) -> fate_code(Failure) ->
@ -85,13 +85,14 @@ fate_code(Failure) ->
?LET({FMap, SMap, AMap}, ?LET({FMap, SMap, AMap},
{non_empty(map(if Failure == 1 -> binary(1); {non_empty(map(if Failure == 1 -> binary(1);
true -> binary(4) end, true -> binary(4) end,
{{list(aefate_type_eqc:fate_type(Size div 3)), aefate_type_eqc:fate_type(Size div 3)}, bbs_code(Failure)})), {sublist(lists:sort([private, payable])), %% deserialize sorts them
{list(gmfate_type_eqc:fate_type(Size div 3)), gmfate_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)),
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( gmb_fate_code:update_annotations(
aeb_fate_code:update_symbols( gmb_fate_code:update_symbols(
aeb_fate_code:update_functions( gmb_fate_code:update_functions(
aeb_fate_code:new(), FMap), SMap), AMap))). gmb_fate_code:new(), FMap), SMap), AMap))).
short_list(Max, Gen) -> short_list(Max, Gen) ->
?LET(N, choose(0, Max), eqc_gen:list(N, Gen)). ?LET(N, choose(0, Max), eqc_gen:list(N, Gen)).
@ -107,7 +108,7 @@ bbs_code(Failure) ->
lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]). lists:zip(lists:seq(0, length(BBs)-1), BBs)))}]).
bb_code(Failure) -> bb_code(Failure) ->
EndBB = [ Op || Op <- valid_opcodes(), aeb_fate_opcodes:end_bb(Op) ], EndBB = [ Op || Op <- valid_opcodes(), gmb_fate_opcodes:end_bb(Op) ],
NonEndBB = valid_opcodes() -- EndBB, NonEndBB = valid_opcodes() -- EndBB,
frequency( frequency(
[{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(short_list(6, elements(NonEndBB))), bblock(Failure, Ops))}, [{if Failure == 3 -> 5; true -> 0 end, ?LET(Ops, non_empty(short_list(6, elements(NonEndBB))), bblock(Failure, Ops))},
@ -117,8 +118,8 @@ bb_code(Failure) ->
bblock(Failure, Ops) -> bblock(Failure, Ops) ->
[ begin [ begin
Mnemonic = aeb_fate_opcodes:mnemonic(Op), Mnemonic = gmb_fate_opcodes:mnemonic(Op),
Arity = aeb_fate_opcodes:args(Op), Arity = gmb_fate_opcodes:args(Op),
case Arity of case Arity of
0 -> Mnemonic; 0 -> Mnemonic;
_ -> list_to_tuple([Mnemonic | _ -> list_to_tuple([Mnemonic |
@ -142,7 +143,7 @@ fuzz(Binary) ->
prop_small() -> prop_small() ->
?FORALL(Value, small_fate_data(4), ?FORALL(Value, small_fate_data(4),
begin begin
Bin = aeb_fate_encoding:serialize(Value), Bin = gmb_fate_encoding:serialize(Value),
Size = byte_size(Bin), Size = byte_size(Bin),
measure(size, Size, measure(size, Size,
?WHENFAIL(eqc:format("Size: ~p\n", [Size]), ?WHENFAIL(eqc:format("Size: ~p\n", [Size]),
@ -150,9 +151,9 @@ prop_small() ->
end). end).
prop_small_type() -> prop_small_type() ->
?FORALL(Type, ?SIZED(Size, aefate_type_eqc:fate_type(Size div 3)), ?FORALL(Type, ?SIZED(Size, gmfate_type_eqc:fate_type(Size div 3)),
begin begin
Bin = iolist_to_binary(aeb_fate_encoding:serialize_type(Type)), Bin = iolist_to_binary(gmb_fate_encoding:serialize_type(Type)),
Size = byte_size(Bin), Size = byte_size(Bin),
measure(size, Size, measure(size, Size,
?WHENFAIL(eqc:format("Size: ~p\n", [Size]), ?WHENFAIL(eqc:format("Size: ~p\n", [Size]),
@ -160,7 +161,7 @@ prop_small_type() ->
end). end).
small_fate_data(N) -> small_fate_data(N) ->
?SIZED(Size, resize(Size div N, aefate_eqc:fate_data())). ?SIZED(Size, resize(Size div N, gmfate_eqc:fate_data())).
small_fate_data_key(N) -> small_fate_data_key(N) ->
?SIZED(Size, ?LET(Data, aefate_eqc:fate_data(Size div N, []), eqc_symbolic:eval(Data))). ?SIZED(Size, ?LET(Data, gmfate_eqc:fate_data(Size div N, []), eqc_symbolic:eval(Data))).

View File

@ -7,9 +7,10 @@
%%% @end %%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan> %%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_eqc). -module(gmfate_eqc).
-include_lib("eqc/include/eqc.hrl"). -include_lib("eqc/include/eqc.hrl").
-include("../include/gmb_fate_data.hrl").
-compile([export_all, nowarn_export_all]). -compile([export_all, nowarn_export_all]).
@ -17,17 +18,17 @@ prop_roundtrip() ->
?FORALL(FateData, fate_data(), ?FORALL(FateData, fate_data(),
measure(bytes, size(term_to_binary(FateData)), measure(bytes, size(term_to_binary(FateData)),
begin begin
Serialized = aeb_fate_encoding:serialize(FateData), Serialized = gmb_fate_encoding:serialize(FateData),
?WHENFAIL(eqc:format("Serialized ~p to ~p~n", [FateData, Serialized]), ?WHENFAIL(eqc:format("Serialized ~p to ~p~n", [FateData, Serialized]),
equals(aeb_fate_encoding:deserialize(Serialized), FateData)) equals(gmb_fate_encoding:deserialize(Serialized), FateData))
end)). end)).
prop_format_scan() -> prop_format_scan() ->
?FORALL(FateData, fate_data(), ?FORALL(FateData, fate_data([variant, map]),
?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]), ?WHENFAIL(eqc:format("Trying to format ~p failed~n", [FateData]),
begin begin
String = aeb_fate_data:format(FateData), String = gmb_fate_data:format(FateData),
{ok, _Scanned, _} = aeb_fate_asm_scan:scan(unicode:characters_to_list(String)), {ok, _Scanned, _} = gmb_fate_asm_scan:scan(unicode:characters_to_list(String)),
true true
end)). end)).
@ -35,21 +36,33 @@ prop_serializes() ->
?FORALL({Data, Garbage}, {fate_data(), binary()}, ?FORALL({Data, Garbage}, {fate_data(), binary()},
?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]), ?WHENFAIL(eqc:format("Trying to serialize/deserialize ~p failed~n", [Data]),
begin begin
Binary = <<(aeb_fate_encoding:serialize(Data))/binary, Garbage/binary>>, Binary = <<(gmb_fate_encoding:serialize(Data))/binary, Garbage/binary>>,
{FateData, Rest} = aeb_fate_encoding:deserialize_one(Binary), {FateData, Rest} = gmb_fate_encoding:deserialize_one(Binary),
measure(binary_size, size(Binary), measure(binary_size, size(Binary),
conjunction([{equal, equals(Data, FateData)}, conjunction([{equal, equals(Data, FateData)},
{rest, equals(Garbage, Rest)}, {rest, equals(Garbage, Rest)},
{size, size(Binary) < 500000}])) {size, size(Binary) < 500000}]))
end)). 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 gmb_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() -> prop_fuzz() ->
in_parallel( in_parallel(
?FORALL(Binary, ?LET(FateData, ?SIZED(Size, resize(Size div 4, fate_data())), aeb_fate_encoding:serialize(FateData)), ?FORALL(Binary, ?LET(FateData, ?SIZED(Size, resize(Size div 4, fate_data())), gmb_fate_encoding:serialize(FateData)),
?FORALL(InjectedBin, injection(Binary), ?FORALL(InjectedBin, injection(Binary),
try Org = aeb_fate_encoding:deserialize(InjectedBin), try Org = gmb_fate_encoding:deserialize(InjectedBin),
NewBin = aeb_fate_encoding:serialize(Org), NewBin = gmb_fate_encoding:serialize(Org),
NewOrg = aeb_fate_encoding:deserialize(NewBin), NewOrg = gmb_fate_encoding:deserialize(NewBin),
measure(success, 1, measure(success, 1,
?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]), ?WHENFAIL(eqc:format("Deserialize ~p gives\n~p\nSerializes to ~p\n", [InjectedBin, Org, NewOrg]),
equals(NewBin, InjectedBin))) equals(NewBin, InjectedBin)))
@ -59,24 +72,25 @@ prop_fuzz() ->
prop_order() -> prop_order() ->
?FORALL(Items, vector(3, fate_data()), ?FORALL(Items, vector(3, fate_data([variant, map])),
begin begin
%% Use lt to take minimum %% Use lt to take minimum
Min = lt_min(Items), Min = lt_min(Items),
Max = lt_max(Items), Max = lt_max(Items),
conjunction([ {minimum, is_empty([ {Min, '>', I} || I<-Items, aeb_fate_data:lt(I, Min)])}, conjunction([ {minimum, is_empty([ {Min, '>', I} || I<-Items, gmb_fate_data:lt(I, Min)])},
{maximum, is_empty([ {Max, '<', I} || I<-Items, aeb_fate_data:lt(Max, I)])}]) {maximum, is_empty([ {Max, '<', I} || I<-Items, gmb_fate_data:lt(Max, I)])},
{asym, gmb_fate_data:lt(Min, Max) orelse Min == Max}])
end). end).
lt_min([X, Y | Rest]) -> lt_min([X, Y | Rest]) ->
case aeb_fate_data:lt(X, Y) of case gmb_fate_data:lt(X, Y) of
true -> lt_min([X | Rest]); true -> lt_min([X | Rest]);
false -> lt_min([Y| Rest]) false -> lt_min([Y| Rest])
end; end;
lt_min([X]) -> X. lt_min([X]) -> X.
lt_max([X, Y | Rest]) -> lt_max([X, Y | Rest]) ->
case aeb_fate_data:lt(X, Y) of case gmb_fate_data:lt(X, Y) of
true -> lt_max([Y | Rest]); true -> lt_max([Y | Rest]);
false -> lt_max([X| Rest]) false -> lt_max([X| Rest])
end; end;
@ -84,22 +98,28 @@ lt_max([X]) -> X.
prop_idempotent() -> prop_idempotent() ->
?FORALL(Items, list({fate_data_key(), fate_data()}), ?FORALL(Items, list({fate_data_key(), fate_data()}),
equals(aeb_fate_encoding:sort(Items), equals(gmb_fate_encoding:sort(Items),
aeb_fate_encoding:sort(aeb_fate_encoding:sort(Items)))). gmb_fate_encoding:sort(gmb_fate_encoding:sort(Items)))).
fate_data(Kind) ->
?SIZED(Size, ?LET(Data, fate_data(Size, Kind), eqc_symbolic:eval(Data))).
fate_data() -> fate_data() ->
?SIZED(Size, ?LET(Data, fate_data(Size, [map, variant]), eqc_symbolic:eval(Data))). fate_data([map, variant, store_map]).
%% keys may contain variants but no maps
fate_data_key() -> fate_data_key() ->
?SIZED(Size, ?LET(Data, fate_data(Size div 4, [variant]), eqc_symbolic:eval(Data))). fate_data([variant]).
fate_data(0, _Options) -> fate_data(0, Options) ->
?LAZY( ?LAZY(
frequency( frequency(
[{5, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])}, [{50, oneof([fate_integer(), fate_boolean(), fate_nil(), fate_unit()])},
{1, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(), {10, oneof([fate_string(), fate_address(), fate_bytes(), fate_contract(),
fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}])); fate_oracle(), fate_oracle_q(), fate_bits(), fate_channel()])}] ++
[{1, fate_store_map()} || lists:member(store_map, Options)]));
fate_data(Size, Options) -> fate_data(Size, Options) ->
?LAZY( ?LAZY(
oneof([fate_data(0, Options), oneof([fate_data(0, Options),
@ -111,20 +131,20 @@ fate_data(Size, Options) ->
|| lists:member(map, Options)])). || lists:member(map, Options)])).
fate_integer() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_integer(X))). fate_integer() -> ?LET(X, oneof([int(), largeint()]), return(gmb_fate_data:make_integer(X))).
fate_bits() -> ?LET(X, oneof([int(), largeint()]), return(aeb_fate_data:make_bits(X))). fate_bits() -> ?LET(X, oneof([int(), largeint()]), return(gmb_fate_data:make_bits(X))).
fate_boolean() -> ?LET(X, elements([true, false]), return(aeb_fate_data:make_boolean(X))). fate_boolean() -> ?LET(X, elements([true, false]), return(gmb_fate_data:make_boolean(X))).
fate_nil() -> aeb_fate_data:make_list([]). fate_nil() -> gmb_fate_data:make_list([]).
fate_unit() -> aeb_fate_data:make_unit(). fate_unit() -> gmb_fate_data:make_unit().
fate_string() -> ?LET(X, frequency([{10, non_quote_string()}, {2, list(non_quote_string())}, 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))}]), {1, ?LET(N, choose(64-3, 64+3), vector(N, $a))}]),
return(aeb_fate_data:make_string(X))). return(gmb_fate_data:make_string(X))).
fate_address() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_address(X))). fate_address() -> ?LET(X, binary(256 div 8), return(gmb_fate_data:make_address(X))).
fate_bytes() -> ?LET(X, non_empty(binary()), return(aeb_fate_data:make_bytes(X))). fate_bytes() -> ?LET(X, non_empty(binary()), return(gmb_fate_data:make_bytes(X))).
fate_contract() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_contract(X))). fate_contract() -> ?LET(X, binary(256 div 8), return(gmb_fate_data:make_contract(X))).
fate_oracle() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle(X))). fate_oracle() -> ?LET(X, binary(256 div 8), return(gmb_fate_data:make_oracle(X))).
fate_oracle_q() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_oracle_query(X))). fate_oracle_q() -> ?LET(X, binary(256 div 8), return(gmb_fate_data:make_oracle_query(X))).
fate_channel() -> ?LET(X, binary(256 div 8), return(aeb_fate_data:make_channel(X))). fate_channel() -> ?LET(X, binary(256 div 8), return(gmb_fate_data:make_channel(X))).
fate_values(Size, N, Options) -> fate_values(Size, N, Options) ->
eqc_gen:list(N, fate_data(Size div max(1, N), Options)). eqc_gen:list(N, fate_data(Size div max(1, N), Options)).
@ -133,23 +153,34 @@ fate_values(Size, N, Options) ->
fate_tuple(Size, Options) -> fate_tuple(Size, Options) ->
?LET(N, choose(0, 6), ?LET(N, choose(0, 6),
?LETSHRINK(Elements, fate_values(Size, N, Options), ?LETSHRINK(Elements, fate_values(Size, N, Options),
return(aeb_fate_data:make_tuple(list_to_tuple(Elements))))). return(gmb_fate_data:make_tuple(list_to_tuple(Elements))))).
fate_variant(Size, Options) -> fate_variant(Size, Options) ->
?LET({L1, L2, {tuple, Args}}, {list(choose(0, 255)), list(choose(0,255)), fate_tuple(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, return(gmb_fate_data:make_variant(L1 ++ [tuple_size(Args)] ++ L2,
length(L1), Args))). length(L1), Args))).
fate_list(Size, Options) -> fate_list(Size, Options) ->
?LET(N, frequency([{20, choose(0, 6)}, {1, choose(64 - 3, 64 + 3)}]), ?LET(N, frequency([{20, choose(0, 6)}, {1, choose(64 - 3, 64 + 3)}]),
?LETSHRINK(Vs, fate_values(Size, N, Options), ?LETSHRINK(Vs, fate_values(Size, N, Options),
return(aeb_fate_data:make_list(Vs)))). return(gmb_fate_data:make_list(Vs)))).
fate_map(Size, Options) -> fate_map(Size, Options) ->
?LET(N, choose(0, 6), ?LET(N, choose(0, 6),
?LETSHRINK(Values, fate_values(Size, N, Options), ?LETSHRINK(Values, fate_values(Size, N, Options),
?LET(Keys, vector(length(Values), fate_data(Size div max(1, N * 2), Options -- [map])), ?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))))))). return(gmb_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(gmb_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(gmb_fate_data:make_map(maps:from_list(lists:zip(Keys, Values))))))).
non_quote_string() -> non_quote_string() ->
?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []). ?SUCHTHAT(S, utf8(), [ quote || <<34>> <= S ] == []).
@ -167,3 +198,14 @@ injection(Binary) ->
is_empty(L) -> is_empty(L) ->
?WHENFAIL(eqc:format("~p\n", [L]), 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.

View File

@ -5,7 +5,7 @@
%%% @end %%% @end
%%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan> %%% Created : 13 Dec 2018 by Thomas Arts <thomas@SpaceGrey.lan>
-module(aefate_type_eqc). -module(gmfate_type_eqc).
-include_lib("eqc/include/eqc.hrl"). -include_lib("eqc/include/eqc.hrl").
@ -18,11 +18,11 @@ prop_roundtrip() ->
?FORALL(FateType, fate_type(), ?FORALL(FateType, fate_type(),
collect(kind(FateType), collect(kind(FateType),
begin begin
Serialized = aeb_fate_encoding:serialize_type(FateType), Serialized = gmb_fate_encoding:serialize_type(FateType),
BinSerialized = list_to_binary(Serialized), BinSerialized = list_to_binary(Serialized),
?WHENFAIL(eqc:format("Serialized ~p to ~p (~p)~n", [FateType, Serialized, BinSerialized]), ?WHENFAIL(eqc:format("Serialized ~p to ~p (~p)~n", [FateType, Serialized, BinSerialized]),
begin begin
{Type, <<>>} = aeb_fate_encoding:deserialize_type(BinSerialized), {Type, <<>>} = gmb_fate_encoding:deserialize_type(BinSerialized),
equals(Type, FateType) equals(Type, FateType)
end) end)
end)). end)).

View File

@ -5,14 +5,14 @@
{erl_opts, [debug_info]}. {erl_opts, [debug_info]}.
{deps, [ {eblake2, "1.0.0"} {deps, [ {eblake2, "1.0.0"}
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git", , {gmserialization, {git, "https://git.qpq.swiss/QPQ-AG/gmserialization.git",
{ref, "816bf99"}}} {ref, "9d2ecc00d32ea295309563e54a81636ecb597e96"}}}
, {getopt, "1.0.1"} , {getopt, "1.0.1"}
]}. ]}.
{escript_incl_apps, [aebytecode, eblake2, aeserialization, getopt]}. {escript_incl_apps, [gmbytecode, eblake2, gmserialization, getopt]}.
{escript_main_app, aebytecode}. {escript_main_app, gmbytecode}.
{escript_name, aefateasm}. {escript_name, gmfateasm}.
{escript_emu_args, "%%!"}. {escript_emu_args, "%%!"}.
{pre_hooks, {pre_hooks,
@ -29,8 +29,8 @@
]}. ]}.
{relx, [{release, {aebytecode, "2.0.1"}, {relx, [{release, {gmbytecode, "3.4.1"},
[aebytecode, eblake2, getopt]}, [gmbytecode, eblake2, getopt]},
{dev_mode, true}, {dev_mode, true},
{include_erts, false}, {include_erts, false},
@ -39,17 +39,17 @@
{profiles, [{binary, [ {profiles, [{binary, [
{deps, [ {eblake2, "1.0.0"} {deps, [ {eblake2, "1.0.0"}
, {aeserialization, {git, "https://github.com/aeternity/aeserialization.git", , {gmserialization, {git, "https://git.qpq.swiss/QPQ-AG/gmserialization.git",
{ref, "b55c372"}}} {ref, "9d2ecc00d32ea295309563e54a81636ecb597e96"}}}
, {getopt, "1.0.1"} , {getopt, "1.0.1"}
]}, ]},
{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)", {post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
escriptize, escriptize,
"cp \"$REBAR_BUILD_DIR/bin/aefateasm\" ./aefateasm"}, "cp \"$REBAR_BUILD_DIR/bin/gmfateasm\" ./gmfateasm"},
{"win32", {"win32",
escriptize, escriptize,
"robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ aefateasm* " "robocopy \"%REBAR_BUILD_DIR%/bin/\" ./ gmfateasm* "
"/njs /njh /nfl /ndl & exit /b 0"} % silence things "/njs /njh /nfl /ndl & exit /b 0"} % silence things
]} ]}
]}, ]},

View File

@ -1,16 +1,23 @@
{"1.1.0", {"1.2.0",
[{<<"aeserialization">>, [{<<"gmserialization">>,
{git,"https://github.com/aeternity/aeserialization.git", {git,"https://git.qpq.swiss/QPQ-AG/gmserialization.git",
{ref,"816bf994ffb5cee218c3f22dc5fea296c9e0882e"}}, {ref,"9d2ecc00d32ea295309563e54a81636ecb597e96"}},
0}, 0},
{<<"base58">>, {<<"base58">>,
{git,"https://github.com/aeternity/erl-base58.git", {git,"https://git.qpq.swiss/QPQ-AG/erl-base58.git",
{ref,"60a335668a60328a29f9731b67c4a0e9e3d50ab6"}}, {ref,"e6aa62eeae3d4388311401f06e4b939bf4e94b9c"}},
1}, 1},
{<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0}, {<<"eblake2">>,{pkg,<<"eblake2">>,<<"1.0.0">>},0},
{<<"enacl">>,
{git,"https://git.qpq.swiss/QPQ-AG/enacl.git",
{ref,"4eb7ec70084ba7c87b1af8797c4c4e90c84f95a2"}},
1},
{<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}. {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},0}]}.
[ [
{pkg_hash,[ {pkg_hash,[
{<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>}, {<<"eblake2">>, <<"EC8AD20E438AAB3F2E8D5D118C366A0754219195F8A0F536587440F8F9BCF2EF">>},
{<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]} {<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}]},
{pkg_hash_ext,[
{<<"eblake2">>, <<"3C4D300A91845B25D501929A26AC2E6F7157480846FAB2347A4C11AE52E08A99">>},
{<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>}]}
]. ].

BIN
rebar3 Executable file

Binary file not shown.

View File

@ -1,755 +0,0 @@
-module(aeb_fate_generate_ops).
-export([ gen_and_halt/1
, generate/0
, generate_documentation/1
, get_ops/0
, test_asm_generator/1]).
gen_and_halt([SrcDirArg, IncludeDirArg]) ->
generate(atom_to_list(SrcDirArg),
atom_to_list(IncludeDirArg)),
halt().
generate() -> generate("src/", "include/").
get_ops() -> gen(ops_defs()).
generate(Src, Include) ->
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.
%% TODO: Some real gas numbers...
ops_defs() ->
%% Opname, Opcode, end_bb, in_auth offchain, gas, format, Constructor, ArgType, ResType, Documentation
[ { 'RETURN', 16#00, true, true, true, 2, [], return, {}, any, "Return from function call, top of stack is return value . The type of the retun value has to match the return type of the function."}
, { 'RETURNR', 16#01, true, true, true, 2, [a], returnr, {any}, any, "Push Arg0 and return from function. The type of the retun value has to match the return type of the function."}
, { 'CALL', 16#02, true, true, true, 4, [a], call, {string}, any, "Call the function Arg0 with args on stack. The types of the arguments has to match the argument typs of the function."}
, { 'CALL_R', 16#03, true, false, true, 8, [a,is,a], call_r, {contract, string, integer}, any, "Remote call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function."}
, { 'CALL_T', 16#04, true, true, true, 4, [a], call_t, {string}, any, "Tail call to function Arg0. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
, { 'CALL_TR', 16#05, true, false, true, 8, [a,is,a], call_tr, {contract, string, integer}, any, "Remote tail call to contract Arg0 and function Arg1 with value Arg2. The types of the arguments has to match the argument typs of the function. And the return type of the called function has to match the type of the current function."}
, { 'CALL_GR', 16#06, true, false, true, 8, [a,is,a,a], call_gr, {contract, string, integer, integer}, any, "Remote call with gas cap in Arg3. Otherwise as CALL_R."}
, { 'CALL_GTR', 16#07, true, false, true, 8, [a,is,a,a], call_gtr, {contract, string, integer, integer}, any, "Remote tail call with gas cap in Arg3. Otherwise as CALL_TR."}
, { 'JUMP', 16#08, true, true, true, 3, [ii], jump, {integer}, none, "Jump to a basic block. The basic block has to exist in the current function."}
, { 'JUMPIF', 16#09, true, true, true, 4, [a,ii], jumpif, {boolean, integer}, none, "Conditional jump to a basic block. If Arg0 then jump to Arg1."}
, { 'SWITCH_V2', 16#0a, true, true, true, 4, [a,ii,ii], switch, {variant, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_V3', 16#0b, true, true, true, 4, [a,ii,ii,ii], switch, {variant, integer, integer, ingeger}, none, "Conditional jump to a basic block on variant tag."}
, { 'SWITCH_VN', 16#0c, true, true, true, 4, [a, li], switch, {variant, {list, integer}}, none, "Conditional jump to a basic block on variant tag."}
, { 'CALL_VALUE', 16#0d, false, true, true, 3, [a], call_value, {}, integer, "The value sent in the current remote call."}
, { 'PUSH', 16#0e, false, true, true, 2, [a], push, {any}, any, "Push argument to stack."}
, { 'DUPA', 16#0f, false, true, true, 3, [], dup, {any}, any, "Duplicate top of stack."}
, { 'DUP', 16#10, false, true, true, 3, [a], dup, {any}, any, "push Arg0 stack pos on top of stack."}
, { 'POP', 16#11, false, true, true, 3, [a], pop, {integer}, integer, "Arg0 := top of stack."}
, { 'INCA', 16#12, false, true, true, 2, [], inc, {integer}, integer, "Increment accumulator."}
, { 'INC', 16#13, false, true, true, 2, [a], inc, {integer}, integer, "Increment argument."}
, { 'DECA', 16#14, false, true, true, 2, [], dec, {integer}, integer, "Decrement accumulator."}
, { 'DEC', 16#15, false, true, true, 2, [a], dec, {integer}, integer, "Decrement argument."}
, { 'ADD', 16#16, false, true, true, 3, [a,a,a], add, {integer, integer}, integer, "Arg0 := Arg1 + Arg2."}
, { 'SUB', 16#17, false, true, true, 3, [a,a,a], sub, {integer, integer}, integer, "Arg0 := Arg1 - Arg2."}
, { 'MUL', 16#18, false, true, true, 3, [a,a,a], mul, {integer, integer}, integer, "Arg0 := Arg1 * Arg2."}
, { 'DIV', 16#19, false, true, true, 3, [a,a,a], divide, {integer, integer}, integer, "Arg0 := Arg1 / Arg2."}
, { 'MOD', 16#1a, false, true, true, 3, [a,a,a], modulo, {integer, integer}, integer, "Arg0 := Arg1 mod Arg2."}
, { 'POW', 16#1b, false, true, true, 3, [a,a,a], pow, {integer, integer}, integer, "Arg0 := Arg1 ^ Arg2."}
, { 'STORE', 16#1c, false, true, true, 3, [a,a], store, {any}, any, "Arg0 := Arg1."}
, { 'SHA3', 16#1d, false, true, true, 30, [a,a], sha3, {any}, hash, "Arg0 := sha3(Arg1)."}
, { 'SHA256', 16#1e, false, true, true, 30, [a,a], sha256, {any}, hash, "Arg0 := sha256(Arg1)."}
, { 'BLAKE2B', 16#1f, false, true, true, 30, [a,a], blake2b, {any}, hash, "Arg0 := blake2b(Arg1)."}
, { 'LT', 16#20, false, true, true, 3, [a,a,a], lt, {integer, integer}, boolean, "Arg0 := Arg1 < Arg2."}
, { 'GT', 16#21, false, true, true, 3, [a,a,a], gt, {integer, integer}, boolean, "Arg0 := Arg1 > Arg2."}
, { 'EQ', 16#22, false, true, true, 3, [a,a,a], eq, {integer, integer}, boolean, "Arg0 := Arg1 = Arg2."}
, { 'ELT', 16#23, false, true, true, 3, [a,a,a], elt, {integer, integer}, boolean, "Arg0 := Arg1 =< Arg2."}
, { 'EGT', 16#24, false, true, true, 3, [a,a,a], egt, {integer, integer}, boolean, "Arg0 := Arg1 >= Arg2."}
, { 'NEQ', 16#25, false, true, true, 3, [a,a,a], neq, {integer, integer}, boolean, "Arg0 := Arg1 /= Arg2."}
, { 'AND', 16#26, false, true, true, 3, [a,a,a], and_op, {boolean, boolean}, boolean, "Arg0 := Arg1 and Arg2."}
, { 'OR', 16#27, false, true, true, 3, [a,a,a], or_op, {boolean, boolean}, boolean, "Arg0 := Arg1 or Arg2."}
, { 'NOT', 16#28, false, true, true, 3, [a,a], not_op, {boolean}, boolean, "Arg0 := not Arg1."}
, { 'TUPLE', 16#29, false, true, true, 3, [a,ii], tuple, {integer}, tuple, "Arg0 := tuple of size = Arg1. Elements on stack."}
, { 'ELEMENT', 16#2a, false, true, true, 3, [a,a,a], element_op, {integer, tuple}, any, "Arg1 := element(Arg2, Arg3)."}
, { 'SETELEMENT', 16#2b, false, true, true, 3, [a,a,a,a], setelement, {integer, tuple, any}, tuple, "Arg0 := a new tuple similar to Arg2, but with element number Arg1 replaced by Arg3."}
, { 'MAP_EMPTY', 16#2c, false, true, true, 3, [a], map_empty, {}, map, "Arg0 := #{}."}
, { 'MAP_LOOKUP', 16#2d, false, true, true, 3, [a,a,a], map_lookup, {map, any}, any, "Arg0 := lookup key Arg2 in map Arg1."}
, { 'MAP_LOOKUPD', 16#2e, false, true, true, 3, [a,a,a,a], map_lookup, {map, any, any}, any, "Arg0 := lookup key Arg2 in map Arg1 if key exists in map otherwise Arg0 := Arg3."}
, { 'MAP_UPDATE', 16#2f, false, true, true, 3, [a,a,a,a], map_update, {map, any, any}, map, "Arg0 := update key Arg2 in map Arg1 with value Arg3."}
, { 'MAP_DELETE', 16#30, false, true, true, 3, [a,a,a], map_delete, {map, any}, map, "Arg0 := delete key Arg2 from map Arg1."}
, { 'MAP_MEMBER', 16#31, false, true, true, 3, [a,a,a], map_member, {map, any}, boolean, "Arg0 := true if key Arg2 is in map Arg1."}
, { 'MAP_FROM_LIST', 16#32, false, true, true, 3, [a,a], map_from_list, {{list, {tuple, [any, any]}}}, map, "Arg0 := make a map from (key, value) list in Arg1."}
, { 'IS_NIL', 16#33, false, true, true, 3, [a,a], is_nil, {list}, boolean, "Arg0 := true if Arg1 == []."}
, { 'CONS', 16#34, false, true, true, 3, [a,a,a], cons, {any, list}, list, "Arg0 := [Arg1|Arg2]."}
, { 'HD', 16#35, false, true, true, 3, [a,a], hd, {list}, any, "Arg0 := head of list Arg1."}
, { 'TL', 16#36, false, true, true, 3, [a,a], tl, {list}, list, "Arg0 := tail of list Arg1."}
, { 'LENGTH', 16#37, false, true, true, 3, [a,a], length, {list}, integer, "Arg0 := length of list Arg1."}
, { 'NIL', 16#38, false, true, true, 3, [a], nil, {}, list, "Arg0 := []."}
, { 'STR_JOIN', 16#39, false, true, true, 3, [a,a,a], str_join, {string, string}, string, "Arg0 := string Arg1 followed by string Arg2."}
, { 'INT_TO_STR', 16#3a, false, true, true, 3, [a,a], int_to_str, {integer}, string, "Arg0 := turn integer Arg1 into a string."}
, { 'ADDR_TO_STR', 16#3b, false, true, true, 3, [a,a], addr_to_str, {address}, string, "Arg0 := turn address Arg1 into a string."}
, { 'STR_REVERSE', 16#3c, false, true, true, 3, [a,a], str_reverse, {string}, string, "Arg0 := the reverse of string Arg1."}
, { 'APPEND', 16#3d, false, true, true, 3, [a,a,a], append, {list, list}, list, "Arg0 := Arg1 ++ Arg2."}
, { 'INT_TO_ADDR', 16#3e, false, true, true, 3, [a,a], int_to_addr, {integer}, address, "Arg0 := turn integer Arg1 into an address."}
, { 'VARIANT', 16#3f, false, true, true, 3, [a,a,a,a], variant, {integer, integer, integer}, variant, "Arg0 := create a variant of size Arg1 with the tag Arg2 (Arg2 < Arg1) and take Arg3 elements from the stack."}
, { 'VARIANT_TEST', 16#40, false, true, true, 3, [a,a,a], variant_test, {variant, integer}, boolean, "Arg0 := true if variant Arg1 has the tag Arg2."}
, { 'VARIANT_ELEMENT', 16#41, false, true, true, 3, [a,a,a], variant_element, {variant, integer}, any, "Arg0 := element number Arg2 from variant Arg1."}
, { 'BITS_NONEA', 16#42, false, true, true, 3, [], bits_none, {}, bits, "push an empty bitmap on the stack."}
, { 'BITS_NONE', 16#43, false, true, true, 3, [a], bits_none, {}, bits, "Arg0 := empty bitmap."}
, { 'BITS_ALLA', 16#44, false, true, true, 3, [], bits_all, {}, bits, "push a full bitmap on the stack."}
, { 'BITS_ALL', 16#45, false, true, true, 3, [a], bits_all, {}, bits, "Arg0 := full bitmap."}
, { 'BITS_ALL_N', 16#46, false, true, true, 3, [a,a], bits_all_n, {integer}, bits, "Arg0 := bitmap with Arg1 bits set."}
, { 'BITS_SET', 16#47, false, true, true, 3, [a,a,a], bits_set, {bits, integer}, bits, "Arg0 := set bit Arg2 of bitmap Arg1."}
, { 'BITS_CLEAR', 16#48, false, true, true, 3, [a,a,a], bits_clear, {bits, integer}, bits, "Arg0 := clear bit Arg2 of bitmap Arg1."}
, { 'BITS_TEST', 16#49, false, true, true, 3, [a,a,a], bits_test, {bits, integer}, boolean, "Arg0 := true if bit Arg2 of bitmap Arg1 is set."}
, { 'BITS_SUM', 16#4a, false, true, true, 3, [a,a], bits_sum, {bits}, integer, "Arg0 := sum of set bits in bitmap Arg1. Exception if infinit bitmap."}
, { 'BITS_OR', 16#4b, false, true, true, 3, [a,a,a], bits_or, {bits, bits}, bits, "Arg0 := Arg1 v Arg2."}
, { 'BITS_AND', 16#4c, false, true, true, 3, [a,a,a], bits_and, {bits, bits}, bits, "Arg0 := Arg1 ^ Arg2."}
, { 'BITS_DIFF', 16#4d, false, true, true, 3, [a,a,a], bits_diff, {bits, bits}, bits, "Arg0 := Arg1 - Arg2."}
, { 'BALANCE', 16#4e, false, true, true, 3, [a], balance, {}, integer, "Arg0 := The current contract balance."}
, { 'ORIGIN', 16#4f, false, true, true, 3, [a], origin, {}, address, "Arg0 := Address of contract called by the call transaction."}
, { 'CALLER', 16#50, false, true, true, 3, [a], caller, {}, address, "Arg0 := The address that signed the call transaction."}
, { 'GASPRICE', 16#51, false, true, true, 3, [a], gasprice, {}, integer, "Arg0 := The current gas price."}
, { 'BLOCKHASH', 16#52, false, true, true, 3, [a,a], blockhash, {integer}, hash, "Arg0 := The blockhash at height."}
, { 'BENEFICIARY', 16#53, false, true, true, 3, [a], beneficiary, {}, address, "Arg0 := The address of the current beneficiary."}
, { 'TIMESTAMP', 16#54, false, true, true, 3, [a], timestamp, {}, integer, "Arg0 := The current timestamp. Unrelaiable, don't use for anything."}
, { 'GENERATION', 16#55, false, true, true, 3, [a], generation, {}, integer, "Arg0 := The block height of the cureent generation."}
, { 'MICROBLOCK', 16#56, false, true, true, 3, [a], microblock, {}, integer, "Arg0 := The current micro block number."}
, { 'DIFFICULTY', 16#57, false, true, true, 3, [a], difficulty, {}, integer, "Arg0 := The current difficulty."}
, { 'GASLIMIT', 16#58, false, true, true, 3, [a], gaslimit, {}, integer, "Arg0 := The current gaslimit."}
, { 'GAS', 16#59, false, true, true, 3, [a], gas, {}, integer, "Arg0 := The amount of gas left."}
, { 'ADDRESS', 16#5a, false, true, true, 3, [a], address, {}, address, "Arg0 := The current contract address."}
, { 'LOG0', 16#5b, false, true, true, 3, [a], log, {string}, none, "Create a log message in the call object."}
, { 'LOG1', 16#5c, false, true, true, 3, [a,a], log, {integer, string}, none, "Create a log message with one topic in the call object."}
, { 'LOG2', 16#5d, false, true, true, 3, [a,a,a], log, {integer, integer, string}, none, "Create a log message with two topics in the call object."}
, { 'LOG3', 16#5e, false, true, true, 3, [a,a,a,a], log, {integer, integer, integer, string}, none, "Create a log message with three topics in the call object."}
, { 'LOG4', 16#5f, false, true, true, 3, [a,a,a,a,a], log, {integer, integer, integer, integer, string}, none, "Create a log message with four topics in the call object."}
%% Transaction ops
, { 'SPEND', 16#60, false, false, true, 3, [a,a], spend, {address, integer}, none, "Transfer Arg1 tokens to account Arg0. (If the contract account has at least that many tokens."}
, { 'ORACLE_REGISTER', 16#61, false, false, false, 3, [a,a,a,a,a,a,a], oracle_register, {signature, address, integer, variant, typerep, typerep}, oracle, "Arg0 := New oracle with address Arg2, query fee Arg3, TTL Arg4, query type Arg5 and response type Arg6. Arg0 contains delegation signature."}
, { 'ORACLE_QUERY', 16#62, false, false, false, 3, [a,a,a,a,a,a,a,a], oracle_query, {oracle, any, integer, variant, variant, typerep, typerep}, oracle_query, "Arg0 := New oracle query for oracle Arg1, question in Arg2, query fee in Arg3, query TTL in Arg4, response TTL in Arg5. Typereps for checking oracle type is in Arg6 and Arg7."}
, { 'ORACLE_RESPOND', 16#63, false, false, false, 3, [a,a,a,a,a,a], oracle_respond, {signature, oracle, oracle_query,any, typerep, typerep}, none, "Respond as oracle Arg1 to query in Arg2 with response Arg3. Arg0 contains delegation signature. Typereps for checking oracle type is in Arg4 and Arg5."}
, { 'ORACLE_EXTEND', 16#64, false, false, false, 3, [a,a,a], oracle_extend, {signature, oracle, variant}, none, "Extend oracle in Arg1 with TTL in Arg2. Arg0 contains delegation signature."}
, { 'ORACLE_GET_ANSWER', 16#65, false, false, true, 3, [a,a,a,a,a], oracle_get_answer, {oracle, oracle_query, typerep, typerep}, any, "Arg0 := option variant with answer (if any) from oracle query in Arg1 given by oracle Arg0. Typereps for checking oracle type is in Arg3 and Arg4."}
, { 'ORACLE_GET_QUESTION', 16#66, false, false, true, 3, [a,a,a,a,a], oracle_get_question, {oracle, oracle_query, typerep, typerep}, any, "Arg0 := question in oracle query Arg2 given to oracle Arg1. Typereps for checking oracle type is in Arg3 and Arg4."}
, { 'ORACLE_QUERY_FEE', 16#67, false, false, true, 3, [a,a], oracle_query_fee, {oracle}, integer, "Arg0 := query fee for oracle Arg1"}
, { 'AENS_RESOLVE', 16#68, false, false, true, 3, [a,a,a,a], aens_resolve, {string, string, typerep}, variant, "Resolve name in Arg0 with tag Arg1. Arg2 describes the type parameter of the resolved name."}
, { 'AENS_PRECLAIM', 16#69, false, false, false, 3, [a,a,a], aens_preclaim, {signature, address, hash}, none, "Preclaim the hash in Arg2 for address in Arg1. Arg0 contains delegation signature."}
, { 'AENS_CLAIM', 16#6a, false, false, false, 3, [a,a,a,a], aens_claim, {signature, address, string, integer}, none, "Claim the name in Arg2 for address in Arg1. Arg3 contains the salt used to hash the preclaim. Arg0 contains delegation signature."}
, { 'AENS_UPDATE', 16#6b, false, false, false, 3, [], aens_update, {}, none, "NYI"}
, { 'AENS_TRANSFER', 16#6c, false, false, false, 3, [a,a,a,a], aens_transfer,{signature, address, address, string}, none, "Transfer ownership of name Arg3 from account Arg1 to Arg2. Arg0 contains delegation signature."}
, { 'AENS_REVOKE', 16#6d, false, false, false, 3, [a,a,a], aens_revoke, {signature, address, string}, none, "Revoke the name in Arg2 from owner Arg1. Arg0 contains delegation signature."}
, { 'BALANCE_OTHER', 16#6e, false, true, true, 3, [a,a], balance_other, {address}, integer, "Arg0 := The balance of address Arg1."}
%% TODO: Reorder these before documenting the specification
, { 'MAP_SIZE', 16#6f, false, true, true, 3, [a,a], map_size_, {map}, integer, "Arg0 := The size of the map Arg1."}
, { 'MAP_TO_LIST', 16#70, false, true, true, 3, [a,a], map_to_list, {map}, list, "Arg0 := The tuple list representation of the map Arg1."}
, { 'STR_LENGTH', 16#71, false, true, true, 3, [a,a], str_length, {string}, integer, "Arg0 := The length of the string Arg1."}
, { 'ECVERIFY', 16#72, false, true, true, 1300, [a,a,a,a], ecverify, {bytes, address, bytes}, boolean, "Arg0 := ecverify(Hash, PubKey, Signature)"}
, { 'ECVERIFY_SECP256K1', 16#73, false, true, true, 1300, [a,a,a,a], ecverify_secp256k1, {bytes, bytes, bytes}, boolean, "Arg0 := ecverify_secp256k1(Hash, PubKey, Signature)"}
, { 'CONTRACT_TO_ADDRESS', 16#74, false, true, true, 3, [a,a], contract_to_address, {contract}, address, "Arg0 := Arg1 - A no-op type conversion"}
, { 'AUTH_TX_HASH', 16#75, false, true, true, 3, [a], auth_tx_hash, {}, variant, "If in GA authentication context return Some(TxHash) otherwise None."}
, { 'BYTES_TO_INT', 16#76, false, true, true, 3, [a,a], bytes_to_int, {bytes}, integer, "Arg0 := bytes_to_int(Arg1)"}
, { 'BYTES_TO_STR', 16#77, false, true, true, 3, [a,a], bytes_to_str, {bytes}, string, "Arg0 := bytes_to_str(Arg1)"}
, { 'ORACLE_CHECK', 16#78, false, false, true, 3, [a,a,a,a], oracle_check, {oracle, typerep, typerep}, bool, "Arg0 := is Arg1 an oracle with the given query (Arg2) and response (Arg3) types"}
, { 'ORACLE_CHECK_QUERY', 16#79, false, false, true, 3, [a,a,a,a,a], oracle_check_query, {oracle, oracle_query, typerep, typerep}, bool, "Arg0 := is Arg2 a query for the oracle Arg1 with the given types (Arg3, Arg4)"}
, { 'IS_ORACLE', 16#7a, false, false, true, 3, [a,a], is_oracle, {address}, bool, "Arg0 := is Arg1 an oracle"}
, { 'IS_CONTRACT', 16#7b, false, false, true, 3, [a,a], is_contract, {address}, bool, "Arg0 := is Arg1 a contract"}
, { 'CREATOR', 16#7c, false, true, true, 3, [a], contract_creator, {}, address, "Arg0 := contract creator"}
, { 'DEACTIVATE', 16#fa, false, true, true, 3, [], deactivate, {}, none, "Mark the current contract for deactivation."}
, { 'ABORT', 16#fb, true, true, true, 3, [a], abort, {string}, none, "Abort execution (dont use all gas) with error message in Arg0."}
, { 'EXIT', 16#fc, true, true, true, 3, [a], exit, {string}, none, "Abort execution (use upp all gas) with error message in Arg0."}
, { 'NOP', 16#fd, false, true, true, 1, [], nop, {}, none, "The no op. does nothing."}
%% FUNCTION 16#fe "Function declaration and entrypoint."
%% EXTEND 16#ff "Reserved for future extensions beyond one byte opcodes."
].
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]),
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"
])]),
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", []),
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 ~-26s :: ~s.\n",
[TypeName, Type])).
gen_fate_code_type(#{type_name := TypeName}) ->
lists:flatten(io_lib:format(" | ~s\n", [TypeName])).
gen_type_exports(#{type_name := TypeName}) ->
lists:flatten(io_lib:format(" , ~s/0\n", [TypeName--"()"])).
gen_constructor_exports(#{constructor_type := Function}) ->
lists:flatten(io_lib:format(" , ~s\n", [Function])).
gen_constructors(#{constructor := Function, format := [],
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(~21s) -> ~21w ;\n",
[Macro, Name])).
gen_m_to_op(#{opname := Name, macro := Macro}) ->
lists:flatten(io_lib:format("m_to_op(~21w) -> ~21s ;\n",
[Name, Macro])).
gen_args(#{macro := Macro, arity := Arity}) ->
lists:flatten(io_lib:format("args(~21s) -> ~2w ;\n",
[Macro, Arity])).
gen_bb(#{macro := Macro, end_bb := EndBB}) ->
lists:flatten(io_lib:format("end_bb(~21s) -> ~w ;\n",
[Macro, EndBB])).
gen_in_auth(#{macro := Macro, in_auth := InAuth}) ->
lists:flatten(io_lib:format("in_auth(~21s) -> ~w ;\n",
[Macro, InAuth])).
gen_allowed_offchain(#{macro := Macro, offchain := Offchain}) ->
lists:flatten(io_lib:format("allowed_offchain(~21s) -> ~w ;\n",
[Macro, Offchain])).
prelude(Doc) ->
"%%%-------------------------------------------------------------------\n"
"%%% @copyright (C) 2019, Aeternity Anstalt\n"
"%%%\n"
"%%% === === N O T E : This file is generated do not edit. === ===\n"
"%%%\n"
"%%% Source is in aeb_fate_generate_ops.erl\n"
"%%% @doc\n"
"%%% "++Doc++
"%%% @end\n"
"%%%-------------------------------------------------------------------\n\n".
gen_defines(#{opname := Name, opcode := OpCode}) ->
lists:flatten(io_lib:format("-define(~-26w, 16#~2.16.0b).\n", [Name, OpCode])).
gen([]) ->
[];
gen([{OpName, OpCode, 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') or (Name =:= 'CALL_TR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Value}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Value)];\n"
"format_op({~w, Contract, {immediate, Function}, Value}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Value)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name}) when (Name =:= 'CALL_GR') or (Name =:= 'CALL_GTR') ->
io_lib:format("format_op({~w, {immediate, Contract}, {immediate, Function}, Value, Gas}, Symbols) ->\n"
" [\"~s \", lookup(Contract, Symbols), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n"
"format_op({~w, Contract, {immediate, Function}, Value, Gas}, Symbols) ->\n"
"[\"~s \", format_arg(a, Contract), \".\", "
"lookup(Function, Symbols), \" \", "
"format_arg(a, Value), \" \", "
"format_arg(a, Gas)];\n",
[Name, atom_to_list(Name), Name, atom_to_list(Name)]);
gen_format(#{opname := Name, format := []}) ->
io_lib:format("format_op(~w, _) -> [\"~s\"];", [Name, atom_to_list(Name)]);
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.
%% TODO: add gas cost and end_bb/in_auth?
generate_documentation(Filename) ->
{ok, File} = file:open(Filename, [write]),
Instructions = lists:flatten([gen_doc(Op)++"\n" || Op <- get_ops()]),
io:format(File,
"### Operations\n\n"
"| OpCode | Name | Args | Description |\n"
"| --- | --- | --- | --- |\n"
"~s"
, [Instructions]),
io:format(File, "\n", []),
file:close(File).
gen_doc(#{ opname := Name
, opcode := OpCode
, arity := _Arity
, end_bb := _EndBB
, format := FateFormat
, macro := _Macro
, type_name := _TypeName
, doc := Doc
, gas := _Gas
, type := _Type
, constructor := _Constructor
, constructor_type := _ConstructorType
}) ->
Arguments =
case FateFormat of
[] -> "";
_ -> lists:join(" ",
[format_arg_doc(A) ||
A <-
lists:zip(FateFormat,
lists:seq(0,length(FateFormat)-1))])
end,
io_lib:format("| 0x~.16b | ~w | ~s | ~s |\n",
[ OpCode
, Name
, Arguments
, Doc]).
format_arg_doc({a, N}) -> io_lib:format("Arg~w", [N]);
format_arg_doc({is,_N}) -> "Identifier";
format_arg_doc({ii,_N}) -> "Integer";
format_arg_doc({li,_N}) -> "[Integers]";
format_arg_doc({t,_N}) -> "Type".

View File

@ -1,32 +1,37 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc %%% @doc
%%% Encode and decode data and function calls according to %%% Encode and decode data and function calls according to
%%% Sophia-AEVM-ABI %%% Sophia-AEVM-ABI
%%% @end %%% @end
%%% Updated : 22 Jan 2025
%%% Created : 25 Jan 2018 %%% Created : 25 Jan 2018
%%% %%%
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_aevm_abi). -module(gmb_aevm_abi).
-vsn("3.4.1").
-define(HASH_SIZE, 32). -define(HASH_SIZE, 32).
-export([ create_calldata/4 -export([ create_calldata/4
, check_calldata/2 , check_calldata/3
, function_type_info/3 , function_type_info/4
, function_type_hash/3 , function_type_hash/3
, arg_typerep_from_function/2 , arg_typerep_from_function/2
, type_hash_from_function_name/2 , type_hash_from_function_name/2
, typereps_from_type_hash/2 , typereps_from_type_hash/2
, function_name_from_type_hash/2 , function_name_from_type_hash/2
, get_function_hash_from_calldata/1 , get_function_hash_from_calldata/1
, is_payable/2
, abi_version/0 , abi_version/0
]). ]).
-type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8. -type hash() :: <<_:256>>. %% 256 = ?HASH_SIZE * 8.
-type function_name() :: binary(). %% String -type function_name() :: binary(). %% String
-type typerep() :: aeb_aevm_data:type(). -type typerep() :: gmb_aevm_data:type().
-type function_type_info() :: { FunctionHash :: hash() -type function_type_info() :: { FunctionHash :: hash()
, FunctionName :: function_name() , FunctionName :: function_name()
, Payable :: boolean()
, ArgType :: binary() %% binary typerep , ArgType :: binary() %% binary typerep
, OutType :: binary() %% binary typerep , OutType :: binary() %% binary typerep
}. }.
@ -48,18 +53,30 @@ create_calldata(FunName, Args, ArgTypes0, RetType) ->
ArgTypes = {tuple, ArgTypes0}, ArgTypes = {tuple, ArgTypes0},
<<TypeHashInt:?HASH_SIZE/unit:8>> = <<TypeHashInt:?HASH_SIZE/unit:8>> =
function_type_hash(list_to_binary(FunName), ArgTypes, RetType), function_type_hash(list_to_binary(FunName), ArgTypes, RetType),
Data = aeb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}), Data = gmb_heap:to_binary({TypeHashInt, list_to_tuple(Args)}),
{ok, Data}. {ok, Data}.
-spec check_calldata(binary(), type_info()) -> -spec check_calldata(binary(), type_info(), boolean()) ->
{'ok', typerep(), typerep()} | {'error', atom()}. {'ok', typerep(), typerep()} | {'error', atom()}.
check_calldata(CallData, TypeInfo) -> check_calldata(CallData, TypeInfo, CheckPayable) ->
%% The first element of the CallData should be the function name %% The first element of the CallData should be the function name
case get_function_hash_from_calldata(CallData) of case get_function_hash_from_calldata(CallData) of
{ok, Hash} -> {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 case typereps_from_type_hash(Hash, TypeInfo) of
{ok, ArgType, OutType} -> {ok, ArgType, OutType} ->
try aeb_heap:from_binary({tuple, [word, ArgType]}, CallData) of try gmb_heap:from_binary({tuple, [word, ArgType]}, CallData) of
{ok, _Something} -> {ok, _Something} ->
{ok, {tuple, [word, ArgType]}, OutType}; {ok, {tuple, [word, ArgType]}, OutType};
{error, _} -> {error, _} ->
@ -70,15 +87,13 @@ check_calldata(CallData, TypeInfo) ->
end; end;
{error, _} -> {error, _} ->
{error, unknown_function} {error, unknown_function}
end;
{error, _What} ->
{error, bad_call_data}
end. end.
-spec get_function_hash_from_calldata(CallData::binary()) -> -spec get_function_hash_from_calldata(CallData::binary()) ->
{ok, binary()} | {error, term()}. {ok, binary()} | {error, term()}.
get_function_hash_from_calldata(CallData) -> get_function_hash_from_calldata(CallData) ->
case aeb_heap:from_binary({tuple, [word]}, CallData) of case gmb_heap:from_binary({tuple, [word]}, CallData) of
{ok, {HashInt}} -> {ok, <<HashInt:?HASH_SIZE/unit:8>>}; {ok, {HashInt}} -> {ok, <<HashInt:?HASH_SIZE/unit:8>>};
{error, _} = Error -> Error {error, _} = Error -> Error
end. end.
@ -86,21 +101,22 @@ get_function_hash_from_calldata(CallData) ->
%%%=================================================================== %%%===================================================================
%%% Handle type info from contract meta data %%% Handle type info from contract meta data
-spec function_type_info(function_name(), [typerep()], typerep()) -> -spec function_type_info(function_name(), boolean(), [typerep()], typerep()) ->
function_type_info(). function_type_info().
function_type_info(Name, ArgTypes, OutType) -> function_type_info(Name, Payable, ArgTypes, OutType) ->
ArgType = {tuple, ArgTypes}, ArgType = {tuple, ArgTypes},
{ function_type_hash(Name, ArgType, OutType) { function_type_hash(Name, ArgType, OutType)
, Name , Name
, aeb_heap:to_binary(ArgType) , Payable
, aeb_heap:to_binary(OutType) , gmb_heap:to_binary(ArgType)
, gmb_heap:to_binary(OutType)
}. }.
-spec function_type_hash(function_name(), typerep(), typerep()) -> hash(). -spec function_type_hash(function_name(), typerep(), typerep()) -> hash().
function_type_hash(Name, ArgType, OutType) when is_binary(Name) -> function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
Bin = iolist_to_binary([ Name Bin = iolist_to_binary([ Name
, aeb_heap:to_binary(ArgType) , gmb_heap:to_binary(ArgType)
, aeb_heap:to_binary(OutType) , gmb_heap:to_binary(OutType)
]), ]),
%% Calculate a 256 bit digest BLAKE2b hash value of a binary %% Calculate a 256 bit digest BLAKE2b hash value of a binary
{ok, Hash} = eblake2:blake2b(?HASH_SIZE, Bin), {ok, Hash} = eblake2:blake2b(?HASH_SIZE, Bin),
@ -110,35 +126,46 @@ function_type_hash(Name, ArgType, OutType) when is_binary(Name) ->
{'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}. {'ok', typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
arg_typerep_from_function(Function, TypeInfo) -> arg_typerep_from_function(Function, TypeInfo) ->
case lists:keyfind(Function, 2, TypeInfo) of case lists:keyfind(Function, 2, TypeInfo) of
{_TypeHash, Function, ArgTypeBin,_OutTypeBin} -> {_TypeHash, Function, ArgTypeBin, _OutTypeBin} ->
case aeb_heap:from_binary(typerep, ArgTypeBin) of arg_typerep_from_type_binary(ArgTypeBin);
{ok, ArgType} -> {ok, ArgType}; {_TypeHash, Function, _Payable, ArgTypeBin, _OutTypeBin} ->
{error,_} -> {error, bad_type_data} arg_typerep_from_type_binary(ArgTypeBin);
end;
false -> false ->
{error, unknown_function} {error, unknown_function}
end. end.
arg_typerep_from_type_binary(ArgTBin) ->
case gmb_heap:from_binary(typerep, ArgTBin) of
{ok, ArgT} -> {ok, ArgT};
{error,_} -> {error, bad_type_data}
end.
-spec typereps_from_type_hash(hash(), type_info()) -> -spec typereps_from_type_hash(hash(), type_info()) ->
{'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}. {'ok', typerep(), typerep()} | {'error', 'bad_type_data' | 'unknown_function'}.
typereps_from_type_hash(TypeHash, TypeInfo) -> typereps_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash,_Function, ArgTypeBin, OutTypeBin} -> {TypeHash, _Function, ArgTypeBin, OutTypeBin} ->
case {aeb_heap:from_binary(typerep, ArgTypeBin), typereps_from_type_binaries(ArgTypeBin, OutTypeBin);
aeb_heap:from_binary(typerep, OutTypeBin)} of {TypeHash, _Function, _Payable, ArgTypeBin, OutTypeBin} ->
{{ok, ArgType}, {ok, OutType}} -> {ok, ArgType, OutType}; typereps_from_type_binaries(ArgTypeBin, OutTypeBin);
{_, _} -> {error, bad_type_data}
end;
false -> false ->
{error, unknown_function} {error, unknown_function}
end. end.
typereps_from_type_binaries(ArgTBin, OutTBin) ->
case {gmb_heap:from_binary(typerep, ArgTBin), gmb_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()) -> -spec function_name_from_type_hash(hash(), type_info()) ->
{'ok', function_name()} {'ok', function_name()}
| {'error', 'unknown_function'}. | {'error', 'unknown_function'}.
function_name_from_type_hash(TypeHash, TypeInfo) -> function_name_from_type_hash(TypeHash, TypeInfo) ->
case lists:keyfind(TypeHash, 1, TypeInfo) of case lists:keyfind(TypeHash, 1, TypeInfo) of
{TypeHash, Function,_ArgTypeBin,_OutTypeBin} -> {TypeHash, Function, _ArgTypeBin, _OutTypeBin} ->
{ok, Function};
{TypeHash, Function, _Payable, _ArgTypeBin, _OutTypeBin} ->
{ok, Function}; {ok, Function};
false -> false ->
{error, unknown_function} {error, unknown_function}
@ -149,8 +176,22 @@ function_name_from_type_hash(TypeHash, TypeInfo) ->
| {'error', 'unknown_function'}. | {'error', 'unknown_function'}.
type_hash_from_function_name(Name, TypeInfo) -> type_hash_from_function_name(Name, TypeInfo) ->
case lists:keyfind(Name, 2, TypeInfo) of case lists:keyfind(Name, 2, TypeInfo) of
{TypeHash, Name,_ArgTypeBin,_OutTypeBin} -> {TypeHash, Name, _ArgTypeBin, _OutTypeBin} ->
{ok, TypeHash};
{TypeHash, Name, _Payable, _ArgTypeBin, _OutTypeBin} ->
{ok, TypeHash}; {ok, TypeHash};
false -> false ->
{error, unknown_function} {error, unknown_function}
end. 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.

View File

@ -1,4 +1,5 @@
-module(aeb_aevm_data). -module(gmb_aevm_data).
-vsn("3.4.1").
-export_type([data/0, -export_type([data/0,
type/0, type/0,

View File

@ -1,4 +1,5 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc Assembler for aevm machine code. %%% @doc Assembler for aevm machine code.
%%% %%%
@ -25,17 +26,19 @@
%%% 4. labels as descibed above. %%% 4. labels as descibed above.
%%% %%%
%%% @end %%% @end
%%% Updated : 22 Jan 2025
%%% Created : 21 Dec 2017 %%% Created : 21 Dec 2017
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_asm). -module(gmb_asm).
-vsn("3.4.1").
-export([ file/2 -export([ file/2
, pp/1 , pp/1
, to_hexstring/1 , to_hexstring/1
]). ]).
-include_lib("aebytecode/include/aeb_opcodes.hrl"). -include_lib("gmbytecode/include/gmb_opcodes.hrl").
pp(Asm) -> pp(Asm) ->
@ -47,10 +50,10 @@ format(Asm) -> format(Asm, 0).
format([{comment, Comment} | Rest], Address) -> format([{comment, Comment} | Rest], Address) ->
";; " ++ Comment ++ "\n" ++ format(Rest, Address); ";; " ++ Comment ++ "\n" ++ format(Rest, Address);
format([Mnemonic | Rest], Address) -> format([Mnemonic | Rest], Address) ->
Op = aeb_opcodes:m_to_op(Mnemonic), Op = gmb_opcodes:m_to_op(Mnemonic),
case (Op >= ?PUSH1) andalso (Op =< ?PUSH32) of case (Op >= ?PUSH1) andalso (Op =< ?PUSH32) of
true -> true ->
Arity = aeb_opcodes:op_size(Op) - 1, Arity = gmb_opcodes:op_size(Op) - 1,
{Args, Code} = get_args(Arity, Rest), {Args, Code} = get_args(Arity, Rest),
" " ++ atom_to_list(Mnemonic) " " ++ atom_to_list(Mnemonic)
++ " " ++ Args ++ "\n" ++ " " ++ Args ++ "\n"
@ -72,7 +75,7 @@ get_args(N, [Arg|Code]) ->
file(Filename, Options) -> file(Filename, Options) ->
{ok, File} = file:read_file(Filename), {ok, File} = file:read_file(Filename),
{ok, Tokens, _} = aeb_asm_scan:scan(binary_to_list(File)), {ok, Tokens, _} = gmb_asm_scan:scan(binary_to_list(File)),
case proplists:lookup(pp_tokens, Options) of case proplists:lookup(pp_tokens, Options) of
{pp_tokens, true} -> {pp_tokens, true} ->
@ -100,8 +103,8 @@ to_hexstring(ByteList) ->
to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) -> to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) ->
OpCode = aeb_opcodes:m_to_op(Op), OpCode = gmb_opcodes:m_to_op(Op),
OpSize = aeb_opcodes:op_size(OpCode), OpSize = gmb_opcodes:op_size(OpCode),
to_bytecode(Rest, Address + OpSize, Env, [OpCode|Code], Opts); to_bytecode(Rest, Address + OpSize, Env, [OpCode|Code], Opts);
to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) -> to_bytecode([{int,_line, Int}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [Int|Code], Opts); to_bytecode(Rest, Address, Env, [Int|Code], Opts);
@ -138,7 +141,7 @@ resolve_refs([Op | Rest], Env, Code) ->
resolve_refs([],_Env, Code) -> Code. resolve_refs([],_Env, Code) -> Code.
expand_args([OP, Arg | Rest]) when OP >= ?PUSH1 andalso OP =< ?PUSH32 -> expand_args([OP, Arg | Rest]) when OP >= ?PUSH1 andalso OP =< ?PUSH32 ->
BitSize = (aeb_opcodes:op_size(OP) - 1) * 8, BitSize = (gmb_opcodes:op_size(OP) - 1) * 8,
Bin = << << X:BitSize>> || X <- [Arg] >>, Bin = << << X:BitSize>> || X <- [Arg] >>,
ArgByteList = binary_to_list(Bin), ArgByteList = binary_to_list(Bin),
[OP | ArgByteList] ++ expand_args(Rest); [OP | ArgByteList] ++ expand_args(Rest);

View File

@ -1,5 +1,6 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- %%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc Assembler lexer. %%% @doc Assembler lexer.
%%% %%%
@ -195,7 +196,7 @@ Erlang code.
-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). -ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]).
-include_lib("aebytecode/include/aeb_opcodes.hrl"). -include_lib("gmbytecode/include/gmb_opcodes.hrl").
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).

View File

@ -1,19 +1,22 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc %%% @doc
%%% Prettyprint aevm machine code %%% Prettyprint aevm machine code
%%% @end %%% @end
%%% Created : 2 Oct 2017 %%% Updated : 22 Jan 2025
%%% Created : 02 Oct 2017
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_disassemble). -module(gmb_disassemble).
-vsn("3.4.1").
-export([ pp/1, -export([ pp/1,
format/2, format/2,
format_address/1 format_address/1
]). ]).
-include_lib("aebytecode/include/aeb_opcodes.hrl"). -include_lib("gmbytecode/include/gmb_opcodes.hrl").
pp(Binary) -> pp(Binary) ->
@ -26,37 +29,37 @@ format(Binary, ErrFormatFun) ->
pp(Address, [Op|Ops], Assembly, ErrFormatFun) -> pp(Address, [Op|Ops], Assembly, ErrFormatFun) ->
case Op of case Op of
X when (X >= ?STOP) andalso (X =< ?SIGNEXTEND) -> X when (X >= ?STOP) andalso (X =< ?SIGNEXTEND) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
X when (X >= ?LT) andalso (X =< ?BYTE) -> X when (X >= ?LT) andalso (X =< ?BYTE) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
X when (X >= ?SHA3) andalso (X =< ?SHA3) -> X when (X >= ?SHA3) andalso (X =< ?SHA3) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
X when (X >= ?ADDRESS) andalso (X =< ?EXTCODECOPY) -> X when (X >= ?ADDRESS) andalso (X =< ?EXTCODECOPY) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
X when (X >= ?BLOCKHASH) andalso (X =< ?GASLIMIT) -> X when (X >= ?BLOCKHASH) andalso (X =< ?GASLIMIT) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
X when (X >= ?POP) andalso (X =< ?JUMPDEST) -> X when (X >= ?POP) andalso (X =< ?JUMPDEST) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
X when (X >= ?PUSH1) andalso (X =< ?PUSH32) -> X when (X >= ?PUSH1) andalso (X =< ?PUSH32) ->
Bytes = X-?PUSH1+1, Bytes = X-?PUSH1+1,
{ArgList, NextOps} = lists:split(Bytes, Ops), {ArgList, NextOps} = lists:split(Bytes, Ops),
Arg = arglist_to_arg(ArgList), Arg = arglist_to_arg(ArgList),
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), [{Arg,8*Bytes}]), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), [{Arg,8*Bytes}]),
next(Address+Bytes, NextOps, Instr, Assembly, ErrFormatFun); next(Address+Bytes, NextOps, Instr, Assembly, ErrFormatFun);
X when (X >= ?DUP1) andalso (X =< ?LOG4) -> X when (X >= ?DUP1) andalso (X =< ?LOG4) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
X when (X >= ?CREATE) andalso (X =< ?DELEGATECALL) -> X when (X >= ?CREATE) andalso (X =< ?DELEGATECALL) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
X when (X >= ?INVALID) andalso (X =< ?SUICIDE) -> X when (X >= ?INVALID) andalso (X =< ?SUICIDE) ->
Instr = pp_instruction(Address, aeb_opcodes:mnemonic(Op), []), Instr = pp_instruction(Address, gmb_opcodes:mnemonic(Op), []),
next(Address, Ops, Instr, Assembly, ErrFormatFun); next(Address, Ops, Instr, Assembly, ErrFormatFun);
_ -> _ ->
ErrFormatFun("unhandled op ~p at ~p",[Op, Address]), ErrFormatFun("unhandled op ~p at ~p",[Op, Address]),

View File

@ -1,13 +1,16 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2019, Aeternity Anstalt %%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc %%% @doc
%%% Encode and decode data and function calls according to %%% Encode and decode data and function calls according to
%%% Sophia-FATE-ABI %%% Sophia-FATE-ABI
%%% @end %%% @end
%%% Updated : 22 Jan 2025
%%% Created : 11 Jun 2019 %%% Created : 11 Jun 2019
%%% %%%
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_fate_abi). -module(gmb_fate_abi).
-vsn("3.4.1").
-export([ create_calldata/2 -export([ create_calldata/2
, decode_calldata/2 , decode_calldata/2
@ -16,7 +19,7 @@
, get_function_type_from_function_hash/2 , get_function_type_from_function_hash/2
, abi_version/0 ]). , abi_version/0 ]).
-include("../include/aeb_fate_data.hrl"). -include("../include/gmb_fate_data.hrl").
%%%=================================================================== %%%===================================================================
%%% API %%% API
@ -29,25 +32,27 @@ abi_version() ->
-spec create_calldata(list(), [term()]) -> {ok, binary()}. -spec create_calldata(list(), [term()]) -> {ok, binary()}.
create_calldata(FunName, Args) -> create_calldata(FunName, Args) ->
FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)), FunctionId = gmb_fate_code:symbol_identifier(list_to_binary(FunName)),
{ok, aeb_fate_encoding:serialize( {ok, gmb_fate_encoding:serialize(
aeb_fate_data:make_tuple({FunctionId, gmb_fate_data:make_tuple({FunctionId,
aeb_fate_data:make_tuple(list_to_tuple(Args))}))}. gmb_fate_data:make_tuple(list_to_tuple(Args))}))}.
-spec decode_calldata(list(), binary()) -> {ok, term()} | {error, term()}. -spec decode_calldata(list(), binary()) -> {ok, term()} | {error, term()}.
decode_calldata(FunName, Calldata) -> decode_calldata(FunName, Calldata) ->
FunctionId = aeb_fate_code:symbol_identifier(list_to_binary(FunName)), FunctionId = gmb_fate_code:symbol_identifier(list_to_binary(FunName)),
case ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(Calldata)) of try ?FATE_TUPLE_ELEMENTS(gmb_fate_encoding:deserialize(Calldata)) of
[FunctionId, FateArgs] -> {ok, ?FATE_TUPLE_ELEMENTS(FateArgs)}; [FunctionId, FateArgs] -> {ok, ?FATE_TUPLE_ELEMENTS(FateArgs)};
_ -> {error, decode_error} _ -> {error, decode_error}
catch _:_ ->
{error, decode_error}
end. end.
-spec get_function_name_from_function_hash(binary(), aeb_fate_code:fcode()) -> -spec get_function_name_from_function_hash(binary(), gmb_fate_code:fcode()) ->
{ok, term()} | {error, term()}. {ok, term()} | {error, term()}.
get_function_name_from_function_hash(<<SymbolHash:4/binary, _:28/binary>>, FateCode) -> 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, FateCode);
get_function_name_from_function_hash(SymbolHash = <<_:4/binary>>, FateCode) -> get_function_name_from_function_hash(SymbolHash = <<_:4/binary>>, FateCode) ->
Symbols = aeb_fate_code:symbols(FateCode), Symbols = gmb_fate_code:symbols(FateCode),
case maps:get(SymbolHash, Symbols, undefined) of case maps:get(SymbolHash, Symbols, undefined) of
undefined -> {error, no_function_matching_function_hash}; undefined -> {error, no_function_matching_function_hash};
Function -> {ok, Function} Function -> {ok, Function}
@ -56,22 +61,22 @@ get_function_name_from_function_hash(SymbolHash = <<_:4/binary>>, FateCode) ->
-spec get_function_hash_from_calldata(binary()) -> -spec get_function_hash_from_calldata(binary()) ->
{ok, binary()} | {error, term()}. {ok, binary()} | {error, term()}.
get_function_hash_from_calldata(CallData) -> get_function_hash_from_calldata(CallData) ->
try ?FATE_TUPLE_ELEMENTS(aeb_fate_encoding:deserialize(CallData)) of try ?FATE_TUPLE_ELEMENTS(gmb_fate_encoding:deserialize(CallData)) of
[FunHash, _Args] -> {ok, FunHash}; [FunHash, _Args] -> {ok, FunHash};
_ -> {error, bad_calldata} _ -> {error, bad_calldata}
catch _:_ -> catch _:_ ->
{error, bad_calldata} {error, bad_calldata}
end. end.
-spec get_function_type_from_function_hash(binary(), aeb_fate_code:fcode()) -> -spec get_function_type_from_function_hash(binary(), gmb_fate_code:fcode()) ->
{ok, term(), term()} | {error, term()}. {ok, term(), term()} | {error, term()}.
get_function_type_from_function_hash(<<SymbolHash:4/binary, _:28/binary>>, FateCode) -> 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);
get_function_type_from_function_hash(SymbolHash, FateCode) -> get_function_type_from_function_hash(SymbolHash, FateCode) ->
Functions = aeb_fate_code:functions(FateCode), Functions = gmb_fate_code:functions(FateCode),
case maps:get(SymbolHash, Functions, undefined) of case maps:get(SymbolHash, Functions, undefined) of
undefined -> undefined ->
{error, no_function_matching_function_hash}; {error, no_function_matching_function_hash};
{{ArgTypes, RetType}, _Code} -> {_Attrs, {ArgTypes, RetType}, _Code} ->
{ok, ArgTypes, RetType} {ok, ArgTypes, RetType}
end. end.

View File

@ -1,4 +1,5 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2019, Aeternity Anstalt %%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc Assembler for Fate machine code. %%% @doc Assembler for Fate machine code.
%%% @end %%% @end
@ -80,10 +81,12 @@
%%% Size: Digits %%% Size: Digits
%%% Tag: Digits %%% Tag: Digits
%%% %%%
%%% Updated : 22 Jan 2025
%%% Created : 21 Dec 2017 %%% Created : 21 Dec 2017
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_fate_asm). -module(gmb_fate_asm).
-vsn("3.4.1").
-export([ assemble_file/3 -export([ assemble_file/3
, asm_to_bytecode/2 , asm_to_bytecode/2
@ -94,8 +97,8 @@
, to_asm/1 , to_asm/1
]). ]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). -include_lib("gmbytecode/include/gmb_fate_opcodes.hrl").
-include_lib("aebytecode/include/aeb_fate_data.hrl"). -include_lib("gmbytecode/include/gmb_fate_data.hrl").
-define(HASH_BYTES, 32). -define(HASH_BYTES, 32).
assemble_file(InFile, OutFile, Options) -> assemble_file(InFile, OutFile, Options) ->
@ -104,12 +107,12 @@ assemble_file(InFile, OutFile, Options) ->
ok = file:write_file(OutFile, BC). ok = file:write_file(OutFile, BC).
function_call(String) -> function_call(String) ->
{ok, Tokens, _} = aeb_fate_asm_scan:scan(String), {ok, Tokens, _} = gmb_fate_asm_scan:scan(String),
parse_function_call(Tokens). parse_function_call(Tokens).
parse_function_call([{id,_,Name}, {'(',_}| Rest]) -> parse_function_call([{id,_,Name}, {'(',_}| Rest]) ->
{Args, []} = to_args(Rest), {Args, []} = to_args(Rest),
aeb_fate_encoding:serialize( gmb_fate_encoding:serialize(
{tuple, {mk_hash(Name), {tuple, list_to_tuple(Args)}}}). {tuple, {mk_hash(Name), {tuple, list_to_tuple(Args)}}}).
@ -129,9 +132,9 @@ pp(FateCode) ->
to_asm(FateCode) -> to_asm(FateCode) ->
Functions = aeb_fate_code:functions(FateCode), Functions = gmb_fate_code:functions(FateCode),
Symbols = aeb_fate_code:symbols(FateCode), Symbols = gmb_fate_code:symbols(FateCode),
Annotations = aeb_fate_code:annotations(FateCode), Annotations = gmb_fate_code:annotations(FateCode),
insert_comments(get_comments(Annotations), 1, insert_comments(get_comments(Annotations), 1,
lists:flatten( lists:flatten(
io_lib:format("~s", io_lib:format("~s",
@ -153,7 +156,7 @@ format_functions(Functions, Symbols) ->
lists:sort(maps:to_list(CodeMap)), lists:sort(maps:to_list(CodeMap)),
Symbols) Symbols)
|| ||
{Name, {Sig, CodeMap}} <- maps:to_list(Functions)]. {Name, {_Attrs, Sig, CodeMap}} <- maps:to_list(Functions)].
format(Name, Sig, BBs, Symbols) -> format(Name, Sig, BBs, Symbols) ->
@ -191,7 +194,7 @@ format_code([], _) ->
""; "";
format_code([Op|Rest], Symbols) -> format_code([Op|Rest], Symbols) ->
[" ", [" ",
aeb_fate_pp:format_op(Op, Symbols), gmb_fate_pp:format_op(Op, Symbols),
"\n", "\n",
format_code(Rest, Symbols)]. format_code(Rest, Symbols)].
@ -201,7 +204,7 @@ read_file(Filename) ->
binary_to_list(File). binary_to_list(File).
asm_to_bytecode(AssemblerCode, Options) -> asm_to_bytecode(AssemblerCode, Options) ->
{ok, Tokens, _} = aeb_fate_asm_scan:scan(AssemblerCode), {ok, Tokens, _} = gmb_fate_asm_scan:scan(AssemblerCode),
case proplists:lookup(pp_tokens, Options) of case proplists:lookup(pp_tokens, Options) of
{pp_tokens, true} -> {pp_tokens, true} ->
@ -209,7 +212,7 @@ asm_to_bytecode(AssemblerCode, Options) ->
none -> none ->
ok ok
end, end,
Env = #{ fate_code => aeb_fate_code:new() Env = #{ fate_code => gmb_fate_code:new()
, functions => #{} , functions => #{}
}, },
@ -218,11 +221,11 @@ asm_to_bytecode(AssemblerCode, Options) ->
FunctionsMap = maps:get(functions, Env1), FunctionsMap = maps:get(functions, Env1),
Functions = [X || {_, X} <- lists:sort(maps:to_list(FunctionsMap))], Functions = [X || {_, X} <- lists:sort(maps:to_list(FunctionsMap))],
FunctionsBin = iolist_to_binary(Functions), FunctionsBin = iolist_to_binary(Functions),
ByteCode = aeb_fate_code:serialize(FateCode, FunctionsBin, Options), ByteCode = gmb_fate_code:serialize(FateCode, FunctionsBin, Options),
{Env, ByteCode}. {Env, ByteCode}.
strip(ByteCode) -> strip(ByteCode) ->
{Code, _Rest} = aeser_rlp:decode_one(ByteCode), {Code, _Rest} = gmser_rlp:decode_one(ByteCode),
Code. Code.
%% ------------------------------------------------------------------- %% -------------------------------------------------------------------
@ -235,7 +238,7 @@ to_bytecode([{function,_line, 'FUNCTION'}|Rest], Address, Env, Code, Opts) ->
{Fun, Rest2} = to_fun_def(Rest), {Fun, Rest2} = to_fun_def(Rest),
to_bytecode(Rest2, Fun, Env2, [], Opts); to_bytecode(Rest2, Fun, Env2, [], Opts);
to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) -> to_bytecode([{mnemonic,_line, Op}|Rest], Address, Env, Code, Opts) ->
OpCode = aeb_fate_opcodes:m_to_op(Op), OpCode = gmb_fate_opcodes:m_to_op(Op),
to_bytecode(Rest, Address, Env, [OpCode|Code], Opts); to_bytecode(Rest, Address, Env, [OpCode|Code], Opts);
to_bytecode([{arg,_line, N}|Rest], Address, Env, Code, Opts) -> to_bytecode([{arg,_line, N}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts); to_bytecode(Rest, Address, Env, [{arg, N}|Code], Opts);
@ -249,37 +252,41 @@ to_bytecode([{boolean,_line, Bool}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, [{immediate, Bool}|Code], Opts); to_bytecode(Rest, Address, Env, [{immediate, Bool}|Code], Opts);
to_bytecode([{string,_line, String}|Rest], Address, Env, Code, Opts) -> to_bytecode([{string,_line, String}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_string(String)}|Code], [{immediate, gmb_fate_data:make_string(String)}|Code],
Opts); Opts);
to_bytecode([{object,_line, {address, Value}}|Rest], to_bytecode([{object,_line, {address, Value}}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_address(Value)}|Code], [{immediate, gmb_fate_data:make_address(Value)}|Code],
Opts); Opts);
to_bytecode([{object,_line, {contract, Value}}|Rest], to_bytecode([{object,_line, {contract, Value}}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_contract(Value)}|Code], [{immediate, gmb_fate_data:make_contract(Value)}|Code],
Opts); Opts);
to_bytecode([{object,_line, {oracle, Value}}|Rest], to_bytecode([{object,_line, {oracle, Value}}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_oracle(Value)}|Code], [{immediate, gmb_fate_data:make_oracle(Value)}|Code],
Opts); Opts);
to_bytecode([{object,_line, {oracle_query, Value}}|Rest], to_bytecode([{object,_line, {oracle_query, Value}}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_oracle_query(Value)}|Code], [{immediate, gmb_fate_data:make_oracle_query(Value)}|Code],
Opts); Opts);
to_bytecode([{object,_line, {channel, Value}}|Rest], to_bytecode([{object,_line, {channel, Value}}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_contract(Value)}|Code], [{immediate, gmb_fate_data:make_contract(Value)}|Code],
Opts); Opts);
to_bytecode([{bytes,_line, Value}|Rest], to_bytecode([{bytes,_line, Value}|Rest],
Address, Env, Code, Opts) -> Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_bytes(Value)}|Code], [{immediate, gmb_fate_data:make_bytes(Value)}|Code],
Opts);
to_bytecode([{contract_bytearray,_line, FateCode}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env,
[{immediate, gmb_fate_data:make_contract_bytearray(FateCode)}|Code],
Opts); Opts);
to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) -> to_bytecode([{id,_line, ID}|Rest], Address, Env, Code, Opts) ->
{Env2, Id} = insert_symbol(list_to_binary(ID), Env), {Env2, Id} = insert_symbol(list_to_binary(ID), Env),
@ -292,19 +299,19 @@ to_bytecode([{'[',_line}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest2, Address, Env, [{immediate, List}|Code], Opts); to_bytecode(Rest2, Address, Env, [{immediate, List}|Code], Opts);
to_bytecode([{'(',_line}|Rest], Address, Env, Code, Opts) -> to_bytecode([{'(',_line}|Rest], Address, Env, Code, Opts) ->
{Elements, Rest2} = parse_tuple(Rest), {Elements, Rest2} = parse_tuple(Rest),
Tuple = aeb_fate_data:make_tuple(list_to_tuple(Elements)), Tuple = gmb_fate_data:make_tuple(list_to_tuple(Elements)),
to_bytecode(Rest2, Address, Env, [{immediate, Tuple}|Code], Opts); to_bytecode(Rest2, Address, Env, [{immediate, Tuple}|Code], Opts);
to_bytecode([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) -> to_bytecode([{start_variant,_line}|_] = Tokens, Address, Env, Code, Opts) ->
{Arities, Tag, Values, Rest} = parse_variant(Tokens), {Arities, Tag, Values, Rest} = parse_variant(Tokens),
Variant = aeb_fate_data:make_variant(Arities, Tag, Values), Variant = gmb_fate_data:make_variant(Arities, Tag, Values),
to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts); to_bytecode(Rest, Address, Env, [{immediate, Variant}|Code], Opts);
to_bytecode([{typerep,_line}|Rest], Address, Env, Code, Opts) -> to_bytecode([{typerep,_line}|Rest], Address, Env, Code, Opts) ->
{Type, Rest1} = to_type(Rest), {Type, Rest1} = to_type(Rest),
TypeRep = aeb_fate_data:make_typerep(Type), TypeRep = gmb_fate_data:make_typerep(Type),
to_bytecode(Rest1, Address, Env, [{immediate, TypeRep}|Code], Opts); to_bytecode(Rest1, Address, Env, [{immediate, TypeRep}|Code], Opts);
to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) -> to_bytecode([{bits,_line, Bits}|Rest], Address, Env, Code, Opts) ->
to_bytecode(Rest, Address, Env, to_bytecode(Rest, Address, Env,
[{immediate, aeb_fate_data:make_bits(Bits)}|Code], Opts); [{immediate, gmb_fate_data:make_bits(Bits)}|Code], Opts);
to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) -> to_bytecode([{comment, Line, Comment}|Rest], Address, Env, Code, Opts) ->
Env2 = insert_annotation(comment, Line, Comment, Env), Env2 = insert_annotation(comment, Line, Comment, Env),
@ -352,7 +359,7 @@ parse_tuple(Tokens) ->
parse_variant([{start_variant,_line} parse_variant([{start_variant,_line}
, {'[', _line} , {'[', _}
| Rest]) -> | Rest]) ->
{Arities, Rest2} = parse_list(Rest), {Arities, Rest2} = parse_list(Rest),
%% Make sure Arities is a list of bytes. %% Make sure Arities is a list of bytes.
@ -360,7 +367,7 @@ parse_variant([{start_variant,_line}
is_integer(A), A < 256], is_integer(A), A < 256],
[{'|',_} [{'|',_}
, {int,_line, Tag} , {int,_, Tag}
, {'|',_} , {'|',_}
, {'(',_} | Rest3] = Rest2, , {'(',_} | Rest3] = Rest2,
{Elements , [{end_variant, _} | Rest4]} = parse_tuple(Rest3), {Elements , [{end_variant, _} | Rest4]} = parse_tuple(Rest3),
@ -379,29 +386,29 @@ parse_value([{'{',_line} | Rest]) -> parse_map(Rest);
parse_value([{'[',_line} | Rest]) -> parse_list(Rest); parse_value([{'[',_line} | Rest]) -> parse_list(Rest);
parse_value([{'(',_line} | Rest]) -> parse_value([{'(',_line} | Rest]) ->
{T, Rest2} = parse_tuple(Rest), {T, Rest2} = parse_tuple(Rest),
{aeb_fate_data:make_tuple(list_to_tuple(T)), Rest2}; {gmb_fate_data:make_tuple(list_to_tuple(T)), Rest2};
parse_value([{bits,_line, Bits} | Rest]) -> parse_value([{bits,_line, Bits} | Rest]) ->
{aeb_fate_data:make_bits(Bits), Rest}; {gmb_fate_data:make_bits(Bits), Rest};
parse_value([{start_variant,_line}|_] = Tokens) -> parse_value([{start_variant,_line}|_] = Tokens) ->
{Arities, Tag, Values, Rest} = parse_variant(Tokens), {Arities, Tag, Values, Rest} = parse_variant(Tokens),
Variant = aeb_fate_data:make_variant(Arities, Tag, Values), Variant = gmb_fate_data:make_variant(Arities, Tag, Values),
{Variant, Rest}; {Variant, Rest};
parse_value([{string,_line, String} | Rest]) -> parse_value([{string,_line, String} | Rest]) ->
{aeb_fate_data:make_string(String), Rest}; {gmb_fate_data:make_string(String), Rest};
parse_value([{object,_line, {address, Address}} | Rest]) -> parse_value([{object,_line, {address, Address}} | Rest]) ->
{aeb_fate_data:make_address(Address), Rest}; {gmb_fate_data:make_address(Address), Rest};
parse_value([{object,_line, {contract, Address}} | Rest]) -> parse_value([{object,_line, {contract, Address}} | Rest]) ->
{aeb_fate_data:make_contract(Address), Rest}; {gmb_fate_data:make_contract(Address), Rest};
parse_value([{object,_line, {oracle, Address}} | Rest]) -> parse_value([{object,_line, {oracle, Address}} | Rest]) ->
{aeb_fate_data:make_oracle(Address), Rest}; {gmb_fate_data:make_oracle(Address), Rest};
parse_value([{object,_line, {oracle_query, Address}} | Rest]) -> parse_value([{object,_line, {oracle_query, Address}} | Rest]) ->
{aeb_fate_data:make_oracle_query(Address), Rest}; {gmb_fate_data:make_oracle_query(Address), Rest};
parse_value([{object,_line, {channel, Address}} | Rest]) -> parse_value([{object,_line, {channel, Address}} | Rest]) ->
{aeb_fate_data:make_channel(Address), Rest}; {gmb_fate_data:make_channel(Address), Rest};
parse_value([{hash,_line, Hash} | Rest]) -> parse_value([{hash,_line, Hash} | Rest]) ->
{aeb_fate_data:make_hash(Hash), Rest}; {gmb_fate_data:make_hash(Hash), Rest};
parse_value([{signature,_line, Hash} | Rest]) -> parse_value([{signature,_line, Hash} | Rest]) ->
{aeb_fate_data:make_signature(Hash), Rest}; {gmb_fate_data:make_signature(Hash), Rest};
parse_value([{typerep,_line} | Rest]) -> parse_value([{typerep,_line} | Rest]) ->
to_type(Rest). to_type(Rest).
@ -481,20 +488,20 @@ insert_fun(none, [], Env) -> Env;
insert_fun({NameString, ArgType, RetType}, Code, #{ fate_code := FateCode insert_fun({NameString, ArgType, RetType}, Code, #{ fate_code := FateCode
, functions := Funs} = Env) -> , functions := Funs} = Env) ->
Name = list_to_binary(NameString), Name = list_to_binary(NameString),
{FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode), {FateCode1, Id} = gmb_fate_code:insert_symbol(Name, FateCode),
BodyByteCode = aeb_fate_code:serialize_code(lists:reverse(Code)), BodyByteCode = gmb_fate_code:serialize_code(lists:reverse(Code)),
SigByteCode = aeb_fate_code:serialize_signature({ArgType, RetType}), SigByteCode = gmb_fate_code:serialize_signature({ArgType, RetType}),
FunByteCode = [?FUNCTION, Id, SigByteCode, BodyByteCode], FunByteCode = [?FUNCTION, Id, gmb_fate_encoding:serialize(0), SigByteCode, BodyByteCode],
Env#{ functions => Funs#{ Id => FunByteCode } Env#{ functions => Funs#{ Id => FunByteCode }
, fate_code => FateCode1}. , fate_code => FateCode1}.
insert_symbol(Name, #{ fate_code := FateCode } = Env) -> insert_symbol(Name, #{ fate_code := FateCode } = Env) ->
{FateCode1, Id} = aeb_fate_code:insert_symbol(Name, FateCode), {FateCode1, Id} = gmb_fate_code:insert_symbol(Name, FateCode),
{ Env#{ fate_code => FateCode1 } { Env#{ fate_code => FateCode1 }
, Id}. , Id}.
insert_annotation(comment, Line, Comment, #{ fate_code := FateCode } = Env) -> insert_annotation(comment, Line, Comment, #{ fate_code := FateCode } = Env) ->
FateCode1 = aeb_fate_code:insert_annotation(comment, Line, Comment, FateCode), FateCode1 = gmb_fate_code:insert_annotation(comment, Line, Comment, FateCode),
Env#{ fate_code => FateCode1}. Env#{ fate_code => FateCode1}.
mk_hash(Id) -> mk_hash(Id) ->

View File

@ -1,6 +1,7 @@
%%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*- %%% -*- erlang-indent-level:4; indent-tabs-mode: nil -*-
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2019, aeternity Anstalt %%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc %%% @doc
%%% Handling FATE code. %%% Handling FATE code.
%%% @end %%% @end
@ -16,7 +17,9 @@ BASE58 = [123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]
BASE64 = [A-Za-z0-9+/=] BASE64 = [A-Za-z0-9+/=]
INT = {DIGIT}+ INT = {DIGIT}+
HEX = 0x{HEXDIGIT}+ HEX = 0x{HEXDIGIT}+
OBJECT = @[a-z][a-z]_{BASE58}+ OBJ_PFX = (ak|ct|ok|oq|ch|sg)
OBJECT = @{OBJ_PFX}_{BASE58}+
CODE = @cb_{BASE64}+
BYTES = #{BASE64}+ BYTES = #{BASE64}+
WS = [\000-\s] WS = [\000-\s]
ID = {LOWER}[a-zA-Z0-9_]* ID = {LOWER}[a-zA-Z0-9_]*
@ -37,6 +40,8 @@ FUNCTION : {token, {function, TokenLine, 'FUNCTION' }}.
{BYTES} : {BYTES} :
{token, {bytes, TokenLine, parse_hash(TokenChars)}}. {token, {bytes, TokenLine, parse_hash(TokenChars)}}.
{CODE} :
{token, {contract_bytearray, TokenLine, parse_contract_bytearray(TokenChars)}}.
{OBJECT} : {OBJECT} :
{token, {object, TokenLine, parse_object(TokenChars)}}. {token, {object, TokenLine, parse_object(TokenChars)}}.
{ID} : {ID} :
@ -96,7 +101,7 @@ Erlang code.
-ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]). -ignore_xref([format_error/1, string/2, token/2, token/3, tokens/2, tokens/3]).
-include_lib("aebytecode/include/aeb_fate_opcodes.hrl"). -include_lib("gmbytecode/include/gmb_fate_opcodes.hrl").
parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16). parse_hex("0x" ++ Chars) -> list_to_integer(Chars, 16).
@ -107,12 +112,16 @@ parse_arg("arg" ++ N) -> list_to_integer(N).
parse_var("var" ++ N) -> list_to_integer(N). parse_var("var" ++ N) -> list_to_integer(N).
parse_hash("#" ++ Chars) -> parse_hash("#" ++ Chars) ->
base64:decode(Chars). base64:decode(Chars).
parse_contract_bytearray("@" ++ Chars) ->
case gmser_api_encoder:decode(unicode:characters_to_binary(Chars)) of
{contract_bytearray, Bin} -> Bin
end.
parse_object([_|Chars]) -> parse_object([_|Chars]) ->
case aeser_api_encoder:decode(unicode:characters_to_binary(Chars)) of case gmser_api_encoder:decode(unicode:characters_to_binary(Chars)) of
{account_pubkey, Bin} -> {address, Bin}; {account_pubkey, Bin} -> {address, Bin};
{contract_pubkey, Bin} -> {contract, Bin}; {contract_pubkey, Bin} -> {contract, Bin};
{oracle_pubkey, Bin} -> {oracle, Bin}; {oracle_pubkey, Bin} -> {oracle, Bin};

View File

@ -1,17 +1,19 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2019, Aeternity Anstalt %%% @copyright (C) 2019, Aeternity Anstalt
%%% @doc %%% @doc
%%% ADT for fate byte code/fate code %%% ADT for fate byte code/fate code
%%% @end %%% @end
%%% %%%
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_fate_code). -module(gmb_fate_code).
-vsn("3.4.1").
-export([ annotations/1 -export([ annotations/1
, deserialize/1 , deserialize/1
, functions/1 , functions/1
, insert_annotation/4 , insert_annotation/4
, insert_fun/4 , insert_fun/5
, insert_symbol/2 , insert_symbol/2
, new/0 , new/0
, serialize/1 , serialize/1
@ -19,18 +21,17 @@
, serialize/3 , serialize/3
, serialize_code/1 , serialize_code/1
, serialize_signature/1 , serialize_signature/1
, strip_init_function/1
, symbol_identifier/1 , symbol_identifier/1
, symbols/1 , symbols/1
]). ]).
-include("../include/aeb_fate_opcodes.hrl"). -include("../include/gmb_fate_opcodes.hrl").
-include("../include/aeb_fate_data.hrl"). -include("../include/gmb_fate_data.hrl").
-ifdef(EQC).
-export([ update_annotations/2 -export([ update_annotations/2
, update_functions/2 , update_functions/2
, update_symbols/2]). , update_symbols/2]).
-endif.
-record(fcode, { functions = #{} :: map() -record(fcode, { functions = #{} :: map()
, symbols = #{} :: map() , symbols = #{} :: map()
@ -72,9 +73,9 @@ symbol_identifier(Bin) ->
{ok, <<X:4/binary,_/binary>> } = eblake2:blake2b(?HASH_BYTES, Bin), {ok, <<X:4/binary,_/binary>> } = eblake2:blake2b(?HASH_BYTES, Bin),
X. X.
insert_fun(Name, {ArgType, RetType}, #{} = BBs, FCode) -> insert_fun(Name, Attrs, {ArgType, RetType}, #{} = BBs, FCode) ->
{F1, ID} = insert_symbol(Name, FCode), {F1, ID} = insert_symbol(Name, FCode),
update_functions(F1, #{ID => {{ArgType, RetType}, BBs}}). update_functions(F1, #{ID => {Attrs, {ArgType, RetType}, BBs}}).
insert_symbol(Name, #fcode{ symbols = Syms } = F) -> insert_symbol(Name, #fcode{ symbols = Syms } = F) ->
ID = symbol_identifier(Name), ID = symbol_identifier(Name),
@ -88,10 +89,16 @@ insert_symbol(Name, #fcode{ symbols = Syms } = F) ->
end. end.
insert_annotation(comment =_Type, Line, Comment, FCode) -> insert_annotation(comment =_Type, Line, Comment, FCode) ->
Key = aeb_fate_data:make_tuple({aeb_fate_data:make_string("comment"), Line}), Key = gmb_fate_data:make_tuple({gmb_fate_data:make_string("comment"), Line}),
Value = aeb_fate_data:make_string(Comment), Value = gmb_fate_data:make_string(Comment),
update_annotations(FCode, #{ Key => Value }). 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 %%% Serialization
%%%=================================================================== %%%===================================================================
@ -106,9 +113,9 @@ serialize(#fcode{} = F, Options) ->
serialize(#fcode{} = F, Functions, Options) -> serialize(#fcode{} = F, Functions, Options) ->
SymbolTable = serialize_symbol_table(F), SymbolTable = serialize_symbol_table(F),
Annotatations = serialize_annotations(F), Annotatations = serialize_annotations(F),
ByteCode = << (aeser_rlp:encode(Functions))/binary, ByteCode = << (gmser_rlp:encode(Functions))/binary,
(aeser_rlp:encode(SymbolTable))/binary, (gmser_rlp:encode(SymbolTable))/binary,
(aeser_rlp:encode(Annotatations))/binary (gmser_rlp:encode(Annotatations))/binary
>>, >>,
case proplists:lookup(pp_hex_string, Options) of case proplists:lookup(pp_hex_string, Options) of
@ -128,19 +135,26 @@ to_hexstring(ByteList) ->
serialize_functions(#fcode{ functions = Functions }) -> serialize_functions(#fcode{ functions = Functions }) ->
%% Sort the functions on name to get a canonical serialisation. %% Sort the functions on name to get a canonical serialisation.
iolist_to_binary( iolist_to_binary(
lists:foldr(fun({Id, {Sig, C}}, Acc) -> lists:foldr(fun({Id, {Attrs, Sig, C}}, Acc) ->
[[?FUNCTION, Id, serialize_signature(Sig), serialize_bbs(C)] | Acc] [[?FUNCTION, Id, serialize_attributes(Attrs), serialize_signature(Sig), serialize_bbs(C)] | Acc]
end, [], lists:sort(maps:to_list(Functions)))). end, [], lists:sort(maps:to_list(Functions)))).
serialize_attributes(Attrs) ->
AttrVal = lists:sum([ attr_value(Attr) || Attr <- Attrs ]),
gmb_fate_encoding:serialize(?MAKE_FATE_INTEGER(AttrVal)).
attr_value(private) -> 1;
attr_value(payable) -> 2.
serialize_signature({Args, RetType}) -> serialize_signature({Args, RetType}) ->
[aeb_fate_encoding:serialize_type({tuple, Args}) | [gmb_fate_encoding:serialize_type({tuple, Args}) |
aeb_fate_encoding:serialize_type(RetType)]. gmb_fate_encoding:serialize_type(RetType)].
serialize_symbol_table(#fcode{ symbols = Symbols }) -> serialize_symbol_table(#fcode{ symbols = Symbols }) ->
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Symbols)). gmb_fate_encoding:serialize(gmb_fate_data:make_map(Symbols)).
serialize_annotations(#fcode{ annotations = Annotations}) -> serialize_annotations(#fcode{ annotations = Annotations }) ->
aeb_fate_encoding:serialize(aeb_fate_data:make_map(Annotations)). gmb_fate_encoding:serialize(gmb_fate_data:make_map(Annotations)).
serialize_bbs(#{} = BBs) -> serialize_bbs(#{} = BBs) ->
serialize_bbs(BBs, 0, []). serialize_bbs(BBs, 0, []).
@ -162,11 +176,11 @@ serialize_op(Op) ->
true -> tuple_to_list(Op); true -> tuple_to_list(Op);
false -> [Op] false -> [Op]
end, end,
[aeb_fate_opcodes:m_to_op(Mnemonic) | serialize_code(Args)]. [gmb_fate_opcodes:m_to_op(Mnemonic) | serialize_code(Args)].
sanity_check(#fcode{ functions = Funs }) -> sanity_check(#fcode{ functions = Funs }) ->
_ = [ case Def of _ = [ case Def of
{_, BBs} when byte_size(Id) == 4 -> sanity_check_bbs(BBs); {_, _, BBs} when byte_size(Id) == 4 -> sanity_check_bbs(BBs);
_ -> error({illegal_function_id, Id}) _ -> error({illegal_function_id, Id})
end || {Id, Def} <- maps:to_list(Funs) ], end || {Id, Def} <- maps:to_list(Funs) ],
ok. ok.
@ -201,12 +215,12 @@ sanity_check_op(IsLast, Op) ->
true -> tuple_to_list(Op); true -> tuple_to_list(Op);
false -> [Op] false -> [Op]
end, end,
safe_sanity_check(IsLast, aeb_fate_opcodes:m_to_op(Mnemonic), Args). safe_sanity_check(IsLast, gmb_fate_opcodes:m_to_op(Mnemonic), Args).
safe_sanity_check(IsLast, Op, Args) -> safe_sanity_check(IsLast, Op, Args) ->
case length(Args) == aeb_fate_opcodes:args(Op) of case length(Args) == gmb_fate_opcodes:args(Op) of
true -> true ->
case IsLast == aeb_fate_opcodes:end_bb(Op) of case IsLast == gmb_fate_opcodes:end_bb(Op) of
true -> ok; true -> ok;
false -> error({wrong_opcode_in_bb, Op}) false -> error({wrong_opcode_in_bb, Op})
end; end;
@ -257,7 +271,7 @@ pad_args(List) ->
end. end.
serialize_data(_, Data) -> serialize_data(_, Data) ->
aeb_fate_encoding:serialize(Data). gmb_fate_encoding:serialize(Data).
%% 00 : stack/unused (depending on instruction) %% 00 : stack/unused (depending on instruction)
%% 01 : argN %% 01 : argN
@ -279,9 +293,9 @@ bits_to_modifier(2#00) -> stack.
%%%=================================================================== %%%===================================================================
deserialize(Bytes) -> deserialize(Bytes) ->
{ByteCode, Rest1} = aeser_rlp:decode_one(Bytes), {ByteCode, Rest1} = gmser_rlp:decode_one(Bytes),
{SymbolTable, Rest2} = aeser_rlp:decode_one(Rest1), {SymbolTable, Rest2} = gmser_rlp:decode_one(Rest1),
{Annotations, <<>>} = aeser_rlp:decode_one(Rest2), {Annotations, <<>>} = gmser_rlp:decode_one(Rest2),
Env = #{ function => none Env = #{ function => none
, bb => 0 , bb => 0
@ -303,33 +317,35 @@ deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
, bb := 0 , bb := 0
, current_bb_code := [] , current_bb_code := []
} = Env) -> } = Env) ->
{Sig, Rest2} = deserialize_signature(Rest), {Attrs, Rest2} = deserialize_attributes(Rest),
Env2 = Env#{function => {<<A,B,C,D>>, Sig}}, {Sig, Rest3} = deserialize_signature(Rest2),
deserialize_functions(Rest2, Env2); Env2 = Env#{function => {<<A,B,C,D>>, Attrs, Sig}},
deserialize_functions(Rest3, Env2);
deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>, deserialize_functions(<<?FUNCTION:8, A, B, C, D, Rest/binary>>,
#{ function := {F, Sig} #{ function := {F, Attrs, Sig}
, bb := BB , bb := BB
, current_bb_code := Code , current_bb_code := Code
, code := Program , code := Program
, functions := Funs} = Env) -> , functions := Funs} = Env) ->
{NewSig, Rest2} = deserialize_signature(Rest), {NewAttrs, Rest2} = deserialize_attributes(Rest),
{NewSig, Rest3} = deserialize_signature(Rest2),
case Code of case Code of
[] -> [] ->
Env2 = Env#{ bb => 0 Env2 = Env#{ bb => 0
, current_bb_code => [] , current_bb_code => []
, function => {<<A,B,C,D>>, NewSig} , function => {<<A,B,C,D>>, NewAttrs, NewSig}
, code => #{} , code => #{}
, functions => Funs#{F => {Sig, Program}}}, , functions => Funs#{F => {Attrs, Sig, Program}}},
deserialize_functions(Rest2, Env2); deserialize_functions(Rest3, Env2);
_ -> _ ->
Env2 = Env#{ bb => 0 Env2 = Env#{ bb => 0
, current_bb_code => [] , current_bb_code => []
, function => {<<A,B,C,D>>, NewSig} , function => {<<A,B,C,D>>, NewAttrs, NewSig}
, code => #{} , code => #{}
, functions => , functions =>
Funs#{F => {Sig, Funs#{F => {Attrs, Sig,
Program#{ BB => lists:reverse(Code)}}}}, Program#{ BB => lists:reverse(Code)}}}},
deserialize_functions(Rest2, Env2) deserialize_functions(Rest3, Env2)
end; end;
deserialize_functions(<<_Op:8, _Rest/binary>>, deserialize_functions(<<_Op:8, _Rest/binary>>,
#{ function := none }) -> #{ function := none }) ->
@ -339,7 +355,7 @@ deserialize_functions(<<Op:8, Rest/binary>>,
, current_bb_code := Code , current_bb_code := Code
, code := Program} = Env) -> , code := Program} = Env) ->
{Rest2, OpCode} = deserialize_op(Op, Rest, Code), {Rest2, OpCode} = deserialize_op(Op, Rest, Code),
case aeb_fate_opcodes:end_bb(Op) of case gmb_fate_opcodes:end_bb(Op) of
true -> true ->
deserialize_functions(Rest2, Env#{ bb => BB+1 deserialize_functions(Rest2, Env#{ bb => BB+1
, current_bb_code => [] , current_bb_code => []
@ -351,7 +367,7 @@ deserialize_functions(<<Op:8, Rest/binary>>,
deserialize_functions(<<>>, #{ function := none deserialize_functions(<<>>, #{ function := none
, functions := Funs}) -> , functions := Funs}) ->
Funs; Funs;
deserialize_functions(<<>>, #{ function := {F, Sig} deserialize_functions(<<>>, #{ function := {F, Attrs, Sig}
, bb := BB , bb := BB
, current_bb_code := Code , current_bb_code := Code
, code := Program , code := Program
@ -361,11 +377,11 @@ deserialize_functions(<<>>, #{ function := {F, Sig}
[] -> Program; [] -> Program;
_ -> Program#{ BB => lists:reverse(Code)} _ -> Program#{ BB => lists:reverse(Code)}
end, end,
Funs#{F => {Sig, FunctionCode}}. Funs#{F => {Attrs, Sig, FunctionCode}}.
deserialize_op(Op, Rest, Code) -> deserialize_op(Op, Rest, Code) ->
OpName = aeb_fate_opcodes:mnemonic(Op), OpName = gmb_fate_opcodes:mnemonic(Op),
case aeb_fate_opcodes:args(Op) of case gmb_fate_opcodes:args(Op) of
0 -> 0 ->
{Rest, [OpName | Code]}; {Rest, [OpName | Code]};
N -> N ->
@ -381,7 +397,7 @@ deserialize_n_args(N, <<M3:2, M2:2, M1:2, M0:2, Rest/binary>>) when N =< 4 ->
stack -> stack ->
{{stack, 0}, Acc}; {{stack, 0}, Acc};
Modifier -> Modifier ->
{Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc), {Arg, Acc2} = gmb_fate_encoding:deserialize_one(Acc),
{{Modifier, Arg}, Acc2} {{Modifier, Arg}, Acc2}
end end
end, Rest, ArgMods); end, Rest, ArgMods);
@ -394,22 +410,34 @@ deserialize_n_args(N, <<M7:2, M6:2, M5:2, M4:2, M3:2, M2:2, M1:2, M0:2,
stack -> stack ->
{{stack, 0}, Acc}; {{stack, 0}, Acc};
Modifier -> Modifier ->
{Arg, Acc2} = aeb_fate_encoding:deserialize_one(Acc), {Arg, Acc2} = gmb_fate_encoding:deserialize_one(Acc),
{{Modifier, Arg}, Acc2} {{Modifier, Arg}, Acc2}
end end
end, Rest, ArgMods). end, Rest, ArgMods).
deserialize_attributes(Binary) ->
{AttrVal, Rest} = gmb_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) -> deserialize_signature(Binary) ->
{{tuple, Args}, Rest} = aeb_fate_encoding:deserialize_type(Binary), {{tuple, Args}, Rest} = gmb_fate_encoding:deserialize_type(Binary),
{RetType, Rest2} = aeb_fate_encoding:deserialize_type(Rest), {RetType, Rest2} = gmb_fate_encoding:deserialize_type(Rest),
{{Args, RetType}, Rest2}. {{Args, RetType}, Rest2}.
deserialize_symbols(Table) -> deserialize_symbols(Table) ->
?FATE_MAP_VALUE(SymbolTable) = aeb_fate_encoding:deserialize(Table), ?FATE_MAP_VALUE(SymbolTable) = gmb_fate_encoding:deserialize(Table),
SymbolTable. SymbolTable.
deserialize_annotations(AnnotationsBin) -> deserialize_annotations(AnnotationsBin) ->
?FATE_MAP_VALUE(Annotations) = aeb_fate_encoding:deserialize(AnnotationsBin), ?FATE_MAP_VALUE(Annotations) = gmb_fate_encoding:deserialize(AnnotationsBin),
Annotations. Annotations.
assert_zero([]) -> assert_zero([]) ->

View File

@ -1,8 +1,9 @@
%% FATE data representation. %% FATE data representation.
%% %%
-include("aeb_fate_data.hrl"). -include("gmb_fate_data.hrl").
-module(aeb_fate_data). -module(gmb_fate_data).
-vsn("3.4.1").
-type fate_integer() :: ?FATE_INTEGER_T. -type fate_integer() :: ?FATE_INTEGER_T.
-type fate_boolean() :: ?FATE_BOOLEAN_T. -type fate_boolean() :: ?FATE_BOOLEAN_T.
@ -10,16 +11,20 @@
-type fate_list() :: ?FATE_LIST_T. -type fate_list() :: ?FATE_LIST_T.
-type fate_unit() :: ?FATE_UNIT_T. -type fate_unit() :: ?FATE_UNIT_T.
-type fate_map() :: ?FATE_MAP_T. -type fate_map() :: ?FATE_MAP_T.
-type fate_store_map() :: ?FATE_STORE_MAP_T.
-type fate_string() :: ?FATE_STRING_T. -type fate_string() :: ?FATE_STRING_T.
-type fate_address() :: ?FATE_ADDRESS_T. -type fate_address() :: ?FATE_ADDRESS_T.
-type fate_hash() :: ?FATE_BYTES_T(32). -type fate_hash() :: ?FATE_BYTES_T(32).
-type fate_signature() :: ?FATE_BYTES_T(64). -type fate_signature() :: ?FATE_BYTES_T(64).
-type fate_contract() :: ?FATE_CONTRACT_T. -type fate_contract() :: ?FATE_CONTRACT_T.
-type fate_oracle() :: ?FATE_ORACLE_T. -type fate_oracle() :: ?FATE_ORACLE_T.
-type fate_oracle_q() :: ?FATE_ORACLE_Q_T.
-type fate_channel() :: ?FATE_CHANNEL_T. -type fate_channel() :: ?FATE_CHANNEL_T.
-type fate_variant() :: ?FATE_VARIANT_T. -type fate_variant() :: ?FATE_VARIANT_T.
-type fate_tuple() :: ?FATE_TUPLE_T. -type fate_tuple() :: ?FATE_TUPLE_T.
-type fate_bits() :: ?FATE_BITS_T. -type fate_bits() :: ?FATE_BITS_T.
-type fate_typerep() :: ?FATE_TYPEREP_T.
-type fate_contract_bytearray() :: ?FATE_CONTRACT_BYTEARRAY_T.
-type fate_type_type() :: integer -type fate_type_type() :: integer
| boolean | boolean
@ -31,10 +36,12 @@
| signature | signature
| contract | contract
| oracle | oracle
| oracle_query
| channel | channel
| bits | bits
| string | string
| {variant, [fate_type_type()]}. | {variant, [fate_type_type()]}
| contract_bytearray.
-type fate_type() :: -type fate_type() ::
@ -50,10 +57,13 @@
| fate_signature() | fate_signature()
| fate_contract() | fate_contract()
| fate_oracle() | fate_oracle()
| fate_oracle_q()
| fate_channel() | fate_channel()
| fate_variant() | fate_variant()
| fate_map() | fate_map()
| fate_bits(). | fate_bits()
| fate_typerep()
| fate_contract_bytearray().
-export_type([fate_type/0 -export_type([fate_type/0
, fate_boolean/0 , fate_boolean/0
@ -71,6 +81,7 @@
, fate_channel/0 , fate_channel/0
, fate_variant/0 , fate_variant/0
, fate_map/0 , fate_map/0
, fate_store_map/0
, fate_bits/0 , fate_bits/0
, fate_type_type/0 , fate_type_type/0
]). ]).
@ -82,6 +93,8 @@
, make_tuple/1 , make_tuple/1
, make_string/1 , make_string/1
, make_map/1 , make_map/1
, make_store_map/1
, make_store_map/2
, make_address/1 , make_address/1
, make_bytes/1 , make_bytes/1
, make_hash/1 , make_hash/1
@ -93,6 +106,7 @@
, make_bits/1 , make_bits/1
, make_unit/0 , make_unit/0
, make_typerep/1 , make_typerep/1
, make_contract_bytearray/1
]). ]).
-export([ -export([
elt/2 elt/2
@ -108,6 +122,8 @@ make_list(L) -> ?MAKE_FATE_LIST(L).
make_unit() -> ?FATE_UNIT. make_unit() -> ?FATE_UNIT.
make_tuple(T) -> ?FATE_TUPLE(T). make_tuple(T) -> ?FATE_TUPLE(T).
make_map(M) -> ?MAKE_FATE_MAP(M). make_map(M) -> ?MAKE_FATE_MAP(M).
make_store_map(Id) -> make_store_map(#{}, Id).
make_store_map(Cache, Id) -> ?FATE_STORE_MAP(Cache, Id).
make_address(X) -> ?FATE_ADDRESS(X). make_address(X) -> ?FATE_ADDRESS(X).
make_bytes(X) -> ?FATE_BYTES(X). make_bytes(X) -> ?FATE_BYTES(X).
make_hash(X) -> make_bytes(X). make_hash(X) -> make_bytes(X).
@ -122,6 +138,7 @@ make_string(S) when is_list(S) ->
?FATE_STRING(iolist_to_binary(S)); ?FATE_STRING(iolist_to_binary(S));
make_string(S) when is_binary(S) -> ?FATE_STRING(S). make_string(S) when is_binary(S) -> ?FATE_STRING(S).
make_typerep(T) -> ?FATE_TYPEREP(T). make_typerep(T) -> ?FATE_TYPEREP(T).
make_contract_bytearray(B) -> ?FATE_CONTRACT_BYTEARRAY(B).
%% Tag points to the selected variant (zero based) %% Tag points to the selected variant (zero based)
%% The arity of this variant is read from the list of provided arities %% The arity of this variant is read from the list of provided arities
@ -166,17 +183,19 @@ format(M) when ?IS_FATE_MAP(M) ->
["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"]; ["{ ", format_kvs(maps:to_list(?FATE_MAP_VALUE(M))), " }"];
format(?FATE_BYTES(X)) -> ["#", base64:encode(X)]; format(?FATE_BYTES(X)) -> ["#", base64:encode(X)];
format(?FATE_ADDRESS(X)) -> format(?FATE_ADDRESS(X)) ->
["@", aeser_api_encoder:encode(account_pubkey, X)]; ["@", gmser_api_encoder:encode(account_pubkey, X)];
format(?FATE_CONTRACT(X)) -> format(?FATE_CONTRACT(X)) ->
["@", aeser_api_encoder:encode(contract_pubkey, X)]; ["@", gmser_api_encoder:encode(contract_pubkey, X)];
format(?FATE_ORACLE(X)) -> format(?FATE_ORACLE(X)) ->
["@", aeser_api_encoder:encode(oracle_pubkey, X)]; ["@", gmser_api_encoder:encode(oracle_pubkey, X)];
format(?FATE_ORACLE_Q(X)) -> format(?FATE_ORACLE_Q(X)) ->
["@", aeser_api_encoder:encode(oracle_query_id, X)]; ["@", gmser_api_encoder:encode(oracle_query_id, X)];
format(?FATE_CHANNEL(X)) -> format(?FATE_CHANNEL(X)) ->
["@", aeser_api_encoder:encode(channel, X)]; ["@", gmser_api_encoder:encode(channel, X)];
format(?FATE_TYPEREP(X)) -> format(?FATE_TYPEREP(X)) ->
["'", io_lib:format("~p", [X])]; ["'", io_lib:format("~p", [X])];
format(?FATE_CONTRACT_BYTEARRAY(B)) ->
["@", gmser_api_encoder:encode(contract_bytearray, B)];
format(V) -> exit({not_a_fate_type, V}). format(V) -> exit({not_a_fate_type, V}).
format_bits(0, Acc) -> Acc; format_bits(0, Acc) -> Acc;
@ -202,6 +221,7 @@ format_kvs(List) ->
%% Total order of FATE terms. %% Total order of FATE terms.
%% Integers < Booleans < Address < Channel < Contract < Oracle %% Integers < Booleans < Address < Channel < Contract < Oracle
%% < Hash < Signature < Bits < String < Tuple < Map < List < Variant %% < Hash < Signature < Bits < String < Tuple < Map < List < Variant
%% < Oracle query < FATE code
-define(ORD_INTEGER , 0). -define(ORD_INTEGER , 0).
-define(ORD_BOOLEAN , 1). -define(ORD_BOOLEAN , 1).
-define(ORD_ADDRESS , 2). -define(ORD_ADDRESS , 2).
@ -216,6 +236,7 @@ format_kvs(List) ->
-define(ORD_LIST , 11). -define(ORD_LIST , 11).
-define(ORD_VARIANT , 12). -define(ORD_VARIANT , 12).
-define(ORD_ORACLE_Q , 13). -define(ORD_ORACLE_Q , 13).
-define(ORD_CONTRACT_BYTEARRAY , 14).
-spec ordinal(fate_type()) -> integer(). -spec ordinal(fate_type()) -> integer().
ordinal(T) when ?IS_FATE_INTEGER(T) -> ?ORD_INTEGER; ordinal(T) when ?IS_FATE_INTEGER(T) -> ?ORD_INTEGER;
@ -231,7 +252,8 @@ ordinal(T) when ?IS_FATE_TUPLE(T) -> ?ORD_TUPLE;
ordinal(T) when ?IS_FATE_MAP(T) -> ?ORD_MAP; ordinal(T) when ?IS_FATE_MAP(T) -> ?ORD_MAP;
ordinal(T) when ?IS_FATE_LIST(T) -> ?ORD_LIST; ordinal(T) when ?IS_FATE_LIST(T) -> ?ORD_LIST;
ordinal(T) when ?IS_FATE_VARIANT(T) -> ?ORD_VARIANT; 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_ORACLE_Q(T) -> ?ORD_ORACLE_Q;
ordinal(T) when ?IS_FATE_CONTRACT_BYTEARRAY(T) -> ?ORD_CONTRACT_BYTEARRAY.
-spec lt(fate_type(), fate_type()) -> boolean(). -spec lt(fate_type(), fate_type()) -> boolean().
@ -259,17 +281,9 @@ lt(?ORD_BITS, A, B) when ?IS_FATE_BITS(A), ?IS_FATE_BITS(B) ->
true; true;
true -> BitsA < BitsB true -> BitsA < BitsB
end; end;
lt(?ORD_STRING,?FATE_STRING(A), ?FATE_STRING(B)) -> lt(?ORD_TUPLE, ?FATE_TUPLE(A), ?FATE_TUPLE(B)) ->
SizeA = size(A), SizeA = tuple_size(A),
SizeB = size(B), SizeB = tuple_size(B),
case SizeA - SizeB of
0 -> A < B;
N -> N < 0
end;
lt(?ORD_TUPLE,?FATE_TUPLE(A), ?FATE_TUPLE(B)) ->
SizeA = size(A),
SizeB = size(B),
case SizeA - SizeB of case SizeA - SizeB of
0 -> tuple_elements_lt(0, A, B, SizeA); 0 -> tuple_elements_lt(0, A, B, SizeA);
N -> N < 0 N -> N < 0
@ -284,19 +298,15 @@ lt(?ORD_MAP, ?FATE_MAP_VALUE(A), ?FATE_MAP_VALUE(B)) ->
lt(?ORD_LIST, ?FATE_LIST_VALUE(_), ?FATE_LIST_VALUE([])) -> false; 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([]), ?FATE_LIST_VALUE(_)) -> true;
lt(?ORD_LIST, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) -> lt(?ORD_LIST, ?FATE_LIST_VALUE([A|RA]), ?FATE_LIST_VALUE([B|RB])) ->
O1 = ordinal(A),
O2 = ordinal(B),
if O1 == O2 ->
if A == B -> lt(RA, RB); if A == B -> lt(RA, RB);
true -> A < B true -> lt(A, B)
end;
true -> O1 < O2
end; end;
lt(?ORD_VARIANT, ?FATE_VARIANT(AritiesA, TagA, TA), lt(?ORD_VARIANT, ?FATE_VARIANT(AritiesA, TagA, TA),
?FATE_VARIANT(AritiesB, TagB, TB)) -> ?FATE_VARIANT(AritiesB, TagB, TB)) ->
if length(AritiesA) < length(AritiesB) -> true; if length(AritiesA) < length(AritiesB) -> true;
length(AritiesA) > length(AritiesB) -> false; length(AritiesA) > length(AritiesB) -> false;
true -> true ->
% Compare element by element consistent with Erlang compare
if AritiesA < AritiesB -> true; if AritiesA < AritiesB -> true;
AritiesA > AritiesB -> false; AritiesA > AritiesB -> false;
true -> true ->
@ -306,7 +316,32 @@ lt(?ORD_VARIANT, ?FATE_VARIANT(AritiesA, TagA, TA),
end end
end end
end; end;
lt(_, A, B) -> A < B. 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).
% 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.
tuple_elements_lt(N,_A,_B, N) -> tuple_elements_lt(N,_A,_B, N) ->
false; false;

View File

@ -29,7 +29,7 @@
%% * First draft of FATE serialization encoding/decoding. %% * First draft of FATE serialization encoding/decoding.
%% Initial experiment with tags %% Initial experiment with tags
%% * Second draft %% * Second draft
%% * FATE data is now defined in aefa_data.erl %% * FATE data is now defined in gmfa_data.erl
%% * Third draft %% * Third draft
%% * Added Bit strings %% * Added Bit strings
%% %%
@ -39,7 +39,8 @@
%% * Handle instructions. %% * Handle instructions.
%% %%
%% ------------------------------------------------------------------------ %% ------------------------------------------------------------------------
-module(aeb_fate_encoding). -module(gmb_fate_encoding).
-vsn("3.4.1").
-export([ deserialize/1 -export([ deserialize/1
, deserialize_one/1 , deserialize_one/1
@ -52,7 +53,7 @@
-export([sort/1]). -export([sort/1]).
-endif. -endif.
-include("aeb_fate_data.hrl"). -include("gmb_fate_data.hrl").
%% Definition of tag scheme. %% Definition of tag scheme.
%% This has to follow the protocol specification. %% This has to follow the protocol specification.
@ -63,7 +64,7 @@
-define(SHORT_STRING , 2#01). %% xxxxxx 01 | [bytes] - when 0 < xxxxxx:size < 64 -define(SHORT_STRING , 2#01). %% xxxxxx 01 | [bytes] - when 0 < xxxxxx:size < 64
%% 11 Set below %% 11 Set below
-define(SHORT_LIST , 2#0011). %% xxxx 0011 | [encoded elements] when 0 < length < 16 -define(SHORT_LIST , 2#0011). %% xxxx 0011 | [encoded elements] when 0 < length < 16
%% xxxx 0111 %% 0111 Set below
-define(TYPE_INTEGER , 2#00000111). %% 0000 0111 - Integer typedef -define(TYPE_INTEGER , 2#00000111). %% 0000 0111 - Integer typedef
-define(TYPE_BOOLEAN , 2#00010111). %% 0001 0111 - Boolean typedef -define(TYPE_BOOLEAN , 2#00010111). %% 0001 0111 - Boolean typedef
-define(TYPE_LIST , 2#00100111). %% 0010 0111 | Type -define(TYPE_LIST , 2#00100111). %% 0010 0111 | Type
@ -74,7 +75,7 @@
-define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef -define(TYPE_STRING , 2#01110111). %% 0111 0111 - string typedef
-define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type] -define(TYPE_VARIANT , 2#10000111). %% 1000 0111 | [Arities] | [Type]
-define(TYPE_BYTES , 2#10010111). %% 1001 0111 - Bytes typedef -define(TYPE_BYTES , 2#10010111). %% 1001 0111 - Bytes typedef
%% 1010 0111 -define(TYPE_CONTRACT_BYTEARRAY,2#10100111). %% 1010 0111 - Fate code typedef
%% 1011 0111 %% 1011 0111
%% 1100 0111 %% 1100 0111
%% 1101 0111 %% 1101 0111
@ -90,9 +91,11 @@
-define(EMPTY_STRING , 2#01011111). %% 0101 1111 -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 -define(FALSE , 2#01111111). %% 0111 1111
%% %% 1000 1111 - FREE (Possibly for bytecode in the future.) -define(
CONTRACT_BYTEARRAY, 2#10001111). %% 1000 1111
-define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array -define(OBJECT , 2#10011111). %% 1001 1111 | ObjectType | RLP encoded Array
-define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values] -define(VARIANT , 2#10101111). %% 1010 1111 | [encoded arities] | encoded tag | [encoded values]
-define(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(NEG_BITS , 2#11001111). %% 1100 1111 | RLP encoded integer (infinite 1:s bitfield)
-define(EMPTY_MAP , 2#11011111). %% 1101 1111 -define(EMPTY_MAP , 2#11011111). %% 1101 1111
-define(NEG_BIG_INT , 2#11101111). %% 1110 1111 | RLP encoded (integer - 64) -define(NEG_BIG_INT , 2#11101111). %% 1110 1111 | RLP encoded (integer - 64)
@ -122,16 +125,18 @@
X =:= ?TYPE_TUPLE orelse X =:= ?TYPE_TUPLE orelse
X =:= ?TYPE_OBJECT orelse X =:= ?TYPE_OBJECT orelse
X =:= ?TYPE_BITS orelse X =:= ?TYPE_BITS orelse
X =:= ?TYPE_BYTES orelse
X =:= ?TYPE_MAP orelse X =:= ?TYPE_MAP orelse
X =:= ?TYPE_STRING orelse X =:= ?TYPE_STRING orelse
X =:= ?TYPE_VARIANT)). X =:= ?TYPE_VARIANT orelse
X =:= ?TYPE_CONTRACT_BYTEARRAY)).
%% -------------------------------------------------- %% --------------------------------------------------
%% Serialize %% Serialize
%% Serialized a Fate data value into a sequence of bytes %% Serialized a Fate data value into a sequence of bytes
%% according to the Fate serialization specification. %% according to the Fate serialization specification.
%% TODO: The type Fate Data is not final yet. %% TODO: The type Fate Data is not final yet.
-spec serialize(aeb_fate_data:fate_type()) -> binary(). -spec serialize(gmb_fate_data:fate_type()) -> binary().
serialize(?FATE_TRUE) -> <<?TRUE>>; serialize(?FATE_TRUE) -> <<?TRUE>>;
serialize(?FATE_FALSE) -> <<?FALSE>>; serialize(?FATE_FALSE) -> <<?FALSE>>;
serialize(?FATE_UNIT) -> <<?EMPTY_TUPLE>>; %% ! Untyped serialize(?FATE_UNIT) -> <<?EMPTY_TUPLE>>; %% ! Untyped
@ -154,15 +159,15 @@ serialize(String) when ?IS_FATE_STRING(String),
serialize(?FATE_BYTES(Bytes)) when is_binary(Bytes) -> serialize(?FATE_BYTES(Bytes)) when is_binary(Bytes) ->
<<?OBJECT, ?OTYPE_BYTES, (serialize(?FATE_STRING(Bytes)))/binary>>; <<?OBJECT, ?OTYPE_BYTES, (serialize(?FATE_STRING(Bytes)))/binary>>;
serialize(?FATE_ADDRESS(Address)) when is_binary(Address) -> serialize(?FATE_ADDRESS(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ADDRESS, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_ADDRESS, (gmser_rlp:encode(Address))/binary>>;
serialize(?FATE_CONTRACT(Address)) when is_binary(Address) -> serialize(?FATE_CONTRACT(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CONTRACT, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_CONTRACT, (gmser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE(Address)) when is_binary(Address) -> serialize(?FATE_ORACLE(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_ORACLE, (gmser_rlp:encode(Address))/binary>>;
serialize(?FATE_ORACLE_Q(Address)) when is_binary(Address) -> serialize(?FATE_ORACLE_Q(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_ORACLE_Q, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_ORACLE_Q, (gmser_rlp:encode(Address))/binary>>;
serialize(?FATE_CHANNEL(Address)) when is_binary(Address) -> serialize(?FATE_CHANNEL(Address)) when is_binary(Address) ->
<<?OBJECT, ?OTYPE_CHANNEL, (aeser_rlp:encode(Address))/binary>>; <<?OBJECT, ?OTYPE_CHANNEL, (gmser_rlp:encode(Address))/binary>>;
serialize(?FATE_TUPLE(T)) when size(T) > 0 -> serialize(?FATE_TUPLE(T)) when size(T) > 0 ->
S = size(T), S = size(T),
L = tuple_to_list(T), L = tuple_to_list(T),
@ -193,6 +198,9 @@ serialize(Map) when ?IS_FATE_MAP(Map) ->
<<?MAP, <<?MAP,
(rlp_encode_int(Size))/binary, (rlp_encode_int(Size))/binary,
(Elements)/binary>>; (Elements)/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)) -> serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
Arities = [A || A <- Arities, is_integer(A), A < 256], Arities = [A || A <- Arities, is_integer(A), A < 256],
Size = length(Arities), Size = length(Arities),
@ -202,7 +210,7 @@ serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
, is_tuple(Values) -> , is_tuple(Values) ->
Arity = lists:nth(Tag+1, Arities), Arity = lists:nth(Tag+1, Arities),
if size(Values) =:= Arity -> if size(Values) =:= Arity ->
EncodedArities = aeser_rlp:encode(list_to_binary(Arities)), EncodedArities = gmser_rlp:encode(list_to_binary(Arities)),
<<?VARIANT, <<?VARIANT,
EncodedArities/binary, EncodedArities/binary,
Tag:8, Tag:8,
@ -211,12 +219,16 @@ serialize(?FATE_VARIANT(Arities, Tag, Values)) ->
end end
end; end;
serialize(?FATE_TYPEREP(T)) -> serialize(?FATE_TYPEREP(T)) ->
iolist_to_binary(serialize_type(T)). iolist_to_binary(serialize_type(T));
serialize(?FATE_CONTRACT_BYTEARRAY(B)) ->
<<?CONTRACT_BYTEARRAY,
(serialize_integer(?FATE_CONTRACT_BYTEARRAY_SIZE(B)))/binary
, B/binary>>.
%% ----------------------------------------------------- %% -----------------------------------------------------
-spec serialize_type(aeb_fate_data:fate_type_type()) -> [byte()]. -spec serialize_type(gmb_fate_data:fate_type_type()) -> [byte()].
serialize_type(integer) -> [?TYPE_INTEGER]; serialize_type(integer) -> [?TYPE_INTEGER];
serialize_type(boolean) -> [?TYPE_BOOLEAN]; serialize_type(boolean) -> [?TYPE_BOOLEAN];
serialize_type(any) -> [?TYPE_ANY]; serialize_type(any) -> [?TYPE_ANY];
@ -227,6 +239,8 @@ serialize_type({tuple, Ts}) ->
N when N =< 255 -> N when N =< 255 ->
[?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]] [?TYPE_TUPLE, N | [serialize_type(T) || T <- Ts]]
end; end;
serialize_type({bytes, any}) ->
[?TYPE_BYTES | binary_to_list(serialize_integer(-1))];
serialize_type({bytes, N}) when 0 =< N -> serialize_type({bytes, N}) when 0 =< N ->
[?TYPE_BYTES | binary_to_list(serialize_integer(N))]; [?TYPE_BYTES | binary_to_list(serialize_integer(N))];
serialize_type(address) -> [?TYPE_OBJECT, ?OTYPE_ADDRESS]; serialize_type(address) -> [?TYPE_OBJECT, ?OTYPE_ADDRESS];
@ -242,10 +256,11 @@ serialize_type({variant, ListOfVariants}) ->
Size = length(ListOfVariants), Size = length(ListOfVariants),
if Size < 256 -> if Size < 256 ->
[?TYPE_VARIANT, Size | [serialize_type(T) || T <- ListOfVariants]] [?TYPE_VARIANT, Size | [serialize_type(T) || T <- ListOfVariants]]
end. end;
serialize_type(contract_bytearray) -> [?TYPE_CONTRACT_BYTEARRAY].
-spec deserialize_type(binary()) -> {aeb_fate_data:fate_type_type(), binary()}. -spec deserialize_type(binary()) -> {gmb_fate_data:fate_type_type(), binary()}.
deserialize_type(<<?TYPE_INTEGER, Rest/binary>>) -> {integer, Rest}; deserialize_type(<<?TYPE_INTEGER, Rest/binary>>) -> {integer, Rest};
deserialize_type(<<?TYPE_BOOLEAN, Rest/binary>>) -> {boolean, Rest}; deserialize_type(<<?TYPE_BOOLEAN, Rest/binary>>) -> {boolean, Rest};
deserialize_type(<<?TYPE_ANY, Rest/binary>>) -> {any, Rest}; deserialize_type(<<?TYPE_ANY, Rest/binary>>) -> {any, Rest};
@ -258,8 +273,12 @@ deserialize_type(<<?TYPE_TUPLE, N, Rest/binary>>) ->
{{tuple, Ts}, Rest2}; {{tuple, Ts}, Rest2};
deserialize_type(<<?TYPE_BYTES, Rest/binary>>) -> deserialize_type(<<?TYPE_BYTES, Rest/binary>>) ->
{N, Rest2} = deserialize_one(Rest), {N, Rest2} = deserialize_one(Rest),
true = is_integer(N) andalso N >= 0, true = is_integer(N),
{{bytes, N}, Rest2}; if N == -1 ->
{{bytes, any}, Rest2};
0 =< N ->
{{bytes, N}, Rest2}
end;
deserialize_type(<<?TYPE_OBJECT, ObjectType, Rest/binary>>) -> deserialize_type(<<?TYPE_OBJECT, ObjectType, Rest/binary>>) ->
case ObjectType of case ObjectType of
?OTYPE_ADDRESS -> {address, Rest}; ?OTYPE_ADDRESS -> {address, Rest};
@ -277,7 +296,8 @@ deserialize_type(<<?TYPE_STRING, Rest/binary>>) ->
{string, Rest}; {string, Rest};
deserialize_type(<<?TYPE_VARIANT, Size, Rest/binary>>) -> deserialize_type(<<?TYPE_VARIANT, Size, Rest/binary>>) ->
{Variants, Rest2} = deserialize_variants(Size, Rest, []), {Variants, Rest2} = deserialize_variants(Size, Rest, []),
{{variant, Variants}, Rest2}. {{variant, Variants}, Rest2};
deserialize_type(<<?TYPE_CONTRACT_BYTEARRAY, Rest/binary>>) -> {contract_bytearray, Rest}.
deserialize_variants(0, Rest, Variants) -> deserialize_variants(0, Rest, Variants) ->
{lists:reverse(Variants), Rest}; {lists:reverse(Variants), Rest};
@ -297,13 +317,13 @@ deserialize_types(N, Binary, Acc) ->
%% ----------------------------------------------------- %% -----------------------------------------------------
rlp_encode_int(S) when S >= 0 -> rlp_encode_int(S) when S >= 0 ->
aeser_rlp:encode(binary:encode_unsigned(S)). gmser_rlp:encode(binary:encode_unsigned(S)).
%% first byte of the binary gives the number of bytes we need <<129>> is 1, <<130>> = 2, %% 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>> %% so <<129, 0>> is <<0>> and <<130, 0, 0>> is <<0, 0>>
rlp_decode_int(Binary) -> rlp_decode_int(Binary) ->
{Bin1, Rest} = aeser_rlp:decode_one(Binary), {Bin1, Rest} = gmser_rlp:decode_one(Binary),
Int = binary:decode_unsigned(Bin1), Int = binary:decode_unsigned(Bin1),
ReEncode = rlp_encode_int(Int), ReEncode = rlp_encode_int(Int),
case <<ReEncode/binary, Rest/binary>> == Binary of case <<ReEncode/binary, Rest/binary>> == Binary of
@ -334,7 +354,7 @@ serialize_bits(B) when is_integer(B) ->
B >= 0 -> <<?POS_BITS, (rlp_encode_int(Abs))/binary>> B >= 0 -> <<?POS_BITS, (rlp_encode_int(Abs))/binary>>
end. end.
-spec deserialize(binary()) -> aeb_fate_data:fate_type(). -spec deserialize(binary()) -> gmb_fate_data:fate_type().
deserialize(B) -> deserialize(B) ->
{T, <<>>} = deserialize2(B), {T, <<>>} = deserialize2(B),
T. T.
@ -372,6 +392,12 @@ deserialize2(<<?LONG_STRING, Rest/binary>>) ->
String = binary:part(Rest2, 0, Size), String = binary:part(Rest2, 0, Size),
Rest3 = binary:part(Rest2, byte_size(Rest2), - (byte_size(Rest2) - Size)), Rest3 = binary:part(Rest2, byte_size(Rest2), - (byte_size(Rest2) - Size)),
{?MAKE_FATE_STRING(String), Rest3}; {?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>>) -> deserialize2(<<S:6, ?SHORT_STRING:2, Rest/binary>>) ->
String = binary:part(Rest, 0, S), String = binary:part(Rest, 0, S),
Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)), Rest2 = binary:part(Rest, byte_size(Rest), - (byte_size(Rest) - S)),
@ -381,7 +407,7 @@ deserialize2(<<?OBJECT, ?OTYPE_BYTES, Rest/binary>>) ->
true = ?IS_FATE_STRING(String), true = ?IS_FATE_STRING(String),
{?FATE_BYTES(?FATE_STRING_VALUE(String)), Rest2}; {?FATE_BYTES(?FATE_STRING_VALUE(String)), Rest2};
deserialize2(<<?OBJECT, ObjectType, Rest/binary>>) -> deserialize2(<<?OBJECT, ObjectType, Rest/binary>>) ->
{A, Rest2} = aeser_rlp:decode_one(Rest), {A, Rest2} = gmser_rlp:decode_one(Rest),
Val = Val =
case ObjectType of case ObjectType of
?OTYPE_ADDRESS -> ?FATE_ADDRESS(A); ?OTYPE_ADDRESS -> ?FATE_ADDRESS(A);
@ -426,8 +452,11 @@ deserialize2(<<?MAP, Rest/binary>>) ->
false -> false ->
error({unknown_map_serialization_format, KVList}) error({unknown_map_serialization_format, KVList})
end; end;
deserialize2(<<?MAP_ID, Rest/binary>>) ->
{Id, Rest1} = rlp_decode_int(Rest),
{?FATE_STORE_MAP(#{}, Id), Rest1};
deserialize2(<<?VARIANT, Rest/binary>>) -> deserialize2(<<?VARIANT, Rest/binary>>) ->
{AritiesBin, <<Tag:8, Rest2/binary>>} = aeser_rlp:decode_one(Rest), {AritiesBin, <<Tag:8, Rest2/binary>>} = gmser_rlp:decode_one(Rest),
Arities = binary_to_list(AritiesBin), Arities = binary_to_list(AritiesBin),
Size = length(Arities), Size = length(Arities),
if Tag > Size -> exit({too_large_tag_in_variant, Tag, Size}); if Tag > Size -> exit({too_large_tag_in_variant, Tag, Size});
@ -474,11 +503,17 @@ sort_and_check(List) ->
sort(KVList) -> sort(KVList) ->
SortFun = fun({K1, _}, {K2, _}) -> SortFun = fun({K1, _}, {K2, _}) ->
aeb_fate_data:elt(K1, K2) gmb_fate_data:elt(K1, K2)
end, end,
lists:sort(SortFun, KVList). lists:sort(SortFun, KVList).
valid_key_type(K) when ?IS_FATE_MAP(K) -> valid_key_type(K) when ?IS_FATE_MAP(K) ->
error({map_as_key_in_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) -> valid_key_type(_K) ->
true. true.

View File

@ -0,0 +1,133 @@
-module(gmb_fate_generate_docs).
-vsn("3.4.1").
-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 <- gmb_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]
).

View File

@ -0,0 +1,800 @@
-module(gmb_fate_generate_ops).
-vsn("3.4.1").
-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 ++ "gmb_fate_opcodes.hrl",
generate_header_file(HrlFile, Ops),
generate_opcodes_ops(gmb_fate_opcodes, HrlFile, Src, Ops),
generate_code_ops(gmb_fate_ops, Src, Ops),
generate_scanner("gmb_fate_asm_scan.template", "gmb_fate_asm_scan.xrl", Src, Ops),
gen_asm_pp(gmb_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#6d -> check_numbering(16#6d+1, Rest); %% Oracles
16#7b -> check_numbering(16#7b+1, Rest); %% Oracles
16#9b -> check_numbering(16#9b+1, Rest); %% Oracles
16#f0 -> check_numbering(16#f0+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."}
%% Intentional gap (was oracles)
, { '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."}
%% Intentional gap (was oracles)
, { '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)"}
%% Intentional gap (was oracles)
, { '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"}
, { 'BYTES_SPLIT_ANY', 16#b1, false, true, true, ?GAS(10), [a, a, a], bytes_split_any, {bytes, integer}, variant, "Arg0 := bytes_split_any(Arg1, Arg2), where a positive Arg2 is the length of the first chunk, and a negative Arg2 is the length of the second chunk. Returns None if byte array is not long enough."}
, { 'BYTES_SIZE', 16#b2, false, true, true, ?GAS(10), [a, a], bytes_size, {bytes}, integer, "Arg0 := bytes_size(Arg1), returns the number of bytes in the byte array."}
, { 'BYTES_TO_FIXED_SIZE', 16#b3, false, true, true, ?GAS(10), [a, a, a], bytes_to_fixed_size, {bytes, integer}, variant, "Arg0 := bytes_to_fixed_size(Arg1, Arg2), returns Some(Arg1') if byte_size(Arg1) == Arg2, None otherwise. The type of Arg1' is bytes(Arg2) but the value is unchanged"}
, { 'INT_TO_BYTES', 16#b4, false, true, true, ?GAS(10), [a, a, a], int_to_bytes, {integer, integer}, bytes, "Arg0 := turn integer Arg1 into a byte array (big endian) length Arg2 (truncating if not fit)."}
, { 'STR_TO_BYTES', 16#b5, false, true, true, ?GAS(10), [a, a], str_to_bytes, {integer}, bytes, "Arg0 := turn string Arg1 into the corresponding byte array."}
, { 'NETWORK_ID', 16#b6, false, true, true, ?GAS(10), [a], network_id, {}, string, "Arg0 := The network_id of the chain."}
, { 'DBG_LOC', 16#f0, false, true, true, ?GAS(0), [a, a], dbg_loc, {string, integer}, none, "Debug Op: Execution location. Args = {file_name, line_num}" }
, { 'DBG_DEF', 16#f1, false, true, true, ?GAS(0), [a, a], dbg_def, {string, any}, none, "Debug Op: Define a variable. Args = {var_name, register}" }
, { 'DBG_UNDEF', 16#f2, false, true, true, ?GAS(0), [a, a], dbg_undef, {string, any}, none, "Debug Op: Undefine a variable. Args = {var_name, register}" }
, { 'DBG_CONTRACT', 16#f3, false, true, true, ?GAS(0), [a], dbg_contract, {string}, none, "Debug Op: Name the current contract. Args: {contract_name}"}
, { '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(\"gmbytecode/include/gmb_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, gmb_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]) -> "gmb_fate_data:fate_string()";
gen_arg_type_specs([ii]) -> "gmb_fate_data:fate_integer()";
gen_arg_type_specs([li]) -> "[gmb_fate_data:fate_integer()]";
gen_arg_type_specs([t]) -> "gmb_fate_data:fate_type_type()";
gen_arg_type_specs([a | Args]) -> "fate_arg(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([is | Args]) -> "gmb_fate_data:fate_string(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([ii | Args]) -> "gmb_fate_data:fate_integer(), " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([li | Args]) -> "[gmb_fate_data:fate_integer()], " ++ gen_arg_type_specs(Args);
gen_arg_type_specs([t | Args]) -> "gmb_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(\"gmbytecode/" ++ 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 gmb_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(gmb_fate_data:fate_string())";
expand_type(ii) -> "fate_arg_immediate(gmb_fate_data:fate_integer())";
expand_type(li) -> "fate_arg_immediate([gmb_fate_data:fate_integer()])";
expand_type(t) -> "gmb_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 gmb_fate_generate_ops.erl\n"
"%%% and gmb_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"
" gmb_fate_data:format(LI);\n"
"format_arg(_, {immediate, I}) ->\n"
" gmb_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.

220
src/gmb_fate_maps.erl Normal file
View File

@ -0,0 +1,220 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @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(gmb_fate_maps).
-vsn("3.4.1").
-include("gmb_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() :: gmb_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() => gmb_fate_data:fate_map() | gmb_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(gmb_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()) -> gmb_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]).

View File

@ -1,4 +1,5 @@
-module(aeb_heap). -module(gmb_heap).
-vsn("3.4.1").
-export([ to_binary/1 -export([ to_binary/1
, to_binary/2 , to_binary/2
@ -14,6 +15,7 @@
, heap_value_maps/1 , heap_value_maps/1
, heap_value_offset/1 , heap_value_offset/1
, heap_value_heap/1 , heap_value_heap/1
, heap_value_byte_size/1
, heap_fragment_maps/1 , heap_fragment_maps/1
, heap_fragment_offset/1 , heap_fragment_offset/1
, heap_fragment_heap/1 , heap_fragment_heap/1
@ -21,8 +23,8 @@
-export_type([binary_value/0, heap_value/0, offset/0, heap_fragment/0]). -export_type([binary_value/0, heap_value/0, offset/0, heap_fragment/0]).
-include_lib("aebytecode/include/aeb_typerep_def.hrl"). -include_lib("gmbytecode/include/gmb_typerep_def.hrl").
-include_lib("aebytecode/include/aeb_heap.hrl"). -include_lib("gmbytecode/include/gmb_heap.hrl").
-type word() :: non_neg_integer(). -type word() :: non_neg_integer().
-type pointer() :: word(). -type pointer() :: word().
@ -90,9 +92,28 @@ heap_value_offset({_, Heap}) -> Heap#heap.offset.
binary() | #{non_neg_integer() => non_neg_integer()}. binary() | #{non_neg_integer() => non_neg_integer()}.
heap_value_heap({_, Heap}) -> Heap#heap.heap. 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 -------------------------------------------------------- %% -- Value to binary --------------------------------------------------------
-spec to_binary(aeb_aevm_data:data()) -> aeb_aevm_data:heap(). -spec to_binary(gmb_aevm_data:data()) -> gmb_aevm_data:heap().
%% Encode the data as a heap where the first word is the value (for unboxed %% 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). %% types) or a pointer to the value (for boxed types).
to_binary(Data) -> to_binary(Data) ->
@ -111,13 +132,17 @@ to_binary1(Data,_Address) when is_integer(Data) ->
{Data,<<>>}; {Data,<<>>};
to_binary1(Data, Address) when is_binary(Data) -> to_binary1(Data, Address) when is_binary(Data) ->
%% a string %% a string
Words = aeb_memory:binary_to_words(Data), Words = gmb_memory:binary_to_words(Data),
{Address,<<(size(Data)):256, << <<W:256>> || W <- Words>>/binary>>}; {Address,<<(size(Data)):256, << <<W:256>> || W <- Words>>/binary>>};
to_binary1({contract_bytearray, FateCode}, Address) when is_binary(FateCode) ->
Words = gmb_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(none, Address) -> to_binary1({variant, 0, []}, Address);
to_binary1({some, Value}, Address) -> to_binary1({variant, 1, [Value]}, Address); to_binary1({some, Value}, Address) -> to_binary1({variant, 1, [Value]}, Address);
to_binary1(word, Address) -> to_binary1({?TYPEREP_WORD_TAG}, Address); to_binary1(word, Address) -> to_binary1({?TYPEREP_WORD_TAG}, Address);
to_binary1(string, Address) -> to_binary1({?TYPEREP_STRING_TAG}, Address); to_binary1(string, Address) -> to_binary1({?TYPEREP_STRING_TAG}, Address);
to_binary1(typerep, Address) -> to_binary1({?TYPEREP_TYPEREP_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(function, Address) -> to_binary1({?TYPEREP_FUN_TAG}, Address);
to_binary1({list, T}, Address) -> to_binary1({?TYPEREP_LIST_TAG, T}, 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({option, T}, Address) -> to_binary1({variant, [[], [T]]}, Address);
@ -248,8 +273,14 @@ from_binary(Visited, typerep, Heap, V) ->
?TYPEREP_TUPLE_TAG -> {tuple, Arg({list, typerep})}; ?TYPEREP_TUPLE_TAG -> {tuple, Arg({list, typerep})};
?TYPEREP_VARIANT_TAG -> {variant, Arg({list, {list, typerep}})}; ?TYPEREP_VARIANT_TAG -> {variant, Arg({list, {list, typerep}})};
?TYPEREP_MAP_TAG -> {map, Arg(typerep), Arg1(typerep, 2)}; ?TYPEREP_MAP_TAG -> {map, Arg(typerep), Arg1(typerep, 2)};
?TYPEREP_FUN_TAG -> function ?TYPEREP_FUN_TAG -> function;
end. ?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) -> map_binary_to_value(KeyType, ValType, N, Bin, Ptr) ->
%% Avoid looping on bogus sizes %% Avoid looping on bogus sizes

View File

@ -1,12 +1,15 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc %%% @doc
%%% Memory speifics that compiler and VM need to agree upon %%% Memory speifics that compiler and VM need to agree upon
%%% @end %%% @end
%%% Updated : 22 Jan 2025
%%% Created : 19 Dec 2018 %%% Created : 19 Dec 2018
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_memory). -module(gmb_memory).
-vsn("3.4.1").
-export([binary_to_words/1]). -export([binary_to_words/1]).

View File

@ -1,12 +1,15 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2017, Aeternity Anstalt %%% @copyright (C) 2017, Aeternity Anstalt
%%% @doc %%% @doc
%%% Opcodes %%% Opcodes
%%% @end %%% @end
%%% Created : 2 Oct 2017 %%% Updated : 22 Jan 2025
%%% Created : 02 Oct 2017
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_opcodes). -module(gmb_opcodes).
-vsn("3.4.1").
-export([ dup/1 -export([ dup/1
, mnemonic/1 , mnemonic/1
@ -17,7 +20,7 @@
, swap/1 , swap/1
]). ]).
-include_lib("aebytecode/include/aeb_opcodes.hrl"). -include_lib("gmbytecode/include/gmb_opcodes.hrl").
%%==================================================================== %%====================================================================

View File

@ -1,18 +1,21 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc %%% @doc
%%% Handle interaction with the aeternity chain %%% Handle interaction with the gmternity chain
%%% through calls to AEternity primitive operations at address 0. %%% through calls to AEternity primitive operations at address 0.
%%% @end %%% @end
%%% Updated : 22 Jan 2025
%%% Created : 18 Dec 2018 %%% Created : 18 Dec 2018
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_primops). -module(gmb_primops).
-vsn("3.4.1").
-export([ is_local_primop_op/1 -export([ is_local_primop_op/1
, op_needs_type_check/1 , op_needs_type_check/1
]). ]).
-include("aeb_opcodes.hrl"). -include("gmb_opcodes.hrl").
is_local_primop_op(Op) when ?PRIM_CALL_IN_MAP_RANGE(Op) -> true; is_local_primop_op(Op) when ?PRIM_CALL_IN_MAP_RANGE(Op) -> true;
is_local_primop_op(Op) when ?PRIM_CALL_IN_CRYPTO_RANGE(Op) -> true; is_local_primop_op(Op) when ?PRIM_CALL_IN_CRYPTO_RANGE(Op) -> true;

View File

@ -1,12 +1,12 @@
{application, aebytecode, {application, gmbytecode,
[{description, "Bytecode definitions, serialization and deserialization for aeternity."}, [{description, "Bytecode definitions, serialization and deserialization for the Gajumaru."},
{vsn, "2.1.0"}, {vsn, "3.4.1"},
{registered, []}, {registered, []},
{applications, {applications,
[kernel, [kernel,
stdlib, stdlib,
eblake2, eblake2,
aeserialization, gmserialization,
getopt getopt
]}, ]},
{env,[]}, {env,[]},

View File

@ -1,4 +1,5 @@
-module(aefateasm). -module(gmfateasm).
-vsn("3.4.1").
-export([main/1]). -export([main/1]).
@ -9,7 +10,7 @@
, {outfile, $o, "out", string, "Output file (experimental)"} ]). , {outfile, $o, "out", string, "Output file (experimental)"} ]).
usage() -> usage() ->
getopt:usage(?OPT_SPEC, "aefateasm"). getopt:usage(?OPT_SPEC, "gmfateasm").
main(Args) -> main(Args) ->
case getopt:parse(?OPT_SPEC, Args) of case getopt:parse(?OPT_SPEC, Args) of
@ -43,8 +44,8 @@ assemble(File, Opts) ->
Verbose = proplists:get_value(verbose, Opts, false), Verbose = proplists:get_value(verbose, Opts, false),
case proplists:get_value(outfile, Opts, undefined) of case proplists:get_value(outfile, Opts, undefined) of
undefined -> undefined ->
Asm = aeb_fate_asm:read_file(File), Asm = gmb_fate_asm:read_file(File),
{Env, BC} = aeb_fate_asm:asm_to_bytecode(Asm, Opts), {Env, BC} = gmb_fate_asm:asm_to_bytecode(Asm, Opts),
case Verbose of case Verbose of
true -> true ->
io:format("Env: ~0p~n", [Env]); io:format("Env: ~0p~n", [Env]);
@ -52,6 +53,6 @@ assemble(File, Opts) ->
end, end,
io:format("Code: ~0p~n", [BC]); io:format("Code: ~0p~n", [BC]);
OutFile -> OutFile ->
aeb_fate_asm:assemble_file(File, OutFile, Opts) gmb_fate_asm:assemble_file(File, OutFile, Opts)
end. end.

View File

@ -1,88 +0,0 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc Basic tests for Fate serialization
%%%
%%% To run:
%%% TEST=aeb_serialize_test rebar3 eunit
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(aeb_serialize_test).
-include_lib("eunit/include/eunit.hrl").
serialize_integer_test() ->
<<0>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(0)),
<<2>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(1)),
<<126>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(63)),
<<111, 0>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(64)),
<<111,130,255,255>> = aeb_fate_encoding:serialize(aeb_fate_data:make_integer(65535 + 64)),
<<111,184,129,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>> =
aeb_fate_encoding:serialize(aeb_fate_data:make_integer(1 bsl 1024 + 64)).
serialize_deserialize_test_() ->
[{lists:flatten(io_lib:format("~p", [X])),
fun() ->
?assertEqual(X,
aeb_fate_encoding:deserialize(aeb_fate_encoding:serialize(X)))
end}
|| X <- sources()].
make_int_list(N) -> [aeb_fate_data:make_integer(I) || I <- lists:seq(1, N)].
sources() ->
FortyTwo = aeb_fate_data:make_integer(42),
Unit = aeb_fate_data:make_unit(),
True = aeb_fate_data:make_boolean(true),
False = aeb_fate_data:make_boolean(false),
Nil = aeb_fate_data:make_list([]),
EmptyString = aeb_fate_data:make_string(""),
EmptyMap = aeb_fate_data:make_map(#{}),
[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,
0,1,2,3,4,5,6,7,8,9,
0,1,2,3,4,5,6,7,8,9,
1,2>>),
aeb_fate_data:make_string(<<"Hello">>),
aeb_fate_data:make_string(
<<"0123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789"
"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)),
aeb_fate_data:make_integer(-65),
aeb_fate_data:make_integer(65),
aeb_fate_data:make_integer(-32432847932847928374983),
aeb_fate_data:make_bits(0),
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([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">>)})
].

View File

@ -1,249 +0,0 @@
;; CONTRACT all_instructions
;; Dont expect this contract to typecheck or run.
;; Just used to check assembler rountrip of all instructions.
FUNCTION foo () : {tuple, []}
RETURN
RETURNR a
CALL "foo"
CALL_R arg125 foo 0
CALL_T "foo"
CALL_TR arg245 foo 4711
CALL_GTR arg245 foo 0 100
CALL_GR arg245 foo 0 4711
JUMP 5514251025295783441695716053282666408426
JUMPIF arg196 0x12c651665
SWITCH_V2 a 63 33
SWITCH_V3 var4 0x1d61723dd 79 7
SWITCH_VN arg0 [1, 2, 3]
PUSH var80
DUPA
DUP a
POP a
STORE arg183 var225
INCA
INC a
DECA
DEC a
ADD a a a
SUB arg35 arg165 var74
MUL 44 35 "foo"
DIV 263838340369912686645632650718169038811 a a
MOD var113 arg80 arg207
POW a a a
LT a 78 var81
GT arg19 4729414120208894485838100532547810615352 var175
EQ 85 a arg164
ELT a arg226 a
EGT a 1 var250
NEQ a a a
AND var255 0x294a24f6 var189
OR (| [2,0] | 0 | ( (), (42) ) |) arg168 var107
NOT arg124 a
TUPLE var999 5019186157739257888756115213149493826410
ELEMENT arg148 var25 a
MAP_EMPTY a
MAP_LOOKUP a a a
MAP_LOOKUPD var112 arg35 a var112
MAP_UPDATE false a a a
MAP_DELETE arg180 a var1
MAP_MEMBER a { true => 4} 94
MAP_FROM_LIST () a
MAP_TO_LIST a { true => 4 }
MAP_SIZE a { true => 42 }
NIL arg91
IS_NIL a var6
CONS arg185 "foo" a
HD a var124
TL arg223 a
LENGTH var216 a
APPEND { 203961992615221001243597889146034217896 => 0x1f53a1843} 281217554184165828643225535776787296845 a
STR_JOIN a a 7144184027126178769820155907121270843348
INT_TO_STR var238 a
ADDR_TO_STR a arg216
STR_REVERSE a @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
STR_LENGTH a "foo"
INT_TO_ADDR arg127 var207
VARIANT a a 0x1f7b72200 a
VARIANT_TEST a arg217 a
VARIANT_ELEMENT a arg103 arg108
BITS_NONEA
BITS_NONE a
BITS_ALLA
BITS_ALL a
BITS_ALL_N a arg135
BITS_SET arg150 a { 0x1a715e2a6 => 3}
BITS_CLEAR arg98 a arg164
BITS_TEST a a (| [0,0,3] | 2 | (1, "foo", ()) |)
BITS_SUM a a
BITS_OR var20 var186 a
BITS_AND a 4 arg203
BITS_DIFF var200 arg247 var20
ADDRESS a
BALANCE a
ORIGIN arg216
CALLER a
GASPRICE arg119
BLOCKHASH a arg110
CALL_VALUE a
BENEFICIARY var163
TIMESTAMP a
GENERATION 242795038229506961431398379342231049652
MICROBLOCK arg43
DIFFICULTY var24
GASLIMIT arg220
GAS var35
LOG0 a
LOG1 arg86 arg208
LOG2 a a (| [0,1,3] | 2 | (1, "foo", ()) |)
LOG3 arg15 arg211 var139 arg44
LOG4 a a 9 a a
DEACTIVATE
SPEND @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv var136
ORACLE_REGISTER arg29 48 ((| [0,1,3] | 2 | (1, "foo", ()) |)) arg65 { <> => false} <>
ORACLE_QUERY
ORACLE_RESPOND
ORACLE_EXTEND
ORACLE_GET_ANSWER
ORACLE_GET_QUESTION
ORACLE_QUERY_FEE
AENS_RESOLVE
AENS_PRECLAIM
AENS_CLAIM
AENS_UPDATE
AENS_TRANSFER
AENS_REVOKE
ECVERIFY
SHA3 a
SHA256 a
BLAKE2B a
ABORT a
EXIT var120
NOP
RETURNR ()
BALANCE_OTHER a arg0
SETELEMENT a 2 (1, "two", 3) 2
AUTH_TX_HASH
CONTRACT_TO_ADDRESS @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv

View File

@ -62,13 +62,13 @@ id_local: JUMPDEST
JUMP JUMP
;; Test the code from the shell ;; Test the code from the shell
;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => list_to_binary(aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", [])), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0 }, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{})). ;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => list_to_binary(gmb_asm:file("apps/gmsophia/test/contracts/identity.aesm", [])), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0 }, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{})).
;; Test the code from the shell with tracing. ;; Test the code from the shell with tracing.
;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", []), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0 }, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{ trace => true})). ;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => gmb_asm:file("apps/gmsophia/test/contracts/identity.aesm", []), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0 }, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{ trace => true})).
;; Test the code from the shell with tracing. ;; Test the code from the shell with tracing.
;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => aeb_asm:file("apps/aesophia/test/contracts/identity.aesm", [pp_tokens, pp_opcodes, pp_patched_code, pp_hex_string]), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0}, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{ trace => true})). ;; aevm_eeevm:eval(aevm_eeevm_state:init(#{ exec => #{ code => gmb_asm:file("apps/gmsophia/test/contracts/identity.aesm", [pp_tokens, pp_opcodes, pp_patched_code, pp_hex_string]), address => 0, caller => 0, data => <<0:256, 42:256>>, gas => 1000000, gasPrice => 1, origin => 0, value => 0}, env => #{currentCoinbase => 0, currentDifficulty => 0, currentGasLimit => 10000, currentNumber => 0, currentTimestamp => 0}, pre => #{}}, #{ trace => true})).
;; aec_conductor:stop_mining(). ;; aec_conductor:stop_mining().

View File

@ -5,4 +5,4 @@ FUNCTION id(integer) -> integer
;; Test the code from the shell ;; Test the code from the shell
;; _build/default/rel/aessembler/bin/aessembler console ;; _build/default/rel/aessembler/bin/aessembler console
;; aeb_aefa:file("../../../../test/asm_code/identity.fate", []). ;; gmb_gmfa:file("../../../../test/asm_code/identity.fate", []).

View File

@ -68,9 +68,6 @@ FUNCTION tuple() : {tuple, [integer, boolean, string, {tuple, [integer, integer]
FUNCTION address() : address FUNCTION address() : address
RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv RETURNR @ak_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION oracle() : oracle
RETURNR @ok_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv
FUNCTION contract() : contract FUNCTION contract() : contract
RETURNR @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv RETURNR @ct_nv5B93FPzRHrGNmMdTDfGdd5xGZvep3MVSpJqzcQmMp59bBCv

12
test/asm_code/meta.fate Normal file
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

View File

@ -4,8 +4,8 @@ FUNCTION preclaim(address, {bytes, 32}) : {tuple, []}
AENS_PRECLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 AENS_PRECLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1
RETURNR {} RETURNR {}
FUNCTION claim(address, string, integer) : {tuple, []} FUNCTION claim(address, string, integer, integer) : {tuple, []}
AENS_CLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 AENS_CLAIM #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== arg0 arg1 arg2 arg3
RETURNR {} RETURNR {}
FUNCTION transfer(address, address, {bytes, 32}) : {tuple, []} FUNCTION transfer(address, address, {bytes, 32}) : {tuple, []}
@ -19,5 +19,3 @@ FUNCTION revoke(address, {bytes, 32}) : {tuple, []}
FUNCTION resolve(string, string) : {variant, [{tuple, []}, {tuple, [address]}]} FUNCTION resolve(string, string) : {variant, [{tuple, []}, {tuple, [address]}]}
AENS_RESOLVE a arg0 arg1 'address AENS_RESOLVE a arg0 arg1 'address
RETURN RETURN

View File

@ -1,32 +0,0 @@
;; 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

View File

@ -28,18 +28,14 @@ FUNCTION tailcall(integer) -> integer
INCA INCA
CALL_T "inc" CALL_T "inc"
FUNCTION remote_call(integer) : integer ;; FUNCTION remote_call(integer) : integer
PUSH arg0 ;; PUSH arg0
CALL_R remote.add_five 0 ;; CALL_R remote.add_five {tuple, [integer]} integer 0 ;; typereps don't parse
INCA ;; INCA
RETURN ;; RETURN
FUNCTION remote_tailcall(integer) : integer
PUSH arg0
CALL_TR remote add_five 0
;; Test the code from the shell ;; Test the code from the shell
;; _build/default/rel/aessembler/bin/aessembler console ;; _build/default/rel/aessembler/bin/aessembler console
;; aeb_aefa:file("../../../../test/asm_code/test.fate", []). ;; gmb_gmfa: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 = gmfa_asm:read_file("../../../../test/asm_code/test.fate"), {Env, BC} = gmfa_asm:asm_to_bytecode(Asm, []), gmfa_asm:bytecode_to_fate_code(BC, []).

View File

@ -1,12 +1,13 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc Basic tests for Fate data %%% @doc Basic tests for Fate data
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_data_test). -module(gmb_data_test).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
format_integer_test() -> format_integer_test() ->
"0" = aeb_fate_data:format(0). "0" = gmb_fate_data:format(0).

View File

@ -1,19 +1,20 @@
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt %%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc Basic tests for Fate serialization %%% @doc Basic tests for Fate serialization
%%% %%%
%%% To run: %%% To run:
%%% TEST=aeb_fate_asm_test rebar3 eunit %%% TEST=gmb_fate_asm_test rebar3 eunit
%%% %%%
%%% @end %%% @end
%%%------------------------------------------------------------------- %%%-------------------------------------------------------------------
-module(aeb_fate_asm_test). -module(gmb_fate_asm_test).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
asm_path() -> asm_path() ->
filename:join(code:lib_dir(aebytecode, test), "asm_code"). filename:join(code:lib_dir(gmbytecode, test), "asm_code").
file_path(File) -> file_path(File) ->
@ -21,11 +22,11 @@ file_path(File) ->
read_file(File) -> read_file(File) ->
FilePath = file_path(File), FilePath = file_path(File),
Asm = aeb_fate_asm:read_file(FilePath), Asm = gmb_fate_asm:read_file(FilePath),
Asm. Asm.
assemble(Asm) -> assemble(Asm) ->
aeb_fate_asm:asm_to_bytecode(Asm, []). gmb_fate_asm:asm_to_bytecode(Asm, []).
asm_disasm_idenity_test() -> asm_disasm_idenity_test() ->
check_roundtrip(identity). check_roundtrip(identity).
@ -48,19 +49,18 @@ sources() ->
, "mapofmap" , "mapofmap"
, "immediates" , "immediates"
, "names" , "names"
, "oracles" , "meta"
%% , "all_instructions"
]. ].
check_roundtrip(File) -> check_roundtrip(File) ->
AssemblerCode = read_file(File), AssemblerCode = read_file(File),
{_Env, ByteCode} = assemble(AssemblerCode), {_Env, ByteCode} = assemble(AssemblerCode),
FateCode = aeb_fate_code:deserialize(ByteCode), FateCode = gmb_fate_code:deserialize(ByteCode),
DissasmCode = aeb_fate_asm:to_asm(FateCode), DissasmCode = gmb_fate_asm:to_asm(FateCode),
{_Env2, ByteCode2} = assemble(DissasmCode), {_Env2, ByteCode2} = assemble(DissasmCode),
ByteCode3 = aeb_fate_code:serialize(FateCode), ByteCode3 = gmb_fate_code:serialize(FateCode),
Code1 = aeb_fate_asm:strip(ByteCode), Code1 = gmb_fate_asm:strip(ByteCode),
Code2 = aeb_fate_asm:strip(ByteCode2), Code2 = gmb_fate_asm:strip(ByteCode2),
Code3 = aeb_fate_asm:strip(ByteCode3), Code3 = gmb_fate_asm:strip(ByteCode3),
?assertEqual(Code1, Code2), ?assertEqual(Code1, Code2),
?assertEqual(Code1, Code3). ?assertEqual(Code1, Code3).

View File

@ -0,0 +1,98 @@
%%%-------------------------------------------------------------------
%%% @copyright (C) 2025, QPQ AG
%%% @copyright (C) 2018, Aeternity Anstalt
%%% @doc Basic tests for Fate serialization
%%%
%%% To run:
%%% TEST=gmb_serialize_test rebar3 eunit
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(gmb_serialize_test).
-include_lib("eunit/include/eunit.hrl").
serialize_integer_test() ->
<<0>> = gmb_fate_encoding:serialize(gmb_fate_data:make_integer(0)),
<<2>> = gmb_fate_encoding:serialize(gmb_fate_data:make_integer(1)),
<<126>> = gmb_fate_encoding:serialize(gmb_fate_data:make_integer(63)),
<<111, 0>> = gmb_fate_encoding:serialize(gmb_fate_data:make_integer(64)),
<<111,130,255,255>> = gmb_fate_encoding:serialize(gmb_fate_data:make_integer(65535 + 64)),
<<111,184,129,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>> =
gmb_fate_encoding:serialize(gmb_fate_data:make_integer(1 bsl 1024 + 64)).
serialize_deserialize_test_() ->
[{lists:flatten(io_lib:format("~p", [X])),
fun() ->
?assertEqual(X,
gmb_fate_encoding:deserialize(gmb_fate_encoding:serialize(X)))
end}
|| X <- sources()].
make_int_list(N) -> [gmb_fate_data:make_integer(I) || I <- lists:seq(1, N)].
sources() ->
FortyTwo = gmb_fate_data:make_integer(42),
Unit = gmb_fate_data:make_unit(),
True = gmb_fate_data:make_boolean(true),
False = gmb_fate_data:make_boolean(false),
Nil = gmb_fate_data:make_list([]),
EmptyString = gmb_fate_data:make_string(""),
EmptyMap = gmb_fate_data:make_map(#{}),
[gmb_fate_data:make_integer(0),
gmb_fate_data:make_integer(1),
True, False, Unit, Nil, EmptyString, EmptyMap,
gmb_fate_data:make_hash(<<1,2,3,4,5>>),
gmb_fate_data:make_signature(<<1,2,3,4,5>>),
gmb_fate_data:make_contract(<<1,2,3,4,5>>),
gmb_fate_data:make_channel(<<1,2,3,4,5>>),
gmb_fate_data:make_list([True]),
gmb_fate_data:make_address(
<<0,1,2,3,4,5,6,7,8,9,
0,1,2,3,4,5,6,7,8,9,
0,1,2,3,4,5,6,7,8,9,
1,2>>),
gmb_fate_data:make_string(<<"Hello">>),
gmb_fate_data:make_string(
<<"0123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789">>), %% Magic concat 80 char string.
gmb_fate_data:make_tuple({True, FortyTwo}),
gmb_fate_data:make_tuple(list_to_tuple(make_int_list(65))),
gmb_fate_data:make_tuple(list_to_tuple(make_int_list(16))),
gmb_fate_data:make_map(#{ gmb_fate_data:make_integer(1) => True, gmb_fate_data:make_integer(2) => False}),
gmb_fate_data:make_map(#{ gmb_fate_data:make_string(<<"foo">>) => gmb_fate_data:make_tuple({FortyTwo, True})}),
gmb_fate_data:make_list(make_int_list(3)),
gmb_fate_data:make_integer(-65),
gmb_fate_data:make_integer(65),
gmb_fate_data:make_integer(-32432847932847928374983),
gmb_fate_data:make_bits(0),
gmb_fate_data:make_bits(1),
gmb_fate_data:make_bits(-1),
gmb_fate_data:make_list(make_int_list(65)),
gmb_fate_data:make_variant([1,2,3], 0, {FortyTwo}),
gmb_fate_data:make_variant([2,0], 1, {}),
gmb_fate_data:make_list([gmb_fate_data:make_variant([0,0,0], 0, {})]),
gmb_fate_data:make_variant([0|| _<-lists:seq(1,255)], 254, {}),
gmb_fate_data:make_variant([0,1,2,3,4,5],
3, {gmb_fate_data:make_boolean(true),
gmb_fate_data:make_list(make_int_list(3)),
gmb_fate_data:make_string(<<"foo">>)}),
%% contract C =
%% type state = int
%% entrypoint init() = 2137
%% cb_+FFGA6Af6sHTrctrcNGwEa8MPei7iEHIjnxcsBzlA5IK0Yn11sCllP5E1kQfADcANwAaDoJvgggZAQM/jC8BEUTWRB8RaW5pdIIvAIU0LjMuMAD7u
gmb_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>>)
].

View File

@ -1,4 +1,4 @@
-module(aebytecode_SUITE). -module(gmbytecode_SUITE).
%% common_test exports %% common_test exports
-export([ all/0 ]). -export([ all/0 ]).
@ -12,8 +12,8 @@ all() ->
[ roundtrip_identy ]. [ roundtrip_identy ].
roundtrip_identy(_Cfg) -> roundtrip_identy(_Cfg) ->
CodeDir = code:lib_dir(aebytecode, test), CodeDir = code:lib_dir(gmbytecode, test),
FileName = filename:join(CodeDir, "asm_code/identity.aesm"), FileName = filename:join(CodeDir, "asm_code/identity.gmsm"),
Code = aeb_asm:file(FileName, []), Code = gmb_asm:file(FileName, []),
ct:log("Code ~p:~n~s~n", [FileName, aeb_disassemble:format(Code, fun io:format/2)]), ct:log("Code ~p:~n~s~n", [FileName, gmb_disassemble:format(Code, fun io:format/2)]),
ok. ok.

19
zomp.meta Normal file
View File

@ -0,0 +1,19 @@
{name,"Gajumaru Bytecode"}.
{type,lib}.
{modules,[]}.
{prefix,none}.
{desc,"A library and stand alone assembler for Gajumaru bytecode. This version supports AEVM bytecode and FATE bytecode."}.
{author,[]}.
{package_id,{"otpr","gmbytecode",{3,4,1}}}.
{deps,[{"otpr","gmserialization",{0,1,2}},
{"otpr","eblake2",{1,0,0}},
{"otpr","getopt",{1,0,2}}]}.
{key_name,none}.
{a_email,[]}.
{c_email,[]}.
{copyright,[]}.
{file_exts,[]}.
{license,skip}.
{repo_url,"https://git.qpq.swiss/QPQ-AG/gmbytecode"}.
{tags,["gajumaru","blockchain","fate","bytecode","crypto","gm"]}.
{ws_url,[]}.

20
zomp_prep Executable file
View File

@ -0,0 +1,20 @@
#! /bin/bash
# This is a small pre-packaging source generation and include correction script that should be
# run before packaging this project for use with ZX/Zomp.
rm -rf _build
rm -f src/gmb_fate_opcodes.erl src/gmb_fate_ops.erl include/gmb_fate_opcodes.hrl src/gmb_fate_asm_scan.xrl src/gmb_fate_pp.erl
make sources
cd src
for f in $(ls --ignore=gmb_fate_generate_ops.erl | grep erl)
do
echo "Updating includes in: $f"
sed -i 's/gmbytecode\/include\///g' "$f"
sed -i 's/\.\.\/include\///g' "$f"
sed -i 's/include_lib/include/g' "$f"
done
cd ..
rm -f ebin/*.beam
rm -f rebar*
rm -rf quickcheck