Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions spell/cspell-list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ backdoors
Bahdanau
basechain
bitcode
bitnumber
bitstring
bitstrings
blockstore
Expand Down Expand Up @@ -72,6 +73,7 @@ hazyone
Héctor
hehe
heisenbugs
highload
hippity
Hoppity
idict
Expand Down Expand Up @@ -168,6 +170,7 @@ Sánchez
sansx
Satoshi
sctx
sdest
seamus
Seamus
Sedov
Expand Down Expand Up @@ -202,6 +205,7 @@ thetonstudio
TIMELOCK
timeouted
Timeouted
Tock
Toncoin
Toncoins
tonstudio
Expand Down
1 change: 1 addition & 0 deletions spell/cspell-tvm-instructions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ SGN
SHA256U
SINGLE
SKIPDICT
SKIPOPTREF
SPLIT
SPLITQ
SREFS
Expand Down
1 change: 1 addition & 0 deletions src/benchmarks/func.build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const main = async () => {

await allInFolderFunc(__dirname, `${__dirname}/../func/stdlib`, [
"./**/func/**/*.fc",
"./**/func/**/*.func",
]);
};

Expand Down
241 changes: 241 additions & 0 deletions src/benchmarks/highload-wallet-v3/bench.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import "@ton/test-utils";

import benchmarkResults from "@/benchmarks/highload-wallet-v3/gas.json";
import benchmarkCodeSizeResults from "@/benchmarks/highload-wallet-v3/size.json";

import {
generateResults,
getStateSizeForAccount,
generateCodeSizeResults,
getUsedGas,
printBenchmarkTable,
type BenchmarkResult,
type CodeSizeResult,
} from "@/benchmarks/utils/gas";

import { resolve } from "path";
import {
beginCell,
Dictionary,
SendMode,
toNano,
internal as internal_relaxed,
} from "@ton/core";
import { HighloadWalletV3 } from "@/benchmarks/highload-wallet-v3/tact/output/highload-wallet-v3_HighloadWalletV3";
import type { SandboxContract, TreasuryContract } from "@ton/sandbox";
import { Blockchain } from "@ton/sandbox";
import type { KeyPair } from "@ton/crypto";
import { getSecureRandomBytes, keyPairFromSeed } from "@ton/crypto";
import { type Step, writeLog } from "@/test/utils/write-vm-log";
import { HighloadQueryId } from "@/benchmarks/highload-wallet-v3/tests/highload-query-id";
import { bufferToBigInt } from "@/benchmarks/wallet-v5/utils";
import {
createExternalRequestCell,
createInternalTransfer,
DEFAULT_TIMEOUT,
fromInitHighloadWalletV3_FunC,
SUBWALLET_ID,
type FromInitHighloadWalletV3,
} from "@/benchmarks/highload-wallet-v3/tests/utils";

