Skip to content

Commit

Permalink
Updated actions, allow naming, empty ref check, docs, actions wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
Skydev0h committed Jan 27, 2024
1 parent f2601ae commit 9545120
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 87 deletions.
8 changes: 4 additions & 4 deletions Specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,11 @@ You need to put two requests in your message body:

Yes. We have considered constant-size schemes where the wallet only stores trusted extension code. However, extension authentication becomes combursome and expensive: plugin needs to transmit additional data and each request needs to recompute plugin’s address. We estimate that for the reasonably sized wallets (less than 100 plugins) authentication via the dictionary lookup would not exceed costs of indirect address authentication.

### Why it can be useful to disable signature authentication mode?
### Why it can be useful to disallow authentication with signature?

Ability to disable authentication by signature enables two related use-cases:
Ability to disallow authentication with signature enables two related use-cases:

1. Two-factor authentication schemes: where control over wallet is fully delegated to an extension that checks two signatures: the user’s one and the signature from the auth service. Naturally, if the signature authentication in the wallet remains enabled, the second factor check is bypassed.
1. Two-factor authentication schemes: where control over wallet is fully delegated to an extension that checks two signatures: the user’s one and the signature from the auth service. Naturally, if the signature authentication in the wallet remains allowed, the second factor check is bypassed.

2. Account recovery: delegating full control to another wallet in case of key compromise or loss. Wallet may contain larger amount of assets and its address could be tied to long-term contracts, therefore delegation to another controlling account is preferred to simply transferring the assets.

