Skip to content

Commit

Permalink
Merge pull request #2 from tonkeeper/fix/update-wallet_id
Browse files Browse the repository at this point in the history
fix: subwallet id replaced with wallet id
  • Loading branch information
siandreev authored Sep 19, 2023
2 parents ffc03d4 + 56ca085 commit bcf55b7
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 59 deletions.
9 changes: 7 additions & 2 deletions Specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ As of 2023 TON network consists of two workchains: -1 (master) and 0 (base). Thi

Action types:

```tlb
```tl-b
// Standard actions from block.tlb:
out_list_empty$_ = OutList 0;
out_list$_ {n:#} prev:^(OutList n) action:OutAction
Expand All @@ -179,7 +179,7 @@ action_delete_ext#5eaef4a4 addr:MsgAddressInt = ExtendedAction;

Authentication modes:

```tlb
```tl-b
signed_request$_
signature: bits512 // 512
subwallet_id: uint32 // 512+32
Expand All @@ -194,6 +194,11 @@ external_signed#7369676E signed:SignedRequest = ExternalMsgBody;
actions$_ {m:#} {n:#} actions:(ActionList n m) = InnerRequest;
```

Contract state:
```tl-b
wallet_id$_ global_id:int32 wc:int8 version:(## 8) subwallet_number:(## 32) = WalletID;
contract_state$_ seqno:# wallet_id:WalletID public_key:(## 256) extensions_dict:(HashmapE 256 int8) = ContractState;
```

## Source code

Expand Down
18 changes: 9 additions & 9 deletions contracts/wallet_v5.fc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)

set_data(begin_cell()
.store_uint(stored_seqno, 32)
.store_uint(stored_subwallet, 32)
.store_uint(stored_subwallet, 80)
.store_uint(public_key, 256)
.store_dict(extensions)
.end_cell());
Expand All @@ -62,7 +62,7 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)
() process_signed_request(slice body, int stored_seqno, int stored_subwallet, int public_key, cell extensions) impure {
var signature = body~load_bits(512);
var cs = body;
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(80), cs~load_uint(32), cs~load_uint(32));

throw_if(36, valid_until <= now());
throw_unless(33, msg_seqno == stored_seqno);
Expand All @@ -75,7 +75,7 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)
stored_seqno = stored_seqno + 1;
set_data(begin_cell()
.store_uint(stored_seqno, 32)
.store_uint(stored_subwallet, 32)
.store_uint(stored_subwallet, 80)
.store_uint(public_key, 256)
.store_dict(extensions)
.end_cell());
Expand All @@ -87,7 +87,7 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)

() recv_external(slice body) impure {
var ds = get_data().begin_parse();
var (stored_seqno, stored_subwallet, public_key, extensions) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256), ds~load_dict());
var (stored_seqno, stored_subwallet, public_key, extensions) = (ds~load_uint(32), ds~load_uint(80), ds~load_uint(256), ds~load_dict());
ds.end_parse();
int auth_kind = body~load_uint(32);
if (auth_kind == 0x7369676E) { ;; "sign"
Expand Down Expand Up @@ -121,7 +121,7 @@ int pack_address(int wc, int hash) impure asm "SWAP INC XOR"; ;; hash ^ (wc+1)
}

var ds = get_data().begin_parse();
var (stored_seqno, stored_subwallet, public_key, extensions) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256), ds~load_dict());
var (stored_seqno, stored_subwallet, public_key, extensions) = (ds~load_uint(32), ds~load_uint(80), ds~load_uint(256), ds~load_dict());
ds.end_parse();

if (auth_kind == 0x6578746E) { ;; "extn"
Expand All @@ -148,18 +148,18 @@ int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}

int get_subwallet_id() method_id {
return get_data().begin_parse().skip_bits(32).preload_uint(32);
int get_wallet_id() method_id {
return get_data().begin_parse().skip_bits(32).preload_uint(80);
}

int get_public_key() method_id {
var cs = get_data().begin_parse().skip_bits(64);
var cs = get_data().begin_parse().skip_bits(32 + 80);
return cs.preload_uint(256);
}

;; 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(32 + 32 + 256);
var ds = get_data().begin_parse().skip_bits(32 + 80 + 256);
return ds~load_dict();
}
6 changes: 2 additions & 4 deletions scripts/deployWalletV5.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { Dictionary, toNano } from 'ton-core';
import { WalletV5 } from '../wrappers/wallet-v5';
import { WalletId, WalletV5 } from '../wrappers/wallet-v5';
import { compile, NetworkProvider } from '@ton-community/blueprint';
import { getSecureRandomBytes, keyPairFromSeed } from 'ton-crypto';

const SUBWALLET_ID = 20230823 + 0;

export async function run(provider: NetworkProvider) {
const keypair = keyPairFromSeed(await getSecureRandomBytes(32));
console.log('KEYPAIR PUBKEY', keypair.publicKey.toString('hex'));
Expand All @@ -14,7 +12,7 @@ export async function run(provider: NetworkProvider) {
WalletV5.createFromConfig(
{
seqno: 0,
subwallet: SUBWALLET_ID,
walletId: new WalletId({ networkGlobalId: -3 }).serialized, // testnet
publicKey: keypair.publicKey,
extensions: Dictionary.empty()
},
Expand Down
8 changes: 4 additions & 4 deletions tests/wallet-v5-extensions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Blockchain, SandboxContract } from '@ton-community/sandbox';
import { Address, beginCell, Cell, Dictionary, Sender, SendMode, toNano } from 'ton-core';
import { WalletV5 } from '../wrappers/wallet-v5';
import { 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';
Expand All @@ -14,7 +14,7 @@ import {
import { TransactionDescriptionGeneric } from 'ton-core/src/types/TransactionDescription';
import { TransactionComputeVm } from 'ton-core/src/types/TransactionComputePhase';

const SUBWALLET_ID = 20230823 + 0;
const WALLET_ID = new WalletId({ networkGlobalId: -239, workChain: 0, subwalletNumber: 0 });

describe('Wallet V5 extensions auth', () => {
let code: Cell;
Expand All @@ -31,7 +31,7 @@ describe('Wallet V5 extensions auth', () => {

function createBody(actionsList: Cell) {
const payload = beginCell()
.storeUint(SUBWALLET_ID, 32)
.storeUint(WALLET_ID.serialized, 80)
.storeUint(validUntil(), 32)
.storeUint(seqno, 32) // seqno
.storeSlice(actionsList.beginParse())
Expand All @@ -53,7 +53,7 @@ describe('Wallet V5 extensions auth', () => {
WalletV5.createFromConfig(
{
seqno: 0,
subwallet: SUBWALLET_ID,
walletId: WALLET_ID.serialized,
publicKey: keypair.publicKey,
extensions: Dictionary.empty()
},
Expand Down
26 changes: 13 additions & 13 deletions tests/wallet-v5-external.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Blockchain, SandboxContract } from '@ton-community/sandbox';
import { Address, beginCell, Cell, Dictionary, Sender, SendMode, toNano } from 'ton-core';
import { Opcodes, WalletV5 } from '../wrappers/wallet-v5';
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';
Expand All @@ -23,7 +23,7 @@ import { WalletV4 } from '../wrappers/wallet-v4';
import { TransactionDescriptionGeneric } from 'ton-core/src/types/TransactionDescription';
import { TransactionComputeVm } from 'ton-core/src/types/TransactionComputePhase';

const SUBWALLET_ID = 20230823 + 0;
const WALLET_ID = new WalletId({ networkGlobalId: -239, workChain: -1, subwalletNumber: 0 });

describe('Wallet V5 sign auth external', () => {
let code: Cell;
Expand All @@ -47,7 +47,7 @@ describe('Wallet V5 sign auth external', () => {
WalletV5.createFromConfig(
{
seqno: params?.seqno ?? 0,
subwallet: params?.subwallet ?? SUBWALLET_ID,
walletId: params?.walletId ?? WALLET_ID.serialized,
publicKey: params?.publicKey ?? _keypair.publicKey,
extensions: params?.extensions ?? Dictionary.empty()
},
Expand All @@ -64,7 +64,7 @@ describe('Wallet V5 sign auth external', () => {

function createBody(actionsList: Cell) {
const payload = beginCell()
.storeUint(SUBWALLET_ID, 32)
.storeUint(WALLET_ID.serialized, 80)
.storeUint(validUntil(), 32)
.storeUint(seqno, 32) // seqno
.storeSlice(actionsList.beginParse())
Expand All @@ -86,7 +86,7 @@ describe('Wallet V5 sign auth external', () => {
WalletV5.createFromConfig(
{
seqno: 0,
subwallet: SUBWALLET_ID,
walletId: WALLET_ID.serialized,
publicKey: keypair.publicKey,
extensions: Dictionary.empty()
},
Expand Down Expand Up @@ -539,14 +539,14 @@ describe('Wallet V5 sign auth external', () => {
const vu = validUntil();

const payload = beginCell()
.storeUint(SUBWALLET_ID, 32)
.storeUint(WALLET_ID.serialized, 80)
.storeUint(vu, 32)
.storeUint(seqno, 32) // seqno
.storeSlice(actionsList.beginParse())
.endCell();

const fakePayload = beginCell()
.storeUint(SUBWALLET_ID, 32)
.storeUint(WALLET_ID.serialized, 80)
.storeUint(vu, 32)
.storeUint(seqno + 1, 32) // seqno
.storeSlice(actionsList.beginParse())
Expand Down Expand Up @@ -576,7 +576,7 @@ describe('Wallet V5 sign auth external', () => {
const actionsList = packActionsList([new ActionSendMsg(SendMode.PAY_GAS_SEPARATELY, msg)]);

const payload = beginCell()
.storeUint(SUBWALLET_ID, 32)
.storeUint(WALLET_ID.serialized, 80)
.storeUint(validUntil(), 32)
.storeUint(seqno, 32) // seqno
.storeSlice(actionsList.beginParse())
Expand Down Expand Up @@ -608,7 +608,7 @@ describe('Wallet V5 sign auth external', () => {
const actionsList = packActionsList([new ActionSendMsg(SendMode.PAY_GAS_SEPARATELY, msg)]);

const payload = beginCell()
.storeUint(SUBWALLET_ID, 32)
.storeUint(WALLET_ID.serialized, 80)
.storeUint(validUntil(), 32)
.storeUint(seqno + 1, 32) // seqno
.storeSlice(actionsList.beginParse())
Expand Down Expand Up @@ -638,7 +638,7 @@ describe('Wallet V5 sign auth external', () => {
const actionsList = packActionsList([new ActionSendMsg(SendMode.PAY_GAS_SEPARATELY, msg)]);

const payload = beginCell()
.storeUint(SUBWALLET_ID, 32)
.storeUint(WALLET_ID.serialized, 80)
.storeUint(Math.round(Date.now() / 1000) - 600, 32)
.storeUint(seqno, 32)
.storeSlice(actionsList.beginParse())
Expand All @@ -659,7 +659,7 @@ describe('Wallet V5 sign auth external', () => {
expect(walletBalanceBefore).toEqual(walletBalanceAfter);
});

it('Should fail if subwallet id is wrong', async () => {
it('Should fail if walletId id is wrong', async () => {
const testReceiver = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y');
const forwardValue = toNano(0.001);

Expand All @@ -668,7 +668,7 @@ describe('Wallet V5 sign auth external', () => {
const actionsList = packActionsList([new ActionSendMsg(SendMode.PAY_GAS_SEPARATELY, msg)]);

const payload = beginCell()
.storeUint(SUBWALLET_ID + 1, 32)
.storeUint(new WalletId({ ...WALLET_ID, subwalletNumber: 1 }).serialized, 80)
.storeUint(validUntil(), 32)
.storeUint(seqno, 32)
.storeSlice(actionsList.beginParse())
Expand Down Expand Up @@ -698,7 +698,7 @@ describe('Wallet V5 sign auth external', () => {
const actionsList = packActionsList([new ActionSendMsg(SendMode.PAY_GAS_SEPARATELY, msg)]);

const payload = beginCell()
.storeUint(SUBWALLET_ID + 1, 32)
.storeUint(WALLET_ID.serialized, 80)
.storeUint(validUntil(), 32)
.storeUint(seqno, 32)
.storeSlice(actionsList.beginParse())
Expand Down
68 changes: 60 additions & 8 deletions tests/wallet-v5-get.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Blockchain, SandboxContract } from '@ton-community/sandbox';
import { Address, beginCell, Cell, Dictionary, Sender, toNano } from 'ton-core';
import { WalletV5 } from '../wrappers/wallet-v5';
import { WalletId, WalletV5 } from '../wrappers/wallet-v5';
import '@ton-community/test-utils';
import { compile } from '@ton-community/blueprint';
import { getSecureRandomBytes, KeyPair, keyPairFromSeed } from 'ton-crypto';
import { bufferToBigInt, packAddress } from './utils';

const SUBWALLET_ID = 20230823 + 0;
const WALLET_ID = new WalletId({ networkGlobalId: -239, workChain: 0, subwalletNumber: 0 });

describe('Wallet V5 get methods', () => {
let code: Cell;
Expand All @@ -30,7 +30,7 @@ describe('Wallet V5 get methods', () => {
WalletV5.createFromConfig(
{
seqno: params?.seqno ?? 0,
subwallet: params?.subwallet ?? SUBWALLET_ID,
walletId: params?.walletId ?? WALLET_ID.serialized,
publicKey: params?.publicKey ?? keypair.publicKey,
extensions: params?.extensions ?? Dictionary.empty()
},
Expand Down Expand Up @@ -68,11 +68,39 @@ describe('Wallet V5 get methods', () => {
expect(actualPubkey).toEqual(bufferToBigInt(keypair.publicKey));
});

it('Get subwallet id', async () => {
const expectedSubWalletId = 20230824;
await deploy({ subwallet: expectedSubWalletId });
const actualSubWalletId = await walletV5.getSubWalletID();
expect(expectedSubWalletId).toEqual(actualSubWalletId);
it('Get wallet id', async () => {
const expectedWalletId = new WalletId({
networkGlobalId: -239,
workChain: 0,
subwalletNumber: 1
});
await deploy({ walletId: expectedWalletId.serialized });
const actualWalletId = await walletV5.getWalletId();
expect(expectedWalletId.serialized).toEqual(actualWalletId.serialized);
});

it('Get subwallet number', async () => {
const subwalletNumber = 12345;
const walletId = new WalletId({
networkGlobalId: -239,
workChain: 0,
subwalletNumber
});
await deploy({ walletId: walletId.serialized });
const actualSubwalletNumber = (await walletV5.getWalletId()).subwalletNumber;
expect(subwalletNumber).toEqual(actualSubwalletNumber);
});

it('Default wallet id', async () => {
const walletId = new WalletId({
networkGlobalId: -239,
workChain: 0,
subwalletNumber: 0,
walletVersion: 'v5'
});
const defaultWalletId = new WalletId();

expect(walletId.serialized).toBe(defaultWalletId.serialized);
});

it('Get extensions dict', async () => {
Expand All @@ -94,4 +122,28 @@ describe('Wallet V5 get methods', () => {
.endCell();
expect(actual?.equals(expected)).toBeTruthy();
});

it('Get extensions array', async () => {
const plugin1 = Address.parse('EQAvDfWFG0oYX19jwNDNBBL1rKNT9XfaGP9HyTb5nb2Eml6y');
const plugin2 = Address.parse('Ef82pT4d8T7TyRsjW2BpGpGYga-lMA4JjQb4D2tc1PXMX28X');

const extensions: Dictionary<bigint, bigint> = Dictionary.empty(
Dictionary.Keys.BigUint(256),
Dictionary.Values.BigInt(8)
);
extensions.set(packAddress(plugin1), BigInt(plugin1.workChain));
extensions.set(packAddress(plugin2), BigInt(plugin2.workChain));

await deploy({ extensions });

const actual = await walletV5.getExtensionsArray();
expect(actual.length).toBe(2);
expect(actual[0].equals(plugin1)).toBeTruthy();
expect(actual[1].equals(plugin2)).toBeTruthy();
});

it('Get empty extensions array', async () => {
const actual = await walletV5.getExtensionsArray();
expect(actual.length).toBe(0);
});
});
Loading

0 comments on commit bcf55b7

Please sign in to comment.