Skip to content

Commit

Permalink
Backported FunC optimizations from entrypoint branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Skydev0h committed Oct 13, 2023
1 parent 8041686 commit 886b9fe
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 61 deletions.
4 changes: 4 additions & 0 deletions Improvements.rst
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,12 @@ total level, with further commits do a `stable development` of contest test case
+----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+
| Another black magic optimization (drop auth_kind later) | 2818 | 3281 | 1906 | 8005 | 2245 | 21.9% | 64017 | 66370 | 37102 | 167489 |
+----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+
| Backported FunC optimizations from entrypoint branch | 2782 | 3373 | 1824 | 7979 | 2245 | 22.1% | 60011 | 64810 | 34138 | 158959 |
+----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+
| *Reminder and origin point: INITIAL* | 3235 | 4210 | 2760 | 10250 | 0 | 0.00% | 64038 | 71163 | 38866 | 174067 |
+----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+
| *Current state of the latest **entrypoint** branch commit* | 2790 | 3074 | 1735 | 7599 | 2651 | 25.9% | 59703 | 61917 | 33604 | 155224 |
+----------------------------------------------------------------+------+------+------+-------+------+-------+-------+-------+-------+--------+

N.B. Contest multiplier: 9905/10250 = 0.9663 (approximate) -> place multipliers ~ 0.3221138, 0.0966341, 0.048317

Expand Down
98 changes: 37 additions & 61 deletions contracts/wallet_v5.fc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

(slice) udict_get_or_return(cell dict, int key_len, int index) impure asm(index dict key_len) "DICTUGET IFNOTRET";

() ignore_int_params(int msg_value, cell full_msg) impure asm "NOP";

;; Extensible wallet contract v5

;; Compresses 8+256-bit address into 256-bit uint by cutting off one bit from sha256.
Expand All @@ -41,8 +43,7 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)
() set_actions_if_simple(slice cs) impure asm "DUP" "1 PLDU" "IFNOTJMP:<{ PLDREF c5 POP }>" "DROP";

;; Dispatches already authenticated request.
() dispatch_complex_request_inline(int stored_seqno, slice cs, slice immutable_tail, int stored_subwallet, int public_key) impure inline {
var extensions = null();
() dispatch_complex_request_inline(slice cs) impure inline {

;; Recurse into extended actions until we reach standard actions
while (cs~load_uint(1)) {
Expand All @@ -53,34 +54,32 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)
set_data(cs~load_ref());
}

var is_add_ext = (op == 0x1c40db9f);
var is_del_ext = (op == 0x5eaef4a4);
;; Add/remove extensions
if ((op == 0x1c40db9f) | (op == 0x5eaef4a4)) {
if ((is_add_ext) | (is_del_ext)) {
(int wc, int hash) = parse_std_addr(cs~load_msg_addr());
int packed_addr = pack_address(wc, hash);

ifnot (immutable_tail.null?()) {
;; immutable_tail = immutable_tail.skip_bits(80 + 256);
extensions = immutable_tail.skip_bits(80 + 256).preload_dict();
;; ds.end_parse() NO MORE guarantees that there is no more data in immutable_tail
;; immutable_tail~skip_bits(immutable_tail.slice_bits()); ;; more resiliency in case of extraneous data
immutable_tail = null();
}
var ds = get_data().begin_parse();
var data_bits = ds~load_bits(32 + 80 + 256);
var extensions = ds.preload_dict();

;; Add extension
if (op == 0x1c40db9f) {
if (is_add_ext) {
(extensions, int success?) = extensions.udict_add_builder?(256, packed_addr, begin_cell().store_int(wc,8));
throw_unless(39, success?);
}
;; Remove extension
if (op == 0x5eaef4a4) {
(extensions, int success?) = extensions.udict_delete?(256, packed_addr);
throw_unless(40, success?);
}
} else
;; Remove extension
;; if (op == 0x5eaef4a4)
;; It can be ONLY 0x1c40db9f OR 0x5eaef4a4 here. No need for second check.
{
(extensions, int success?) = extensions.udict_delete?(256, packed_addr);
throw_unless(40, success?);
}

set_data(begin_cell()
.store_uint(stored_seqno, 32)
.store_uint(stored_subwallet, 80)
.store_uint(public_key, 256)
.store_slice(data_bits)
.store_dict(extensions)
.end_cell());
}
Expand All @@ -95,17 +94,10 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)
return ();
}