Expand Down Expand Up @@ -157,7 +157,7 @@ action_list_extended$1 {m:#} {n:#} action:ExtendedAction prev:^(ActionList n m)
action_add_ext#1c40db9f addr:MsgAddressInt = ExtendedAction;
action_delete_ext#5eaef4a4 addr:MsgAddressInt = ExtendedAction;
action_set_public_key_enabled#20cbb95a enabled:(## 1) = ExtendedAction;
action_set_signature_auth_allowed#20cbb95a allowed:(## 1) = ExtendedAction;
```

Authentication modes:
Expand Down
34 changes: 26 additions & 8 deletions contracts/wallet_v5.fc
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const int size::flags = 4;
(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_public_key_enabled_prefix(slice body) impure asm "x{20cbb95a} 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";

Expand All @@ -50,7 +50,15 @@ int pack_address((int, int) address) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)
return ();
}
-}
() set_actions_if_simple(slice cs) impure asm "x{4_} SDBEGINSQ" "IFJMP:<{ PLDREF verify_actions INLINECALLDICT c5 POP }>" "DROP";
() set_actions_if_simple(slice cs) impure asm """
x{4_} SDBEGINSQ
IFJMP:<{
PLDREF
verify_actions INLINECALLDICT
c5 POP
}>
DROP
""";

cell verify_actions(cell c5) inline {
;; Comment out code starting from here to disable checks (unsafe version)
Expand Down Expand Up @@ -104,20 +112,25 @@ cell verify_actions(cell c5) inline {
.store_dict(extensions)
.end_cell());
}
elseif (cs~check_and_remove_set_public_key_enabled_prefix()) {
var enable = cs~load_uint(1);
elseif (cs~check_and_remove_set_signature_auth_allowed_prefix()) {
var allow = cs~load_uint(1);
var ds = get_data().begin_parse();
var stored_seqno = ds~load_int(size::stored_seqno);
var immutable_tail = ds; ;; stored_subwallet ~ public_key ~ extensions
if (enable) {
if (allow) {
;; allow
if (stored_seqno < 0) {
;; Can't be disabled with 0 because disabling increments seqno
;; -123 -> 123 -> 124
stored_seqno = - stored_seqno;
stored_seqno = stored_seqno + 1;
}
} else {
;; disallow
if (stored_seqno >= 0) {
ds = ds.skip_bits(size::stored_seqno + 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;
Expand Down Expand Up @@ -245,9 +258,13 @@ cell verify_actions(cell c5) inline {

() recv_internal(cell full_msg, slice body) impure inline {

__install_root_cell_repacker_hook__(); ;; this function from root_cell_repacker can be called in any method
;; this function from root_cell_repacker can be called in any method
;; no opcodes are added by this function to recv_internal directly
__install_root_cell_repacker_hook__();

return_if(body.slice_empty?()); ;; return right away if the body is empty (simple transfer)
;; 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?());

;; 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();
Expand Down Expand Up @@ -294,6 +311,7 @@ cell verify_actions(cell c5) inline {
;; Get methods

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));
}

Expand All @@ -313,6 +331,6 @@ cell get_extensions() method_id {
return ds~load_dict();
}

int is_public_key_enabled() method_id {
int get_is_signature_auth_allowed() method_id {
return get_data().begin_parse().preload_int(size::stored_seqno) >= 0;
}
96 changes: 23 additions & 73 deletions tests/actions.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
import {
Address,
beginCell,
Cell,
CurrencyCollection,
MessageRelaxed,
SendMode,
storeMessageRelaxed,
storeCurrencyCollection
} from 'ton-core';

export type LibRef = Cell | bigint;
import { Address, beginCell, Cell, MessageRelaxed, SendMode, storeMessageRelaxed } from 'ton-core';

export class ActionSendMsg {
public static readonly tag = 0x0ec3c86d;
Expand All @@ -27,63 +16,6 @@ export class ActionSendMsg {
}
}

export class ActionSetCode {
public static readonly tag = 0xad4de08e;

public readonly tag = ActionSetCode.tag;

constructor(public readonly newCode: Cell) {}

public serialize(): Cell {
return beginCell().storeUint(this.tag, 32).storeRef(this.newCode).endCell();
}
}

export class ActionReserveCurrency {
public static readonly tag = 0x36e6b809;

public readonly tag = ActionReserveCurrency.tag;

constructor(public readonly mode: SendMode, public readonly currency: CurrencyCollection) {}

public serialize(): Cell {
return beginCell()
.storeUint(this.tag, 32)
.storeUint(this.mode, 8)
.store(storeCurrencyCollection(this.currency))
.endCell();
}
}

export class ActionChangeLibrary {
public static readonly tag = 0x26fa1dd4;

public readonly tag = ActionChangeLibrary.tag;

constructor(public readonly mode: number, public readonly libRef: LibRef) {}

public serialize(): Cell {
const cell = beginCell().storeUint(this.tag, 32).storeUint(this.mode, 7);
if (typeof this.libRef === 'bigint') {
return cell.storeUint(0, 1).storeUint(this.libRef, 256).endCell();
}

return cell.storeUint(1, 1).storeRef(this.libRef).endCell();
}
}

export class ActionSetData {
public static readonly tag = 0x1ff8ea0b;

public readonly tag = ActionSetData.tag;

constructor(public readonly data: Cell) {}

public serialize(): Cell {
return beginCell().storeUint(this.tag, 32).storeRef(this.data).endCell();
}
}

export class ActionAddExtension {
public static readonly tag = 0x1c40db9f;

Expand All @@ -108,14 +40,32 @@ export class ActionRemoveExtension {
}
}

export type OutAction = ActionSendMsg | ActionSetCode | ActionReserveCurrency | ActionChangeLibrary;
export type ExtendedAction = ActionSetData | ActionAddExtension | ActionRemoveExtension;
export class ActionSetSignatureAuthAllowed {
public static readonly tag = 0x20cbb95a;

public readonly tag = ActionSetSignatureAuthAllowed.tag;

constructor(public readonly allowed: Boolean) {}

public serialize(): Cell {
return beginCell()
.storeUint(this.tag, 32)
.storeUint(this.allowed ? 1 : 0, 1)
.endCell();
}
}

export type OutAction = ActionSendMsg;
export type ExtendedAction =
| ActionAddExtension
| ActionRemoveExtension
| ActionSetSignatureAuthAllowed;

export function isExtendedAction(action: OutAction | ExtendedAction): action is ExtendedAction {
return (
action.tag === ActionSetData.tag ||
action.tag === ActionAddExtension.tag ||
action.tag === ActionRemoveExtension.tag
action.tag === ActionRemoveExtension.tag ||
action.tag === ActionSetSignatureAuthAllowed.tag
);
}

Expand Down
2 changes: 1 addition & 1 deletion types.tlb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ action_list_extended$1 {m:#} {n:#} action:ExtendedAction prev:^(ActionList n m)

action_add_ext#1c40db9f addr:MsgAddressInt = ExtendedAction;
action_delete_ext#5eaef4a4 addr:MsgAddressInt = ExtendedAction;
action_set_public_key_enabled#20cbb95a enabled:(## 1) = ExtendedAction;
action_set_signature_auth_allowed#20cbb95a allowed:(## 1) = ExtendedAction;

signed_request$_
signature: bits512 // 512
Expand Down
2 changes: 1 addition & 1 deletion wrappers/wallet-v5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const Opcodes = {
action_extended_set_data: 0x1ff8ea0b,
action_extended_add_extension: 0x1c40db9f,
action_extended_remove_extension: 0x5eaef4a4,
action_extended_set_public_key_enabled: 0x20cbb95a,
action_extended_set_signature_auth_allowed: 0x20cbb95a,
auth_extension: 0x6578746e,
auth_signed: 0x7369676e,
auth_signed_internal: 0x73696e74
Expand Down

0 comments on commit 9545120

Please sign in to comment.