Skip to content

Commit 2eb5331

Browse files
authored
Merge branch 'main' into pmakhnev/fix-ts-wrapper-with-init
2 parents e8b05c0 + 4ef078c commit 2eb5331

File tree

15 files changed

+314
-48
lines changed

15 files changed

+314
-48
lines changed

dev-docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7676
- Incorrect call generation to a mutation function: PR [#1608](https://github.com/tact-lang/tact/pull/1608)
7777
- Allow constant/trait constants depend on each other: PR [#1622](https://github.com/tact-lang/tact/pull/1622)
7878
- Combine all generated FunC code into a single file: PR [#1698](https://github.com/tact-lang/tact/pull/1698)
79+
- Runtime `sha256` now work for arbitrary strings with length >= 128: PR [#1626](https://github.com/tact-lang/tact/pull/1626)
7980
- Generated code in TypeScript wrappers for contract with `init(init: Init)`: PR [#1709](https://github.com/tact-lang/tact/pull/1709)
8081

8182
### Docs

src/abi/global.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { writeExpression } from "../generator/writers/writeExpression";
1010
import { throwCompilationError } from "../error/errors";
1111
import { getErrorId } from "../types/resolveErrors";
1212
import { AbiFunction } from "./AbiFunction";
13-
import { sha256_sync } from "@ton/crypto";
1413
import path from "path";
1514
import { cwd } from "process";
1615
import { posixNormalize } from "../utils/filePath";
@@ -20,6 +19,7 @@ import {
2019
interpretEscapeSequences,
2120
} from "../optimizer/interpreter";
2221
import { isLiteral } from "../ast/ast-helpers";
22+
import { sha256 } from "../utils/sha256";
2323

2424
export const GlobalFunctions: Map<string, AbiFunction> = new Map([
2525
[
@@ -383,14 +383,12 @@ export const GlobalFunctions: Map<string, AbiFunction> = new Map([
383383
// FIXME: This one does not need fixing, because it is carried out inside a "isLiteral" check.
384384
// Remove this comment once the optimization step is added
385385
const str = ensureSimplifiedString(resolved0).value;
386-
return BigInt(
387-
"0x" + sha256_sync(str).toString("hex"),
388-
).toString(10);
386+
return sha256(str).value.toString(10);
389387
}
390388

391-
// Otherwise, revert back to runtime hash through SHA256U
389+
// Otherwise, revert back to runtime hash through HASHEXT_SHA256
392390
const exp = writeExpression(resolved[0]!, ctx);
393-
return `string_hash(${exp})`;
391+
return `__tact_sha256(${exp})`;
394392
}
395393

396394
// Slice case

src/generator/Writer.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { CompilerContext } from "../context/context";
22
import { escapeUnicodeControlCodes, trimIndent } from "../utils/text";
33
import { topologicalSort } from "../utils/utils";
44
import { Writer } from "../utils/Writer";
5+
import { TactInternalCompilerError } from "../error/errors";
56

67
type Flag = "inline" | "impure" | "inline_ref";
78

@@ -83,10 +84,10 @@ export class WriterContext {
8384
}
8485
}
8586
if (missing.size > 0) {
86-
throw new Error(
87+
throw new TactInternalCompilerError(
8788
`Functions ${Array.from(missing.keys())
8889
.map((v) => `"${v}"`)
89-
.join(", ")} wasn't rendered`,
90+
.join(", ")} wasn't processed and generated`,
9091
);
9192
}
9293

src/optimizer/interpreter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
} from "../types/resolveDescriptors";
2222
import { getExpType } from "../types/resolveExpression";
2323
import { showValue, TypeRef } from "../types/types";
24-
import { sha256_sync } from "@ton/crypto";
2524
import { defaultParser, getParser, Parser } from "../grammar/grammar";
2625
import { dummySrcInfo, SrcInfo } from "../grammar";
2726
import {
@@ -33,6 +32,7 @@ import {
3332
isSelfId,
3433
} from "../ast/ast-helpers";
3534
import { divFloor, modFloor } from "./util";
35+
import { sha256 } from "../utils/sha256";
3636

3737
// TVM integers are signed 257-bit integers
3838
const minTvmInt: bigint = -(2n ** 256n);
@@ -1310,7 +1310,7 @@ export class Interpreter {
13101310
}
13111311
const str = ensureSimplifiedString(expr);
13121312
return this.util.makeNumberLiteral(
1313-
BigInt("0x" + sha256_sync(str.value).toString("hex")),
1313+
sha256(str.value).value,
13141314
ast.loc,
13151315
);
13161316
}

src/stdlib/stdlib.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,11 @@ files["std/internal/text.tact"] =
563563
files["std/stdlib_ex.fc"] =
564564
"Zm9yYWxsIFggLT4gdHVwbGUgX190YWN0X3NldCh0dXBsZSB4LCBYIHYsIGludCBpKSBhc20gIlNFVElOREVYVkFSUSI7CigpIF9fdGFjdF9ub3AoKSBhc20gIk5PUCI7" +
565565
"CnNsaWNlIF9fdGFjdF9zdHJfdG9fc2xpY2Uoc2xpY2UgcykgYXNtICJOT1AiOwpzbGljZSBfX3RhY3Rfc2xpY2VfdG9fc3RyKHNsaWNlIHMpIGFzbSAiTk9QIjsKc2xp" +
566-
"Y2UgX190YWN0X2FkZHJlc3NfdG9fc2xpY2Uoc2xpY2UgcykgYXNtICJOT1AiOw==";
566+
"Y2UgX190YWN0X2FkZHJlc3NfdG9fc2xpY2Uoc2xpY2UgcykgYXNtICJOT1AiOwoKKCkgX190YWN0X3NoYV9wdXNoKHNsaWNlIGRhdGEpIGltcHVyZSBhc20gIk9ORSI7" +
567+
"CmludCBfX3RhY3Rfc2hhX3Nob3VsZF9wcm9jZWVkKCkgYXNtICJPVkVSIFNSRUZTIDAgTkVRSU5UIjsKKCkgX190YWN0X3NoYV9vcGVyYXRlKCkgaW1wdXJlIGFzbSAi" +
568+
"T1ZFUiBMRFJFRiBzMCBQT1AgQ1RPUyBzMCBzMSBYQ0hHIElOQyI7CmludCBfX3RhY3Rfc2hhX2hhc2hfZXh0KCkgYXNtICJIQVNIRVhUX1NIQTI1NiI7CgppbnQgX190" +
569+
"YWN0X3NoYTI1NihzbGljZSBkYXRhKSB7CiAgICBfX3RhY3Rfc2hhX3B1c2goZGF0YSk7CiAgICB3aGlsZSAoX190YWN0X3NoYV9zaG91bGRfcHJvY2VlZCgpKSB7CiAg" +
570+
"ICAgICAgX190YWN0X3NoYV9vcGVyYXRlKCk7CiAgICB9CiAgICByZXR1cm4gX190YWN0X3NoYV9oYXNoX2V4dCgpOwp9Cg==";
567571
files["std/stdlib.fc"] =
568572
"OzsgU3RhbmRhcmQgbGlicmFyeSBmb3IgZnVuQwo7OwoKey0KICAgIFRoaXMgZmlsZSBpcyBwYXJ0IG9mIFRPTiBGdW5DIFN0YW5kYXJkIExpYnJhcnkuCgogICAgRnVu" +
569573
"QyBTdGFuZGFyZCBMaWJyYXJ5IGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkKICAgIGl0IHVuZGVyIHRoZSB0ZXJt" +

src/stdlib/stdlib/std/stdlib_ex.fc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,17 @@ forall X -> tuple __tact_set(tuple x, X v, int i) asm "SETINDEXVARQ";
22
() __tact_nop() asm "NOP";
33
slice __tact_str_to_slice(slice s) asm "NOP";
44
slice __tact_slice_to_str(slice s) asm "NOP";
5-
slice __tact_address_to_slice(slice s) asm "NOP";
5+
slice __tact_address_to_slice(slice s) asm "NOP";
6+
7+
() __tact_sha_push(slice data) impure asm "ONE";
8+
int __tact_sha_should_proceed() asm "OVER SREFS 0 NEQINT";
9+
() __tact_sha_operate() impure asm "OVER LDREF s0 POP CTOS s0 s1 XCHG INC";
10+
int __tact_sha_hash_ext() asm "HASHEXT_SHA256";
11+
12+
int __tact_sha256(slice data) {
13+
__tact_sha_push(data);
14+
while (__tact_sha_should_proceed()) {
15+
__tact_sha_operate();
16+
}
17+
return __tact_sha_hash_ext();
18+
}

src/test/benchmarks/benchmarks.spec.ts

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ import {
1010
SandboxContract,
1111
TreasuryContract,
1212
} from "@ton/sandbox";
13-
import { Forward } from "./contracts/output/forward_Forward";
1413
import { Functions } from "./contracts/output/functions_Functions";
14+
import { Sha256Small } from "./contracts/output/benchmark_sha256_small_Sha256Small";
15+
import { Sha256Big } from "./contracts/output/benchmark_sha256_big_Sha256Big";
16+
import { Sha256AsSlice } from "./contracts/output/benchmark_sha256_as_slice_Sha256AsSlice";
17+
import { Forward } from "./contracts/output/forward_Forward";
1518
import "@ton/test-utils";
19+
import { getUsedGas } from "./util";
1620

1721
function measureGas(txs: BlockchainTransaction[]) {
1822
return (
@@ -67,4 +71,97 @@ describe("benchmarks", () => {
6771
const codeSize = testContract.init!.code.toBoc().length;
6872
expect(codeSize).toMatchSnapshot("code size");
6973
});
74+
75+
async function hashStringSmall(
76+
sha256: SandboxContract<Sha256Small>,
77+
s: string,
78+
): Promise<bigint> {
79+
const result = await sha256.send(
80+
treasure.getSender(),
81+
{ value: toNano(1) },
82+
{ $$type: "HashData", value: s },
83+
);
84+
85+
return getUsedGas(result);
86+
}
87+
88+
async function hashStringBig(
89+
sha256: SandboxContract<Sha256Big>,
90+
s: string,
91+
): Promise<bigint> {
92+
const result = await sha256.send(
93+
treasure.getSender(),
94+
{ value: toNano(1) },
95+
{ $$type: "HashData", value: s },
96+
);
97+
98+
return getUsedGas(result);
99+
}
100+
101+
async function hashStringAsSLice(
102+
sha256: SandboxContract<Sha256AsSlice>,
103+
s: string,
104+
): Promise<bigint> {
105+
const result = await sha256.send(
106+
treasure.getSender(),
107+
{ value: toNano(1) },
108+
{ $$type: "HashData", value: s },
109+
);
110+
111+
return getUsedGas(result);
112+
}
113+
114+
it("benchmark sha256", async () => {
115+
const sha256Small = blockchain.openContract(
116+
await Sha256Small.fromInit(),
117+
);
118+
const sha256Big = blockchain.openContract(await Sha256Big.fromInit());
119+
const sha256AsSlice = blockchain.openContract(
120+
await Sha256AsSlice.fromInit(),
121+
);
122+
123+
await sha256Small.send(
124+
treasure.getSender(),
125+
{ value: toNano(1) },
126+
null,
127+
);
128+
await sha256Big.send(treasure.getSender(), { value: toNano(1) }, null);
129+
await sha256AsSlice.send(
130+
treasure.getSender(),
131+
{ value: toNano(1) },
132+
null,
133+
);
134+
135+
await hashStringBig(sha256Big, "hello world");
136+
await hashStringSmall(sha256Small, "hello world");
137+
await hashStringAsSLice(sha256AsSlice, "hello world");
138+
139+
expect(await hashStringBig(sha256Big, "hello world")).toEqual(3039n);
140+
expect(await hashStringSmall(sha256Small, "hello world")).toEqual(
141+
2516n,
142+
);
143+
expect(await hashStringAsSLice(sha256AsSlice, "hello world")).toEqual(
144+
2516n,
145+
);
146+
147+
expect(await hashStringBig(sha256Big, "hello world".repeat(5))).toEqual(
148+
3040n,
149+
);
150+
expect(
151+
await hashStringSmall(sha256Small, "hello world".repeat(5)),
152+
).toEqual(2516n);
153+
expect(
154+
await hashStringAsSLice(sha256AsSlice, "hello world".repeat(5)),
155+
).toEqual(2516n);
156+
157+
expect(
158+
await hashStringBig(sha256Big, "hello world".repeat(10)),
159+
).toEqual(3042n);
160+
expect(
161+
await hashStringSmall(sha256Small, "hello world".repeat(10)),
162+
).toEqual(2516n);
163+
expect(
164+
await hashStringAsSLice(sha256AsSlice, "hello world".repeat(10)),
165+
).toEqual(2516n);
166+
});
70167
});
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
message HashData {
2+
value: String;
3+
}
4+
5+
contract Sha256AsSlice {
6+
result: Int = 0;
7+
8+
receive() {}
9+
10+
receive(h: HashData) {
11+
self.result += sha256(h.value.asSlice());
12+
}
13+
14+
get fun res(): Int {
15+
return self.result;
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
message HashData {
2+
value: String;
3+
}
4+
5+
contract Sha256Big {
6+
result: Int = 0;
7+
8+
receive() {}
9+
10+
receive(h: HashData) {
11+
self.result += sha256(h.value);
12+
}
13+
14+
get fun res(): Int {
15+
return self.result;
16+
}
17+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
asm fun sha256_native(s: String): Int {
2+
SHA256U
3+
}
4+
5+
message HashData {
6+
value: String;
7+
}
8+
9+
contract Sha256Small {
10+
result: Int = 0;
11+
12+
receive() {}
13+
14+
receive(h: HashData) {
15+
self.result += sha256_native(h.value);
16+
}
17+
18+
get fun res(): Int {
19+
return self.result;
20+
}
21+
}

0 commit comments

Comments
 (0)