Skip to content

Commit

Permalink
fix: error message for comment (text) receivers with 124 bytes or more (
Browse files Browse the repository at this point in the history
  • Loading branch information
i582 authored Feb 5, 2025
1 parent 50612f9 commit 7f3bb27
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 7 deletions.
1 change: 1 addition & 0 deletions dev-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow constant/trait constants depend on each other: PR [#1622](https://github.com/tact-lang/tact/pull/1622)
- Combine all generated FunC code into a single file: PR [#1698](https://github.com/tact-lang/tact/pull/1698)
- Runtime `sha256` now work for arbitrary strings with length >= 128: PR [#1626](https://github.com/tact-lang/tact/pull/1626)
- Error message for comment (text) receivers with 124 bytes or more: PR [#1711](https://github.com/tact-lang/tact/pull/1711)

### Docs

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/book/receive.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ To receive a message of the required type, you need to declare a receiver functi
There are several receiver functions. All receiver functions are processed in the order they are listed below:

* `receive(){:tact}` - called when an empty message is sent to the contract
* `receive("message"){:tact}` - called when a text message with a specific comment is sent to the contract
* `receive("message"){:tact}` - called when a text message with a specific comment is sent to the contract (maximum `"message"{:tact}` length is 123 bytes)
* `receive(str: String){:tact}` - called when an arbitrary text message is sent to the contract
* `receive(msg: MyMessage){:tact}` - called when a binary message of type `MyMessage` is sent to the contract
* `receive(msg: Slice){:tact}` - called when binary message of unknown type is sent to the contract
Expand Down
22 changes: 17 additions & 5 deletions src/generator/writers/writeRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ import { ops } from "./ops";
import { resolveFuncType } from "./resolveFuncType";
import { resolveFuncTypeUnpack } from "./resolveFuncTypeUnpack";
import { writeStatement } from "./writeFunction";
import { AstNumber } from "../../ast/ast";
import { AstNumber, AstReceiver } from "../../ast/ast";
import { throwCompilationError } from "../../error/errors";

export function commentPseudoOpcode(comment: string, ast: AstReceiver): string {
const buffer = Buffer.from(comment, "utf8");
if (buffer.length > 123) {
throwCompilationError(
`receiver message is too long, max length is 123 bytes, but given ${buffer.length}`,
ast.loc,
);
}

export function commentPseudoOpcode(comment: string): string {
return beginCell()
.storeUint(0, 32)
.storeBuffer(Buffer.from(comment, "utf8"))
.storeBuffer(buffer)
.endCell()
.hash()
.toString("hex", 0, 64);
Expand Down Expand Up @@ -206,7 +215,10 @@ export function writeRouter(
selector.kind ===
(internal ? "internal-comment" : "external-comment")
) {
const hash = commentPseudoOpcode(selector.comment);
const hash = commentPseudoOpcode(
selector.comment,
r.ast,
);
ctx.append();
ctx.append(
`;; Receive "${selector.comment}" message`,
Expand Down Expand Up @@ -365,7 +377,7 @@ export function writeReceiver(
selector.kind === "internal-comment" ||
selector.kind === "external-comment"
) {
const hash = commentPseudoOpcode(selector.comment);
const hash = commentPseudoOpcode(selector.comment, f.ast);
ctx.append(
`(${selfType}, ()) ${ops.receiveText(self.name, selector.kind === "internal-comment" ? "internal" : "external", hash)}(${selfType + " " + funcIdOf("self")}) impure inline {`,
);
Expand Down
185 changes: 185 additions & 0 deletions src/types/__snapshots__/resolveDescriptors.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,24 @@ exports[`resolveDescriptors should fail descriptors for read-self-in-init-functi
"
`;

exports[`resolveDescriptors should fail descriptors for receive-message-too-big 1`] = `
"<unknown>:4:5: receiver message is too long, max length is 123 bytes, but given 124
3 | contract Foo {
> 4 | receive("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {}
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 | }
"
`;

exports[`resolveDescriptors should fail descriptors for receive-message-too-big-2 1`] = `
"<unknown>:4:5: receiver message is too long, max length is 123 bytes, but given 156
3 | contract Foo {
> 4 | receive("💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀") {}
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 | }
"
`;

exports[`resolveDescriptors should fail descriptors for scope-contract-shadows-contract 1`] = `
"<unknown>:12:1: Type "Main" already exists
11 |
Expand Down Expand Up @@ -23708,6 +23726,173 @@ exports[`resolveDescriptors should resolve descriptors for override-for-existing

exports[`resolveDescriptors should resolve descriptors for override-for-existing-function-in-trait-with-multi-inheritance-from-base-trait 2`] = `[]`;

exports[`resolveDescriptors should resolve descriptors for receive-message-with-biggest-allowed-size 1`] = `
[
{
"ast": {
"attributes": [],
"declarations": [],
"id": 2,
"kind": "trait",
"loc": trait BaseTrait {},
"name": {
"id": 1,
"kind": "id",
"loc": BaseTrait,
"text": "BaseTrait",
},
"traits": [],
},
"constants": [],
"dependsOn": [],
"fields": [],
"functions": Map {},
"header": null,
"init": null,
"interfaces": [],
"kind": "trait",
"name": "BaseTrait",
"origin": "user",
"partialFieldCount": 0,
"receivers": [],
"signature": null,
"tlb": null,
"traits": [],
"uid": 1020,
},
{
"ast": {
"attributes": [],
"declarations": [
{
"id": 7,
"kind": "receiver",
"loc": receive("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {},
"selector": {
"id": 6,
"kind": "internal",
"loc": receive,
"subKind": {
"comment": {
"id": 4,
"kind": "string",
"loc": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
"id": 5,
"kind": "comment",
},
},
"statements": [],
},
],
"id": 8,
"kind": "contract",
"loc": contract Foo {
receive("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {}
},
"name": {
"id": 3,
"kind": "id",
"loc": Foo,
"text": "Foo",
},
"traits": [],
},
"constants": [],
"dependsOn": [],
"fields": [],
"functions": Map {},
"header": null,
"init": {
"ast": {
"id": 10,
"kind": "contract_init",
"loc": contract Foo {
receive("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {}
},
"params": [],
"statements": [],
},
"params": [],
},
"interfaces": [],
"kind": "contract",
"name": "Foo",
"origin": "user",
"partialFieldCount": 0,
"receivers": [
{
"ast": {
"id": 7,
"kind": "receiver",
"loc": receive("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {},
"selector": {
"id": 6,
"kind": "internal",
"loc": receive,
"subKind": {
"comment": {
"id": 4,
"kind": "string",
"loc": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"value": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
"id": 5,
"kind": "comment",
},
},
"statements": [],
},
"selector": {
"comment": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"kind": "internal-comment",
},
},
],
"signature": null,
"tlb": null,
"traits": [
{
"ast": {
"attributes": [],
"declarations": [],
"id": 2,
"kind": "trait",
"loc": trait BaseTrait {},
"name": {
"id": 1,
"kind": "id",
"loc": BaseTrait,
"text": "BaseTrait",
},
"traits": [],
},
"constants": [],
"dependsOn": [],
"fields": [],
"functions": Map {},
"header": null,
"init": null,
"interfaces": [],
"kind": "trait",
"name": "BaseTrait",
"origin": "user",
"partialFieldCount": 0,
"receivers": [],
"signature": null,
"tlb": null,
"traits": [],
"uid": 1020,
},
],
"uid": 10576,
},
]
`;

exports[`resolveDescriptors should resolve descriptors for receive-message-with-biggest-allowed-size 2`] = `[]`;

exports[`resolveDescriptors should resolve descriptors for scope-loops 1`] = `[]`;

exports[`resolveDescriptors should resolve descriptors for scope-loops 2`] = `
Expand Down
2 changes: 1 addition & 1 deletion src/types/resolveSignatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ function checkCommentMessageReceiver(
rcvAst: AstReceiver,
usedOpcodes: Map<commentOpcode, messageType>,
) {
const opcode = commentPseudoOpcode(rcv.comment);
const opcode = commentPseudoOpcode(rcv.comment, rcvAst);
if (usedOpcodes.has(opcode)) {
throwCompilationError(
`Receive functions of a contract or trait cannot process comments with the same hashes: hashes of comment strings "${rcv.comment}" and "${usedOpcodes.get(opcode)}" are equal`,
Expand Down
5 changes: 5 additions & 0 deletions src/types/test-failed/receive-message-too-big-2.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
trait BaseTrait {}

contract Foo {
receive("💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀💀") {}
}
5 changes: 5 additions & 0 deletions src/types/test-failed/receive-message-too-big.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
trait BaseTrait {}

contract Foo {
receive("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {}
}
5 changes: 5 additions & 0 deletions src/types/test/receive-message-with-biggest-allowed-size.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
trait BaseTrait {}

contract Foo {
receive("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") {}
}

0 comments on commit 7f3bb27

Please sign in to comment.