function benchHighloadWalletV3(
benchmarkResult: BenchmarkResult,
codeSizeResults: CodeSizeResult,
fromInit: FromInitHighloadWalletV3,
) {
let blockchain: Blockchain;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setup() instead of global vars

let deployer: SandboxContract<TreasuryContract>;
let receiver: SandboxContract<TreasuryContract>;
let wallet: SandboxContract<HighloadWalletV3>;
let keypair: KeyPair;

let step: Step;

const justTestFlow = async (kind: "external" | "internal") => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename it to something meaningful

const testReceiver = receiver.address;
const forwardToSelfValue = toNano(0.17239);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this const?

const forwardToReceiverValue = toNano(1);
const queryId = HighloadQueryId.fromSeqno(0n);

const internalMessage = createInternalTransfer(wallet, {
actions: [
{
type: "sendMsg",
mode: SendMode.PAY_GAS_SEPARATELY,
outMsg: internal_relaxed({
to: testReceiver,
value: forwardToReceiverValue,
body: null,
}),
},
],
queryId,
value: forwardToSelfValue,
});

const externalRequestCell = createExternalRequestCell(
keypair.secretKey,
{
message: internalMessage,
mode: SendMode.PAY_GAS_SEPARATELY,
queryId,
createdAt: ~~(Date.now() / 1000),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
createdAt: ~~(Date.now() / 1000),
createdAt: (Date.now() / 1000),

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, just Math.floor() is needed here, not ~~

subwalletId: SUBWALLET_ID,
timeout: DEFAULT_TIMEOUT,
},
);

const result = await step("externalTransfer & internalTransfer", () =>
wallet.sendExternal(externalRequestCell.asSlice()),
);

expect(result.transactions).toHaveTransaction({
to: wallet.address,
success: true,
exitCode: 0,
outMessagesCount: 1,
});

expect(result.transactions).toHaveTransaction({
from: wallet.address,
to: wallet.address,
value: forwardToSelfValue,
success: true,
exitCode: 0,
outMessagesCount: 1,
});

expect(result.transactions).toHaveTransaction({
from: wallet.address,
to: testReceiver,
value: forwardToReceiverValue,
});

const externalTransferGasUsed = getUsedGas(result, "external");

const internalTransferGasUsed = getUsedGas(result, "internal");

if (kind == "external") {
expect(externalTransferGasUsed).toEqual(
benchmarkResult.gas["externalTransfer"],
);
} else {
expect(internalTransferGasUsed).toEqual(
benchmarkResult.gas["internalTransfer"],
);
}
};

beforeEach(async () => {
blockchain = await Blockchain.create();

deployer = await blockchain.treasury("deployer");
receiver = await blockchain.treasury("receiver");

keypair = keyPairFromSeed(await getSecureRandomBytes(32));

step = writeLog({
path: resolve(__dirname, "output", "log.yaml"),
blockchain,
});

wallet = blockchain.openContract(
await fromInit(
bufferToBigInt(keypair.publicKey),
BigInt(SUBWALLET_ID),
Dictionary.empty(),
Dictionary.empty(),
0n,
BigInt(DEFAULT_TIMEOUT),
),
);

// Deploy wallet
const deployResult = await wallet.send(
deployer.getSender(),
{
value: toNano("0.05"),
},
beginCell().endCell().asSlice(),
);

expect(deployResult.transactions).toHaveTransaction({
from: deployer.address,
to: wallet.address,
deploy: true,
success: true,
});

// Top up wallet balance
await deployer.send({
to: wallet.address,
value: toNano("10"),
sendMode: SendMode.PAY_GAS_SEPARATELY,
});
});

it("check correctness of deploy", async () => {
const lastCleanTime = await wallet.getGetLastCleanTime();

expect(lastCleanTime).toBe(0n);

const walletPublicKey = await wallet.getGetPublicKey();

expect(walletPublicKey).toBe(bufferToBigInt(keypair.publicKey));
});

it("externalTransfer", async () => {
await justTestFlow("external");
});

it("internalTransfer", async () => {
await justTestFlow("internal");
});

it("cells", async () => {
expect(
(await getStateSizeForAccount(blockchain, wallet.address)).cells,
).toEqual(codeSizeResults.size["cells"]);
});

it("bits", async () => {
expect(
(await getStateSizeForAccount(blockchain, wallet.address)).bits,
).toEqual(codeSizeResults.size["bits"]);
});
}

describe("Highload Wallet v3 Gas Benchmarks", () => {
const fullResults = generateResults(benchmarkResults);
const fullCodeSizeResults = generateCodeSizeResults(
benchmarkCodeSizeResults,
);

describe("func", () => {
const funcCodeSize = fullCodeSizeResults.at(0)!;
const funcResult = fullResults.at(0)!;

benchHighloadWalletV3(
funcResult,
funcCodeSize,
fromInitHighloadWalletV3_FunC,
);
});

describe("tact", () => {
const tactCodeSize = fullCodeSizeResults.at(-1)!;
const tactResult = fullResults.at(-1)!;
benchHighloadWalletV3(
tactResult,
tactCodeSize,
HighloadWalletV3.fromInit.bind(HighloadWalletV3),
);
});

afterAll(() => {
printBenchmarkTable(fullResults, fullCodeSizeResults, {
implementationName: "FunC",
printMode: "full",
});
});
});
Loading