() dispatch_complex_request_iref(int stored_seqno, slice cs, slice immutable_tail, int stored_subwallet, int public_key) impure inline_ref {
() dispatch_complex_request_iref(slice cs) impure inline_ref {
;; this function is explicitly included as an inline reference - not completely inlined
;; completely inlining it causes undesirable code split and noticeable gas increase in some paths
dispatch_complex_request_inline(stored_seqno, cs, immutable_tail, stored_subwallet, public_key);
}

() dispatch_request(int public_key, slice cs, slice immutable_tail, int stored_subwallet, int stored_seqno) impure inline {
set_actions_if_simple(cs); ;; <- ifnot (cs.preload_uint(1)) { set_actions(cs.preload_ref()); return (); }
;; <<<<<<<<<<---------- CONTEST TEST CUTOFF POINT ---------->>>>>>>>>>
;; inline_ref required because otherwise it will produce undesirable JMPREF
dispatch_complex_request_iref(stored_seqno, cs, immutable_tail, stored_subwallet, public_key);
dispatch_complex_request_inline(cs);
}

() dispatch_extension_request(slice cs, var dummy1, var dummy2) impure inline {
Expand All @@ -114,29 +106,27 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)
;;
dummy1~impure_touch(); ;; DROP merged to 2DROP!
dummy2~impure_touch(); ;; Thats 3 BLKDROP now!
var ds = get_data().begin_parse();
var stored_seqno = ds~load_uint(32);
var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions
var stored_subwallet = ds~load_uint(80);
var public_key = ds.preload_uint(256);
;; Note: If after modifications code becomes so large it undesirably splits to cells,
;; replace with dispatch_complex_request_iref. This won't affect test cases but will affect GGC slightly.
dispatch_complex_request_inline(stored_seqno, cs, immutable_tail, stored_subwallet, public_key);
dispatch_complex_request_iref(cs);
}

;; Verifies signed request, prevents replays and proceeds with `dispatch_request`.
() process_signed_request(slice body, int stored_seqno, slice immutable_tail, int stored_subwallet, int public_key) impure inline {
() process_signed_request(slice body) impure inline {
;; The precise order of operations here is VERY important. Any other order results in unneccessary stack shuffles.

var signature = body~load_bits(512);
throw_unless(35, check_signature(slice_hash(body), signature, public_key));

var cs = body;
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(80), cs~load_uint(32), cs~load_uint(32));

var ds = get_data().begin_parse();
var stored_seqno = ds~load_uint(32);
var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions
var stored_subwallet = ds~load_uint(80);
var public_key = ds.preload_uint(256);

;; Only such checking order results in least amount of gas
throw_unless(34, subwallet_id == stored_subwallet);
throw_unless(35, check_signature(slice_hash(body), signature, public_key));
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, subwallet_id == stored_subwallet);
throw_if(36, valid_until <= now());

accept_message();
Expand All @@ -150,24 +140,17 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)

commit();

dispatch_request(public_key, cs, immutable_tail, stored_subwallet, stored_seqno);
}
set_actions_if_simple(cs); ;; <- ifnot (cs.preload_uint(1)) { set_actions(cs.preload_ref()); return (); }
;; <<<<<<<<<<---------- CONTEST TEST CUTOFF POINT ---------->>>>>>>>>>

(slice, int) entry_point(slice body, int meth_id) impure {
return (body, meth_id);
;; inline_ref required because otherwise it will produce undesirable JMPREF
dispatch_complex_request_iref(cs);
}

() recv_external(slice body) impure {
int auth_kind = body~load_uint(32);

return_if_not_equal(auth_kind, 0x7369676E); ;; "sign"

var ds = get_data().begin_parse();
var stored_seqno = ds~load_uint(32);
var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions
var stored_subwallet = ds~load_uint(80);
var public_key = ds.preload_uint(256);
process_signed_request(body, stored_seqno, immutable_tail, stored_subwallet, public_key);
process_signed_request(body);
return();
}

Expand Down Expand Up @@ -206,19 +189,12 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)

return_if_not_equal(auth_kind, 0x7369676E); ;; "sign" authenticated by signature

var ds = get_data().begin_parse();
var stored_seqno = ds~load_uint(32);
var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions
var stored_subwallet = ds~load_uint(80);
var public_key = ds.preload_uint(256);

;; Process the rest of the slice just like the signed request.
process_signed_request(body, stored_seqno, immutable_tail, stored_subwallet, public_key);
process_signed_request(body);
return (); ;; Explicit returns escape function faster and const less gas (suddenly!)

}


;; Get methods

int seqno() method_id {
Expand Down

0 comments on commit 886b9fe

Please sign in to comment.