Skip to content

Commit

Permalink
work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Gusarich committed May 17, 2024
1 parent 79e87d5 commit aaab72f
Show file tree
Hide file tree
Showing 25 changed files with 277 additions and 293 deletions.
10 changes: 10 additions & 0 deletions src/generator/writers/resolveFuncFlatPack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ export function resolveFuncFlatPack(
throw Error("Void type is not allowed in function arguments: " + name);
}

if (descriptor.kind === "merkle_proof") {
return [name + `'rootHash`].concat(
resolveFuncFlatPack(
getType(ctx.ctx, descriptor.name),
name + `'data`,
ctx,
),
);
}

// TypeDescription
if (descriptor.kind === "primitive") {
return [name];
Expand Down
6 changes: 6 additions & 0 deletions src/generator/writers/resolveFuncFlatTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export function resolveFuncFlatTypes(
throw Error("Void type is not allowed in function arguments");
}

if (descriptor.kind === "merkle_proof") {
return ["int"].concat(
resolveFuncFlatTypes(getType(ctx.ctx, descriptor.name), ctx),
);
}

// TypeDescription
if (descriptor.kind === "primitive") {
return [resolveFuncType(descriptor, ctx)];
Expand Down
4 changes: 4 additions & 0 deletions src/generator/writers/resolveFuncTupledType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export function resolveFuncTupledType(
return "()";
}

if (descriptor.kind === "merkle_proof") {
return "tuple";
}

// TypeDescription
if (descriptor.kind === "primitive") {
if (descriptor.name === "Int") {
Expand Down
13 changes: 13 additions & 0 deletions src/generator/writers/resolveFuncType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ export function resolveFuncType(
return "()";
}

if (descriptor.kind === "merkle_proof") {
return (
"(int, " +
resolveFuncType(
getType(ctx.ctx, descriptor.name),
ctx,
false,
usePartialFields,
) +
")"
);
}

// TypeDescription
if (descriptor.kind === "primitive") {
if (descriptor.name === "Int") {
Expand Down
4 changes: 4 additions & 0 deletions src/generator/writers/resolveFuncTypeFromAbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export function resolveFuncTypeFromAbi(
for (const f of fields) {
if (f.kind === "dict") {
res.push("cell");
} else if (f.kind === "merkle") {

Check failure on line 16 in src/generator/writers/resolveFuncTypeFromAbi.ts

View workflow job for this annotation

GitHub Actions / test (18.x, windows-latest)

This comparison appears to be unintentional because the types '"simple"' and '"merkle"' have no overlap.
const t = getType(ctx.ctx, f.name);

Check failure on line 17 in src/generator/writers/resolveFuncTypeFromAbi.ts

View workflow job for this annotation

GitHub Actions / test (18.x, windows-latest)

Property 'name' does not exist on type 'never'.
const loaded = t.fields.map((v) => v.abi.type);
res.push("(int, " + resolveFuncTypeFromAbi(loaded, ctx) + ")");
} else if (f.kind === "simple") {
if (f.type === "int" || f.type === "uint" || f.type === "bool") {
res.push("int");
Expand Down
6 changes: 6 additions & 0 deletions src/generator/writers/resolveFuncTypeFromAbiUnpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ export function resolveFuncTypeFromAbiUnpack(
for (const f of fields) {
if (f.type.kind === "dict") {
res.push(`${name}'${f.name}`);
} else if (f.type.kind === "merkle") {

Check failure on line 17 in src/generator/writers/resolveFuncTypeFromAbiUnpack.ts

View workflow job for this annotation

GitHub Actions / test (18.x, windows-latest)

This comparison appears to be unintentional because the types '"simple"' and '"merkle"' have no overlap.
const t = getType(ctx.ctx, f.type.name);

Check failure on line 18 in src/generator/writers/resolveFuncTypeFromAbiUnpack.ts

View workflow job for this annotation

GitHub Actions / test (18.x, windows-latest)

Property 'name' does not exist on type 'never'.
const loaded = t.fields.map((v) => v.abi);
res.push(
resolveFuncTypeFromAbiUnpack(`${name}'${f.name}`, loaded, ctx),
);
} else if (f.type.kind === "simple") {
if (
f.type.type === "int" ||
Expand Down
4 changes: 4 additions & 0 deletions src/generator/writers/resolveFuncTypeUnpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export function resolveFuncTypeUnpack(
throw Error("Void type is not allowed in function arguments: " + name);
}

if (descriptor.kind === "merkle_proof") {
return `(${name}'rootHash, ${name}'data)`;
}

// TypeDescription
if (descriptor.kind === "primitive") {
return name;
Expand Down
14 changes: 13 additions & 1 deletion src/generator/writers/writeExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,13 +483,25 @@ export function writeExpression(f: ASTExpression, ctx: WriterContext): string {
const src = getExpType(ctx.ctx, f.src);
if (
src === null ||
((src.kind !== "ref" || src.optional) && src.kind !== "ref_bounced")
((src.kind !== "ref" || src.optional) &&
src.kind !== "ref_bounced" &&
src.kind !== "merkle_proof")
) {
throwError(
`Cannot access field of non-struct type: ${printTypeRef(src)}`,
f.ref,
);
}

if (src.kind === "merkle_proof") {
if (f.name === "rootHash") {
return `${writeExpression(f.src, ctx)}'rootHash`;
}
if (f.name === "data") {
return `${writeExpression(f.src, ctx)}'data`;
}
}

const srcT = getType(ctx.ctx, src.name);

// Resolve field
Expand Down
21 changes: 20 additions & 1 deletion src/generator/writers/writeSerialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,13 @@ function writeSerializerField(
}
return;
}
if (op.kind === "merkle_proof") {
ctx.used(`__tact_store_merkle_proof`);
ctx.append(
`build_${gen} = __tact_store_merkle_proof(build_${gen}, ${fieldName});`,
);
return;
}

throw Error("Unsupported field kind: " + op.kind);
}
Expand Down Expand Up @@ -432,7 +439,8 @@ function writeFieldParser(
ctx: WriterContext,
) {
const op = f.op;
const varName = `var v'${f.name}`;
const name = `v'${f.name}`;
const varName = `var ${name}`;

// Handle int
if (op.kind === "int") {
Expand Down Expand Up @@ -590,6 +598,17 @@ function writeFieldParser(
}
return;
}
if (op.kind === "merkle_proof") {
console.log(varName);
ctx.used(`__tact_load_merkle_proof`);
ctx.append(
`var (${name}'rootHash, ${name}'data) = __tact_load_merkle_proof(sc_${gen});`,
);
ctx.append(
`${varName} = (${name}'rootHash, ${ops.reader(op.dataType, ctx)}(${name}'data.begin_parse()));`,
);
return;
}

throw Error("Unsupported field kind: " + op.kind);
}
32 changes: 32 additions & 0 deletions src/generator/writers/writeStdlib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,38 @@ export function writeStdlib(ctx: WriterContext) {
ctx.skip("__tact_slice_to_str");
ctx.skip("__tact_address_to_slice");

//
// Extotic cell (libraries, merkle proofs, merkle updates)
//

ctx.fun("__tact_load_merkle_proof", () => {
ctx.signature(`(int, cell) __tact_load_merkle_proof(slice cs)`);
ctx.flag("inline");
ctx.context("stdlib");
ctx.body(() => {
ctx.write(`
cell proof_cell = cs~load_ref();
(slice cs, int exotic?) = proof_cell.begin_parse_exotic();
throw_unless(42, exotic?);
throw_unless(43, cs~load_uint(8) == 3);
return (cs~load_uint(256), cs~load_ref());
`);
});
});

ctx.fun("__tact_store_merkle_proof", () => {
ctx.signature(
`builder __tact_store_merkle_proof(builder b, int rootHash, cell data)`,
);
ctx.flag("inline");
ctx.context("stdlib");
ctx.body(() => {
ctx.write(`
return b.store_ref(begin_cell().store_uint(3, 8).store_uint(rootHash, 256).store_ref(data).end_cell_exotic());
`);
});
});

//
// Addresses
//
Expand Down
13 changes: 12 additions & 1 deletion src/grammar/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,18 @@ export type ASTTypeRefBounced = {
ref: ASTRef;
};

export type ASTTypeRef = ASTTypeRefSimple | ASTTypeRefMap | ASTTypeRefBounced;
export type ASTTypeRefMerkleProof = {
kind: "type_ref_merkle_proof";
id: number;
name: string;
ref: ASTRef;
};

export type ASTTypeRef =
| ASTTypeRefSimple
| ASTTypeRefMap
| ASTTypeRefBounced
| ASTTypeRefMerkleProof;

//
// Expressions
Expand Down
1 change: 1 addition & 0 deletions src/grammar/grammar.ohm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Tact {
| typeLiteral --required
| "map" "<" typeLiteral (as id)? "," typeLiteral (as id)? ">" --map
| "bounced" "<" typeLiteral ">" --bounced
| "merkleProof" "<" typeLiteral (as id)? ">" --merkleProof
Field = id ":" Type ";" --default
| id ":" Type "=" Expression ";" --defaultWithInit
| id ":" Type as id ";" --withSerialization
Expand Down
7 changes: 7 additions & 0 deletions src/grammar/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,13 @@ semantics.addOperation<ASTNode>("resolve_expression", {
ref: createRef(this),
});
},
Type_merkleProof(_arg0, _arg1, arg2, _arg3, _arg4, _arg5) {
return createNode({
kind: "type_ref_merkle_proof",
name: arg2.sourceString,
ref: createRef(this),
});
},

// Binary
ExpressionAdd_add(arg0, _arg1, arg2) {
Expand Down
3 changes: 2 additions & 1 deletion src/imports/stdlib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,8 @@ files['stdlib.fc'] =
'Ozs7IEVxdWl2YWxlbnQgdG8gcmFuZG9taXplKGN1cl9sdCgpKTsuCigpIHJhbmRvbWl6ZV9sdCgpIGltcHVyZSBhc20gIkxUSU1FIiAiQUREUkFORCI7Cgo7OzsgQ2hl' +
'Y2tzIHdoZXRoZXIgdGhlIGRhdGEgcGFydHMgb2YgdHdvIHNsaWNlcyBjb2luc2lkZQppbnQgZXF1YWxfc2xpY2VfYml0cyAoc2xpY2UgYSwgc2xpY2UgYikgYXNtICJT' +
'REVRIjsKCjs7OyBDb25jYXRlbmF0ZXMgdHdvIGJ1aWxkZXJzCmJ1aWxkZXIgc3RvcmVfYnVpbGRlcihidWlsZGVyIHRvLCBidWlsZGVyIGZyb20pIGFzbSAiU1RCUiI7' +
'Cg==';
'Cgooc2xpY2UsIGludCkgYmVnaW5fcGFyc2VfZXhvdGljKGNlbGwgYykgYXNtICJYQ1RPUyI7CmNlbGwgZW5kX2NlbGxfZXhvdGljKHNsaWNlIHMpIGFzbSAiRU5EWEMi' +
'Ow==';
files['stdlib.tact'] =
'aW1wb3J0ICIuL3N0ZC9wcmltaXRpdmVzIjsKaW1wb3J0ICIuL3N0ZC9jZWxscyI7CmltcG9ydCAiLi9zdGQvY3J5cHRvIjsKaW1wb3J0ICIuL3N0ZC90ZXh0IjsKaW1w' +
'b3J0ICIuL3N0ZC9tYXRoIjsKaW1wb3J0ICIuL3N0ZC9jb250cmFjdCI7CmltcG9ydCAiLi9zdGQvZGVidWciOwppbXBvcnQgIi4vc3RkL2NvbnRleHQiOwppbXBvcnQg' +
Expand Down
5 changes: 5 additions & 0 deletions src/packaging/fileFormat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export const typeFormat = z.union([
.optional()
.nullable(),
}),
z.object({
kind: z.literal("merkle"),
type: z.string(),
name: z.string(),
}),
]);

export const initFormat = z.object({
Expand Down
10 changes: 10 additions & 0 deletions src/storage/allocator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export function getOperationSize(src: AllocationOperationType): {
}
} else if (src.kind === "map") {
return { bits: 1, refs: 1 };
} else if (src.kind === "merkle_proof") {
return { bits: 0, refs: 1 };
} else if (src.kind === "struct") {
if (src.ref) {
if (src.optional) {
Expand Down Expand Up @@ -295,6 +297,14 @@ export function getAllocationOperationFromField(
return { kind: "map" };
}

// Merkle proof and update
if (src.kind === "merkle") {

Check failure on line 301 in src/storage/allocator.ts

View workflow job for this annotation

GitHub Actions / test (18.x, windows-latest)

Property 'kind' does not exist on type 'never'.
if (src.type === "proof") {

Check failure on line 302 in src/storage/allocator.ts

View workflow job for this annotation

GitHub Actions / test (18.x, windows-latest)

Property 'type' does not exist on type 'never'.
return { kind: "merkle_proof", dataType: src.name };

Check failure on line 303 in src/storage/allocator.ts

View workflow job for this annotation

GitHub Actions / test (18.x, windows-latest)

Property 'name' does not exist on type 'never'.
}
throw Error("Unsupported merkle type " + src.type);

Check failure on line 305 in src/storage/allocator.ts

View workflow job for this annotation

GitHub Actions / test (18.x, windows-latest)

Property 'type' does not exist on type 'never'.
}

throw new Error("Unsupported operation");
}

Expand Down
4 changes: 4 additions & 0 deletions src/storage/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export type AllocationOperationType =
| {
kind: "map";
}
| {
kind: "merkle_proof";
dataType: string;
}
| {
kind: "string";
optional: boolean;
Expand Down
38 changes: 38 additions & 0 deletions src/test/features/merkle-trees.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import "@stdlib/deploy";

struct AirdropEntry {
address: Address;
amount: Int as coins;
}

struct ProvableData {
justForTestPurposes: Int;
entries: map<Int, AirdropEntry>;
anoterOne: Int as uint32;
}

message ProcessClaim {
queryId: Int as uint64;
proof: merkleProof<ProvableData>;
index: Int as uint256;
}

contract MerkleTreesTestContract with Deployable {
rootHash: Int;

init(rootHash: Int) {
self.rootHash = rootHash;
}

receive(msg: ProcessClaim) {
require(msg.proof.rootHash == self.rootHash, "wrong merkle root");
let entry: AirdropEntry = msg.proof.data.entries.get(msg.index)!!;

send(SendParameters{
to: entry.address,
value: 0,
mode: SendIgnoreErrors,
body: entry.toCell()
});
}
}
18 changes: 18 additions & 0 deletions src/types/resolveABITypeRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,16 @@ export function resolveABIType(src: ASTField): ABITypeRef {
return { kind: "dict", key, keyFormat, value, valueFormat };
}

// Merkle Proof

if (src.type.kind === "type_ref_merkle_proof") {
return {
kind: "merkle",
type: "proof",
name: src.type.name,
};
}

throwError(`Unsupported type`, src.ref);
}

Expand Down Expand Up @@ -432,5 +442,13 @@ export function createABITypeRefFromTypeRef(
throw Error("Unexpected bounced reference");
}

if (src.kind === "merkle_proof") {
return {
kind: "merkle",
type: "proof",
name: src.name,
};
}

throw Error(`Unsupported type`);
}
Loading

0 comments on commit aaab72f

Please sign in to comment.