Skip to content

Commit cf65145

Browse files
committed
test: size limits of a single contract
WIP: - [x] (more or less done) map size limits - [ ] max number of cells — there's an issue with Fift decompiler, blocking progress. Besides, the total limit from TON side is known: $2^{16}$ cells per account state, according to [config param 43](https://docs.ton.org/develop/howto/blockchain-configs#param-43) - [ ] max number of struct fields
1 parent 4a0f8fd commit cf65145

File tree

6 files changed

+578
-0
lines changed

6 files changed

+578
-0
lines changed

src/test/size-limits/cells.spec.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { toNano } from "@ton/core";
2+
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
3+
import { MaxCellNumberTester as TestContract } from "./contracts/output/cell-number-limits_MaxCellNumberTester";
4+
import "@ton/test-utils";
5+
6+
// According to config param 43, the absolute max is 2^16 cells by default
7+
// The test below is used to know what's the max for Tact contracts
8+
describe("cell number limits", () => {
9+
let blockchain: Blockchain;
10+
let treasure: SandboxContract<TreasuryContract>;
11+
let contract: SandboxContract<TestContract>;
12+
13+
beforeEach(async () => {
14+
blockchain = await Blockchain.create();
15+
blockchain.verbosity.print = false;
16+
treasure = await blockchain.treasury("treasure", {
17+
balance: 1_000_000_000n,
18+
resetBalanceIfZero: true,
19+
});
20+
contract = blockchain.openContract(await TestContract.fromInit());
21+
22+
const deployResult = await contract.send(
23+
treasure.getSender(),
24+
{ value: toNano("100000") },
25+
null,
26+
);
27+
expect(deployResult.transactions).toHaveTransaction({
28+
from: treasure.address,
29+
to: contract.address,
30+
success: true,
31+
deploy: true,
32+
});
33+
});
34+
35+
it("should test cell number limits", async () => {
36+
// TODO: a test, that adds more cells to the mix.
37+
const sendResult = await contract.send(
38+
treasure.getSender(),
39+
{ value: toNano("1") },
40+
null, // FIXME: ← placeholder, until issues with Fift decompiler of the contract are resolved
41+
// {
42+
// $$type: "AddCells",
43+
// number: BigInt(16), // NOTE: adjust
44+
// },
45+
);
46+
expect(sendResult.transactions).toHaveTransaction({
47+
from: treasure.address,
48+
to: contract.address,
49+
success: false,
50+
actionResultCode: 50,
51+
});
52+
});
53+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// giantNestedCell
2+
import "./giant-nested-cell.tact";
3+
4+
message AddCells { number: Int as uint8 }
5+
6+
contract MaxCellNumberTester {
7+
c1: Cell; c2: Cell; c3: Cell; c4: Cell;
8+
c5: Cell; c6: Cell; c7: Cell; c8: Cell;
9+
c9: Cell; c10: Cell; c11: Cell; c12: Cell;
10+
c13: Cell; c14: Cell; c15: Cell; c16: Cell;
11+
cExtra: Cell;
12+
13+
// NOTE: for some reason, Fift's BoC cannot handle neither 2^13 nor 2^12 cells.
14+
// It just breaks 1k cells short of deserializing each. May be an issue of this wasm fift version (we may need to upgrade it to recently released 2024.09 version)
15+
16+
/// Setup
17+
init() {
18+
self.c1 = giantNestedCell; // 2^{12} - 2 empty cells
19+
self.c2 = giantNestedCell; // 2^{12} - 2 empty cells
20+
self.c3 = giantNestedCell; // 2^{12} - 2 empty cells
21+
self.c4 = giantNestedCell; // 2^{12} - 2 empty cells
22+
self.c5 = giantNestedCell; // 2^{12} - 2 empty cells
23+
self.c6 = giantNestedCell; // 2^{12} - 2 empty cells
24+
self.c7 = giantNestedCell; // 2^{12} - 2 empty cells
25+
self.c8 = giantNestedCell; // 2^{12} - 2 empty cells
26+
self.c9 = giantNestedCell; // 2^{12} - 2 empty cells
27+
self.c10 = giantNestedCell; // 2^{12} - 2 empty cells
28+
self.c11 = giantNestedCell; // 2^{12} - 2 empty cells
29+
self.c12 = giantNestedCell; // 2^{12} - 2 empty cells
30+
self.c13 = giantNestedCell; // 2^{12} - 2 empty cells
31+
self.c14 = giantNestedCell; // 2^{12} - 2 empty cells
32+
self.c15 = giantNestedCell; // 2^{12} - 2 empty cells
33+
self.c16 = giantNestedCell; // 2^{12} - 2 empty cells
34+
self.cExtra = emptyCell(); // 1 empty cell
35+
// Overall: 2^{16} - 2^{4} + 1 = 65520 cells
36+
}
37+
38+
/// To handle deployment
39+
receive() {}
40+
41+
/// To add X cells
42+
receive(msg: AddCells) {
43+
let b = beginCell().storeRef(emptyCell());
44+
45+
// 2 in b, 1 in cExtra already, and X in msg.number, therefore: X - (2 - 1)
46+
repeat (msg.number - 1) {
47+
b = beginCell().storeRef(b.endCell());
48+
}
49+
50+
self.cExtra = b.endCell();
51+
}
52+
}

src/test/size-limits/contracts/giant-nested-cell.tact

+1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
//
2+
// map<Int, Int>
3+
//
4+
5+
message AddIntInt {
6+
batchSize: Int as uint8;
7+
startingValue: Int as uint16;
8+
}
9+
10+
contract MapIntInt {
11+
/// Target map
12+
m: map<Int as uint16, Int as uint16>;
13+
14+
/// To handle deployment
15+
receive() {}
16+
17+
/// To add an item
18+
receive(msg: AddIntInt) {
19+
let curVal = msg.startingValue;
20+
repeat (msg.batchSize) {
21+
self.m.set(curVal, curVal);
22+
curVal += 1;
23+
}
24+
}
25+
}
26+
27+
//
28+
// map<Int, Bool>
29+
//
30+
31+
message AddIntBool {
32+
batchSize: Int as uint8;
33+
startingKey: Int;
34+
}
35+
36+
contract MapIntBool {
37+
/// Target map
38+
m: map<Int, Bool>;
39+
40+
/// To handle deployment
41+
receive() {}
42+
43+
/// To add an item
44+
receive(msg: AddIntBool) {
45+
let curVal = msg.startingKey;
46+
repeat (msg.batchSize) {
47+
self.m.set(curVal, false);
48+
curVal += 1;
49+
}
50+
}
51+
}
52+
53+
//
54+
// map<Int, Cell>
55+
//
56+
57+
message AddIntCell {
58+
batchSize: Int as uint8;
59+
startingKey: Int;
60+
}
61+
62+
contract MapIntCell {
63+
/// Target map
64+
m: map<Int, Cell>;
65+
66+
/// To handle deployment
67+
receive() {}
68+
69+
/// To add an item
70+
receive(msg: AddIntCell) {
71+
let curVal = msg.startingKey;
72+
repeat (msg.batchSize) {
73+
self.m.set(curVal, emptyCell());
74+
curVal += 1;
75+
}
76+
}
77+
}
78+
79+
//
80+
// map<Int, Address>
81+
//
82+
83+
message AddIntAddress {
84+
batchSize: Int as uint8;
85+
startingKey: Int;
86+
}
87+
88+
contract MapIntAddress {
89+
/// Target map
90+
m: map<Int, Address>;
91+
92+
/// To handle deployment
93+
receive() {}
94+
95+
/// To add an item
96+
receive(msg: AddIntAddress) {
97+
let curVal = msg.startingKey;
98+
let myAddr = myAddress(); // TODO: different addresses
99+
repeat (msg.batchSize) {
100+
self.m.set(curVal, myAddr);
101+
curVal += 1;
102+
}
103+
}
104+
}
105+
106+
//
107+
// map<Int, Struct>
108+
//
109+
110+
message AddIntStruct {
111+
batchSize: Int as uint8;
112+
startingKey: Int;
113+
}
114+
115+
struct BoolBool { yes: Bool }
116+
117+
contract MapIntStruct {
118+
/// Target map
119+
m: map<Int, BoolBool>;
120+
121+
/// To handle deployment
122+
receive() {}
123+
124+
/// To add an item
125+
receive(msg: AddIntStruct) {
126+
let curVal = msg.startingKey;
127+
let stBool = BoolBool{ yes: true };
128+
repeat (msg.batchSize) {
129+
self.m.set(curVal, stBool);
130+
curVal += 1;
131+
}
132+
}
133+
}
134+
135+
//
136+
// map<Int, Message>
137+
//
138+
139+
message AddIntMessage {
140+
batchSize: Int as uint8;
141+
startingKey: Int;
142+
}
143+
144+
message(0x2A) TheAnswer {}
145+
146+
contract MapIntMessage {
147+
/// Target map
148+
m: map<Int, TheAnswer>;
149+
150+
/// To handle deployment
151+
receive() {}
152+
153+
/// To add an item
154+
receive(msg: AddIntMessage) {
155+
let curVal = msg.startingKey;
156+
let msgEmpty = TheAnswer{};
157+
repeat (msg.batchSize) {
158+
self.m.set(curVal, msgEmpty);
159+
curVal += 1;
160+
}
161+
}
162+
}
163+
164+
// TODO: all the same, but with Addresses as keys.
165+
// NOTE: contractAddress(StateInit{code: emptyCell(), data: beginCell().storeUint(curVal, 16).endCell()})

0 commit comments

Comments
 (0)