From 1d8a9611fe3f164c2c8722559ca25299f42570f4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 6 May 2024 18:08:06 +0200 Subject: [PATCH 1/5] add failing unit test for dynamic call --- src/lib/mina/test/dynamic-call.unit-test.ts | 90 +++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/lib/mina/test/dynamic-call.unit-test.ts diff --git a/src/lib/mina/test/dynamic-call.unit-test.ts b/src/lib/mina/test/dynamic-call.unit-test.ts new file mode 100644 index 0000000000..eb4381297b --- /dev/null +++ b/src/lib/mina/test/dynamic-call.unit-test.ts @@ -0,0 +1,90 @@ +import { Constructor } from 'src/bindings/lib/provable-generic.js'; +import { + Bool, + UInt64, + SmartContract, + method, + PublicKey, + Mina, +} from '../../../index.js'; + +/** + * Tests that we can call a subcontract dynamically based on the address + * as long as its signature matches the signature our contract was compiled against. + */ + +type Subcontract = SmartContract & { + submethod(a: UInt64, b: UInt64): Promise; +}; + +// two implementations with same signature of the called method, but different provable logic + +class SubcontractA extends SmartContract implements Subcontract { + @method.returns(Bool) + async submethod(a: UInt64, b: UInt64): Promise { + return a.greaterThan(b); + } +} + +class SubcontractB extends SmartContract implements Subcontract { + @method.returns(Bool) + async submethod(a: UInt64, b: UInt64): Promise { + return a.mul(b).equals(UInt64.from(42)); + } +} + +// caller contract that calls the subcontract + +class Caller extends SmartContract { + Subcontract: Constructor = SubcontractA; + + @method + async call(a: UInt64, b: UInt64, address: PublicKey) { + const subcontract = new this.Subcontract(address); + await subcontract.submethod(a, b); + } +} + +// test + +// setup + +let Local = await Mina.LocalBlockchain({ proofsEnabled: true }); +Mina.setActiveInstance(Local); + +let [sender, callerAccount, aAccount, bAccount] = Local.testAccounts; + +await SubcontractA.compile(); +await SubcontractB.compile(); +await Caller.compile(); + +let caller = new Caller(callerAccount); +let a = new SubcontractA(aAccount); +let b = new SubcontractB(bAccount); + +await Mina.transaction(sender, async () => { + await caller.deploy(); + await a.deploy(); + await b.deploy(); +}) + .sign([callerAccount.key, aAccount.key, bAccount.key, sender.key]) + .send(); + +// subcontract A call + +let x = UInt64.from(10); +let y = UInt64.from(5); + +await Mina.transaction(sender, () => caller.call(x, y, aAccount)) + .prove() + .sign([sender.key]) + .send(); + +// subcontract B call + +caller.Subcontract = SubcontractB; + +await Mina.transaction(sender, () => caller.call(x, y, bAccount)) + .prove() + .sign([sender.key]) + .send(); From 2a237e386bb38944ab3c5c984c7c4078686b88d5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 6 May 2024 18:31:03 +0200 Subject: [PATCH 2/5] fix some debugging code --- src/lib/mina/zkapp.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/lib/mina/zkapp.ts b/src/lib/mina/zkapp.ts index cea652cffb..786b81a17d 100644 --- a/src/lib/mina/zkapp.ts +++ b/src/lib/mina/zkapp.ts @@ -1612,13 +1612,23 @@ function diffRecursive( let { transaction, index, accountUpdate: input } = inputData; diff(transaction, index, prover.toPretty(), input.toPretty()); // TODO - let inputChildren = accountUpdateLayout()!.get(input)!.children.mutable!; - let proverChildren = accountUpdateLayout()!.get(prover)!.children.mutable!; + let proverChildren = accountUpdateLayout()?.get(prover)?.children.mutable; + if (proverChildren === undefined) return; + + // collect input children + let inputChildren: AccountUpdate[] = []; + let callDepth = input.body.callDepth; + for (let i = index; i < transaction.accountUpdates.length; i++) { + let update = transaction.accountUpdates[i]; + if (update.body.callDepth <= callDepth) break; + if (update.body.callDepth === callDepth + 1) inputChildren.push(update); + } + let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { - let inputChild = inputChildren[i].mutable; + let inputChild = inputChildren[i]; let child = proverChildren[i].mutable; - if (!inputChild || !child) return; + if (!child) return; diffRecursive(child, { transaction, index, accountUpdate: inputChild }); } } From 5a9c0b8e6fe8413d87d646b0d626e01e0b17a922 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 6 May 2024 21:10:23 +0200 Subject: [PATCH 3/5] fix test --- src/lib/mina/test/dynamic-call.unit-test.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/mina/test/dynamic-call.unit-test.ts b/src/lib/mina/test/dynamic-call.unit-test.ts index eb4381297b..006bf9ef5e 100644 --- a/src/lib/mina/test/dynamic-call.unit-test.ts +++ b/src/lib/mina/test/dynamic-call.unit-test.ts @@ -1,4 +1,3 @@ -import { Constructor } from 'src/bindings/lib/provable-generic.js'; import { Bool, UInt64, @@ -35,12 +34,12 @@ class SubcontractB extends SmartContract implements Subcontract { // caller contract that calls the subcontract -class Caller extends SmartContract { - Subcontract: Constructor = SubcontractA; +let Subcontract: new (...args: any) => Subcontract = SubcontractA; +class Caller extends SmartContract { @method async call(a: UInt64, b: UInt64, address: PublicKey) { - const subcontract = new this.Subcontract(address); + const subcontract = new Subcontract(address); await subcontract.submethod(a, b); } } @@ -82,7 +81,7 @@ await Mina.transaction(sender, () => caller.call(x, y, aAccount)) // subcontract B call -caller.Subcontract = SubcontractB; +Subcontract = SubcontractB; await Mina.transaction(sender, () => caller.call(x, y, bAccount)) .prove() From 8d7a2e06587269eede8219de120ea43fd36a6390 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 6 May 2024 21:18:16 +0200 Subject: [PATCH 4/5] make test prettier --- src/lib/mina/test/dynamic-call.unit-test.ts | 23 ++++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/lib/mina/test/dynamic-call.unit-test.ts b/src/lib/mina/test/dynamic-call.unit-test.ts index 006bf9ef5e..51238215ea 100644 --- a/src/lib/mina/test/dynamic-call.unit-test.ts +++ b/src/lib/mina/test/dynamic-call.unit-test.ts @@ -1,3 +1,9 @@ +/** + * Tests that shows we can call a subcontract dynamically based on the address, + * as long as its signature matches the signature our contract was compiled against. + * + * In other words, the exact implementation/constraints of zkApp methods we call are not hard-coded in the caller contract. + */ import { Bool, UInt64, @@ -7,11 +13,6 @@ import { Mina, } from '../../../index.js'; -/** - * Tests that we can call a subcontract dynamically based on the address - * as long as its signature matches the signature our contract was compiled against. - */ - type Subcontract = SmartContract & { submethod(a: UInt64, b: UInt64): Promise; }; @@ -34,17 +35,19 @@ class SubcontractB extends SmartContract implements Subcontract { // caller contract that calls the subcontract -let Subcontract: new (...args: any) => Subcontract = SubcontractA; - class Caller extends SmartContract { @method async call(a: UInt64, b: UInt64, address: PublicKey) { - const subcontract = new Subcontract(address); + const subcontract = new Caller.Subcontract(address); await subcontract.submethod(a, b); } + + // subcontract to call. this property is changed below + // TODO: having to set this property is a hack, it would be nice to + static Subcontract: new (...args: any) => Subcontract = SubcontractA; } -// test +// TEST BELOW // setup @@ -81,7 +84,7 @@ await Mina.transaction(sender, () => caller.call(x, y, aAccount)) // subcontract B call -Subcontract = SubcontractB; +Caller.Subcontract = SubcontractB; await Mina.transaction(sender, () => caller.call(x, y, bAccount)) .prove() From 271ec6fb2eb72dde895f0459b7bdf226d21f6af4 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Mon, 6 May 2024 22:32:53 +0200 Subject: [PATCH 5/5] Update src/lib/mina/test/dynamic-call.unit-test.ts --- src/lib/mina/test/dynamic-call.unit-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/mina/test/dynamic-call.unit-test.ts b/src/lib/mina/test/dynamic-call.unit-test.ts index 51238215ea..97ef19d9d3 100644 --- a/src/lib/mina/test/dynamic-call.unit-test.ts +++ b/src/lib/mina/test/dynamic-call.unit-test.ts @@ -43,7 +43,7 @@ class Caller extends SmartContract { } // subcontract to call. this property is changed below - // TODO: having to set this property is a hack, it would be nice to + // TODO: having to set this property is a hack, it would be nice to pass the contract as parameter static Subcontract: new (...args: any) => Subcontract = SubcontractA; }