From 92471dda68cb115c008d9d9dc788e4de48ddd8ca Mon Sep 17 00:00:00 2001 From: Skydev0h Date: Fri, 14 Jun 2024 18:30:35 +0300 Subject: [PATCH 01/67] Some improvements and changes to the contract 1) Internal messages can use any send_mode 2) Action list verification accounts for exotic cells 4) Removed unneccessary (and maybe even harming) shortcuts 5) Deduplicated process_signed_xxx code into single fun --- contracts/wallet_v5.fc | 124 +++++++++++++---------------------------- 1 file changed, 38 insertions(+), 86 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index e8497e4..5f771c0 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -17,6 +17,8 @@ const int size::flags = 4; (slice) udict_get_or_return(cell dict, int key_len, int index) impure asm(index dict key_len) "DICTUGET" "IFNOTRET"; +(slice, int) begin_parse_xc(cell c) asm "XCTOS"; + (slice) enforce_and_remove_sign_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; (slice, int) check_and_remove_extn_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; (slice, int) check_and_remove_sint_prefix(slice body) impure asm "x{73696E74} SDBEGINSQ"; @@ -48,18 +50,18 @@ int count_trailing_ones(slice cs) asm "SDCNTTRAIL1"; slice get_last_bits(slice s, int n) asm "SDCUTLAST"; slice remove_last_bits(slice s, int n) asm "SDSKIPLAST"; -cell verify_actions(cell c5) inline { +cell verify_actions(cell c5, int is_external) inline { ;; Comment out code starting from here to disable checks (unsafe version) ;; {- - slice c5s = c5.begin_parse(); + (slice c5s, _) = c5.begin_parse_xc(); return_if(c5s.slice_empty?()); do { ;; only send_msg is allowed, set_code or reserve_currency are not c5s = c5s.enforce_and_remove_action_send_msg_prefix(); ;; enforce that send_mode has 2 bit set ;; for that load 7 bits and make sure that they end with 1 - throw_if(37, count_trailing_zeroes(c5s.preload_bits(7))); - c5s = c5s.preload_ref().begin_parse(); + throw_if(37, is_external & count_trailing_zeroes(c5s.preload_bits(7))); + (c5s, _) = c5s.preload_ref().begin_parse_xc(); } until (c5s.slice_empty?()); ;; -} return c5; @@ -68,7 +70,7 @@ cell verify_actions(cell c5) inline { ;; Dispatches already authenticated request. ;; 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(slice cs) impure inline_ref { +() dispatch_complex_request(slice cs, int is_external) impure inline_ref { ;; Recurse into extended actions until we reach standard actions while (cs~load_int(1)) { @@ -144,81 +146,18 @@ cell verify_actions(cell c5) inline { } ;; At this point we are at `action_list_basic$0 {n:#} actions:^(OutList n) = ActionList n 0;` ;; Simply set the C5 register with all pre-computed actions after verification: - set_actions(cs.preload_ref().verify_actions()); + set_actions(cs.preload_ref().verify_actions(is_external)); return (); } ;; ------------------------------------------------------------------------------------------------ ;; Verifies signed request, prevents replays and proceeds with `dispatch_request`. -() process_signed_request_from_external_message(slice full_body) impure inline { - ;; The precise order of operations here is VERY important. Any other order results in unneccessary stack shuffles. - slice signature = full_body.get_last_bits(512); - slice signed = full_body.remove_last_bits(512); - - var cs = signed.skip_bits(32); - var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(size::subwallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::msg_seqno)); - - var ds = get_data().begin_parse(); - var stored_seqno = ds~load_int(size::stored_seqno); - var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions - var stored_subwallet = ds~load_uint(size::stored_subwallet); - var public_key = ds.preload_uint(size::public_key); - - ;; TODO: Consider moving signed into separate ref, slice_hash consumes 500 gas just like cell creation! - ;; Only such checking order results in least amount of gas - throw_unless(35, check_signature(slice_hash(signed), signature, public_key)); - ;; If public key is disabled, stored_seqno is strictly less than zero: stored_seqno < 0 - ;; However, msg_seqno is uint, therefore it can be only greater or equal to zero: msg_seqno >= 0 - ;; Thus, if public key is disabled, these two domains NEVER intersect, and additional check is not needed - throw_unless(33, msg_seqno == stored_seqno); - throw_unless(34, subwallet_id == stored_subwallet); - throw_if(36, valid_until <= now()); - - accept_message(); - - ;; Store and commit the seqno increment to prevent replays even if the subsequent requests fail. - stored_seqno = stored_seqno + 1; - set_data(begin_cell() - .store_int(stored_seqno, size::stored_seqno) - .store_slice(immutable_tail) ;; stored_subwallet ~ public_key ~ extensions - .end_cell()); - - commit(); - - if (count_leading_zeroes(cs)) { ;; starts with bit 0 - return set_actions(cs.preload_ref().verify_actions()); +() process_signed_request(slice full_body, int is_external) impure inline { + ifnot (is_external) { + ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) + return_if(full_body.slice_bits() < 32 + size::subwallet_id + size::valid_until + size::msg_seqno + 1 + 512); } - ;; <<<<<<<<<<---------- Simple primary cases gas evaluation ends here ---------->>>>>>>>>> - - ;; inline_ref required because otherwise it will produce undesirable JMPREF - dispatch_complex_request(cs); -} - -() recv_external(slice body) impure inline { - slice full_body = body; - ;; 0x7369676E ("sign") external message authenticated by signature - body = enforce_and_remove_sign_prefix(body); - process_signed_request_from_external_message(full_body); - return(); -} - -;; ------------------------------------------------------------------------------------------------ - -() dispatch_extension_request(slice cs, var dummy1) impure inline { - if (count_leading_zeroes(cs)) { ;; starts with bit 0 - return set_actions(cs.preload_ref().verify_actions()); - } - ;; <<<<<<<<<<---------- Simple primary cases gas evaluation ends here ---------->>>>>>>>>> - ;; - dummy1~impure_touch(); ;; DROP merged to 2DROP! - dispatch_complex_request(cs); -} - -;; Same logic as above function but with return_* instead of throw_* and additional checks to prevent bounces -() process_signed_request_from_internal_message(slice full_body) impure inline { - ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(full_body.slice_bits() < 32 + size::subwallet_id + size::valid_until + size::msg_seqno + 1 + 512); ;; The precise order of operations here is VERY important. Any other order results in unneccessary stack shuffles. slice signature = full_body.get_last_bits(512); @@ -234,16 +173,22 @@ cell verify_actions(cell c5) inline { var public_key = ds.preload_uint(size::public_key); ;; Note on bouncing/nonbouncing behaviour: - ;; In principle, the wallet should not bounce incoming messages as to avoid + ;; In principle, the wallet should not bounce incoming messages as to avoid ;; returning deposits back to the sender due to opcode misinterpretation. ;; However, specifically for "gasless" transactions (signed messages relayed by a 3rd party), ;; there is a risk for the relaying party to be abused: their coins should be bounced back in case of a race condition or delays. ;; We resolve this dilemma by silently failing at the signature check (therefore ordinary deposits with arbitrary opcodes never bounce), ;; but failing with exception (therefore bouncing) after the signature check. - + ;; TODO: Consider moving signed into separate ref, slice_hash consumes 500 gas just like cell creation! - ;; Only such checking order results in least amount of gas - return_unless(check_signature(slice_hash(signed), signature, public_key)); + int signature_is_valid = check_signature(slice_hash(signed), signature, public_key); + if (is_external) { + throw_unless(35, signature_is_valid); + } else { + ifnot (signature_is_valid) { + return(); + } + } ;; If public key is disabled, stored_seqno is strictly less than zero: stored_seqno < 0 ;; However, msg_seqno is uint, therefore it can be only greater or equal to zero: msg_seqno >= 0 ;; Thus, if public key is disabled, these two domains NEVER intersect, and additional check is not needed @@ -251,6 +196,10 @@ cell verify_actions(cell c5) inline { throw_unless(34, subwallet_id == stored_subwallet); throw_if(36, valid_until <= now()); + if (is_external) { + accept_message(); + } + ;; Store and commit the seqno increment to prevent replays even if the subsequent requests fail. stored_seqno = stored_seqno + 1; set_data(begin_cell() @@ -260,15 +209,19 @@ cell verify_actions(cell c5) inline { commit(); - if (count_leading_zeroes(cs)) { ;; starts with bit 0 - return set_actions(cs.preload_ref().verify_actions()); - } - ;; <<<<<<<<<<---------- Simple primary cases gas evaluation ends here ---------->>>>>>>>>> + dispatch_complex_request(cs, is_external); +} - ;; inline_ref required because otherwise it will produce undesirable JMPREF - dispatch_complex_request(cs); +() recv_external(slice body) impure inline { + slice full_body = body; + ;; 0x7369676E ("sign") external message authenticated by signature + body = enforce_and_remove_sign_prefix(body); + process_signed_request(full_body, true); + return(); } +;; ------------------------------------------------------------------------------------------------ + () recv_internal(cell full_msg, slice body) impure inline { ;; return right away if there are no references @@ -305,8 +258,7 @@ cell verify_actions(cell c5) inline { ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). var wc = extensions.udict_get_or_return(256, packed_sender_addr); ;; kindof ifnot (success?) { return(); } - ;; auth_kind and wc are passed into dispatch_extension_request and later are dropped in batch with 3 BLKDROP - dispatch_extension_request(body, wc); ;; Special route for external address authenticated request + dispatch_complex_request(body, false); return (); } @@ -316,7 +268,7 @@ cell verify_actions(cell c5) inline { return_unless(is_sint?); ;; Process the rest of the slice just like the signed request. - process_signed_request_from_internal_message(full_body); + process_signed_request(full_body, false); return (); ;; Explicit returns escape function faster and const less gas (suddenly!) } From 7d1d14494925ce629bc0d8bbe17783decf476c7a Mon Sep 17 00:00:00 2001 From: Skydev0h Date: Fri, 14 Jun 2024 18:49:36 +0300 Subject: [PATCH 02/67] Plugins now bound to same workchain as the wallet, no packing (6) --- contracts/wallet_v5.fc | 20 ++++++++++---------- tests/utils.ts | 3 +-- tests/wallet-v5-external.spec.ts | 2 +- tests/wallet-v5-get.spec.ts | 4 ++-- tests/wallet-v5-internal.spec.ts | 2 +- wrappers/wallet-v5.ts | 2 +- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 5f771c0..79f3fe6 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -32,11 +32,6 @@ const int size::flags = 4; ;; Extensible wallet contract v5 -;; Compresses 8+256-bit address into 256-bit uint by cutting off one bit from sha256. -;; This allows us to save on wrapping the address in a cell and make plugin requests cheaper. -;; This method also unpacks address hash if you pass packed hash with the original wc. -int pack_address((int, int) address) impure asm "SWAP" "INC" "XOR"; ;; hash ^ (wc+1) - ;; Stores pre-computed list of actions (mostly `action_send_msg`) in the actions register. () set_actions(cell action_list) impure asm "c5 POP"; @@ -79,7 +74,9 @@ cell verify_actions(cell c5, int is_external) inline { ;; Add/remove extensions 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) ); + (int my_wc, _) = parse_std_addr(my_address()); + + throw_unless(45, my_wc == wc); var ds = get_data().begin_parse(); var data_bits = ds~load_bits(size::stored_seqno + size::stored_subwallet + size::public_key); @@ -88,13 +85,13 @@ cell verify_actions(cell c5, int is_external) inline { ;; Add extension if (is_add_ext?) { - (extensions, int success?) = extensions.udict_add_builder?(256, packed_addr, begin_cell().store_int(wc,8)); + (extensions, int success?) = extensions.udict_add_builder?(256, hash, begin_cell().store_int(wc,8)); throw_unless(39, 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); + (extensions, int success?) = extensions.udict_delete?(256, hash); throw_unless(40, success?); throw_if(44, null?(extensions) & (stored_seqno < 0)); } @@ -248,7 +245,10 @@ cell verify_actions(cell c5, int is_external) inline { if (is_extn?) { ;; "extn" authenticated by extension ;; Authenticate extension by its address. - int packed_sender_addr = pack_address(parse_std_addr(full_msg_slice~load_msg_addr())); ;; no PLDMSGADDR exists + (int sender_wc, int sender_hash) = parse_std_addr(full_msg_slice~load_msg_addr()); ;; no PLDMSGADDR exists + (int my_wc, _) = parse_std_addr(my_address()); + + return_unless(my_wc == sender_wc); var ds = get_data().begin_parse(); ;; It is not required to read this data here, maybe ext is doing simple transfer where those are not needed @@ -256,7 +256,7 @@ cell verify_actions(cell c5, int is_external) inline { ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). - var wc = extensions.udict_get_or_return(256, packed_sender_addr); ;; kindof ifnot (success?) { return(); } + extensions.udict_get_or_return(256, sender_hash); ;; kindof ifnot (success?) { return(); } dispatch_complex_request(body, false); return (); diff --git a/tests/utils.ts b/tests/utils.ts index 8257d4b..d309bf9 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -5,8 +5,7 @@ export function bufferToBigInt(buffer: Buffer): bigint { } export function packAddress(address: Address) { - const wcPlus = address.workChain + 1; - return bufferToBigInt(address.hash) ^ BigInt(wcPlus); + return bufferToBigInt(address.hash); } export function validUntil(ttlMs = 1000 * 60 * 3) { diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index 3d17d1d..69e26c2 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -293,7 +293,7 @@ describe('Wallet V5 sign auth external', () => { }); it('Add two extensions and do a transfer', async () => { - const testExtension1 = Address.parse('Ef82pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX28X'); + const testExtension1 = Address.parse('EQA2pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX5Bf'); const testExtension2 = Address.parse('EQCgYDKqfTh7zVj9BQwOIPs4SuOhM7wnIjb6bdtM2AJf_Z9G'); const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); diff --git a/tests/wallet-v5-get.spec.ts b/tests/wallet-v5-get.spec.ts index c3fac3a..6ec99d2 100644 --- a/tests/wallet-v5-get.spec.ts +++ b/tests/wallet-v5-get.spec.ts @@ -107,7 +107,7 @@ describe('Wallet V5 get methods', () => { it('Get extensions dict', async () => { const plugin1 = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - const plugin2 = Address.parse('Ef82pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX28X'); + const plugin2 = Address.parse('EQA2pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX5Bf'); const extensions: Dictionary = Dictionary.empty( Dictionary.Keys.BigUint(256), @@ -127,7 +127,7 @@ describe('Wallet V5 get methods', () => { it('Get extensions array', async () => { const plugin1 = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - const plugin2 = Address.parse('Ef82pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX28X'); + const plugin2 = Address.parse('EQA2pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX5Bf'); const extensions: Dictionary = Dictionary.empty( Dictionary.Keys.BigUint(256), diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index 61f79f0..6459520 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -266,7 +266,7 @@ describe('Wallet V5 sign auth internal', () => { }); it('Add two extensions and do a transfer', async () => { - const testExtension1 = Address.parse('Ef82pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX28X'); + const testExtension1 = Address.parse('EQA2pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX5Bf'); const testExtension2 = Address.parse('EQCgYDKqfTh7zVj9BQwOIPs4SuOhM7wnIjb6bdtM2AJf_Z9G'); const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index fe0c708..89c991b 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -232,7 +232,7 @@ export class WalletV5 implements Contract { return dict.keys().map(key => { const wc = dict.get(key)!; - const addressHex = key ^ (wc + 1n); + const addressHex = key; return Address.parseRaw(`${wc}:${addressHex.toString(16)}`); }); } From 1f9b0a3a19edfab4ae4d6bfd23eb0fea1e5fc181 Mon Sep 17 00:00:00 2001 From: Skydev0h Date: Fri, 14 Jun 2024 19:15:57 +0300 Subject: [PATCH 03/67] Moved signature_auth_disable to separate variable from seq_no sign (3) --- Specification.md | 2 +- contracts/wallet_v5.fc | 57 +++++++++++++++--------------- scripts/deployWalletV5.ts | 1 + tests/wallet-v5-extensions.spec.ts | 11 ++---- tests/wallet-v5-external.spec.ts | 13 ++++--- tests/wallet-v5-get.spec.ts | 1 + tests/wallet-v5-internal.spec.ts | 15 ++++---- types.tlb | 2 +- wrappers/wallet-v5.ts | 4 ++- 9 files changed, 52 insertions(+), 54 deletions(-) diff --git a/Specification.md b/Specification.md index ed44015..fdfef5d 100644 --- a/Specification.md +++ b/Specification.md @@ -180,7 +180,7 @@ actions$_ {m:#} {n:#} actions:(ActionList n m) = InnerRequest; Contract state: ```tl-b wallet_id$_ global_id:# wc:int8 version:(## 8) subwallet_number:# = WalletID; -contract_state$_ seqno:int33 wallet_id:WalletID public_key:(## 256) extensions_dict:(HashmapE 256 int8) = ContractState; +contract_state$_ signature_auth_disabled:(## 1) seqno:# wallet_id:WalletID public_key:(## 256) extensions_dict:(HashmapE 256 int8) = ContractState; ``` ## Source code diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 79f3fe6..e18faf8 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -2,7 +2,8 @@ #include "imports/stdlib.fc"; -const int size::stored_seqno = 33; +const int size::signature_auth_disabled = 1; +const int size::stored_seqno = 32; const int size::stored_subwallet = 80; const int size::public_key = 256; @@ -79,8 +80,8 @@ cell verify_actions(cell c5, int is_external) inline { throw_unless(45, my_wc == wc); var ds = get_data().begin_parse(); - var data_bits = ds~load_bits(size::stored_seqno + size::stored_subwallet + size::public_key); - var stored_seqno = data_bits.preload_int(size::stored_seqno); + var data_bits = ds~load_bits(size::signature_auth_disabled + size::stored_seqno + size::stored_subwallet + size::public_key); + var signature_auth_disabled = data_bits.preload_int(size::signature_auth_disabled); var extensions = ds.preload_dict(); ;; Add extension @@ -93,7 +94,7 @@ cell verify_actions(cell c5, int is_external) inline { { (extensions, int success?) = extensions.udict_delete?(256, hash); throw_unless(40, success?); - throw_if(44, null?(extensions) & (stored_seqno < 0)); + throw_if(44, null?(extensions) & (signature_auth_disabled)); } set_data(begin_cell() @@ -104,28 +105,24 @@ cell verify_actions(cell c5, int is_external) inline { elseif (cs~check_and_remove_set_signature_auth_allowed_prefix()) { var allow? = cs~load_int(1); var ds = get_data().begin_parse(); - var stored_seqno = ds~load_int(size::stored_seqno); + var signature_auth_disabled = ds~load_int(size::signature_auth_disabled); + var stored_seqno = ds~load_uint(size::stored_seqno); var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions if (allow?) { ;; allow - throw_unless(43, stored_seqno < 0); - ;; Can't be disallowed with 0 because disallowing increments seqno - ;; -123 -> 123 -> 124 - stored_seqno = - stored_seqno; - stored_seqno = stored_seqno + 1; + throw_unless(43, signature_auth_disabled); + signature_auth_disabled = false; } else { ;; disallow - throw_unless(43, stored_seqno >= 0); + throw_if(43, signature_auth_disabled); ds = ds.skip_bits(size::stored_subwallet + size::public_key); var extensions_is_not_null = ds.preload_uint(1); throw_unless(42, extensions_is_not_null); - ;; Corner case: 0 -> 1 -> -1 - ;; 123 -> 124 -> -124 - stored_seqno = stored_seqno + 1; - stored_seqno = - stored_seqno; + signature_auth_disabled = true; } set_data(begin_cell() - .store_int(stored_seqno, size::stored_seqno) + .store_int(signature_auth_disabled, size::signature_auth_disabled) + .store_uint(stored_seqno, size::stored_seqno) .store_slice(immutable_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); } @@ -153,7 +150,7 @@ cell verify_actions(cell c5, int is_external) inline { () process_signed_request(slice full_body, int is_external) impure inline { ifnot (is_external) { ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(full_body.slice_bits() < 32 + size::subwallet_id + size::valid_until + size::msg_seqno + 1 + 512); + return_if(full_body.slice_bits() < 32 + size::signature_auth_disabled + size::subwallet_id + size::valid_until + size::msg_seqno + 1 + 512); } ;; The precise order of operations here is VERY important. Any other order results in unneccessary stack shuffles. @@ -164,7 +161,9 @@ cell verify_actions(cell c5, int is_external) inline { var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(size::subwallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::msg_seqno)); var ds = get_data().begin_parse(); - var stored_seqno = ds~load_int(size::stored_seqno); + var signature_auth_disabled = ds~load_int(size::signature_auth_disabled); + + var stored_seqno = ds~load_uint(size::stored_seqno); var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions var stored_subwallet = ds~load_uint(size::stored_subwallet); var public_key = ds.preload_uint(size::public_key); @@ -180,15 +179,16 @@ cell verify_actions(cell c5, int is_external) inline { ;; TODO: Consider moving signed into separate ref, slice_hash consumes 500 gas just like cell creation! int signature_is_valid = check_signature(slice_hash(signed), signature, public_key); if (is_external) { + throw_if(32, signature_auth_disabled); throw_unless(35, signature_is_valid); } else { + if (signature_auth_disabled) { + return(); + } ifnot (signature_is_valid) { return(); } } - ;; If public key is disabled, stored_seqno is strictly less than zero: stored_seqno < 0 - ;; However, msg_seqno is uint, therefore it can be only greater or equal to zero: msg_seqno >= 0 - ;; Thus, if public key is disabled, these two domains NEVER intersect, and additional check is not needed throw_unless(33, msg_seqno == stored_seqno); throw_unless(34, subwallet_id == stored_subwallet); throw_if(36, valid_until <= now()); @@ -200,7 +200,8 @@ cell verify_actions(cell c5, int is_external) inline { ;; Store and commit the seqno increment to prevent replays even if the subsequent requests fail. stored_seqno = stored_seqno + 1; set_data(begin_cell() - .store_int(stored_seqno, size::stored_seqno) + .store_int(false, size::signature_auth_disabled) ;; it cannot be true, otherwise execution would not get here + .store_uint(stored_seqno, size::stored_seqno) .store_slice(immutable_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); @@ -252,7 +253,7 @@ cell verify_actions(cell c5, int is_external) inline { var ds = get_data().begin_parse(); ;; It is not required to read this data here, maybe ext is doing simple transfer where those are not needed - var extensions = ds.skip_bits(size::stored_seqno + size::stored_subwallet + size::public_key).preload_dict(); + var extensions = ds.skip_bits(size::signature_auth_disabled + size::stored_seqno + size::stored_subwallet + size::public_key).preload_dict(); ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). @@ -278,25 +279,25 @@ cell verify_actions(cell c5, int is_external) inline { int seqno() method_id { ;; Use absolute value to do not confuse apps with negative seqno if key is disabled - return abs(get_data().begin_parse().preload_int(size::stored_seqno)); + return get_data().begin_parse().skip_bits(size::signature_auth_disabled).preload_uint(size::stored_seqno); } int get_wallet_id() method_id { - return get_data().begin_parse().skip_bits(size::stored_seqno).preload_uint(size::stored_subwallet); + return get_data().begin_parse().skip_bits(size::signature_auth_disabled + size::stored_seqno).preload_uint(size::stored_subwallet); } int get_public_key() method_id { - var cs = get_data().begin_parse().skip_bits(size::stored_seqno + size::stored_subwallet); + var cs = get_data().begin_parse().skip_bits(size::signature_auth_disabled + size::stored_seqno + size::stored_subwallet); return cs.preload_uint(size::public_key); } ;; Returns raw dictionary (or null if empty) where keys are packed addresses and the `wc` is stored in leafs. ;; User should unpack the address using the same packing function using `wc` to restore the original address. cell get_extensions() method_id { - var ds = get_data().begin_parse().skip_bits(size::stored_seqno + size::stored_subwallet + size::public_key); + var ds = get_data().begin_parse().skip_bits(size::signature_auth_disabled + size::stored_seqno + size::stored_subwallet + size::public_key); return ds~load_dict(); } int get_is_signature_auth_allowed() method_id { - return get_data().begin_parse().preload_int(size::stored_seqno) >= 0; + return ~ get_data().begin_parse().preload_int(size::signature_auth_disabled); } diff --git a/scripts/deployWalletV5.ts b/scripts/deployWalletV5.ts index 39f87e4..ce66fbb 100644 --- a/scripts/deployWalletV5.ts +++ b/scripts/deployWalletV5.ts @@ -15,6 +15,7 @@ export async function run(provider: NetworkProvider) { const walletV5 = provider.open( WalletV5.createFromConfig( { + signature_auth_disabled: false, seqno: 0, walletId: new WalletId({ networkGlobalId: -3 }).serialized, // testnet publicKey: keypair.publicKey, diff --git a/tests/wallet-v5-extensions.spec.ts b/tests/wallet-v5-extensions.spec.ts index ae91fdd..9b8522d 100644 --- a/tests/wallet-v5-extensions.spec.ts +++ b/tests/wallet-v5-extensions.spec.ts @@ -68,6 +68,7 @@ describe('Wallet V5 extensions auth', () => { walletV5 = blockchain.openContract( WalletV5.createFromConfig( { + signature_auth_disabled: false, seqno: 0, walletId: WALLET_ID.serialized, publicKey: keypair.publicKey, @@ -391,10 +392,7 @@ describe('Wallet V5 extensions auth', () => { expect(isSignatureAuthAllowed1).toEqual(-1); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 2); - - // Allowing or disallowing signature auth increments seqno, need to re-read - seqno = contract_seqno; + expect(contract_seqno).toEqual(seqno); const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const forwardValue = toNano(0.001); @@ -480,10 +478,7 @@ describe('Wallet V5 extensions auth', () => { expect(isSignatureAuthAllowed1).toEqual(-1); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 2); - - // Allowing or disallowing signature auth increments seqno, need to re-read - seqno = contract_seqno; + expect(contract_seqno).toEqual(seqno); const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const forwardValue = toNano(0.001); diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index 69e26c2..5c6ee97 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -58,6 +58,7 @@ describe('Wallet V5 sign auth external', () => { const _walletV5 = blockchain.openContract( WalletV5.createFromConfig( { + signature_auth_disabled: params?.signature_auth_disabled ?? false, seqno: params?.seqno ?? 0, walletId: params?.walletId ?? WALLET_ID.serialized, publicKey: params?.publicKey ?? _keypair.publicKey, @@ -100,6 +101,7 @@ describe('Wallet V5 sign auth external', () => { walletV5 = blockchain.openContract( WalletV5.createFromConfig( { + signature_auth_disabled: false, seqno: 0, walletId: WALLET_ID.serialized, publicKey: keypair.publicKey, @@ -746,7 +748,7 @@ describe('Wallet V5 sign auth external', () => { expect(isSignatureAuthAllowed).toEqual(0); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 1); + expect(contract_seqno).toEqual(seqno); }); it('Should add ext and disallow signature auth in separate txs', async () => { @@ -797,7 +799,7 @@ describe('Wallet V5 sign auth external', () => { expect(isSignatureAuthAllowed2).toEqual(0); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 1); + expect(contract_seqno).toEqual(seqno); }); it('Should add ext, disallow sign, allow sign, remove ext in one tx; send in other', async () => { @@ -823,10 +825,7 @@ describe('Wallet V5 sign auth external', () => { expect(isSignatureAuthAllowed).toEqual(-1); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 2); - - // Allowing or disallowing signature auth increments seqno, need to re-read - seqno = contract_seqno; + expect(contract_seqno).toEqual(seqno); const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const forwardValue = toNano(0.001); @@ -932,7 +931,7 @@ describe('Wallet V5 sign auth external', () => { expect(isSignatureAuthAllowed).toEqual(0); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 1); + expect(contract_seqno).toEqual(seqno); await disableConsoleError(() => expect(walletV5.sendExternalSignedMessage(createBody(packActionsList([])))).rejects.toThrow() diff --git a/tests/wallet-v5-get.spec.ts b/tests/wallet-v5-get.spec.ts index 6ec99d2..c6bca5e 100644 --- a/tests/wallet-v5-get.spec.ts +++ b/tests/wallet-v5-get.spec.ts @@ -31,6 +31,7 @@ describe('Wallet V5 get methods', () => { walletV5 = blockchain.openContract( WalletV5.createFromConfig( { + signature_auth_disabled: params?.signature_auth_disabled ?? false, seqno: params?.seqno ?? 0, walletId: params?.walletId ?? WALLET_ID.serialized, publicKey: params?.publicKey ?? keypair.publicKey, diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index 6459520..dc51b7c 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -51,6 +51,7 @@ describe('Wallet V5 sign auth internal', () => { const _walletV5 = blockchain.openContract( WalletV5.createFromConfig( { + signature_auth_disabled: params?.signature_auth_disabled ?? false, seqno: params?.seqno ?? 0, walletId: params?.walletId ?? WALLET_ID.serialized, publicKey: params?.publicKey ?? _keypair.publicKey, @@ -93,6 +94,7 @@ describe('Wallet V5 sign auth internal', () => { walletV5 = blockchain.openContract( WalletV5.createFromConfig( { + signature_auth_disabled: false, seqno: 0, walletId: WALLET_ID.serialized, publicKey: keypair.publicKey, @@ -941,7 +943,7 @@ describe('Wallet V5 sign auth internal', () => { expect(isSignatureAuthAllowed).toEqual(0); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 1); + expect(contract_seqno).toEqual(seqno); }); it('Should add ext and disallow signature auth in separate txs', async () => { @@ -1008,7 +1010,7 @@ describe('Wallet V5 sign auth internal', () => { expect(isSignatureAuthAllowed2).toEqual(0); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 1); + expect(contract_seqno).toEqual(seqno); }); it('Should add ext, disallow sign, allow sign, remove ext in one tx; send in other', async () => { @@ -1041,10 +1043,7 @@ describe('Wallet V5 sign auth internal', () => { expect(isSignatureAuthAllowed).toEqual(-1); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 2); - - // Allowing or disallowing signature auth increments seqno, need to re-read - seqno = contract_seqno; + expect(contract_seqno).toEqual(seqno); const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const forwardValue = toNano(0.001); @@ -1175,7 +1174,7 @@ describe('Wallet V5 sign auth internal', () => { expect(isSignatureAuthAllowed).toEqual(0); const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno + 1); + expect(contract_seqno).toEqual(seqno); const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const forwardValue = toNano(0.001); @@ -1194,7 +1193,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt2.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(33); + ).toEqual(0); expect(receipt2.transactions).not.toHaveTransaction({ from: walletV5.address, diff --git a/types.tlb b/types.tlb index 0ac3940..6390684 100644 --- a/types.tlb +++ b/types.tlb @@ -27,4 +27,4 @@ actions$_ {m:#} {n:#} actions:(ActionList n m) = InnerRequest; // Contract state wallet_id$_ global_id:int32 wc:int8 version:(## 8) subwallet_number:(## 32) = WalletID; -contract_state$_ seqno:int33 wallet_id:WalletID public_key:(## 256) extensions_dict:(HashmapE 256 int8) = ContractState; +contract_state$_ signature_auth_disabled:(## 1) seqno:# wallet_id:WalletID public_key:(## 256) extensions_dict:(HashmapE 256 int8) = ContractState; diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index 89c991b..3d5fd45 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -15,6 +15,7 @@ import { import { bufferToBigInt } from '../tests/utils'; export type WalletV5Config = { + signature_auth_disabled: boolean; seqno: number; walletId: bigint; publicKey: Buffer; @@ -23,7 +24,8 @@ export type WalletV5Config = { export function walletV5ConfigToCell(config: WalletV5Config): Cell { return beginCell() - .storeInt(config.seqno, 33) + .storeBit(config.signature_auth_disabled) + .storeUint(config.seqno, 32) .storeUint(config.walletId, 80) .storeBuffer(config.publicKey, 32) .storeDict(config.extensions, Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(8)) From a210caabc58b737d4c2fbe6e3676020b5c88dda3 Mon Sep 17 00:00:00 2001 From: Skydev0h Date: Fri, 14 Jun 2024 19:38:39 +0300 Subject: [PATCH 04/67] Fix `getExtensionsArray()` method when address hash starts with `00` Co-authored-by: Dmytro Polunin --- tests/wallet-v5-get.spec.ts | 11 ++++++++--- wrappers/wallet-v5.ts | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/wallet-v5-get.spec.ts b/tests/wallet-v5-get.spec.ts index c6bca5e..cb36bea 100644 --- a/tests/wallet-v5-get.spec.ts +++ b/tests/wallet-v5-get.spec.ts @@ -127,8 +127,11 @@ describe('Wallet V5 get methods', () => { }); it('Get extensions array', async () => { - const plugin1 = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - const plugin2 = Address.parse('EQA2pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX5Bf'); + const plugin1 = Address.parse( + '0:0000F5851B4A185F5F63C0D0CD0412F5ACA353F577DA18FF47C936F99DBD0000' + ); + const plugin2 = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); + const plugin3 = Address.parse('EQA2pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX5Bf'); const extensions: Dictionary = Dictionary.empty( Dictionary.Keys.BigUint(256), @@ -136,13 +139,15 @@ describe('Wallet V5 get methods', () => { ); extensions.set(packAddress(plugin1), BigInt(plugin1.workChain)); extensions.set(packAddress(plugin2), BigInt(plugin2.workChain)); + extensions.set(packAddress(plugin3), BigInt(plugin3.workChain)); await deploy({ extensions }); const actual = await walletV5.getExtensionsArray(); - expect(actual.length).toBe(2); + expect(actual.length).toBe(3); expect(actual[0].equals(plugin1)).toBeTruthy(); expect(actual[1].equals(plugin2)).toBeTruthy(); + expect(actual[2].equals(plugin3)).toBeTruthy(); }); it('Get empty extensions array', async () => { diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index 3d5fd45..b62870e 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -235,7 +235,7 @@ export class WalletV5 implements Contract { return dict.keys().map(key => { const wc = dict.get(key)!; const addressHex = key; - return Address.parseRaw(`${wc}:${addressHex.toString(16)}`); + return Address.parseRaw(`${wc}:${addressHex.toString(16).padStart(64, '0')}`); }); } } From ba7e312deea7b24681b404b6c6bbb5dd9d5bea54 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 10:27:55 +0400 Subject: [PATCH 05/67] add builds to git --- .gitignore | 1 - build/library-deployer.compiled.json | 1 + build/wallet_v5.compiled.json | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 build/library-deployer.compiled.json create mode 100644 build/wallet_v5.compiled.json diff --git a/.gitignore b/.gitignore index ef9f336..6f05462 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ node_modules temp -build .idea .env diff --git a/build/library-deployer.compiled.json b/build/library-deployer.compiled.json new file mode 100644 index 0000000..3acdc09 --- /dev/null +++ b/build/library-deployer.compiled.json @@ -0,0 +1 @@ +{"hex":"b5ee9c72410106010030000114ff00f4a413f4bcf2c80b0102012003020006f2f0010202d1050400193b511cbec1b232483ec13b552000053c00601cfc59c2"} \ No newline at end of file diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json new file mode 100644 index 0000000..823ea0f --- /dev/null +++ b/build/wallet_v5.compiled.json @@ -0,0 +1 @@ +{"hex":"b5ee9c7241021301000226000114ff00f4a413f4bcf2c80b0102012004020102f203011420d728239b4b3b74307f0f0201480e050201200706001bbe5f0f6a2684080b8eb90fa021840201200b080201200a090019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760d0c0012a880ed44d0d70a00b30018ab9ced44d08071d721d70bff028ed020c702dc01d0d60301c713dc01d72c232bc3a3748ea101fa4030fa44f828fa443058badded44d0810171d721f4058307f40edd3070db3c8e8c3120d72c239b4b73a431dd70e2100f01f08ef5eda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f910289602f26001f2a39e02945f09db31e001945f08db31e1e25122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd81002cc9401d200018edbd72c20e206dcfc2091709901d72c22f577a52412e25210b18e3b30d72c21065dcad48e2dd200ed44d0d200d31f5205953001f2ab709f02f26b01810150d721d70b00f2aa7fe2c8ca00cb1f58cf16c9ed5492f229e2e30dd74cd001e8d74c011211005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009001fa4001fa44f828fa443022baf2aded44d0810171d71821d70a0001f405069d3002c8ca0740148307f453f2a79e33048307f45bf2a8206e58b0f26ce2c85003cf1612f400c9ed545d9452a0"} \ No newline at end of file From 1d447047e4d221132cd76734bd91103eccb4629c Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 10:36:52 +0400 Subject: [PATCH 06/67] cosmetic: rename constants (no changes in compiled code) --- contracts/wallet_v5.fc | 54 +++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index e18faf8..085f50a 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -2,16 +2,12 @@ #include "imports/stdlib.fc"; -const int size::signature_auth_disabled = 1; -const int size::stored_seqno = 32; -const int size::stored_subwallet = 80; +const int size::bool = 1; +const int size::seqno = 32; +const int size::wallet_id = 80; const int size::public_key = 256; - -const int size::subwallet_id = 80; const int size::valid_until = 32; -const int size::msg_seqno = 32; - -const int size::flags = 4; +const int size::message_flags = 4; () return_if(int cond) impure asm "IFRET"; () return_unless(int cond) impure asm "IFNOTRET"; @@ -80,8 +76,8 @@ cell verify_actions(cell c5, int is_external) inline { throw_unless(45, my_wc == wc); var ds = get_data().begin_parse(); - var data_bits = ds~load_bits(size::signature_auth_disabled + size::stored_seqno + size::stored_subwallet + size::public_key); - var signature_auth_disabled = data_bits.preload_int(size::signature_auth_disabled); + var data_bits = ds~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key); + var signature_auth_disabled = data_bits.preload_int(size::bool); var extensions = ds.preload_dict(); ;; Add extension @@ -105,8 +101,8 @@ cell verify_actions(cell c5, int is_external) inline { elseif (cs~check_and_remove_set_signature_auth_allowed_prefix()) { var allow? = cs~load_int(1); var ds = get_data().begin_parse(); - var signature_auth_disabled = ds~load_int(size::signature_auth_disabled); - var stored_seqno = ds~load_uint(size::stored_seqno); + var signature_auth_disabled = ds~load_int(size::bool); + var stored_seqno = ds~load_uint(size::seqno); var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions if (allow?) { ;; allow @@ -115,14 +111,14 @@ cell verify_actions(cell c5, int is_external) inline { } else { ;; disallow throw_if(43, signature_auth_disabled); - ds = ds.skip_bits(size::stored_subwallet + size::public_key); + ds = ds.skip_bits(size::wallet_id + size::public_key); var extensions_is_not_null = ds.preload_uint(1); throw_unless(42, extensions_is_not_null); signature_auth_disabled = true; } set_data(begin_cell() - .store_int(signature_auth_disabled, size::signature_auth_disabled) - .store_uint(stored_seqno, size::stored_seqno) + .store_int(signature_auth_disabled, size::bool) + .store_uint(stored_seqno, size::seqno) .store_slice(immutable_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); } @@ -150,7 +146,7 @@ cell verify_actions(cell c5, int is_external) inline { () process_signed_request(slice full_body, int is_external) impure inline { ifnot (is_external) { ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(full_body.slice_bits() < 32 + size::signature_auth_disabled + size::subwallet_id + size::valid_until + size::msg_seqno + 1 + 512); + return_if(full_body.slice_bits() < 32 + size::bool + size::wallet_id + size::valid_until + size::seqno + 1 + 512); } ;; The precise order of operations here is VERY important. Any other order results in unneccessary stack shuffles. @@ -158,14 +154,14 @@ cell verify_actions(cell c5, int is_external) inline { slice signed = full_body.remove_last_bits(512); var cs = signed.skip_bits(32); - var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(size::subwallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::msg_seqno)); + var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); var ds = get_data().begin_parse(); - var signature_auth_disabled = ds~load_int(size::signature_auth_disabled); + var signature_auth_disabled = ds~load_int(size::bool); - var stored_seqno = ds~load_uint(size::stored_seqno); + var stored_seqno = ds~load_uint(size::seqno); var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions - var stored_subwallet = ds~load_uint(size::stored_subwallet); + var stored_subwallet = ds~load_uint(size::wallet_id); var public_key = ds.preload_uint(size::public_key); ;; Note on bouncing/nonbouncing behaviour: @@ -200,8 +196,8 @@ cell verify_actions(cell c5, int is_external) inline { ;; Store and commit the seqno increment to prevent replays even if the subsequent requests fail. stored_seqno = stored_seqno + 1; set_data(begin_cell() - .store_int(false, size::signature_auth_disabled) ;; it cannot be true, otherwise execution would not get here - .store_uint(stored_seqno, size::stored_seqno) + .store_int(false, size::bool) ;; it cannot be true, otherwise execution would not get here + .store_uint(stored_seqno, size::seqno) .store_slice(immutable_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); @@ -229,7 +225,7 @@ cell verify_actions(cell c5, int is_external) 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 s_flags = full_msg_slice~load_bits(size::flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... + var s_flags = full_msg_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... ;; If bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. return_if(count_trailing_ones(s_flags)); @@ -253,7 +249,7 @@ cell verify_actions(cell c5, int is_external) inline { var ds = get_data().begin_parse(); ;; It is not required to read this data here, maybe ext is doing simple transfer where those are not needed - var extensions = ds.skip_bits(size::signature_auth_disabled + size::stored_seqno + size::stored_subwallet + size::public_key).preload_dict(); + var extensions = ds.skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key).preload_dict(); ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). @@ -279,25 +275,25 @@ cell verify_actions(cell c5, int is_external) inline { int seqno() method_id { ;; Use absolute value to do not confuse apps with negative seqno if key is disabled - return get_data().begin_parse().skip_bits(size::signature_auth_disabled).preload_uint(size::stored_seqno); + return get_data().begin_parse().skip_bits(size::bool).preload_uint(size::seqno); } int get_wallet_id() method_id { - return get_data().begin_parse().skip_bits(size::signature_auth_disabled + size::stored_seqno).preload_uint(size::stored_subwallet); + return get_data().begin_parse().skip_bits(size::bool + size::seqno).preload_uint(size::wallet_id); } int get_public_key() method_id { - var cs = get_data().begin_parse().skip_bits(size::signature_auth_disabled + size::stored_seqno + size::stored_subwallet); + var cs = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id); return cs.preload_uint(size::public_key); } ;; Returns raw dictionary (or null if empty) where keys are packed addresses and the `wc` is stored in leafs. ;; User should unpack the address using the same packing function using `wc` to restore the original address. cell get_extensions() method_id { - var ds = get_data().begin_parse().skip_bits(size::signature_auth_disabled + size::stored_seqno + size::stored_subwallet + size::public_key); + var ds = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key); return ds~load_dict(); } int get_is_signature_auth_allowed() method_id { - return ~ get_data().begin_parse().preload_int(size::signature_auth_disabled); + return ~ get_data().begin_parse().preload_int(size::bool); } From 99493bc8fe9a0897aa035d642a4a9844e925c338 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 10:40:22 +0400 Subject: [PATCH 07/67] cosmetic: types (no changes in compiled code) --- contracts/wallet_v5.fc | 58 +++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 085f50a..3478720 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -12,11 +12,11 @@ const int size::message_flags = 4; () return_if(int cond) impure asm "IFRET"; () return_unless(int cond) impure asm "IFNOTRET"; -(slice) udict_get_or_return(cell dict, int key_len, int index) impure asm(index dict key_len) "DICTUGET" "IFNOTRET"; +slice udict_get_or_return(cell dict, int key_len, int index) impure asm(index dict key_len) "DICTUGET" "IFNOTRET"; (slice, int) begin_parse_xc(cell c) asm "XCTOS"; -(slice) enforce_and_remove_sign_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; +slice enforce_and_remove_sign_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; (slice, int) check_and_remove_extn_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; (slice, int) check_and_remove_sint_prefix(slice body) impure asm "x{73696E74} SDBEGINSQ"; @@ -25,7 +25,7 @@ const int size::message_flags = 4; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{5eaef4a4} SDBEGINSQ"; (slice, int) check_and_remove_set_signature_auth_allowed_prefix(slice body) impure asm "x{20cbb95a} SDBEGINSQ"; -(slice) enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; +slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; ;; Extensible wallet contract v5 @@ -66,8 +66,8 @@ cell verify_actions(cell c5, int is_external) inline { ;; Recurse into extended actions until we reach standard actions while (cs~load_int(1)) { - var is_add_ext? = cs~check_and_remove_add_extension_prefix(); - var is_del_ext? = is_add_ext? ? 0 : cs~check_and_remove_remove_extension_prefix(); + int is_add_ext? = cs~check_and_remove_add_extension_prefix(); + int is_del_ext? = is_add_ext? ? 0 : cs~check_and_remove_remove_extension_prefix(); ;; Add/remove extensions if (is_add_ext? | is_del_ext?) { (int wc, int hash) = parse_std_addr(cs~load_msg_addr()); @@ -75,10 +75,10 @@ cell verify_actions(cell c5, int is_external) inline { throw_unless(45, my_wc == wc); - var ds = get_data().begin_parse(); - var data_bits = ds~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key); - var signature_auth_disabled = data_bits.preload_int(size::bool); - var extensions = ds.preload_dict(); + slice ds = get_data().begin_parse(); + slice data_bits = ds~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key); + int signature_auth_disabled = data_bits.preload_int(size::bool); + cell extensions = ds.preload_dict(); ;; Add extension if (is_add_ext?) { @@ -99,11 +99,11 @@ cell verify_actions(cell c5, int is_external) inline { .end_cell()); } elseif (cs~check_and_remove_set_signature_auth_allowed_prefix()) { - var allow? = cs~load_int(1); - var ds = get_data().begin_parse(); - var signature_auth_disabled = ds~load_int(size::bool); - var stored_seqno = ds~load_uint(size::seqno); - var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions + int allow? = cs~load_int(1); + slice ds = get_data().begin_parse(); + int signature_auth_disabled = ds~load_int(size::bool); + int stored_seqno = ds~load_uint(size::seqno); + slice immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions if (allow?) { ;; allow throw_unless(43, signature_auth_disabled); @@ -112,7 +112,7 @@ cell verify_actions(cell c5, int is_external) inline { ;; disallow throw_if(43, signature_auth_disabled); ds = ds.skip_bits(size::wallet_id + size::public_key); - var extensions_is_not_null = ds.preload_uint(1); + int extensions_is_not_null = ds.preload_uint(1); throw_unless(42, extensions_is_not_null); signature_auth_disabled = true; } @@ -153,16 +153,16 @@ cell verify_actions(cell c5, int is_external) inline { slice signature = full_body.get_last_bits(512); slice signed = full_body.remove_last_bits(512); - var cs = signed.skip_bits(32); - var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); + slice cs = signed.skip_bits(32); + (int subwallet_id, int valid_until, int msg_seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); - var ds = get_data().begin_parse(); - var signature_auth_disabled = ds~load_int(size::bool); + slice ds = get_data().begin_parse(); + int signature_auth_disabled = ds~load_int(size::bool); - var stored_seqno = ds~load_uint(size::seqno); - var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions - var stored_subwallet = ds~load_uint(size::wallet_id); - var public_key = ds.preload_uint(size::public_key); + int stored_seqno = ds~load_uint(size::seqno); + slice immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions + int stored_subwallet = ds~load_uint(size::wallet_id); + int public_key = ds.preload_uint(size::public_key); ;; Note on bouncing/nonbouncing behaviour: ;; In principle, the wallet should not bounce incoming messages as to avoid @@ -223,9 +223,9 @@ cell verify_actions(cell c5, int is_external) inline { return_if(body.slice_refs_empty?()); ;; 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(); + slice full_msg_slice = full_msg.begin_parse(); - var s_flags = full_msg_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... + slice s_flags = full_msg_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... ;; If bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. return_if(count_trailing_ones(s_flags)); @@ -247,9 +247,9 @@ cell verify_actions(cell c5, int is_external) inline { return_unless(my_wc == sender_wc); - var ds = get_data().begin_parse(); + slice ds = get_data().begin_parse(); ;; It is not required to read this data here, maybe ext is doing simple transfer where those are not needed - var extensions = ds.skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key).preload_dict(); + cell extensions = ds.skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key).preload_dict(); ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). @@ -283,14 +283,14 @@ int get_wallet_id() method_id { } int get_public_key() method_id { - var cs = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id); + slice cs = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id); return cs.preload_uint(size::public_key); } ;; Returns raw dictionary (or null if empty) where keys are packed addresses and the `wc` is stored in leafs. ;; User should unpack the address using the same packing function using `wc` to restore the original address. cell get_extensions() method_id { - var ds = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key); + slice ds = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key); return ds~load_dict(); } From 0d5cc02ee1a240acb3c8fe6f02735c9d394f313b Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 11:22:50 +0400 Subject: [PATCH 08/67] cosmetic: rename vars, avoid abbreviations (no changes in compiled code) --- contracts/wallet_v5.fc | 200 ++++++++++++++++++++--------------------- 1 file changed, 100 insertions(+), 100 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 3478720..b77b5c4 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -9,28 +9,28 @@ const int size::public_key = 256; const int size::valid_until = 32; const int size::message_flags = 4; -() return_if(int cond) impure asm "IFRET"; -() return_unless(int cond) impure asm "IFNOTRET"; +() return_if(int condition) impure asm "IFRET"; +() return_unless(int condition) impure asm "IFNOTRET"; -slice udict_get_or_return(cell dict, int key_len, int index) impure asm(index dict key_len) "DICTUGET" "IFNOTRET"; +slice udict_get_or_return(cell dict, int key_length, int index) impure asm(index dict key_length) "DICTUGET" "IFNOTRET"; -(slice, int) begin_parse_xc(cell c) asm "XCTOS"; +(slice, int) begin_parse_raw(cell c) asm "XCTOS"; -slice enforce_and_remove_sign_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; -(slice, int) check_and_remove_extn_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; -(slice, int) check_and_remove_sint_prefix(slice body) impure asm "x{73696E74} SDBEGINSQ"; +slice enforce_and_remove_signed_external_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; +(slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; +(slice, int) check_and_remove_signed_internal_prefix(slice body) impure asm "x{73696E74} SDBEGINSQ"; ;; (slice, int) check_and_remove_set_data_prefix(slice body) impure asm "x{1ff8ea0b} SDBEGINSQ"; (slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{1c40db9f} SDBEGINSQ"; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{5eaef4a4} SDBEGINSQ"; -(slice, int) check_and_remove_set_signature_auth_allowed_prefix(slice body) impure asm "x{20cbb95a} SDBEGINSQ"; +(slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{20cbb95a} SDBEGINSQ"; slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; ;; Extensible wallet contract v5 ;; Stores pre-computed list of actions (mostly `action_send_msg`) in the actions register. -() set_actions(cell action_list) impure asm "c5 POP"; +() set_c5_actions(cell action_list) impure asm "c5 POP"; int count_leading_zeroes(slice cs) asm "SDCNTLEAD0"; int count_trailing_zeroes(slice cs) asm "SDCNTTRAIL0"; @@ -42,19 +42,19 @@ int count_trailing_ones(slice cs) asm "SDCNTTRAIL1"; slice get_last_bits(slice s, int n) asm "SDCUTLAST"; slice remove_last_bits(slice s, int n) asm "SDSKIPLAST"; -cell verify_actions(cell c5, int is_external) inline { +cell verify_c5_actions(cell c5, int is_external) inline { ;; Comment out code starting from here to disable checks (unsafe version) ;; {- - (slice c5s, _) = c5.begin_parse_xc(); - return_if(c5s.slice_empty?()); + (slice cs, _) = c5.begin_parse_raw(); + return_if(cs.slice_empty?()); do { ;; only send_msg is allowed, set_code or reserve_currency are not - c5s = c5s.enforce_and_remove_action_send_msg_prefix(); + cs = cs.enforce_and_remove_action_send_msg_prefix(); ;; enforce that send_mode has 2 bit set ;; for that load 7 bits and make sure that they end with 1 - throw_if(37, is_external & count_trailing_zeroes(c5s.preload_bits(7))); - (c5s, _) = c5s.preload_ref().begin_parse_xc(); - } until (c5s.slice_empty?()); + throw_if(37, is_external & count_trailing_zeroes(cs.preload_bits(7))); + (cs, _) = cs.preload_ref().begin_parse_raw(); + } until (cs.slice_empty?()); ;; -} return c5; } @@ -62,64 +62,64 @@ cell verify_actions(cell c5, int is_external) inline { ;; Dispatches already authenticated request. ;; 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(slice cs, int is_external) impure inline_ref { +() process_actions(slice cs, int is_external) impure inline_ref { ;; Recurse into extended actions until we reach standard actions while (cs~load_int(1)) { - int is_add_ext? = cs~check_and_remove_add_extension_prefix(); - int is_del_ext? = is_add_ext? ? 0 : cs~check_and_remove_remove_extension_prefix(); + int is_add_extension = cs~check_and_remove_add_extension_prefix(); + int is_remove_extension = is_add_extension ? 0 : cs~check_and_remove_remove_extension_prefix(); ;; Add/remove extensions - if (is_add_ext? | is_del_ext?) { - (int wc, int hash) = parse_std_addr(cs~load_msg_addr()); - (int my_wc, _) = parse_std_addr(my_address()); + if (is_add_extension | is_remove_extension) { + (int address_wc, int address_hash) = parse_std_addr(cs~load_msg_addr()); + (int my_address_wc, _) = parse_std_addr(my_address()); - throw_unless(45, my_wc == wc); + throw_unless(45, my_address_wc == address_wc); - slice ds = get_data().begin_parse(); - slice data_bits = ds~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key); - int signature_auth_disabled = data_bits.preload_int(size::bool); - cell extensions = ds.preload_dict(); + slice data_slice = get_data().begin_parse(); + slice data_slice_before_extensions = data_slice~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key); + int is_signature_disabled = data_slice_before_extensions.preload_int(size::bool); + cell extensions = data_slice.preload_dict(); ;; Add extension - if (is_add_ext?) { - (extensions, int success?) = extensions.udict_add_builder?(256, hash, begin_cell().store_int(wc,8)); - throw_unless(39, success?); + if (is_add_extension) { + (extensions, int is_success) = extensions.udict_add_builder?(256, address_hash, begin_cell().store_int(address_wc,8)); + throw_unless(39, is_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, hash); - throw_unless(40, success?); - throw_if(44, null?(extensions) & (signature_auth_disabled)); + (extensions, int is_success) = extensions.udict_delete?(256, address_hash); + throw_unless(40, is_success); + throw_if(44, null?(extensions) & is_signature_disabled); } set_data(begin_cell() - .store_slice(data_bits) + .store_slice(data_slice_before_extensions) .store_dict(extensions) .end_cell()); } - elseif (cs~check_and_remove_set_signature_auth_allowed_prefix()) { - int allow? = cs~load_int(1); - slice ds = get_data().begin_parse(); - int signature_auth_disabled = ds~load_int(size::bool); - int stored_seqno = ds~load_uint(size::seqno); - slice immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions - if (allow?) { + elseif (cs~check_and_remove_set_signature_allowed_prefix()) { + int allow_signature = cs~load_int(1); + slice data_slice = get_data().begin_parse(); + int is_signature_disabled = data_slice~load_int(size::bool); + int stored_seqno = data_slice~load_uint(size::seqno); + slice data_tail = data_slice; ;; stored_subwallet ~ public_key ~ extensions + if (allow_signature) { ;; allow - throw_unless(43, signature_auth_disabled); - signature_auth_disabled = false; + throw_unless(43, is_signature_disabled); + is_signature_disabled = false; } else { ;; disallow - throw_if(43, signature_auth_disabled); - ds = ds.skip_bits(size::wallet_id + size::public_key); - int extensions_is_not_null = ds.preload_uint(1); - throw_unless(42, extensions_is_not_null); - signature_auth_disabled = true; + throw_if(43, is_signature_disabled); + data_slice = data_slice.skip_bits(size::wallet_id + size::public_key); + int is_extensions_not_empty = data_slice.preload_uint(1); + throw_unless(42, is_extensions_not_empty); + is_signature_disabled = true; } set_data(begin_cell() - .store_int(signature_auth_disabled, size::bool) + .store_int(is_signature_disabled, size::bool) .store_uint(stored_seqno, size::seqno) - .store_slice(immutable_tail) ;; stored_subwallet ~ public_key ~ extensions + .store_slice(data_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); } ;; Uncomment to allow set_data (for unsafe version) @@ -136,33 +136,33 @@ cell verify_actions(cell c5, int is_external) inline { } ;; At this point we are at `action_list_basic$0 {n:#} actions:^(OutList n) = ActionList n 0;` ;; Simply set the C5 register with all pre-computed actions after verification: - set_actions(cs.preload_ref().verify_actions(is_external)); + set_c5_actions(cs.preload_ref().verify_c5_actions(is_external)); return (); } ;; ------------------------------------------------------------------------------------------------ ;; Verifies signed request, prevents replays and proceeds with `dispatch_request`. -() process_signed_request(slice full_body, int is_external) impure inline { +() process_signed_request(slice in_msg_body, int is_external) impure inline { ifnot (is_external) { ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(full_body.slice_bits() < 32 + size::bool + size::wallet_id + size::valid_until + size::seqno + 1 + 512); + return_if(in_msg_body.slice_bits() < 32 + size::bool + size::wallet_id + size::valid_until + size::seqno + 1 + 512); } ;; The precise order of operations here is VERY important. Any other order results in unneccessary stack shuffles. - slice signature = full_body.get_last_bits(512); - slice signed = full_body.remove_last_bits(512); + slice signature = in_msg_body.get_last_bits(512); + slice signed_slice = in_msg_body.remove_last_bits(512); - slice cs = signed.skip_bits(32); - (int subwallet_id, int valid_until, int msg_seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); + slice cs = signed_slice.skip_bits(32); ;; skip signed_internal or signer_external prefix + (int wallet_id, int valid_until, int seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); - slice ds = get_data().begin_parse(); - int signature_auth_disabled = ds~load_int(size::bool); + slice data_slice = get_data().begin_parse(); + int is_signature_disabled = data_slice~load_int(size::bool); - int stored_seqno = ds~load_uint(size::seqno); - slice immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions - int stored_subwallet = ds~load_uint(size::wallet_id); - int public_key = ds.preload_uint(size::public_key); + int stored_seqno = data_slice~load_uint(size::seqno); + slice data_tail = data_slice; ;; stored_subwallet ~ public_key ~ extensions + int stored_wallet_id = data_slice~load_uint(size::wallet_id); + int public_key = data_slice.preload_uint(size::public_key); ;; Note on bouncing/nonbouncing behaviour: ;; In principle, the wallet should not bounce incoming messages as to avoid @@ -173,20 +173,20 @@ cell verify_actions(cell c5, int is_external) inline { ;; but failing with exception (therefore bouncing) after the signature check. ;; TODO: Consider moving signed into separate ref, slice_hash consumes 500 gas just like cell creation! - int signature_is_valid = check_signature(slice_hash(signed), signature, public_key); + int is_signature_valid = check_signature(slice_hash(signed_slice), signature, public_key); if (is_external) { - throw_if(32, signature_auth_disabled); - throw_unless(35, signature_is_valid); + throw_if(32, is_signature_disabled); + throw_unless(35, is_signature_valid); } else { - if (signature_auth_disabled) { - return(); + if (is_signature_disabled) { + return (); } - ifnot (signature_is_valid) { - return(); + ifnot (is_signature_valid) { + return (); } } - throw_unless(33, msg_seqno == stored_seqno); - throw_unless(34, subwallet_id == stored_subwallet); + throw_unless(33, seqno == stored_seqno); + throw_unless(34, wallet_id == stored_wallet_id); throw_if(36, valid_until <= now()); if (is_external) { @@ -198,37 +198,37 @@ cell verify_actions(cell c5, int is_external) inline { set_data(begin_cell() .store_int(false, size::bool) ;; it cannot be true, otherwise execution would not get here .store_uint(stored_seqno, size::seqno) - .store_slice(immutable_tail) ;; stored_subwallet ~ public_key ~ extensions + .store_slice(data_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); commit(); - dispatch_complex_request(cs, is_external); + process_actions(cs, is_external); } -() recv_external(slice body) impure inline { - slice full_body = body; +() recv_external(slice in_msg_body) impure inline { + slice body = in_msg_body; ;; 0x7369676E ("sign") external message authenticated by signature - body = enforce_and_remove_sign_prefix(body); - process_signed_request(full_body, true); + in_msg_body = enforce_and_remove_signed_external_prefix(in_msg_body); + process_signed_request(body, true); return(); } ;; ------------------------------------------------------------------------------------------------ -() recv_internal(cell full_msg, slice body) impure inline { +() recv_internal(cell in_msg_full, slice in_msg_body) impure inline { ;; return right away if there are no references ;; correct messages always have a ref, because any code paths ends with preload_ref - return_if(body.slice_refs_empty?()); + return_if(in_msg_body.slice_refs_empty?()); ;; Any attempt to postpone msg_value deletion will result in s2 POP -> SWAP change. No use at all. - slice full_msg_slice = full_msg.begin_parse(); + slice in_msg_full_slice = in_msg_full.begin_parse(); - slice s_flags = full_msg_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... + slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... ;; If bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. - return_if(count_trailing_ones(s_flags)); + return_if(count_trailing_ones(message_flags_slice)); ;; slicy_return_if_bounce(begin_cell().store_uint(3, 4).end_cell().begin_parse()); ;; TEST!!! @@ -236,36 +236,36 @@ cell verify_actions(cell c5, int is_external) inline { ;; - 0x6578746E "extn" authenticated by extension ;; - 0x73696E74 "sint" internal message authenticated by signature - (body, int is_extn?) = check_and_remove_extn_prefix(body); ;; 0x6578746E ("extn") + (in_msg_body, int is_extension_action) = check_and_remove_extension_action_prefix(in_msg_body); ;; 0x6578746E ("extn") ;; IFJMPREF because unconditionally returns inside - if (is_extn?) { ;; "extn" authenticated by extension + if (is_extension_action) { ;; "extn" authenticated by extension ;; Authenticate extension by its address. - (int sender_wc, int sender_hash) = parse_std_addr(full_msg_slice~load_msg_addr()); ;; no PLDMSGADDR exists - (int my_wc, _) = parse_std_addr(my_address()); + (int sender_address_wc, int sender_address_hash) = parse_std_addr(in_msg_full_slice~load_msg_addr()); ;; no PLDMSGADDR exists + (int my_address_wc, _) = parse_std_addr(my_address()); - return_unless(my_wc == sender_wc); + return_unless(my_address_wc == sender_address_wc); - slice ds = get_data().begin_parse(); + slice data_slice = get_data().begin_parse(); ;; It is not required to read this data here, maybe ext is doing simple transfer where those are not needed - cell extensions = ds.skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key).preload_dict(); + cell extensions = data_slice.skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key).preload_dict(); ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). - extensions.udict_get_or_return(256, sender_hash); ;; kindof ifnot (success?) { return(); } + extensions.udict_get_or_return(256, sender_address_hash); ;; kindof ifnot (success?) { return(); } - dispatch_complex_request(body, false); + process_actions(in_msg_body, false); return (); } - slice full_body = body; - (_, int is_sint?) = check_and_remove_sint_prefix(body); ;; 0x73696E74 ("sint") - sign internal - return_unless(is_sint?); + slice body = in_msg_body; + (_, int is_signed_internal) = check_and_remove_signed_internal_prefix(in_msg_body); ;; 0x73696E74 ("sint") - sign internal + return_unless(is_signed_internal); ;; Process the rest of the slice just like the signed request. - process_signed_request(full_body, false); + process_signed_request(body, false); return (); ;; Explicit returns escape function faster and const less gas (suddenly!) } @@ -283,15 +283,15 @@ int get_wallet_id() method_id { } int get_public_key() method_id { - slice cs = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id); - return cs.preload_uint(size::public_key); + slice data_slice = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id); + return data_slice.preload_uint(size::public_key); } ;; Returns raw dictionary (or null if empty) where keys are packed addresses and the `wc` is stored in leafs. ;; User should unpack the address using the same packing function using `wc` to restore the original address. cell get_extensions() method_id { - slice ds = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key); - return ds~load_dict(); + slice data_slice = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key); + return data_slice~load_dict(); } int get_is_signature_auth_allowed() method_id { From ce56813db3f2910ac5f138ec8f6f7b9999b89c69 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 11:32:20 +0400 Subject: [PATCH 09/67] cosmetic: remove comments about optimizations (no changes in compiled code) --- contracts/wallet_v5.fc | 96 +++++++++--------------------------------- 1 file changed, 21 insertions(+), 75 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index b77b5c4..d6d1ace 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -20,51 +20,38 @@ slice enforce_and_remove_signed_external_prefix(slice body) impure asm "x{736967 (slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; (slice, int) check_and_remove_signed_internal_prefix(slice body) impure asm "x{73696E74} SDBEGINSQ"; -;; (slice, int) check_and_remove_set_data_prefix(slice body) impure asm "x{1ff8ea0b} SDBEGINSQ"; (slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{1c40db9f} SDBEGINSQ"; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{5eaef4a4} SDBEGINSQ"; (slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{20cbb95a} SDBEGINSQ"; slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; -;; Extensible wallet contract v5 - ;; Stores pre-computed list of actions (mostly `action_send_msg`) in the actions register. () set_c5_actions(cell action_list) impure asm "c5 POP"; -int count_leading_zeroes(slice cs) asm "SDCNTLEAD0"; int count_trailing_zeroes(slice cs) asm "SDCNTTRAIL0"; int count_trailing_ones(slice cs) asm "SDCNTTRAIL1"; -;; (slice, slice) split(slice s, int bits, int refs) asm "SPLIT"; -;; (slice, slice, int) split?(slice s, int bits, int refs) asm "SPLIT" "NULLSWAPIFNOT"; - slice get_last_bits(slice s, int n) asm "SDCUTLAST"; slice remove_last_bits(slice s, int n) asm "SDSKIPLAST"; cell verify_c5_actions(cell c5, int is_external) inline { - ;; Comment out code starting from here to disable checks (unsafe version) - ;; {- (slice cs, _) = c5.begin_parse_raw(); return_if(cs.slice_empty?()); + do { ;; only send_msg is allowed, set_code or reserve_currency are not cs = cs.enforce_and_remove_action_send_msg_prefix(); - ;; enforce that send_mode has 2 bit set - ;; for that load 7 bits and make sure that they end with 1 + ;; enforce that send_mode has 2 bit set, for that load 7 bits and make sure that they end with 1 throw_if(37, is_external & count_trailing_zeroes(cs.preload_bits(7))); (cs, _) = cs.preload_ref().begin_parse_raw(); } until (cs.slice_empty?()); - ;; -} + return c5; } -;; Dispatches already authenticated request. -;; 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 () process_actions(slice cs, int is_external) impure inline_ref { - - ;; Recurse into extended actions until we reach standard actions + ;; Loop extended actions until we reach standard actions while (cs~load_int(1)) { int is_add_extension = cs~check_and_remove_add_extension_prefix(); int is_remove_extension = is_add_extension ? 0 : cs~check_and_remove_remove_extension_prefix(); @@ -84,10 +71,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { if (is_add_extension) { (extensions, int is_success) = extensions.udict_add_builder?(256, address_hash, begin_cell().store_int(address_wc,8)); throw_unless(39, is_success); - } else - ;; Remove extension if (op == 0x5eaef4a4) - ;; It can be ONLY 0x1c40db9f OR 0x5eaef4a4 here. No need for second check. - { + } else { ;; Remove extension (extensions, int is_success) = extensions.udict_delete?(256, address_hash); throw_unless(40, is_success); throw_if(44, null?(extensions) & is_signature_disabled); @@ -97,19 +81,17 @@ cell verify_c5_actions(cell c5, int is_external) inline { .store_slice(data_slice_before_extensions) .store_dict(extensions) .end_cell()); - } - elseif (cs~check_and_remove_set_signature_allowed_prefix()) { + + } elseif (cs~check_and_remove_set_signature_allowed_prefix()) { int allow_signature = cs~load_int(1); slice data_slice = get_data().begin_parse(); int is_signature_disabled = data_slice~load_int(size::bool); int stored_seqno = data_slice~load_uint(size::seqno); slice data_tail = data_slice; ;; stored_subwallet ~ public_key ~ extensions - if (allow_signature) { - ;; allow + if (allow_signature) { ;; allow throw_unless(43, is_signature_disabled); is_signature_disabled = false; - } else { - ;; disallow + } else { ;; disallow throw_if(43, is_signature_disabled); data_slice = data_slice.skip_bits(size::wallet_id + size::public_key); int is_extensions_not_empty = data_slice.preload_uint(1); @@ -121,20 +103,11 @@ cell verify_c5_actions(cell c5, int is_external) inline { .store_uint(stored_seqno, size::seqno) .store_slice(data_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); - } - ;; Uncomment to allow set_data (for unsafe version) - {- - elseif (cs~check_and_remove_set_data_prefix()) { - set_data(cs~load_ref()); - } - -} - else { - ;; need to throw on unsupported actions for correct flow and for testability + } else { throw(41); ;; unsupported action } cs = cs.preload_ref().begin_parse(); } - ;; At this point we are at `action_list_basic$0 {n:#} actions:^(OutList n) = ActionList n 0;` ;; Simply set the C5 register with all pre-computed actions after verification: set_c5_actions(cs.preload_ref().verify_c5_actions(is_external)); return (); @@ -142,14 +115,12 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; ------------------------------------------------------------------------------------------------ -;; Verifies signed request, prevents replays and proceeds with `dispatch_request`. () process_signed_request(slice in_msg_body, int is_external) impure inline { ifnot (is_external) { ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) return_if(in_msg_body.slice_bits() < 32 + size::bool + size::wallet_id + size::valid_until + size::seqno + 1 + 512); } - ;; The precise order of operations here is VERY important. Any other order results in unneccessary stack shuffles. slice signature = in_msg_body.get_last_bits(512); slice signed_slice = in_msg_body.remove_last_bits(512); @@ -164,15 +135,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { int stored_wallet_id = data_slice~load_uint(size::wallet_id); int public_key = data_slice.preload_uint(size::public_key); - ;; Note on bouncing/nonbouncing behaviour: - ;; In principle, the wallet should not bounce incoming messages as to avoid - ;; returning deposits back to the sender due to opcode misinterpretation. - ;; However, specifically for "gasless" transactions (signed messages relayed by a 3rd party), - ;; there is a risk for the relaying party to be abused: their coins should be bounced back in case of a race condition or delays. - ;; We resolve this dilemma by silently failing at the signature check (therefore ordinary deposits with arbitrary opcodes never bounce), - ;; but failing with exception (therefore bouncing) after the signature check. - - ;; TODO: Consider moving signed into separate ref, slice_hash consumes 500 gas just like cell creation! int is_signature_valid = check_signature(slice_hash(signed_slice), signature, public_key); if (is_external) { throw_if(32, is_signature_disabled); @@ -196,7 +158,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Store and commit the seqno increment to prevent replays even if the subsequent requests fail. stored_seqno = stored_seqno + 1; set_data(begin_cell() - .store_int(false, size::bool) ;; it cannot be true, otherwise execution would not get here + .store_int(false, size::bool) .store_uint(stored_seqno, size::seqno) .store_slice(data_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); @@ -208,7 +170,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { () recv_external(slice in_msg_body) impure inline { slice body = in_msg_body; - ;; 0x7369676E ("sign") external message authenticated by signature in_msg_body = enforce_and_remove_signed_external_prefix(in_msg_body); process_signed_request(body, true); return(); @@ -217,43 +178,31 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; ------------------------------------------------------------------------------------------------ () recv_internal(cell in_msg_full, slice in_msg_body) impure inline { + return_if(in_msg_body.slice_refs_empty?()); ;; message with actions always have a ref - ;; return right away if there are no references - ;; correct messages always have a ref, because any code paths ends with preload_ref - return_if(in_msg_body.slice_refs_empty?()); - - ;; Any attempt to postpone msg_value deletion will result in s2 POP -> SWAP change. No use at all. slice in_msg_full_slice = in_msg_full.begin_parse(); - slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddressInt ... + slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - ;; If bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. + ;; skip bounced messages - if bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. return_if(count_trailing_ones(message_flags_slice)); - ;; slicy_return_if_bounce(begin_cell().store_uint(3, 4).end_cell().begin_parse()); ;; TEST!!! + (in_msg_body, int is_extension_action) = check_and_remove_extension_action_prefix(in_msg_body); - ;; We accept two kinds of authenticated messages: - ;; - 0x6578746E "extn" authenticated by extension - ;; - 0x73696E74 "sint" internal message authenticated by signature - - (in_msg_body, int is_extension_action) = check_and_remove_extension_action_prefix(in_msg_body); ;; 0x6578746E ("extn") - - ;; IFJMPREF because unconditionally returns inside - if (is_extension_action) { ;; "extn" authenticated by extension + if (is_extension_action) { ;; Authenticate extension by its address. - (int sender_address_wc, int sender_address_hash) = parse_std_addr(in_msg_full_slice~load_msg_addr()); ;; no PLDMSGADDR exists + (int sender_address_wc, int sender_address_hash) = parse_std_addr(in_msg_full_slice~load_msg_addr()); (int my_address_wc, _) = parse_std_addr(my_address()); return_unless(my_address_wc == sender_address_wc); slice data_slice = get_data().begin_parse(); - ;; It is not required to read this data here, maybe ext is doing simple transfer where those are not needed cell extensions = data_slice.skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key).preload_dict(); ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). - extensions.udict_get_or_return(256, sender_address_hash); ;; kindof ifnot (success?) { return(); } + extensions.udict_get_or_return(256, sender_address_hash); process_actions(in_msg_body, false); return (); @@ -261,12 +210,11 @@ cell verify_c5_actions(cell c5, int is_external) inline { } slice body = in_msg_body; - (_, int is_signed_internal) = check_and_remove_signed_internal_prefix(in_msg_body); ;; 0x73696E74 ("sint") - sign internal + (_, int is_signed_internal) = check_and_remove_signed_internal_prefix(in_msg_body); return_unless(is_signed_internal); - ;; Process the rest of the slice just like the signed request. process_signed_request(body, false); - return (); ;; Explicit returns escape function faster and const less gas (suddenly!) + return (); } @@ -274,7 +222,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Get methods int seqno() method_id { - ;; Use absolute value to do not confuse apps with negative seqno if key is disabled return get_data().begin_parse().skip_bits(size::bool).preload_uint(size::seqno); } @@ -287,8 +234,7 @@ int get_public_key() method_id { return data_slice.preload_uint(size::public_key); } -;; Returns raw dictionary (or null if empty) where keys are packed addresses and the `wc` is stored in leafs. -;; User should unpack the address using the same packing function using `wc` to restore the original address. +;; Returns raw dictionary (or null if empty) where keys are address hashes. Workchains of extensions are same with wallet smart contract workchain cell get_extensions() method_id { slice data_slice = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key); return data_slice~load_dict(); From b58bbb97731cd6a348691798f2c2ca3d3087f17f Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 11:37:23 +0400 Subject: [PATCH 10/67] cosmetic: add additional consts for sizes (no changes in compiled code) --- contracts/wallet_v5.fc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index d6d1ace..f5443b7 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -8,6 +8,9 @@ const int size::wallet_id = 80; const int size::public_key = 256; const int size::valid_until = 32; const int size::message_flags = 4; +const int size::signature = 512; +const int size::message_type_prefix = 32; +const int size::address_hash_size = 256; () return_if(int condition) impure asm "IFRET"; () return_unless(int condition) impure asm "IFNOTRET"; @@ -69,10 +72,10 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Add extension if (is_add_extension) { - (extensions, int is_success) = extensions.udict_add_builder?(256, address_hash, begin_cell().store_int(address_wc,8)); + (extensions, int is_success) = extensions.udict_add_builder?(size::address_hash_size, address_hash, begin_cell().store_int(address_wc,8)); throw_unless(39, is_success); } else { ;; Remove extension - (extensions, int is_success) = extensions.udict_delete?(256, address_hash); + (extensions, int is_success) = extensions.udict_delete?(size::address_hash_size, address_hash); throw_unless(40, is_success); throw_if(44, null?(extensions) & is_signature_disabled); } @@ -118,13 +121,13 @@ cell verify_c5_actions(cell c5, int is_external) inline { () process_signed_request(slice in_msg_body, int is_external) impure inline { ifnot (is_external) { ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(in_msg_body.slice_bits() < 32 + size::bool + size::wallet_id + size::valid_until + size::seqno + 1 + 512); + return_if(in_msg_body.slice_bits() < size::message_type_prefix + size::bool + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); } - slice signature = in_msg_body.get_last_bits(512); - slice signed_slice = in_msg_body.remove_last_bits(512); + slice signature = in_msg_body.get_last_bits(size::signature); + slice signed_slice = in_msg_body.remove_last_bits(size::signature); - slice cs = signed_slice.skip_bits(32); ;; skip signed_internal or signer_external prefix + slice cs = signed_slice.skip_bits(size::message_type_prefix); ;; skip signed_internal or signer_external prefix (int wallet_id, int valid_until, int seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); slice data_slice = get_data().begin_parse(); @@ -202,7 +205,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). - extensions.udict_get_or_return(256, sender_address_hash); + extensions.udict_get_or_return(size::address_hash_size, sender_address_hash); process_actions(in_msg_body, false); return (); From f1f4cb4bd969f7e167e638dabd58685ffc9d1692 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 11:48:46 +0400 Subject: [PATCH 11/67] cosmetic: add error const (no changes in compiled code) --- contracts/wallet_v5.fc | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index f5443b7..19d44dd 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -2,6 +2,20 @@ #include "imports/stdlib.fc"; +const int error::invalid_c5_action = 37; +const int error::extension_wrong_workchain = 45; +const int error::add_extension_error = 39; +const int error::remove_extension_error = 40; +const int error::disable_signature_when_extensions_is_empty = 44; +const int error::this_signature_mode_already_set = 43; +const int error::remove_last_extension_when_signature_disabled = 42; +const int error::unspported_action = 41; +const int error::signature_disabled = 32; +const int error::invalid_signature = 35; +const int error::invalid_seqno = 33; +const int error::invalid_wallet_id = 34; +const int error::expired = 36; + const int size::bool = 1; const int size::seqno = 32; const int size::wallet_id = 80; @@ -46,7 +60,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; only send_msg is allowed, set_code or reserve_currency are not cs = cs.enforce_and_remove_action_send_msg_prefix(); ;; enforce that send_mode has 2 bit set, for that load 7 bits and make sure that they end with 1 - throw_if(37, is_external & count_trailing_zeroes(cs.preload_bits(7))); + throw_if(error::invalid_c5_action, is_external & count_trailing_zeroes(cs.preload_bits(7))); (cs, _) = cs.preload_ref().begin_parse_raw(); } until (cs.slice_empty?()); @@ -63,7 +77,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { (int address_wc, int address_hash) = parse_std_addr(cs~load_msg_addr()); (int my_address_wc, _) = parse_std_addr(my_address()); - throw_unless(45, my_address_wc == address_wc); + throw_unless(error::extension_wrong_workchain, my_address_wc == address_wc); slice data_slice = get_data().begin_parse(); slice data_slice_before_extensions = data_slice~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key); @@ -73,11 +87,11 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Add extension if (is_add_extension) { (extensions, int is_success) = extensions.udict_add_builder?(size::address_hash_size, address_hash, begin_cell().store_int(address_wc,8)); - throw_unless(39, is_success); + throw_unless( error::add_extension_error, is_success); } else { ;; Remove extension (extensions, int is_success) = extensions.udict_delete?(size::address_hash_size, address_hash); - throw_unless(40, is_success); - throw_if(44, null?(extensions) & is_signature_disabled); + throw_unless(error::remove_extension_error, is_success); + throw_if(error::disable_signature_when_extensions_is_empty, null?(extensions) & is_signature_disabled); } set_data(begin_cell() @@ -92,13 +106,13 @@ cell verify_c5_actions(cell c5, int is_external) inline { int stored_seqno = data_slice~load_uint(size::seqno); slice data_tail = data_slice; ;; stored_subwallet ~ public_key ~ extensions if (allow_signature) { ;; allow - throw_unless(43, is_signature_disabled); + throw_unless(error::this_signature_mode_already_set, is_signature_disabled); is_signature_disabled = false; } else { ;; disallow - throw_if(43, is_signature_disabled); + throw_if(error::this_signature_mode_already_set, is_signature_disabled); data_slice = data_slice.skip_bits(size::wallet_id + size::public_key); int is_extensions_not_empty = data_slice.preload_uint(1); - throw_unless(42, is_extensions_not_empty); + throw_unless(error::remove_last_extension_when_signature_disabled, is_extensions_not_empty); is_signature_disabled = true; } set_data(begin_cell() @@ -107,7 +121,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { .store_slice(data_tail) ;; stored_subwallet ~ public_key ~ extensions .end_cell()); } else { - throw(41); ;; unsupported action + throw(error::unspported_action); } cs = cs.preload_ref().begin_parse(); } @@ -140,8 +154,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { int is_signature_valid = check_signature(slice_hash(signed_slice), signature, public_key); if (is_external) { - throw_if(32, is_signature_disabled); - throw_unless(35, is_signature_valid); + throw_if(error::signature_disabled, is_signature_disabled); + throw_unless( error::invalid_signature, is_signature_valid); } else { if (is_signature_disabled) { return (); @@ -150,9 +164,9 @@ cell verify_c5_actions(cell c5, int is_external) inline { return (); } } - throw_unless(33, seqno == stored_seqno); - throw_unless(34, wallet_id == stored_wallet_id); - throw_if(36, valid_until <= now()); + throw_unless(error::invalid_seqno, seqno == stored_seqno); + throw_unless(error::invalid_wallet_id, wallet_id == stored_wallet_id); + throw_if(error::expired, valid_until <= now()); if (is_external) { accept_message(); From 0537f52437da10a96f12351fe11e3e6aab3f8726 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 11:54:55 +0400 Subject: [PATCH 12/67] cosmetic: subwallet_id->wallet_id in comments (no changes in compiled code) --- contracts/wallet_v5.fc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 19d44dd..5097ae1 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -104,7 +104,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice data_slice = get_data().begin_parse(); int is_signature_disabled = data_slice~load_int(size::bool); int stored_seqno = data_slice~load_uint(size::seqno); - slice data_tail = data_slice; ;; stored_subwallet ~ public_key ~ extensions + slice data_tail = data_slice; ;; wallet_id, public_key, extensions if (allow_signature) { ;; allow throw_unless(error::this_signature_mode_already_set, is_signature_disabled); is_signature_disabled = false; @@ -118,7 +118,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { set_data(begin_cell() .store_int(is_signature_disabled, size::bool) .store_uint(stored_seqno, size::seqno) - .store_slice(data_tail) ;; stored_subwallet ~ public_key ~ extensions + .store_slice(data_tail) ;; wallet_id, public_key, extensions .end_cell()); } else { throw(error::unspported_action); @@ -148,7 +148,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { int is_signature_disabled = data_slice~load_int(size::bool); int stored_seqno = data_slice~load_uint(size::seqno); - slice data_tail = data_slice; ;; stored_subwallet ~ public_key ~ extensions + slice data_tail = data_slice; ;; wallet_id, public_key, extensions int stored_wallet_id = data_slice~load_uint(size::wallet_id); int public_key = data_slice.preload_uint(size::public_key); @@ -177,7 +177,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { set_data(begin_cell() .store_int(false, size::bool) .store_uint(stored_seqno, size::seqno) - .store_slice(data_tail) ;; stored_subwallet ~ public_key ~ extensions + .store_slice(data_tail) ;; wallet_id, public_key, extensions .end_cell()); commit(); From 0574f376ab9df5321b783015312bf610d624067b Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 12:11:36 +0400 Subject: [PATCH 13/67] don't store workchain in extensions dict values --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 2 +- tests/wallet-v5-extensions.spec.ts | 18 +++++++------- tests/wallet-v5-external.spec.ts | 36 ++++++++++++++-------------- tests/wallet-v5-get.spec.ts | 16 ++++++------- tests/wallet-v5-internal.spec.ts | 38 +++++++++++++++--------------- wrappers/wallet-v5.ts | 6 ++--- 7 files changed, 59 insertions(+), 59 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 823ea0f..41b1cf8 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021301000226000114ff00f4a413f4bcf2c80b0102012004020102f203011420d728239b4b3b74307f0f0201480e050201200706001bbe5f0f6a2684080b8eb90fa021840201200b080201200a090019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760d0c0012a880ed44d0d70a00b30018ab9ced44d08071d721d70bff028ed020c702dc01d0d60301c713dc01d72c232bc3a3748ea101fa4030fa44f828fa443058badded44d0810171d721f4058307f40edd3070db3c8e8c3120d72c239b4b73a431dd70e2100f01f08ef5eda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f910289602f26001f2a39e02945f09db31e001945f08db31e1e25122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd81002cc9401d200018edbd72c20e206dcfc2091709901d72c22f577a52412e25210b18e3b30d72c21065dcad48e2dd200ed44d0d200d31f5205953001f2ab709f02f26b01810150d721d70b00f2aa7fe2c8ca00cb1f58cf16c9ed5492f229e2e30dd74cd001e8d74c011211005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009001fa4001fa44f828fa443022baf2aded44d0810171d71821d70a0001f405069d3002c8ca0740148307f453f2a79e33048307f45bf2a8206e58b0f26ce2c85003cf1612f400c9ed545d9452a0"} \ No newline at end of file +{"hex":"b5ee9c7241021301000229000114ff00f4a413f4bcf2c80b0102012004020102f203011420d728239b4b3b74307f0f0201480e050201200706001bbe5f0f6a2684080b8eb90fa021840201200b080201200a090019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760d0c0012a880ed44d0d70a00b30018ab9ced44d08071d721d70bff028ed020c702dc01d0d60301c713dc01d72c232bc3a3748ea101fa4030fa44f828fa443058badded44d0810171d721f4058307f40edd3070db3c8e8c3120d72c239b4b73a431dd70e2100f01f08ef5eda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f910289602f26001f2a39e02945f09db31e001945f08db31e1e25122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd81002cc9401d200018edbd72c20e206dcfc2091709901d72c22f577a52412e25210b18e3b30d72c21065dcad48e2dd200ed44d0d200d31f5205953001f2ab709f02f26b01810150d721d70b00f2aa7fe2c8ca00cb1f58cf16c9ed5492f229e2e30dd74cd001e8d74c011211005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009601fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1050248307f45bf2a8206e5004b0f26c02e2c85003cf1612f400c9ed54c2158adf"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 5097ae1..8196e97 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -86,7 +86,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Add extension if (is_add_extension) { - (extensions, int is_success) = extensions.udict_add_builder?(size::address_hash_size, address_hash, begin_cell().store_int(address_wc,8)); + (extensions, int is_success) = extensions.udict_add_builder?(size::address_hash_size, address_hash, begin_cell().store_int(-1, 1)); throw_unless( error::add_extension_error, is_success); } else { ;; Remove extension (extensions, int is_success) = extensions.udict_delete?(size::address_hash_size, address_hash); diff --git a/tests/wallet-v5-extensions.spec.ts b/tests/wallet-v5-extensions.spec.ts index 9b8522d..0d5e331 100644 --- a/tests/wallet-v5-extensions.spec.ts +++ b/tests/wallet-v5-extensions.spec.ts @@ -197,17 +197,17 @@ describe('Wallet V5 extensions auth', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(2); expect(extensionsDict.get(packAddress(sender.address!))).toEqual( - BigInt(sender.address!.workChain) + -1n ); expect(extensionsDict.get(packAddress(testOtherExtension))).toEqual( - BigInt(testOtherExtension.workChain) + -1n ); }); @@ -230,15 +230,15 @@ describe('Wallet V5 extensions auth', () => { const extensionsDict1 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict1.size).toEqual(2); expect(extensionsDict1.get(packAddress(sender.address!))).toEqual( - BigInt(sender.address!.workChain) + -1n ); expect(extensionsDict1.get(packAddress(otherExtension))).toEqual( - BigInt(otherExtension.workChain) + -1n ); const actions2 = packActionsList([new ActionRemoveExtension(otherExtension)]); @@ -252,12 +252,12 @@ describe('Wallet V5 extensions auth', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(1); expect(extensionsDict.get(packAddress(sender.address!))).toEqual( - BigInt(sender.address!.workChain) + -1n ); expect(extensionsDict.get(packAddress(otherExtension))).toEqual(undefined); }); @@ -279,7 +279,7 @@ describe('Wallet V5 extensions auth', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(0); diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index 5c6ee97..1e22499 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -208,14 +208,14 @@ describe('Wallet V5 sign auth external', () => { const extensions = await walletV5.getExtensions(); const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), extensions ); expect(extensionsDict.size).toEqual(1); - const storedWC = extensionsDict.get(packAddress(testExtension)); - expect(storedWC).toEqual(BigInt(testExtension.workChain)); + const dictValue = extensionsDict.get(packAddress(testExtension)); + expect(dictValue).toEqual(-1n); }); it('Send single transfers to a deployed wallet', async () => { @@ -328,7 +328,7 @@ describe('Wallet V5 sign auth external', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); @@ -336,10 +336,10 @@ describe('Wallet V5 sign auth external', () => { accountForGas(receipt.transactions); expect(extensionsDict.get(packAddress(testExtension1))).toEqual( - BigInt(testExtension1.workChain) + -1n ); expect(extensionsDict.get(packAddress(testExtension2))).toEqual( - BigInt(testExtension2.workChain) + -1n ); }); @@ -350,19 +350,19 @@ describe('Wallet V5 sign auth external', () => { const receipt1 = await walletV5.sendExternalSignedMessage(createBody(actionsList1)); const extensionsDict1 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict1.size).toEqual(1); expect(extensionsDict1.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); const actionsList2 = packActionsList([new ActionRemoveExtension(testExtension)]); const receipt2 = await walletV5.sendExternalSignedMessage(createBody(actionsList2)); const extensionsDict2 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); @@ -413,12 +413,12 @@ describe('Wallet V5 sign auth external', () => { accountForGas(receipt1.transactions); const extensionsDict1 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict1.size).toEqual(1); expect(extensionsDict1.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); const actionsList2 = packActionsList([new ActionAddExtension(testExtension)]); @@ -433,12 +433,12 @@ describe('Wallet V5 sign auth external', () => { const extensionsDict2 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict2.size).toEqual(1); expect(extensionsDict2.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); }); @@ -457,7 +457,7 @@ describe('Wallet V5 sign auth external', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(0); @@ -769,14 +769,14 @@ describe('Wallet V5 sign auth external', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(1); expect(extensionsDict.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); @@ -917,14 +917,14 @@ describe('Wallet V5 sign auth external', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(1); expect(extensionsDict.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); diff --git a/tests/wallet-v5-get.spec.ts b/tests/wallet-v5-get.spec.ts index cb36bea..e919724 100644 --- a/tests/wallet-v5-get.spec.ts +++ b/tests/wallet-v5-get.spec.ts @@ -112,16 +112,16 @@ describe('Wallet V5 get methods', () => { const extensions: Dictionary = Dictionary.empty( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8) + Dictionary.Values.BigInt(1) ); - extensions.set(packAddress(plugin1), BigInt(plugin1.workChain)); - extensions.set(packAddress(plugin2), BigInt(plugin2.workChain)); + extensions.set(packAddress(plugin1), -1n); + extensions.set(packAddress(plugin2), -1n); await deploy({ extensions }); const actual = await walletV5.getExtensions(); const expected = beginCell() - .storeDictDirect(extensions, Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(8)) + .storeDictDirect(extensions, Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(1)) .endCell(); expect(actual?.equals(expected)).toBeTruthy(); }); @@ -135,11 +135,11 @@ describe('Wallet V5 get methods', () => { const extensions: Dictionary = Dictionary.empty( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8) + Dictionary.Values.BigInt(1) ); - extensions.set(packAddress(plugin1), BigInt(plugin1.workChain)); - extensions.set(packAddress(plugin2), BigInt(plugin2.workChain)); - extensions.set(packAddress(plugin3), BigInt(plugin3.workChain)); + extensions.set(packAddress(plugin1), -1n); + extensions.set(packAddress(plugin2), -1n); + extensions.set(packAddress(plugin3), -1n); await deploy({ extensions }); diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index dc51b7c..ecc7f25 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -207,14 +207,14 @@ describe('Wallet V5 sign auth internal', () => { const extensions = await walletV5.getExtensions(); const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), extensions ); expect(extensionsDict.size).toEqual(1); const storedWC = extensionsDict.get(packAddress(testExtension)); - expect(storedWC).toEqual(BigInt(testExtension.workChain)); + expect(storedWC).toEqual(-1n); }); it('Send two transfers', async () => { @@ -304,17 +304,17 @@ describe('Wallet V5 sign auth internal', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(2); expect(extensionsDict.get(packAddress(testExtension1))).toEqual( - BigInt(testExtension1.workChain) + -1n ); expect(extensionsDict.get(packAddress(testExtension2))).toEqual( - BigInt(testExtension2.workChain) + -1n ); }); @@ -328,12 +328,12 @@ describe('Wallet V5 sign auth internal', () => { }); const extensionsDict1 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict1.size).toEqual(1); expect(extensionsDict1.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); const actionsList2 = packActionsList([new ActionRemoveExtension(testExtension)]); @@ -343,7 +343,7 @@ describe('Wallet V5 sign auth internal', () => { }); const extensionsDict2 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); @@ -402,12 +402,12 @@ describe('Wallet V5 sign auth internal', () => { }); const extensionsDict1 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict1.size).toEqual(1); expect(extensionsDict1.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); const actionsList2 = packActionsList([new ActionAddExtension(testExtension)]); @@ -425,12 +425,12 @@ describe('Wallet V5 sign auth internal', () => { const extensionsDict2 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict2.size).toEqual(1); expect(extensionsDict2.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); }); @@ -452,7 +452,7 @@ describe('Wallet V5 sign auth internal', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(0); @@ -922,14 +922,14 @@ describe('Wallet V5 sign auth internal', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(1); expect(extensionsDict.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); expect( @@ -965,14 +965,14 @@ describe('Wallet V5 sign auth internal', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(1); expect(extensionsDict.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); expect( @@ -1153,14 +1153,14 @@ describe('Wallet V5 sign auth internal', () => { const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), await walletV5.getExtensions() ); expect(extensionsDict.size).toEqual(1); expect(extensionsDict.get(packAddress(testExtension))).toEqual( - BigInt(testExtension.workChain) + -1n ); expect( diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index b62870e..648f6a9 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -28,7 +28,7 @@ export function walletV5ConfigToCell(config: WalletV5Config): Cell { .storeUint(config.seqno, 32) .storeUint(config.walletId, 80) .storeBuffer(config.publicKey, 32) - .storeDict(config.extensions, Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(8)) + .storeDict(config.extensions, Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(1)) .endCell(); } @@ -228,12 +228,12 @@ export class WalletV5 implements Contract { const dict: Dictionary = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(8), + Dictionary.Values.BigInt(1), extensions ); return dict.keys().map(key => { - const wc = dict.get(key)!; + const wc = 0n; const addressHex = key; return Address.parseRaw(`${wc}:${addressHex.toString(16).padStart(64, '0')}`); }); From 2f20a49f459aa6a5eec5b6588565776c26df372f Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 12:18:16 +0400 Subject: [PATCH 14/67] remove unnecessary micro-optimiazations --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 41b1cf8..b2ddf86 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021301000229000114ff00f4a413f4bcf2c80b0102012004020102f203011420d728239b4b3b74307f0f0201480e050201200706001bbe5f0f6a2684080b8eb90fa021840201200b080201200a090019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760d0c0012a880ed44d0d70a00b30018ab9ced44d08071d721d70bff028ed020c702dc01d0d60301c713dc01d72c232bc3a3748ea101fa4030fa44f828fa443058badded44d0810171d721f4058307f40edd3070db3c8e8c3120d72c239b4b73a431dd70e2100f01f08ef5eda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f910289602f26001f2a39e02945f09db31e001945f08db31e1e25122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd81002cc9401d200018edbd72c20e206dcfc2091709901d72c22f577a52412e25210b18e3b30d72c21065dcad48e2dd200ed44d0d200d31f5205953001f2ab709f02f26b01810150d721d70b00f2aa7fe2c8ca00cb1f58cf16c9ed5492f229e2e30dd74cd001e8d74c011211005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009601fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1050248307f45bf2a8206e5004b0f26c02e2c85003cf1612f400c9ed54c2158adf"} \ No newline at end of file +{"hex":"b5ee9c724102130100022b000114ff00f4a413f4bcf2c80b0102012004020102f203011420d728239b4b3b74307f0f0201480e050201200706001bbe5f0f6a2684080b8eb90fa021840201200b080201200a090019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760d0c0012a880ed44d0d70a00b30018ab9ced44d08071d721d70bff0292d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3c8e8c3120d72c239b4b73a431dd70e2100f01f08ef5eda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f910289602f26001f2a39e02945f09db31e001945f08db31e1e25122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd81002cc9401d200018edbd72c20e206dcfc2091709901d72c22f577a52412e25210b18e3b30d72c21065dcad48e2dd200ed44d0d200d31f5205953001f2ab709f02f26b01810150d721d70b00f2aa7fe2c8ca00cb1f58cf16c9ed5492f229e2e30dd74cd001e8d74c011211005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009601fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1050248307f45bf2a8206e5004b0f26c02e2c85003cf1612f400c9ed54b93c17e2"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 8196e97..651740b 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -29,8 +29,6 @@ const int size::address_hash_size = 256; () return_if(int condition) impure asm "IFRET"; () return_unless(int condition) impure asm "IFNOTRET"; -slice udict_get_or_return(cell dict, int key_length, int index) impure asm(index dict key_length) "DICTUGET" "IFNOTRET"; - (slice, int) begin_parse_raw(cell c) asm "XCTOS"; slice enforce_and_remove_signed_external_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; @@ -127,7 +125,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { } ;; Simply set the C5 register with all pre-computed actions after verification: set_c5_actions(cs.preload_ref().verify_c5_actions(is_external)); - return (); } ;; ------------------------------------------------------------------------------------------------ @@ -189,7 +186,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice body = in_msg_body; in_msg_body = enforce_and_remove_signed_external_prefix(in_msg_body); process_signed_request(body, true); - return(); } ;; ------------------------------------------------------------------------------------------------ @@ -219,7 +215,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). - extensions.udict_get_or_return(size::address_hash_size, sender_address_hash); + (_, int extension_found) = extensions.udict_get?(size::address_hash_size, sender_address_hash); + return_unless(extension_found); process_actions(in_msg_body, false); return (); @@ -231,8 +228,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { return_unless(is_signed_internal); process_signed_request(body, false); - return (); - } ;; ------------------------------------------------------------------------------------------------ From edef7977d48bf8432a1fb955aa64610fb4c53429 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 12:25:50 +0400 Subject: [PATCH 15/67] simplify --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 13 +++++-------- tests/wallet-v5-internal.spec.ts | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index b2ddf86..f4fdfbc 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102130100022b000114ff00f4a413f4bcf2c80b0102012004020102f203011420d728239b4b3b74307f0f0201480e050201200706001bbe5f0f6a2684080b8eb90fa021840201200b080201200a090019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760d0c0012a880ed44d0d70a00b30018ab9ced44d08071d721d70bff0292d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3c8e8c3120d72c239b4b73a431dd70e2100f01f08ef5eda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f910289602f26001f2a39e02945f09db31e001945f08db31e1e25122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd81002cc9401d200018edbd72c20e206dcfc2091709901d72c22f577a52412e25210b18e3b30d72c21065dcad48e2dd200ed44d0d200d31f5205953001f2ab709f02f26b01810150d721d70b00f2aa7fe2c8ca00cb1f58cf16c9ed5492f229e2e30dd74cd001e8d74c011211005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009601fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1050248307f45bf2a8206e5004b0f26c02e2c85003cf1612f400c9ed54b93c17e2"} \ No newline at end of file +{"hex":"b5ee9c7241021201000291000114ff00f4a413f4bcf2c80b01020120030201f8f220d728239b4b3b74307f8eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2605122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d040201200605001bbe5f0f6a2684080b8eb90fa021840201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0012a880ed44d0d70a00b30018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01fa3120d72c239b4b73a431dd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2605122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd80f02cc9401d200018edbd72c20e206dcfc2091709901d72c22f577a52412e25210b18e3b30d72c21065dcad48e2dd200ed44d0d200d31f5205953001f2ab709f02f26b01810150d721d70b00f2aa7fe2c8ca00cb1f58cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009601fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1050248307f45bf2a8206e5004b0f26c02e2c85003cf1612f400c9ed548a58b220"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 651740b..28a82c6 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -150,17 +150,14 @@ cell verify_c5_actions(cell c5, int is_external) inline { int public_key = data_slice.preload_uint(size::public_key); int is_signature_valid = check_signature(slice_hash(signed_slice), signature, public_key); - if (is_external) { - throw_if(error::signature_disabled, is_signature_disabled); - throw_unless( error::invalid_signature, is_signature_valid); - } else { - if (is_signature_disabled) { - return (); - } - ifnot (is_signature_valid) { + ifnot (is_signature_valid) { + if (is_external) { + throw_unless(error::invalid_signature, is_signature_valid); + } else { return (); } } + throw_if(error::signature_disabled, is_signature_disabled); throw_unless(error::invalid_seqno, seqno == stored_seqno); throw_unless(error::invalid_wallet_id, wallet_id == stored_wallet_id); throw_if(error::expired, valid_until <= now()); diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index ecc7f25..46c12a6 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -1193,7 +1193,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt2.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(0); + ).toEqual(32); expect(receipt2.transactions).not.toHaveTransaction({ from: walletV5.address, From 4f773954295bbd02727bf6147c630d932716799c Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 12:53:03 +0400 Subject: [PATCH 16/67] refactor signature_allowed --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 30 ++++++++++++++---------------- tests/wallet-v5-extensions.spec.ts | 2 +- tests/wallet-v5-external.spec.ts | 4 ++-- tests/wallet-v5-get.spec.ts | 2 +- tests/wallet-v5-internal.spec.ts | 4 ++-- wrappers/wallet-v5.ts | 4 ++-- 7 files changed, 23 insertions(+), 25 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index f4fdfbc..64b5712 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021201000291000114ff00f4a413f4bcf2c80b01020120030201f8f220d728239b4b3b74307f8eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2605122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d040201200605001bbe5f0f6a2684080b8eb90fa021840201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0012a880ed44d0d70a00b30018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01fa3120d72c239b4b73a431dd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2605122baf2a15036baf2a2f823bbf2642292f800dea470c8ca00cb1f01cf16c9ed54f80fdb3cd80f02cc9401d200018edbd72c20e206dcfc2091709901d72c22f577a52412e25210b18e3b30d72c21065dcad48e2dd200ed44d0d200d31f5205953001f2ab709f02f26b01810150d721d70b00f2aa7fe2c8ca00cb1f58cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009601fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1050248307f45bf2a8206e5004b0f26c02e2c85003cf1612f400c9ed548a58b220"} \ No newline at end of file +{"hex":"b5ee9c724102120100028b000114ff00f4a413f4bcf2c80b01020120030201f8f220d728239b4b3b74307f8eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d040201200605001bbe5f0f6a2684080b8eb90fa021840201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01fa3120d72c239b4b73a431dd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d20052049430f26b7f9e01f2ab810150d721d70b00f2aa70e2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5435df666e"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 28a82c6..b7e3ccd 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -79,7 +79,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice data_slice = get_data().begin_parse(); slice data_slice_before_extensions = data_slice~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key); - int is_signature_disabled = data_slice_before_extensions.preload_int(size::bool); + int is_signature_allowed = data_slice_before_extensions.preload_int(size::bool); cell extensions = data_slice.preload_dict(); ;; Add extension @@ -89,7 +89,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { } else { ;; Remove extension (extensions, int is_success) = extensions.udict_delete?(size::address_hash_size, address_hash); throw_unless(error::remove_extension_error, is_success); - throw_if(error::disable_signature_when_extensions_is_empty, null?(extensions) & is_signature_disabled); + throw_if(error::disable_signature_when_extensions_is_empty, null?(extensions) & (~ is_signature_allowed)); } set_data(begin_cell() @@ -100,22 +100,20 @@ cell verify_c5_actions(cell c5, int is_external) inline { } elseif (cs~check_and_remove_set_signature_allowed_prefix()) { int allow_signature = cs~load_int(1); slice data_slice = get_data().begin_parse(); - int is_signature_disabled = data_slice~load_int(size::bool); - int stored_seqno = data_slice~load_uint(size::seqno); - slice data_tail = data_slice; ;; wallet_id, public_key, extensions + int is_signature_allowed = data_slice~load_int(size::bool); + slice data_tail = data_slice; ;; seqno, wallet_id, public_key, extensions if (allow_signature) { ;; allow - throw_unless(error::this_signature_mode_already_set, is_signature_disabled); - is_signature_disabled = false; + throw_if(error::this_signature_mode_already_set, is_signature_allowed); + is_signature_allowed = true; } else { ;; disallow - throw_if(error::this_signature_mode_already_set, is_signature_disabled); - data_slice = data_slice.skip_bits(size::wallet_id + size::public_key); + throw_unless(error::this_signature_mode_already_set, is_signature_allowed); + data_slice = data_slice.skip_bits(size::seqno + size::wallet_id + size::public_key); int is_extensions_not_empty = data_slice.preload_uint(1); throw_unless(error::remove_last_extension_when_signature_disabled, is_extensions_not_empty); - is_signature_disabled = true; + is_signature_allowed = false; } set_data(begin_cell() - .store_int(is_signature_disabled, size::bool) - .store_uint(stored_seqno, size::seqno) + .store_int(is_signature_allowed, size::bool) .store_slice(data_tail) ;; wallet_id, public_key, extensions .end_cell()); } else { @@ -142,7 +140,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { (int wallet_id, int valid_until, int seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); slice data_slice = get_data().begin_parse(); - int is_signature_disabled = data_slice~load_int(size::bool); + int is_signature_allowed = data_slice~load_int(size::bool); int stored_seqno = data_slice~load_uint(size::seqno); slice data_tail = data_slice; ;; wallet_id, public_key, extensions @@ -157,7 +155,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { return (); } } - throw_if(error::signature_disabled, is_signature_disabled); + throw_unless(error::signature_disabled, is_signature_allowed); throw_unless(error::invalid_seqno, seqno == stored_seqno); throw_unless(error::invalid_wallet_id, wallet_id == stored_wallet_id); throw_if(error::expired, valid_until <= now()); @@ -169,7 +167,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Store and commit the seqno increment to prevent replays even if the subsequent requests fail. stored_seqno = stored_seqno + 1; set_data(begin_cell() - .store_int(false, size::bool) + .store_int(true, size::bool) ;; is_signature_allowed .store_uint(stored_seqno, size::seqno) .store_slice(data_tail) ;; wallet_id, public_key, extensions .end_cell()); @@ -250,5 +248,5 @@ cell get_extensions() method_id { } int get_is_signature_auth_allowed() method_id { - return ~ get_data().begin_parse().preload_int(size::bool); + return get_data().begin_parse().preload_int(size::bool); } diff --git a/tests/wallet-v5-extensions.spec.ts b/tests/wallet-v5-extensions.spec.ts index 0d5e331..d4aa8e9 100644 --- a/tests/wallet-v5-extensions.spec.ts +++ b/tests/wallet-v5-extensions.spec.ts @@ -68,7 +68,7 @@ describe('Wallet V5 extensions auth', () => { walletV5 = blockchain.openContract( WalletV5.createFromConfig( { - signature_auth_disabled: false, + signatureAllowed: true, seqno: 0, walletId: WALLET_ID.serialized, publicKey: keypair.publicKey, diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index 1e22499..a71ed3f 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -58,7 +58,7 @@ describe('Wallet V5 sign auth external', () => { const _walletV5 = blockchain.openContract( WalletV5.createFromConfig( { - signature_auth_disabled: params?.signature_auth_disabled ?? false, + signatureAllowed: true, seqno: params?.seqno ?? 0, walletId: params?.walletId ?? WALLET_ID.serialized, publicKey: params?.publicKey ?? _keypair.publicKey, @@ -101,7 +101,7 @@ describe('Wallet V5 sign auth external', () => { walletV5 = blockchain.openContract( WalletV5.createFromConfig( { - signature_auth_disabled: false, + signatureAllowed: true, seqno: 0, walletId: WALLET_ID.serialized, publicKey: keypair.publicKey, diff --git a/tests/wallet-v5-get.spec.ts b/tests/wallet-v5-get.spec.ts index e919724..d6f3251 100644 --- a/tests/wallet-v5-get.spec.ts +++ b/tests/wallet-v5-get.spec.ts @@ -31,7 +31,7 @@ describe('Wallet V5 get methods', () => { walletV5 = blockchain.openContract( WalletV5.createFromConfig( { - signature_auth_disabled: params?.signature_auth_disabled ?? false, + signatureAllowed: true, seqno: params?.seqno ?? 0, walletId: params?.walletId ?? WALLET_ID.serialized, publicKey: params?.publicKey ?? keypair.publicKey, diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index 46c12a6..6e1e3ff 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -51,7 +51,7 @@ describe('Wallet V5 sign auth internal', () => { const _walletV5 = blockchain.openContract( WalletV5.createFromConfig( { - signature_auth_disabled: params?.signature_auth_disabled ?? false, + signatureAllowed: true, seqno: params?.seqno ?? 0, walletId: params?.walletId ?? WALLET_ID.serialized, publicKey: params?.publicKey ?? _keypair.publicKey, @@ -94,7 +94,7 @@ describe('Wallet V5 sign auth internal', () => { walletV5 = blockchain.openContract( WalletV5.createFromConfig( { - signature_auth_disabled: false, + signatureAllowed: true, seqno: 0, walletId: WALLET_ID.serialized, publicKey: keypair.publicKey, diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index 648f6a9..9c55ad8 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -15,7 +15,7 @@ import { import { bufferToBigInt } from '../tests/utils'; export type WalletV5Config = { - signature_auth_disabled: boolean; + signatureAllowed: boolean; seqno: number; walletId: bigint; publicKey: Buffer; @@ -24,7 +24,7 @@ export type WalletV5Config = { export function walletV5ConfigToCell(config: WalletV5Config): Cell { return beginCell() - .storeBit(config.signature_auth_disabled) + .storeBit(config.signatureAllowed) .storeUint(config.seqno, 32) .storeUint(config.walletId, 80) .storeBuffer(config.publicKey, 32) From 9869feffa55d72046e24b3942a0a16842160d8d0 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 13:01:00 +0400 Subject: [PATCH 17/67] preload_dict --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 64b5712..32b3108 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100028b000114ff00f4a413f4bcf2c80b01020120030201f8f220d728239b4b3b74307f8eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d040201200605001bbe5f0f6a2684080b8eb90fa021840201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01fa3120d72c239b4b73a431dd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d20052049430f26b7f9e01f2ab810150d721d70b00f2aa70e2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5435df666e"} \ No newline at end of file +{"hex":"b5ee9c724102120100028a000114ff00f4a413f4bcf2c80b01020120030201f8f220d728239b4b3b74307f8eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01fa3120d72c239b4b73a431dd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d20052049430f26b7f9e01f2ab810170d721d70b00f2aa70e2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5450b275e0"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index b7e3ccd..cf6ecb3 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -31,6 +31,8 @@ const int size::address_hash_size = 256; (slice, int) begin_parse_raw(cell c) asm "XCTOS"; +cell preload_dict(slice s) asm "PLDDICT"; + slice enforce_and_remove_signed_external_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; (slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; (slice, int) check_and_remove_signed_internal_prefix(slice body) impure asm "x{73696E74} SDBEGINSQ"; @@ -244,7 +246,7 @@ int get_public_key() method_id { ;; Returns raw dictionary (or null if empty) where keys are address hashes. Workchains of extensions are same with wallet smart contract workchain cell get_extensions() method_id { slice data_slice = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key); - return data_slice~load_dict(); + return data_slice.preload_dict(); } int get_is_signature_auth_allowed() method_id { From 5a218427d285ac65afc2e806d40d2a4fa2aa2f10 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 13:03:21 +0400 Subject: [PATCH 18/67] get-methods cosmetic --- contracts/wallet_v5.fc | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index cf6ecb3..af00864 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -230,25 +230,32 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; ------------------------------------------------------------------------------------------------ ;; Get methods +int get_is_signature_auth_allowed() method_id { + return get_data().begin_parse() + .preload_int(size::bool); +} + int seqno() method_id { - return get_data().begin_parse().skip_bits(size::bool).preload_uint(size::seqno); + return get_data().begin_parse() + .skip_bits(size::bool) + .preload_uint(size::seqno); } int get_wallet_id() method_id { - return get_data().begin_parse().skip_bits(size::bool + size::seqno).preload_uint(size::wallet_id); + return get_data().begin_parse() + .skip_bits(size::bool + size::seqno) + .preload_uint(size::wallet_id); } int get_public_key() method_id { - slice data_slice = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id); - return data_slice.preload_uint(size::public_key); + return get_data().begin_parse() + .skip_bits(size::bool + size::seqno + size::wallet_id) + .preload_uint(size::public_key); } ;; Returns raw dictionary (or null if empty) where keys are address hashes. Workchains of extensions are same with wallet smart contract workchain cell get_extensions() method_id { - slice data_slice = get_data().begin_parse().skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key); - return data_slice.preload_dict(); -} - -int get_is_signature_auth_allowed() method_id { - return get_data().begin_parse().preload_int(size::bool); -} + return get_data().begin_parse() + .skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key) + .preload_dict(); +} \ No newline at end of file From ad1d90b57842ec404a872a3ba32474377d640c3d Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 13:12:09 +0400 Subject: [PATCH 19/67] simplify --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 32b3108..e96ea13 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100028a000114ff00f4a413f4bcf2c80b01020120030201f8f220d728239b4b3b74307f8eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01fa3120d72c239b4b73a431dd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d20052049430f26b7f9e01f2ab810170d721d70b00f2aa70e2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5450b275e0"} \ No newline at end of file +{"hex":"b5ee9c7241021401000296000114ff00f4a413f4bcf2c80b0102012005020102f203011c20d70b1f82107369676ebaf2af7f0401e28eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd8110201480f0602012008070019be5f0f6a2684080b8eb90fa02c0201200c090201200b0a0019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760e0d0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e111001fe3120d70b1f821073696e74badd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd81102c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d20052049430f26b7f9e01f2ab810170d721d70b00f2aa70e2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011312005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5434d7f9d7"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index af00864..ad96ac6 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -15,6 +15,7 @@ const int error::invalid_signature = 35; const int error::invalid_seqno = 33; const int error::invalid_wallet_id = 34; const int error::expired = 36; +const int error::invalid_message_type = 47; const int size::bool = 1; const int size::seqno = 32; @@ -26,16 +27,15 @@ const int size::signature = 512; const int size::message_type_prefix = 32; const int size::address_hash_size = 256; +const int signed_external = 0x7369676E; +const int signed_internal = 0x73696E74; + () return_if(int condition) impure asm "IFRET"; () return_unless(int condition) impure asm "IFNOTRET"; (slice, int) begin_parse_raw(cell c) asm "XCTOS"; -cell preload_dict(slice s) asm "PLDDICT"; - -slice enforce_and_remove_signed_external_prefix(slice body) impure asm "x{7369676E} SDBEGINS"; (slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; -(slice, int) check_and_remove_signed_internal_prefix(slice body) impure asm "x{73696E74} SDBEGINSQ"; (slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{1c40db9f} SDBEGINSQ"; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{5eaef4a4} SDBEGINSQ"; @@ -180,9 +180,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { } () recv_external(slice in_msg_body) impure inline { - slice body = in_msg_body; - in_msg_body = enforce_and_remove_signed_external_prefix(in_msg_body); - process_signed_request(body, true); + throw_unless(error::invalid_message_type, in_msg_body.preload_uint(size::message_type_prefix) == signed_external); + process_signed_request(in_msg_body, true); } ;; ------------------------------------------------------------------------------------------------ @@ -220,11 +219,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { } - slice body = in_msg_body; - (_, int is_signed_internal) = check_and_remove_signed_internal_prefix(in_msg_body); - return_unless(is_signed_internal); - - process_signed_request(body, false); + return_unless(in_msg_body.preload_uint(size::message_type_prefix) == signed_internal); + process_signed_request(in_msg_body, false); } ;; ------------------------------------------------------------------------------------------------ From 2e31a01f9ee40240b6a39717342dc412f2677329 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 13:39:59 +0400 Subject: [PATCH 20/67] simplify --- contracts/wallet_v5.fc | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index ad96ac6..cb38bbf 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -43,15 +43,15 @@ const int signed_internal = 0x73696E74; slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; -;; Stores pre-computed list of actions (mostly `action_send_msg`) in the actions register. -() set_c5_actions(cell action_list) impure asm "c5 POP"; - int count_trailing_zeroes(slice cs) asm "SDCNTTRAIL0"; int count_trailing_ones(slice cs) asm "SDCNTTRAIL1"; slice get_last_bits(slice s, int n) asm "SDCUTLAST"; slice remove_last_bits(slice s, int n) asm "SDSKIPLAST"; +;; Stores pre-computed list of actions (mostly `action_send_msg`) in the actions register. +() set_c5_actions(cell action_list) impure asm "c5 POP"; + cell verify_c5_actions(cell c5, int is_external) inline { (slice cs, _) = c5.begin_parse_raw(); return_if(cs.slice_empty?()); @@ -103,17 +103,15 @@ cell verify_c5_actions(cell c5, int is_external) inline { int allow_signature = cs~load_int(1); slice data_slice = get_data().begin_parse(); int is_signature_allowed = data_slice~load_int(size::bool); + throw_if(error::this_signature_mode_already_set, is_signature_allowed == allow_signature); + is_signature_allowed = allow_signature; + slice data_tail = data_slice; ;; seqno, wallet_id, public_key, extensions - if (allow_signature) { ;; allow - throw_if(error::this_signature_mode_already_set, is_signature_allowed); - is_signature_allowed = true; - } else { ;; disallow - throw_unless(error::this_signature_mode_already_set, is_signature_allowed); - data_slice = data_slice.skip_bits(size::seqno + size::wallet_id + size::public_key); - int is_extensions_not_empty = data_slice.preload_uint(1); + ifnot (allow_signature) { ;; disallow + int is_extensions_not_empty = data_slice.skip_bits(size::seqno + size::wallet_id + size::public_key).preload_uint(1); throw_unless(error::remove_last_extension_when_signature_disabled, is_extensions_not_empty); - is_signature_allowed = false; } + set_data(begin_cell() .store_int(is_signature_allowed, size::bool) .store_slice(data_tail) ;; wallet_id, public_key, extensions @@ -190,9 +188,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { return_if(in_msg_body.slice_refs_empty?()); ;; message with actions always have a ref slice in_msg_full_slice = in_msg_full.begin_parse(); - slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - ;; skip bounced messages - if bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. return_if(count_trailing_ones(message_flags_slice)); @@ -206,8 +202,9 @@ cell verify_c5_actions(cell c5, int is_external) inline { return_unless(my_address_wc == sender_address_wc); - slice data_slice = get_data().begin_parse(); - cell extensions = data_slice.skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key).preload_dict(); + cell extensions = get_data().begin_parse() + .skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key) + .preload_dict(); ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). From 5424092acd0ef857ece3d848f40ca87767477daa Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 14:09:42 +0400 Subject: [PATCH 21/67] c5 comments --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 24 ++++++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index e96ea13..339a04b 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021401000296000114ff00f4a413f4bcf2c80b0102012005020102f203011c20d70b1f82107369676ebaf2af7f0401e28eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd8110201480f0602012008070019be5f0f6a2684080b8eb90fa02c0201200c090201200b0a0019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760e0d0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e111001fe3120d70b1f821073696e74badd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd81102c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d20052049430f26b7f9e01f2ab810170d721d70b00f2aa70e2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011312005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5434d7f9d7"} \ No newline at end of file +{"hex":"b5ee9c7241021401000296000114ff00f4a413f4bcf2c80b0102012005020102f203011c20d70b1f82107369676ebaf2af7f0401e28eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd8110201480f0602012008070019be5f0f6a2684080b8eb90fa02c0201200c090201200b0a0019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760e0d0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e111001fe3120d70b1f821073696e74badd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd81102c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70b00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011312005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5481665378"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index cb38bbf..bea3f11 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -33,23 +33,28 @@ const int signed_internal = 0x73696E74; () return_if(int condition) impure asm "IFRET"; () return_unless(int condition) impure asm "IFNOTRET"; -(slice, int) begin_parse_raw(cell c) asm "XCTOS"; - (slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; (slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{1c40db9f} SDBEGINSQ"; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{5eaef4a4} SDBEGINSQ"; (slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{20cbb95a} SDBEGINSQ"; -slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; - int count_trailing_zeroes(slice cs) asm "SDCNTTRAIL0"; int count_trailing_ones(slice cs) asm "SDCNTTRAIL1"; slice get_last_bits(slice s, int n) asm "SDCUTLAST"; slice remove_last_bits(slice s, int n) asm "SDSKIPLAST"; -;; Stores pre-computed list of actions (mostly `action_send_msg`) in the actions register. + +(slice, int) begin_parse_raw(cell c) asm "XCTOS"; + +;; `action_send_msg` has 0x0ec3c86d prefix +;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380 +slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; + +;; Put raw list of OutActions to C5 register. +;; OutList TLB-schema - https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L378 +;; C5 register - https://docs.ton.org/tvm.pdf, page 11 () set_c5_actions(cell action_list) impure asm "c5 POP"; cell verify_c5_actions(cell c5, int is_external) inline { @@ -57,10 +62,14 @@ cell verify_c5_actions(cell c5, int is_external) inline { return_if(cs.slice_empty?()); do { - ;; only send_msg is allowed, set_code or reserve_currency are not + ;; only `action_send_msg` is allowed, `action_set_code` or `action_reserve_currency` are not cs = cs.enforce_and_remove_action_send_msg_prefix(); - ;; enforce that send_mode has 2 bit set, for that load 7 bits and make sure that they end with 1 + + ;; action_send_msg#0ec3c86d mode:(## 8) out_msg:^(MessageRelaxed Any) = OutAction; + ;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380 + ;; enforce that send_mode has +2 bit (ignore errors) set for external message. For that load 7 bits and make sure that they end with 1 throw_if(error::invalid_c5_action, is_external & count_trailing_zeroes(cs.preload_bits(7))); + (cs, _) = cs.preload_ref().begin_parse_raw(); } until (cs.slice_empty?()); @@ -141,7 +150,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice data_slice = get_data().begin_parse(); int is_signature_allowed = data_slice~load_int(size::bool); - int stored_seqno = data_slice~load_uint(size::seqno); slice data_tail = data_slice; ;; wallet_id, public_key, extensions int stored_wallet_id = data_slice~load_uint(size::wallet_id); From b3d712dfb2365727a92303d3a1325f3432b1a057 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 14:12:39 +0400 Subject: [PATCH 22/67] fix size check --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 339a04b..e39b697 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021401000296000114ff00f4a413f4bcf2c80b0102012005020102f203011c20d70b1f82107369676ebaf2af7f0401e28eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd8110201480f0602012008070019be5f0f6a2684080b8eb90fa02c0201200c090201200b0a0019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760e0d0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e111001fe3120d70b1f821073696e74badd708eeeeda2edfb209821d7498102b2b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd81102c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70b00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011312005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5481665378"} \ No newline at end of file +{"hex":"b5ee9c7241021401000296000114ff00f4a413f4bcf2c80b0102012005020102f203011c20d70b1f82107369676ebaf2af7f0401e28eeeeda2edfb209821d7498102b1b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd8110201480f0602012008070019be5f0f6a2684080b8eb90fa02c0201200c090201200b0a0019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760e0d0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e111001fe3120d70b1f821073696e74badd708eeeeda2edfb209821d7498102b1b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd81102c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70b00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011312005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5422d94c60"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index bea3f11..eade0a7 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -139,7 +139,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { () process_signed_request(slice in_msg_body, int is_external) impure inline { ifnot (is_external) { ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(in_msg_body.slice_bits() < size::message_type_prefix + size::bool + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); + return_if(in_msg_body.slice_bits() < size::message_type_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); } slice signature = in_msg_body.get_last_bits(size::signature); From 520dec7cf700583adeb071ec21c1c92e54053762 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 14:14:42 +0400 Subject: [PATCH 23/67] fix error naming --- contracts/wallet_v5.fc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index eade0a7..7dbba04 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -6,9 +6,9 @@ const int error::invalid_c5_action = 37; const int error::extension_wrong_workchain = 45; const int error::add_extension_error = 39; const int error::remove_extension_error = 40; -const int error::disable_signature_when_extensions_is_empty = 44; +const int error::disable_signature_when_extensions_is_empty = 42; const int error::this_signature_mode_already_set = 43; -const int error::remove_last_extension_when_signature_disabled = 42; +const int error::remove_last_extension_when_signature_disabled = 44; const int error::unspported_action = 41; const int error::signature_disabled = 32; const int error::invalid_signature = 35; @@ -100,7 +100,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { } else { ;; Remove extension (extensions, int is_success) = extensions.udict_delete?(size::address_hash_size, address_hash); throw_unless(error::remove_extension_error, is_success); - throw_if(error::disable_signature_when_extensions_is_empty, null?(extensions) & (~ is_signature_allowed)); + throw_if(error::remove_last_extension_when_signature_disabled, null?(extensions) & (~ is_signature_allowed)); } set_data(begin_cell() @@ -118,12 +118,12 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice data_tail = data_slice; ;; seqno, wallet_id, public_key, extensions ifnot (allow_signature) { ;; disallow int is_extensions_not_empty = data_slice.skip_bits(size::seqno + size::wallet_id + size::public_key).preload_uint(1); - throw_unless(error::remove_last_extension_when_signature_disabled, is_extensions_not_empty); + throw_unless(error::disable_signature_when_extensions_is_empty, is_extensions_not_empty); } set_data(begin_cell() .store_int(is_signature_allowed, size::bool) - .store_slice(data_tail) ;; wallet_id, public_key, extensions + .store_slice(data_tail) ;; seqno, wallet_id, public_key, extensions .end_cell()); } else { throw(error::unspported_action); From 243d7d200d0cdf5e9603c413e2e99954afdd198a Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 14:18:06 +0400 Subject: [PATCH 24/67] simplify --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index e39b697..82acd73 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021401000296000114ff00f4a413f4bcf2c80b0102012005020102f203011c20d70b1f82107369676ebaf2af7f0401e28eeeeda2edfb209821d7498102b1b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd8110201480f0602012008070019be5f0f6a2684080b8eb90fa02c0201200c090201200b0a0019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760e0d0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e111001fe3120d70b1f821073696e74badd708eeeeda2edfb209821d7498102b1b9dcdf218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd81102c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70b00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011312005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed5422d94c60"} \ No newline at end of file +{"hex":"b5ee9c7241021201000282000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70b00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed549bc93548"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 7dbba04..61eccad 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -137,11 +137,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; ------------------------------------------------------------------------------------------------ () process_signed_request(slice in_msg_body, int is_external) impure inline { - ifnot (is_external) { - ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(in_msg_body.slice_bits() < size::message_type_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); - } - slice signature = in_msg_body.get_last_bits(size::signature); slice signed_slice = in_msg_body.remove_last_bits(size::signature); @@ -225,6 +220,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { } return_unless(in_msg_body.preload_uint(size::message_type_prefix) == signed_internal); + ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) + return_if(in_msg_body.slice_bits() < size::message_type_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); process_signed_request(in_msg_body, false); } From a39b7b09091c4dac2b723a488707381721532920 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 14:25:53 +0400 Subject: [PATCH 25/67] simplify --- contracts/wallet_v5.fc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 61eccad..bfd012c 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -195,9 +195,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; skip bounced messages - if bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. return_if(count_trailing_ones(message_flags_slice)); - (in_msg_body, int is_extension_action) = check_and_remove_extension_action_prefix(in_msg_body); - - if (is_extension_action) { + if (in_msg_body~check_and_remove_extension_action_prefix()) { ;; Authenticate extension by its address. (int sender_address_wc, int sender_address_hash) = parse_std_addr(in_msg_full_slice~load_msg_addr()); From fe128dde8bfb8eb658790d4e7baa3f5945f5b801 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 21 Jun 2024 14:33:23 +0400 Subject: [PATCH 26/67] cosmetic-optimization --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 82acd73..06ccffc 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021201000282000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70b00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55009801fa4001fa44f828fa443058baf2aded44d0810171d71821d70a0001f405059d307fc8ca0040048307f453f2a78e1150248307f45bf2a8206e04b314b0f26c02e2c85003cf1612f400c9ed549bc93548"} \ No newline at end of file +{"hex":"b5ee9c724102120100027d000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70b00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed543fe9ade5"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index bfd012c..8421606 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -90,7 +90,6 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice data_slice = get_data().begin_parse(); slice data_slice_before_extensions = data_slice~load_bits(size::bool + size::seqno + size::wallet_id + size::public_key); - int is_signature_allowed = data_slice_before_extensions.preload_int(size::bool); cell extensions = data_slice.preload_dict(); ;; Add extension @@ -100,6 +99,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { } else { ;; Remove extension (extensions, int is_success) = extensions.udict_delete?(size::address_hash_size, address_hash); throw_unless(error::remove_extension_error, is_success); + int is_signature_allowed = data_slice_before_extensions.preload_int(size::bool); throw_if(error::remove_last_extension_when_signature_disabled, null?(extensions) & (~ is_signature_allowed)); } From ab0233d08f811facafac7b09413e25a8f6a17aba Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Sat, 22 Jun 2024 12:41:14 +0400 Subject: [PATCH 27/67] explicit () in binary operations --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 06ccffc..7359f89 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100027d000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9102091309928945f0adb31e1f2a3e201f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70b00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d7393020c700dc8e1ad72820761e436c20d71d06c7125220b0f265d74cd7393020c700e65bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed543fe9ade5"} \ No newline at end of file +{"hex":"b5ee9c724102120100027b000114ff00f4a413f4bcf2c80b01020120030201e4f220d70b1f82107369676ebaf2af7f8ee0eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff027cd020c702dc01d0d60301c713c200dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f23120d70b1f821073696e74badd20d7498102b1b9dc708ee0eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005421d7393020c700dc8e1cd72820761e436c20d71d06c712c2005220b0f265d74cd7393020c700e65bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed548c2eefa4"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 8421606..69c819b 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -39,11 +39,15 @@ const int signed_internal = 0x73696E74; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{5eaef4a4} SDBEGINSQ"; (slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{20cbb95a} SDBEGINSQ"; -int count_trailing_zeroes(slice cs) asm "SDCNTTRAIL0"; -int count_trailing_ones(slice cs) asm "SDCNTTRAIL1"; +;;; returns the number of trailing zeroes in slice s. +int count_trailing_zeroes(slice s) asm "SDCNTTRAIL0"; +;;; returns the number of trailing ones in slice s. +int count_trailing_ones(slice s) asm "SDCNTTRAIL1"; -slice get_last_bits(slice s, int n) asm "SDCUTLAST"; -slice remove_last_bits(slice s, int n) asm "SDSKIPLAST"; +;;; returns the last 0 ≤ l ≤ 1023 bits of s. +slice get_last_bits(slice s, int l) asm "SDCUTLAST"; +;;; returns all but the last 0 ≤ l ≤ 1023 bits of s. +slice remove_last_bits(slice s, int l) asm "SDSKIPLAST"; (slice, int) begin_parse_raw(cell c) asm "XCTOS"; @@ -68,7 +72,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; action_send_msg#0ec3c86d mode:(## 8) out_msg:^(MessageRelaxed Any) = OutAction; ;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380 ;; enforce that send_mode has +2 bit (ignore errors) set for external message. For that load 7 bits and make sure that they end with 1 - throw_if(error::invalid_c5_action, is_external & count_trailing_zeroes(cs.preload_bits(7))); + throw_if(error::invalid_c5_action, is_external & (count_trailing_zeroes(cs.preload_bits(7)) > 0)); (cs, _) = cs.preload_ref().begin_parse_raw(); } until (cs.slice_empty?()); @@ -117,7 +121,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice data_tail = data_slice; ;; seqno, wallet_id, public_key, extensions ifnot (allow_signature) { ;; disallow - int is_extensions_not_empty = data_slice.skip_bits(size::seqno + size::wallet_id + size::public_key).preload_uint(1); + int is_extensions_not_empty = data_slice.skip_bits(size::seqno + size::wallet_id + size::public_key).preload_int(1); throw_unless(error::disable_signature_when_extensions_is_empty, is_extensions_not_empty); } @@ -153,7 +157,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { int is_signature_valid = check_signature(slice_hash(signed_slice), signature, public_key); ifnot (is_signature_valid) { if (is_external) { - throw_unless(error::invalid_signature, is_signature_valid); + throw(error::invalid_signature); } else { return (); } @@ -193,7 +197,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice in_msg_full_slice = in_msg_full.begin_parse(); slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool ;; skip bounced messages - if bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. - return_if(count_trailing_ones(message_flags_slice)); + return_if(count_trailing_ones(message_flags_slice) > 0); if (in_msg_body~check_and_remove_extension_action_prefix()) { From 477bf6d0175e88904188ff28ddec6ebf6c2f8500 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Sat, 22 Jun 2024 12:48:11 +0400 Subject: [PATCH 28/67] cosmetic --- contracts/wallet_v5.fc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 69c819b..e520647 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -27,11 +27,13 @@ const int size::signature = 512; const int size::message_type_prefix = 32; const int size::address_hash_size = 256; -const int signed_external = 0x7369676E; -const int signed_internal = 0x73696E74; +const int prefix::signed_external = 0x7369676E; +const int prefix::signed_internal = 0x73696E74; -() return_if(int condition) impure asm "IFRET"; -() return_unless(int condition) impure asm "IFNOTRET"; +;;; performs a RET, but only if integer f is non-zero. If f is a NaN, throws an integer overflow exception. +() return_if(int f) impure asm "IFRET"; +;;; performs a RET, but only if integer f is zero +() return_unless(int f) impure asm "IFNOTRET"; (slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; @@ -144,7 +146,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice signature = in_msg_body.get_last_bits(size::signature); slice signed_slice = in_msg_body.remove_last_bits(size::signature); - slice cs = signed_slice.skip_bits(size::message_type_prefix); ;; skip signed_internal or signer_external prefix + slice cs = signed_slice.skip_bits(size::message_type_prefix); ;; skip signed_internal or signed_external prefix (int wallet_id, int valid_until, int seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); slice data_slice = get_data().begin_parse(); @@ -185,7 +187,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { } () recv_external(slice in_msg_body) impure inline { - throw_unless(error::invalid_message_type, in_msg_body.preload_uint(size::message_type_prefix) == signed_external); + throw_unless(error::invalid_message_type, in_msg_body.preload_uint(size::message_type_prefix) == prefix::signed_external); process_signed_request(in_msg_body, true); } @@ -221,7 +223,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { } - return_unless(in_msg_body.preload_uint(size::message_type_prefix) == signed_internal); + return_unless(in_msg_body.preload_uint(size::message_type_prefix) == prefix::signed_internal); ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) return_if(in_msg_body.slice_bits() < size::message_type_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); process_signed_request(in_msg_body, false); From 029e60a7009eb5d3887425eff3296d598d07fd9f Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Sat, 22 Jun 2024 12:56:16 +0400 Subject: [PATCH 29/67] fix return --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 7359f89..cc3eeca 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100027b000114ff00f4a413f4bcf2c80b01020120030201e4f220d70b1f82107369676ebaf2af7f8ee0eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff027cd020c702dc01d0d60301c713c200dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f23120d70b1f821073696e74badd20d7498102b1b9dc708ee0eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005421d7393020c700dc8e1cd72820761e436c20d71d06c712c2005220b0f265d74cd7393020c700e65bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed548c2eefa4"} \ No newline at end of file +{"hex":"b5ee9c724102120100027f000114ff00f4a413f4bcf2c80b01020120030201e4f220d70b1f82107369676ebaf2af7f8ee0eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff027cd020c702dc01d0d60301c713c200dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f23120d70b1f821073696e74badd20d7498102b1b9dc708ee0eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005c21d7393020c700915b8e208e1cd72820761e436c20d71d06c712c2005220b0f265d74cd7393020c700e65be2ed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54dd2a9a2b"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index e520647..34343af 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -65,7 +65,9 @@ slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c8 cell verify_c5_actions(cell c5, int is_external) inline { (slice cs, _) = c5.begin_parse_raw(); - return_if(cs.slice_empty?()); + if (cs.slice_empty?()) { + return c5; + } do { ;; only `action_send_msg` is allowed, `action_set_code` or `action_reserve_currency` are not From 59108961442ac43f42f462a62581d28ad76f479f Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Sat, 22 Jun 2024 14:00:17 +0400 Subject: [PATCH 30/67] simplify --- contracts/wallet_v5.fc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 34343af..4528a5d 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -65,11 +65,8 @@ slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c8 cell verify_c5_actions(cell c5, int is_external) inline { (slice cs, _) = c5.begin_parse_raw(); - if (cs.slice_empty?()) { - return c5; - } - do { + while (~ cs.slice_empty?()) { ;; only `action_send_msg` is allowed, `action_set_code` or `action_reserve_currency` are not cs = cs.enforce_and_remove_action_send_msg_prefix(); @@ -79,7 +76,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { throw_if(error::invalid_c5_action, is_external & (count_trailing_zeroes(cs.preload_bits(7)) > 0)); (cs, _) = cs.preload_ref().begin_parse_raw(); - } until (cs.slice_empty?()); + } return c5; } @@ -201,7 +198,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice in_msg_full_slice = in_msg_full.begin_parse(); slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool ;; skip bounced messages - if bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. - return_if(count_trailing_ones(message_flags_slice) > 0); + return_if(count_trailing_ones(message_flags_slice)); if (in_msg_body~check_and_remove_extension_action_prefix()) { From 2ba7fdaea517e36978ca20375a9e429d1970f6c1 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Sat, 22 Jun 2024 14:06:27 +0400 Subject: [PATCH 31/67] commit only for external --- contracts/wallet_v5.fc | 4 +++- tests/wallet-v5-internal.spec.ts | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 4528a5d..6e2c3cd 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -180,7 +180,9 @@ cell verify_c5_actions(cell c5, int is_external) inline { .store_slice(data_tail) ;; wallet_id, public_key, extensions .end_cell()); - commit(); + if (is_external) { + commit(); + } process_actions(cs, is_external); } diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index 6e1e3ff..e0eea79 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -865,7 +865,7 @@ describe('Wallet V5 sign auth internal', () => { body: createBody(actionsList) }); - expect(receipt.transactions.length).toEqual(2); + expect(receipt.transactions.length).toEqual(3); // sender_wallet -> wallet_v5 -> bounced expect( ( @@ -889,7 +889,7 @@ describe('Wallet V5 sign auth internal', () => { body: createBody(actionsList) }); - expect(receipt.transactions.length).toEqual(2); + expect(receipt.transactions.length).toEqual(3); // sender_wallet -> wallet_v5 -> bounced expect( ( @@ -1091,7 +1091,7 @@ describe('Wallet V5 sign auth internal', () => { body: createBody(actionsList) }); - expect(receipt.transactions.length).toEqual(2); + expect(receipt.transactions.length).toEqual(3); // sender_wallet -> wallet_v5 -> bounced accountForGas(receipt.transactions); expect( @@ -1120,7 +1120,7 @@ describe('Wallet V5 sign auth internal', () => { body: createBody(actionsList) }); - expect(receipt.transactions.length).toEqual(2); + expect(receipt.transactions.length).toEqual(3); // sender_wallet -> wallet_v5 -> bounced accountForGas(receipt.transactions); expect( From c10105a8d2540d963615f69feda57a5668d69f49 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Sat, 22 Jun 2024 14:23:09 +0400 Subject: [PATCH 32/67] xctos comment --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index cc3eeca..1c19892 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100027f000114ff00f4a413f4bcf2c80b01020120030201e4f220d70b1f82107369676ebaf2af7f8ee0eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff027cd020c702dc01d0d60301c713c200dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f23120d70b1f821073696e74badd20d7498102b1b9dc708ee0eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed54f80fdb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005c21d7393020c700915b8e208e1cd72820761e436c20d71d06c712c2005220b0f265d74cd7393020c700e65be2ed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54dd2a9a2b"} \ No newline at end of file +{"hex":"b5ee9c724102120100027d000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54fe4d9457"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 6e2c3cd..3bb4914 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -51,9 +51,6 @@ slice get_last_bits(slice s, int l) asm "SDCUTLAST"; ;;; returns all but the last 0 ≤ l ≤ 1023 bits of s. slice remove_last_bits(slice s, int l) asm "SDSKIPLAST"; - -(slice, int) begin_parse_raw(cell c) asm "XCTOS"; - ;; `action_send_msg` has 0x0ec3c86d prefix ;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380 slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; @@ -63,6 +60,10 @@ slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c8 ;; C5 register - https://docs.ton.org/tvm.pdf, page 11 () set_c5_actions(cell action_list) impure asm "c5 POP"; +;; XCTOS doesn't automatically load exotic cells (unlike CTOS `begin_parse`). We use it in `verify_c5_actions` because during action phase processing exotic cells in c5 won't be unfolded too. +;;; transforms an ordinary or exotic cell into a Slice, as if it were an ordinary cell. A flag is returned indicating whether c is exotic. If that be the case, its type can later be deserialized from the first eight bits of s. +(slice, int) begin_parse_raw(cell c) asm "XCTOS"; + cell verify_c5_actions(cell c5, int is_external) inline { (slice cs, _) = c5.begin_parse_raw(); @@ -70,9 +71,12 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; only `action_send_msg` is allowed, `action_set_code` or `action_reserve_currency` are not cs = cs.enforce_and_remove_action_send_msg_prefix(); + ;; enforce that send_mode has +2 bit (ignore errors) set for external message. + ;; if such send_mode is not set and sending fails at the action phase (for example due to insufficient balance) then the seqno will not be increased and the external message will be processed again and again. + ;; action_send_msg#0ec3c86d mode:(## 8) out_msg:^(MessageRelaxed Any) = OutAction; ;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380 - ;; enforce that send_mode has +2 bit (ignore errors) set for external message. For that load 7 bits and make sure that they end with 1 + ;; load 7 bits and make sure that they end with 1 throw_if(error::invalid_c5_action, is_external & (count_trailing_zeroes(cs.preload_bits(7)) > 0)); (cs, _) = cs.preload_ref().begin_parse_raw(); From 1f3c1770dead1fd4c6466f6b63fb01b25996cd88 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Sat, 22 Jun 2024 14:53:43 +0400 Subject: [PATCH 33/67] update stdlib from stablecoin-contract --- contracts/imports/stdlib.fc | 41 +++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/contracts/imports/stdlib.fc b/contracts/imports/stdlib.fc index fa048f6..241993d 100644 --- a/contracts/imports/stdlib.fc +++ b/contracts/imports/stdlib.fc @@ -1,6 +1,21 @@ ;; Standard library for funC ;; +{- + This file is part of TON FunC Standard Library. + + FunC Standard Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FunC Standard Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + +-} + {- # Tuple manipulation primitives The names and the types are mostly self-explaining. @@ -26,7 +41,7 @@ forall X -> tuple cons(X head, tuple tail) asm "CONS"; forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; ;;; Extracts the tail and the head of lisp-style list. -forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS"; +forall X -> (tuple, X) list_next(tuple list) asm(-> 1 0) "UNCONS"; ;;; Returns the head of lisp-style list. forall X -> X car(tuple list) asm "CAR"; @@ -244,11 +259,9 @@ cont bless(slice s) impure asm "BLESS"; () commit() impure asm "COMMIT"; ;;; Not implemented -;;() buy_gas(int gram) impure asm "BUYGAS"; - ;;; Computes the amount of gas that can be bought for `amount` nanoTONs, ;;; and sets `gl` accordingly in the same way as [set_gas_limit]. -() buy_gas(int amount) impure asm "BUYGAS"; +;;() buy_gas(int amount) impure asm "BUYGAS"; ;;; Computes the minimum of two integers [x] and [y]. int min(int x, int y) asm "MIN"; @@ -285,12 +298,12 @@ slice begin_parse(cell c) asm "CTOS"; () end_parse(slice s) impure asm "ENDS"; ;;; Loads the first reference from the slice. -(slice, cell) load_ref(slice s) asm( -> 1 0) "LDREF"; +(slice, cell) load_ref(slice s) asm(-> 1 0) "LDREF"; ;;; Preloads the first reference from the slice. cell preload_ref(slice s) asm "PLDREF"; - {- Functions below are commented because are implemented on compilator level for optimisation -} +{- Functions below are commented because are implemented on compilator level for optimisation -} ;;; Loads a signed [len]-bit integer from a slice [s]. ;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; @@ -311,8 +324,8 @@ cell preload_ref(slice s) asm "PLDREF"; ;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; ;;; Loads serialized amount of TonCoins (any unsigned integer up to `2^128 - 1`). -(slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS"; -(slice, int) load_coins(slice s) asm( -> 1 0) "LDGRAMS"; +(slice, int) load_grams(slice s) asm(-> 1 0) "LDGRAMS"; +(slice, int) load_coins(slice s) asm(-> 1 0) "LDVARUINT16"; ;;; Returns all but the first `0 ≤ len ≤ 1023` bits of `slice` [s]. slice skip_bits(slice s, int len) asm "SDSKIPFIRST"; @@ -330,7 +343,7 @@ slice slice_last(slice s, int len) asm "SDCUTLAST"; ;;; Loads a dictionary `D` (HashMapE) from `slice` [s]. ;;; (returns `null` if `nothing` constructor is used). -(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT"; +(slice, cell) load_dict(slice s) asm(-> 1 0) "LDDICT"; ;;; Preloads a dictionary `D` from `slice` [s]. cell preload_dict(slice s) asm "PLDDICT"; @@ -342,7 +355,7 @@ slice skip_dict(slice s) asm "SKIPDICT"; ;;; In other words loads 1 bit and if it is true ;;; loads first ref and return it with slice remainder ;;; otherwise returns `null` and slice remainder -(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF"; +(slice, cell) load_maybe_ref(slice s) asm(-> 1 0) "LDOPTREF"; ;;; Preloads (Maybe ^Cell) from `slice` [s]. cell preload_maybe_ref(slice s) asm "PLDOPTREF"; @@ -434,7 +447,7 @@ builder store_slice(builder b, slice s) asm "STSLICER"; ;;; ;;; Store amounts of TonCoins to the builder as VarUInteger 16 builder store_grams(builder b, int x) asm "STGRAMS"; -builder store_coins(builder b, int x) asm "STGRAMS"; +builder store_coins(builder b, int x) asm "STVARUINT16"; ;;; Stores dictionary `D` represented by `cell` [c] or `null` into `builder` [b]. ;;; In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise. @@ -485,7 +498,7 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; ;;; Loads from slice [s] the only prefix that is a valid `MsgAddress`, ;;; and returns both this prefix `s'` and the remainder `s''` of [s] as slices. -(slice, slice) load_msg_addr(slice s) asm( -> 1 0) "LDMSGADDR"; +(slice, slice) load_msg_addr(slice s) asm(-> 1 0) "LDMSGADDR"; ;;; Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`. ;;; If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown. @@ -618,8 +631,6 @@ int get_seed() impure asm "RANDSEED"; () randomize_lt() impure asm "LTIME" "ADDRAND"; ;;; Checks whether the data parts of two slices coinside -int equal_slice_bits(slice a, slice b) asm "SDEQ"; -int equal_slices(slice a, slice b) asm "SDEQ"; - +int equal_slices_bits(slice a, slice b) asm "SDEQ"; ;;; Concatenates two builders builder store_builder(builder to, builder from) asm "STBR"; \ No newline at end of file From 464e68d29f1b200e02667ffffbd07c47248debc3 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Sat, 22 Jun 2024 14:55:31 +0400 Subject: [PATCH 34/67] rename get_is_signature_auth_allowed -> is_signature_allowed --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 2 +- wrappers/wallet-v5.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 1c19892..482f3e6 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100027d000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200a0702012009080019b45d1da89a10043ae43ae169f00017b592fda89a0e3ae43ae163f00202760c0b0010a880ed44d0d70a000018ab9ced44d08071d721d70bff0278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54fe4d9457"} \ No newline at end of file +{"hex":"b5ee9c724102120100027f000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae169f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08071d721d70bff80278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54a956c8eb"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 3bb4914..f8d40e6 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -237,7 +237,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; ------------------------------------------------------------------------------------------------ ;; Get methods -int get_is_signature_auth_allowed() method_id { +int is_signature_allowed() method_id { return get_data().begin_parse() .preload_int(size::bool); } diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index 9c55ad8..aa6fece 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -203,7 +203,7 @@ export class WalletV5 implements Contract { async getIsSignatureAuthAllowed(provider: ContractProvider) { const state = await provider.getState(); if (state.state.type === 'active') { - let res = await provider.get('get_is_signature_auth_allowed', []); + let res = await provider.get('is_signature_allowed', []); return res.stack.readNumber(); } else { return -1; From d5ea3816e89f810119af5c70a44c5f4c3fbdd6b8 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Mon, 24 Jun 2024 10:54:37 +0400 Subject: [PATCH 35/67] comments --- contracts/wallet_v5.fc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index f8d40e6..a8162d3 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -55,16 +55,18 @@ slice remove_last_bits(slice s, int l) asm "SDSKIPLAST"; ;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380 slice enforce_and_remove_action_send_msg_prefix(slice body) impure asm "x{0ec3c86d} SDBEGINS"; -;; Put raw list of OutActions to C5 register. -;; OutList TLB-schema - https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L378 -;; C5 register - https://docs.ton.org/tvm.pdf, page 11 +;;; put raw list of OutActions to C5 register. +;;; OutList TLB-schema - https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L378 +;;; C5 register - https://docs.ton.org/tvm.pdf, page 11 () set_c5_actions(cell action_list) impure asm "c5 POP"; -;; XCTOS doesn't automatically load exotic cells (unlike CTOS `begin_parse`). We use it in `verify_c5_actions` because during action phase processing exotic cells in c5 won't be unfolded too. ;;; transforms an ordinary or exotic cell into a Slice, as if it were an ordinary cell. A flag is returned indicating whether c is exotic. If that be the case, its type can later be deserialized from the first eight bits of s. (slice, int) begin_parse_raw(cell c) asm "XCTOS"; cell verify_c5_actions(cell c5, int is_external) inline { + ;; XCTOS doesn't automatically load exotic cells (unlike CTOS `begin_parse`). + ;; we use it in `verify_c5_actions` because during action phase processing exotic cells in c5 won't be unfolded too. + ;; exotic cell starts with 0x02, 0x03 or 0x04 so it will not pass action_send_msg prefix check (slice cs, _) = c5.begin_parse_raw(); while (~ cs.slice_empty?()) { From 816e4468a2a821c03f0fbc8ea9b588f37734a038 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Mon, 24 Jun 2024 11:00:13 +0400 Subject: [PATCH 36/67] add query_id to extension request so that the extension can recognise the error response --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 3 +++ wrappers/wallet-v5.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 482f3e6..ee4b456 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100027f000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae169f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08071d721d70bff80278d020c702dc01d0d60301c713dc01d72c232bc3a3748ea301fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd70db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54a956c8eb"} \ No newline at end of file +{"hex":"b5ee9c7241021201000283000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae169f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08071d721d70bff80280d020c702dc01d0d60301c713dc01d72c232bc3a3748ea701fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd8040d72170db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed5475319bcc"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index a8162d3..0bf08ad 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -26,6 +26,7 @@ const int size::message_flags = 4; const int size::signature = 512; const int size::message_type_prefix = 32; const int size::address_hash_size = 256; +const int size::query_id = 64; const int prefix::signed_external = 0x7369676E; const int prefix::signed_internal = 0x73696E74; @@ -225,6 +226,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { (_, int extension_found) = extensions.udict_get?(size::address_hash_size, sender_address_hash); return_unless(extension_found); + in_msg_body~skip_bits(size::query_id); ;; skip query_id + process_actions(in_msg_body, false); return (); diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index aa6fece..4f2a7d0 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -159,6 +159,7 @@ export class WalletV5 implements Contract { sendMode: SendMode.PAY_GAS_SEPARATELY, body: beginCell() .storeUint(Opcodes.auth_extension, 32) + .storeUint(0, 64) // query id .storeSlice(opts.body.beginParse()) .endCell() }); From 43fabdf74fec537309eedb46903993e21f727d01 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Tue, 25 Jun 2024 18:23:53 +0400 Subject: [PATCH 37/67] action prefix 8bit --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 6 +++--- tests/actions.ts | 12 ++++++------ tests/wallet-v5-external.spec.ts | 2 +- tests/wallet-v5-internal.spec.ts | 2 +- wrappers/wallet-v5.ts | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index ee4b456..4ccb7ef 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021201000283000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae169f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08071d721d70bff80280d020c702dc01d0d60301c713dc01d72c232bc3a3748ea701fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd8040d72170db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02c09401d200018ed5d72c20e206dcfc2091709901d72c22f577a52412e25210b18e3530d72c21065dcad48e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed5475319bcc"} \ No newline at end of file +{"hex":"b5ee9c724102120100027a000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae169f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08071d721d70bff80280d020c702dc01d0d60301c713dc01d72c232bc3a3748ea701fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd8040d72170db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02ae9401d200018eccd72c08142091709601d72c081c12e25210b18e3230d72c08248e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54edaac72e"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 0bf08ad..a5561d5 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -38,9 +38,9 @@ const int prefix::signed_internal = 0x73696E74; (slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; -(slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{1c40db9f} SDBEGINSQ"; -(slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{5eaef4a4} SDBEGINSQ"; -(slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{20cbb95a} SDBEGINSQ"; +(slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{02} SDBEGINSQ"; +(slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{03} SDBEGINSQ"; +(slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{04} SDBEGINSQ"; ;;; returns the number of trailing zeroes in slice s. int count_trailing_zeroes(slice s) asm "SDCNTTRAIL0"; diff --git a/tests/actions.ts b/tests/actions.ts index 6212e60..9788e53 100644 --- a/tests/actions.ts +++ b/tests/actions.ts @@ -18,31 +18,31 @@ export class ActionSendMsg { } export class ActionAddExtension { - public static readonly tag = 0x1c40db9f; + public static readonly tag = 0x02; public readonly tag = ActionAddExtension.tag; constructor(public readonly address: Address) {} public serialize(): Cell { - return beginCell().storeUint(this.tag, 32).storeAddress(this.address).endCell(); + return beginCell().storeUint(this.tag, 8).storeAddress(this.address).endCell(); } } export class ActionRemoveExtension { - public static readonly tag = 0x5eaef4a4; + public static readonly tag = 0x03; public readonly tag = ActionRemoveExtension.tag; constructor(public readonly address: Address) {} public serialize(): Cell { - return beginCell().storeUint(this.tag, 32).storeAddress(this.address).endCell(); + return beginCell().storeUint(this.tag, 8).storeAddress(this.address).endCell(); } } export class ActionSetSignatureAuthAllowed { - public static readonly tag = 0x20cbb95a; + public static readonly tag = 0x04; public readonly tag = ActionSetSignatureAuthAllowed.tag; @@ -50,7 +50,7 @@ export class ActionSetSignatureAuthAllowed { public serialize(): Cell { return beginCell() - .storeUint(this.tag, 32) + .storeUint(this.tag, 8) .storeUint(this.allowed ? 1 : 0, 1) .endCell(); } diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index a71ed3f..dfba9f6 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -190,7 +190,7 @@ describe('Wallet V5 sign auth external', () => { const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const addExtensionAction = beginCell() - .storeUint(Opcodes.action_extended_add_extension, 32) + .storeUint(Opcodes.action_extended_add_extension, 8) .storeAddress(testExtension) .endCell(); diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index e0eea79..50df21a 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -186,7 +186,7 @@ describe('Wallet V5 sign auth internal', () => { const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const addExtensionAction = beginCell() - .storeUint(Opcodes.action_extended_add_extension, 32) + .storeUint(Opcodes.action_extended_add_extension, 8) .storeAddress(testExtension) .endCell(); diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index 4f2a7d0..932818c 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -36,9 +36,9 @@ export const Opcodes = { action_send_msg: 0x0ec3c86d, action_set_code: 0xad4de08e, action_extended_set_data: 0x1ff8ea0b, - action_extended_add_extension: 0x1c40db9f, - action_extended_remove_extension: 0x5eaef4a4, - action_extended_set_signature_auth_allowed: 0x20cbb95a, + action_extended_add_extension: 0x02, + action_extended_remove_extension: 0x03, + action_extended_set_signature_auth_allowed: 0x04, auth_extension: 0x6578746e, auth_signed: 0x7369676e, auth_signed_internal: 0x73696e74 From 68a086618e4f1af99f986595a558329ebdd13b0c Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Tue, 25 Jun 2024 18:42:08 +0400 Subject: [PATCH 38/67] wallet_id 32bit --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 2 +- tests/wallet-v5-extensions.spec.ts | 2 +- tests/wallet-v5-external.spec.ts | 16 ++++---- tests/wallet-v5-get.spec.ts | 2 +- tests/wallet-v5-internal.spec.ts | 16 ++++---- wrappers/wallet-v5.ts | 62 +++++++++++++++--------------- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 4ccb7ef..1d9598a 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100027a000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080b8eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae169f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08071d721d70bff80280d020c702dc01d0d60301c713dc01d72c232bc3a3748ea701fa4030fa44f828fa443058badded44d0810171d721f4058307f40e6fa131dd8040d72170db3ce30e0f0e01f83120d70b1f821073696e74badd20d7498102b1b9dc708ee3eda2edfb218308d722028308d723208020d721d34fd31fd31fed44d0d200d31f20d34fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02ae9401d200018eccd72c08142091709601d72c081c12e25210b18e3230d72c08248e27d200ed44d0d2005113baf26b54503091319b01810170d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810171d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54edaac72e"} \ No newline at end of file +{"hex":"b5ee9c724102120100027a000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080a0eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae163f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff80280d020c702dc01d0d60301c713dc01d72c232bc3a3748ea701fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d72170db3ce30e0f0e01f83120d70b1f821073696e74badd20d749810281b9dc708ee3eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02ae9401d200018eccd72c08142091709601d72c081c12e25210b18e3230d72c08248e27d200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54a9269d25"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index a5561d5..9aa01b6 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -19,7 +19,7 @@ const int error::invalid_message_type = 47; const int size::bool = 1; const int size::seqno = 32; -const int size::wallet_id = 80; +const int size::wallet_id = 32; const int size::public_key = 256; const int size::valid_until = 32; const int size::message_flags = 4; diff --git a/tests/wallet-v5-extensions.spec.ts b/tests/wallet-v5-extensions.spec.ts index d4aa8e9..f70e186 100644 --- a/tests/wallet-v5-extensions.spec.ts +++ b/tests/wallet-v5-extensions.spec.ts @@ -45,7 +45,7 @@ describe('Wallet V5 extensions auth', () => { function createBody(actionsList: Cell) { const payload = beginCell() .storeUint(Opcodes.auth_signed_internal, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) // seqno .storeSlice(actionsList.beginParse()) diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index dfba9f6..1660055 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -78,7 +78,7 @@ describe('Wallet V5 sign auth external', () => { function createBody(actionsList: Cell) { const payload = beginCell() .storeUint(Opcodes.auth_signed, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -475,7 +475,7 @@ describe('Wallet V5 sign auth external', () => { const vu = validUntil(); const payload = beginCell() - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(vu, 32) .storeUint(seqno, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -483,7 +483,7 @@ describe('Wallet V5 sign auth external', () => { const fakePayload = beginCell() .storeUint(Opcodes.auth_signed, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(vu, 32) .storeUint(seqno + 1, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -514,7 +514,7 @@ describe('Wallet V5 sign auth external', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -547,7 +547,7 @@ describe('Wallet V5 sign auth external', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno + 1, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -578,7 +578,7 @@ describe('Wallet V5 sign auth external', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(Math.round(Date.now() / 1000) - 600, 32) .storeUint(seqno, 32) .storeSlice(actionsList.beginParse()) @@ -609,7 +609,7 @@ describe('Wallet V5 sign auth external', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed, 32) - .storeUint(new WalletId({ ...WALLET_ID, subwalletNumber: 1 }).serialized, 80) + .storeUint(new WalletId({ ...WALLET_ID, subwalletNumber: 1 }).serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) .storeSlice(actionsList.beginParse()) @@ -640,7 +640,7 @@ describe('Wallet V5 sign auth external', () => { const payload = beginCell() // auth_signed_internal used instead of auth_signed .storeUint(Opcodes.auth_signed_internal, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) .storeSlice(actionsList.beginParse()) diff --git a/tests/wallet-v5-get.spec.ts b/tests/wallet-v5-get.spec.ts index d6f3251..8611407 100644 --- a/tests/wallet-v5-get.spec.ts +++ b/tests/wallet-v5-get.spec.ts @@ -79,7 +79,7 @@ describe('Wallet V5 get methods', () => { }); await deploy({ walletId: expectedWalletId.serialized }); const actualWalletId = await walletV5.getWalletId(); - expect(expectedWalletId.serialized).toEqual(actualWalletId.serialized); + expect(expectedWalletId.subwalletNumber).toEqual(actualWalletId.subwalletNumber); }); it('Get subwallet number', async () => { diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index 50df21a..60463a5 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -71,7 +71,7 @@ describe('Wallet V5 sign auth internal', () => { function createBody(actionsList: Cell) { const payload = beginCell() .storeUint(Opcodes.auth_signed_internal, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -471,14 +471,14 @@ describe('Wallet V5 sign auth internal', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed_internal, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(vu, 32) .storeUint(seqno, 32) // seqno .storeSlice(actionsList.beginParse()) .endCell(); const fakePayload = beginCell() - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(vu, 32) .storeUint(seqno + 1, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -523,7 +523,7 @@ describe('Wallet V5 sign auth internal', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed_internal, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -577,7 +577,7 @@ describe('Wallet V5 sign auth internal', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed_internal, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno + 1, 32) // seqno .storeSlice(actionsList.beginParse()) @@ -622,7 +622,7 @@ describe('Wallet V5 sign auth internal', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed_internal, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(Math.round(Date.now() / 1000) - 600, 32) .storeUint(seqno, 32) .storeSlice(actionsList.beginParse()) @@ -667,7 +667,7 @@ describe('Wallet V5 sign auth internal', () => { const payload = beginCell() .storeUint(Opcodes.auth_signed_internal, 32) - .storeUint(new WalletId({ ...WALLET_ID, subwalletNumber: 1 }).serialized, 80) + .storeUint(new WalletId({ ...WALLET_ID, subwalletNumber: 1 }).serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) .storeSlice(actionsList.beginParse()) @@ -712,7 +712,7 @@ describe('Wallet V5 sign auth internal', () => { const payload = beginCell() // auth_signed used instead of auth_signed_internal .storeUint(Opcodes.auth_signed, 32) - .storeUint(WALLET_ID.serialized, 80) + .storeUint(WALLET_ID.serialized, 32) .storeUint(validUntil(), 32) .storeUint(seqno, 32) .storeSlice(actionsList.beginParse()) diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index 932818c..9a90c8c 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -26,7 +26,7 @@ export function walletV5ConfigToCell(config: WalletV5Config): Cell { return beginCell() .storeBit(config.signatureAllowed) .storeUint(config.seqno, 32) - .storeUint(config.walletId, 80) + .storeUint(config.walletId, 32) .storeBuffer(config.publicKey, 32) .storeDict(config.extensions, Dictionary.Keys.BigUint(256), Dictionary.Values.BigInt(1)) .endCell(); @@ -49,30 +49,30 @@ export class WalletId { v5: 0 }; - static deserialize(walletId: bigint | Buffer): WalletId { - const bitReader = new BitReader( - new BitString( - typeof walletId === 'bigint' ? Buffer.from(walletId.toString(16), 'hex') : walletId, - 0, - 80 - ) - ); - const networkGlobalId = bitReader.loadInt(32); - const workChain = bitReader.loadInt(8); - const walletVersionRaw = bitReader.loadUint(8); - const subwalletNumber = bitReader.loadUint(32); - - const walletVersion = Object.entries(this.versionsSerialisation).find( - ([_, value]) => value === walletVersionRaw - )?.[0] as WalletId['walletVersion'] | undefined; - - if (walletVersion === undefined) { - throw new Error( - `Can't deserialize walletId: unknown wallet version ${walletVersionRaw}` - ); - } - - return new WalletId({ networkGlobalId, workChain, walletVersion, subwalletNumber }); + static deserialize(walletId: bigint): WalletId { + // const bitReader = new BitReader( + // new BitString( + // typeof walletId === 'bigint' ? Buffer.from(walletId.toString(16), 'hex') : walletId, + // 0, + // 32 + // ) + // ); + // const networkGlobalId = bitReader.loadInt(32); + // const workChain = bitReader.loadInt(8); + // const walletVersionRaw = bitReader.loadUint(8); + const subwalletNumber = walletId; + // + // const walletVersion = Object.entries(this.versionsSerialisation).find( + // ([_, value]) => value === walletVersionRaw + // )?.[0] as WalletId['walletVersion'] | undefined; + // + // if (walletVersion === undefined) { + // throw new Error( + // `Can't deserialize walletId: unknown wallet version ${walletVersionRaw}` + // ); + // } + // + return new WalletId({ networkGlobalId: 0, workChain: 0, walletVersion: 'v5', subwalletNumber: Number(walletId) }); } readonly walletVersion: 'v5'; @@ -97,13 +97,13 @@ export class WalletId { this.subwalletNumber = args?.subwalletNumber ?? 0; this.walletVersion = args?.walletVersion ?? 'v5'; - const bitBuilder = new BitBuilder(80); - bitBuilder.writeInt(this.networkGlobalId, 32); - bitBuilder.writeInt(this.workChain, 8); - bitBuilder.writeUint(WalletId.versionsSerialisation[this.walletVersion], 8); - bitBuilder.writeUint(this.subwalletNumber, 32); + // const bitBuilder = new BitBuilder(32); + // bitBuilder.writeInt(this.networkGlobalId, 32); + // bitBuilder.writeInt(this.workChain, 8); + // bitBuilder.writeUint(WalletId.versionsSerialisation[this.walletVersion], 8); + // bitBuilder.writeUint(this.subwalletNumber, 32); - this.serialized = bufferToBigInt(bitBuilder.buffer()); + this.serialized = BigInt(this.subwalletNumber) // bufferToBigInt(bitBuilder.buffer()); } } From 21f229a000d2056e86fb60af1ccb1bbacffc3dd8 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Tue, 25 Jun 2024 18:49:39 +0400 Subject: [PATCH 39/67] change refs in message (tests not synced yet) --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 1d9598a..f499330 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100027a000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080a0eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae163f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff80280d020c702dc01d0d60301c713dc01d72c232bc3a3748ea701fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d72170db3ce30e0f0e01f83120d70b1f821073696e74badd20d749810281b9dc708ee3eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02ae9401d200018eccd72c08142091709601d72c081c12e25210b18e3230d72c08248e27d200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2e30dd74cd001e8d74c011110005021d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54a9269d25"} \ No newline at end of file +{"hex":"b5ee9c724102120100028b000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080a0eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae163f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff80278d001d0d60301c713dc01d72c232bc3a3748ea701fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d72170db3ce30e0f0e01f83120d70b1f821073696e74badd20d749810281b9dc708ee3eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02c4eda2edfb01f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b39130e0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac0009330db31e0d74cd01110006430d72c08248e27d200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed547da1ee1f"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 9aa01b6..095a327 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -89,8 +89,18 @@ cell verify_c5_actions(cell c5, int is_external) inline { } () process_actions(slice cs, int is_external) impure inline_ref { + cell c5_actions = cs~load_maybe_ref(); + if (~ cell_null?(c5_actions)) { + ;; Simply set the C5 register with all pre-computed actions after verification: + set_c5_actions(c5_actions.verify_c5_actions(is_external)); + } + int has_other_actions = cs~load_int(1); + if (~ has_other_actions) { + return (); + } + ;; Loop extended actions until we reach standard actions - while (cs~load_int(1)) { + while (true) { int is_add_extension = cs~check_and_remove_add_extension_prefix(); int is_remove_extension = is_add_extension ? 0 : cs~check_and_remove_remove_extension_prefix(); ;; Add/remove extensions @@ -140,10 +150,11 @@ cell verify_c5_actions(cell c5, int is_external) inline { } else { throw(error::unspported_action); } + if (cs.slice_refs() == 0) { + return (); + } cs = cs.preload_ref().begin_parse(); } - ;; Simply set the C5 register with all pre-computed actions after verification: - set_c5_actions(cs.preload_ref().verify_c5_actions(is_external)); } ;; ------------------------------------------------------------------------------------------------ @@ -202,7 +213,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; ------------------------------------------------------------------------------------------------ () recv_internal(cell in_msg_full, slice in_msg_body) impure inline { - return_if(in_msg_body.slice_refs_empty?()); ;; message with actions always have a ref + ;; return_if(in_msg_body.slice_refs_empty?()); ;; message with actions always have a ref todo slice in_msg_full_slice = in_msg_full.begin_parse(); slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool From 0a40931ab366ade923a7f7b42a40bb223e8ceda9 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Tue, 25 Jun 2024 18:51:57 +0400 Subject: [PATCH 40/67] only extension can change signature mode (tests not synced yet) --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index f499330..cf68c06 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102120100028b000114ff00f4a413f4bcf2c80b01020120030201eaf220d70b1f82107369676ebaf2af7f8ee3eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f0201480d0402012006050019be5f0f6a2684080a0eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae163f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff80278d001d0d60301c713dc01d72c232bc3a3748ea701fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d72170db3ce30e0f0e01f83120d70b1f821073696e74badd20d749810281b9dc708ee3eda2edfb218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fdedb3cd80f02c4eda2edfb01f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b39130e0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac0009330db31e0d74cd01110006430d72c08248e27d200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed547da1ee1f"} \ No newline at end of file +{"hex":"b5ee9c7241021201000295000114ff00f4a413f4bcf2c80b01020120030201f0f220d70b1f82107369676ebaf2af7f708ee5eda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd80f0201480d0402012006050019be5f0f6a2684080a0eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae163f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff8027ad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3ce30e0f0e01fe3120d70b1f821073696e74badd20d749810281b9dc70708ee5eda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd80f02c4eda2edfb02f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b3915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac000935bdb31e0d74cd01110006a30d72c08248e2a21f2b0d200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed549235d7cc"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 095a327..c8ca637 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -16,6 +16,7 @@ const int error::invalid_seqno = 33; const int error::invalid_wallet_id = 34; const int error::expired = 36; const int error::invalid_message_type = 47; +const int error::only_extension_can_change_signature_mode = 48; const int size::bool = 1; const int size::seqno = 32; @@ -88,7 +89,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { return c5; } -() process_actions(slice cs, int is_external) impure inline_ref { +() process_actions(slice cs, int is_external, int is_extension) impure inline_ref { cell c5_actions = cs~load_maybe_ref(); if (~ cell_null?(c5_actions)) { ;; Simply set the C5 register with all pre-computed actions after verification: @@ -131,6 +132,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { .end_cell()); } elseif (cs~check_and_remove_set_signature_allowed_prefix()) { + throw_unless(error::only_extension_can_change_signature_mode, is_extension); int allow_signature = cs~load_int(1); slice data_slice = get_data().begin_parse(); int is_signature_allowed = data_slice~load_int(size::bool); @@ -159,7 +161,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; ------------------------------------------------------------------------------------------------ -() process_signed_request(slice in_msg_body, int is_external) impure inline { +() process_signed_request(slice in_msg_body, int is_external, int is_extension) impure inline { slice signature = in_msg_body.get_last_bits(size::signature); slice signed_slice = in_msg_body.remove_last_bits(size::signature); @@ -202,12 +204,12 @@ cell verify_c5_actions(cell c5, int is_external) inline { commit(); } - process_actions(cs, is_external); + process_actions(cs, is_external, is_external); } () recv_external(slice in_msg_body) impure inline { throw_unless(error::invalid_message_type, in_msg_body.preload_uint(size::message_type_prefix) == prefix::signed_external); - process_signed_request(in_msg_body, true); + process_signed_request(in_msg_body, true, false); } ;; ------------------------------------------------------------------------------------------------ @@ -239,7 +241,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { in_msg_body~skip_bits(size::query_id); ;; skip query_id - process_actions(in_msg_body, false); + process_actions(in_msg_body, false, true); return (); } @@ -247,7 +249,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { return_unless(in_msg_body.preload_uint(size::message_type_prefix) == prefix::signed_internal); ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) return_if(in_msg_body.slice_bits() < size::message_type_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); - process_signed_request(in_msg_body, false); + process_signed_request(in_msg_body, false, false); } ;; ------------------------------------------------------------------------------------------------ From 3d822d93154fe340aad31304d09f209c18859eb9 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Tue, 25 Jun 2024 18:53:38 +0400 Subject: [PATCH 41/67] allow work with (~ is_signature_allowed) and empty extensions --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index cf68c06..1363e2c 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021201000295000114ff00f4a413f4bcf2c80b01020120030201f0f220d70b1f82107369676ebaf2af7f708ee5eda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd80f0201480d0402012006050019be5f0f6a2684080a0eb90fa02c0201200c0702012009080019b45d1da89a10043ae43ae163f00201200b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff8027ad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3ce30e0f0e01fe3120d70b1f821073696e74badd20d749810281b9dc70708ee5eda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd70bff09f90140b9f9109927945f09db31e1f223df01f2a05122baf2a15036baf2a2f823bbf2642292f800dea47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd80f02c4eda2edfb02f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b3915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac000935bdb31e0d74cd01110006a30d72c08248e2a21f2b0d200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed549235d7cc"} \ No newline at end of file +{"hex":"b5ee9c7241021301000238000114ff00f4a413f4bcf2c80b0102012004020102f203011e20d70b1f82107369676ebaf2af7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200d080201200a090019b45d1da89a10043ae43ae163f00201200c0b0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802aad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3c8e973120d70b1f821073696e74badd20d749810281b9dc7070e2100f01de8eeceda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109928945f0adb31e1f223df02b35007b0f2605125baf2a15036baf2a2f823bbf2642292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd81002c4eda2edfb02f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b3915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac000935bdb31e0d74cd01211006a30d72c08248e2a21f2b0d200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54b19a10ad"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index c8ca637..cefafd2 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -173,7 +173,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { int stored_seqno = data_slice~load_uint(size::seqno); slice data_tail = data_slice; ;; wallet_id, public_key, extensions int stored_wallet_id = data_slice~load_uint(size::wallet_id); - int public_key = data_slice.preload_uint(size::public_key); + int public_key = data_slice~load_uint(size::public_key); + int is_extensions_not_empty = data_slice.preload_int(1); int is_signature_valid = check_signature(slice_hash(signed_slice), signature, public_key); ifnot (is_signature_valid) { @@ -183,7 +184,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { return (); } } - throw_unless(error::signature_disabled, is_signature_allowed); + throw_if(error::signature_disabled, (~ is_signature_allowed) & is_extensions_not_empty); throw_unless(error::invalid_seqno, seqno == stored_seqno); throw_unless(error::invalid_wallet_id, wallet_id == stored_wallet_id); throw_if(error::expired, valid_until <= now()); From c5dc057f4b65f1481bf61e1086248ee6a45a109c Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 15:55:55 +0400 Subject: [PATCH 42/67] refactor errors - only only_extension_can_change_signature_mode number changed --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 1363e2c..c2d1b30 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021301000238000114ff00f4a413f4bcf2c80b0102012004020102f203011e20d70b1f82107369676ebaf2af7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200d080201200a090019b45d1da89a10043ae43ae163f00201200c0b0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802aad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3c8e973120d70b1f821073696e74badd20d749810281b9dc7070e2100f01de8eeceda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109928945f0adb31e1f223df02b35007b0f2605125baf2a15036baf2a2f823bbf2642292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd81002c4eda2edfb02f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b3915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac000935bdb31e0d74cd01211006a30d72c08248e2a21f2b0d200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54b19a10ad"} \ No newline at end of file +{"hex":"b5ee9c7241021301000238000114ff00f4a413f4bcf2c80b0102012004020102f203011e20d70b1f82107369676ebaf2af7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200d080201200a090019b45d1da89a10043ae43ae163f00201200c0b0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802aad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3c8e973120d70b1f821073696e74badd20d749810281b9dc7070e2100f01de8eeceda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109928945f0adb31e1f223df02b35007b0f2605125baf2a15036baf2a2f823bbf2642292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd81002c4eda2edfb02f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b3915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac000935bdb31e0d74cd01211006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed545d03e60a"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index cefafd2..d5aeba9 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -2,21 +2,21 @@ #include "imports/stdlib.fc"; +const int error::signature_disabled = 32; +const int error::invalid_seqno = 33; +const int error::invalid_wallet_id = 34; +const int error::invalid_signature = 35; +const int error::expired = 36; const int error::invalid_c5_action = 37; -const int error::extension_wrong_workchain = 45; const int error::add_extension_error = 39; const int error::remove_extension_error = 40; +const int error::unspported_action = 41; const int error::disable_signature_when_extensions_is_empty = 42; const int error::this_signature_mode_already_set = 43; const int error::remove_last_extension_when_signature_disabled = 44; -const int error::unspported_action = 41; -const int error::signature_disabled = 32; -const int error::invalid_signature = 35; -const int error::invalid_seqno = 33; -const int error::invalid_wallet_id = 34; -const int error::expired = 36; +const int error::extension_wrong_workchain = 45; +const int error::only_extension_can_change_signature_mode = 46; const int error::invalid_message_type = 47; -const int error::only_extension_can_change_signature_mode = 48; const int size::bool = 1; const int size::seqno = 32; From c678610ea93170a1c5cbba4b8fbbcb2ae9f89003 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 16:02:21 +0400 Subject: [PATCH 43/67] refactor errors naming --- contracts/wallet_v5.fc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index d5aeba9..9bcb20d 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -7,9 +7,9 @@ const int error::invalid_seqno = 33; const int error::invalid_wallet_id = 34; const int error::invalid_signature = 35; const int error::expired = 36; -const int error::invalid_c5_action = 37; -const int error::add_extension_error = 39; -const int error::remove_extension_error = 40; +const int error::external_send_message_must_have_ignore_errors_send_mode = 37; +const int error::add_extension = 39; +const int error::remove_extension = 40; const int error::unspported_action = 41; const int error::disable_signature_when_extensions_is_empty = 42; const int error::this_signature_mode_already_set = 43; @@ -81,7 +81,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; action_send_msg#0ec3c86d mode:(## 8) out_msg:^(MessageRelaxed Any) = OutAction; ;; https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L380 ;; load 7 bits and make sure that they end with 1 - throw_if(error::invalid_c5_action, is_external & (count_trailing_zeroes(cs.preload_bits(7)) > 0)); + throw_if(error::external_send_message_must_have_ignore_errors_send_mode, is_external & (count_trailing_zeroes(cs.preload_bits(7)) > 0)); (cs, _) = cs.preload_ref().begin_parse_raw(); } @@ -118,10 +118,10 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Add extension if (is_add_extension) { (extensions, int is_success) = extensions.udict_add_builder?(size::address_hash_size, address_hash, begin_cell().store_int(-1, 1)); - throw_unless( error::add_extension_error, is_success); + throw_unless( error::add_extension, is_success); } else { ;; Remove extension (extensions, int is_success) = extensions.udict_delete?(size::address_hash_size, address_hash); - throw_unless(error::remove_extension_error, is_success); + throw_unless(error::remove_extension, is_success); int is_signature_allowed = data_slice_before_extensions.preload_int(size::bool); throw_if(error::remove_last_extension_when_signature_disabled, null?(extensions) & (~ is_signature_allowed)); } From aff9c49123d8912c425692024f2d20730a594144 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 16:05:59 +0400 Subject: [PATCH 44/67] refactor errors --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index c2d1b30..a756272 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021301000238000114ff00f4a413f4bcf2c80b0102012004020102f203011e20d70b1f82107369676ebaf2af7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200d080201200a090019b45d1da89a10043ae43ae163f00201200c0b0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802aad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3c8e973120d70b1f821073696e74badd20d749810281b9dc7070e2100f01de8eeceda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109928945f0adb31e1f223df02b35007b0f2605125baf2a15036baf2a2f823bbf2642292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd81002c4eda2edfb02f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b3915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac000935bdb31e0d74cd01211006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed545d03e60a"} \ No newline at end of file +{"hex":"b5ee9c7241021301000238000114ff00f4a413f4bcf2c80b0102012004020102f203011e20d70b1f82107369676ebaf2a67f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200d080201200a090019b45d1da89a10043ae43ae163f00201200c0b0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802aad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3c8e973120d70b1f821073696e74badd20d749810281b9dc7070e2100f01de8eeceda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109928945f0adb31e1f223df02b35007b0f2605125baf2a15036baf2a2f823bbf2642292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd81002c4eda2edfb02f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b3915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac000935bdb31e0d74cd01211006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed5409c293b9"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 9bcb20d..6d209e0 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -8,6 +8,7 @@ const int error::invalid_wallet_id = 34; const int error::invalid_signature = 35; const int error::expired = 36; const int error::external_send_message_must_have_ignore_errors_send_mode = 37; +const int error::invalid_message_type = 38; const int error::add_extension = 39; const int error::remove_extension = 40; const int error::unspported_action = 41; @@ -16,7 +17,6 @@ const int error::this_signature_mode_already_set = 43; const int error::remove_last_extension_when_signature_disabled = 44; const int error::extension_wrong_workchain = 45; const int error::only_extension_can_change_signature_mode = 46; -const int error::invalid_message_type = 47; const int size::bool = 1; const int size::seqno = 32; From e4b8c5bca7002e79db772c22d5aaef4f895f29e2 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 16:13:16 +0400 Subject: [PATCH 45/67] cosmetic (bytecode not changed) --- contracts/wallet_v5.fc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 6d209e0..d6cdff2 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -32,17 +32,17 @@ const int size::query_id = 64; const int prefix::signed_external = 0x7369676E; const int prefix::signed_internal = 0x73696E74; -;;; performs a RET, but only if integer f is non-zero. If f is a NaN, throws an integer overflow exception. -() return_if(int f) impure asm "IFRET"; -;;; performs a RET, but only if integer f is zero -() return_unless(int f) impure asm "IFNOTRET"; - (slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; (slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{02} SDBEGINSQ"; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{03} SDBEGINSQ"; (slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{04} SDBEGINSQ"; +;;; performs a RET, but only if integer f is non-zero. If f is a NaN, throws an integer overflow exception. +() return_if(int f) impure asm "IFRET"; +;;; performs a RET, but only if integer f is zero +() return_unless(int f) impure asm "IFNOTRET"; + ;;; returns the number of trailing zeroes in slice s. int count_trailing_zeroes(slice s) asm "SDCNTTRAIL0"; ;;; returns the number of trailing ones in slice s. From dd9ff2899e74e3e130c58e4f65e72b327b4fa408 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 16:23:37 +0400 Subject: [PATCH 46/67] cosmetic comment (bytecode not changed) --- contracts/wallet_v5.fc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index d6cdff2..96a80b6 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -72,7 +72,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { (slice cs, _) = c5.begin_parse_raw(); while (~ cs.slice_empty?()) { - ;; only `action_send_msg` is allowed, `action_set_code` or `action_reserve_currency` are not + ;; only `action_send_msg` is allowed; `action_set_code`, `action_reserve_currency` or `action_change_library` are not. cs = cs.enforce_and_remove_action_send_msg_prefix(); ;; enforce that send_mode has +2 bit (ignore errors) set for external message. From b7b8fd89111d3e9c281e742dcbdce0f49d42a431 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 16:33:21 +0400 Subject: [PATCH 47/67] simplify --- contracts/wallet_v5.fc | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 96a80b6..c79fb4e 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -91,14 +91,11 @@ cell verify_c5_actions(cell c5, int is_external) inline { () process_actions(slice cs, int is_external, int is_extension) impure inline_ref { cell c5_actions = cs~load_maybe_ref(); - if (~ cell_null?(c5_actions)) { + ifnot (cell_null?(c5_actions)) { ;; Simply set the C5 register with all pre-computed actions after verification: set_c5_actions(c5_actions.verify_c5_actions(is_external)); } - int has_other_actions = cs~load_int(1); - if (~ has_other_actions) { - return (); - } + return_unless(cs~load_int(1)); ;; has_other_actions ;; Loop extended actions until we reach standard actions while (true) { @@ -152,9 +149,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { } else { throw(error::unspported_action); } - if (cs.slice_refs() == 0) { - return (); - } + return_unless(cs.slice_refs()); cs = cs.preload_ref().begin_parse(); } } From 3dc030e79df3b6631449c6822276758bf8afab2c Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 16:33:35 +0400 Subject: [PATCH 48/67] fix mistake --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index a756272..fabf5f3 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021301000238000114ff00f4a413f4bcf2c80b0102012004020102f203011e20d70b1f82107369676ebaf2a67f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200d080201200a090019b45d1da89a10043ae43ae163f00201200c0b0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802aad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3c8e973120d70b1f821073696e74badd20d749810281b9dc7070e2100f01de8eeceda2edfb30218308d722028308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ccf9109928945f0adb31e1f223df02b35007b0f2605125baf2a15036baf2a2f823bbf2642292f800de01a47fc8ca00cb1f01cf16c9ed542092f80fde20db3cd81002c4eda2edfb02f404216eb38e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55926c21e2d20001b3915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74ac000935bdb31e0d74cd01211006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed5409c293b9"} \ No newline at end of file +{"hex":"b5ee9c7241021301000228000114ff00f4a413f4bcf2c80b0102012004020102f203011e20d70b1f82107369676ebaf2a67f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200d080201200a090019b45d1da89a10043ae43ae163f00201200c0b0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802aad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3c8e973120d70b1f821073696e74badd20d749810281b9dc7070e2100f01da8eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81002a802f404216e926c218e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55e2d20001ddebd72c08142091709601d72c081c12e25210b1e30f20d74addd74cd01211006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed5487fb70d0"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index c79fb4e..8a54766 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -200,7 +200,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { commit(); } - process_actions(cs, is_external, is_external); + process_actions(cs, is_external, is_extension); } () recv_external(slice in_msg_body) impure inline { From e6ef9e4ee56a0e5b9f7f9fbaa13804ae86748bf6 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 16:46:38 +0400 Subject: [PATCH 49/67] receive tons --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index fabf5f3..02d71d8 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021301000228000114ff00f4a413f4bcf2c80b0102012004020102f203011e20d70b1f82107369676ebaf2a67f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200d080201200a090019b45d1da89a10043ae43ae163f00201200c0b0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802aad001d0d60301c713dc01d72c232bc3a3748ea801fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3c8e973120d70b1f821073696e74badd20d749810281b9dc7070e2100f01da8eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81002a802f404216e926c218e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55e2d20001ddebd72c08142091709601d72c081c12e25210b1e30f20d74addd74cd01211006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed5487fb70d0"} \ No newline at end of file +{"hex":"b5ee9c72410214010002ab000114ff00f4a413f4bcf2c80b0102012005020102f203011e20d70b1f82107369676ebaf2a67f700401da8eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd8110201480f0602012008070019be5f0f6a2684080a0eb90fa02c0201200e090201200b0a0019b45d1da89a10043ae43ae163f00201200d0c0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802bad020d749c120dc20d70b1f2082106578746eba21821073696e74bab1dd02d0d60301c713dc0282106578746eba8eac8020d72101fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3ce30e111001f03120d749810281b9dc70708eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81102a802f404216e926c218e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55e2d20001ddebd72c08142091709601d72c081c12e25210b1e30f20d74addd74cd01312006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54b2d3ba0e"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 8a54766..fbb4cef 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -8,7 +8,7 @@ const int error::invalid_wallet_id = 34; const int error::invalid_signature = 35; const int error::expired = 36; const int error::external_send_message_must_have_ignore_errors_send_mode = 37; -const int error::invalid_message_type = 38; +const int error::invalid_message_operation = 38; const int error::add_extension = 39; const int error::remove_extension = 40; const int error::unspported_action = 41; @@ -25,14 +25,13 @@ const int size::public_key = 256; const int size::valid_until = 32; const int size::message_flags = 4; const int size::signature = 512; -const int size::message_type_prefix = 32; +const int size::message_operation_prefix = 32; const int size::address_hash_size = 256; const int size::query_id = 64; const int prefix::signed_external = 0x7369676E; const int prefix::signed_internal = 0x73696E74; - -(slice, int) check_and_remove_extension_action_prefix(slice body) impure asm "x{6578746E} SDBEGINSQ"; +const int prefix::extension_action = 0x6578746E; (slice, int) check_and_remove_add_extension_prefix(slice body) impure asm "x{02} SDBEGINSQ"; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{03} SDBEGINSQ"; @@ -160,7 +159,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { slice signature = in_msg_body.get_last_bits(size::signature); slice signed_slice = in_msg_body.remove_last_bits(size::signature); - slice cs = signed_slice.skip_bits(size::message_type_prefix); ;; skip signed_internal or signed_external prefix + slice cs = signed_slice.skip_bits(size::message_operation_prefix); ;; skip signed_internal or signed_external prefix (int wallet_id, int valid_until, int seqno) = (cs~load_uint(size::wallet_id), cs~load_uint(size::valid_until), cs~load_uint(size::seqno)); slice data_slice = get_data().begin_parse(); @@ -204,21 +203,24 @@ cell verify_c5_actions(cell c5, int is_external) inline { } () recv_external(slice in_msg_body) impure inline { - throw_unless(error::invalid_message_type, in_msg_body.preload_uint(size::message_type_prefix) == prefix::signed_external); + throw_unless(error::invalid_message_operation, in_msg_body.preload_uint(size::message_operation_prefix) == prefix::signed_external); process_signed_request(in_msg_body, true, false); } ;; ------------------------------------------------------------------------------------------------ () recv_internal(cell in_msg_full, slice in_msg_body) impure inline { - ;; return_if(in_msg_body.slice_refs_empty?()); ;; message with actions always have a ref todo + return_if(in_msg_body.slice_bits() < size::message_operation_prefix); + int op = in_msg_body.preload_uint(size::message_operation_prefix); + return_unless((op == prefix::extension_action) | (op == prefix::signed_internal)); slice in_msg_full_slice = in_msg_full.begin_parse(); slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool ;; skip bounced messages - if bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. return_if(count_trailing_ones(message_flags_slice)); - if (in_msg_body~check_and_remove_extension_action_prefix()) { + if (op == prefix::extension_action) { + in_msg_body~skip_bits(size::message_operation_prefix); ;; Authenticate extension by its address. (int sender_address_wc, int sender_address_hash) = parse_std_addr(in_msg_full_slice~load_msg_addr()); @@ -242,9 +244,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { } - return_unless(in_msg_body.preload_uint(size::message_type_prefix) == prefix::signed_internal); ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(in_msg_body.slice_bits() < size::message_type_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); + return_if(in_msg_body.slice_bits() < size::message_operation_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); process_signed_request(in_msg_body, false, false); } From 39d0feb8d5dc59ef94c977c2762bee0ad1809ee5 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 16:47:40 +0400 Subject: [PATCH 50/67] get_wallet_id -> get_subwallet_id, same with v4 --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 02d71d8..64d2d02 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c72410214010002ab000114ff00f4a413f4bcf2c80b0102012005020102f203011e20d70b1f82107369676ebaf2a67f700401da8eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd8110201480f0602012008070019be5f0f6a2684080a0eb90fa02c0201200e090201200b0a0019b45d1da89a10043ae43ae163f00201200d0c0011b262fb513435c280200017b325fb51341c75c875c2c7e00019bb39ced44d08041d721d70bff802bad020d749c120dc20d70b1f2082106578746eba21821073696e74bab1dd02d0d60301c713dc0282106578746eba8eac8020d72101fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3ce30e111001f03120d749810281b9dc70708eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81102a802f404216e926c218e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55e2d20001ddebd72c08142091709601d72c081c12e25210b1e30f20d74addd74cd01312006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed54b2d3ba0e"} \ No newline at end of file +{"hex":"b5ee9c72410214010002ab000114ff00f4a413f4bcf2c80b0102012005020102f203011e20d70b1f82107369676ebaf2a67f700401da8eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd8110201480f0602012008070019be5f0f6a2684080a0eb90fa02c0201200c090201480b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0e0d0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002bad020d749c120dc20d70b1f2082106578746eba21821073696e74bab1dd02d0d60301c713dc0282106578746eba8eac8020d72101fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3ce30e111001f03120d749810281b9dc70708eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81102a802f404216e926c218e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55e2d20001ddebd72c08142091709601d72c081c12e25210b1e30f20d74addd74cd01312006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed543e0648b9"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index fbb4cef..76ab150 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -263,7 +263,7 @@ int seqno() method_id { .preload_uint(size::seqno); } -int get_wallet_id() method_id { +int get_subwallet_id() method_id { return get_data().begin_parse() .skip_bits(size::bool + size::seqno) .preload_uint(size::wallet_id); From 444dddea493a25db12ab10129e90a065d8db42bd Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 19:06:21 +0400 Subject: [PATCH 51/67] return_unless not work --- contracts/wallet_v5.fc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 76ab150..7d7a9c6 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -148,7 +148,9 @@ cell verify_c5_actions(cell c5, int is_external) inline { } else { throw(error::unspported_action); } - return_unless(cs.slice_refs()); + ifnot (cs.slice_refs()) { + return (); + } cs = cs.preload_ref().begin_parse(); } } From 3bd769e937e91816f5ea4799c3df1ad10b3b5d81 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 26 Jun 2024 20:59:08 +0400 Subject: [PATCH 52/67] update wrappers and tests --- tests/actions.ts | 52 +++- tests/wallet-v5-extensions.spec.ts | 18 +- tests/wallet-v5-external.spec.ts | 468 ++++++++++++++--------------- tests/wallet-v5-internal.spec.ts | 140 ++++++--- wrappers/wallet-v5.ts | 2 +- 5 files changed, 386 insertions(+), 294 deletions(-) diff --git a/tests/actions.ts b/tests/actions.ts index 9788e53..e4ff070 100644 --- a/tests/actions.ts +++ b/tests/actions.ts @@ -89,21 +89,47 @@ function packActionsListOut(actions: (OutAction | ExtendedAction)[]): Cell { .endCell(); } -function packActionsListExtended(actions: (OutAction | ExtendedAction)[]): Cell { - const [action, ...rest] = actions; - - if (!action || !isExtendedAction(action)) { - return beginCell() - .storeUint(0, 1) - .storeRef(packActionsListOut(actions.slice().reverse())) // tvm handles actions from c5 in reversed order - .endCell(); +function packExtendedActions(extendedActions: ExtendedAction[]): Cell { + const first = extendedActions[0]; + const rest = extendedActions.slice(1); + let builder = beginCell() + .storeSlice(first.serialize().beginParse()); + if (rest.length > 0) { + builder = builder.storeRef(packExtendedActions(extendedActions.slice(1))); } + return builder.endCell(); +} - return beginCell() - .storeUint(1, 1) - .storeSlice(action.serialize().beginParse()) - .storeRef(packActionsListExtended(rest)) - .endCell(); +function packActionsListExtended(actions: (OutAction | ExtendedAction)[]): Cell { + const extendedActions: ExtendedAction[] = []; + const outActions: OutAction[] = []; + actions.forEach(action => { + if (isExtendedAction(action)) { + extendedActions.push(action); + } else { + outActions.push(action); + } + }); + + let builder = beginCell(); + if (outActions.length === 0) { + builder = builder.storeUint(0, 1); + } else { + builder = builder.storeMaybeRef(packActionsListOut(outActions.slice().reverse())); + } + if (extendedActions.length === 0) { + builder = builder.storeUint(0, 1); + } else { + const first = extendedActions[0]; + const rest = extendedActions.slice(1); + builder = builder + .storeUint(1, 1) + .storeSlice(first.serialize().beginParse()); + if (rest.length > 0) { + builder = builder.storeRef(packExtendedActions(rest)); + } + } + return builder.endCell(); } export function packActionsList(actions: (OutAction | ExtendedAction)[]): Cell { diff --git a/tests/wallet-v5-extensions.spec.ts b/tests/wallet-v5-extensions.spec.ts index f70e186..68929d7 100644 --- a/tests/wallet-v5-extensions.spec.ts +++ b/tests/wallet-v5-extensions.spec.ts @@ -326,11 +326,15 @@ describe('Wallet V5 extensions auth', () => { await walletV5.sendInternalSignedMessage(sender, { value: toNano(0.1), body: createBody(packActionsList([ - new ActionAddExtension(sender.address!), - new ActionSetSignatureAuthAllowed(false) + new ActionAddExtension(sender.address!) ])) }); + const receipt0 = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: packActionsList([new ActionSetSignatureAuthAllowed(false)]) + }); + const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const forwardValue = toNano(0.001); const receiverBalanceBefore = (await blockchain.getContract(testReceiver)).balance; @@ -362,11 +366,17 @@ describe('Wallet V5 extensions auth', () => { await walletV5.sendInternalSignedMessage(sender, { value: toNano(0.1), body: createBody(packActionsList([ - new ActionAddExtension(sender.address!), - new ActionSetSignatureAuthAllowed(false) + new ActionAddExtension(sender.address!) ])) }); + await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: packActionsList([ + new ActionSetSignatureAuthAllowed(false) + ]) + }); + const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); expect(isSignatureAuthAllowed).toEqual(0); diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index 1660055..fd693fe 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -147,13 +147,13 @@ describe('Wallet V5 sign auth external', () => { .endCell(); const actionsList = beginCell() - .storeUint(0, 1) - .storeRef( + .storeMaybeRef( beginCell() - .storeRef(beginCell().endCell()) + .storeRef(beginCell().endCell()) // empty child - end of action list .storeSlice(sendTxactionAction.beginParse()) .endCell() ) + .storeUint(0, 1) // no other_actions .endCell(); if (config.microscope) @@ -195,8 +195,8 @@ describe('Wallet V5 sign auth external', () => { .endCell(); const actionsList = beginCell() - .storeUint(1, 1) - .storeRef(beginCell().storeUint(0, 1).storeRef(beginCell().endCell()).endCell()) + .storeUint(0, 1) // no c5 actions + .storeUint(1, 1) // have other actions .storeSlice(addExtensionAction.beginParse()) .endCell(); @@ -693,7 +693,7 @@ describe('Wallet V5 sign auth external', () => { expect(walletBalanceBefore).toEqual(walletBalanceAfter); }); - it('Should fail disallowing signature auth with no exts', async () => { + it('only_extension_can_change_signature_mode', async () => { const actionsList = packActionsList([ new ActionSetSignatureAuthAllowed(false) ]); @@ -704,237 +704,237 @@ describe('Wallet V5 sign auth external', () => { (receipt.transactions[0].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(42); + ).toEqual(46); // only_extension_can_change_signature_mode const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); expect(isSignatureAuthAllowed).toEqual(-1); }); - it('Should fail allowing signature auth when allowed', async () => { - const actionsList = packActionsList([ - new ActionSetSignatureAuthAllowed(true) - ]); - const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); - - expect( - ( - (receipt.transactions[0].description as TransactionDescriptionGeneric) - .computePhase as TransactionComputeVm - ).exitCode - ).toEqual(43); - - const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); - expect(isSignatureAuthAllowed).toEqual(-1); - }); - - it('Should add ext and disallow signature auth', async () => { - const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - - const actionsList = packActionsList([ - new ActionAddExtension(testExtension), - new ActionSetSignatureAuthAllowed(false) - ]); - const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); - accountForGas(receipt.transactions); - - expect( - ( - (receipt.transactions[0].description as TransactionDescriptionGeneric) - .computePhase as TransactionComputeVm - ).exitCode - ).toEqual(0); - - const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); - expect(isSignatureAuthAllowed).toEqual(0); - - const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno); - }); - - it('Should add ext and disallow signature auth in separate txs', async () => { - const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - - const actionsList = packActionsList([ - new ActionAddExtension(testExtension) - ]); - const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); - accountForGas(receipt.transactions); - - expect( - ( - (receipt.transactions[0].description as TransactionDescriptionGeneric) - .computePhase as TransactionComputeVm - ).exitCode - ).toEqual(0); - - const extensionsDict = Dictionary.loadDirect( - Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(1), - await walletV5.getExtensions() - ); - - expect(extensionsDict.size).toEqual(1); - - expect(extensionsDict.get(packAddress(testExtension))).toEqual( - -1n - ); - - const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); - expect(isSignatureAuthAllowed).toEqual(-1); - - const actionsList2 = packActionsList([ - new ActionSetSignatureAuthAllowed(false) - ]); - const receipt2 = await walletV5.sendExternalSignedMessage(createBody(actionsList2)); - accountForGas(receipt2.transactions); - - expect( - ( - (receipt2.transactions[0].description as TransactionDescriptionGeneric) - .computePhase as TransactionComputeVm - ).exitCode - ).toEqual(0); - - const isSignatureAuthAllowed2 = await walletV5.getIsSignatureAuthAllowed(); - expect(isSignatureAuthAllowed2).toEqual(0); - - const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno); - }); - - it('Should add ext, disallow sign, allow sign, remove ext in one tx; send in other', async () => { - const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - - const actionsList = packActionsList([ - new ActionAddExtension(testExtension), - new ActionSetSignatureAuthAllowed(false), - new ActionSetSignatureAuthAllowed(true), - new ActionRemoveExtension(testExtension) - ]); - const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); - accountForGas(receipt.transactions); - - expect( - ( - (receipt.transactions[0].description as TransactionDescriptionGeneric) - .computePhase as TransactionComputeVm - ).exitCode - ).toEqual(0); - - const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); - expect(isSignatureAuthAllowed).toEqual(-1); - - const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno); - - const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - const forwardValue = toNano(0.001); - - const receiverBalanceBefore = (await blockchain.getContract(testReceiver)).balance; - - const msg = createMsgInternal({ dest: testReceiver, value: forwardValue }); - - const actionsList2 = packActionsList([ - new ActionSendMsg(SendMode.PAY_GAS_SEPARATELY, msg) - ]); - - const receipt2 = await walletV5.sendExternalSignedMessage(createBody(actionsList2)); - - expect(receipt2.transactions.length).toEqual(2); - accountForGas(receipt2.transactions); - - expect(receipt2.transactions).toHaveTransaction({ - from: walletV5.address, - to: testReceiver, - value: forwardValue - }); - - const fee = receipt2.transactions[1].totalFees.coins; - const receiverBalanceAfter = (await blockchain.getContract(testReceiver)).balance; - expect(receiverBalanceAfter).toEqual(receiverBalanceBefore + forwardValue - fee); - }); - - it('Should fail removing last extension with signature auth disabled', async () => { - const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - - const actionsList = packActionsList([ - new ActionAddExtension(testExtension), - new ActionSetSignatureAuthAllowed(false), - new ActionRemoveExtension(testExtension) - ]); - const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); - accountForGas(receipt.transactions); - - expect( - ( - (receipt.transactions[0].description as TransactionDescriptionGeneric) - .computePhase as TransactionComputeVm - ).exitCode - ).toEqual(44); - - const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); - expect(isSignatureAuthAllowed).toEqual(-1); - }); - - it('Should fail disallowing signature auth twice in tx', async () => { - const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - - const actionsList = packActionsList([ - new ActionAddExtension(testExtension), - new ActionSetSignatureAuthAllowed(false), - new ActionSetSignatureAuthAllowed(false) - ]); - const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); - accountForGas(receipt.transactions); - - expect( - ( - (receipt.transactions[0].description as TransactionDescriptionGeneric) - .computePhase as TransactionComputeVm - ).exitCode - ).toEqual(43); - - const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); - expect(isSignatureAuthAllowed).toEqual(-1); // throw when handling, packet is dropped - }); - - it('Should add ext, disallow sig auth; fail different signed tx', async () => { - const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); - - const actionsList = packActionsList([ - new ActionAddExtension(testExtension), - new ActionSetSignatureAuthAllowed(false) - ]); - const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); - accountForGas(receipt.transactions); - - expect( - ( - (receipt.transactions[0].description as TransactionDescriptionGeneric) - .computePhase as TransactionComputeVm - ).exitCode - ).toEqual(0); - - const extensionsDict = Dictionary.loadDirect( - Dictionary.Keys.BigUint(256), - Dictionary.Values.BigInt(1), - await walletV5.getExtensions() - ); - - expect(extensionsDict.size).toEqual(1); - - expect(extensionsDict.get(packAddress(testExtension))).toEqual( - -1n - ); - - const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); - expect(isSignatureAuthAllowed).toEqual(0); - - const contract_seqno = await walletV5.getSeqno(); - expect(contract_seqno).toEqual(seqno); - - await disableConsoleError(() => - expect(walletV5.sendExternalSignedMessage(createBody(packActionsList([])))).rejects.toThrow() - ); - }); + // it('Should fail allowing signature auth when allowed', async () => { + // const actionsList = packActionsList([ + // new ActionSetSignatureAuthAllowed(true) + // ]); + // const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); + // + // expect( + // ( + // (receipt.transactions[0].description as TransactionDescriptionGeneric) + // .computePhase as TransactionComputeVm + // ).exitCode + // ).toEqual(43); + // + // const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); + // expect(isSignatureAuthAllowed).toEqual(-1); + // }); + + // it('Should add ext and disallow signature auth', async () => { + // const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); + // + // const actionsList = packActionsList([ + // new ActionAddExtension(testExtension), + // new ActionSetSignatureAuthAllowed(false) + // ]); + // const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); + // accountForGas(receipt.transactions); + // + // expect( + // ( + // (receipt.transactions[0].description as TransactionDescriptionGeneric) + // .computePhase as TransactionComputeVm + // ).exitCode + // ).toEqual(0); + // + // const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); + // expect(isSignatureAuthAllowed).toEqual(0); + // + // const contract_seqno = await walletV5.getSeqno(); + // expect(contract_seqno).toEqual(seqno); + // }); + + // it('Should add ext and disallow signature auth in separate txs', async () => { + // const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); + // + // const actionsList = packActionsList([ + // new ActionAddExtension(testExtension) + // ]); + // const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); + // accountForGas(receipt.transactions); + // + // expect( + // ( + // (receipt.transactions[0].description as TransactionDescriptionGeneric) + // .computePhase as TransactionComputeVm + // ).exitCode + // ).toEqual(0); + // + // const extensionsDict = Dictionary.loadDirect( + // Dictionary.Keys.BigUint(256), + // Dictionary.Values.BigInt(1), + // await walletV5.getExtensions() + // ); + // + // expect(extensionsDict.size).toEqual(1); + // + // expect(extensionsDict.get(packAddress(testExtension))).toEqual( + // -1n + // ); + // + // const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); + // expect(isSignatureAuthAllowed).toEqual(-1); + // + // const actionsList2 = packActionsList([ + // new ActionSetSignatureAuthAllowed(false) + // ]); + // const receipt2 = await walletV5.sendExternalSignedMessage(createBody(actionsList2)); + // accountForGas(receipt2.transactions); + // + // expect( + // ( + // (receipt2.transactions[0].description as TransactionDescriptionGeneric) + // .computePhase as TransactionComputeVm + // ).exitCode + // ).toEqual(0); + // + // const isSignatureAuthAllowed2 = await walletV5.getIsSignatureAuthAllowed(); + // expect(isSignatureAuthAllowed2).toEqual(0); + // + // const contract_seqno = await walletV5.getSeqno(); + // expect(contract_seqno).toEqual(seqno); + // }); + // + // it('Should add ext, disallow sign, allow sign, remove ext in one tx; send in other', async () => { + // const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); + // + // const actionsList = packActionsList([ + // new ActionAddExtension(testExtension), + // new ActionSetSignatureAuthAllowed(false), + // new ActionSetSignatureAuthAllowed(true), + // new ActionRemoveExtension(testExtension) + // ]); + // const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); + // accountForGas(receipt.transactions); + // + // expect( + // ( + // (receipt.transactions[0].description as TransactionDescriptionGeneric) + // .computePhase as TransactionComputeVm + // ).exitCode + // ).toEqual(0); + // + // const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); + // expect(isSignatureAuthAllowed).toEqual(-1); + // + // const contract_seqno = await walletV5.getSeqno(); + // expect(contract_seqno).toEqual(seqno); + // + // const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); + // const forwardValue = toNano(0.001); + // + // const receiverBalanceBefore = (await blockchain.getContract(testReceiver)).balance; + // + // const msg = createMsgInternal({ dest: testReceiver, value: forwardValue }); + // + // const actionsList2 = packActionsList([ + // new ActionSendMsg(SendMode.PAY_GAS_SEPARATELY, msg) + // ]); + // + // const receipt2 = await walletV5.sendExternalSignedMessage(createBody(actionsList2)); + // + // expect(receipt2.transactions.length).toEqual(2); + // accountForGas(receipt2.transactions); + // + // expect(receipt2.transactions).toHaveTransaction({ + // from: walletV5.address, + // to: testReceiver, + // value: forwardValue + // }); + // + // const fee = receipt2.transactions[1].totalFees.coins; + // const receiverBalanceAfter = (await blockchain.getContract(testReceiver)).balance; + // expect(receiverBalanceAfter).toEqual(receiverBalanceBefore + forwardValue - fee); + // }); + // + // it('Should fail removing last extension with signature auth disabled', async () => { + // const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); + // + // const actionsList = packActionsList([ + // new ActionAddExtension(testExtension), + // new ActionSetSignatureAuthAllowed(false), + // new ActionRemoveExtension(testExtension) + // ]); + // const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); + // accountForGas(receipt.transactions); + // + // expect( + // ( + // (receipt.transactions[0].description as TransactionDescriptionGeneric) + // .computePhase as TransactionComputeVm + // ).exitCode + // ).toEqual(44); + // + // const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); + // expect(isSignatureAuthAllowed).toEqual(-1); + // }); + // + // it('Should fail disallowing signature auth twice in tx', async () => { + // const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); + // + // const actionsList = packActionsList([ + // new ActionAddExtension(testExtension), + // new ActionSetSignatureAuthAllowed(false), + // new ActionSetSignatureAuthAllowed(false) + // ]); + // const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); + // accountForGas(receipt.transactions); + // + // expect( + // ( + // (receipt.transactions[0].description as TransactionDescriptionGeneric) + // .computePhase as TransactionComputeVm + // ).exitCode + // ).toEqual(43); + // + // const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); + // expect(isSignatureAuthAllowed).toEqual(-1); // throw when handling, packet is dropped + // }); + // + // it('Should add ext, disallow sig auth; fail different signed tx', async () => { + // const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); + // + // const actionsList = packActionsList([ + // new ActionAddExtension(testExtension), + // new ActionSetSignatureAuthAllowed(false) + // ]); + // const receipt = await walletV5.sendExternalSignedMessage(createBody(actionsList)); + // accountForGas(receipt.transactions); + // + // expect( + // ( + // (receipt.transactions[0].description as TransactionDescriptionGeneric) + // .computePhase as TransactionComputeVm + // ).exitCode + // ).toEqual(0); + // + // const extensionsDict = Dictionary.loadDirect( + // Dictionary.Keys.BigUint(256), + // Dictionary.Values.BigInt(1), + // await walletV5.getExtensions() + // ); + // + // expect(extensionsDict.size).toEqual(1); + // + // expect(extensionsDict.get(packAddress(testExtension))).toEqual( + // -1n + // ); + // + // const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); + // expect(isSignatureAuthAllowed).toEqual(0); + // + // const contract_seqno = await walletV5.getSeqno(); + // expect(contract_seqno).toEqual(seqno); + // + // await disableConsoleError(() => + // expect(walletV5.sendExternalSignedMessage(createBody(packActionsList([])))).rejects.toThrow() + // ); + // }); }); diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index 60463a5..0c9d35b 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -140,13 +140,13 @@ describe('Wallet V5 sign auth internal', () => { .endCell(); const actionsList = beginCell() - .storeUint(0, 1) - .storeRef( + .storeMaybeRef( beginCell() .storeRef(beginCell().endCell()) .storeSlice(sendTxactionAction.beginParse()) .endCell() ) + .storeUint(0, 1) // no other actions .endCell(); if (config.microscope) @@ -191,8 +191,8 @@ describe('Wallet V5 sign auth internal', () => { .endCell(); const actionsList = beginCell() + .storeUint(0, 1) // no c5 actions .storeUint(1, 1) - .storeRef(beginCell().storeUint(0, 1).storeRef(beginCell().endCell()).endCell()) .storeSlice(addExtensionAction.beginParse()) .endCell(); @@ -856,15 +856,23 @@ describe('Wallet V5 sign auth internal', () => { it('Should fail disallowing signature auth with no exts', async () => { const actionsList = packActionsList([ - new ActionSetSignatureAuthAllowed(false) + new ActionAddExtension(sender.address!) ]); - const receipt = await walletV5.sendInternal(sender, { + await walletV5.sendInternal(sender, { sendMode: SendMode.PAY_GAS_SEPARATELY, value: toNano(0.1), body: createBody(actionsList) }); + const receipt = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: packActionsList([ + new ActionRemoveExtension(sender.address!), + new ActionSetSignatureAuthAllowed(false) + ]) + }); + expect(receipt.transactions.length).toEqual(3); // sender_wallet -> wallet_v5 -> bounced expect( @@ -879,14 +887,21 @@ describe('Wallet V5 sign auth internal', () => { }); it('Should fail allowing signature auth when allowed', async () => { + await walletV5.sendInternal(sender, { + sendMode: SendMode.PAY_GAS_SEPARATELY, + value: toNano(0.1), + body: createBody(packActionsList([ + new ActionAddExtension(sender.address!) + ])) + }); + const actionsList = packActionsList([ new ActionSetSignatureAuthAllowed(true) ]); - const receipt = await walletV5.sendInternal(sender, { - sendMode: SendMode.PAY_GAS_SEPARATELY, - value: toNano(0.1), - body: createBody(actionsList) + const receipt = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: actionsList }); expect(receipt.transactions.length).toEqual(3); // sender_wallet -> wallet_v5 -> bounced @@ -903,6 +918,14 @@ describe('Wallet V5 sign auth internal', () => { }); it('Should add ext and disallow signature auth', async () => { + await walletV5.sendInternal(sender, { + sendMode: SendMode.PAY_GAS_SEPARATELY, + value: toNano(0.1), + body: createBody(packActionsList([ + new ActionAddExtension(sender.address!) + ])) + }); + const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const actionsList = packActionsList([ @@ -910,10 +933,9 @@ describe('Wallet V5 sign auth internal', () => { new ActionSetSignatureAuthAllowed(false) ]); - const receipt = await walletV5.sendInternal(sender, { - sendMode: SendMode.PAY_GAS_SEPARATELY, - value: toNano(0.1), - body: createBody(actionsList) + const receipt = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: actionsList }); expect(receipt.transactions.length).toEqual(2); @@ -926,7 +948,7 @@ describe('Wallet V5 sign auth internal', () => { await walletV5.getExtensions() ); - expect(extensionsDict.size).toEqual(1); + expect(extensionsDict.size).toEqual(2); expect(extensionsDict.get(packAddress(testExtension))).toEqual( -1n @@ -947,16 +969,23 @@ describe('Wallet V5 sign auth internal', () => { }); it('Should add ext and disallow signature auth in separate txs', async () => { + await walletV5.sendInternal(sender, { + sendMode: SendMode.PAY_GAS_SEPARATELY, + value: toNano(0.1), + body: createBody(packActionsList([ + new ActionAddExtension(sender.address!) + ])) + }); + const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const actionsList = packActionsList([ new ActionAddExtension(testExtension) ]); - const receipt = await walletV5.sendInternal(sender, { - sendMode: SendMode.PAY_GAS_SEPARATELY, - value: toNano(0.1), - body: createBody(actionsList) + const receipt = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: actionsList }); expect(receipt.transactions.length).toEqual(2); @@ -969,7 +998,7 @@ describe('Wallet V5 sign auth internal', () => { await walletV5.getExtensions() ); - expect(extensionsDict.size).toEqual(1); + expect(extensionsDict.size).toEqual(2); expect(extensionsDict.get(packAddress(testExtension))).toEqual( -1n @@ -989,10 +1018,9 @@ describe('Wallet V5 sign auth internal', () => { new ActionSetSignatureAuthAllowed(false) ]); - const receipt2 = await walletV5.sendInternal(sender, { - sendMode: SendMode.PAY_GAS_SEPARATELY, - value: toNano(0.1), - body: createBody(actionsList2) + const receipt2 = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: actionsList2 }); expect(receipt2.transactions.length).toEqual(2); @@ -1014,6 +1042,14 @@ describe('Wallet V5 sign auth internal', () => { }); it('Should add ext, disallow sign, allow sign, remove ext in one tx; send in other', async () => { + await walletV5.sendInternal(sender, { + sendMode: SendMode.PAY_GAS_SEPARATELY, + value: toNano(0.1), + body: createBody(packActionsList([ + new ActionAddExtension(sender.address!) + ])) + }); + const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const actionsList = packActionsList([ @@ -1022,11 +1058,9 @@ describe('Wallet V5 sign auth internal', () => { new ActionSetSignatureAuthAllowed(true), new ActionRemoveExtension(testExtension), ]); - - const receipt = await walletV5.sendInternal(sender, { - sendMode: SendMode.PAY_GAS_SEPARATELY, - value: toNano(0.1), - body: createBody(actionsList) + const receipt = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: actionsList }); expect(receipt.transactions.length).toEqual(2); @@ -1077,18 +1111,26 @@ describe('Wallet V5 sign auth internal', () => { }); it('Should fail removing last extension with signature auth disabled', async () => { + await walletV5.sendInternal(sender, { + sendMode: SendMode.PAY_GAS_SEPARATELY, + value: toNano(0.1), + body: createBody(packActionsList([ + new ActionAddExtension(sender.address!) + ])) + }); + const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const actionsList = packActionsList([ new ActionAddExtension(testExtension), new ActionSetSignatureAuthAllowed(false), - new ActionRemoveExtension(testExtension) + new ActionRemoveExtension(testExtension), + new ActionRemoveExtension(sender.address!) ]); - const receipt = await walletV5.sendInternal(sender, { - sendMode: SendMode.PAY_GAS_SEPARATELY, - value: toNano(0.1), - body: createBody(actionsList) + const receipt = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: actionsList }); expect(receipt.transactions.length).toEqual(3); // sender_wallet -> wallet_v5 -> bounced @@ -1106,6 +1148,14 @@ describe('Wallet V5 sign auth internal', () => { }); it('Should fail disallowing signature auth twice in tx', async () => { + await walletV5.sendInternal(sender, { + sendMode: SendMode.PAY_GAS_SEPARATELY, + value: toNano(0.1), + body: createBody(packActionsList([ + new ActionAddExtension(sender.address!) + ])) + }); + const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const actionsList = packActionsList([ @@ -1114,10 +1164,9 @@ describe('Wallet V5 sign auth internal', () => { new ActionSetSignatureAuthAllowed(false) ]); - const receipt = await walletV5.sendInternal(sender, { - sendMode: SendMode.PAY_GAS_SEPARATELY, - value: toNano(0.1), - body: createBody(actionsList) + const receipt = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: actionsList }); expect(receipt.transactions.length).toEqual(3); // sender_wallet -> wallet_v5 -> bounced @@ -1135,6 +1184,14 @@ describe('Wallet V5 sign auth internal', () => { }); it('Should add ext, disallow sig auth; fail different signed tx', async () => { + await walletV5.sendInternal(sender, { + sendMode: SendMode.PAY_GAS_SEPARATELY, + value: toNano(0.1), + body: createBody(packActionsList([ + new ActionAddExtension(sender.address!) + ])) + }); + const testExtension = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y'); const actionsList = packActionsList([ @@ -1142,10 +1199,9 @@ describe('Wallet V5 sign auth internal', () => { new ActionSetSignatureAuthAllowed(false) ]); - const receipt = await walletV5.sendInternal(sender, { - sendMode: SendMode.PAY_GAS_SEPARATELY, - value: toNano(0.1), - body: createBody(actionsList) + const receipt = await walletV5.sendInternalMessageFromExtension(sender, { + value: toNano('0.1'), + body: actionsList }); expect(receipt.transactions.length).toEqual(2); @@ -1157,7 +1213,7 @@ describe('Wallet V5 sign auth internal', () => { await walletV5.getExtensions() ); - expect(extensionsDict.size).toEqual(1); + expect(extensionsDict.size).toEqual(2); expect(extensionsDict.get(packAddress(testExtension))).toEqual( -1n diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index 9a90c8c..5e59c57 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -212,7 +212,7 @@ export class WalletV5 implements Contract { } async getWalletId(provider: ContractProvider) { - const result = await provider.get('get_wallet_id', []); + const result = await provider.get('get_subwallet_id', []); return WalletId.deserialize(result.stack.readBigNumber()); } From 38f7e3a6783fd69a9837b862af14b88a62a33902 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Thu, 27 Jun 2024 13:27:15 +0400 Subject: [PATCH 53/67] fix typo --- contracts/wallet_v5.fc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 7d7a9c6..af26d5e 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -11,7 +11,7 @@ const int error::external_send_message_must_have_ignore_errors_send_mode = 37; const int error::invalid_message_operation = 38; const int error::add_extension = 39; const int error::remove_extension = 40; -const int error::unspported_action = 41; +const int error::unsupported_action = 41; const int error::disable_signature_when_extensions_is_empty = 42; const int error::this_signature_mode_already_set = 43; const int error::remove_last_extension_when_signature_disabled = 44; @@ -146,7 +146,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { .store_slice(data_tail) ;; seqno, wallet_id, public_key, extensions .end_cell()); } else { - throw(error::unspported_action); + throw(error::unsupported_action); } ifnot (cs.slice_refs()) { return (); From 64cff845f50f40d67c901b42237b3ac9051a3134 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 28 Jun 2024 12:43:24 +0400 Subject: [PATCH 54/67] change error codes to avoid collision with tvm-exit-codes --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 30 +++++++++++++++--------------- tests/wallet-v5-external.spec.ts | 8 ++++---- tests/wallet-v5-internal.spec.ts | 22 +++++++++++----------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 64d2d02..89e1d96 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c72410214010002ab000114ff00f4a413f4bcf2c80b0102012005020102f203011e20d70b1f82107369676ebaf2a67f700401da8eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd8110201480f0602012008070019be5f0f6a2684080a0eb90fa02c0201200c090201480b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0e0d0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002bad020d749c120dc20d70b1f2082106578746eba21821073696e74bab1dd02d0d60301c713dc0282106578746eba8eac8020d72101fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3ce30e111001f03120d749810281b9dc70708eeaeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109929945f0bdb31e1f223df02b35007b0f2605125baf2a15037baf2a2f823bbf2642392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81102a802f404216e926c218e290221d739309420c700b38e19d72820761e436c20d71d06c712c2005220b0f265d74cd73930e85bed55e2d20001ddebd72c08142091709601d72c081c12e25210b1e30f20d74addd74cd01312006a30d72c08248e2a21f2aed200ed44d0d2005113baf26b54503091319b01810140d721d70a00f2aae2c8ca0058cf16c9ed5492f229e2008e01fa4001fa44f828fa443058baf2aded44d0810141d718f405049c7fc8ca0040048307f453f2a78e12038307f45bf2a822d70a00216e01b3b0f26ce2c85003cf1612f400c9ed543e0648b9"} \ No newline at end of file +{"hex":"b5ee9c72410214010002c7000114ff00f4a413f4bcf2c80b0102012005020102f203012020d70b1f82107369676ebaf2e08a7f700401e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd8110201480f0602012008070019be5f0f6a2684080a0eb90fa02c0201200c090201480b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0e0d0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002bad020d749c120dc20d70b1f2082106578746eba21821073696e74bab1dd02d0d60301c713dc0282106578746eba8eac8020d72101fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3ce30e111001fa3120d749810281b9dc70708eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81102baeda2edfb02f404216e926c218e2a0221d739309420c700b38e1ad72820761e436c20d71d06c712c2005220b0f2d089d74cd73930e85bed55e2d20001ddebd72c08142091709601d72c081c12e25210b1e30f20d74a935bdb31e1d74cd01312007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed5458c947bd"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index af26d5e..8e6e6b0 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -2,21 +2,21 @@ #include "imports/stdlib.fc"; -const int error::signature_disabled = 32; -const int error::invalid_seqno = 33; -const int error::invalid_wallet_id = 34; -const int error::invalid_signature = 35; -const int error::expired = 36; -const int error::external_send_message_must_have_ignore_errors_send_mode = 37; -const int error::invalid_message_operation = 38; -const int error::add_extension = 39; -const int error::remove_extension = 40; -const int error::unsupported_action = 41; -const int error::disable_signature_when_extensions_is_empty = 42; -const int error::this_signature_mode_already_set = 43; -const int error::remove_last_extension_when_signature_disabled = 44; -const int error::extension_wrong_workchain = 45; -const int error::only_extension_can_change_signature_mode = 46; +const int error::signature_disabled = 132; +const int error::invalid_seqno = 133; +const int error::invalid_wallet_id = 134; +const int error::invalid_signature = 135; +const int error::expired = 136; +const int error::external_send_message_must_have_ignore_errors_send_mode = 137; +const int error::invalid_message_operation = 138; +const int error::add_extension = 139; +const int error::remove_extension = 140; +const int error::unsupported_action = 141; +const int error::disable_signature_when_extensions_is_empty = 142; +const int error::this_signature_mode_already_set = 143; +const int error::remove_last_extension_when_signature_disabled = 144; +const int error::extension_wrong_workchain = 145; +const int error::only_extension_can_change_signature_mode = 146; const int size::bool = 1; const int size::seqno = 32; diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index fd693fe..893b179 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -386,7 +386,7 @@ describe('Wallet V5 sign auth external', () => { (receipt.transactions[0].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(41); + ).toEqual(141); }); it('Should fail SetCode action', async () => { @@ -429,7 +429,7 @@ describe('Wallet V5 sign auth external', () => { (receipt.transactions[0].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(39); + ).toEqual(139); const extensionsDict2 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), @@ -453,7 +453,7 @@ describe('Wallet V5 sign auth external', () => { (receipt.transactions[0].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(40); + ).toEqual(140); const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), @@ -704,7 +704,7 @@ describe('Wallet V5 sign auth external', () => { (receipt.transactions[0].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(46); // only_extension_can_change_signature_mode + ).toEqual(146); // only_extension_can_change_signature_mode const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); expect(isSignatureAuthAllowed).toEqual(-1); diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index 0c9d35b..597a197 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -370,7 +370,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(41); + ).toEqual(141); }); it('Should fail SetCode action', async () => { @@ -421,7 +421,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(39); + ).toEqual(139); const extensionsDict2 = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), @@ -448,7 +448,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(40); + ).toEqual(140); const extensionsDict = Dictionary.loadDirect( Dictionary.Keys.BigUint(256), @@ -599,7 +599,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(33); + ).toEqual(133); expect(receipt.transactions).not.toHaveTransaction({ from: walletV5.address, @@ -644,7 +644,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(36); + ).toEqual(136); expect(receipt.transactions).not.toHaveTransaction({ from: walletV5.address, @@ -689,7 +689,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(34); + ).toEqual(134); expect(receipt.transactions).not.toHaveTransaction({ from: walletV5.address, @@ -880,7 +880,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(42); + ).toEqual(142); const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); expect(isSignatureAuthAllowed).toEqual(-1); @@ -911,7 +911,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(43); + ).toEqual(143); const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); expect(isSignatureAuthAllowed).toEqual(-1); @@ -1141,7 +1141,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(44); + ).toEqual(144); const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); expect(isSignatureAuthAllowed).toEqual(-1); @@ -1177,7 +1177,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(43); + ).toEqual(143); const isSignatureAuthAllowed = await walletV5.getIsSignatureAuthAllowed(); expect(isSignatureAuthAllowed).toEqual(-1); // throw when handling, packet is dropped @@ -1249,7 +1249,7 @@ describe('Wallet V5 sign auth internal', () => { (receipt2.transactions[1].description as TransactionDescriptionGeneric) .computePhase as TransactionComputeVm ).exitCode - ).toEqual(32); + ).toEqual(132); expect(receipt2.transactions).not.toHaveTransaction({ from: walletV5.address, From 6e81ad3d7dea3a9b18638aa9e201548ac1eb4720 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Fri, 28 Jun 2024 12:48:01 +0400 Subject: [PATCH 55/67] remove return_if, return_unless because its dangerous --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 33 +++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 89e1d96..9111636 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c72410214010002c7000114ff00f4a413f4bcf2c80b0102012005020102f203012020d70b1f82107369676ebaf2e08a7f700401e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd8110201480f0602012008070019be5f0f6a2684080a0eb90fa02c0201200c090201480b0a0011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0e0d0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002bad020d749c120dc20d70b1f2082106578746eba21821073696e74bab1dd02d0d60301c713dc0282106578746eba8eac8020d72101fa4030fa44f828fa443058badded44d0810141d721f4058307f40e6fa131dd8040d721707fdb3ce30e111001fa3120d749810281b9dc70708eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81102baeda2edfb02f404216e926c218e2a0221d739309420c700b38e1ad72820761e436c20d71d06c712c2005220b0f2d089d74cd73930e85bed55e2d20001ddebd72c08142091709601d72c081c12e25210b1e30f20d74a935bdb31e1d74cd01312007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed5458c947bd"} \ No newline at end of file +{"hex":"b5ee9c7241021301000267000114ff00f4a413f4bcf2c80b0102012004020102f203012020d70b1f82107369676ebaf2e08a7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200b080201480a090011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0d0c0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810281b99130e07070e2100f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81002c2eda2edfb02f404216e926c218e2a0221d739309420c700b38e1ad72820761e436c20d71d06c712c2005220b0f2d089d74cd73930e85bed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a935bdb31e1d74cd01211007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54ad2777ad"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 8e6e6b0..4879bed 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -37,11 +37,6 @@ const int prefix::extension_action = 0x6578746E; (slice, int) check_and_remove_remove_extension_prefix(slice body) impure asm "x{03} SDBEGINSQ"; (slice, int) check_and_remove_set_signature_allowed_prefix(slice body) impure asm "x{04} SDBEGINSQ"; -;;; performs a RET, but only if integer f is non-zero. If f is a NaN, throws an integer overflow exception. -() return_if(int f) impure asm "IFRET"; -;;; performs a RET, but only if integer f is zero -() return_unless(int f) impure asm "IFNOTRET"; - ;;; returns the number of trailing zeroes in slice s. int count_trailing_zeroes(slice s) asm "SDCNTTRAIL0"; ;;; returns the number of trailing ones in slice s. @@ -94,7 +89,9 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Simply set the C5 register with all pre-computed actions after verification: set_c5_actions(c5_actions.verify_c5_actions(is_external)); } - return_unless(cs~load_int(1)); ;; has_other_actions + if (cs~load_int(1) == 0) { ;; has_other_actions + return (); + } ;; Loop extended actions until we reach standard actions while (true) { @@ -212,14 +209,20 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; ------------------------------------------------------------------------------------------------ () recv_internal(cell in_msg_full, slice in_msg_body) impure inline { - return_if(in_msg_body.slice_bits() < size::message_operation_prefix); + if (in_msg_body.slice_bits() < size::message_operation_prefix) { + return (); + } int op = in_msg_body.preload_uint(size::message_operation_prefix); - return_unless((op == prefix::extension_action) | (op == prefix::signed_internal)); + if ((op != prefix::extension_action) & (op != prefix::signed_internal)) { + return (); + } slice in_msg_full_slice = in_msg_full.begin_parse(); slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool ;; skip bounced messages - if bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. - return_if(count_trailing_ones(message_flags_slice)); + if (count_trailing_ones(message_flags_slice) > 0) { + return (); + } if (op == prefix::extension_action) { in_msg_body~skip_bits(size::message_operation_prefix); @@ -228,7 +231,9 @@ cell verify_c5_actions(cell c5, int is_external) inline { (int sender_address_wc, int sender_address_hash) = parse_std_addr(in_msg_full_slice~load_msg_addr()); (int my_address_wc, _) = parse_std_addr(my_address()); - return_unless(my_address_wc == sender_address_wc); + if (my_address_wc != sender_address_wc) { + return (); + } cell extensions = get_data().begin_parse() .skip_bits(size::bool + size::seqno + size::wallet_id + size::public_key) @@ -237,7 +242,9 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; Note that some random contract may have deposited funds with this prefix, ;; so we accept the funds silently instead of throwing an error (wallet v4 does the same). (_, int extension_found) = extensions.udict_get?(size::address_hash_size, sender_address_hash); - return_unless(extension_found); + ifnot (extension_found) { + return (); + } in_msg_body~skip_bits(size::query_id); ;; skip query_id @@ -247,7 +254,9 @@ cell verify_c5_actions(cell c5, int is_external) inline { } ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - return_if(in_msg_body.slice_bits() < size::message_operation_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature); + if (in_msg_body.slice_bits() < size::message_operation_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature) { + return (); + } process_signed_request(in_msg_body, false, false); } From facbfcf195e8b87ba3ee2985f94c9ff70dda41ed Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Mon, 1 Jul 2024 14:35:12 +0400 Subject: [PATCH 56/67] additional checks for c5 --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index 9111636..f4f9855 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c7241021301000267000114ff00f4a413f4bcf2c80b0102012004020102f203012020d70b1f82107369676ebaf2e08a7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200b080201480a090011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0d0c0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810281b99130e07070e2100f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81002c2eda2edfb02f404216e926c218e2a0221d739309420c700b38e1ad72820761e436c20d71d06c712c2005220b0f2d089d74cd73930e85bed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a935bdb31e1d74cd01211007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54ad2777ad"} \ No newline at end of file +{"hex":"b5ee9c724102140100028d000114ff00f4a413f4bcf2c80b0102012004020102f203012020d70b1f82107369676ebaf2e08a7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200b080201480a090011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0d0c0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810281b99130e07070e2100f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f4eda2edfb02f404216e926c218e4d0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128100febbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f201312110014d74a935bdb31e1d74cd0007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed542304d79e"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 4879bed..c668780 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -17,6 +17,7 @@ const int error::this_signature_mode_already_set = 143; const int error::remove_last_extension_when_signature_disabled = 144; const int error::extension_wrong_workchain = 145; const int error::only_extension_can_change_signature_mode = 146; +const int error::invalid_c5 = 147; const int size::bool = 1; const int size::seqno = 32; @@ -65,10 +66,15 @@ cell verify_c5_actions(cell c5, int is_external) inline { ;; exotic cell starts with 0x02, 0x03 or 0x04 so it will not pass action_send_msg prefix check (slice cs, _) = c5.begin_parse_raw(); + int count = 0; + while (~ cs.slice_empty?()) { ;; only `action_send_msg` is allowed; `action_set_code`, `action_reserve_currency` or `action_change_library` are not. cs = cs.enforce_and_remove_action_send_msg_prefix(); + throw_unless(error::invalid_c5, cs.slice_bits() == 8); ;; send_mode + throw_unless(error::invalid_c5, cs.slice_refs() == 2); ;; next-action-ref and MessageRelaxed ref + ;; enforce that send_mode has +2 bit (ignore errors) set for external message. ;; if such send_mode is not set and sending fails at the action phase (for example due to insufficient balance) then the seqno will not be increased and the external message will be processed again and again. @@ -78,7 +84,10 @@ cell verify_c5_actions(cell c5, int is_external) inline { throw_if(error::external_send_message_must_have_ignore_errors_send_mode, is_external & (count_trailing_zeroes(cs.preload_bits(7)) > 0)); (cs, _) = cs.preload_ref().begin_parse_raw(); + count += 1; } + throw_unless(error::invalid_c5, count <= 254); + throw_unless(error::invalid_c5, cs.slice_refs() == 0); return c5; } From 84eaf15caf79b354507116964c283da08efbb564 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Mon, 1 Jul 2024 22:41:49 +0400 Subject: [PATCH 57/67] fix numbers --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index f4f9855..f59c6d4 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102140100028d000114ff00f4a413f4bcf2c80b0102012004020102f203012020d70b1f82107369676ebaf2e08a7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200b080201480a090011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0d0c0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810281b99130e07070e2100f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f4eda2edfb02f404216e926c218e4d0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128100febbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f201312110014d74a935bdb31e1d74cd0007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed542304d79e"} \ No newline at end of file +{"hex":"b5ee9c724102140100028c000114ff00f4a413f4bcf2c80b0102012004020102f203012020d70b1f82107369676ebaf2e08a7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200b080201480a090011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0d0c0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810282b99130e07070e2100f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a1312110010935bdb31e1d74cd0007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed545bfe7180"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index c668780..91cf366 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -86,7 +86,7 @@ cell verify_c5_actions(cell c5, int is_external) inline { (cs, _) = cs.preload_ref().begin_parse_raw(); count += 1; } - throw_unless(error::invalid_c5, count <= 254); + throw_unless(error::invalid_c5, count <= 255); throw_unless(error::invalid_c5, cs.slice_refs() == 0); return c5; @@ -262,8 +262,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { } - ;; Additional check to make sure that there are enough bits for reading (+1 for actual actions flag) - if (in_msg_body.slice_bits() < size::message_operation_prefix + size::wallet_id + size::valid_until + size::seqno + 1 + size::signature) { + ;; Additional check to make sure that there are enough bits for reading (+2 for maybe c5 and maybe other actions bits) + if (in_msg_body.slice_bits() < size::message_operation_prefix + size::wallet_id + size::valid_until + size::seqno + 2 + size::signature) { return (); } process_signed_request(in_msg_body, false, false); From ee52524050be61051164d07e3a7a8816235aa9c9 Mon Sep 17 00:00:00 2001 From: Trinketer22 Date: Tue, 2 Jul 2024 21:50:16 +0300 Subject: [PATCH 58/67] Upgrade packages --- package-lock.json | 1472 ++++++++++++++++++++++++--------------------- package.json | 24 +- 2 files changed, 805 insertions(+), 691 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7ee2edc..e27a735 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,11 @@ "name": "WalletV5", "version": "0.0.1", "devDependencies": { - "@ton-community/blueprint": "^0.12.0", - "@ton-community/sandbox": "^0.11.0", - "@ton-community/test-utils": "^0.3.0", + "@ton/blueprint": "^0.21.0", + "@ton/core": "^0.56.3", + "@ton/crypto": "^3.2.0", + "@ton/sandbox": "^0.20.0", + "@ton/test-utils": "^0.4.2", "@types/jest": "^29.5.0", "@types/node": "^20.2.5", "@typescript-eslint/eslint-plugin": "^5.38.1", @@ -21,14 +23,12 @@ "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-unused-imports": "^2.0.0", - "jest": "^29.5.0", + "jest": "^29.7.0", "prettier": "^2.8.6", "ton": "~13.6.0", - "ton-core": "^0.51.0", - "ton-crypto": "^3.2.0", "ts-jest": "^29.0.5", "ts-node": "^10.9.1", - "typescript": "^4.9.5" + "typescript": "^5.5.2" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -60,106 +60,44 @@ "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.10", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", + "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", - "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", + "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.11", - "@babel/parser": "^7.22.11", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helpers": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -173,21 +111,15 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", + "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -195,14 +127,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", + "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -211,62 +143,66 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", + "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", "dev": true, + "dependencies": { + "@babel/types": "^7.24.7" + }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", + "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", + "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", + "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -285,79 +221,80 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", + "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", + "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", + "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" + "@babel/template": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -426,9 +363,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz", - "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -615,34 +552,34 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", + "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", - "debug": "^4.1.0", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", + "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-function-name": "^7.24.7", + "@babel/helper-hoist-variables": "^7.24.7", + "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/types": "^7.24.7", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -650,13 +587,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", + "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -822,6 +759,12 @@ "multiformats": "^9.5.4" } }, + "node_modules/@ipld/dag-pb/node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "dev": true + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -848,16 +791,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.4.tgz", - "integrity": "sha512-wNK6gC0Ha9QeEPSkeJedQuTQqxZYnDPuDcDhVuVatRvMkL4D0VTvFVZj+Yuh6caG2aOfzkUZ36KtCmLNtR02hw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -865,15 +808,15 @@ } }, "node_modules/@jest/core": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.4.tgz", - "integrity": "sha512-U/vq5ccNTSVgYH7mHnodHmCffGWHJnz/E1BEWlLuK5pM4FZmGfBn/nrJGLjUsSmyx3otCeqc1T31F4y08AMDLg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/reporters": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", @@ -881,21 +824,21 @@ "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.6.3", - "jest-config": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-resolve-dependencies": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "jest-watcher": "^29.6.4", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -912,37 +855,37 @@ } }, "node_modules/@jest/environment": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.4.tgz", - "integrity": "sha512-sQ0SULEjA1XUTHmkBRl7A1dyITM9yb1yb3ZNKPX3KlTd6IG7mWUe3e2yfExtC2Zz1Q+mMckOLHmL/qLiuQJrBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.4", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-Warhsa7d23+3X5bLbrbYvaehcgX5TLYhI03JKoedTiI8uJU4IhqYBWF7OSSgUyz4IgLpUYPkK0AehA5/fRclAA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "dependencies": { - "expect": "^29.6.4", - "jest-snapshot": "^29.6.4" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz", - "integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3" @@ -952,47 +895,47 @@ } }, "node_modules/@jest/fake-timers": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.4.tgz", - "integrity": "sha512-6UkCwzoBK60edXIIWb0/KWkuj7R7Qq91vVInOe3De6DSpaEiqjKcJw4F7XUet24Wupahj9J6PlR09JqJ5ySDHw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.4.tgz", - "integrity": "sha512-wVIn5bdtjlChhXAzVXavcY/3PEjf4VqM174BM3eGL5kMxLiZD5CLnbmkEyA1Dwh9q8XjP6E8RwjBsY/iCWrWsA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", - "jest-mock": "^29.6.3" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.4.tgz", - "integrity": "sha512-sxUjWxm7QdchdrD3NfWKrL8FBsortZeibSJv4XLjESOOjSUOkjQcb0ZHJwfhEGIvBvTluTzfG2yZWZhkrXJu8g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", @@ -1006,9 +949,9 @@ "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1053,12 +996,12 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.4.tgz", - "integrity": "sha512-uQ1C0AUEN90/dsyEirgMLlouROgSY+Wc/JanVVk0OiUKa5UFh7sJpMEM3aoUBAz2BRNvUJ8j3d294WFuRxSyOQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", + "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" @@ -1068,14 +1011,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.4.tgz", - "integrity": "sha512-E84M6LbpcRq3fT4ckfKs9ryVanwkaIB0Ws9bw3/yP4seRLg/VaCZ/LgW0MCq5wwk4/iP/qnilD41aj2fsw2RMg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1083,9 +1026,9 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.4.tgz", - "integrity": "sha512-8thgRSiXUqtr/pPGY/OsyHuMjGyhVnWrFAwoxmIemlBuiMyU1WFs0tXoNxzcr4A4uErs/ABre76SGmrr5ab/AA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -1096,9 +1039,9 @@ "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -1126,14 +1069,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -1149,9 +1092,9 @@ } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" @@ -1164,9 +1107,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1183,6 +1126,12 @@ "murmurhash3js-revisited": "^3.0.0" } }, + "node_modules/@multiformats/murmur3/node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1292,9 +1241,9 @@ "dev": true }, "node_modules/@scarf/scarf": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.1.1.tgz", - "integrity": "sha512-VGbKDbk1RFIaSmdVb0cNjjWJoRWRI/Weo23AjRCC2nryO0iAS8pzsToJfPVPtVs74WHw4L1UTADNdIYRLkirZQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.3.0.tgz", + "integrity": "sha512-lHKK8M5CTcpFj2hZDB3wIjb0KAbEOgDmiJGDv1WBRfQgRm/a8/XMEkG/N1iM01xgbUDsPQwi42D+dFo1XPAKew==", "dev": true, "hasInstallScript": true }, @@ -1305,9 +1254,9 @@ "dev": true }, "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" @@ -1323,51 +1272,70 @@ } }, "node_modules/@tact-lang/compiler": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@tact-lang/compiler/-/compiler-1.1.3.tgz", - "integrity": "sha512-2UnHMW4S1+Rb5hUQAkNZWnQ8XJV4wRq+z3gAjchOzFiZEFNzE/46aIp7E1Jx+dFkfckdWXtGqHbhkRw6rc4CfQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@tact-lang/compiler/-/compiler-1.4.0.tgz", + "integrity": "sha512-MUZ8ulTrIs0sgs1tdNww7yan6ozMGNO7xR6S0yKZc57E0EN9o72vRqKdQW7k3iS3+MvltKPt6RVbNamtuVJ1tQ==", "dev": true, "dependencies": { "@ipld/dag-pb": "2.1.18", - "@tact-lang/opcode": "^0.0.13", - "arg": "^5.0.2", + "@tact-lang/opcode": "^0.0.14", + "@ton/core": "0.56.3", + "@ton/crypto": "^3.2.0", "blockstore-core": "1.0.5", "change-case": "^4.1.2", "ipfs-unixfs-importer": "9.0.10", + "meow": "^13.2.0", "mkdirp": "^2.1.3", - "multiformats": "9.9.0", - "ohm-js": "16.5.0", - "path-normalize": "^6.0.10", + "multiformats": "^13.1.0", + "ohm-js": "^17.1.0", + "path-normalize": "^6.0.13", "prando": "^6.0.1", - "qs": "^6.11.0", - "ton-core": ">=0.49.0", - "ton-crypto": "^3.2.0", - "zod": "^3.20.2" + "qs": "^6.12.1", + "zod": "^3.22.4" }, "bin": { "tact": "bin/tact" } }, "node_modules/@tact-lang/opcode": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@tact-lang/opcode/-/opcode-0.0.13.tgz", - "integrity": "sha512-4FGp1p3ahVrXr2QbyD2FqmnvXTMYapTlRJSPhj4O1L2yIq7dp0CkFL9EdKOCUfyirT0X5en78PHLfj0CuQGD7A==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@tact-lang/opcode/-/opcode-0.0.14.tgz", + "integrity": "sha512-8FKHK2jwvViRBReO2t40DCkHAP9KPTRWZof4kdsAUJFlyeWIC8SsRQSl9QkZxF+48WvjDduKNqN5Ltb80paufA==", "dev": true, "peerDependencies": { - "ton-core": ">=0.49.0", - "ton-crypto": "^3.2.0" + "@ton/core": ">=0.49.2", + "@ton/crypto": "^3.2.0" } }, - "node_modules/@ton-community/blueprint": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@ton-community/blueprint/-/blueprint-0.12.0.tgz", - "integrity": "sha512-QytcjOQCKtmaseEuEeuBGiKuQ649nwXGw4U3aGw7zz/SOd3SidMoTKAK3KE2VL84L8WRlw9HZx6wumJUkWPF7Q==", + "node_modules/@ton-community/func-js": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@ton-community/func-js/-/func-js-0.7.0.tgz", + "integrity": "sha512-VYJsv6Pqz6+qh3HlZWReBG5W9RXutAdIFYDqmblPSCXfjBhx/QjON/3WoppzUVrqQQdD0BVIh4PR+xRHRCBNhw==", + "dev": true, + "dependencies": { + "@ton-community/func-js-bin": "0.4.4-newops.1", + "arg": "^5.0.2" + }, + "bin": { + "func-js": "dist/cli.js" + } + }, + "node_modules/@ton-community/func-js-bin": { + "version": "0.4.4-newops.1", + "resolved": "https://registry.npmjs.org/@ton-community/func-js-bin/-/func-js-bin-0.4.4-newops.1.tgz", + "integrity": "sha512-TV4t6XhmItq4t+wv4pV30yEwb+YvdmsKo4Ig4B0zp4PLdI0r9iZHz4y5bBHcXmDQDRqulXzK6kTgfHvs2CIsaQ==", + "dev": true + }, + "node_modules/@ton/blueprint": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@ton/blueprint/-/blueprint-0.21.0.tgz", + "integrity": "sha512-87oHve+Xy+/IiyS1qNzs8JTGrxSI4kQrsNr7eERhZvC4qYmfAxpT0OBAJWx/qP+fcpzUOWR+vRiQqcJQQwwrLA==", "dev": true, "dependencies": { "@orbs-network/ton-access": "^2.3.3", - "@tact-lang/compiler": "^1.1.3", - "@ton-community/func-js": "^0.6.2", - "@tonconnect/sdk": "^2.1.3", + "@tact-lang/compiler": "^1.3.0", + "@ton-community/func-js": "^0.7.0", + "@tonconnect/sdk": "^2.2.0", "arg": "^5.0.2", "chalk": "^4.1.0", "dotenv": "^16.1.4", @@ -1380,52 +1348,65 @@ "blueprint": "dist/cli/cli.js" }, "peerDependencies": { - "ton": ">=13.4.1", - "ton-core": ">=0.48.0", - "ton-crypto": ">=3.2.0" + "@ton/core": ">=0.56.0", + "@ton/crypto": ">=3.2.0", + "@ton/ton": ">=13.11.0" } }, - "node_modules/@ton-community/func-js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@ton-community/func-js/-/func-js-0.6.2.tgz", - "integrity": "sha512-5bewe8APG2TVgIPLUV5atQfAy+NtdjjGBfsWUeRdVUclzQ5H2wZ8aJsVNLiDBpKSNEKdOAP/1PownOFeodpQHg==", + "node_modules/@ton/core": { + "version": "0.56.3", + "resolved": "https://registry.npmjs.org/@ton/core/-/core-0.56.3.tgz", + "integrity": "sha512-HVkalfqw8zqLLPehtq0CNhu5KjVzc7IrbDwDHPjGoOSXmnqSobiWj8a5F+YuWnZnEbQKtrnMGNOOjVw4LG37rg==", "dev": true, "dependencies": { - "@ton-community/func-js-bin": "0.4.4", - "arg": "^5.0.2" + "symbol.inspect": "1.0.1" }, - "bin": { - "func-js": "dist/cli.js" + "peerDependencies": { + "@ton/crypto": ">=3.2.0" } }, - "node_modules/@ton-community/func-js-bin": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/@ton-community/func-js-bin/-/func-js-bin-0.4.4.tgz", - "integrity": "sha512-zCSVXmh+rFMgouzTbWkSVDIt1Z5i36u9rws/Kuqn89/a0vhA1aEoJJ3oJypz0TjWKJQveU4doJsPlqu7UT2zkw==", - "dev": true + "node_modules/@ton/crypto": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@ton/crypto/-/crypto-3.2.0.tgz", + "integrity": "sha512-50RkwReEuV2FkxSZ8ht/x9+n0ZGtwRKGsJ0ay4I/HFhkYVG/awIIBQeH0W4j8d5lADdO5h01UtX8PJ8AjiejjA==", + "dev": true, + "dependencies": { + "@ton/crypto-primitives": "2.0.0", + "jssha": "3.2.0", + "tweetnacl": "1.0.3" + } + }, + "node_modules/@ton/crypto-primitives": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ton/crypto-primitives/-/crypto-primitives-2.0.0.tgz", + "integrity": "sha512-wttiNClmGbI6Dfy/8oyNnsIV0b/qYkCJz4Gn4eP62lJZzMtVQ94Ko7nikDX1EfYHkLI1xpOitWpW+8ZuG6XtDg==", + "dev": true, + "dependencies": { + "jssha": "3.2.0" + } }, - "node_modules/@ton-community/sandbox": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@ton-community/sandbox/-/sandbox-0.11.0.tgz", - "integrity": "sha512-3tlSprRBTSu9m0tJTC3cl4MXQep1vfNMPqk9+JAXSRJu9ToEvIUVpqO6MQNkbz9LkKDuOEBs5vyqT37DlKKcWw==", + "node_modules/@ton/sandbox": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@ton/sandbox/-/sandbox-0.20.0.tgz", + "integrity": "sha512-uci6DRDZGW1eu+hHgbVzf4lDTi29PV+5XKPC8ZyYUJaoOtulkHDtgyrfZ1H5QSOVOmUIjHDQhPwLsn1kU51yHw==", "dev": true, "peerDependencies": { - "ton-core": ">=0.48.0", - "ton-crypto": ">=3.2.0" + "@ton/core": ">=0.56.0", + "@ton/crypto": ">=3.2.0" } }, - "node_modules/@ton-community/test-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@ton-community/test-utils/-/test-utils-0.3.0.tgz", - "integrity": "sha512-eCw1c6a0TcKwiYEA4fmzPq+7dJtUx0UFYu+UEoRznIxEOcpEb8Ssjb9yMeiJEzbtUVMCkhEtpztdKt75ngDRWg==", + "node_modules/@ton/test-utils": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@ton/test-utils/-/test-utils-0.4.2.tgz", + "integrity": "sha512-fthY8Nrlmy8jnOl/vx6yjeKzzu62ZXMe7ej9Xg7rb4d3511V7dVQK+nw4YLSW5+dD/6WT03dFuNZXnuMYy5fHw==", "dev": true, "dependencies": { "node-inspect-extracted": "^2.0.0" }, "peerDependencies": { "@jest/globals": "*", - "chai": "*", - "ton-core": ">=0.36.1" + "@ton/core": ">=0.49.2", + "chai": "*" }, "peerDependenciesMeta": { "@jest/globals": { @@ -1436,6 +1417,36 @@ } } }, + "node_modules/@ton/ton": { + "version": "13.11.2", + "resolved": "https://registry.npmjs.org/@ton/ton/-/ton-13.11.2.tgz", + "integrity": "sha512-EPqW+ZTe0MmfqguJEIGMuAqTAFRKMEce95HlDx8h6CGn2y3jiMgV1/oO+WpDIOiX+1wnTu+xtajk8JTWr8nKRQ==", + "dev": true, + "peer": true, + "dependencies": { + "axios": "^1.6.7", + "dataloader": "^2.0.0", + "symbol.inspect": "1.0.1", + "teslabot": "^1.3.0", + "zod": "^3.21.4" + }, + "peerDependencies": { + "@ton/core": ">=0.56.0", + "@ton/crypto": ">=3.2.0" + } + }, + "node_modules/@ton/ton/node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dev": true, + "peer": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/@tonconnect/isomorphic-eventsource": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/@tonconnect/isomorphic-eventsource/-/isomorphic-eventsource-0.0.1.tgz", @@ -1455,9 +1466,9 @@ } }, "node_modules/@tonconnect/protocol": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/@tonconnect/protocol/-/protocol-2.2.5.tgz", - "integrity": "sha512-kR0E+CWZl6JrE/30283v+sRiAvEu21t1xOLFx6f/BxlCNLY2wki39+L32+iicX8gn/Ig99L1flr9TAI9QW9bnQ==", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@tonconnect/protocol/-/protocol-2.2.6.tgz", + "integrity": "sha512-kyoDz5EqgsycYP+A+JbVsAUYHNT059BCrK+m0pqxykMODwpziuSAXfwAZmHcg8v7NB9VKYbdFY55xKeXOuEd0w==", "dev": true, "dependencies": { "tweetnacl": "^1.0.3", @@ -1541,18 +1552,18 @@ } }, "node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -2188,12 +2199,12 @@ } }, "node_modules/babel-jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", - "integrity": "sha512-meLj23UlSLddj6PC+YTOFRgDAtjnZom8w/ACsrx0gtPtv5cJZk0A5Unk5bV4wixD7XaPCN1fQvpww8czkZURmw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.4", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", @@ -2347,6 +2358,12 @@ "multiformats": "^9.4.7" } }, + "node_modules/blockstore-core/node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "dev": true + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2370,9 +2387,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", + "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", "dev": true, "funding": [ { @@ -2389,10 +2406,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" + "caniuse-lite": "^1.0.30001629", + "electron-to-chromium": "^1.4.796", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.16" }, "bin": { "browserslist": "cli.js" @@ -2453,13 +2470,19 @@ "dev": true }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2494,9 +2517,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001524", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", - "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==", + "version": "1.0.30001638", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001638.tgz", + "integrity": "sha512-5SuJUJ7cZnhPpeLHaH0c/HPAnAHZvS6ElWyHK9GSIbVOQABLzowiI2pjmpvZ1WEbkyz46iFd4UXlOHR5SqgfMQ==", "dev": true, "funding": [ { @@ -2591,9 +2614,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "dev": true }, "node_modules/cli-cursor": { @@ -2609,9 +2632,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "engines": { "node": ">=6" @@ -2744,6 +2767,27 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2788,9 +2832,9 @@ } }, "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -2828,6 +2872,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/define-properties": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", @@ -2915,21 +2976,21 @@ } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "dev": true, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/electron-to-chromium": { - "version": "1.4.503", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.503.tgz", - "integrity": "sha512-LF2IQit4B0VrUHFeQkWhZm97KuJSGF2WJqq1InpY+ECpFRkXd8yTIaTtJxsO0OKDmiBYwWqcrNaXOurn2T2wiA==", + "version": "1.4.812", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.812.tgz", + "integrity": "sha512-7L8fC2Ey/b6SePDFKR2zHAy4mbdp1/38Yk5TsARO66W3hC5KEaeKMMHoxwtuH+jcu2AYLSn9QX04i95t6Fl1Hg==", "dev": true }, "node_modules/emittery": { @@ -3018,6 +3079,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", @@ -3059,9 +3141,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { "node": ">=6" @@ -3659,16 +3741,16 @@ } }, "node_modules/expect": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz", - "integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.4", + "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3" + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -3831,9 +3913,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -3874,9 +3956,9 @@ } }, "node_modules/fp-ts": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.1.tgz", - "integrity": "sha512-by7U5W8dkIzcvDofUcO42yl9JbnHTEDBrzu3pt5fKT+Z4Oy85I21K80EYJYdjQGC2qum4Vo55Ag57iiIK4FYuA==", + "version": "2.16.6", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.16.6.tgz", + "integrity": "sha512-v7w209VPj4L6pPn/ftFRJu31Oa8QagwcVw7BZmLCUWU4AQoc954rX9ogSIahDf67Pg+GjPbkW/Kn9XWnlWJG0g==", "dev": true }, "node_modules/fs.realpath": { @@ -3900,10 +3982,13 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.6", @@ -3957,15 +4042,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4159,12 +4248,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4209,6 +4298,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/header-case": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", @@ -4380,6 +4481,12 @@ "multiformats": "^9.0.4" } }, + "node_modules/interface-blockstore/node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "dev": true + }, "node_modules/interface-store": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/interface-store/-/interface-store-2.0.2.tgz", @@ -4401,9 +4508,9 @@ } }, "node_modules/io-ts": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.20.tgz", - "integrity": "sha512-Rq2BsYmtwS5vVttie4rqrOCIfHCS9TgpRLFpKQCM1wZBBRY9nWVGmEvm2FnDbSE2un1UE39DvFpTR5UL47YDcA==", + "version": "2.2.21", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-2.2.21.tgz", + "integrity": "sha512-zz2Z69v9ZIC3mMLYWIeoUcwWD6f+O7yP92FMVVaXEOSZH1jnVBmET/urd/uoarD1WGBY4rCj8TAyMPzsGNzMFQ==", "dev": true, "peerDependencies": { "fp-ts": "^2.5.0" @@ -4463,6 +4570,12 @@ "npm": ">=7.0.0" } }, + "node_modules/ipfs-unixfs-importer/node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "dev": true + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -4794,14 +4907,14 @@ } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", - "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", "dev": true, "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" }, @@ -4809,26 +4922,11 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-instrument/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -4836,12 +4934,6 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-instrument/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -4871,9 +4963,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -4929,15 +5021,15 @@ "dev": true }, "node_modules/jest": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.4.tgz", - "integrity": "sha512-tEFhVQFF/bzoYV1YuGyzLPZ6vlPrdfvDmmAxudA1dLEuiztqg2Rkx20vkKY32xiDROcD2KXlgZ7Cu8RPeEHRKw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", + "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.4" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -4955,13 +5047,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", - "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { "execa": "^5.0.0", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -4969,28 +5061,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.4.tgz", - "integrity": "sha512-YXNrRyntVUgDfZbjXWBMPslX1mQ8MrSG0oM/Y06j9EYubODIyHWP8hMUbjbZ19M3M+zamqEur7O80HODwACoJw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/expect": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-runtime": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -5000,22 +5092,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.4.tgz", - "integrity": "sha512-+uMCQ7oizMmh8ZwRfZzKIEszFY9ksjjEQnTEMTaL7fYiL3Kw4XhqT9bYh+A4DQKUb67hZn2KbtEnDuHvcgK4pQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { - "@jest/core": "^29.6.4", - "@jest/test-result": "^29.6.4", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { @@ -5034,31 +5125,31 @@ } }, "node_modules/jest-config": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.4.tgz", - "integrity": "sha512-JWohr3i9m2cVpBumQFv2akMEnFEPVOh+9L2xIBJhJ0zOaci2ZXuKJj0tgMKQCBZAKA09H049IR4HVS/43Qb19A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.4", + "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", - "babel-jest": "^29.6.4", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.4", - "jest-environment-node": "^29.6.4", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", "jest-get-type": "^29.6.3", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runner": "^29.6.4", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -5079,24 +5170,24 @@ } }, "node_modules/jest-diff": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz", - "integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", - "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -5106,33 +5197,33 @@ } }, "node_modules/jest-each": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", - "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", - "jest-util": "^29.6.3", - "pretty-format": "^29.6.3" + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.4.tgz", - "integrity": "sha512-i7SbpH2dEIFGNmxGCpSc2w9cA4qVD+wfvg2ZnfQ7XVrKL0NA5uDVBIiGH8SR4F0dKEv/0qI5r+aDomDf04DpEQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.3", - "jest-util": "^29.6.3" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5148,9 +5239,9 @@ } }, "node_modules/jest-haste-map": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.4.tgz", - "integrity": "sha512-12Ad+VNTDHxKf7k+M65sviyynRoZYuL1/GTuhEVb8RYsNSNln71nANRb/faSyWvx0j+gHcivChXHIoMJrGYjog==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -5160,8 +5251,8 @@ "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", - "jest-util": "^29.6.3", - "jest-worker": "^29.6.4", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -5173,37 +5264,37 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", - "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz", - "integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", - "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", @@ -5212,7 +5303,7 @@ "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -5221,14 +5312,14 @@ } }, "node_modules/jest-mock": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", - "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.3" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5261,17 +5352,17 @@ } }, "node_modules/jest-resolve": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.4.tgz", - "integrity": "sha512-fPRq+0vcxsuGlG0O3gyoqGTAxasagOxEuyoxHeyxaZbc9QNek0AmJWSkhjlMG+mTsj+8knc/mWb3fXlRNVih7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.3", - "jest-validate": "^29.6.3", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -5281,43 +5372,43 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.4.tgz", - "integrity": "sha512-7+6eAmr1ZBF3vOAJVsfLj1QdqeXG+WYhidfLHBRZqGN24MFRIiKG20ItpLw2qRAsW/D2ZUUmCNf6irUr/v6KHA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "dependencies": { "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.6.4" + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.4.tgz", - "integrity": "sha512-SDaLrMmtVlQYDuG0iSPYLycG8P9jLI+fRm8AF/xPKhYDB2g6xDWjXBrR5M8gEWsK6KVFlebpZ4QsrxdyIX1Jaw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.4", - "@jest/environment": "^29.6.4", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.6.3", - "jest-environment-node": "^29.6.4", - "jest-haste-map": "^29.6.4", - "jest-leak-detector": "^29.6.3", - "jest-message-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-runtime": "^29.6.4", - "jest-util": "^29.6.3", - "jest-watcher": "^29.6.4", - "jest-worker": "^29.6.4", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -5326,17 +5417,17 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.4.tgz", - "integrity": "sha512-s/QxMBLvmwLdchKEjcLfwzP7h+jsHvNEtxGP5P+Fl1FMaJX2jMiIqe4rJw4tFprzCwuSvVUo9bn0uj4gNRXsbA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.4", - "@jest/fake-timers": "^29.6.4", - "@jest/globals": "^29.6.4", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", @@ -5344,13 +5435,13 @@ "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-mock": "^29.6.3", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.6.4", - "jest-snapshot": "^29.6.4", - "jest-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -5359,9 +5450,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.4.tgz", - "integrity": "sha512-VC1N8ED7+4uboUKGIDsbvNAZb6LakgIPgAF4RSpF13dN6YaMokfRqO+BaqK4zIh6X3JffgwbzuGqDEjHm/MrvA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -5369,20 +5460,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.4", - "@jest/transform": "^29.6.4", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.4", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.4", + "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.6.4", - "jest-message-util": "^29.6.3", - "jest-util": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.3", + "pretty-format": "^29.7.0", "semver": "^7.5.3" }, "engines": { @@ -5423,9 +5514,9 @@ "dev": true }, "node_modules/jest-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", - "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -5440,9 +5531,9 @@ } }, "node_modules/jest-validate": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", - "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", @@ -5450,7 +5541,7 @@ "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5469,18 +5560,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.4.tgz", - "integrity": "sha512-oqUWvx6+On04ShsT00Ir9T4/FvBeEh2M9PTubgITPxDa739p4hoQweWPRGyYeaojgT0xTpZKF0Y/rSY1UgMxvQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.4", + "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { @@ -5488,13 +5579,13 @@ } }, "node_modules/jest-worker": { - "version": "29.6.4", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.4.tgz", - "integrity": "sha512-6dpvFV4WjcWbDVGgHTWo/aupl8/LbBx2NSKfiwqf79xC/yeJjKHT1+StcKy/2KTmW16hE68ccKVOtXf+WZGz7Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.3", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -5724,26 +5815,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-dir/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -5751,12 +5827,6 @@ "node": ">=10" } }, - "node_modules/make-dir/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -5772,6 +5842,18 @@ "tmpl": "1.0.5" } }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-options": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", @@ -5885,9 +5967,9 @@ "dev": true }, "node_modules/multiformats": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", - "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.1.1.tgz", + "integrity": "sha512-JiptvwMmlxlzIlLLwhCi/srf/nk409UL0eUBr0kioRJq15hqqKyg68iftrBvhCRjR6Rw4fkNnSc4ZJXJDuta/Q==", "dev": true }, "node_modules/murmurhash3js-revisited": { @@ -5963,9 +6045,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/normalize-path": { @@ -6010,10 +6092,13 @@ "dev": true }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6106,9 +6191,9 @@ } }, "node_modules/ohm-js": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/ohm-js/-/ohm-js-16.5.0.tgz", - "integrity": "sha512-OXuB3g1cdP6vCyaGziLmihLkBrtfH9H9jmYp5nRqad093KVzkUSi062IVv5l+3SB/HIbMyDvBqhgR3Q3S+EEnw==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/ohm-js/-/ohm-js-17.1.0.tgz", + "integrity": "sha512-xc3B5dgAjTBQGHaH7B58M2Pmv6WvzrJ/3/7LeUzXNg0/sY3jQPdSd/S2SstppaleO77rifR1tyhdfFGNIwxf2Q==", "dev": true, "engines": { "node": ">=0.12.1" @@ -6361,9 +6446,9 @@ } }, "node_modules/path-normalize": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/path-normalize/-/path-normalize-6.0.12.tgz", - "integrity": "sha512-/lgDQDNQVtfOCKpQ9/Zr64/pT4OWmCiHDBtHCnJO/jkxXmnETI2Bes/E3y9yDbplYFMFvjRPSINfPvYxVrxEYA==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/path-normalize/-/path-normalize-6.0.13.tgz", + "integrity": "sha512-PfC1Pc+IEhI77UEN731pj2nMs9gHAV36IA6IW6VdXWjoQesf+jtO9hdMUqTRS6mwR0T5rmyUrQzd5vw0VwL1Lw==", "dev": true, "engines": { "node": ">=16.0.0" @@ -6385,9 +6470,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { @@ -6466,9 +6551,9 @@ } }, "node_modules/pretty-format": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", - "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", @@ -6530,6 +6615,13 @@ "pbts": "bin/pbts" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "peer": true + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -6540,9 +6632,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { @@ -6565,12 +6657,12 @@ } }, "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", "dev": true, "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -6878,6 +6970,23 @@ "upper-case-first": "^2.0.2" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6900,14 +7009,18 @@ } }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7255,6 +7368,7 @@ "resolved": "https://registry.npmjs.org/ton-core/-/ton-core-0.51.0.tgz", "integrity": "sha512-FgejId/X1o7nNc5g8DBW1+9piwFolVU3e83Z8TudDCALik29YPhGOqLHSvcVYux3QV7SL0qCNaKgA3mV2nNfCA==", "dev": true, + "peer": true, "dependencies": { "symbol.inspect": "1.0.1" }, @@ -7518,9 +7632,9 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true }, "node_modules/tsutils": { @@ -7655,16 +7769,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", + "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/uint8arrays": { @@ -7676,6 +7790,12 @@ "multiformats": "^9.4.2" } }, + "node_modules/uint8arrays/node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", + "dev": true + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -7692,9 +7812,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", "dev": true, "funding": [ { @@ -7711,8 +7831,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -7767,25 +7887,19 @@ "dev": true }, "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -7811,9 +7925,9 @@ "dev": true }, "node_modules/whatwg-fetch": { - "version": "3.6.17", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz", - "integrity": "sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==", + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", "dev": true }, "node_modules/whatwg-url": { @@ -7973,9 +8087,9 @@ } }, "node_modules/zod": { - "version": "3.22.2", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.2.tgz", - "integrity": "sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==", + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", "dev": true, "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index a6a8954..3488bce 100644 --- a/package.json +++ b/package.json @@ -12,19 +12,13 @@ "scalpel": "bash ./scripts/scalpel.sh" }, "devDependencies": { - "@ton-community/blueprint": "^0.12.0", - "@ton-community/sandbox": "^0.11.0", - "@ton-community/test-utils": "^0.3.0", + "@ton/blueprint": "^0.21.0", + "@ton/core": "^0.56.3", + "@ton/crypto": "^3.2.0", + "@ton/sandbox": "^0.20.0", + "@ton/test-utils": "^0.4.2", "@types/jest": "^29.5.0", "@types/node": "^20.2.5", - "jest": "^29.5.0", - "prettier": "^2.8.6", - "ton": "~13.6.0", - "ton-core": "^0.51.0", - "ton-crypto": "^3.2.0", - "ts-jest": "^29.0.5", - "ts-node": "^10.9.1", - "typescript": "^4.9.5", "@typescript-eslint/eslint-plugin": "^5.38.1", "@typescript-eslint/parser": "^5.38.1", "eslint": "8.22.0", @@ -32,6 +26,12 @@ "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-unused-imports": "^2.0.0" + "eslint-plugin-unused-imports": "^2.0.0", + "jest": "^29.7.0", + "prettier": "^2.8.6", + "ton": "~13.6.0", + "ts-jest": "^29.0.5", + "ts-node": "^10.9.1", + "typescript": "^5.5.2" } } From 047852ac96a45a8ed418d18960fa6d621a667ed3 Mon Sep 17 00:00:00 2001 From: Trinketer22 Date: Tue, 2 Jul 2024 21:52:43 +0300 Subject: [PATCH 59/67] Migrate to @ton packages --- tests/actions.ts | 2 +- tests/test-only-actions.ts | 4 ++-- tests/wallet-v5-extensions.spec.ts | 12 ++++++------ tests/wallet-v5-external.spec.ts | 11 ++++++----- tests/wallet-v5-get.spec.ts | 8 ++++---- tests/wallet-v5-internal.spec.ts | 12 ++++++------ wrappers/library-deployer.ts | 4 ++-- wrappers/wallet-v5.ts | 12 ++++++++++-- wrappers/wallet_v5.compile.ts | 2 +- 9 files changed, 38 insertions(+), 29 deletions(-) diff --git a/tests/actions.ts b/tests/actions.ts index e4ff070..cacba0b 100644 --- a/tests/actions.ts +++ b/tests/actions.ts @@ -1,4 +1,4 @@ -import { Address, beginCell, Cell, MessageRelaxed, SendMode, storeMessageRelaxed } from 'ton-core'; +import { Address, beginCell, Cell, MessageRelaxed, SendMode, storeMessageRelaxed } from '@ton/core'; import { isTestOnlyExtendedAction, TestOnlyExtendedAction, TestOnlyOutAction } from './test-only-actions'; export class ActionSendMsg { diff --git a/tests/test-only-actions.ts b/tests/test-only-actions.ts index 7517351..00ca35d 100644 --- a/tests/test-only-actions.ts +++ b/tests/test-only-actions.ts @@ -7,7 +7,7 @@ import { SendMode, storeCurrencyCollection, storeMessageRelaxed -} from 'ton-core'; +} from '@ton/core'; import { ExtendedAction, OutAction @@ -79,4 +79,4 @@ export function isTestOnlyExtendedAction(action: OutAction | ExtendedAction): ac return ( action.tag === ActionSetData.tag ); -} \ No newline at end of file +} diff --git a/tests/wallet-v5-extensions.spec.ts b/tests/wallet-v5-extensions.spec.ts index 68929d7..6822105 100644 --- a/tests/wallet-v5-extensions.spec.ts +++ b/tests/wallet-v5-extensions.spec.ts @@ -1,8 +1,8 @@ -import {Blockchain, BlockchainTransaction, SandboxContract} from '@ton-community/sandbox'; -import { Address, beginCell, Cell, Dictionary, Sender, SendMode, toNano } from 'ton-core'; +import {Blockchain, BlockchainTransaction, SandboxContract} from '@ton/sandbox'; +import { Address, beginCell, Cell, Dictionary, Sender, SendMode, toNano } from '@ton/core'; import { Opcodes, WalletId, WalletV5 } from '../wrappers/wallet-v5'; -import '@ton-community/test-utils'; -import { compile } from '@ton-community/blueprint'; +import '@ton/test-utils'; +import { compile } from '@ton/blueprint'; import { getSecureRandomBytes, KeyPair, keyPairFromSeed, sign } from 'ton-crypto'; import { bufferToBigInt, createMsgInternal, packAddress, validUntil } from './utils'; import { @@ -11,8 +11,8 @@ import { ActionSendMsg, ActionSetSignatureAuthAllowed, packActionsList } from './actions'; -import { TransactionDescriptionGeneric } from 'ton-core/src/types/TransactionDescription'; -import { TransactionComputeVm } from 'ton-core/src/types/TransactionComputePhase'; +import { TransactionDescriptionGeneric } from '@ton/core/src/types/TransactionDescription'; +import { TransactionComputeVm } from '@ton/core/src/types/TransactionComputePhase'; import { buildBlockchainLibraries, LibraryDeployer } from '../wrappers/library-deployer'; import { default as config } from './config'; diff --git a/tests/wallet-v5-external.spec.ts b/tests/wallet-v5-external.spec.ts index 893b179..9316123 100644 --- a/tests/wallet-v5-external.spec.ts +++ b/tests/wallet-v5-external.spec.ts @@ -1,9 +1,10 @@ -import {Blockchain, BlockchainTransaction, SandboxContract} from '@ton-community/sandbox'; -import { Address, beginCell, Cell, Dictionary, internal, Sender, SendMode, toNano } from 'ton-core'; +import {Blockchain, BlockchainTransaction, SandboxContract} from '@ton/sandbox'; +import { Address, beginCell, Cell, Dictionary, internal, Sender, SendMode, toNano } from '@ton/core'; import { Opcodes, WalletId, WalletV5 } from '../wrappers/wallet-v5'; -import '@ton-community/test-utils'; -import { compile } from '@ton-community/blueprint'; -import { getSecureRandomBytes, KeyPair, keyPairFromSeed, sign } from 'ton-crypto'; +import '@ton/test-utils'; +import { compile } from '@ton/blueprint'; +import { getSecureRandomBytes, KeyPair, keyPairFromSeed, sign } from '@ton/crypto'; + import { bufferToBigInt, createMsgInternal, diff --git a/tests/wallet-v5-get.spec.ts b/tests/wallet-v5-get.spec.ts index 8611407..1283249 100644 --- a/tests/wallet-v5-get.spec.ts +++ b/tests/wallet-v5-get.spec.ts @@ -1,8 +1,8 @@ -import { Blockchain, SandboxContract } from '@ton-community/sandbox'; -import { Address, beginCell, Cell, Dictionary, Sender, toNano } from 'ton-core'; +import { Blockchain, SandboxContract } from '@ton/sandbox'; +import { Address, beginCell, Cell, Dictionary, Sender, toNano } from '@ton/core'; import { WalletId, WalletV5 } from '../wrappers/wallet-v5'; -import '@ton-community/test-utils'; -import { compile } from '@ton-community/blueprint'; +import '@ton/test-utils'; +import { compile } from '@ton/blueprint'; import { getSecureRandomBytes, KeyPair, keyPairFromSeed } from 'ton-crypto'; import { bufferToBigInt, packAddress } from './utils'; import { buildBlockchainLibraries, LibraryDeployer } from '../wrappers/library-deployer'; diff --git a/tests/wallet-v5-internal.spec.ts b/tests/wallet-v5-internal.spec.ts index 597a197..963535f 100644 --- a/tests/wallet-v5-internal.spec.ts +++ b/tests/wallet-v5-internal.spec.ts @@ -1,8 +1,8 @@ -import {Blockchain, BlockchainTransaction, SandboxContract} from '@ton-community/sandbox'; -import { Address, beginCell, Cell, Dictionary, Sender, SendMode, toNano } from 'ton-core'; +import {Blockchain, BlockchainTransaction, SandboxContract} from '@ton/sandbox'; +import { Address, beginCell, Cell, Dictionary, Sender, SendMode, toNano } from '@ton/core'; import { Opcodes, WalletId, WalletV5 } from '../wrappers/wallet-v5'; -import '@ton-community/test-utils'; -import { compile } from '@ton-community/blueprint'; +import '@ton/test-utils'; +import { compile } from '@ton/blueprint'; import { getSecureRandomBytes, KeyPair, keyPairFromSeed, sign } from 'ton-crypto'; import { bufferToBigInt, createMsgInternal, disableConsoleError, packAddress, validUntil } from './utils'; import { @@ -11,8 +11,8 @@ import { ActionSendMsg, ActionSetSignatureAuthAllowed, packActionsList } from './actions'; -import { TransactionDescriptionGeneric } from 'ton-core/src/types/TransactionDescription'; -import { TransactionComputeVm } from 'ton-core/src/types/TransactionComputePhase'; +import { TransactionDescriptionGeneric } from '@ton/core/src/types/TransactionDescription'; +import { TransactionComputeVm } from '@ton/core/src/types/TransactionComputePhase'; import { buildBlockchainLibraries, LibraryDeployer } from '../wrappers/library-deployer'; import { default as config } from './config'; import { ActionSetCode, ActionSetData } from './test-only-actions'; diff --git a/wrappers/library-deployer.ts b/wrappers/library-deployer.ts index efec67e..cb3c15c 100644 --- a/wrappers/library-deployer.ts +++ b/wrappers/library-deployer.ts @@ -11,8 +11,8 @@ import { Sender, SendMode, SimpleLibrary -} from 'ton-core'; -import { SimpleLibraryValue } from 'ton-core/dist/types/SimpleLibrary'; +} from '@ton/core'; +import { SimpleLibraryValue } from '@ton/core/dist/types/SimpleLibrary'; export type LibraryDeployerConfig = { libraryCode: Cell; diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index 5e59c57..d95c0b0 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -9,11 +9,19 @@ import { contractAddress, ContractProvider, Dictionary, + MessageRelaxed, + storeOutList, + OutAction, Sender, - SendMode -} from 'ton-core'; + SendMode, + Builder, + OutActionSendMsg, + toNano +} from '@ton/core'; import { bufferToBigInt } from '../tests/utils'; +import { sign } from '@ton/crypto'; + export type WalletV5Config = { signatureAllowed: boolean; seqno: number; diff --git a/wrappers/wallet_v5.compile.ts b/wrappers/wallet_v5.compile.ts index c4cb126..e9b50f6 100644 --- a/wrappers/wallet_v5.compile.ts +++ b/wrappers/wallet_v5.compile.ts @@ -1,4 +1,4 @@ -import { CompilerConfig } from '@ton-community/blueprint'; +import { CompilerConfig } from '@ton/blueprint'; export const compile: CompilerConfig = { lang: 'func', From 982f30448b977978e09edc0f5f1b9ba0b4cc9f58 Mon Sep 17 00:00:00 2001 From: Trinketer22 Date: Tue, 2 Jul 2024 21:54:57 +0300 Subject: [PATCH 60/67] Minor fix --- wrappers/wallet-v5.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/wrappers/wallet-v5.ts b/wrappers/wallet-v5.ts index d95c0b0..27963df 100644 --- a/wrappers/wallet-v5.ts +++ b/wrappers/wallet-v5.ts @@ -182,12 +182,7 @@ export class WalletV5 implements Contract { } async sendExternalSignedMessage(provider: ContractProvider, body: Cell) { - await provider.external( - beginCell() - // .storeUint(Opcodes.auth_signed, 32) // Is signed inside message - .storeSlice(body.beginParse()) - .endCell() - ); + await provider.external(body); } async sendExternal(provider: ContractProvider, body: Cell) { @@ -242,7 +237,7 @@ export class WalletV5 implements Contract { ); return dict.keys().map(key => { - const wc = 0n; + const wc = this.address.workChain; const addressHex = key; return Address.parseRaw(`${wc}:${addressHex.toString(16).padStart(64, '0')}`); }); From ff8cd7498c3cd5c74aa8173151ab8c404082b624 Mon Sep 17 00:00:00 2001 From: Trinketer22 Date: Tue, 2 Jul 2024 21:55:32 +0300 Subject: [PATCH 61/67] Extra moar utils for the utils god --- tests/gasUtils.ts | 415 ++++++++++++++++++++++++++++++++++++++++++++++ tests/utils.ts | 65 +++++++- 2 files changed, 479 insertions(+), 1 deletion(-) create mode 100644 tests/gasUtils.ts diff --git a/tests/gasUtils.ts b/tests/gasUtils.ts new file mode 100644 index 0000000..f322cea --- /dev/null +++ b/tests/gasUtils.ts @@ -0,0 +1,415 @@ +import { Cell, Slice, toNano, beginCell, Address, Dictionary, Message, DictionaryValue, Transaction, BitString, SendMode, MessageRelaxed, CommonMessageInfoInternal, storeMessage, storeMessageRelaxed } from '@ton/core'; +import { internal } from '@ton/sandbox'; +import { randomAddress } from './utils'; + +export type GasPrices = { + flat_gas_limit: bigint, + flat_gas_price: bigint, + gas_price: bigint; +}; +export type StorageValue = { + utime_sice: number, + bit_price_ps: bigint, + cell_price_ps: bigint, + mc_bit_price_ps: bigint, + mc_cell_price_ps: bigint +}; + + +export type MsgPrices = ReturnType; +export type FullFees = ReturnType; + +export class StorageStats { + bits: bigint; + cells: bigint; + + constructor(bits?: number | bigint, cells?: number | bigint) { + this.bits = bits !== undefined ? BigInt(bits) : 0n; + this.cells = cells !== undefined ? BigInt(cells) : 0n; + } + add(...stats: StorageStats[]) { + let cells = this.cells, bits = this.bits; + for (let stat of stats) { + bits += stat.bits; + cells += stat.cells; + } + return new StorageStats(bits, cells); + } + sub(...stats: StorageStats[]) { + let cells = this.cells, bits = this.bits; + for (let stat of stats) { + bits -= stat.bits; + cells -= stat.cells; + } + return new StorageStats(bits, cells); + } + addBits(bits: number | bigint) { + return new StorageStats(this.bits + BigInt(bits), this.cells); + } + subBits(bits: number | bigint) { + return new StorageStats(this.bits - BigInt(bits), this.cells); + } + addCells(cells: number | bigint) { + return new StorageStats(this.bits, this.cells + BigInt(cells)); + } + subCells(cells: number | bigint) { + return new StorageStats(this.bits, this.cells - BigInt(cells)); + } + + toString() : string { + return JSON.stringify({ + bits: this.bits.toString(), + cells: this.cells.toString() + }); + } +} + +export function computedGeneric(transaction: T) { + if(transaction.description.type !== "generic") + throw("Expected generic transactionaction"); + if(transaction.description.computePhase.type !== "vm") + throw("Compute phase expected") + return transaction.description.computePhase; +} + +export function storageGeneric(transaction: T) { + if(transaction.description.type !== "generic") + throw("Expected generic transactionaction"); + const storagePhase = transaction.description.storagePhase; + if(storagePhase === null || storagePhase === undefined) + throw("Storage phase expected") + return storagePhase; +} + +function shr16ceil(src: bigint) { + let rem = src % BigInt(65536); + let res = src / 65536n; // >> BigInt(16); + if (rem != BigInt(0)) { + res += BigInt(1); + } + return res; +} + +export function collectCellStats(cell: Cell, visited:Array, skipRoot: boolean = false): StorageStats { + let bits = skipRoot ? 0n : BigInt(cell.bits.length); + let cells = skipRoot ? 0n : 1n; + let hash = cell.hash().toString(); + if (visited.includes(hash)) { + // We should not account for current cell data if visited + return new StorageStats(); + } + else { + visited.push(hash); + } + for (let ref of cell.refs) { + let r = collectCellStats(ref, visited); + cells += r.cells; + bits += r.bits; + } + return new StorageStats(bits, cells); +} + +export function getGasPrices(configRaw: Cell, workchain: 0 | -1): GasPrices { + const config = configRaw.beginParse().loadDictDirect(Dictionary.Keys.Int(32), Dictionary.Values.Cell()); + + const ds = config.get(21 + workchain)!.beginParse(); + if(ds.loadUint(8) !== 0xd1) { + throw new Error("Invalid flat gas prices tag!"); + } + + const flat_gas_limit = ds.loadUintBig(64); + const flat_gas_price = ds.loadUintBig(64); + + if(ds.loadUint(8) !== 0xde) { + throw new Error("Invalid gas prices tag!"); + } + return { + flat_gas_limit, + flat_gas_price, + gas_price: ds.preloadUintBig(64) + }; +} + +export function setGasPrice(configRaw: Cell, prices: GasPrices, workchain: 0 | -1) : Cell { + const config = configRaw.beginParse().loadDictDirect(Dictionary.Keys.Int(32), Dictionary.Values.Cell()); + const idx = 21 + workchain; + const ds = config.get(idx)!; + const tail = ds.beginParse().skip(8 + 64 + 64 + 8 + 64); + + const newPrices = beginCell().storeUint(0xd1, 8) + .storeUint(prices.flat_gas_limit, 64) + .storeUint(prices.flat_gas_price, 64) + .storeUint(0xde, 8) + .storeUint(prices.gas_price, 64) + .storeSlice(tail) + .endCell(); + config.set(idx, newPrices); + + return beginCell().storeDictDirect(config).endCell(); +} + +export const storageValue : DictionaryValue = { + serialize: (src, builder) => { + builder.storeUint(0xcc, 8) + .storeUint(src.utime_sice, 32) + .storeUint(src.bit_price_ps, 64) + .storeUint(src.cell_price_ps, 64) + .storeUint(src.mc_bit_price_ps, 64) + .storeUint(src.mc_cell_price_ps, 64) + }, + parse: (src) => { + return { + utime_sice: src.skip(8).loadUint(32), + bit_price_ps: src.loadUintBig(64), + cell_price_ps: src.loadUintBig(64), + mc_bit_price_ps: src.loadUintBig(64), + mc_cell_price_ps: src.loadUintBig(64) + }; + } + }; + +export function getStoragePrices(configRaw: Cell) { + const config = configRaw.beginParse().loadDictDirect(Dictionary.Keys.Int(32), Dictionary.Values.Cell()); + const storageData = Dictionary.loadDirect(Dictionary.Keys.Uint(32),storageValue, config.get(18)!); + const values = storageData.values(); + + return values[values.length - 1]; +} +export function calcStorageFee(prices: StorageValue, stats: StorageStats, duration: bigint) { + return shr16ceil((stats.bits * prices.bit_price_ps + stats.cells * prices.cell_price_ps) * duration) +} +export function setStoragePrices(configRaw: Cell, prices: StorageValue) { + const config = configRaw.beginParse().loadDictDirect(Dictionary.Keys.Int(32), Dictionary.Values.Cell()); + const storageData = Dictionary.loadDirect(Dictionary.Keys.Uint(32),storageValue, config.get(18)!); + storageData.set(storageData.values().length - 1, prices); + config.set(18, beginCell().storeDictDirect(storageData).endCell()); + return beginCell().storeDictDirect(config).endCell(); +} + +export function computeGasFee(prices: GasPrices, gas: bigint): bigint { + if(gas <= prices.flat_gas_limit) { + return prices.flat_gas_price; + } + return prices.flat_gas_price + prices.gas_price * (gas - prices.flat_gas_limit) / 65536n +} + +export function computeDefaultForwardFee(msgPrices: MsgPrices) { + return msgPrices.lumpPrice - ((msgPrices.lumpPrice * msgPrices.firstFrac) >> BigInt(16)); +} + +export function computeCellForwardFees(msgPrices: MsgPrices, msg: Cell) { + let storageStats = collectCellStats(msg, [], true); + return computeFwdFees(msgPrices, storageStats.cells, storageStats.bits); +} +export function computeMessageForwardFees(msgPrices: MsgPrices, msg: Message) { + // let msg = loadMessageRelaxed(cell.beginParse()); + let storageStats = new StorageStats(); + + if( msg.info.type !== "internal") { + throw Error("Helper intended for internal messages"); + } + const defaultFwd = computeDefaultForwardFee(msgPrices); + // If message forward fee matches default than msg cell is flat + if(msg.info.forwardFee == defaultFwd) { + return {fees: {total: msgPrices.lumpPrice, res : defaultFwd, remaining: defaultFwd}, stats: storageStats}; + } + let visited : Array = []; + // Init + if (msg.init) { + let addBits = 5n; // Minimal additional bits + let refCount = 0; + if(msg.init.splitDepth) { + addBits += 5n; + } + if(msg.init.libraries) { + refCount++; + storageStats = storageStats.add(collectCellStats(beginCell().storeDictDirect(msg.init.libraries).endCell(), visited, true)); + } + if(msg.init.code) { + refCount++; + storageStats = storageStats.add(collectCellStats(msg.init.code, visited)) + } + if(msg.init.data) { + refCount++; + storageStats = storageStats.add(collectCellStats(msg.init.data, visited)); + } + if(refCount >= 2) { //https://github.com/ton-blockchain/ton/blob/51baec48a02e5ba0106b0565410d2c2fd4665157/crypto/block/transaction.cpp#L2079 + storageStats.cells++; + storageStats.bits += addBits; + } + } + const lumpBits = BigInt(msg.body.bits.length); + const bodyStats = collectCellStats(msg.body,visited, true); + storageStats = storageStats.add(bodyStats); + + // NOTE: Extra currencies are ignored for now + let fees = computeFwdFeesVerbose(msgPrices, BigInt(storageStats.cells), BigInt(storageStats.bits)); + // Meeh + if(fees.remaining < msg.info.forwardFee) { + // console.log(`Remaining ${fees.remaining} < ${msg.info.forwardFee} lump bits:${lumpBits}`); + storageStats = storageStats.addCells(1).addBits(lumpBits); + fees = computeFwdFeesVerbose(msgPrices, storageStats.cells, storageStats.bits); + } + if(fees.remaining != msg.info.forwardFee) { + console.log("Result fees:", fees); + console.log(msg); + console.log(fees.remaining); + throw(new Error("Something went wrong in fee calcuation!")); + } + return {fees, stats: storageStats}; +} + +export const configParseMsgPrices = (sc: Slice) => { + + let magic = sc.loadUint(8); + + if(magic != 0xea) { + throw Error("Invalid message prices magic number!"); + } + return { + lumpPrice:sc.loadUintBig(64), + bitPrice: sc.loadUintBig(64), + cellPrice: sc.loadUintBig(64), + ihrPriceFactor: sc.loadUintBig(32), + firstFrac: sc.loadUintBig(16), + nextFrac: sc.loadUintBig(16) + }; +} + +export const setMsgPrices = (configRaw: Cell, prices: MsgPrices, workchain: 0 | -1) => { + const config = configRaw.beginParse().loadDictDirect(Dictionary.Keys.Int(32), Dictionary.Values.Cell()); + + const priceCell = beginCell().storeUint(0xea, 8) + .storeUint(prices.lumpPrice, 64) + .storeUint(prices.bitPrice, 64) + .storeUint(prices.cellPrice, 64) + .storeUint(prices.ihrPriceFactor, 32) + .storeUint(prices.firstFrac, 16) + .storeUint(prices.nextFrac, 16) + .endCell(); + config.set(25 + workchain, priceCell); + + return beginCell().storeDictDirect(config).endCell(); +} + +export const getMsgPrices = (configRaw: Cell, workchain: 0 | -1 ) => { + + const config = configRaw.beginParse().loadDictDirect(Dictionary.Keys.Int(32), Dictionary.Values.Cell()); + + const prices = config.get(25 + workchain); + + if(prices === undefined) { + throw Error("No prices defined in config"); + } + + return configParseMsgPrices(prices.beginParse()); +} + +export function computeFwdFees(msgPrices: MsgPrices, cells: bigint, bits: bigint) { + return msgPrices.lumpPrice + (shr16ceil((msgPrices.bitPrice * bits) + + (msgPrices.cellPrice * cells)) + ); +} + +export function computeFwdFeesVerbose(msgPrices: MsgPrices, cells: bigint | number, bits: bigint | number) { + const fees = computeFwdFees(msgPrices, BigInt(cells), BigInt(bits)); + + const res = (fees * msgPrices.firstFrac) >> 16n; + return { + total: fees, + res, + remaining: fees - res + } +} + +export const setPrecompiledGas = (configRaw: Cell, code_hash: Buffer, gas_usage: number) => { + const config = configRaw.beginParse().loadDictDirect(Dictionary.Keys.Int(32), Dictionary.Values.Cell()); + + const entry = beginCell().storeUint(0xb0, 8) + .storeUint(gas_usage, 64) + .endCell().beginParse(); + let dict = Dictionary.empty(Dictionary.Keys.Buffer(32), Dictionary.Values.BitString(8 + 64)); + dict.set(code_hash, entry.loadBits(8 + 64)); + const param = beginCell().storeUint(0xc0, 8).storeBit(1).storeRef(beginCell().storeDictDirect(dict).endCell()).endCell(); + + config.set(45, param); + + return beginCell().storeDictDirect(config).endCell(); +}; + +export const estimateMessageImpact = (message: MessageRelaxed, sendTx: T, msgPrices: MsgPrices, balanceBefore: bigint, mode: SendMode, computed: boolean) => { + + if(message.info.type !== 'internal') { + throw new TypeError("External message is not supported!"); + } + + const computePhase = computedGeneric(sendTx); + + let inValue = 0n; + let inMessage = sendTx.inMessage; + let feesPaid = false; + + if(inMessage) { + if(inMessage.info.type == 'internal') { + inValue = inMessage.info.value.coins; + } + else if(inMessage.info.type == 'external-in') { + // Negative because of import cost + inValue -= computeCellForwardFees(msgPrices, beginCell().store(storeMessage(inMessage)).endCell()); + } + else { + throw new TypeError("external-out can't be incomming message!"); + } + } + + const msgPacked = beginCell().store(storeMessageRelaxed(message)).endCell(); + + const fees = computeCellForwardFees(msgPrices, msgPacked); + + let expOut = message.info.value.coins; + + let balanceAfter = balanceBefore - expOut; + // Usually means it's not the first action, so gas has already been deducted and credit added + if(!computed) { + balanceAfter += inValue - computePhase.gasFees; + } + + if(!(mode & SendMode.PAY_GAS_SEPARATELY)) { + expOut -= fees; + feesPaid = true; + } + else { + balanceAfter -= fees; + } + /* + else if(mode & SendMode.PAY_GAS_SEPARATELY) { + if(!(mode & SendMode.CARRY_ALL_REMAINING_BALANCE) || (mode & SendMode.CARRY_ALL_REMAINING_INCOMING_VALUE)) { + balanceAfter -= fees; + } + } + */ + if(mode & SendMode.CARRY_ALL_REMAINING_BALANCE) { + expOut = balanceAfter - fees + message.info.value.coins; + balanceAfter = 0n; + } + if(mode & SendMode.CARRY_ALL_REMAINING_INCOMING_VALUE) { + if(mode & SendMode.CARRY_ALL_REMAINING_BALANCE) { + throw new TypeError("Mode 64 and 128 is not compatible"); + } + if(!inMessage) { + throw new Error("Mode 64 doesn't work without incomming message"); + } + if(inMessage.info.type != 'internal') { + throw new Error("Mode 64 doesn't work with external incomming message"); + } + + expOut = inValue - computePhase.gasFees + message.info.value.coins - fees; + balanceAfter -= inValue - computePhase.gasFees; + /* + if(!feesPaid) { + expOut -= fees; + } + */ + } + return {expValue: expOut, balanceAfter}; +} diff --git a/tests/utils.ts b/tests/utils.ts index d309bf9..8a32688 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,4 +1,4 @@ -import { Address, beginCell, Cell, CurrencyCollection, MessageRelaxed, StateInit } from 'ton-core'; +import { Address, beginCell, Cell, CurrencyCollection, MessageRelaxed, StateInit } from '@ton/core'; export function bufferToBigInt(buffer: Buffer): bigint { return BigInt('0x' + buffer.toString('hex')); @@ -37,6 +37,69 @@ export function createMsgInternal(params: { }; } +export const randomAddress = (wc: number = 0) => { + const buf = Buffer.alloc(32); + for (let i = 0; i < buf.length; i++) { + buf[i] = Math.floor(Math.random() * 256); + } + return new Address(wc, buf); +}; + +export const differentAddress = (old: Address) => { + let newAddr: Address; + do { + newAddr = randomAddress(old.workChain); + } while(newAddr.equals(old)); + + return newAddr; +} + +const getRandom = (min:number, max:number) => { + return Math.random() * (max - min) + min; +} + +export const getRandomInt = (min: number, max: number) => { + return Math.round(getRandom(min, max)); +} + +export const pickRandomN = (min: number, max: number, count: number): number[] => { + if(count > max - min) { + throw new Error("Element count can't be larger than range"); + } + + let uniqSet: Set = new Set(); + let foundCount = 0; + // I know it' inefficient + do { + const atempt = getRandomInt(min, max) + if(!uniqSet.has(atempt)) { + foundCount++; + uniqSet.add(atempt); + } + } while(foundCount < count); + + return [...uniqSet]; +} + +export const pickRandomNFrom = (count: number, from: T[]): T[] => { + let resultPick: T[] = new Array(count); + const pickIdxs = pickRandomN(0, from.length - 1, count); + + for(let i = 0; i < pickIdxs.length; i++) { + resultPick[i] = from[pickIdxs[i]]; + } + + return resultPick; +} +export const testArgs = (...args: unknown[]) => { + for(let arg of args) { + if(arg === undefined || arg === null) { + throw TypeError("Required argument is missing!"); + } + } +} + + export async function disableConsoleError(callback: () => Promise): Promise { const errorsHandler = console.error; console.error = () => {}; From d34988638170eb341fa7ffa516295ee02224b3bf Mon Sep 17 00:00:00 2001 From: Trinketer22 Date: Tue, 2 Jul 2024 21:55:54 +0300 Subject: [PATCH 62/67] Test wrapper and constants --- wrappers/Errors.ts | 18 ++++ wrappers/wallet-v5-test.ts | 190 +++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 wrappers/Errors.ts create mode 100644 wrappers/wallet-v5-test.ts diff --git a/wrappers/Errors.ts b/wrappers/Errors.ts new file mode 100644 index 0000000..b1c3b93 --- /dev/null +++ b/wrappers/Errors.ts @@ -0,0 +1,18 @@ +export abstract class ErrorsV5 { + static readonly signature_disabled = 132; + static readonly invalid_seqno = 133; + static readonly invalid_wallet_id = 134; + static readonly invalid_signature = 135; + static readonly expired = 136; + static readonly external_send_message_must_have_ignore_errors_send_mode = 137; + static readonly invalid_message_operation = 138; + static readonly add_extension = 139; + static readonly remove_extension = 140; + static readonly unsupported_action = 141; + static readonly disable_signature_when_extensions_is_empty = 142; + static readonly this_signature_mode_already_set = 143; + static readonly remove_last_extension_when_signature_disabled = 144; + static readonly extension_wrong_workchain = 145; + static readonly only_extension_can_change_signature_mode = 146; + static readonly invalid_c5 = 147; +} diff --git a/wrappers/wallet-v5-test.ts b/wrappers/wallet-v5-test.ts new file mode 100644 index 0000000..2d5c09e --- /dev/null +++ b/wrappers/wallet-v5-test.ts @@ -0,0 +1,190 @@ +import { Cell, beginCell, Sender, ContractProvider, SendMode, MessageRelaxed, Address, toNano, contractAddress, OutAction, OutActionSendMsg, Builder, storeOutList } from '@ton/core'; +import { WalletV5, WalletV5Config, walletV5ConfigToCell, Opcodes } from './wallet-v5'; +import { sign } from '@ton/crypto'; + +export type WalletActions = { + wallet?: OutAction[] | Cell, + extended?: ExtendedAction[] | Cell +} + +export type ExtensionAdd = { + type: 'add_extension', + address: Address +} +export type ExtensionRemove = { + type: 'remove_extension', + address: Address +} + +export type SetSignatureAuth = { + type: 'sig_auth', + allowed: boolean +} + +export type ExtendedAction = ExtensionAdd | ExtensionRemove | SetSignatureAuth; + +export type MessageOut = { + message: MessageRelaxed, + mode: SendMode +}; + +function storeWalletActions(actions: WalletActions) { + // store compatable + return (builder: Builder) => { + let hasExtendedActions = false; + if(actions.wallet) { + let actionCell: Cell | null = null; + if(actions.wallet instanceof Cell) { + actionCell = actions.wallet; + } + else if(actions.wallet.length > 0) { + actionCell = beginCell().store(storeOutList(actions.wallet)).endCell(); + } + builder.storeMaybeRef(actionCell); + } + else { + builder.storeBit(false); + } + if(actions.extended) { + if(actions.extended instanceof Cell) { + builder.storeBit(true); + builder.storeSlice(actions.extended.asSlice()); + } + else if(actions.extended.length > 0) { + builder.storeBit(true); + builder.store(storeExtendedActions(actions.extended)); + } + else { + builder.storeBit(false); + } + } + else { + builder.storeBit(false); + } + } +} + +function storeExtensionAction(action: ExtendedAction) { + return (builder: Builder) => { + if(action.type == 'add_extension') { + builder.storeUint(2, 8).storeAddress(action.address); + } + else if(action.type == 'remove_extension') { + builder.storeUint(3, 8).storeAddress(action.address); + } + else { + builder.storeUint(4, 8).storeBit(action.allowed); + } + } +} + +export function storeExtendedActions(actions: ExtendedAction[]) { + const cell = actions.reverse().reduce((curCell, action) => { + const ds = beginCell().store(storeExtensionAction(action)); + if(curCell.bits.length > 0) { + ds.storeRef(curCell); + } + return ds.endCell(); + }, beginCell().endCell()); + + return (builder: Builder) => builder.storeSlice(cell.beginParse()); +} + +export function message2action(msg: MessageOut) : OutActionSendMsg { + return { + type: 'sendMsg', + mode: msg.mode, + outMsg: msg.message + } +} + + +export class WalletV5Test extends WalletV5 { + constructor(readonly address: Address, readonly init?: { code: Cell; data: Cell }) { + super(address, init); + } + static createFromAddress(address: Address) { + return new WalletV5Test(address); + } + + static createFromConfig(config: WalletV5Config, code: Cell, workchain = 0) { + const data = walletV5ConfigToCell(config); + const init = { code, data }; + return new WalletV5Test(contractAddress(workchain, init), init); + } + static requestMessage(internal: boolean, wallet_id: bigint, valid_until: number, seqno: bigint | number, actions: WalletActions, key?: Buffer) { + const op = internal ? Opcodes.auth_signed_internal : Opcodes.auth_signed; + const msgBody = beginCell().storeUint(op, 32) + .storeUint(wallet_id, 32) + .storeUint(valid_until, 32) + .storeUint(seqno, 32) + .store(storeWalletActions(actions)) + .endCell(); + return key ? WalletV5Test.signRequestMessage(msgBody, key) : msgBody; + } + + static signRequestMessage(msg: Cell, key: Buffer) { + const signature = sign(msg.hash(), key); + + return beginCell().storeSlice(msg.asSlice()).storeBuffer(signature).endCell(); + } + async sendMessagesExternal(provider: ContractProvider, + wallet_id: bigint, + valid_until: number, + seqno: bigint | number, + key: Buffer, messages: MessageOut[]) { + const actions: OutActionSendMsg[] = messages.map(message2action); + + await provider.external( + WalletV5Test.requestMessage(false, wallet_id, valid_until, seqno, {wallet: actions}, key) + ); + } + + static extensionMessage(actions: WalletActions, query_id: bigint | number = 0) { + return beginCell() + .storeUint(Opcodes.auth_extension, 32) + .storeUint(query_id, 64) + .store(storeWalletActions(actions)) + .endCell(); + } + async sendExtensionActions(provider: ContractProvider, + via: Sender, + actions: WalletActions, + value: bigint = toNano('0.1'), + query_id: bigint | number = 0) { + + await provider.internal(via, { + value, + body: WalletV5Test.extensionMessage(actions, query_id), + sendMode: SendMode.PAY_GAS_SEPARATELY + }); + } + + async sendMessagesInternal(provider: ContractProvider, via: Sender, + wallet_id: bigint, + valid_until: number, + seqno: bigint | number, + key: Buffer, messages: MessageOut[], value: bigint = toNano('0.05')) { + + const actions: OutActionSendMsg[] = messages.map(message2action); + + await provider.internal(via, { + value, + body: WalletV5Test.requestMessage(true, wallet_id, valid_until, seqno, {wallet: actions}, key), + sendMode: SendMode.PAY_GAS_SEPARATELY + }); + } + + /* + async sendAddExtensionViaExternal(provider: ContractProvider, + wallet_id: bigint, + valid_until: number, + seqno: bigint | number, + key: Buffer, + extensions: Address[]) { + const reqMsg = WalletV5Test.requestMessage(false, wallet_id, valid_until, seqno, {extension: beginCell().endCell()}, key); + + await provider.external(reqMsg); + } + */ +} From 57e37aae304fd93119d04dcd1cecb332310a0a4d Mon Sep 17 00:00:00 2001 From: Trinketer22 Date: Tue, 2 Jul 2024 21:56:14 +0300 Subject: [PATCH 63/67] W5 wallet tests --- tests/WalletW5.spec.ts | 2870 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2870 insertions(+) create mode 100644 tests/WalletW5.spec.ts diff --git a/tests/WalletW5.spec.ts b/tests/WalletW5.spec.ts new file mode 100644 index 0000000..4307a98 --- /dev/null +++ b/tests/WalletW5.spec.ts @@ -0,0 +1,2870 @@ +import { compile } from '@ton/blueprint'; +import { + Address, + Cell, + Dictionary, + toNano, + Transaction, + internal as internal_relaxed, + beginCell, + SendMode, + Sender, + OutAction, + OutActionSendMsg, + contractAddress, + ExternalAddress, + storeOutAction +} from '@ton/core'; +import '@ton/test-utils'; +import { + Blockchain, + BlockchainSnapshot, + EmulationError, + SandboxContract, + SendMessageResult, + internal, + TreasuryContract +} from '@ton/sandbox'; +import { KeyPair, getSecureRandomBytes, keyPairFromSeed } from '@ton/crypto'; +import { Opcodes, walletV5ConfigToCell } from '../wrappers/wallet-v5'; +import { bufferToBigInt, getRandomInt, pickRandomNFrom } from './utils'; +import { findTransactionRequired, randomAddress } from '@ton/test-utils'; +import { estimateMessageImpact, getMsgPrices, MsgPrices, storageGeneric } from './gasUtils'; +import { ErrorsV5 } from '../wrappers/Errors'; +import { + WalletV5Test, + MessageOut, + WalletActions, + ExtendedAction, + message2action, + ExtensionAdd, + ExtensionRemove +} from '../wrappers/wallet-v5-test'; + +describe('Wallet v5 external tests', () => { + let blockchain: Blockchain; + let keys: KeyPair; + let wallet: SandboxContract; + let newWallet: SandboxContract; + let walletId: bigint; + const validOpCodes = [ + Opcodes.auth_signed, + Opcodes.auth_signed_internal, + Opcodes.auth_extension + ]; + const defaultExternalMode = SendMode.PAY_GAS_SEPARATELY | SendMode.IGNORE_ERRORS; + + let mockMessage: MessageOut; + let owner: SandboxContract; + let testWalletBc: SandboxContract; + let testWalletMc: SandboxContract; + let testExtensionBc: SandboxContract; + let testExtensionMc: SandboxContract; + + let initialState: BlockchainSnapshot; + let hasExtension: BlockchainSnapshot; + let hasMcWallet: BlockchainSnapshot; + + let msgPrices: MsgPrices; + let msgPricesMc: MsgPrices; + // let gasPrices: GasPrices; + + let code: Cell; + + let curTime: () => number; + let loadFrom: (snap: BlockchainSnapshot) => Promise; + let getWalletData: (from?: Address) => Promise; + let someMessages: (num: number) => OutActionSendMsg[]; + let someExtensions: ( + num: number, + action: 'add_extension' | 'remove_extension' + ) => ExtendedAction[]; + let assertMockMessage: (txs: Transaction[], from?: Address) => void; + let assertInternal: (txs: Transaction[], from: Address, exp: number) => void; + let shouldRejectWith: (p: Promise, code: number) => Promise; + let assertSendMessages: ( + exp: number, + wallet_id: bigint, + valid_until: number, + seqno: bigint | number, + messages: MessageOut[], + key: Buffer, + via?: Sender | ExtensionSender + ) => Promise; + + //type PartialBy = Omit & Partial>; + type TestArgs = { + walletId: bigint; + valid_until: number; + seqno: bigint | number; + actions: WalletActions; + key: Buffer; + prevState?: Cell; + extra?: any; + }; + type TestCase = (arg: TestArgs) => Promise; + type ExtensionSender = ( + arg: WalletActions + ) => Promise<{ op: number; res: SendMessageResult; is_inernal: boolean }>; + + /* Idea behind those wrappers is that we have common expectations of state + /* Everything common between Internal/External/Extension actions goes to wrapper. + /* Anything case specific goes to callbacks + */ + let testSendModes: ( + internal: boolean, + exp: number, + mask: SendMode, + modes: SendMode[], + customSender?: ExtensionSender + ) => Promise; + let extensionSender: ExtensionSender; + let testSendInit: (shouldSucceed: TestCase, validateNewWallet: TestCase) => Promise; + let testSetCode: (shouldSucceed: TestCase, validate: TestCase) => Promise; + let testAddExt: (shouldSucceed: TestCase, custom_addr?: Address) => Promise; + let testAddExtAlreadyIn: (shouldFail: TestCase) => Promise; + let testAddExtWrongChain: (shouldFail: TestCase, validateOnMc: TestCase) => Promise; + let testRemoveExt: (shouldSucceed: TestCase) => Promise; + let testAddRemoveSend: (shouldSucceed: TestCase) => Promise; + let testRemoveExtNonExistent: (shouldSucceed: TestCase) => Promise; + + beforeAll(async () => { + blockchain = await Blockchain.create(); + code = await compile('wallet_v5'); + keys = keyPairFromSeed(await getSecureRandomBytes(32)); + + owner = await blockchain.treasury('wallet_owner'); + testWalletBc = await blockchain.treasury('test_wallet', { workchain: 0 }); + testWalletMc = await blockchain.treasury('test_wallet', { workchain: -1 }); + + testExtensionBc = await blockchain.treasury('test_extension', { workchain: 0 }); + testExtensionMc = await blockchain.treasury('test_extension', { workchain: -1 }); + + mockMessage = { + message: internal_relaxed({ + to: testWalletBc.address, + value: toNano('1'), + body: beginCell().storeUint(0xdeadbeef, 32).endCell() + }), + mode: defaultExternalMode + }; + + msgPrices = getMsgPrices(blockchain.config, 0); + msgPricesMc = getMsgPrices(blockchain.config, -1); + + walletId = BigInt(getRandomInt(10, 1337)); + wallet = blockchain.openContract( + WalletV5Test.createFromConfig( + { + seqno: 0, + walletId, + signatureAllowed: true, + publicKey: keys.publicKey, + extensions: Dictionary.empty() + }, + code + ) + ); + + const deploy = await wallet.sendDeploy(owner.getSender(), toNano('100000')); + + expect(deploy.transactions).toHaveTransaction({ + on: wallet.address, + from: owner.address, + aborted: false, + deploy: true + }); + + initialState = blockchain.snapshot(); + + curTime = () => { + return blockchain.now ?? Math.floor(Date.now() / 1000); + }; + + loadFrom = async snap => { + if (snap == undefined) { + throw new Error("Snapshot doesn't exist yet. Check tests order"); + } + await blockchain.loadFrom(snap); + }; + getWalletData = async (address?: Address) => { + const contractAddress = address ?? wallet.address; + const smc = await blockchain.getContract(contractAddress); + if (!smc.account.account) throw 'Account not found'; + if (smc.account.account.storage.state.type != 'active') + throw 'Atempting to get data on inactive account'; + if (!smc.account.account.storage.state.state.data) throw 'Data is not present'; + return smc.account.account.storage.state.state.data; + }; + + someMessages = n => { + const messages: OutActionSendMsg[] = new Array(n); + for (let i = 0; i < n; i++) { + messages[i] = { + type: 'sendMsg', + mode: defaultExternalMode, + outMsg: internal_relaxed({ + to: testWalletBc.address, + value: toNano('1'), + body: beginCell().storeUint(i, 32).endCell() + }) + }; + } + return messages; + }; + someExtensions = (n, action) => { + const extensions: ExtendedAction[] = new Array(n); + + for (let i = 0; i < n; i++) { + extensions[i] = { + type: action, + address: randomAddress() + }; + } + + return extensions; + }; + + assertMockMessage = (txs, from) => { + const fromAddr = from ?? wallet.address; + expect(txs).toHaveTransaction({ + on: testWalletBc.address, + from: fromAddr, + value: toNano('1'), + body: beginCell().storeUint(0xdeadbeef, 32).endCell() + }); + }; + assertInternal = (txs, from, exp) => { + const expSuccess = exp == 0; + expect(txs).toHaveTransaction({ + on: wallet.address, + from, + success: expSuccess, + aborted: !expSuccess, + outMessagesCount: !expSuccess ? 1 : 0 + }); + }; + shouldRejectWith = async (p, code) => { + try { + const res = await p; + console.log((res as any).transactions[0].description); + throw new Error(`Should throw ${code}`); + } catch (e: unknown) { + if (e instanceof EmulationError) { + expect(e.exitCode !== undefined && e.exitCode == code).toBe(true); + } else { + throw e; + } + } + }; + + assertSendMessages = async (exp, wallet_id, valid_until, seqno, messages, key, via) => { + let res: SendMessageResult; + let op: number; + let isInternal: boolean; + + const smc = await blockchain.getContract(wallet.address); + let balanceBefore = BigInt(smc.balance); + + if (typeof via == 'function') { + const customRes = await via({ wallet: messages.map(message2action) }); + isInternal = customRes.is_inernal; + res = customRes.res; + op = customRes.op; + } else { + if (via) { + op = Opcodes.auth_signed_internal; + isInternal = true; + res = await wallet.sendMessagesInternal( + via, + wallet_id, + valid_until, + seqno, + key, + messages + ); + } else { + isInternal = false; + op = Opcodes.auth_signed; + res = await wallet.sendMessagesExternal( + wallet_id, + valid_until, + seqno, + key, + messages + ); + } + } + + if (exp == 0) { + const sendTx = findTransactionRequired(res.transactions, { + on: wallet.address, + op, + aborted: false, + outMessagesCount: messages.length + }); + // console.log(sendTx.description); + // console.log(sendTx.blockchainLogs); + + const storageFee = storageGeneric(sendTx).storageFeesCollected; + + balanceBefore -= storageFee; + + for (let i = 0; i < messages.length; i++) { + // console.log("Message:", i); + const msgOut = sendTx.outMessages.get(i)!; + if (msgOut.info.type == 'internal') { + const curPrices = msgOut.info.dest.workChain == 0 ? msgPrices : msgPricesMc; + const estMessage = estimateMessageImpact( + messages[i].message, + sendTx, + curPrices, + balanceBefore, + messages[i].mode, + i > 0 + ); + expect(res.transactions).toHaveTransaction({ + on: msgOut.info.dest, + from: wallet.address, + value: estMessage.expValue, + body: msgOut.body + }); + balanceBefore = estMessage.balanceAfter; + } + } + // console.log("Calculated balance:", balanceBefore); + // console.log("Real balance:", smc.balance); + expect(balanceBefore).toEqual(smc.balance); + } else { + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + outMessagesCount: isInternal ? 1 : 0, // On internal we should bounce + aborted: isInternal, + op, + exitCode: exp + }); + } + return res; + }; + testSendModes = async (internal, exp, mask, modes, sender) => { + let testMsgs: MessageOut[] = []; + let i = 0; + let seqNo = await wallet.getSeqno(); + const oldSeqno = seqNo; + const prevState = blockchain.snapshot(); + const testSender = sender ?? internal ? owner.getSender() : undefined; + /* + if(testSender == sender) { + console.log("Custom sender is working!"); + } + */ + try { + for (let mode of modes) { + // console.log("Testing mode:", mode); + const newMsg: MessageOut = { + message: internal_relaxed({ + to: testWalletBc.address, + value: toNano(1), + body: beginCell().storeUint(i++, 32).endCell() + }), + mode: mode | mask + }; + testMsgs.push(newMsg); + + // Test in single mode first + await assertSendMessages( + exp, + walletId, + curTime() + 1000, + seqNo, + [newMsg], + keys.secretKey, + testSender + ); + expect(await wallet.getSeqno()).toEqual(++seqNo); + } + + await blockchain.loadFrom(prevState); + + // Now all at once + await assertSendMessages( + exp, + walletId, + curTime() + 1000, + oldSeqno, + testMsgs, + keys.secretKey, + testSender + ); + expect(await wallet.getSeqno()).toEqual(oldSeqno + 1); + } finally { + await blockchain.loadFrom(prevState); + } + }; + extensionSender = async actions => { + const res = await wallet.sendExtensionActions(testExtensionBc.getSender(), actions); + return { + is_inernal: true, + res, + op: Opcodes.auth_extension + }; + }; + testSendInit = async (shouldWork, validateNewWallet) => { + let newWalletId: bigint; + let seqNo = await wallet.getSeqno(); + + do { + newWalletId = BigInt(getRandomInt(1000, 100000)); + } while (newWalletId == walletId); + + const newWalletData = walletV5ConfigToCell({ + walletId: newWalletId, + seqno: 0, + signatureAllowed: true, + publicKey: keys.publicKey, // Same key + extensions: Dictionary.empty() + }); + + // Deploying it to masterchain + const newAddress = contractAddress(-1, { code, data: newWalletData }); + + const testArgs: TestArgs = { + walletId, + valid_until: curTime() + 100, + seqno: seqNo, + actions: { + wallet: [ + { + type: 'sendMsg', + outMsg: internal_relaxed({ + to: newAddress, + value: toNano('100'), + init: { + code, + data: newWalletData + } + }), + mode: defaultExternalMode + } + ] + }, + key: keys.secretKey, + extra: { new_address: newAddress } + }; + + await shouldWork(testArgs); + + newWallet = blockchain.openContract(WalletV5Test.createFromAddress(newAddress)); + + // New wallet should be able to send message with current key + + await validateNewWallet({ + walletId: newWalletId, + valid_until: curTime() + 100, + seqno: 0, + actions: {}, // Won't be used by this handler anyway + key: keys.secretKey + }); + // res = await newWallet.sendMessagesExternal(newWalletId, curTime() + 100, 0, keys.secretKey, [mockMessage]); + + // Let's test getters while we can + expect((await newWallet.getWalletId()).subwalletNumber).toEqual(Number(newWalletId)); + expect(await newWallet.getPublicKey()).toEqual(bufferToBigInt(keys.publicKey)); + expect(await newWallet.getIsSignatureAuthAllowed()).toBe(-1); + + hasMcWallet = blockchain.snapshot(); + }; + testSetCode = async (testCb, validateCb) => { + let testMsgs: OutAction[] = new Array(254); + const newCode = beginCell().storeUint(getRandomInt(0, 1000), 32).endCell(); + let seqNo = await wallet.getSeqno(); + + const setCodeAction: OutAction = { + type: 'setCode', + newCode + }; + + testMsgs = someMessages(254); // Saving space for set_code + + const onlySetCode = [setCodeAction]; + const setCodeLast = [...testMsgs, setCodeAction]; + const setCodeFirst = [setCodeAction, ...testMsgs]; + const setCodeShuffle = [...testMsgs]; + + const setCodeIdx = getRandomInt(1, setCodeShuffle.length - 1); + // Just replace some random position with setCode + setCodeShuffle[setCodeIdx] = setCodeAction; + + const extraSetCode = [...setCodeShuffle]; + let newIdx = setCodeIdx; + + do { + newIdx = getRandomInt(1, setCodeShuffle.length - 1); + } while (newIdx == setCodeIdx); + // Insert another one, in case code removes first matched only + extraSetCode[newIdx] = setCodeAction; + + const prevState = await getWalletData(); + const defaultArgs = { + walletId, + seqno: seqNo, + valid_until: curTime() + 1000, + key: keys.secretKey, + prevState + }; + for (let actionSet of [ + onlySetCode, + setCodeLast, + setCodeFirst, + setCodeShuffle, + extraSetCode + ]) { + //const setCodeRequest = WalletV5Test.requestMessage(false, walletId, curTime() + 100, seqNo, {wallet: actionSet}, keys.secretKey); + const negTestArgs: TestArgs = { + ...defaultArgs, + seqno: seqNo, + actions: { wallet: actionSet } + }; + // const negTestArgs: TestArgs = {...defaultArgs, actions: {wallet: actionSet}}; + await testCb(negTestArgs); + seqNo = await wallet.getSeqno(); + } + + // Validate that it has nothing to do with message list + await validateCb({ ...defaultArgs, seqno: seqNo, actions: { wallet: testMsgs } }); + }; + testAddExt = async (checkTx, customAddr) => { + let seqNo = await wallet.getSeqno(); + + const extensionAddr = customAddr ?? testExtensionBc.address; + const testArgs: TestArgs = { + walletId, + valid_until: curTime() + 100, + seqno: seqNo, + actions: { + extended: [ + { + type: 'add_extension', + address: extensionAddr + } + ] + }, + key: keys.secretKey + }; + + await checkTx(testArgs); + + const installedExt = await wallet.getExtensionsArray(); + expect(installedExt.findIndex(a => a.equals(extensionAddr))).toBeGreaterThanOrEqual(0); + // expect(await wallet.getSeqno()).toEqual(seqNo + 1); + }; + testAddExtAlreadyIn = async checkTx => { + await loadFrom(hasExtension); + + const installedBefore = await wallet.getExtensionsArray(); + let seqNo = await wallet.getSeqno(); + + const testArgs: TestArgs = { + walletId, + valid_until: curTime() + 100, + seqno: seqNo, + actions: { + extended: [ + { + type: 'add_extension', + address: testExtensionBc.address + } + ] + }, + key: keys.secretKey + }; + + await checkTx(testArgs); + + const installedAfter = await wallet.getExtensionsArray(); + expect(installedBefore.length).toEqual(installedAfter.length); + + for (let i = 0; i < installedBefore.length; i++) { + expect(installedBefore[i].equals(installedAfter[i])).toBe(true); + } + }; + testAddExtWrongChain = async (shouldReject, validate) => { + const prevState = blockchain.snapshot(); + let seqNo = await wallet.getSeqno(); + const installedBefore = await wallet.getExtensionsArray(); + + let testArgs: TestArgs = { + walletId, + valid_until: curTime() + 100, + seqno: seqNo, + actions: { + extended: [ + { + type: 'add_extension', + address: testExtensionMc.address + } + ] + }, + key: keys.secretKey + }; + + await shouldReject(testArgs); + + const installedAfter = await wallet.getExtensionsArray(); + expect(installedBefore.length).toEqual(installedAfter.length); + + for (let i = 0; i < installedBefore.length; i++) { + expect(installedBefore[i].equals(installedAfter[i])).toBe(true); + } + // But it should work for the wallet in basechain + + const newSeqNo = await newWallet.getSeqno(); + const newId = BigInt((await newWallet.getWalletId()).subwalletNumber); + + testArgs = { + walletId: newId, + valid_until: curTime() + 100, + seqno: newSeqNo, + actions: { + extended: [ + { + type: 'add_extension', + address: testExtensionMc.address + } + ] + }, + key: keys.secretKey + }; + + let installedExt = await newWallet.getExtensionsArray(); + expect(installedExt.findIndex(a => a.equals(testExtensionMc.address))).toBe(-1); + + await validate(testArgs); + + installedExt = await newWallet.getExtensionsArray(); + expect( + installedExt.findIndex(a => a.equals(testExtensionMc.address)) + ).toBeGreaterThanOrEqual(0); + // expect(await wallet.getSeqno()).toEqual(seqNo + 1); + + await loadFrom(prevState); + }; + testRemoveExt = async shouldRemove => { + await loadFrom(hasExtension); + let seqNo = await wallet.getSeqno(); + let installedExt = await wallet.getExtensionsArray(); + expect(installedExt[0].equals(testExtensionBc.address)).toBe(true); + + const testArgs: TestArgs = { + walletId, + valid_until: curTime() + 100, + seqno: seqNo, + actions: { + extended: [ + { + type: 'remove_extension', + address: testExtensionBc.address + } + ] + }, + key: keys.secretKey + }; + + await shouldRemove(testArgs); + + installedExt = await wallet.getExtensionsArray(); + expect(installedExt.findIndex(a => a.equals(testExtensionBc.address))).toBe(-1); + }; + testRemoveExtNonExistent = async shouldFail => { + await loadFrom(hasExtension); + let seqNo = await wallet.getSeqno(); + const differentExt = await blockchain.treasury('totally different extension'); + const installedBefore = await wallet.getExtensionsArray(); + + expect(installedBefore.length).toBe(1); + expect(installedBefore[0].equals(testExtensionBc.address)).toBe(true); + + const testArgs: TestArgs = { + walletId, + valid_until: curTime() + 100, + seqno: seqNo, + actions: { + extended: [ + { + type: 'remove_extension', + address: differentExt.address + } + ] + }, + key: keys.secretKey + }; + await shouldFail(testArgs); + + const extAfter = await wallet.getExtensionsArray(); + expect(extAfter.length).toBe(1); + // expect(await wallet.getSeqno()).toEqual(seqNo + 1); + expect( + extAfter.findIndex(e => e.equals(testExtensionBc.address)) + ).toBeGreaterThanOrEqual(0); + }; + testAddRemoveSend = async shouldWork => { + const prevState = blockchain.snapshot(); + const seqNo = await wallet.getSeqno(); + + const extBefore = await wallet.getExtensionsArray(); + const testMessages = someMessages(255); // Full pack + const testExtensions = someExtensions(100, 'add_extension'); + + // Let's pick some of those for removal + const removeExt = pickRandomNFrom(5, testExtensions).map(e => { + const res: ExtensionRemove = { + type: 'remove_extension', + address: (e as ExtensionAdd).address + }; + return res; + }); + // console.log("Remove extensions:", removeExt); + const shouldStay = (testExtensions as ExtensionAdd[]) + .filter(e => removeExt.find(r => r.address.equals(e.address)) == undefined) + .map(e => e.address); + shouldStay.push(...extBefore); + testExtensions.push(...removeExt); + + const testArgs: TestArgs = { + walletId, + valid_until: curTime() + 100, + seqno: seqNo, + actions: { + wallet: testMessages, + extended: testExtensions + }, + key: keys.secretKey + }; + + await shouldWork(testArgs); + + const extAfter = await wallet.getExtensionsArray(); + + expect(extAfter.length).toEqual(shouldStay.length); + for (let i = 0; i < shouldStay.length; i++) { + const testAddr = shouldStay[i]; + expect(extAfter.findIndex(addr => addr.equals(testAddr))).toBeGreaterThanOrEqual(0); + expect(removeExt.findIndex(e => e.address.equals(testAddr))).toBe(-1); + } + // expect(await wallet.getSeqno()).toEqual(seqNo + 1); + await loadFrom(prevState); + }; + }); + describe('Basic', () => { + it('should deploy', async () => {}); + it('should be able to receive basic transfer', async () => { + const testWallet = await blockchain.treasury('test_wallet'); + const assertSimple = async (body?: Cell) => { + const res = await testWallet.send({ + to: wallet.address, + value: toNano(getRandomInt(1, 100)), + body, + sendMode: SendMode.PAY_GAS_SEPARATELY + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testWallet.address, + aborted: false, + outMessagesCount: 0 + }); + }; + + await assertSimple(); + await assertSimple( + beginCell().storeUint(0, 32).storeStringTail('Hey, bruh!').endCell() + ); + + const validSet = new Set(validOpCodes); + let testOp: number; + + do { + testOp = getRandomInt(1, (1 << 32) - 1); + } while (validSet.has(testOp)); + + await assertSimple( + beginCell().storeUint(testOp, 32).storeUint(curTime(), 64).endCell() + ); + }); + }); + describe('Actions', () => { + describe('External', () => { + it('should be able to send message to arbitrary address', async () => { + const msgValue = toNano(getRandomInt(1, 10)); + const randomBody = beginCell().storeUint(curTime(), 64).endCell(); + const seqNo = BigInt(await wallet.getSeqno()); + + await assertSendMessages( + 0, + walletId, + curTime() + 1000, + seqNo, + [ + { + message: internal_relaxed({ + to: testWalletBc.address, + value: msgValue, + body: randomBody + }), + mode: defaultExternalMode + }, + { + message: internal_relaxed({ + to: testWalletMc.address, + value: msgValue, + body: randomBody + }), + mode: defaultExternalMode + } + ], + keys.secretKey + ); + + const seqnoAfter = BigInt(await wallet.getSeqno()); + expect(seqnoAfter).toEqual(seqNo + 1n); + }); + it('should reject message with wrong signature', async () => { + const seqNo = await wallet.getSeqno(); + const badKeys = keyPairFromSeed(await getSecureRandomBytes(32)); + + await shouldRejectWith( + wallet.sendMessagesExternal( + walletId, + curTime() + 1000, + seqNo, + badKeys.secretKey, + [mockMessage] + ), + ErrorsV5.invalid_signature + ); + expect(await wallet.getSeqno()).toEqual(seqNo); + + const res = await wallet.sendMessagesExternal( + walletId, + curTime() + 1000, + seqNo, + keys.secretKey, + [mockMessage] + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + aborted: false, + outMessagesCount: 1 + }); + }); + it('should reject external message with prefix other than signed_external', async () => { + // All of the valid ops except acceptable, plus one random + const nonExternalOps = [ + ...validOpCodes.filter(op => op != Opcodes.auth_signed), + 0xdeadbeef + ]; + const seqNo = await wallet.getSeqno(); + const validMsg = WalletV5Test.requestMessage( + false, + walletId, + curTime() + 1000, + BigInt(seqNo), + {} + ); + const msgTail = validMsg.beginParse().skip(32); // skip op; + + for (let op of nonExternalOps) { + const newMsg = WalletV5Test.signRequestMessage( + beginCell().storeUint(op, 32).storeSlice(msgTail).endCell(), + keys.secretKey + ); + await shouldRejectWith( + wallet.sendExternalSignedMessage(newMsg), + ErrorsV5.invalid_message_operation + ); + // Should not change seqno + expect(await wallet.getSeqno()).toEqual(seqNo); + } + + // Validate that original message works + const res = await wallet.sendExternalSignedMessage( + WalletV5Test.signRequestMessage(validMsg, keys.secretKey) + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + aborted: false + }); + expect(await wallet.getSeqno()).toEqual(seqNo + 1); + }); + it('should be able to send up to 255 messages', async () => { + let testMsgs: MessageOut[] = new Array(255); + const seqNo = BigInt(await wallet.getSeqno()); + + for (let i = 0; i < 255; i++) { + testMsgs[i] = { + message: internal_relaxed({ + to: testWalletBc.address, + value: toNano('1'), + body: beginCell().storeUint(i, 32).endCell() + }), + mode: defaultExternalMode + }; + } + + await assertSendMessages( + 0, + walletId, + curTime() + 1000, + seqNo, + testMsgs, + keys.secretKey + ); + }); + it('should be able to send messages with different send modes', async () => { + await testSendModes(false, 0, SendMode.IGNORE_ERRORS, [ + SendMode.NONE, + SendMode.PAY_GAS_SEPARATELY, + SendMode.CARRY_ALL_REMAINING_BALANCE + ]); + }); + it('should reject send modes without IGNORE_ERRORS', async () => { + await testSendModes( + false, + ErrorsV5.external_send_message_must_have_ignore_errors_send_mode, + 0, + [ + SendMode.NONE, + SendMode.PAY_GAS_SEPARATELY, + SendMode.CARRY_ALL_REMAINING_BALANCE, + SendMode.CARRY_ALL_REMAINING_BALANCE | SendMode.DESTROY_ACCOUNT_IF_ZERO + ] + ); + }); + it('should be able to send message with init state', async () => { + await testSendInit( + async args => { + if (!Address.isAddress(args.extra.new_address)) { + throw new TypeError('Callback requires wallet address'); + } + const reqMsg = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(reqMsg); + expect(res.transactions).toHaveTransaction({ + on: args.extra.new_address, + aborted: false, + deploy: true + }); + }, + async args => { + // New wallet should be able to send from new wallet via external + const res = await newWallet.sendMessagesExternal( + args.walletId, + args.valid_until, + args.seqno, + args.key, + [mockMessage] + ); + assertMockMessage(res.transactions, newWallet.address); + } + ); + }); + it('should be able to send external message', async () => { + const seqNo = await wallet.getSeqno(); + const testPayload = BigInt(getRandomInt(0, 100000)); + const testBody = beginCell().storeUint(testPayload, 32).endCell(); + + const res = await wallet.sendMessagesExternal( + walletId, + curTime() + 100, + seqNo, + keys.secretKey, + [ + { + message: { + info: { + type: 'external-out', + createdAt: 0, + createdLt: 0n, + dest: new ExternalAddress(testPayload, 32), + src: null + }, + body: testBody + }, + mode: defaultExternalMode + } + ] + ); + + const txSuccess = findTransactionRequired(res.transactions, { + on: wallet.address, + op: Opcodes.auth_signed, + aborted: false + }); + + expect(txSuccess.externals.length).toBe(1); + + const extOut = txSuccess.externals[0]; + + expect(extOut.info.dest!.value).toBe(testPayload); + expect(extOut.body).toEqualCell(testBody); + }); + it('should reject message with invalid seqno', async () => { + const seqNo = await wallet.getSeqno(); + expect(seqNo).toBeGreaterThan(2); // For better test + const testDelta = getRandomInt(2, seqNo); + + for (let testSeq of [seqNo - 1, seqNo + 1, seqNo + testDelta, seqNo - testDelta]) { + await shouldRejectWith( + wallet.sendMessagesExternal( + walletId, + curTime() + 100, + testSeq, + keys.secretKey, + [mockMessage] + ), + ErrorsV5.invalid_seqno + ); + expect(await wallet.getSeqno()).toEqual(seqNo); + } + }); + it('should reject message with invalid subwallet', async () => { + const seqNo = await wallet.getSeqno(); + const testDelta = BigInt(getRandomInt(2, Number(walletId))); + + for (let testId of [ + walletId - 1n, + walletId + 1n, + walletId - testDelta, + walletId + testDelta + ]) { + await shouldRejectWith( + wallet.sendMessagesExternal( + testId, + curTime() + 100, + seqNo, + keys.secretKey, + [mockMessage] + ), + ErrorsV5.invalid_wallet_id + ); + expect(await wallet.getSeqno()).toEqual(seqNo); + } + }); + it('should reject expired message', async () => { + blockchain.now = curTime(); // Stop ticking + const seqNo = await wallet.getSeqno(); + const testDelta = getRandomInt(1, 10000); + + // We're treating current time as expired. Should we? + for (let testUntil of [blockchain.now, blockchain.now - testDelta]) { + await shouldRejectWith( + wallet.sendMessagesExternal(walletId, testUntil, seqNo, keys.secretKey, [ + mockMessage + ]), + ErrorsV5.expired + ); + expect(await wallet.getSeqno()).toEqual(seqNo); + } + + const res = await wallet.sendMessagesExternal( + walletId, + blockchain.now + 1, + seqNo, + keys.secretKey, + [mockMessage] + ); + + assertMockMessage(res.transactions); + expect(await wallet.getSeqno()).toEqual(seqNo + 1); + }); + it('should reject set_code action', async () => { + await testSetCode( + async args => { + if (args.actions == undefined || args.key == undefined) { + throw new Error('Actions and keys are required'); + } + const setCodeRequest = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(setCodeRequest); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + outMessagesCount: 0, + success: true, // Because of commit call + exitCode: 9 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + }, + async args => { + if (args.actions == undefined || args.key == undefined) { + throw new Error('Actions and keys are required'); + } + const sendJustMessages = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(sendJustMessages); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + aborted: false, + outMessagesCount: 254, + exitCode: 0 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + } + ); + }); + it('should be able to add extension', async () => { + await testAddExt(async args => { + const reqMsg = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(reqMsg); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + outMessagesCount: 0, + exitCode: 0 // Because of commit we can't rely on compute phase status + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + }); + + hasExtension = blockchain.snapshot(); + }); + it('should not be able to install already installed extendsion', async () => { + await testAddExtAlreadyIn(async args => { + const reqMsg = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(reqMsg); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + outMessagesCount: 0, + exitCode: ErrorsV5.add_extension + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + }); + }); + it('should not be able to install extension from different chain', async () => { + await testAddExtWrongChain( + async args => { + const reqMsg = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(reqMsg); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + outMessagesCount: 0, + exitCode: ErrorsV5.extension_wrong_workchain + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + }, + async args => { + const reqMsg = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await newWallet.sendExternalSignedMessage(reqMsg); + expect(res.transactions).toHaveTransaction({ + on: newWallet.address, + op: Opcodes.auth_signed, + outMessagesCount: 0, + exitCode: 0 // We're good now + }); + expect(await newWallet.getSeqno()).toEqual(Number(args.seqno) + 1); + } + ); + }); + it('should be able to remove extension', async () => { + await testRemoveExt(async args => { + const reqMsg = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(reqMsg); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + outMessagesCount: 0, + exitCode: 0 // Because of commit we can't rely on compute phase status + }); + }); + }); + it('should throw on removing non-existent extension', async () => { + await testRemoveExtNonExistent(async args => { + const reqMsg = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(reqMsg); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + outMessagesCount: 0, + exitCode: ErrorsV5.remove_extension + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + }); + }); + // Doesn't make much sense, since inderectly tested in too many places + it.skip('empty action list should increase seqno', async () => { + const seqNo = await wallet.getSeqno(); + const testMsg = WalletV5Test.requestMessage( + false, + walletId, + curTime() + 100, + seqNo, + {}, + keys.secretKey + ); + const res = await wallet.sendExternalSignedMessage(testMsg); + + expect(await wallet.getSeqno()).toEqual(seqNo + 1); + }); + it('should be able to add/remove extensions and send messages in one go', async () => { + await testAddRemoveSend(async args => { + const reqMsg = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(reqMsg); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + aborted: false, + outMessagesCount: 255 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + }); + }); + }); + describe('Internal', () => { + it('should be able to send message to arbitrary address', async () => { + const msgValue = toNano(getRandomInt(1, 10)); + const randomBody = beginCell().storeUint(curTime(), 64).endCell(); + const seqNo = BigInt(await wallet.getSeqno()); + + const res = await assertSendMessages( + 0, + walletId, + curTime() + 1000, + seqNo, + [ + { + message: internal_relaxed({ + to: testWalletBc.address, + value: msgValue, + body: randomBody + }), + mode: SendMode.PAY_GAS_SEPARATELY + }, + { + message: internal_relaxed({ + to: testWalletMc.address, + value: msgValue, + body: randomBody + }), + mode: SendMode.PAY_GAS_SEPARATELY + } + ], + keys.secretKey, + owner.getSender() + ); + + const seqnoAfter = BigInt(await wallet.getSeqno()); + expect(seqnoAfter).toEqual(seqNo + 1n); + }); + it('should ignore message with wrong signature', async () => { + const seqNo = await wallet.getSeqno(); + const badKeys = keyPairFromSeed(await getSecureRandomBytes(32)); + const stateBefore = await getWalletData(); + + const msgActions = [message2action(mockMessage)]; + let testMsg = WalletV5Test.requestMessage( + true, + walletId, + curTime() + 100, + seqNo, + { wallet: msgActions }, + badKeys.secretKey + ); + let res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: testMsg + }); + assertInternal(res.transactions, owner.address, 0); + expect(await getWalletData()).toEqualCell(stateBefore); + + testMsg = WalletV5Test.requestMessage( + true, + walletId, + curTime() + 100, + seqNo, + { wallet: msgActions }, + keys.secretKey + ); + res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: testMsg + }); + assertMockMessage(res.transactions); + expect(await wallet.getSeqno()).toEqual(seqNo + 1); + }); + + it('should bounce message with invalid subwallet', async () => { + const seqNo = await wallet.getSeqno(); + const testDelta = BigInt(getRandomInt(2, Number(walletId))); + const stateBefore = await getWalletData(); + + for (let testId of [ + walletId - 1n, + walletId + 1n, + walletId - testDelta, + walletId + testDelta + ]) { + const res = await wallet.sendMessagesInternal( + owner.getSender(), + testId, + curTime() + 100, + seqNo, + keys.secretKey, + [mockMessage] + ); + assertInternal(res.transactions, owner.address, ErrorsV5.invalid_wallet_id); + expect(await getWalletData()).toEqualCell(stateBefore); + } + }); + it('should bounce expired message', async () => { + blockchain.now = curTime(); // Stop ticking + const seqNo = await wallet.getSeqno(); + const testDelta = getRandomInt(1, 10000); + const stateBefore = await getWalletData(); + + // We're treating current time as expired. Should we? + for (let testUntil of [blockchain.now, blockchain.now - testDelta]) { + const res = await wallet.sendMessagesInternal( + owner.getSender(), + walletId, + testUntil, + seqNo, + keys.secretKey, + [mockMessage] + ); + assertInternal(res.transactions, owner.address, ErrorsV5.expired); + expect(await getWalletData()).toEqualCell(stateBefore); + } + + const res = await wallet.sendMessagesExternal( + walletId, + blockchain.now + 1, + seqNo, + keys.secretKey, + [mockMessage] + ); + assertMockMessage(res.transactions); + expect(await wallet.getSeqno()).toEqual(seqNo + 1); + }); + it('should reject set_code action', async () => { + await testSetCode( + async args => { + const setCodeRequest = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: setCodeRequest + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed_internal, + outMessagesCount: 1, // bounce + aborted: true, + success: false, // No commit anymore + exitCode: 9 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno)); // On internal seqno is not commited + }, + async args => { + if (args.actions == undefined || args.key == undefined) { + throw new Error('Actions and keys are required'); + } + const sendJustMessages = WalletV5Test.requestMessage( + false, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExternalSignedMessage(sendJustMessages); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + aborted: false, + outMessagesCount: 254, + exitCode: 0 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + } + ); + }); + it('should bounce message with invalid seqno', async () => { + const seqNo = await wallet.getSeqno(); + expect(seqNo).toBeGreaterThan(2); // For better test + const testDelta = getRandomInt(2, seqNo); + const stateBefore = await getWalletData(); + + for (let testSeq of [seqNo - 1, seqNo + 1, seqNo + testDelta, seqNo - testDelta]) { + const res = await wallet.sendMessagesInternal( + owner.getSender(), + walletId, + curTime() + 100, + testSeq, + keys.secretKey, + [mockMessage] + ); + assertInternal(res.transactions, owner.address, ErrorsV5.invalid_seqno); + expect(await getWalletData()).toEqualCell(stateBefore); + } + }); + it('should ignore internal message with prefix other than signed_internal', async () => { + // All of the valid ops except acceptable, plus one random + const nonExternalOps = [ + ...validOpCodes.filter(op => op != Opcodes.auth_signed_internal), + 0xdeadbeef + ]; + const seqNo = await wallet.getSeqno(); + // Not yet signed + const validMsg = WalletV5Test.requestMessage( + true, + walletId, + curTime() + 1000, + BigInt(seqNo), + {} + ); + const msgTail = validMsg.beginParse().skip(32); // skip op; + + for (let op of nonExternalOps) { + const newMsg = WalletV5Test.signRequestMessage( + beginCell().storeUint(op, 32).storeSlice(msgTail).endCell(), + keys.secretKey + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: newMsg + }); + assertInternal(res.transactions, owner.address, 0); // return no bounce + // Should not change seqno + expect(await wallet.getSeqno()).toEqual(seqNo); + } + + // Validate that original message works + const successMsg = WalletV5Test.signRequestMessage(validMsg, keys.secretKey); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: successMsg + }); + assertInternal(res.transactions, owner.address, 0); + expect(await wallet.getSeqno()).toEqual(seqNo + 1); + }); + it('should ignore internal message with correct prefix, but incorrect length', async () => { + const seqNo = await wallet.getSeqno(); + // So we have message with bad wallet id + const badMsg = WalletV5Test.requestMessage( + true, + walletId - 1n, + curTime() + 1000, + BigInt(seqNo), + {}, + keys.secretKey + ); + + // Now we have it's truncated version + const msgTrunc = beginCell() + .storeBits(badMsg.beginParse().loadBits(badMsg.bits.length - 10)) + .endCell(); // off by one + + let res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: msgTrunc + }); + // Now, because it's truncated it gets ignored + assertInternal(res.transactions, owner.address, 0); + + res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: badMsg + }); + assertInternal(res.transactions, owner.address, ErrorsV5.invalid_wallet_id); + // If we send it as is, the subwallet exception will trigger + }); + it('should be able to send up to 255 messages', async () => { + let testMsgs: MessageOut[] = new Array(255); + const seqNo = BigInt(await wallet.getSeqno()); + + for (let i = 0; i < 255; i++) { + testMsgs[i] = { + message: internal_relaxed({ + to: testWalletBc.address, + value: toNano('1'), + body: beginCell().storeUint(i, 32).endCell() + }), + mode: defaultExternalMode + }; + } + await assertSendMessages( + 0, + walletId, + curTime() + 1000, + seqNo, + testMsgs, + keys.secretKey + ); + }); + it('should be able to send message with init state', async () => { + await testSendInit( + async args => { + if (!Address.isAddress(args.extra.new_address)) { + throw new TypeError('Callback requires wallet address'); + } + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: reqMsg + }); + + expect(res.transactions).toHaveTransaction({ + on: args.extra.new_address, + aborted: false, + deploy: true + }); + }, + async args => { + // New wallet should be able to send from new wallet via external + const res = await newWallet.sendMessagesInternal( + owner.getSender(), + args.walletId, + args.valid_until, + args.seqno, + args.key, + [mockMessage], + toNano('1') + ); + assertMockMessage(res.transactions, newWallet.address); + } + ); + }); + it('should be able to send external message', async () => { + const seqNo = await wallet.getSeqno(); + const testPayload = BigInt(getRandomInt(0, 100000)); + const testBody = beginCell().storeUint(testPayload, 32).endCell(); + + const res = await wallet.sendMessagesInternal( + owner.getSender(), + walletId, + curTime() + 100, + seqNo, + keys.secretKey, + [ + { + message: { + info: { + type: 'external-out', + createdAt: 0, + createdLt: 0n, + dest: new ExternalAddress(testPayload, 32), + src: null + }, + body: testBody + }, + mode: defaultExternalMode + } + ] + ); + + const txSuccess = findTransactionRequired(res.transactions, { + on: wallet.address, + from: owner.address, + op: Opcodes.auth_signed_internal, + aborted: false + }); + + expect(txSuccess.externals.length).toBe(1); + + const extOut = txSuccess.externals[0]; + + expect(extOut.info.dest!.value).toBe(testPayload); + expect(extOut.body).toEqualCell(testBody); + }); + + it('should be able to send messages with various send modes', async () => { + // Internal should work with + await testSendModes(true, 0, SendMode.IGNORE_ERRORS, [ + SendMode.NONE, + SendMode.PAY_GAS_SEPARATELY, + SendMode.CARRY_ALL_REMAINING_INCOMING_VALUE, + SendMode.CARRY_ALL_REMAINING_BALANCE + ]); + // And without IGNORE_ERRORS + await testSendModes(true, 0, 0, [ + SendMode.NONE, + SendMode.PAY_GAS_SEPARATELY, + SendMode.CARRY_ALL_REMAINING_INCOMING_VALUE, + SendMode.CARRY_ALL_REMAINING_BALANCE + ]); + }); + it('should bounce on set_code action', async () => { + await testSetCode( + async args => { + if ( + args.actions == undefined || + args.key == undefined || + args.prevState == undefined + ) { + throw new Error('Actions keys and state are required'); + } + const setCodeRequest = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: setCodeRequest + }); + assertInternal(res.transactions, owner.address, 9); + expect(await getWalletData()).toEqualCell(args.prevState); + }, + async args => { + if ( + args.actions == undefined || + args.key == undefined || + args.prevState == undefined + ) { + throw new Error('Actions keys and state are required'); + } + const sendJustMessages = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: sendJustMessages + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed_internal, + aborted: false, + outMessagesCount: 254, + exitCode: 0 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + } + ); + }); + it('should be able to add extension', async () => { + await loadFrom(initialState); + await testAddExt(async args => { + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: reqMsg + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: owner.address, + op: Opcodes.auth_signed_internal, + outMessagesCount: 0, + aborted: false, + exitCode: 0 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + }); + hasExtension = blockchain.snapshot(); + }); + it('should not be able to install already installed extendsion', async () => { + await testAddExtAlreadyIn(async args => { + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: reqMsg + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: owner.address, + op: Opcodes.auth_signed_internal, + aborted: true, + outMessagesCount: 1, + exitCode: ErrorsV5.add_extension + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno)); + }); + }); + it('should not be able to install extension from different chain', async () => { + await loadFrom(hasMcWallet); + await testAddExtWrongChain( + async args => { + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: reqMsg + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: owner.address, + op: Opcodes.auth_signed_internal, + outMessagesCount: 1, + aborted: true, + exitCode: ErrorsV5.extension_wrong_workchain + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno)); + }, + async args => { + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await newWallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: reqMsg + }); + expect(res.transactions).toHaveTransaction({ + on: newWallet.address, + from: owner.address, + op: Opcodes.auth_signed_internal, + outMessagesCount: 0, + aborted: false + }); + expect(await newWallet.getSeqno()).toEqual(Number(args.seqno) + 1); + } + ); + }); + it('should be able to remove extension', async () => { + await testRemoveExt(async args => { + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: reqMsg + }); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: owner.address, + op: Opcodes.auth_signed_internal, + outMessagesCount: 0, + aborted: false + }); + }); + }); + it('should throw on removing non-existent extension', async () => { + await testRemoveExtNonExistent(async args => { + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: reqMsg + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed_internal, + outMessagesCount: 1, + aborted: true, + exitCode: ErrorsV5.remove_extension + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno)); + }); + }); + it('should be able to add/remove extensions and send messages in one go', async () => { + await testAddRemoveSend(async args => { + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: reqMsg + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed_internal, + aborted: false, + outMessagesCount: 255 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); + }); + }); + describe('Bounce', () => { + it('should ignore bounced mesages', async () => { + await loadFrom(hasExtension); + const seqNo = await wallet.getSeqno(); + const mockActions: WalletActions = { wallet: [message2action(mockMessage)] }; + + // Note that in reality bounce gets prefixed by 0xFFFFFFFF + // With current code, that would mean message would be ignored + // due to op check + // However we still could test as if TVM doesn't add prefix to bounce somehow + const intReq = WalletV5Test.requestMessage( + true, + walletId, + curTime() + 1000, + seqNo, + mockActions, + keys.secretKey + ); + const extReq = WalletV5Test.extensionMessage(mockActions); + // ihr_disable and bounce flags combinations + let flagFuzz = [ + [false, false], + [true, false], + [false, true], + [true, true] + ]; + + const stateBefore = await getWalletData(); + + for (let flags of flagFuzz) { + let res = await blockchain.sendMessage( + internal({ + from: owner.address, + to: wallet.address, + body: intReq, + value: toNano('1'), + bounced: true, + ihrDisabled: flags[0], + bounce: flags[1] + }) + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed_internal, + aborted: false, + outMessagesCount: 0 + }); + expect(await getWalletData()).toEqualCell(stateBefore); + + res = await blockchain.sendMessage( + internal({ + from: testExtensionBc.address, + to: wallet.address, + body: extReq, + value: toNano('1'), + bounced: true, + ihrDisabled: flags[0], + bounce: flags[1] + }) + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_extension, + aborted: false, + outMessagesCount: 0 + }); + expect(await getWalletData()).toEqualCell(stateBefore); + } + + // Let's proove that bounce flag is the reason + const resInt = await blockchain.sendMessage( + internal({ + from: owner.address, + to: wallet.address, + body: intReq, + value: toNano('1'), + ihrDisabled: true, + bounce: true + }) + ); + assertMockMessage(resInt.transactions); + + const resExt = await blockchain.sendMessage( + internal({ + from: testExtensionBc.address, + to: wallet.address, + body: extReq, + value: toNano('1'), + ihrDisabled: false, + bounce: false + }) + ); + assertMockMessage(resExt.transactions); + }); + }); + }); + describe('Extension', () => { + let actionFuzz: WalletActions[]; + beforeAll(async () => { + actionFuzz = [ + { wallet: [message2action(mockMessage)] }, + { wallet: someMessages(10) }, + { extended: someExtensions(5, 'add_extension') }, + { wallet: someMessages(5), extended: someExtensions(5, 'add_extension') }, + { extended: [{ type: 'remove_extension', address: testExtensionBc.address }] }, + { + wallet: someMessages(5), + extended: [{ type: 'remove_extension', address: testExtensionBc.address }] + }, + { extended: [{ type: 'sig_auth', allowed: false }] }, + { wallet: someMessages(5), extended: [{ type: 'sig_auth', allowed: false }] } + ]; + + await loadFrom(hasExtension); + }); + + it('should be able to send message to arbitrary address', async () => { + const msgValue = toNano(getRandomInt(1, 10)); + const randomBody = beginCell().storeUint(curTime(), 64).endCell(); + + await assertSendMessages( + 0, + walletId, + curTime() + 1000, + 0, + [ + { + message: internal_relaxed({ + to: testWalletBc.address, + value: msgValue, + body: randomBody + }), + mode: SendMode.PAY_GAS_SEPARATELY + }, + { + message: internal_relaxed({ + to: testWalletMc.address, + value: msgValue, + body: randomBody + }), + mode: SendMode.PAY_GAS_SEPARATELY + } + ], + keys.secretKey, + extensionSender + ); + }); + it('extension action is only allowed from installed extension address', async () => { + const differentExt = await blockchain.treasury('Not installed'); + + const stateBefore = await getWalletData(); + + for (let testSender of [owner, differentExt]) { + for (let actions of actionFuzz) { + const res = await wallet.sendExtensionActions( + testSender.getSender(), + actions + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testSender.address, + op: Opcodes.auth_extension, + aborted: false, + outMessagesCount: 0 + }); + expect(await getWalletData()).toEqualCell(stateBefore); + } + } + + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + actionFuzz[0] + ); + + assertMockMessage(res.transactions); + expect(await getWalletData()).toEqualCell(stateBefore); + }); + it('extension request with same hash from different workchain should be ignored', async () => { + // Those should completely equal by hash + expect(testExtensionBc.address.hash.equals(testExtensionMc.address.hash)).toBe( + true + ); + + // Extension with such has is installed + const curExt = await wallet.getExtensionsArray(); + expect( + curExt.findIndex(e => e.hash.equals(testExtensionMc.address.hash)) + ).toBeGreaterThanOrEqual(0); + + const stateBefore = await getWalletData(); + + for (let actions of actionFuzz) { + const res = await wallet.sendExtensionActions( + testExtensionMc.getSender(), + actions + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionMc.address, + op: Opcodes.auth_extension, + aborted: false, + outMessagesCount: 0 + }); + expect(await getWalletData()).toEqualCell(stateBefore); + } + }); + it('should be able to send up to 255 messages', async () => { + let testMsgs: MessageOut[] = new Array(255); + const seqNo = BigInt(await wallet.getSeqno()); + + for (let i = 0; i < 255; i++) { + testMsgs[i] = { + message: internal_relaxed({ + to: testWalletBc.address, + value: toNano('1'), + body: beginCell().storeUint(i, 32).endCell() + }), + mode: defaultExternalMode + }; + } + await assertSendMessages( + 0, + walletId, + curTime() + 1000, + seqNo, + testMsgs, + keys.secretKey, + extensionSender + ); + }); + it('should be able to send messages with various send modes', async () => { + let modeSet = [ + SendMode.NONE, + SendMode.PAY_GAS_SEPARATELY, + SendMode.CARRY_ALL_REMAINING_INCOMING_VALUE, + SendMode.CARRY_ALL_REMAINING_BALANCE + ]; + await testSendModes(true, 0, SendMode.IGNORE_ERRORS, modeSet, extensionSender); + // And without IGNORE_ERRORS + await testSendModes(true, 0, 0, modeSet, extensionSender); + }); + it('should be able to send message with init state', async () => { + await testSendInit( + async args => { + if (!Address.isAddress(args.extra.new_address)) { + throw new TypeError('Callback requires wallet address'); + } + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions + ); + + expect(res.transactions).toHaveTransaction({ + on: args.extra.new_address, + aborted: false, + deploy: true + }); + }, + async args => { + // Couldn't think of any better + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + { + wallet: [message2action(mockMessage)] + }, + args.key + ); + + const testMsg = internal_relaxed({ + to: newWallet.address, + value: toNano('2'), + body: reqMsg + }); + + const res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + wallet: [ + { + type: 'sendMsg', + mode: SendMode.PAY_GAS_SEPARATELY, + outMsg: testMsg + } + ] + }); + + // So tx chain ext->basechain wallet->mc wallet->mock message + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: false, + outMessagesCount: 1 + }); + expect(res.transactions).toHaveTransaction({ + on: newWallet.address, + from: wallet.address, + op: Opcodes.auth_signed_internal, + aborted: false, + outMessagesCount: 1 + }); + // Finally mock messages goes live + assertMockMessage(res.transactions, newWallet.address); + } + ); + }); + it('should be able to send external message', async () => { + const testPayload = BigInt(getRandomInt(0, 100000)); + const testBody = beginCell().storeUint(testPayload, 32).endCell(); + + const res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + wallet: [ + { + type: 'sendMsg', + mode: SendMode.NONE, + outMsg: { + info: { + type: 'external-out', + createdAt: 0, + createdLt: 0n, + dest: new ExternalAddress(testPayload, 32), + src: null + }, + body: testBody + } + } + ] + }); + + const txSuccess = findTransactionRequired(res.transactions, { + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: false + }); + + expect(txSuccess.externals.length).toBe(1); + + const extOut = txSuccess.externals[0]; + + expect(extOut.info.dest!.value).toBe(testPayload); + expect(extOut.body).toEqualCell(testBody); + }); + it('should bounce set_code action', async () => { + await testSetCode( + async args => { + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_extension, + outMessagesCount: 1, // bounce + aborted: true, + success: false, // No commit anymore + exitCode: 9 + }); + expect(await wallet.getSeqno()).toEqual(Number(args.seqno)); // On internal seqno is not commited + }, + async args => { + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: false, + outMessagesCount: 254, + exitCode: 0 + }); + } + ); + }); + it('should be able to add extension', async () => { + const randomExtAddres = randomAddress(); + await testAddExt(async args => { + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + outMessagesCount: 0, + aborted: false, + exitCode: 0 + }); + }, randomExtAddres); + }); + it('should not be able to install already installed extendsion', async () => { + await testAddExtAlreadyIn(async args => { + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: true, + outMessagesCount: 1, + exitCode: ErrorsV5.add_extension + }); + }); + }); + it('should not be able to install extension from different chain', async () => { + await loadFrom(hasMcWallet); + await testAddExtWrongChain( + async args => { + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions, + toNano('1') + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + outMessagesCount: 1, + aborted: true, + exitCode: ErrorsV5.extension_wrong_workchain + }); + }, + async args => { + const reqMsg = WalletV5Test.requestMessage( + true, + args.walletId, + args.valid_until, + args.seqno, + args.actions, + args.key + ); + const testMsg = internal_relaxed({ + to: newWallet.address, + value: toNano('2'), + body: reqMsg + }); + + const res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + wallet: [ + { + type: 'sendMsg', + mode: SendMode.PAY_GAS_SEPARATELY, + outMsg: testMsg + } + ] + }); + // So via extension we've sent signed add_extension message + // through our wallet to the masterchain wallet + // And it should end up being installed + expect(res.transactions).toHaveTransaction({ + on: newWallet.address, + from: wallet.address, + op: Opcodes.auth_signed_internal, + outMessagesCount: 0, + aborted: false + }); + } + ); + }); + it('should be able to remove extension', async () => { + await testRemoveExt(async args => { + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions, + toNano('1') + ); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + outMessagesCount: 0, + aborted: false + }); + }); + }); + it('should throw on removing non-existent extension', async () => { + await testRemoveExtNonExistent(async args => { + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + outMessagesCount: 1, + aborted: true, + exitCode: ErrorsV5.remove_extension + }); + }); + }); + it('should be able to add/remove extensions and send messages in one go', async () => { + await testAddRemoveSend(async args => { + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + args.actions, + toNano('1') + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: false, + outMessagesCount: 255 + }); + }); + }); + }); + describe('Malformed action list', () => { + it('action list exceeding 255 elements should be rejected', async () => { + await loadFrom(hasExtension); + let seqNo = await wallet.getSeqno(); + let tooMuch = someMessages(256); + const extReq = WalletV5Test.requestMessage( + false, + walletId, + curTime() + 100, + seqNo, + { wallet: tooMuch }, + keys.secretKey + ); + + let res = await wallet.sendExternalSignedMessage(extReq); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + outMessagesCount: 0, + exitCode: ErrorsV5.invalid_c5 + }); + expect(await wallet.getSeqno()).toEqual(++seqNo); + + const intReq = WalletV5Test.requestMessage( + true, + walletId, + curTime() + 100, + seqNo, + { wallet: tooMuch }, + keys.secretKey + ); + res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: intReq + }); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed_internal, + aborted: true, + outMessagesCount: 1, + exitCode: ErrorsV5.invalid_c5 + }); + + res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + { wallet: tooMuch }, + toNano('1') + ); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_extension, + aborted: true, + outMessagesCount: 1, + exitCode: ErrorsV5.invalid_c5 + }); + }); + it('should reject action list with extra data/refs', async () => { + let seqNo = await wallet.getSeqno(); + const testActionRaw = beginCell().store( + storeOutAction({ + type: 'sendMsg', + mode: SendMode.PAY_GAS_SEPARATELY, + outMsg: mockMessage.message + }) + ); + + const ds = testActionRaw.asSlice(); + ds.loadRef(); // Drop one + const noRef = beginCell().storeSlice(ds).endCell(); + const excessiveData = beginCell() + .storeSlice(testActionRaw.asSlice()) + .storeBit(true) + .endCell(); + const truncated = beginCell() + .storeBits(testActionRaw.asSlice().loadBits(testActionRaw.bits - 1)) + .endCell(); + const extraRef = beginCell() + .storeSlice(testActionRaw.asSlice()) + .storeRef(beginCell().storeUint(0x0ec3c86d, 32).endCell()) + .endCell(); + + const origActions = beginCell() + .storeRef(beginCell().endCell()) + .storeSlice(testActionRaw.asSlice()) + .endCell(); + + for (let payload of [excessiveData, truncated, extraRef, noRef]) { + const actionList = beginCell() + .storeRef(beginCell().endCell()) + .storeSlice(payload.asSlice()) + .endCell(); + + const intReq = WalletV5Test.requestMessage( + true, + walletId, + curTime() + 100, + seqNo, + { wallet: actionList }, + keys.secretKey + ); + let res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: intReq + }); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed_internal, + aborted: true, + outMessagesCount: 1, + exitCode: ErrorsV5.invalid_c5 + }); + + const extReq = WalletV5Test.requestMessage( + false, + walletId, + curTime() + 100, + seqNo, + { wallet: actionList }, + keys.secretKey + ); + res = await wallet.sendExternalSignedMessage(extReq); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_signed, + aborted: false, + outMessagesCount: 0, + exitCode: ErrorsV5.invalid_c5 + }); + expect(await wallet.getSeqno()).toEqual(++seqNo); + + res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + { wallet: actionList }, + toNano('1') + ); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + op: Opcodes.auth_extension, + aborted: true, + outMessagesCount: 1, + exitCode: ErrorsV5.invalid_c5 + }); + } + + const intReq = WalletV5Test.requestMessage( + true, + walletId, + curTime() + 100, + seqNo, + { wallet: origActions }, + keys.secretKey + ); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: intReq + }); + assertMockMessage(res.transactions); + }); + }); + }); + describe('Signature auth', () => { + type OwnerArguments = { walletId: bigint; seqno: number | bigint; key: Buffer }; + let signatureDisabled: BlockchainSnapshot; + let signatureEnabled: BlockchainSnapshot; + let multipleExtensions: BlockchainSnapshot; + /* + let testRemoveExtension: (exp: number, + reqType:RequestType, + extension: Address, + via: Sender, commonArgs: OwnerArguments) => Promise; + */ + beforeAll(async () => { + await loadFrom(hasExtension); + }); + + it('extension should be able to set signature mode', async () => { + const seqNo = await wallet.getSeqno(); + const allowedBefore = await wallet.getIsSignatureAuthAllowed(); + expect(allowedBefore).toBe(-1); + signatureEnabled = blockchain.snapshot(); + + let res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + extended: [ + { + type: 'sig_auth', + allowed: false + } + ] + }); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: false + }); + expect(await wallet.getIsSignatureAuthAllowed()).toBe(0); + expect(await wallet.getSeqno()).toEqual(seqNo); + signatureDisabled = blockchain.snapshot(); + + res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + extended: [ + { + type: 'sig_auth', + allowed: true + } + ] + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: false + }); + expect(await wallet.getIsSignatureAuthAllowed()).toBe(-1); + expect(await wallet.getSeqno()).toEqual(seqNo); + signatureEnabled = blockchain.snapshot(); // Usefull? + }); + it('should reject atempt to change sig auth via internal/external request', async () => { + let seqNo = await wallet.getSeqno(); + + const disableSigAuth: ExtendedAction = { + type: 'sig_auth', + allowed: false + }; + const enableSigAuth: ExtendedAction = { + type: 'sig_auth', + allowed: true + }; + + const mockExtensions = someExtensions(100, 'add_extension'); + const randIdx = getRandomInt(0, mockExtensions.length - 2); + + /* + const msgInt = WalletV5Test.requestMessage(true, walletId, curTime() + 100, seqNo, testWalletAction, keys.secretKey); + const msgExt = WalletV5Test.requestMessage(false, walletId, curTime() + 100, seqNo, testWalletAction, keys.secretKey); + */ + + //const res = await wallet.sendExtensionActions(testExtensionBc.getSender(), testWalletActions); + + // const fromInt = async (message) => await wallet.sendInternalSignedMessage(owner.getSender(), {value: toNano('1'), body: message}); + // const fromExt = async (message) => await wallet.sendExternalSignedMessage(message); + + for (let action of [disableSigAuth, enableSigAuth]) { + const actionSingle = [action]; + const actionFirst = [action, ...mockExtensions]; + const actionLast = [...mockExtensions, action]; + const actionRandom = [...mockExtensions]; + actionRandom[randIdx] = action; + + for (let actionSet of [actionSingle, actionFirst, actionLast, actionRandom]) { + const msgInt = WalletV5Test.requestMessage( + true, + walletId, + curTime() + 100, + seqNo, + { extended: actionSet }, + keys.secretKey + ); + const msgExt = WalletV5Test.requestMessage( + false, + walletId, + curTime() + 100, + seqNo, + { extended: actionSet }, + keys.secretKey + ); + // Meh, kinda much + for (let testMsg of [msgInt, msgExt]) { + if (testMsg == msgInt) { + const stateBefore = await getWalletData(); + const res = await wallet.sendInternalSignedMessage(owner.getSender(), { + value: toNano('1'), + body: msgInt + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + aborted: true, + exitCode: ErrorsV5.only_extension_can_change_signature_mode + }); + expect(await getWalletData()).toEqualCell(stateBefore); + } else { + const res = await wallet.sendExternalSignedMessage(msgExt); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + aborted: false, + exitCode: ErrorsV5.only_extension_can_change_signature_mode + }); + expect(await wallet.getSeqno()).toEqual(++seqNo); + } + } + } + } + }); + it('should reject sig auth if mode is already set', async () => { + let i = 0; + for (let testState of [signatureDisabled, signatureEnabled]) { + await loadFrom(testState); + let stateBefore = await getWalletData(); + let res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + extended: [ + { + type: 'sig_auth', + allowed: Boolean(i++) + } + ] + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: true, + exitCode: ErrorsV5.this_signature_mode_already_set + }); + expect(await getWalletData()).toEqualCell(stateBefore); + } + }); + it('should not accept signed external when signature auth is disabled and extension present', async () => { + await loadFrom(signatureDisabled); + const seqNo = await wallet.getSeqno(); + await shouldRejectWith( + wallet.sendMessagesExternal(walletId, curTime() + 100, seqNo, keys.secretKey, [ + mockMessage + ]), + ErrorsV5.signature_disabled + ); + expect(await wallet.getSeqno()).toEqual(seqNo); + }); + it('should not accept signed internal when signature auth is disabled and exension is present', async () => { + await loadFrom(signatureDisabled); + const seqNo = await wallet.getSeqno(); + await assertSendMessages( + ErrorsV5.signature_disabled, + walletId, + curTime() + 100, + seqNo, + [mockMessage], + keys.secretKey, + owner.getSender() + ); + }); + it('extension should be able to add another extension when sig auth is disabled', async () => { + await loadFrom(signatureDisabled); + const testExtAddr = randomAddress(); + const stateBefore = await getWalletData(); + const extBefore = await wallet.getExtensionsArray(); + expect(extBefore.length).toBe(1); + + const res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + extended: [{ type: 'add_extension', address: testExtAddr }] + }); + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: false + }); + + const extAfter = await wallet.getExtensionsArray(); + expect(extAfter.length).toBe(2); + expect(extAfter.findIndex(a => a.equals(testExtAddr))).toBeGreaterThanOrEqual(0); + + multipleExtensions = blockchain.snapshot(); + }); + it('should not allow to remove last extension when sig auth is disabled', async () => { + await loadFrom(signatureDisabled); + + const stateBefore = await getWalletData(); + const extBefore = await wallet.getExtensionsArray(); + expect(extBefore.length).toBe(1); + + let res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + extended: [ + { + type: 'remove_extension', + address: extBefore[0] + } + ] + }); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: true, + exitCode: ErrorsV5.remove_last_extension_when_signature_disabled + }); + expect(await getWalletData()).toEqualCell(stateBefore); + }); + it('should remove extension if sig auth disabled and at lease one left', async () => { + await loadFrom(multipleExtensions); + + const extBefore = await wallet.getExtensionsArray(); + expect(extBefore.length).toBeGreaterThan(1); + + const pickExt = extBefore[getRandomInt(0, extBefore.length - 1)]; + + const res = await wallet.sendExtensionActions(testExtensionBc.getSender(), { + extended: [{ type: 'remove_extension', address: pickExt }] + }); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + from: testExtensionBc.address, + op: Opcodes.auth_extension, + aborted: false + }); + + const extAfter = await wallet.getExtensionsArray(); + expect(extAfter.length).toBe(extBefore.length - 1); + expect(extAfter.findIndex(a => a.equals(pickExt))).toBe(-1); + }); + it('should not allow to remove last extension and then disable sig auth', async () => { + await loadFrom(signatureEnabled); + const stateBefore = await getWalletData(); + const testWalletActions: WalletActions = { + extended: [ + { type: 'remove_extension', address: testExtensionBc.address }, + { type: 'sig_auth', allowed: false } + ] + }; + + const res = await wallet.sendExtensionActions( + testExtensionBc.getSender(), + testWalletActions + ); + + expect(res.transactions).toHaveTransaction({ + on: wallet.address, + aborted: true, + exitCode: ErrorsV5.disable_signature_when_extensions_is_empty + }); + + expect(await getWalletData()).toEqualCell(stateBefore); + }); + }); +}); From 6220bd65a5dc370f6daaebc33ad6262541fcf076 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 3 Jul 2024 13:45:10 +0400 Subject: [PATCH 64/67] rebuild --- build/wallet_v5.compiled.json | 2 +- jest.config.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index f59c6d4..da9c222 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hex":"b5ee9c724102140100028c000114ff00f4a413f4bcf2c80b0102012004020102f203012020d70b1f82107369676ebaf2e08a7f700f0201480e0502012007060019be5f0f6a2684080a0eb90fa02c0201200b080201480a090011b262fb513435c280200017b325fb51341c75c875c2c7e002016e0d0c0019af1df6a2684010eb90eb858fc00019adce76a2684020eb90eb85ffc002f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810282b99130e07070e2100f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a1312110010935bdb31e1d74cd0007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de2009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed545bfe7180"} \ No newline at end of file +{"hash":"8122d374ce05988e668315db78ea4f97973d5b2147f2883c555420f79de71f64","hashBase64":"gSLTdM4FmI5mgxXbeOpPl5c9WyFH8og8VVQg953nH2Q=","hex":"b5ee9c724102140100028c000114ff00f4a413f4bcf2c80b01020120020d020148030402f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810282b99130e07070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e012020d70b1f82107369676ebaf2e08a7f700f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0eae2b2de"} \ No newline at end of file diff --git a/jest.config.ts b/jest.config.ts index 884cddc..9075ecd 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -4,6 +4,7 @@ const config: Config = { preset: 'ts-jest', testEnvironment: 'node', testPathIgnorePatterns: ['/node_modules/', '/dist/'], + testTimeout: 15000 }; export default config; From 2f962c360c2f7fc7eb4a53d691c64280a1cc7dfc Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 3 Jul 2024 13:54:15 +0400 Subject: [PATCH 65/67] optimize bounced check --- contracts/wallet_v5.fc | 9 +- tests/WalletW5.spec.ts | 190 ++++++++++++++++++++--------------------- 2 files changed, 98 insertions(+), 101 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 91cf366..f5ede4d 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -226,16 +226,13 @@ cell verify_c5_actions(cell c5, int is_external) inline { return (); } - slice in_msg_full_slice = in_msg_full.begin_parse(); - slice message_flags_slice = in_msg_full_slice~load_bits(size::message_flags); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - ;; skip bounced messages - if bounced flag (last bit) is set amount of trailing ones will be non-zero, else it will be zero. - if (count_trailing_ones(message_flags_slice) > 0) { - return (); - } + ;; bounded messages has 0xffffff prefix and skipped by op check if (op == prefix::extension_action) { in_msg_body~skip_bits(size::message_operation_prefix); + slice in_msg_full_slice = in_msg_full.begin_parse(); + in_msg_full_slice~skip_bits(size::message_flags); ;; Authenticate extension by its address. (int sender_address_wc, int sender_address_hash) = parse_std_addr(in_msg_full_slice~load_msg_addr()); (int my_address_wc, _) = parse_std_addr(my_address()); diff --git a/tests/WalletW5.spec.ts b/tests/WalletW5.spec.ts index 4307a98..263fd85 100644 --- a/tests/WalletW5.spec.ts +++ b/tests/WalletW5.spec.ts @@ -1905,101 +1905,101 @@ describe('Wallet v5 external tests', () => { expect(await wallet.getSeqno()).toEqual(Number(args.seqno) + 1); }); }); - describe('Bounce', () => { - it('should ignore bounced mesages', async () => { - await loadFrom(hasExtension); - const seqNo = await wallet.getSeqno(); - const mockActions: WalletActions = { wallet: [message2action(mockMessage)] }; - - // Note that in reality bounce gets prefixed by 0xFFFFFFFF - // With current code, that would mean message would be ignored - // due to op check - // However we still could test as if TVM doesn't add prefix to bounce somehow - const intReq = WalletV5Test.requestMessage( - true, - walletId, - curTime() + 1000, - seqNo, - mockActions, - keys.secretKey - ); - const extReq = WalletV5Test.extensionMessage(mockActions); - // ihr_disable and bounce flags combinations - let flagFuzz = [ - [false, false], - [true, false], - [false, true], - [true, true] - ]; - - const stateBefore = await getWalletData(); - - for (let flags of flagFuzz) { - let res = await blockchain.sendMessage( - internal({ - from: owner.address, - to: wallet.address, - body: intReq, - value: toNano('1'), - bounced: true, - ihrDisabled: flags[0], - bounce: flags[1] - }) - ); - expect(res.transactions).toHaveTransaction({ - on: wallet.address, - op: Opcodes.auth_signed_internal, - aborted: false, - outMessagesCount: 0 - }); - expect(await getWalletData()).toEqualCell(stateBefore); - - res = await blockchain.sendMessage( - internal({ - from: testExtensionBc.address, - to: wallet.address, - body: extReq, - value: toNano('1'), - bounced: true, - ihrDisabled: flags[0], - bounce: flags[1] - }) - ); - expect(res.transactions).toHaveTransaction({ - on: wallet.address, - op: Opcodes.auth_extension, - aborted: false, - outMessagesCount: 0 - }); - expect(await getWalletData()).toEqualCell(stateBefore); - } - - // Let's proove that bounce flag is the reason - const resInt = await blockchain.sendMessage( - internal({ - from: owner.address, - to: wallet.address, - body: intReq, - value: toNano('1'), - ihrDisabled: true, - bounce: true - }) - ); - assertMockMessage(resInt.transactions); - - const resExt = await blockchain.sendMessage( - internal({ - from: testExtensionBc.address, - to: wallet.address, - body: extReq, - value: toNano('1'), - ihrDisabled: false, - bounce: false - }) - ); - assertMockMessage(resExt.transactions); - }); - }); + // describe('Bounce', () => { + // it('should ignore bounced mesages', async () => { + // await loadFrom(hasExtension); + // const seqNo = await wallet.getSeqno(); + // const mockActions: WalletActions = { wallet: [message2action(mockMessage)] }; + // + // // Note that in reality bounce gets prefixed by 0xFFFFFFFF + // // With current code, that would mean message would be ignored + // // due to op check + // // However we still could test as if TVM doesn't add prefix to bounce somehow + // const intReq = WalletV5Test.requestMessage( + // true, + // walletId, + // curTime() + 1000, + // seqNo, + // mockActions, + // keys.secretKey + // ); + // const extReq = WalletV5Test.extensionMessage(mockActions); + // // ihr_disable and bounce flags combinations + // let flagFuzz = [ + // [false, false], + // [true, false], + // [false, true], + // [true, true] + // ]; + // + // const stateBefore = await getWalletData(); + // + // for (let flags of flagFuzz) { + // let res = await blockchain.sendMessage( + // internal({ + // from: owner.address, + // to: wallet.address, + // body: intReq, + // value: toNano('1'), + // bounced: true, + // ihrDisabled: flags[0], + // bounce: flags[1] + // }) + // ); + // expect(res.transactions).toHaveTransaction({ + // on: wallet.address, + // op: Opcodes.auth_signed_internal, + // aborted: false, + // outMessagesCount: 0 + // }); + // expect(await getWalletData()).toEqualCell(stateBefore); + // + // res = await blockchain.sendMessage( + // internal({ + // from: testExtensionBc.address, + // to: wallet.address, + // body: extReq, + // value: toNano('1'), + // bounced: true, + // ihrDisabled: flags[0], + // bounce: flags[1] + // }) + // ); + // expect(res.transactions).toHaveTransaction({ + // on: wallet.address, + // op: Opcodes.auth_extension, + // aborted: false, + // outMessagesCount: 0 + // }); + // expect(await getWalletData()).toEqualCell(stateBefore); + // } + // + // // Let's proove that bounce flag is the reason + // const resInt = await blockchain.sendMessage( + // internal({ + // from: owner.address, + // to: wallet.address, + // body: intReq, + // value: toNano('1'), + // ihrDisabled: true, + // bounce: true + // }) + // ); + // assertMockMessage(resInt.transactions); + // + // const resExt = await blockchain.sendMessage( + // internal({ + // from: testExtensionBc.address, + // to: wallet.address, + // body: extReq, + // value: toNano('1'), + // ihrDisabled: false, + // bounce: false + // }) + // ); + // assertMockMessage(resExt.transactions); + // }); + // }); }); describe('Extension', () => { let actionFuzz: WalletActions[]; From b66b1031e0977872d245b2ae697e9f6c4efb8cd4 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 3 Jul 2024 13:56:53 +0400 Subject: [PATCH 66/67] more accurate check --- build/wallet_v5.compiled.json | 2 +- contracts/wallet_v5.fc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/wallet_v5.compiled.json b/build/wallet_v5.compiled.json index da9c222..d76e9c0 100644 --- a/build/wallet_v5.compiled.json +++ b/build/wallet_v5.compiled.json @@ -1 +1 @@ -{"hash":"8122d374ce05988e668315db78ea4f97973d5b2147f2883c555420f79de71f64","hashBase64":"gSLTdM4FmI5mgxXbeOpPl5c9WyFH8og8VVQg953nH2Q=","hex":"b5ee9c724102140100028c000114ff00f4a413f4bcf2c80b01020120020d020148030402f2d020d749c120915b8f6e20d70b1f2082106578746ebd21821073696e74bdb0925f03e002d0d60301c713c200925f03e00282106578746eba8eb08020d72101fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810282b99130e07070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e012020d70b1f82107369676ebaf2e08a7f700f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd0eae2b2de"} \ No newline at end of file +{"hash":"8b59a99853b0a3c983a6a4a3a695805892813736027e7a765bf6ce5b17b0b0d7","hashBase64":"i1mpmFOwo8mDpqSjppWAWJKBNzYCfnp2W/bOWxewsNc=","hex":"b5ee9c7241021401000282000114ff00f4a413f4bcf2c80b01020120020d020148030402ded020d749c120915b8f6420d70b1f2082106578746ebd21821073696e74bdb0925f03e082106578746eba8eb48020d72101d074d721fa4030fa44f828fa443058bd915be0ed44d0810141d721f4058307f40e6fa1319130e18040d721707fdb3ce03120d749810280b99130e07070e2100f020120050c020120060902016e07080019adce76a2684020eb90eb85ffc00019af1df6a2684010eb90eb858fc00201480a0b0017b325fb51341c75c875c2c7e00011b262fb513435c280200019be5f0f6a2684080a0eb90fa02c0102f20e012020d70b1f82107369676ebaf2e08a7f700f01e48eefeda2edfb228308d722038308d723208020d721d31fd31fd31fed44d0d200d31f20d31fd3ffd70a000af90140ddf9109a29945f0bdb31e1f2c087df02b35007b0f2d0845125baf2e0855037baf2e086f823bbf2d0882392f800de01a47fc8ca00cb1f01cf16c9ed542192f80fdedb3cd81003f6eda2edfb02f404216e926c218e4c0221d73930709421c700b38e2d01d72820761e436c20d749c008f2e09320d74ac002f2e09320d71d06c712c2005230b0f2d089d74cd7393001a4e86c128407bbf2e093d74ac000f2e093ed55e2d20001c000915be0ebd72c08142091709601d72c081c12e25210b1e30f20d74a111213009601fa4001fa44f828fa443058baf2e091ed44d0810141d718f405049d7fc8ca0040048307f453f2e08b8e14038307f45bf2e08c22d70a00216e01b3b0f2d090e2c85003cf1612f400c9ed54007230d72c08248e2d21f2e092d200ed44d0d2005113baf2d08f54503091319c01810140d721d70a00f2e08ee2c8ca0058cf16c9ed5493f2c08de20010935bdb31e1d74cd09084a9e7"} \ No newline at end of file diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index f5ede4d..650f3ef 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -259,8 +259,8 @@ cell verify_c5_actions(cell c5, int is_external) inline { } - ;; Additional check to make sure that there are enough bits for reading (+2 for maybe c5 and maybe other actions bits) - if (in_msg_body.slice_bits() < size::message_operation_prefix + size::wallet_id + size::valid_until + size::seqno + 2 + size::signature) { + ;; Additional check to make sure that there are enough bits for reading before signature check + if (in_msg_body.slice_bits() < size::message_operation_prefix + size::wallet_id + size::valid_until + size::seqno + size::signature) { return (); } process_signed_request(in_msg_body, false, false); From 62bfd99f1cd2a709053641a470d44ab1157bb620 Mon Sep 17 00:00:00 2001 From: tolya-yanot Date: Wed, 3 Jul 2024 14:03:03 +0400 Subject: [PATCH 67/67] delete unused code --- contracts/wallet_v5.fc | 2 -- 1 file changed, 2 deletions(-) diff --git a/contracts/wallet_v5.fc b/contracts/wallet_v5.fc index 650f3ef..28cf307 100644 --- a/contracts/wallet_v5.fc +++ b/contracts/wallet_v5.fc @@ -40,8 +40,6 @@ const int prefix::extension_action = 0x6578746E; ;;; returns the number of trailing zeroes in slice s. int count_trailing_zeroes(slice s) asm "SDCNTTRAIL0"; -;;; returns the number of trailing ones in slice s. -int count_trailing_ones(slice s) asm "SDCNTTRAIL1"; ;;; returns the last 0 ≤ l ≤ 1023 bits of s. slice get_last_bits(slice s, int l) asm "SDCUTLAST";