Skip to content

Commit

Permalink
test: size limits of a single contract
Browse files Browse the repository at this point in the history
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
  • Loading branch information
novusnota committed Sep 13, 2024
1 parent a2863d4 commit 8560201
Show file tree
Hide file tree
Showing 6 changed files with 578 additions and 0 deletions.
53 changes: 53 additions & 0 deletions src/test/size-limits/cells.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { toNano } from "@ton/core";
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
import { MaxCellNumberTester as TestContract } from "./contracts/output/cell-number-limits_MaxCellNumberTester";
import "@ton/test-utils";

// According to config param 43, the absolute max is 2^16 cells by default
// The test below is used to know what's the max for Tact contracts
describe("cell number limits", () => {
let blockchain: Blockchain;
let treasure: SandboxContract<TreasuryContract>;
let contract: SandboxContract<TestContract>;

beforeEach(async () => {
blockchain = await Blockchain.create();
blockchain.verbosity.print = false;
treasure = await blockchain.treasury("treasure", {
balance: 1_000_000_000n,
resetBalanceIfZero: true,
});
contract = blockchain.openContract(await TestContract.fromInit());

const deployResult = await contract.send(
treasure.getSender(),
{ value: toNano("100000") },
null,
);
expect(deployResult.transactions).toHaveTransaction({
from: treasure.address,
to: contract.address,
success: true,
deploy: true,
});
});

it("should test cell number limits", async () => {
// TODO: a test, that adds more cells to the mix.
const sendResult = await contract.send(
treasure.getSender(),
{ value: toNano("1") },
null, // FIXME: ← placeholder, until issues with Fift decompiler of the contract are resolved
// {
// $$type: "AddCells",
// number: BigInt(16), // NOTE: adjust
// },
);
expect(sendResult.transactions).toHaveTransaction({
from: treasure.address,
to: contract.address,
success: false,
actionResultCode: 50,
});
});
});
52 changes: 52 additions & 0 deletions src/test/size-limits/contracts/cell-number-limits.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// giantNestedCell
import "./giant-nested-cell.tact";

message AddCells { number: Int as uint8 }

contract MaxCellNumberTester {
c1: Cell; c2: Cell; c3: Cell; c4: Cell;
c5: Cell; c6: Cell; c7: Cell; c8: Cell;
c9: Cell; c10: Cell; c11: Cell; c12: Cell;
c13: Cell; c14: Cell; c15: Cell; c16: Cell;
cExtra: Cell;

// NOTE: for some reason, Fift's BoC cannot handle neither 2^13 nor 2^12 cells.
// 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)

/// Setup
init() {
self.c1 = giantNestedCell; // 2^{12} - 2 empty cells
self.c2 = giantNestedCell; // 2^{12} - 2 empty cells
self.c3 = giantNestedCell; // 2^{12} - 2 empty cells
self.c4 = giantNestedCell; // 2^{12} - 2 empty cells
self.c5 = giantNestedCell; // 2^{12} - 2 empty cells
self.c6 = giantNestedCell; // 2^{12} - 2 empty cells
self.c7 = giantNestedCell; // 2^{12} - 2 empty cells
self.c8 = giantNestedCell; // 2^{12} - 2 empty cells
self.c9 = giantNestedCell; // 2^{12} - 2 empty cells
self.c10 = giantNestedCell; // 2^{12} - 2 empty cells
self.c11 = giantNestedCell; // 2^{12} - 2 empty cells
self.c12 = giantNestedCell; // 2^{12} - 2 empty cells
self.c13 = giantNestedCell; // 2^{12} - 2 empty cells
self.c14 = giantNestedCell; // 2^{12} - 2 empty cells
self.c15 = giantNestedCell; // 2^{12} - 2 empty cells
self.c16 = giantNestedCell; // 2^{12} - 2 empty cells
self.cExtra = emptyCell(); // 1 empty cell
// Overall: 2^{16} - 2^{4} + 1 = 65520 cells
}

/// To handle deployment
receive() {}

/// To add X cells
receive(msg: AddCells) {
let b = beginCell().storeRef(emptyCell());

// 2 in b, 1 in cExtra already, and X in msg.number, therefore: X - (2 - 1)
repeat (msg.number - 1) {
b = beginCell().storeRef(b.endCell());
}

self.cExtra = b.endCell();
}
}
1 change: 1 addition & 0 deletions src/test/size-limits/contracts/giant-nested-cell.tact

Large diffs are not rendered by default.

165 changes: 165 additions & 0 deletions src/test/size-limits/contracts/map-size-limits.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
//
// map<Int, Int>
//

message AddIntInt {
batchSize: Int as uint8;
startingValue: Int as uint16;
}

contract MapIntInt {
/// Target map
m: map<Int as uint16, Int as uint16>;

/// To handle deployment
receive() {}

/// To add an item
receive(msg: AddIntInt) {
let curVal = msg.startingValue;
repeat (msg.batchSize) {
self.m.set(curVal, curVal);
curVal += 1;
}
}
}

//
// map<Int, Bool>
//

message AddIntBool {
batchSize: Int as uint8;
startingKey: Int;
}

contract MapIntBool {
/// Target map
m: map<Int, Bool>;

/// To handle deployment
receive() {}

/// To add an item
receive(msg: AddIntBool) {
let curVal = msg.startingKey;
repeat (msg.batchSize) {
self.m.set(curVal, false);
curVal += 1;
}
}
}

//
// map<Int, Cell>
//

message AddIntCell {
batchSize: Int as uint8;
startingKey: Int;
}

contract MapIntCell {
/// Target map
m: map<Int, Cell>;

/// To handle deployment
receive() {}

/// To add an item
receive(msg: AddIntCell) {
let curVal = msg.startingKey;
repeat (msg.batchSize) {
self.m.set(curVal, emptyCell());
curVal += 1;
}
}
}

//
// map<Int, Address>
//

message AddIntAddress {
batchSize: Int as uint8;
startingKey: Int;
}

contract MapIntAddress {
/// Target map
m: map<Int, Address>;

/// To handle deployment
receive() {}

/// To add an item
receive(msg: AddIntAddress) {
let curVal = msg.startingKey;
let myAddr = myAddress(); // TODO: different addresses
repeat (msg.batchSize) {
self.m.set(curVal, myAddr);
curVal += 1;
}
}
}

//
// map<Int, Struct>
//

message AddIntStruct {
batchSize: Int as uint8;
startingKey: Int;
}

struct BoolBool { yes: Bool }

contract MapIntStruct {
/// Target map
m: map<Int, BoolBool>;

/// To handle deployment
receive() {}

/// To add an item
receive(msg: AddIntStruct) {
let curVal = msg.startingKey;
let stBool = BoolBool{ yes: true };
repeat (msg.batchSize) {
self.m.set(curVal, stBool);
curVal += 1;
}
}
}

//
// map<Int, Message>
//

message AddIntMessage {
batchSize: Int as uint8;
startingKey: Int;
}

message(0x2A) TheAnswer {}

contract MapIntMessage {
/// Target map
m: map<Int, TheAnswer>;

/// To handle deployment
receive() {}

/// To add an item
receive(msg: AddIntMessage) {
let curVal = msg.startingKey;
let msgEmpty = TheAnswer{};
repeat (msg.batchSize) {
self.m.set(curVal, msgEmpty);
curVal += 1;
}
}
}

// TODO: all the same, but with Addresses as keys.
// NOTE: contractAddress(StateInit{code: emptyCell(), data: beginCell().storeUint(curVal, 16).endCell()})
Loading

0 comments on commit 8560201

Please sign in to comment.