diff --git a/Improvements.rst b/Improvements.rst index f900d34..077ff1e 100644 --- a/Improvements.rst +++ b/Improvements.rst @@ -109,11 +109,35 @@ total level, with further commits do a `stable development` of contest test case +----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+ | Use SDBEGINSQ to check internal message prefixes | 2710 | 3283 | 1736 | 7729 | 2521 | 24.6% | 59237 | 64090 | 33578 | 156905 | +----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+ -| *Reminder and origin point: INITIAL* | 3235 | 4210 | 2760 | 10250 | 0 | 0.00% | 64038 | 71163 | 38866 | 174067 | +| Backport some optimizations from EP and coalesce code | 2699 | 3165 | 1828 | 7692 | 2558 | 25.0% | 59108 | 63031 | 34141 | 156280 | +----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+ -| *Current state of the latest entrypoint branch commit* | 2718 | 2984 | 1647 | 7349 | 2901 | 28.3% | 59045 | 61210 | 33044 | 153299 | +| *Reminder and origin point: INITIAL* | 3235 | 4210 | 2760 | 10250 | 0 | 0.00% | 64038 | 71163 | 38866 | 174067 | +----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+ +*It seems that backporting optimization wiggles around values here and there.* To get the maximum possible gas savings please consider taking +a look at `entrypoint` ("radical") branch. Since optimizations are carefully made there, they decrease used gas by all cases without compromises. + +As an example, here is a comparison of used gas in main ("conservative") and entrypoint ("radical") branches: + ++-----------------+----------+----------------+ +| Test case | main gas | entrypoint gas | ++=================+==========+================+ +| External | 2699 | **2707** | ++-----------------+----------+----------------+ +| Internal | 3165 | **2963** | ++-----------------+----------+----------------+ +| Extension | 1828 | **1626** | ++-----------------+----------+----------------+ +| External GGC | 59108 | **58893** | ++-----------------+----------+----------------+ +| Internal GGC | 63031 | **61011** | ++-----------------+----------+----------------+ +| Extension GGC | 34141 | **32929** | ++-----------------+----------+----------------+ + +The external test case uses a tiny miniscule more gas due to if ordering, making it way around messes up cell slicing completely. +Nevertheless, external global counter is still less, therefore the overall result is not that bad. + N.B. Contest multiplier: 9905/10250 = 0.9663 (approximate) -> place multipliers ~ 0.3221138, 0.0966341, 0.048317 Details and rationale diff --git a/contracts/imports/entrypoint.fc b/contracts/imports/entrypoint.fc new file mode 100644 index 0000000..f41b014 --- /dev/null +++ b/contracts/imports/entrypoint.fc @@ -0,0 +1,33 @@ +;; Basic entry point for received messages (both external and internal). +;; recv_internal and recv_external are NOT implicitly used anymore! +;; use msg_value or full_msg ONLY if you are sure these are supplied - if is_external = 0! ~ a very thin ice here! + +;; N.B. If compiled by an compiler that does not support entry_point_recv this method will be ignored and the +;; contract would compile, however the gas usage will be higher due to recv_internal/external not inlined in root + +;; Not supported in the main (conservative) branch. +;; Please use the entrypoint branch for the best gas optimizations and usage! +{- + Direct gas comparison on a specific commit between main and entrypoint: + main entrypoint + External test case: 2699 2707 (a bit more due to if ordering, making it way around messes up cell slicing completely) + Internal test case: 3165 2963 + Extension test case: 1828 1626 + + main entrypoint + External global ctr: 59108 58893 (not all is bad with external messages overall) + Internal global ctr: 63031 61011 + Extension global ctr: 34141 32929 +-} + + +{- +() entry_point_recv(int msg_value, cell full_msg, slice body, int is_external) impure { + ifnot (is_external) { + recv_internal(msg_value, full_msg, body); + return(); + } + recv_external(body); + ignore_int_params(msg_value, full_msg); ;; does nothing but prevents dropping them which will cause stack underflow +} +-} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 8cbd4fc..843c8fc 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -16,6 +16,10 @@ } -} () return_if_bounce(int flags) impure asm "1 PUSHINT" "AND" "IFRET"; +;; () extravagant_return_if_bounce(int flags) impure asm "c0 PUSH" "0 IFBITJMP" "DROP"; + +;; SDCNTTRAIL1 will count trailing ones in slice. If bounced flag (last bit) is set it will be non-zero, else zero. +() slicy_return_if_bounce(slice s_flags) impure asm "SDCNTTRAIL1" "IFRET"; () return_if_not_equal(int a, int b) impure asm "EQUAL" "IFNOTRET"; () return_if_not(int a) impure asm "IFNOTRET"; @@ -24,6 +28,15 @@ () ignore_int_params(int msg_value, cell full_msg) impure asm "NOP"; +(slice) enforce_and_remove_sign_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; +(slice) enforce_and_remove_extn_prefix(slice body) impure asm "x{6578746E} SDBEGINS"; + +(slice, int) check_and_remove_sign_prefix(slice body) impure asm "x{7369676E} SDBEGINSQ"; +(slice, int) check_and_remove_extn_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; + +(slice) check_and_remove_sign_prefix_or_ret(slice body) impure asm "x{7369676E} SDBEGINSQ" "IFNOTRET"; +(slice) check_and_remove_extn_prefix_or_ret(slice body) impure asm "x{6578746E} SDBEGINSQ" "IFNOTRET"; + ;; Extensible wallet contract v5 ;; Compresses 8+256-bit address into 256-bit uint by cutting off one bit from sha256. @@ -40,7 +53,7 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1) return (); } -} -() set_actions_if_simple(slice cs) impure asm "DUP" "1 PLDU" "IFNOTJMP:<{ PLDREF c5 POP }>" "DROP"; +() set_actions_if_simple(slice cs) impure asm "x{4_} SDBEGINSQ" "IFJMP:<{ PLDREF c5 POP }>" "DROP"; ;; Dispatches already authenticated request. () dispatch_complex_request_inline(slice cs) impure inline { @@ -146,9 +159,7 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1) dispatch_complex_request_iref(cs); } -(slice) enforce_and_remove_sign_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; - -() recv_external(slice body) impure { +() recv_external(slice body) impure inline { ;; int auth_kind = body~load_uint(32); ;; return_if_not_equal(auth_kind, 0x7369676E); ;; "sign" body = enforce_and_remove_sign_prefix(body); @@ -156,18 +167,17 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1) return(); } -(slice, int) check_and_remove_sign_prefix(slice body) impure asm "x{7369676E} SDBEGINSQ"; -(slice, int) check_and_remove_extn_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; - -(slice) check_and_remove_sign_prefix_or_ret(slice body) impure asm "x{7369676E} SDBEGINSQ" "IFNOTRET"; - -() recv_internal(int msg_value, cell full_msg, slice body) impure { +() recv_internal(int msg_value, cell full_msg, slice body) impure inline { ;; Any attempt to postpone msg_value deletion will result in s2 POP -> SWAP change. No use at all. var full_msg_slice = full_msg.begin_parse(); - var flags = full_msg_slice~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... + ;; var flags = full_msg_slice~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... ;; return_if_bounce(3); <- as a test (since there are no bounce tests) - this works (breaks tests)! - return_if_bounce(flags); ;; <- if (flags & 1) { return (); } + ;; return_if_bounce(flags); ;; <- if (flags & 1) { return (); } + var s_flags = full_msg_slice~load_bits(4); + slicy_return_if_bounce(s_flags); + + ;; slicy_return_if_bounce(begin_cell().store_uint(3, 4).end_cell().begin_parse()); ;; TEST!!! ;; (int auth_kind, body) = body.load_uint32_or_ret(); ;; loads uint32 from body or returns if not enough bits right away @@ -205,6 +215,12 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1) } +;; N.B. If compiled by an compiler that does not support entry_point_recv this method will be ignored and the +;; contract would compile, however the gas usage will be higher due to recv_internal/external not inlined in root + +;; Moved to separate file to make it easier to backport stuff from entrypoint branch to main branch +#include "imports/entrypoint.fc"; + ;; Get methods int seqno() method_id {