From 4ac50498bd483fa29b88b47e4c97ddf8b554dc07 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 30 Oct 2023 15:29:53 +0100 Subject: [PATCH 001/524] bump bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 864cb03859..0d36b882ae 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 864cb038593cdad62ce7101990050f03b9d261be +Subproject commit 0d36b882ae8942dedd1506c1c9c3dbc6981ec7db From 7268251f0f0c48f855dba6ef807e67504cc78c4c Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 30 Oct 2023 15:30:23 +0100 Subject: [PATCH 002/524] add txnVersion to verification key permission --- src/lib/account_update.ts | 37 +++++++++++++++---- src/lib/mina.ts | 5 ++- src/lib/mina/account.ts | 5 ++- src/mina-signer/src/sign-zkapp-command.ts | 2 + .../src/test-vectors/accountUpdate.ts | 5 ++- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index c1518b1cfa..930ad181af 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -26,7 +26,11 @@ import { } from '../bindings/mina-transaction/transaction-leaves.js'; import { TokenId as Base58TokenId } from './base58-encodings.js'; import { hashWithPrefix, packToFields } from './hash.js'; -import { mocks, prefixes } from '../bindings/crypto/constants.js'; +import { + mocks, + prefixes, + protocolVersions, +} from '../bindings/crypto/constants.js'; import { Context } from './global-context.js'; import { assert } from './errors.js'; import { MlArray } from './ml/base.js'; @@ -63,6 +67,10 @@ export { const ZkappStateLength = 8; +const TxnVersion = { + current: () => UInt32.from(512), +}; + type SmartContractContext = { this: SmartContract; methodCallDepth: number; @@ -199,7 +207,10 @@ interface Permissions extends Permissions_ { * key associated with the circuit tied to this account. Effectively * "upgradeability" of the smart contract. */ - setVerificationKey: Permission; + setVerificationKey: { + auth: Permission; + txnVersion: UInt32; + }; /** * The {@link Permission} corresponding to the ability to set the zkapp uri @@ -265,7 +276,10 @@ let Permissions = { receive: Permission.none(), setDelegate: Permission.signature(), setPermissions: Permission.signature(), - setVerificationKey: Permission.signature(), + setVerificationKey: { + auth: Permission.signature(), + txnVersion: TxnVersion.current(), + }, setZkappUri: Permission.signature(), editActionState: Permission.proof(), setTokenSymbol: Permission.signature(), @@ -281,7 +295,10 @@ let Permissions = { receive: Permission.none(), setDelegate: Permission.signature(), setPermissions: Permission.signature(), - setVerificationKey: Permission.signature(), + setVerificationKey: { + auth: Permission.signature(), + txnVersion: TxnVersion.current(), + }, setZkappUri: Permission.signature(), editActionState: Permission.signature(), setTokenSymbol: Permission.signature(), @@ -298,7 +315,10 @@ let Permissions = { access: Permission.none(), setDelegate: Permission.none(), setPermissions: Permission.none(), - setVerificationKey: Permission.none(), + setVerificationKey: { + auth: Permission.signature(), + txnVersion: TxnVersion.current(), + }, setZkappUri: Permission.none(), editActionState: Permission.none(), setTokenSymbol: Permission.none(), @@ -314,7 +334,10 @@ let Permissions = { access: Permission.impossible(), setDelegate: Permission.impossible(), setPermissions: Permission.impossible(), - setVerificationKey: Permission.impossible(), + setVerificationKey: { + auth: Permission.signature(), + txnVersion: TxnVersion.current(), + }, setZkappUri: Permission.impossible(), editActionState: Permission.impossible(), setTokenSymbol: Permission.impossible(), @@ -350,7 +373,7 @@ let Permissions = { return Object.fromEntries( Object.entries(permissions).map(([k, v]) => [ k, - Permissions.fromString(v), + Permissions.fromString(typeof v === 'string' ? v : v.auth), ]) ) as unknown as Permissions; }, diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 505d917d04..3f264976a0 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -300,6 +300,9 @@ function newTransaction(transaction: ZkappCommand, proofsEnabled?: boolean) { let self: Transaction = { transaction, sign(additionalKeys?: PrivateKey[]) { + self.transaction.accountUpdates.forEach( + (a) => (a.body.callData = Field(5)) + ); self.transaction = addMissingSignatures(self.transaction, additionalKeys); return self; }, @@ -1280,7 +1283,7 @@ async function verifyAccountUpdate( case 'delegate': return perm.setDelegate; case 'verificationKey': - return perm.setVerificationKey; + return perm.setVerificationKey.auth; case 'permissions': return perm.setPermissions; case 'zkappUri': diff --git a/src/lib/mina/account.ts b/src/lib/mina/account.ts index c45d38587b..0969bedde3 100644 --- a/src/lib/mina/account.ts +++ b/src/lib/mina/account.ts @@ -44,7 +44,10 @@ type FetchedAccount = { receive: AuthRequired; setDelegate: AuthRequired; setPermissions: AuthRequired; - setVerificationKey: AuthRequired; + setVerificationKey: { + auth: AuthRequired; + txnVersion: string; + }; setZkappUri: AuthRequired; editActionState: AuthRequired; setTokenSymbol: AuthRequired; diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 69578ed193..43e13b08ca 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -119,6 +119,8 @@ function transactionCommitments(zkappCommand: ZkappCommand) { feePayerDigest, commitment, ]); + console.log('commitment', commitment); + console.log('fullCommitment', fullCommitment); return { commitment, fullCommitment }; } diff --git a/src/mina-signer/src/test-vectors/accountUpdate.ts b/src/mina-signer/src/test-vectors/accountUpdate.ts index cb8817b9d4..76eaaa662b 100644 --- a/src/mina-signer/src/test-vectors/accountUpdate.ts +++ b/src/mina-signer/src/test-vectors/accountUpdate.ts @@ -19,7 +19,10 @@ let accountUpdateExample: Json.AccountUpdate = { receive: 'Proof', setDelegate: 'Signature', setPermissions: 'None', - setVerificationKey: 'None', + setVerificationKey: { + auth: 'None', + txnVersion: '3', + }, setZkappUri: 'Signature', editActionState: 'Proof', setTokenSymbol: 'Signature', From 326208ab9a7ec7e5077c2bf077deacd18c62d8bc Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 30 Oct 2023 16:24:40 +0100 Subject: [PATCH 003/524] add generated constant --- src/lib/account_update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 930ad181af..fe5cf41e4c 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -68,7 +68,7 @@ export { const ZkappStateLength = 8; const TxnVersion = { - current: () => UInt32.from(512), + current: () => UInt32.from(protocolVersions.txnVersion), }; type SmartContractContext = { From 84d969cdddae66cb55045b025f5c1a44b3dec888 Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 30 Oct 2023 16:25:33 +0100 Subject: [PATCH 004/524] remove debug code --- src/lib/mina.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 3f264976a0..e115ef7f12 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -300,9 +300,6 @@ function newTransaction(transaction: ZkappCommand, proofsEnabled?: boolean) { let self: Transaction = { transaction, sign(additionalKeys?: PrivateKey[]) { - self.transaction.accountUpdates.forEach( - (a) => (a.body.callData = Field(5)) - ); self.transaction = addMissingSignatures(self.transaction, additionalKeys); return self; }, From d4f6252681affcf73ded88b4e454ad581324bcad Mon Sep 17 00:00:00 2001 From: Florian Date: Mon, 30 Oct 2023 17:47:57 +0100 Subject: [PATCH 005/524] add custom js deriver --- src/bindings | 2 +- src/lib/account_update.ts | 1 + src/lib/account_update.unit-test.ts | 2 ++ src/lib/testing/random.ts | 2 ++ src/mina-signer/src/sign-zkapp-command.ts | 2 -- 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/bindings b/src/bindings index 0d36b882ae..3809752bc4 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 0d36b882ae8942dedd1506c1c9c3dbc6981ec7db +Subproject commit 3809752bc43a77d18f7a67eaddf7ea0671312c60 diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index fe5cf41e4c..a416fd1772 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -718,6 +718,7 @@ class AccountUpdate implements Types.AccountUpdate { this, isSelf ); + this.account = account; this.network = network; this.currentSlot = currentSlot; diff --git a/src/lib/account_update.unit-test.ts b/src/lib/account_update.unit-test.ts index a280b22712..ed35a7ceef 100644 --- a/src/lib/account_update.unit-test.ts +++ b/src/lib/account_update.unit-test.ts @@ -30,6 +30,8 @@ function createAccountUpdate() { // convert accountUpdate to fields in pure JS, leveraging generated code let fields2 = Types.AccountUpdate.toFields(accountUpdate); + console.log(json); + // this is useful console output in the case the test should fail if (fields1.length !== fields2.length) { console.log( diff --git a/src/lib/testing/random.ts b/src/lib/testing/random.ts index b334f61523..c23d52589a 100644 --- a/src/lib/testing/random.ts +++ b/src/lib/testing/random.ts @@ -135,6 +135,7 @@ const Generators: Generators = { null: constant(null), string: base58(nat(50)), // TODO replace various strings, like signature, with parsed types number: nat(3), + TransactionVersion: uint32, }; let typeToBigintGenerator = new Map, Random>( [TypeMap, PrimitiveMap, customTypes] @@ -238,6 +239,7 @@ const JsonGenerators: JsonGenerators = { null: constant(null), string: base58(nat(50)), number: nat(3), + TransactionVersion: json_.uint32, }; let typeToJsonGenerator = new Map, Random>( [TypeMap, PrimitiveMap, customTypes] diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 43e13b08ca..69578ed193 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -119,8 +119,6 @@ function transactionCommitments(zkappCommand: ZkappCommand) { feePayerDigest, commitment, ]); - console.log('commitment', commitment); - console.log('fullCommitment', fullCommitment); return { commitment, fullCommitment }; } From 4de770dc5442720740640762517af0ab7e4f94a5 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 31 Oct 2023 14:14:48 +0100 Subject: [PATCH 006/524] add TransactionVersion --- src/index.ts | 1 + src/lib/account_update.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 0813d3da4d..5e684ddb03 100644 --- a/src/index.ts +++ b/src/index.ts @@ -51,6 +51,7 @@ export { AccountUpdate, Permissions, ZkappPublicInput, + TransactionVersion, } from './lib/account_update.js'; export type { TransactionStatus } from './lib/fetch.js'; diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index a416fd1772..ac94a8f091 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -39,7 +39,7 @@ import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; // external API -export { AccountUpdate, Permissions, ZkappPublicInput }; +export { AccountUpdate, Permissions, ZkappPublicInput, TransactionVersion }; // internal API export { smartContractContext, @@ -67,7 +67,7 @@ export { const ZkappStateLength = 8; -const TxnVersion = { +const TransactionVersion = { current: () => UInt32.from(protocolVersions.txnVersion), }; @@ -278,7 +278,7 @@ let Permissions = { setPermissions: Permission.signature(), setVerificationKey: { auth: Permission.signature(), - txnVersion: TxnVersion.current(), + txnVersion: TransactionVersion.current(), }, setZkappUri: Permission.signature(), editActionState: Permission.proof(), @@ -297,7 +297,7 @@ let Permissions = { setPermissions: Permission.signature(), setVerificationKey: { auth: Permission.signature(), - txnVersion: TxnVersion.current(), + txnVersion: TransactionVersion.current(), }, setZkappUri: Permission.signature(), editActionState: Permission.signature(), @@ -317,7 +317,7 @@ let Permissions = { setPermissions: Permission.none(), setVerificationKey: { auth: Permission.signature(), - txnVersion: TxnVersion.current(), + txnVersion: TransactionVersion.current(), }, setZkappUri: Permission.none(), editActionState: Permission.none(), @@ -336,7 +336,7 @@ let Permissions = { setPermissions: Permission.impossible(), setVerificationKey: { auth: Permission.signature(), - txnVersion: TxnVersion.current(), + txnVersion: TransactionVersion.current(), }, setZkappUri: Permission.impossible(), editActionState: Permission.impossible(), From ec32d2a639298f31b5589ce1395757484e2ed3d5 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 31 Oct 2023 14:15:13 +0100 Subject: [PATCH 007/524] adjust examples to work with minimal solution --- src/bindings | 2 +- src/examples/zkapps/dex/upgradability.ts | 6 +++++- src/examples/zkapps/voting/dummyContract.ts | 6 +++++- src/examples/zkapps/voting/membership.ts | 6 +++++- src/examples/zkapps/voting/voting.ts | 6 +++++- src/examples/zkapps/zkapp-self-update.ts | 7 ++++++- 6 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/bindings b/src/bindings index 3809752bc4..5f9f3a3a30 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 3809752bc43a77d18f7a67eaddf7ea0671312c60 +Subproject commit 5f9f3a3a30d3e98e7f42764ddf4d5c3b9db45a9e diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 31d4812a51..2cee0e003c 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -5,6 +5,7 @@ import { Permissions, PrivateKey, UInt64, + TransactionVersion, } from 'snarkyjs'; import { createDex, TokenContract, addresses, keys, tokenIds } from './dex.js'; import { expect } from 'expect'; @@ -427,7 +428,10 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { let update = AccountUpdate.createSigned(addresses.dex); update.account.permissions.set({ ...Permissions.initial(), - setVerificationKey: Permissions.impossible(), + setVerificationKey: { + auth: Permissions.impossible(), + txnVersion: TransactionVersion.current(), + }, }); }); await tx.prove(); diff --git a/src/examples/zkapps/voting/dummyContract.ts b/src/examples/zkapps/voting/dummyContract.ts index e2ca2d1068..bc543d85a0 100644 --- a/src/examples/zkapps/voting/dummyContract.ts +++ b/src/examples/zkapps/voting/dummyContract.ts @@ -6,6 +6,7 @@ import { method, DeployArgs, Permissions, + TransactionVersion, } from 'snarkyjs'; export class DummyContract extends SmartContract { @@ -18,7 +19,10 @@ export class DummyContract extends SmartContract { editState: Permissions.proofOrSignature(), editActionState: Permissions.proofOrSignature(), setPermissions: Permissions.proofOrSignature(), - setVerificationKey: Permissions.proofOrSignature(), + setVerificationKey: { + auth: Permissions.signature(), + txnVersion: TransactionVersion.current(), + }, incrementNonce: Permissions.proofOrSignature(), }); this.sum.set(Field(0)); diff --git a/src/examples/zkapps/voting/membership.ts b/src/examples/zkapps/voting/membership.ts index 8a7d38611d..42e1609c0b 100644 --- a/src/examples/zkapps/voting/membership.ts +++ b/src/examples/zkapps/voting/membership.ts @@ -12,6 +12,7 @@ import { provablePure, AccountUpdate, Provable, + TransactionVersion, } from 'snarkyjs'; import { Member } from './member.js'; import { ParticipantPreconditions } from './preconditions.js'; @@ -74,7 +75,10 @@ export class Membership_ extends SmartContract { editState: Permissions.proofOrSignature(), editActionState: Permissions.proofOrSignature(), setPermissions: Permissions.proofOrSignature(), - setVerificationKey: Permissions.proofOrSignature(), + setVerificationKey: { + auth: Permissions.proofOrSignature(), + txnVersion: TransactionVersion.current(), + }, incrementNonce: Permissions.proofOrSignature(), }); } diff --git a/src/examples/zkapps/voting/voting.ts b/src/examples/zkapps/voting/voting.ts index 2cef7197c3..034eaf3f93 100644 --- a/src/examples/zkapps/voting/voting.ts +++ b/src/examples/zkapps/voting/voting.ts @@ -12,6 +12,7 @@ import { provablePure, AccountUpdate, Provable, + TransactionVersion, } from 'snarkyjs'; import { Member } from './member.js'; @@ -103,7 +104,10 @@ export class Voting_ extends SmartContract { editState: Permissions.proofOrSignature(), editActionState: Permissions.proofOrSignature(), incrementNonce: Permissions.proofOrSignature(), - setVerificationKey: Permissions.none(), + setVerificationKey: { + auth: Permissions.none(), + txnVersion: TransactionVersion.current(), + }, setPermissions: Permissions.proofOrSignature(), }); this.accumulatedVotes.set(Reducer.initialActionState); diff --git a/src/examples/zkapps/zkapp-self-update.ts b/src/examples/zkapps/zkapp-self-update.ts index 1400c6554a..9baa9770ce 100644 --- a/src/examples/zkapps/zkapp-self-update.ts +++ b/src/examples/zkapps/zkapp-self-update.ts @@ -12,6 +12,8 @@ import { AccountUpdate, Circuit, Provable, + TransactionVersion, + UInt32, } from 'snarkyjs'; class Foo extends SmartContract { @@ -19,7 +21,10 @@ class Foo extends SmartContract { super.init(); this.account.permissions.set({ ...Permissions.default(), - setVerificationKey: Permissions.proof(), + setVerificationKey: { + auth: Permissions.proof(), + txnVersion: TransactionVersion.current(), + }, }); } From 3785f9bae532285fb5d30ff3f91ed5f90d0899ab Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 31 Oct 2023 14:51:31 +0100 Subject: [PATCH 008/524] dump vks --- src/examples/regression_test.json | 94 +++++++++++++++---------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/examples/regression_test.json b/src/examples/regression_test.json index c8916cc0c4..ac928854b5 100644 --- a/src/examples/regression_test.json +++ b/src/examples/regression_test.json @@ -1,139 +1,139 @@ { "Voting_": { - "digest": "2f0ca1dca22d7af925d31d23f3d4e37686ec1755a9f069063ae3b6300f6b3920", + "digest": "3d94f280d3d143b91564d659073b641b21f6e0ca8d8549c4f9f37434ebeea399", "methods": { "voterRegistration": { "rows": 1260, - "digest": "5d6fa3b1f0f781fba8894da098827088" + "digest": "9f1d08069b07c165fe425b9b1b83d3ba" }, "candidateRegistration": { "rows": 1260, - "digest": "abf93b4d926a8743bf622da4ee0f88b2" + "digest": "6e9e689b9d1bd2eedcc323c842cf8c87" }, "approveRegistrations": { - "rows": 1146, - "digest": "197ce95e1895f940278034d20ab24274" + "rows": 1147, + "digest": "6189a2bf4c618401d61d30aac6328cea" }, "vote": { - "rows": 1674, - "digest": "a0dd0be93734dc39d52fc7d60d0f0bab" + "rows": 1675, + "digest": "ab1190db893274db9cd64d8c306d8b4a" }, "countVotes": { "rows": 5797, - "digest": "9c5503e03dcbb6b04c907eb1da43e26e" + "digest": "cf02e1cd65980bafaf249c0444bfddf3" } }, "verificationKey": { - "data": "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", - "hash": "11565671297372915311669581079391063231397733208021234592662736001743584177724" + "data": "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", + "hash": "3937169542001515602242969811817979001997698429197375219350451752328949933498" } }, "Membership_": { - "digest": "fb87402442d6984de28d7bab62deb87ab8a7f46d5c2d59da4980ae6927dd1ec", + "digest": "110319a1c90f943e63a60a60b414cb4c5c807238a108f221127cf1470253e8b9", "methods": { "addEntry": { "rows": 1354, - "digest": "bdb5dd653383ec699e25e418135aeec0" + "digest": "f44d9087799be73eb45d70624acbe2e5" }, "isMember": { "rows": 470, - "digest": "f1b63907b48c40cd01b1d34166fa86eb" + "digest": "dc1d26c7e30d3e14bd0d562685846026" }, "publish": { "rows": 695, - "digest": "c6be510154c0e624a41756ad349df9de" + "digest": "358a5f723f262cc3a429632a08366782" } }, "verificationKey": { - "data": "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", - "hash": "11513384784058506063139870141763328684986961462198948028100954667309086537868" + "data": "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", + "hash": "9996223880170732158764822649803321417212830666367673158223414893469449014464" } }, "HelloWorld": { - "digest": "5efddf140c592bb0bfd51d9c77252bcc0c0fab1fbd7fbaf85bd3bb051a96596", + "digest": "1f5a4490d9660960367eba05253fb1ee5a8c19ec4cfaa3808a0a3bb021fb49e4", "methods": { "update": { "rows": 2352, - "digest": "217224955830a2c0db65a6dcc91cae29" + "digest": "5fd358199036f97e4d5dd88b8c90f644" } }, "verificationKey": { - "data": "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", - "hash": "16374078687847242751733960472737775252305759960836159362409166419576402399087" + "data": "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", + "hash": "5137663734069454542185980113506904745018869331949819257646538371828146355743" } }, "TokenContract": { - "digest": "10ccc8e9c4a5788084a73993036c15ea25a5b2643ad06b76614de7e2910b2cc", + "digest": "1626d65d9dddb02c2efb061ccd86dde1be0eb2da1f6d2a9d5fc1dd67f6a13a69", "methods": { "init": { "rows": 656, - "digest": "b6debf22e710ad6bfb67177517ed66ea" + "digest": "69166c148367fb957d6ee9ccc88e32bf" }, "init2": { "rows": 653, - "digest": "b5ac64e93cd25de68a96f1a7b8cee9aa" + "digest": "d37b3b719bbc6bafe7569bf00867d45d" }, "deployZkapp": { "rows": 703, - "digest": "9b45cc038648bab83ea14ed64b08aa79" + "digest": "e9f389959f9e151a157e067457f65ad9" }, "approveUpdate": { - "rows": 1929, - "digest": "5ededd9323d9347dc5c91250c08b5206" + "rows": 1944, + "digest": "4c139968037dddff3d5b96b2c2b7f7d6" }, "approveAny": { - "rows": 1928, - "digest": "b34bc95ca48bfc7fc09f8d1b84215389" + "rows": 1943, + "digest": "10540506937a8e900702c11166a28851" }, "approveUpdateAndSend": { - "rows": 2322, - "digest": "7359c73a15841a7c958b97cc86da58e6" + "rows": 2325, + "digest": "6f3a192b42c9b9543833d1419f1cc5fa" }, "transferToAddress": { "rows": 1045, - "digest": "79bdb6579a9f42dc4ec2d3e9ceedbe1c" + "digest": "5563c415f4cbc4e55deee27b28cc2b36" }, "transferToUpdate": { - "rows": 2327, - "digest": "f7297e43a8082e4677855bca9a206a88" + "rows": 2330, + "digest": "c01ade54a6dd1c2b8727cabdf6bdb239" }, "getBalance": { "rows": 687, - "digest": "738dbad00d454b34d06dd87a346aff11" + "digest": "709f06f2e08cfe43ddf3ccbd60e642c8" } }, "verificationKey": { - "data": "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", - "hash": "19471120960548999816351394077166332230185600464930675317083534065611063009411" + "data": "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", + "hash": "12366494402222742422656395986651384766285850064146879115569953837710228942559" } }, "Dex": { - "digest": "2039439604f1b4a88563bd34178bb380c0d998cc662b8fe9b9ea1164eefe70c7", + "digest": "3d52c586067f910c9e2d2c4845f87bc8a94ecae0f29173925a3c84ac792fb4c9", "methods": { "supplyLiquidityBase": { - "rows": 3752, - "digest": "8d00a233c53340e6ef8898bb40ad919c" + "rows": 3754, + "digest": "94cc0fcf15c730ce5985eab050af69ee" }, "swapX": { - "rows": 1986, - "digest": "23a2ddbc46117681d58c03a130cfee3f" + "rows": 1987, + "digest": "b19ba33a99fce487fa57a26d4f94acef" }, "swapY": { - "rows": 1986, - "digest": "c218b1e81ac390ba81678a5be89bc7a7" + "rows": 1987, + "digest": "4de0b8ee16a3d16c1c35ca35b7afb5b3" }, "burnLiquidity": { "rows": 719, - "digest": "63d13fc14775e43e4f6dd6245b74d516" + "digest": "2efa6087ee0f835cd54af2f2be520738" }, "transfer": { "rows": 1045, - "digest": "dd6f98961085734ff5ec578746db3a53" + "digest": "f884b397fd90eb23d5687d9540fd2ba9" } }, "verificationKey": { - "data": "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", - "hash": "14012766312159048636010037107657830276597168503405582242210403680501924916774" + "data": "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", + "hash": "25368023113388663131248907317546981854837405077704880729120589306824127621207" } }, "Group Primitive": { From b58bb48156790f77358c194f86a8349b6828edd1 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Mon, 27 Nov 2023 14:52:57 +0100 Subject: [PATCH 009/524] remove debug code --- src/lib/account_update.unit-test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/account_update.unit-test.ts b/src/lib/account_update.unit-test.ts index ed35a7ceef..a280b22712 100644 --- a/src/lib/account_update.unit-test.ts +++ b/src/lib/account_update.unit-test.ts @@ -30,8 +30,6 @@ function createAccountUpdate() { // convert accountUpdate to fields in pure JS, leveraging generated code let fields2 = Types.AccountUpdate.toFields(accountUpdate); - console.log(json); - // this is useful console output in the case the test should fail if (fields1.length !== fields2.length) { console.log( From cc46f5b0cd709bd713a1480fcb160be49d166db7 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 1 Dec 2023 18:29:00 +0100 Subject: [PATCH 010/524] it works --- src/examples/sha256.ts | 208 ++++++++++++++++++++++++++++++++++++++ src/lib/gadgets/sha256.ts | 166 ++++++++++++++++++++++++++++++ src/lib/int.ts | 16 +-- 3 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 src/examples/sha256.ts create mode 100644 src/lib/gadgets/sha256.ts diff --git a/src/examples/sha256.ts b/src/examples/sha256.ts new file mode 100644 index 0000000000..41ef423716 --- /dev/null +++ b/src/examples/sha256.ts @@ -0,0 +1,208 @@ +// https://csrc.nist.gov/pubs/fips/180-4/upd1/final + +import { UInt32, Provable } from 'o1js'; +import { bitsToBytes } from 'src/bindings/lib/binable.js'; + +export { SHA256 }; + +function processStringToMessageBlocks(s: string) { + let msgBits = s + .split('') + .map((c) => { + let binary = c.charCodeAt(0).toString(2); + return '00000000'.substr(binary.length) + binary; + }) + .join(''); + + let l = msgBits.length; + msgBits = msgBits + '1'; + console.log('msgBits:', msgBits.length); + + // calculate k in l + 1 +k = 448 mod 512 + let remainder = (448 - (l + 1)) % 512; + + let k = (remainder + 512) % 512; + console.log(k); + let padding = '0'.repeat(k); + msgBits = msgBits + padding; + let lBits = l.toString(2); + msgBits = msgBits + '0'.repeat(64 - lBits.length) + lBits; + console.log('msgBits:', msgBits.length); + let bitBlocks32 = []; + for (let i = 0; i < msgBits.length; i += 32) { + bitBlocks32.push(UInt32.from(BigInt('0b' + msgBits.substr(i, 32)))); + } + + let lengthBlocks = bitBlocks32.length; + let blocks = []; + for (let i = 0; i < lengthBlocks; i += 16) { + let block = bitBlocks32.slice(i, i + 16); + blocks.push(block); + } + return blocks; +} + +// constants §4.2.2 +const K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +].map((k) => UInt32.from(k)); + +// initial hash values §5.3.3 +const H = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19, +].map((h) => UInt32.from(h)); + +const SHA256 = { + hash(msg: string) { + // preprocessing §6.2 + // padding the message $5.1.1 into blocks that are a multiple of 512 + let messageBlocks = Provable.witness( + Provable.Array(Provable.Array(UInt32, 16), 6), + () => { + console.log('hashing <' + msg + '>'); + return processStringToMessageBlocks(msg); + } + ); + + const N = messageBlocks.length; + + for (let i = 0; i < N; i++) { + const M = messageBlocks[i]; + // for each message block of 16 x 32 bytes do: + const W = new Array(64); + + // prepare message block + for (let t = 0; t <= 15; t++) W[t] = M[t]; + for (let t = 16; t <= 63; t++) { + W[t] = DeltaOne(W[t - 2]) + .addMod32(W[t - 7]) + .addMod32(DeltaZero(W[t - 15]).addMod32(W[t - 16])); + } + // initialize working variables + let a = H[0]; + let b = H[1]; + let c = H[2]; + let d = H[3]; + let e = H[4]; + let f = H[5]; + let g = H[6]; + let h = H[7]; + + // main loop + for (let t = 0; t <= 63; t++) { + const T1 = h + .addMod32(SigmaOne(e)) + .addMod32(Ch(e, f, g)) + .addMod32(K[t]) + .addMod32(W[t]); + + const T2 = SigmaZero(a).addMod32(Maj(a, b, c)); + + h = g; + g = f; + f = e; + e = d.addMod32(T1); + d = c; + c = b; + b = a; + a = T1.addMod32(T2); + } + + // new intermediate hash value + + H[0] = H[0].addMod32(a); + H[1] = H[1].addMod32(b); + H[2] = H[2].addMod32(c); + H[3] = H[3].addMod32(d); + H[4] = H[4].addMod32(e); + H[5] = H[5].addMod32(f); + H[6] = H[6].addMod32(g); + H[7] = H[7].addMod32(h); + } + + Provable.asProver(() => { + let hex = ''; + for (let h = 0; h < H.length; h++) + hex = hex + ('00000000' + H[h].toBigint().toString(16)).slice(-8); + console.log('sha256 digest:', hex); + }); + }, +}; + +Provable.runAndCheck(() => + SHA256.hash( + 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest' + ) +); +let cs = Provable.constraintSystem(() => SHA256.hash('test')); +console.log('rows:', cs.rows); +/* function toUInt8(msg: string) { + return msg.split('').map((c) => UInt8.from(c.charCodeAt(0))); +} */ + +function Ch(x: UInt32, y: UInt32, z: UInt32) { + let xAndY = x.and(y); + let xNotAndZ = x.not().and(z); + return xAndY.xor(xNotAndZ); +} + +function Maj(x: UInt32, y: UInt32, z: UInt32) { + let xAndY = x.and(y); + let xAndZ = x.and(z); + let yAndZ = y.and(z); + + return xAndY.xor(xAndZ).xor(yAndZ); +} + +function SigmaZero(x: UInt32) { + let rotr2 = ROTR(2, x); + let rotr13 = ROTR(13, x); + let rotr22 = ROTR(22, x); + + return rotr2.xor(rotr13).xor(rotr22); +} + +function SigmaOne(x: UInt32) { + let rotr6 = ROTR(6, x); + let rotr11 = ROTR(11, x); + let rotr25 = ROTR(25, x); + + return rotr6.xor(rotr11).xor(rotr25); +} + +// lowercase sigma = delta to avoid confusing function names + +function DeltaZero(x: UInt32) { + let rotr7 = ROTR(7, x); + let rotr18 = ROTR(18, x); + let shr3 = SHR(3, x); + + return rotr7.xor(rotr18).xor(shr3); +} + +function DeltaOne(x: UInt32) { + let rotr17 = ROTR(17, x); + let rotr19 = ROTR(19, x); + let shr10 = SHR(10, x); + return rotr17.xor(rotr19).xor(shr10); +} + +function ROTR(n: number, x: UInt32) { + return x.rotate(n, 'right'); +} + +function SHR(n: number, x: UInt32) { + let val = x.rightShift(n); + return val; +} diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts new file mode 100644 index 0000000000..3693b82bb1 --- /dev/null +++ b/src/lib/gadgets/sha256.ts @@ -0,0 +1,166 @@ +// https://csrc.nist.gov/pubs/fips/180-4/upd1/final + +import { UInt32 } from '../int.js'; +import { Provable } from '../provable.js'; + +export { SHA256 }; + +// constants §4.2.2 +const K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +].map((k) => UInt32.from(k)); + +// initial hash values §5.3.3 +const H = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19, +].map((h) => UInt32.from(h)); + +const SHA256 = { + hash(msg: string) { + // preprocessing §6.2 + // padding the message $5.1.1 into blocks that are a multiple of 512 + let l = 8 * 8; + let k = 448 - (l + 1); + let padding = [1, ...new Array(k).fill(0)]; + + let messageBlocks = [ + // M1 + new Array(16).fill(UInt32.from(1)), + new Array(16).fill(UInt32.from(1)), + ]; + + for (let i = 0; i < messageBlocks.length; i++) { + const M = messageBlocks[i]; + // for each message block of 16 x 32 bytes do: + const W = new Array(64); + + // prepare message block + for (let t = 0; t < 16; t++) W[t] = M[t]; + for (let t = 16; t < 64; t++) { + W[t] = DeltaOne(W[t - 2]) + .addMod32(W[t - 7]) + .add(DeltaZero(W[t - 15]).add(W[t - 16])); + } + // initialize working variables + let a = H[0], + b = H[1], + c = H[2], + d = H[3], + e = H[4], + f = H[5], + g = H[6], + h = H[7]; + + // main loop + for (let t = 0; t < 64; t++) { + const T1 = h + .addMod32(SigmaOne(e)) + .addMod32(Ch(e, f, g)) + .addMod32(K[t]) + .addMod32(W[t]); + + const T2 = SigmaOne(a).addMod32(Maj(a, b, c)); + + h = g; + g = f; + f = e; + e = d.addMod32(T1); + d = c; + c = b; + b = a; + a = T1.addMod32(T2); + } + + // new intermediate hash value + + H[0] = H[0].addMod32(a); + H[1] = H[1].addMod32(b); + H[2] = H[2].addMod32(c); + H[3] = H[3].addMod32(d); + H[4] = H[4].addMod32(e); + H[5] = H[5].addMod32(f); + H[6] = H[6].addMod32(g); + H[7] = H[7].addMod32(h); + } + + /* + let hex = ''; + for (let h = 0; h < H.length; h++) + hex = hex + ('00000000' + H[h].toBigint().toString(16)).slice(-8); + console.log(hex); + */ + }, +}; + +let cs = Provable.constraintSystem(() => SHA256.hash('abc')); +console.log(cs); +/* function toUInt8(msg: string) { + return msg.split('').map((c) => UInt8.from(c.charCodeAt(0))); +} */ + +function Ch(x: UInt32, y: UInt32, z: UInt32) { + let xAndY = x.and(y); + let xNotAndZ = x.not().and(z); + return xAndY.xor(xNotAndZ); +} + +function Maj(x: UInt32, y: UInt32, z: UInt32) { + let xAndY = x.and(y); + let xAndZ = x.and(z); + let yAndZ = y.and(z); + + return xAndY.xor(xAndZ).xor(yAndZ); +} + +function SigmaZero(x: UInt32) { + let rotr2 = ROTR(2, x); + let rotr13 = ROTR(13, x); + let rotr22 = ROTR(22, x); + + return rotr2.xor(rotr13).xor(rotr22); +} + +function SigmaOne(x: UInt32) { + let rotr6 = ROTR(6, x); + let rotr11 = ROTR(11, x); + let rotr25 = ROTR(25, x); + + return rotr6.xor(rotr11).xor(rotr25); +} + +// lowercase sigma = delta to avoid confusing function names + +function DeltaZero(x: UInt32) { + let rotr7 = ROTR(7, x); + let rotr18 = ROTR(18, x); + let shr3 = SHR(3, x); + + return rotr7.xor(rotr18).xor(shr3); +} + +function DeltaOne(x: UInt32) { + let rotr17 = ROTR(17, x); + let rotr19 = ROTR(19, x); + let shr10 = SHR(10, x); + return rotr17.xor(rotr19).xor(shr10); +} + +function ROTR(n: number, x: UInt32) { + return x.rotate(n, 'right'); +} + +function SHR(n: number, x: UInt32) { + let val = x.rightShift(n); + return val; +} diff --git a/src/lib/int.ts b/src/lib/int.ts index 6fe765e7db..ac59f5c78e 100644 --- a/src/lib/int.ts +++ b/src/lib/int.ts @@ -703,6 +703,10 @@ class UInt32 extends CircuitValue { Gadgets.rangeCheck32(z); return new UInt32(z); } + + addMod32(y: UInt32) { + return new UInt32(Gadgets.addMod32(this.value, y.value)); + } /** * Subtraction with underflow checking. */ @@ -732,7 +736,7 @@ class UInt32 extends CircuitValue { * ``` */ xor(x: UInt32) { - return Gadgets.xor(this.value, x.value, UInt32.NUM_BITS); + return UInt32.from(Gadgets.xor(this.value, x.value, UInt32.NUM_BITS)); } /** @@ -763,7 +767,7 @@ class UInt32 extends CircuitValue { * @param a - The value to apply NOT to. */ not() { - return Gadgets.not(this.value, UInt32.NUM_BITS, false); + return UInt32.from(Gadgets.not(this.value, UInt32.NUM_BITS, false)); } /** @@ -795,7 +799,7 @@ class UInt32 extends CircuitValue { * ``` */ rotate(bits: number, direction: 'left' | 'right' = 'left') { - return Gadgets.rotate32(this.value, bits, direction); + return UInt32.from(Gadgets.rotate32(this.value, bits, direction)); } /** @@ -818,7 +822,7 @@ class UInt32 extends CircuitValue { * ``` */ leftShift(bits: number) { - return Gadgets.leftShift32(this.value, bits); + return UInt32.from(Gadgets.leftShift32(this.value, bits)); } /** @@ -841,7 +845,7 @@ class UInt32 extends CircuitValue { * ``` */ rightShift(bits: number) { - return Gadgets.rightShift64(this.value, bits); + return UInt32.from(Gadgets.rightShift64(this.value, bits)); } /** @@ -870,7 +874,7 @@ class UInt32 extends CircuitValue { * ``` */ and(x: UInt32) { - return Gadgets.and(this.value, x.value, UInt32.NUM_BITS); + return UInt32.from(Gadgets.and(this.value, x.value, UInt32.NUM_BITS)); } /** From 35a15f5b43e05b9601524a6caf822bc2b6ab799a Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 1 Dec 2023 19:38:10 +0100 Subject: [PATCH 011/524] add dummy api --- src/examples/sha256.ts | 260 ++++++++----------------------------- src/lib/gadgets/gadgets.ts | 4 + src/lib/gadgets/sha256.ts | 131 +++++++++++-------- 3 files changed, 134 insertions(+), 261 deletions(-) diff --git a/src/examples/sha256.ts b/src/examples/sha256.ts index 41ef423716..8b14e65e4a 100644 --- a/src/examples/sha256.ts +++ b/src/examples/sha256.ts @@ -1,208 +1,58 @@ -// https://csrc.nist.gov/pubs/fips/180-4/upd1/final - -import { UInt32, Provable } from 'o1js'; -import { bitsToBytes } from 'src/bindings/lib/binable.js'; - -export { SHA256 }; - -function processStringToMessageBlocks(s: string) { - let msgBits = s - .split('') - .map((c) => { - let binary = c.charCodeAt(0).toString(2); - return '00000000'.substr(binary.length) + binary; - }) - .join(''); - - let l = msgBits.length; - msgBits = msgBits + '1'; - console.log('msgBits:', msgBits.length); - - // calculate k in l + 1 +k = 448 mod 512 - let remainder = (448 - (l + 1)) % 512; - - let k = (remainder + 512) % 512; - console.log(k); - let padding = '0'.repeat(k); - msgBits = msgBits + padding; - let lBits = l.toString(2); - msgBits = msgBits + '0'.repeat(64 - lBits.length) + lBits; - console.log('msgBits:', msgBits.length); - let bitBlocks32 = []; - for (let i = 0; i < msgBits.length; i += 32) { - bitBlocks32.push(UInt32.from(BigInt('0b' + msgBits.substr(i, 32)))); - } - - let lengthBlocks = bitBlocks32.length; - let blocks = []; - for (let i = 0; i < lengthBlocks; i += 16) { - let block = bitBlocks32.slice(i, i + 16); - blocks.push(block); - } - return blocks; -} - -// constants §4.2.2 -const K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -].map((k) => UInt32.from(k)); - -// initial hash values §5.3.3 -const H = [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, - 0x1f83d9ab, 0x5be0cd19, -].map((h) => UInt32.from(h)); - -const SHA256 = { - hash(msg: string) { - // preprocessing §6.2 - // padding the message $5.1.1 into blocks that are a multiple of 512 - let messageBlocks = Provable.witness( - Provable.Array(Provable.Array(UInt32, 16), 6), - () => { - console.log('hashing <' + msg + '>'); - return processStringToMessageBlocks(msg); - } - ); - - const N = messageBlocks.length; - - for (let i = 0; i < N; i++) { - const M = messageBlocks[i]; - // for each message block of 16 x 32 bytes do: - const W = new Array(64); - - // prepare message block - for (let t = 0; t <= 15; t++) W[t] = M[t]; - for (let t = 16; t <= 63; t++) { - W[t] = DeltaOne(W[t - 2]) - .addMod32(W[t - 7]) - .addMod32(DeltaZero(W[t - 15]).addMod32(W[t - 16])); - } - // initialize working variables - let a = H[0]; - let b = H[1]; - let c = H[2]; - let d = H[3]; - let e = H[4]; - let f = H[5]; - let g = H[6]; - let h = H[7]; - - // main loop - for (let t = 0; t <= 63; t++) { - const T1 = h - .addMod32(SigmaOne(e)) - .addMod32(Ch(e, f, g)) - .addMod32(K[t]) - .addMod32(W[t]); - - const T2 = SigmaZero(a).addMod32(Maj(a, b, c)); - - h = g; - g = f; - f = e; - e = d.addMod32(T1); - d = c; - c = b; - b = a; - a = T1.addMod32(T2); - } - - // new intermediate hash value - - H[0] = H[0].addMod32(a); - H[1] = H[1].addMod32(b); - H[2] = H[2].addMod32(c); - H[3] = H[3].addMod32(d); - H[4] = H[4].addMod32(e); - H[5] = H[5].addMod32(f); - H[6] = H[6].addMod32(g); - H[7] = H[7].addMod32(h); - } - - Provable.asProver(() => { - let hex = ''; - for (let h = 0; h < H.length; h++) - hex = hex + ('00000000' + H[h].toBigint().toString(16)).slice(-8); - console.log('sha256 digest:', hex); - }); - }, +import assert from 'assert'; +import { Gadgets, Provable, UInt32 } from 'o1js'; + +const Parser = Gadgets.SHA256.processStringToMessageBlocks; + +const Hash = Gadgets.SHA256.hash; + +const run = (msg: string, expected: string) => { + let messageBlocks = Provable.witness( + Provable.Array(Provable.Array(UInt32, 16), 1), + () => Parser(msg) + ); + let digest = Hash(messageBlocks); + Provable.asProver(() => { + let y = toHex(digest); + assert(expected === y, `expected ${expected} got ${y}`); + }); }; -Provable.runAndCheck(() => - SHA256.hash( - 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest' - ) -); -let cs = Provable.constraintSystem(() => SHA256.hash('test')); -console.log('rows:', cs.rows); -/* function toUInt8(msg: string) { - return msg.split('').map((c) => UInt8.from(c.charCodeAt(0))); -} */ - -function Ch(x: UInt32, y: UInt32, z: UInt32) { - let xAndY = x.and(y); - let xNotAndZ = x.not().and(z); - return xAndY.xor(xNotAndZ); -} - -function Maj(x: UInt32, y: UInt32, z: UInt32) { - let xAndY = x.and(y); - let xAndZ = x.and(z); - let yAndZ = y.and(z); - - return xAndY.xor(xAndZ).xor(yAndZ); -} - -function SigmaZero(x: UInt32) { - let rotr2 = ROTR(2, x); - let rotr13 = ROTR(13, x); - let rotr22 = ROTR(22, x); - - return rotr2.xor(rotr13).xor(rotr22); -} - -function SigmaOne(x: UInt32) { - let rotr6 = ROTR(6, x); - let rotr11 = ROTR(11, x); - let rotr25 = ROTR(25, x); - - return rotr6.xor(rotr11).xor(rotr25); -} - -// lowercase sigma = delta to avoid confusing function names - -function DeltaZero(x: UInt32) { - let rotr7 = ROTR(7, x); - let rotr18 = ROTR(18, x); - let shr3 = SHR(3, x); - - return rotr7.xor(rotr18).xor(shr3); -} - -function DeltaOne(x: UInt32) { - let rotr17 = ROTR(17, x); - let rotr19 = ROTR(19, x); - let shr10 = SHR(10, x); - return rotr17.xor(rotr19).xor(shr10); -} - -function ROTR(n: number, x: UInt32) { - return x.rotate(n, 'right'); -} - -function SHR(n: number, x: UInt32) { - let val = x.rightShift(n); - return val; +Provable.runAndCheck(() => { + run( + 'duck', + '2d2370db2447ff8cf4f3accd68c85aa119a9c893effd200a9b69176e9fc5eb98' + ); + run( + 'doggo', + '8aa89c66e2c453b71400ac832a345d872c33147150267be5402552ee19b3d4ce' + ); + run( + 'frog', + '74fa5327cc0f4e947789dd5e989a61a8242986a596f170640ac90337b1da1ee4' + ); +}); + +let cs = Provable.constraintSystem(() => { + run( + 'duck', + '2d2370db2447ff8cf4f3accd68c85aa119a9c893effd200a9b69176e9fc5eb98' + ); + run( + 'doggo', + '8aa89c66e2c453b71400ac832a345d872c33147150267be5402552ee19b3d4ce' + ); + run( + 'frog', + '74fa5327cc0f4e947789dd5e989a61a8242986a596f170640ac90337b1da1ee4' + ); +}); + +console.log(cs); + +function toHex(xs: UInt32[]) { + let hex = ''; + for (let h = 0; h < xs.length; h++) + hex = hex + ('00000000' + xs[h].toBigint().toString(16)).slice(-8); + + return hex; } diff --git a/src/lib/gadgets/gadgets.ts b/src/lib/gadgets/gadgets.ts index d095184388..0e7b781f3a 100644 --- a/src/lib/gadgets/gadgets.ts +++ b/src/lib/gadgets/gadgets.ts @@ -22,6 +22,7 @@ import { import { Field } from '../core.js'; import { ForeignField, Field3 } from './foreign-field.js'; import { divMod32, addMod32 } from './arithmetic.js'; +import { SHA256 } from './sha256.js'; export { Gadgets }; @@ -668,6 +669,9 @@ const Gadgets = { * ``` * */ addMod32, + + // TODO: everything + SHA256: SHA256, }; export namespace Gadgets { diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 3693b82bb1..e78c5dfd37 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -5,72 +5,101 @@ import { Provable } from '../provable.js'; export { SHA256 }; -// constants §4.2.2 -const K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -].map((k) => UInt32.from(k)); - -// initial hash values §5.3.3 -const H = [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, - 0x1f83d9ab, 0x5be0cd19, -].map((h) => UInt32.from(h)); +function processStringToMessageBlocks(s: string) { + let msgBits = s + .split('') + .map((c) => { + let binary = c.charCodeAt(0).toString(2); + return '00000000'.substr(binary.length) + binary; + }) + .join(''); + + let l = msgBits.length; + msgBits = msgBits + '1'; + + // calculate k in l + 1 +k = 448 mod 512 + let remainder = (448 - (l + 1)) % 512; + + let k = (remainder + 512) % 512; + let padding = '0'.repeat(k); + msgBits = msgBits + padding; + let lBits = l.toString(2); + msgBits = msgBits + '0'.repeat(64 - lBits.length) + lBits; + + let bitBlocks32 = []; + for (let i = 0; i < msgBits.length; i += 32) { + bitBlocks32.push(UInt32.from(BigInt('0b' + msgBits.substr(i, 32)))); + } + + let lengthBlocks = bitBlocks32.length; + let blocks = []; + for (let i = 0; i < lengthBlocks; i += 16) { + let block = bitBlocks32.slice(i, i + 16); + blocks.push(block); + } + return blocks; +} const SHA256 = { - hash(msg: string) { - // preprocessing §6.2 + hash(data: UInt32[][]) { + // constants §4.2.2 + const K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + ].map((k) => UInt32.from(k)); + + // initial hash values §5.3.3 + const H = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19, + ].map((h) => UInt32.from(h)); + + // TODO: correct dynamic preprocessing §6.2 // padding the message $5.1.1 into blocks that are a multiple of 512 - let l = 8 * 8; - let k = 448 - (l + 1); - let padding = [1, ...new Array(k).fill(0)]; + let messageBlocks = data; - let messageBlocks = [ - // M1 - new Array(16).fill(UInt32.from(1)), - new Array(16).fill(UInt32.from(1)), - ]; + const N = messageBlocks.length; - for (let i = 0; i < messageBlocks.length; i++) { + for (let i = 0; i < N; i++) { const M = messageBlocks[i]; // for each message block of 16 x 32 bytes do: const W = new Array(64); // prepare message block - for (let t = 0; t < 16; t++) W[t] = M[t]; - for (let t = 16; t < 64; t++) { + for (let t = 0; t <= 15; t++) W[t] = M[t]; + for (let t = 16; t <= 63; t++) { W[t] = DeltaOne(W[t - 2]) .addMod32(W[t - 7]) - .add(DeltaZero(W[t - 15]).add(W[t - 16])); + .addMod32(DeltaZero(W[t - 15]).addMod32(W[t - 16])); } // initialize working variables - let a = H[0], - b = H[1], - c = H[2], - d = H[3], - e = H[4], - f = H[5], - g = H[6], - h = H[7]; + let a = H[0]; + let b = H[1]; + let c = H[2]; + let d = H[3]; + let e = H[4]; + let f = H[5]; + let g = H[6]; + let h = H[7]; // main loop - for (let t = 0; t < 64; t++) { + for (let t = 0; t <= 63; t++) { const T1 = h .addMod32(SigmaOne(e)) .addMod32(Ch(e, f, g)) .addMod32(K[t]) .addMod32(W[t]); - const T2 = SigmaOne(a).addMod32(Maj(a, b, c)); + const T2 = SigmaZero(a).addMod32(Maj(a, b, c)); h = g; g = f; @@ -94,21 +123,11 @@ const SHA256 = { H[7] = H[7].addMod32(h); } - /* - let hex = ''; - for (let h = 0; h < H.length; h++) - hex = hex + ('00000000' + H[h].toBigint().toString(16)).slice(-8); - console.log(hex); - */ + return H; }, + processStringToMessageBlocks: processStringToMessageBlocks, }; -let cs = Provable.constraintSystem(() => SHA256.hash('abc')); -console.log(cs); -/* function toUInt8(msg: string) { - return msg.split('').map((c) => UInt8.from(c.charCodeAt(0))); -} */ - function Ch(x: UInt32, y: UInt32, z: UInt32) { let xAndY = x.and(y); let xNotAndZ = x.not().and(z); From a1e8f17b36a477e49d3fa070ae16b190f2d272cf Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 1 Dec 2023 21:45:53 +0100 Subject: [PATCH 012/524] maybe some improvements --- src/lib/gadgets/sha256.ts | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index e78c5dfd37..7ff6f96d01 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -1,7 +1,8 @@ // https://csrc.nist.gov/pubs/fips/180-4/upd1/final +import { Field } from '../field.js'; import { UInt32 } from '../int.js'; -import { Provable } from '../provable.js'; +import { Gadgets } from './gadgets.js'; export { SHA256 }; @@ -72,15 +73,21 @@ const SHA256 = { for (let i = 0; i < N; i++) { const M = messageBlocks[i]; // for each message block of 16 x 32 bytes do: - const W = new Array(64); + const W: UInt32[] = []; // prepare message block for (let t = 0; t <= 15; t++) W[t] = M[t]; for (let t = 16; t <= 63; t++) { - W[t] = DeltaOne(W[t - 2]) + let temp = DeltaOne(W[t - 2]) + .value.add(W[t - 7].value) + .add(DeltaZero(W[t - 15]).value.add(W[t - 16].value)); + + W[t] = UInt32.from(Gadgets.divMod32(temp).remainder); + /* W[t] = DeltaOne(W[t - 2]) .addMod32(W[t - 7]) - .addMod32(DeltaZero(W[t - 15]).addMod32(W[t - 16])); + .addMod32(DeltaZero(W[t - 15]).addMod32(W[t - 16])); */ } + // initialize working variables let a = H[0]; let b = H[1]; @@ -93,13 +100,17 @@ const SHA256 = { // main loop for (let t = 0; t <= 63; t++) { - const T1 = h - .addMod32(SigmaOne(e)) - .addMod32(Ch(e, f, g)) - .addMod32(K[t]) - .addMod32(W[t]); - - const T2 = SigmaZero(a).addMod32(Maj(a, b, c)); + const T1 = new UInt32( + Gadgets.divMod32( + h.value + .add(SigmaOne(e).value) + .add(Ch(e, f, g).value) + .add(K[t].value) + .add(W[t].value) + ).remainder + ); + + const unreducedT2 = SigmaZero(a).value.add(Maj(a, b, c).value); h = g; g = f; @@ -108,7 +119,7 @@ const SHA256 = { d = c; c = b; b = a; - a = T1.addMod32(T2); + a = UInt32.from(Gadgets.divMod32(unreducedT2.add(T1.value)).remainder); } // new intermediate hash value From aa85082ebc85da7049927e37d5cf700038f082de Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 1 Dec 2023 21:59:44 +0100 Subject: [PATCH 013/524] slight refac --- src/examples/sha256.ts | 61 +++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/src/examples/sha256.ts b/src/examples/sha256.ts index 8b14e65e4a..ac9dce1076 100644 --- a/src/examples/sha256.ts +++ b/src/examples/sha256.ts @@ -2,9 +2,31 @@ import assert from 'assert'; import { Gadgets, Provable, UInt32 } from 'o1js'; const Parser = Gadgets.SHA256.processStringToMessageBlocks; - const Hash = Gadgets.SHA256.hash; +const testVectors = [ + { + msg: '', + expected: + 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + }, + { + msg: 'duck', + expected: + '2d2370db2447ff8cf4f3accd68c85aa119a9c893effd200a9b69176e9fc5eb98', + }, + { + msg: 'doggo', + expected: + '8aa89c66e2c453b71400ac832a345d872c33147150267be5402552ee19b3d4ce', + }, + { + msg: 'frog', + expected: + '74fa5327cc0f4e947789dd5e989a61a8242986a596f170640ac90337b1da1ee4', + }, +]; + const run = (msg: string, expected: string) => { let messageBlocks = Provable.witness( Provable.Array(Provable.Array(UInt32, 16), 1), @@ -17,34 +39,23 @@ const run = (msg: string, expected: string) => { }); }; +console.log('running plain'); +testVectors.forEach((v) => { + run(v.msg, v.expected); +}); + +console.log('run and check'); Provable.runAndCheck(() => { - run( - 'duck', - '2d2370db2447ff8cf4f3accd68c85aa119a9c893effd200a9b69176e9fc5eb98' - ); - run( - 'doggo', - '8aa89c66e2c453b71400ac832a345d872c33147150267be5402552ee19b3d4ce' - ); - run( - 'frog', - '74fa5327cc0f4e947789dd5e989a61a8242986a596f170640ac90337b1da1ee4' - ); + testVectors.forEach((v) => { + run(v.msg, v.expected); + }); }); +console.log('constraint system'); let cs = Provable.constraintSystem(() => { - run( - 'duck', - '2d2370db2447ff8cf4f3accd68c85aa119a9c893effd200a9b69176e9fc5eb98' - ); - run( - 'doggo', - '8aa89c66e2c453b71400ac832a345d872c33147150267be5402552ee19b3d4ce' - ); - run( - 'frog', - '74fa5327cc0f4e947789dd5e989a61a8242986a596f170640ac90337b1da1ee4' - ); + testVectors.forEach((v) => { + run(v.msg, v.expected); + }); }); console.log(cs); From 880a4129ac375f48b76dc8e1c4ee8b9532f9b41b Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 1 Dec 2023 22:16:21 +0100 Subject: [PATCH 014/524] more efficient hashing --- src/lib/gadgets/sha256.ts | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 7ff6f96d01..c85dc47ff3 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -78,14 +78,11 @@ const SHA256 = { // prepare message block for (let t = 0; t <= 15; t++) W[t] = M[t]; for (let t = 16; t <= 63; t++) { - let temp = DeltaOne(W[t - 2]) + let unreduced = DeltaOne(W[t - 2]) .value.add(W[t - 7].value) .add(DeltaZero(W[t - 15]).value.add(W[t - 16].value)); - W[t] = UInt32.from(Gadgets.divMod32(temp).remainder); - /* W[t] = DeltaOne(W[t - 2]) - .addMod32(W[t - 7]) - .addMod32(DeltaZero(W[t - 15]).addMod32(W[t - 16])); */ + W[t] = UInt32.from(Gadgets.divMod32(unreduced).remainder); } // initialize working variables @@ -100,26 +97,24 @@ const SHA256 = { // main loop for (let t = 0; t <= 63; t++) { - const T1 = new UInt32( - Gadgets.divMod32( - h.value - .add(SigmaOne(e).value) - .add(Ch(e, f, g).value) - .add(K[t].value) - .add(W[t].value) - ).remainder - ); + const unreducedT1 = h.value + .add(SigmaOne(e).value) + .add(Ch(e, f, g).value) + .add(K[t].value) + .add(W[t].value); const unreducedT2 = SigmaZero(a).value.add(Maj(a, b, c).value); h = g; g = f; f = e; - e = d.addMod32(T1); + e = UInt32.from(Gadgets.divMod32(d.value.add(unreducedT1)).remainder); d = c; c = b; b = a; - a = UInt32.from(Gadgets.divMod32(unreducedT2.add(T1.value)).remainder); + a = UInt32.from( + Gadgets.divMod32(unreducedT2.add(unreducedT1)).remainder + ); } // new intermediate hash value From 79068e350835575e906a1689dbc7973ba833c2b5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 5 Dec 2023 21:58:20 +0100 Subject: [PATCH 015/524] faster rotation of 3 values at once --- src/lib/gadgets/sha256.ts | 82 ++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index c85dc47ff3..84b267d325 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -1,7 +1,8 @@ // https://csrc.nist.gov/pubs/fips/180-4/upd1/final - import { Field } from '../field.js'; import { UInt32 } from '../int.js'; +import { TupleN } from '../util/types.js'; +import { assert, bitSlice, exists } from './common.js'; import { Gadgets } from './gadgets.js'; export { SHA256 }; @@ -149,18 +150,12 @@ function Maj(x: UInt32, y: UInt32, z: UInt32) { } function SigmaZero(x: UInt32) { - let rotr2 = ROTR(2, x); - let rotr13 = ROTR(13, x); - let rotr22 = ROTR(22, x); - + let [rotr2, rotr13, rotr22] = ROTR3(x, [2, 13, 22]); return rotr2.xor(rotr13).xor(rotr22); } function SigmaOne(x: UInt32) { - let rotr6 = ROTR(6, x); - let rotr11 = ROTR(11, x); - let rotr25 = ROTR(25, x); - + let [rotr6, rotr11, rotr25] = ROTR3(x, [6, 11, 25]); return rotr6.xor(rotr11).xor(rotr25); } @@ -189,3 +184,72 @@ function SHR(n: number, x: UInt32) { let val = x.rightShift(n); return val; } + +function ROTR3Simple(u: UInt32, bits: TupleN): TupleN { + let [r0, r1, r2] = bits; + return [ROTR(r0, u), ROTR(r1, u), ROTR(r2, u)]; +} + +function ROTR3(u: UInt32, bits: TupleN): TupleN { + if (u.isConstant()) return ROTR3Simple(u, bits); + + let [r0, r1, r2] = bits; // TODO assert bits are sorted + let x = u.value; + + let d0 = r0; + let d1 = r1 - r0; + let d2 = r2 - r1; + let d3 = 32 - r2; + + // decompose x into 4 chunks of size d0, d1, d2, d3 + let [x0, x1, x2, x3] = exists(4, () => { + let xx = x.toBigInt(); + return [ + bitSlice(xx, 0, d0), + bitSlice(xx, r0, d1), + bitSlice(xx, r1, d2), + bitSlice(xx, r2, d3), + ]; + }); + + // range check each chunk + rangeCheckNSmall(x0, d0); + rangeCheckNSmall(x1, d1); + rangeCheckNSmall(x2, d2); + assert(d3 <= 16, 'expected d3 <= 16'); + rangeCheckNSmall(x3, 16); // cheaper and sufficient + + // prove x decomposition + + // x === x0 + x1*2^d0 + x2*2^(d0+d1) + x3*2^(d0+d1+d2) + let x23 = x2.add(x3.mul(1 << d2)).seal(); + let x123 = x1.add(x23.mul(1 << d1)).seal(); + x0.add(x123.mul(1 << d0)).assertEquals(x); + + // reassemble chunks into rotated values + + // rotr(x, r0) = x1 + x2*2^d1 + x3*2^(d1+d2) + x0*2^(d1+d2+d3) + let xRotR0 = x123.add(x0.mul(1 << (d1 + d2 + d3))).seal(); + + // rotr(x, r1) = x2 + x3*2^d2 + x0*2^(d2+d3) + x1*2^(d2+d3+d0) + let x01 = x0.add(x1.mul(1 << d0)).seal(); + let xRotR1 = x23.add(x01.mul(1 << (d2 + d3))).seal(); + + // rotr(x, r2) = x3 + x0*2^d3 + x1*2^(d3+d0) + x2*2^(d3+d0+d1) + let x012 = x01.add(x2.mul(1 << (d0 + d1))).seal(); + let xRotR2 = x3.add(x012.mul(1 << d3)).seal(); + + return TupleN.map([xRotR0, xRotR1, xRotR2], (x) => UInt32.from(x)); +} + +function rangeCheckNSmall(x: Field, n: number) { + assert(n <= 16, 'expected n <= 16'); + + // x < 2^16 + x.rangeCheckHelper(16).assertEquals(x); + if (n === 16) return; + + // 2^(16-n)*x < 2^16, which implies x < 2^n + let xScaled = x.mul(1 << (16 - n)).seal(); + xScaled.rangeCheckHelper(16).assertEquals(xScaled); +} From e2bcd0dfa5dd54e53fa929efe56ec803641332f4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 08:00:08 +0100 Subject: [PATCH 016/524] save some range checks --- src/lib/gadgets/sha256.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 84b267d325..61daa37ed2 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -190,6 +190,7 @@ function ROTR3Simple(u: UInt32, bits: TupleN): TupleN { return [ROTR(r0, u), ROTR(r1, u), ROTR(r2, u)]; } +// assumes that outputs are range-checked to 32 bits externally function ROTR3(u: UInt32, bits: TupleN): TupleN { if (u.isConstant()) return ROTR3Simple(u, bits); @@ -213,11 +214,12 @@ function ROTR3(u: UInt32, bits: TupleN): TupleN { }); // range check each chunk - rangeCheckNSmall(x0, d0); - rangeCheckNSmall(x1, d1); - rangeCheckNSmall(x2, d2); - assert(d3 <= 16, 'expected d3 <= 16'); - rangeCheckNSmall(x3, 16); // cheaper and sufficient + // we only need to range check to 16 bits relying on the requirement that + // the rotated values are range-checked to 32 bits later; see comments below + rangeCheckNSmall(x0, 16); + rangeCheckNSmall(x1, 16); + rangeCheckNSmall(x2, 16); + rangeCheckNSmall(x3, 16); // prove x decomposition @@ -225,19 +227,23 @@ function ROTR3(u: UInt32, bits: TupleN): TupleN { let x23 = x2.add(x3.mul(1 << d2)).seal(); let x123 = x1.add(x23.mul(1 << d1)).seal(); x0.add(x123.mul(1 << d0)).assertEquals(x); + // ^ proves that 2^(32-d3)*x3 < x < 2^32 => x3 < 2^d3 // reassemble chunks into rotated values // rotr(x, r0) = x1 + x2*2^d1 + x3*2^(d1+d2) + x0*2^(d1+d2+d3) let xRotR0 = x123.add(x0.mul(1 << (d1 + d2 + d3))).seal(); + // ^ proves that 2^(32-d0)*x0 < xRotR0 => x0 < 2^d0 if we check xRotR0 < 2^32 later // rotr(x, r1) = x2 + x3*2^d2 + x0*2^(d2+d3) + x1*2^(d2+d3+d0) let x01 = x0.add(x1.mul(1 << d0)).seal(); let xRotR1 = x23.add(x01.mul(1 << (d2 + d3))).seal(); + // ^ proves that 2^(32-d1)*x1 < xRotR1 => x1 < 2^d1 if we check xRotR1 < 2^32 later // rotr(x, r2) = x3 + x0*2^d3 + x1*2^(d3+d0) + x2*2^(d3+d0+d1) let x012 = x01.add(x2.mul(1 << (d0 + d1))).seal(); let xRotR2 = x3.add(x012.mul(1 << d3)).seal(); + // ^ proves that 2^(32-d2)*x2 < xRotR2 => x2 < 2^d2 if we check xRotR2 < 2^32 later return TupleN.map([xRotR0, xRotR1, xRotR2], (x) => UInt32.from(x)); } From a82c63b7f805f7461237d1913035bca5c2ec4540 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 08:03:26 +0100 Subject: [PATCH 017/524] clean up a bit --- src/lib/gadgets/sha256.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 61daa37ed2..76e2a2f49e 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -216,10 +216,10 @@ function ROTR3(u: UInt32, bits: TupleN): TupleN { // range check each chunk // we only need to range check to 16 bits relying on the requirement that // the rotated values are range-checked to 32 bits later; see comments below - rangeCheckNSmall(x0, 16); - rangeCheckNSmall(x1, 16); - rangeCheckNSmall(x2, 16); - rangeCheckNSmall(x3, 16); + rangeCheck16(x0); + rangeCheck16(x1); + rangeCheck16(x2); + rangeCheck16(x3); // prove x decomposition @@ -252,10 +252,13 @@ function rangeCheckNSmall(x: Field, n: number) { assert(n <= 16, 'expected n <= 16'); // x < 2^16 - x.rangeCheckHelper(16).assertEquals(x); + rangeCheck16(x); if (n === 16) return; // 2^(16-n)*x < 2^16, which implies x < 2^n - let xScaled = x.mul(1 << (16 - n)).seal(); - xScaled.rangeCheckHelper(16).assertEquals(xScaled); + rangeCheck16(x.mul(1 << (16 - n)).seal()); +} + +function rangeCheck16(x: Field) { + x.rangeCheckHelper(16).assertEquals(x); } From 17c7c88c4f349152ff971aebc9299ed5a944cf4b Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 08:33:29 +0100 Subject: [PATCH 018/524] support Field as input to UInt32.xor --- src/lib/int.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/int.ts b/src/lib/int.ts index ac59f5c78e..f161e83187 100644 --- a/src/lib/int.ts +++ b/src/lib/int.ts @@ -735,8 +735,10 @@ class UInt32 extends CircuitValue { * c.assertEquals(0b0110); * ``` */ - xor(x: UInt32) { - return UInt32.from(Gadgets.xor(this.value, x.value, UInt32.NUM_BITS)); + xor(x: UInt32 | Field) { + return UInt32.from( + Gadgets.xor(this.value, UInt32.from(x).value, UInt32.NUM_BITS) + ); } /** From 62db5b27f5cb718255a34f4a9f545410246e9f53 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 08:34:10 +0100 Subject: [PATCH 019/524] cover delta0/1 with fast 3-rotation --- src/lib/gadgets/sha256.ts | 47 +++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 76e2a2f49e..eec0ca3949 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -150,30 +150,21 @@ function Maj(x: UInt32, y: UInt32, z: UInt32) { } function SigmaZero(x: UInt32) { - let [rotr2, rotr13, rotr22] = ROTR3(x, [2, 13, 22]); - return rotr2.xor(rotr13).xor(rotr22); + return sigma(x, [2, 13, 22]); } function SigmaOne(x: UInt32) { - let [rotr6, rotr11, rotr25] = ROTR3(x, [6, 11, 25]); - return rotr6.xor(rotr11).xor(rotr25); + return sigma(x, [6, 11, 25]); } // lowercase sigma = delta to avoid confusing function names function DeltaZero(x: UInt32) { - let rotr7 = ROTR(7, x); - let rotr18 = ROTR(18, x); - let shr3 = SHR(3, x); - - return rotr7.xor(rotr18).xor(shr3); + return sigma(x, [3, 7, 18], true); } function DeltaOne(x: UInt32) { - let rotr17 = ROTR(17, x); - let rotr19 = ROTR(19, x); - let shr10 = SHR(10, x); - return rotr17.xor(rotr19).xor(shr10); + return sigma(x, [10, 17, 19], true); } function ROTR(n: number, x: UInt32) { @@ -185,14 +176,16 @@ function SHR(n: number, x: UInt32) { return val; } -function ROTR3Simple(u: UInt32, bits: TupleN): TupleN { +function sigmaSimple(u: UInt32, bits: TupleN, firstShifted = false) { let [r0, r1, r2] = bits; - return [ROTR(r0, u), ROTR(r1, u), ROTR(r2, u)]; + let rot0 = firstShifted ? SHR(r0, u) : ROTR(r0, u); + let rot1 = ROTR(r1, u); + let rot2 = ROTR(r2, u); + return rot0.xor(rot1).xor(rot2); } -// assumes that outputs are range-checked to 32 bits externally -function ROTR3(u: UInt32, bits: TupleN): TupleN { - if (u.isConstant()) return ROTR3Simple(u, bits); +function sigma(u: UInt32, bits: TupleN, firstShifted = false) { + if (u.isConstant()) return sigmaSimple(u, bits, firstShifted); let [r0, r1, r2] = bits; // TODO assert bits are sorted let x = u.value; @@ -231,9 +224,19 @@ function ROTR3(u: UInt32, bits: TupleN): TupleN { // reassemble chunks into rotated values - // rotr(x, r0) = x1 + x2*2^d1 + x3*2^(d1+d2) + x0*2^(d1+d2+d3) - let xRotR0 = x123.add(x0.mul(1 << (d1 + d2 + d3))).seal(); - // ^ proves that 2^(32-d0)*x0 < xRotR0 => x0 < 2^d0 if we check xRotR0 < 2^32 later + let xRotR0: Field; + + if (!firstShifted) { + // rotr(x, r0) = x1 + x2*2^d1 + x3*2^(d1+d2) + x0*2^(d1+d2+d3) + xRotR0 = x123.add(x0.mul(1 << (d1 + d2 + d3))).seal(); + // ^ proves that 2^(32-d0)*x0 < xRotR0 => x0 < 2^d0 if we check xRotR0 < 2^32 later + } else { + // shr(x, r0) = x1 + x2*2^d1 + x3*2^(d1+d2) + xRotR0 = x123; + + // finish x0 < 2^d0 proof: + rangeCheck16(x0.mul(1 << (16 - d0)).seal()); + } // rotr(x, r1) = x2 + x3*2^d2 + x0*2^(d2+d3) + x1*2^(d2+d3+d0) let x01 = x0.add(x1.mul(1 << d0)).seal(); @@ -245,7 +248,7 @@ function ROTR3(u: UInt32, bits: TupleN): TupleN { let xRotR2 = x3.add(x012.mul(1 << d3)).seal(); // ^ proves that 2^(32-d2)*x2 < xRotR2 => x2 < 2^d2 if we check xRotR2 < 2^32 later - return TupleN.map([xRotR0, xRotR1, xRotR2], (x) => UInt32.from(x)); + return UInt32.from(xRotR0).xor(xRotR1).xor(xRotR2); } function rangeCheckNSmall(x: Field, n: number) { From c783258449997831146fff5be5c2b0e538993476 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 08:38:18 +0100 Subject: [PATCH 020/524] remove unused function --- src/lib/gadgets/sha256.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index eec0ca3949..d513b610f4 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -251,17 +251,6 @@ function sigma(u: UInt32, bits: TupleN, firstShifted = false) { return UInt32.from(xRotR0).xor(xRotR1).xor(xRotR2); } -function rangeCheckNSmall(x: Field, n: number) { - assert(n <= 16, 'expected n <= 16'); - - // x < 2^16 - rangeCheck16(x); - if (n === 16) return; - - // 2^(16-n)*x < 2^16, which implies x < 2^n - rangeCheck16(x.mul(1 << (16 - n)).seal()); -} - function rangeCheck16(x: Field) { x.rangeCheckHelper(16).assertEquals(x); } From ef874db7b8d3768ace200ba9f6f80a95e001e0a3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 09:25:14 +0100 Subject: [PATCH 021/524] express maj with fewer xors --- src/lib/gadgets/common.ts | 12 ++++++++++++ src/lib/gadgets/sha256.ts | 21 ++++++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts index 196ca64e73..4f50bb1c27 100644 --- a/src/lib/gadgets/common.ts +++ b/src/lib/gadgets/common.ts @@ -17,6 +17,7 @@ export { witnessSlice, witnessNextValue, divideWithRemainder, + toBigints, }; function existsOne(compute: () => bigint) { @@ -60,6 +61,17 @@ function toVars>( return Tuple.map(fields, toVar); } +/** + * Convert several Fields to bigints. + */ +function toBigints< + T extends Tuple<{ toBigInt(): bigint } | { toBigint(): bigint }> +>(...fields: T) { + return Tuple.map(fields, (x) => + 'toBigInt' in x ? x.toBigInt() : x.toBigint() + ); +} + function assert(stmt: boolean, message?: string): asserts stmt { if (!stmt) { throw Error(message ?? 'Assertion failed'); diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index d513b610f4..3370250906 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -2,7 +2,7 @@ import { Field } from '../field.js'; import { UInt32 } from '../int.js'; import { TupleN } from '../util/types.js'; -import { assert, bitSlice, exists } from './common.js'; +import { bitSlice, exists, existsOne, toBigints } from './common.js'; import { Gadgets } from './gadgets.js'; export { SHA256 }; @@ -142,11 +142,22 @@ function Ch(x: UInt32, y: UInt32, z: UInt32) { } function Maj(x: UInt32, y: UInt32, z: UInt32) { - let xAndY = x.and(y); - let xAndZ = x.and(z); - let yAndZ = y.and(z); + if (x.isConstant() && y.isConstant() && z.isConstant()) { + let [x0, y0, z0] = toBigints(x, y, z); + return UInt32.from((x0 & y0) ^ (x0 & z0) ^ (y0 & z0)); + } + + let maj = existsOne(() => { + let [x0, y0, z0] = toBigints(x, y, z); + return (x0 & y0) ^ (x0 & z0) ^ (y0 & z0); + }); - return xAndY.xor(xAndZ).xor(yAndZ); + // maj(x, y, z) = (x & y) ^ (x & z) ^ (y & z) can be alternatively expressed as + // x + y + z = 2*maj(x, y, z) + (x ^ y ^ z) + let sum = x.value.add(y.value).add(z.value).seal(); + let xor = x.xor(y).xor(z); + maj.mul(2).add(xor.value).assertEquals(sum); + return UInt32.from(maj); } function SigmaZero(x: UInt32) { From 0ba733ad28f408a9775b1c5b5e32e5a28953f524 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 09:48:59 +0100 Subject: [PATCH 022/524] speed up Ch by replacing 1 XOR with addition --- src/lib/gadgets/sha256.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 3370250906..47748b369b 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -138,7 +138,9 @@ const SHA256 = { function Ch(x: UInt32, y: UInt32, z: UInt32) { let xAndY = x.and(y); let xNotAndZ = x.not().and(z); - return xAndY.xor(xNotAndZ); + // because of the occurence of x and ~x, the bits of (x & y) and (~x & z) are disjoint + // therefore, we can use + instead of XOR which is faster in a circuit + return UInt32.from(xAndY.value.add(xNotAndZ.value).seal()); } function Maj(x: UInt32, y: UInt32, z: UInt32) { From cf486594858ae1dad91acf4d07481fed355ee425 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 10:37:30 +0100 Subject: [PATCH 023/524] simplify logic --- src/lib/gadgets/sha256.ts | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 47748b369b..b09e370218 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -136,29 +136,20 @@ const SHA256 = { }; function Ch(x: UInt32, y: UInt32, z: UInt32) { - let xAndY = x.and(y); - let xNotAndZ = x.not().and(z); - // because of the occurence of x and ~x, the bits of (x & y) and (~x & z) are disjoint - // therefore, we can use + instead of XOR which is faster in a circuit - return UInt32.from(xAndY.value.add(xNotAndZ.value).seal()); + // ch(x, y, z) = (x & y) ^ (~x & z) + // = (x & y) + (~x & z) (since x & ~x = 0) + let xAndY = x.and(y).value; + let xNotAndZ = x.not().and(z).value; + let ch = xAndY.add(xNotAndZ).seal(); + return UInt32.from(ch); } function Maj(x: UInt32, y: UInt32, z: UInt32) { - if (x.isConstant() && y.isConstant() && z.isConstant()) { - let [x0, y0, z0] = toBigints(x, y, z); - return UInt32.from((x0 & y0) ^ (x0 & z0) ^ (y0 & z0)); - } - - let maj = existsOne(() => { - let [x0, y0, z0] = toBigints(x, y, z); - return (x0 & y0) ^ (x0 & z0) ^ (y0 & z0); - }); - - // maj(x, y, z) = (x & y) ^ (x & z) ^ (y & z) can be alternatively expressed as - // x + y + z = 2*maj(x, y, z) + (x ^ y ^ z) + // maj(x, y, z) = (x & y) ^ (x & z) ^ (y & z) + // = (x + y + z - (x ^ y ^ z)) / 2 let sum = x.value.add(y.value).add(z.value).seal(); - let xor = x.xor(y).xor(z); - maj.mul(2).add(xor.value).assertEquals(sum); + let xor = x.xor(y).xor(z).value; + let maj = sum.sub(xor).div(2).seal(); return UInt32.from(maj); } From 3efe56f2e5e45df8cca2d5e3bdab1bee4e1f5954 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 10:39:19 +0100 Subject: [PATCH 024/524] let's not introduce an unused function --- src/lib/gadgets/common.ts | 12 ------------ src/lib/gadgets/sha256.ts | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts index 4f50bb1c27..196ca64e73 100644 --- a/src/lib/gadgets/common.ts +++ b/src/lib/gadgets/common.ts @@ -17,7 +17,6 @@ export { witnessSlice, witnessNextValue, divideWithRemainder, - toBigints, }; function existsOne(compute: () => bigint) { @@ -61,17 +60,6 @@ function toVars>( return Tuple.map(fields, toVar); } -/** - * Convert several Fields to bigints. - */ -function toBigints< - T extends Tuple<{ toBigInt(): bigint } | { toBigint(): bigint }> ->(...fields: T) { - return Tuple.map(fields, (x) => - 'toBigInt' in x ? x.toBigInt() : x.toBigint() - ); -} - function assert(stmt: boolean, message?: string): asserts stmt { if (!stmt) { throw Error(message ?? 'Assertion failed'); diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index b09e370218..caa43f80fa 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -2,7 +2,7 @@ import { Field } from '../field.js'; import { UInt32 } from '../int.js'; import { TupleN } from '../util/types.js'; -import { bitSlice, exists, existsOne, toBigints } from './common.js'; +import { bitSlice, exists } from './common.js'; import { Gadgets } from './gadgets.js'; export { SHA256 }; From 1b4279b32f1e825e9aae585af4f8e3f3f9c120fd Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 6 Dec 2023 17:38:51 +0100 Subject: [PATCH 025/524] do smaller range checks on the quotient for add mod 32 --- src/lib/gadgets/arithmetic.ts | 13 +++++++++---- src/lib/gadgets/sha256.ts | 8 +++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/lib/gadgets/arithmetic.ts b/src/lib/gadgets/arithmetic.ts index 414ddfe814..31350bfa6a 100644 --- a/src/lib/gadgets/arithmetic.ts +++ b/src/lib/gadgets/arithmetic.ts @@ -1,12 +1,13 @@ +import { Bool } from '../bool.js'; import { provableTuple } from '../circuit_value.js'; import { Field } from '../core.js'; import { assert } from '../errors.js'; import { Provable } from '../provable.js'; -import { rangeCheck32 } from './range-check.js'; +import { rangeCheck32, rangeCheckN } from './range-check.js'; export { divMod32, addMod32 }; -function divMod32(n: Field) { +function divMod32(n: Field, quotientBits = 32) { if (n.isConstant()) { assert( n.toBigInt() < 1n << 64n, @@ -32,7 +33,11 @@ function divMod32(n: Field) { } ); - rangeCheck32(quotient); + if (quotientBits === 1) { + Bool.check(Bool.Unsafe.ofField(quotient)); + } else { + rangeCheckN(quotientBits, quotient); + } rangeCheck32(remainder); n.assertEquals(quotient.mul(1n << 32n).add(remainder)); @@ -44,5 +49,5 @@ function divMod32(n: Field) { } function addMod32(x: Field, y: Field) { - return divMod32(x.add(y)).remainder; + return divMod32(x.add(y), 1).remainder; } diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index caa43f80fa..5c389a9e52 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -83,7 +83,7 @@ const SHA256 = { .value.add(W[t - 7].value) .add(DeltaZero(W[t - 15]).value.add(W[t - 16].value)); - W[t] = UInt32.from(Gadgets.divMod32(unreduced).remainder); + W[t] = UInt32.from(Gadgets.divMod32(unreduced, 16).remainder); } // initialize working variables @@ -109,12 +109,14 @@ const SHA256 = { h = g; g = f; f = e; - e = UInt32.from(Gadgets.divMod32(d.value.add(unreducedT1)).remainder); + e = UInt32.from( + Gadgets.divMod32(d.value.add(unreducedT1), 16).remainder + ); d = c; c = b; b = a; a = UInt32.from( - Gadgets.divMod32(unreducedT2.add(unreducedT1)).remainder + Gadgets.divMod32(unreducedT2.add(unreducedT1), 16).remainder ); } From 99094d612b82e09ffa4e1491538c5bd238aec8a5 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 19 Dec 2023 12:06:14 +0100 Subject: [PATCH 026/524] start touching up SHA256 API --- src/examples/sha256.ts | 61 +++------------------------------- src/lib/gadgets/sha256.ts | 70 ++++++++++++++++++++++++++++++++++++--- src/lib/int.ts | 7 ++++ 3 files changed, 77 insertions(+), 61 deletions(-) diff --git a/src/examples/sha256.ts b/src/examples/sha256.ts index ac9dce1076..a38926d72c 100644 --- a/src/examples/sha256.ts +++ b/src/examples/sha256.ts @@ -1,65 +1,14 @@ -import assert from 'assert'; -import { Gadgets, Provable, UInt32 } from 'o1js'; +import { Bytes, Field, Gadgets, Provable, UInt32, UInt64, UInt8 } from 'o1js'; -const Parser = Gadgets.SHA256.processStringToMessageBlocks; -const Hash = Gadgets.SHA256.hash; +type FlexibleBytes = Bytes | (UInt8 | bigint | number)[] | Uint8Array; -const testVectors = [ - { - msg: '', - expected: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - }, - { - msg: 'duck', - expected: - '2d2370db2447ff8cf4f3accd68c85aa119a9c893effd200a9b69176e9fc5eb98', - }, - { - msg: 'doggo', - expected: - '8aa89c66e2c453b71400ac832a345d872c33147150267be5402552ee19b3d4ce', - }, - { - msg: 'frog', - expected: - '74fa5327cc0f4e947789dd5e989a61a8242986a596f170640ac90337b1da1ee4', - }, -]; - -const run = (msg: string, expected: string) => { - let messageBlocks = Provable.witness( - Provable.Array(Provable.Array(UInt32, 16), 1), - () => Parser(msg) - ); - let digest = Hash(messageBlocks); - Provable.asProver(() => { - let y = toHex(digest); - assert(expected === y, `expected ${expected} got ${y}`); - }); -}; - -console.log('running plain'); -testVectors.forEach((v) => { - run(v.msg, v.expected); -}); - -console.log('run and check'); Provable.runAndCheck(() => { - testVectors.forEach((v) => { - run(v.msg, v.expected); - }); -}); + let digest = Gadgets.SHA256.hash(Bytes.fromString('Hello world!')); -console.log('constraint system'); -let cs = Provable.constraintSystem(() => { - testVectors.forEach((v) => { - run(v.msg, v.expected); + Provable.asProver(() => { + console.log(toHex(digest)); }); }); - -console.log(cs); - function toHex(xs: UInt32[]) { let hex = ''; for (let h = 0; h < xs.length; h++) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 5c389a9e52..4e2591b858 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -1,6 +1,8 @@ // https://csrc.nist.gov/pubs/fips/180-4/upd1/final import { Field } from '../field.js'; -import { UInt32 } from '../int.js'; +import { UInt32, UInt8 } from '../int.js'; +import { Bytes, FlexibleBytes, createBytes } from '../provable-types/bytes.js'; +import { Provable } from '../provable.js'; import { TupleN } from '../util/types.js'; import { bitSlice, exists } from './common.js'; import { Gadgets } from './gadgets.js'; @@ -42,8 +44,66 @@ function processStringToMessageBlocks(s: string) { return blocks; } +function padding(data: FlexibleBytes): UInt32[][] { + // pad the data with zeros to reach the desired length + let zeroPadded = Bytes.from(data); + + // now pad the data to reach the format expected by sha256 + // pad 1 bit, followed by k zero bits where k is the smallest non-negative solution to + // l + 1 + k = 448 mod 512 + // then append a 64bit block containing the length of the original message in bits + // TODO: question, most values are witnessed because we dont need to prove the padding. + // The user is incentivized to provide the correct padding because otherwise the hash will be wrong. + + let l = data.length * 8; // length in bits + let k = (448 - (l + 1)) % 512; + let totalPaddingLength = (k + 1 + 64) / 8; // total length of the padding + + let padding = Provable.witness( + Provable.Array(UInt8, totalPaddingLength), + () => { + let padding = ( + '1' + + '0'.repeat(k) + + '0'.repeat(64 - l.toString(2).length) + + l.toString(2) + ).match(/.{1,8}/g)!; + + let xs = padding.map((x) => UInt8.from(BigInt('0b' + x))); + return xs; + } + ); + + // concatenate the padding with the original padded data + let messageBlocks = zeroPadded.bytes.concat(padding); + + // split the message into 32bit chunks + let chunks: UInt32[] = []; + + for (let i = 0; i < messageBlocks.length; i += 4) { + chunks.push(concat(messageBlocks.slice(i, i + 4))); + } + + // split message blocks into 16 element sized blocks + let xs: UInt32[][] = []; + for (let i = 0; i < chunks.length; i += 16) { + xs.push(chunks.slice(i, i + 16)); + } + return xs; +} + +function concat(xs: UInt8[]) { + let target = new Field(0); + xs.forEach((x) => { + target = Gadgets.leftShift32(target, 8).add(x.value); + }); + Gadgets.rangeCheck32(target); + return new UInt32(target); +} + const SHA256 = { - hash(data: UInt32[][]) { + hash(data: FlexibleBytes) { + const message = Bytes.from(data); // constants §4.2.2 const K = [ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, @@ -67,13 +127,13 @@ const SHA256 = { // TODO: correct dynamic preprocessing §6.2 // padding the message $5.1.1 into blocks that are a multiple of 512 - let messageBlocks = data; + let messageBlocks = padding(data); const N = messageBlocks.length; for (let i = 0; i < N; i++) { const M = messageBlocks[i]; - // for each message block of 16 x 32 bytes do: + // for each message block of 16 x 32bit do: const W: UInt32[] = []; // prepare message block @@ -254,7 +314,7 @@ function sigma(u: UInt32, bits: TupleN, firstShifted = false) { let xRotR2 = x3.add(x012.mul(1 << d3)).seal(); // ^ proves that 2^(32-d2)*x2 < xRotR2 => x2 < 2^d2 if we check xRotR2 < 2^32 later - return UInt32.from(xRotR0).xor(xRotR1).xor(xRotR2); + return UInt32.from(xRotR0).xor(new UInt32(xRotR1)).xor(new UInt32(xRotR2)); } function rangeCheck16(x: Field) { diff --git a/src/lib/int.ts b/src/lib/int.ts index 9bae3afdf1..a98ff8cf4b 100644 --- a/src/lib/int.ts +++ b/src/lib/int.ts @@ -629,6 +629,13 @@ class UInt32 extends CircuitValue { return new UInt32(Field((1n << 32n) - 1n)); } + /** + * Addition modulo 2^32. Check {@link Gadgets.addMod32} for a detailed description. + */ + addMod32(y: UInt32) { + return new UInt32(Gadgets.addMod32(this.value, y.value)); + } + /** * Integer division with remainder. * From 691ac671aed98f24a553e824ec33dccf717cca81 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 19 Dec 2023 15:07:29 +0100 Subject: [PATCH 027/524] finish API, start tests --- src/examples/sha256.ts | 37 ++++++++-- src/lib/gadgets/sha256.ts | 111 ++++++++++++---------------- src/lib/gadgets/sha256.unit-test.ts | 19 +++++ 3 files changed, 95 insertions(+), 72 deletions(-) create mode 100644 src/lib/gadgets/sha256.unit-test.ts diff --git a/src/examples/sha256.ts b/src/examples/sha256.ts index a38926d72c..f6c694952d 100644 --- a/src/examples/sha256.ts +++ b/src/examples/sha256.ts @@ -1,14 +1,35 @@ -import { Bytes, Field, Gadgets, Provable, UInt32, UInt64, UInt8 } from 'o1js'; +import { + Bytes, + Field, + Gadgets, + Provable, + UInt32, + UInt64, + UInt8, + ZkProgram, +} from 'o1js'; -type FlexibleBytes = Bytes | (UInt8 | bigint | number)[] | Uint8Array; +/* +let SHA256 = ZkProgram({ + name: 'sha256', + publicOutput: Bytes(32).provable, + methods: { + sha256: { + privateInputs: [Bytes12.provable], + method(xs: Bytes12) { + return Gadgets.SHA256.hash(xs); + }, + }, + }, +}); -Provable.runAndCheck(() => { - let digest = Gadgets.SHA256.hash(Bytes.fromString('Hello world!')); +await SHA256.compile(); */ +class BytesN extends Bytes(58) {} + +let preimage = BytesN.fromString('hello world!'); + +Gadgets.SHA256.hash(preimage); - Provable.asProver(() => { - console.log(toHex(digest)); - }); -}); function toHex(xs: UInt32[]) { let hex = ''; for (let h = 0; h < xs.length; h++) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 4e2591b858..b466993088 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -9,41 +9,6 @@ import { Gadgets } from './gadgets.js'; export { SHA256 }; -function processStringToMessageBlocks(s: string) { - let msgBits = s - .split('') - .map((c) => { - let binary = c.charCodeAt(0).toString(2); - return '00000000'.substr(binary.length) + binary; - }) - .join(''); - - let l = msgBits.length; - msgBits = msgBits + '1'; - - // calculate k in l + 1 +k = 448 mod 512 - let remainder = (448 - (l + 1)) % 512; - - let k = (remainder + 512) % 512; - let padding = '0'.repeat(k); - msgBits = msgBits + padding; - let lBits = l.toString(2); - msgBits = msgBits + '0'.repeat(64 - lBits.length) + lBits; - - let bitBlocks32 = []; - for (let i = 0; i < msgBits.length; i += 32) { - bitBlocks32.push(UInt32.from(BigInt('0b' + msgBits.substr(i, 32)))); - } - - let lengthBlocks = bitBlocks32.length; - let blocks = []; - for (let i = 0; i < lengthBlocks; i += 16) { - let block = bitBlocks32.slice(i, i + 16); - blocks.push(block); - } - return blocks; -} - function padding(data: FlexibleBytes): UInt32[][] { // pad the data with zeros to reach the desired length let zeroPadded = Bytes.from(data); @@ -63,9 +28,9 @@ function padding(data: FlexibleBytes): UInt32[][] { Provable.Array(UInt8, totalPaddingLength), () => { let padding = ( - '1' + - '0'.repeat(k) + - '0'.repeat(64 - l.toString(2).length) + + '1' + // append 1 bit + '0'.repeat(k) + // append k zero bits + '0'.repeat(64 - l.toString(2).length) + // append 64bit containing the length of the original message l.toString(2) ).match(/.{1,8}/g)!; @@ -75,23 +40,24 @@ function padding(data: FlexibleBytes): UInt32[][] { ); // concatenate the padding with the original padded data - let messageBlocks = zeroPadded.bytes.concat(padding); + let paddedMessage = zeroPadded.bytes.concat(padding); // split the message into 32bit chunks let chunks: UInt32[] = []; - for (let i = 0; i < messageBlocks.length; i += 4) { - chunks.push(concat(messageBlocks.slice(i, i + 4))); + for (let i = 0; i < paddedMessage.length; i += 4) { + chunks.push(concat(paddedMessage.slice(i, i + 4))); } - // split message blocks into 16 element sized blocks - let xs: UInt32[][] = []; + // split message into 16 element sized message blocks + let messageBlocks: UInt32[][] = []; for (let i = 0; i < chunks.length; i += 16) { - xs.push(chunks.slice(i, i + 16)); + messageBlocks.push(chunks.slice(i, i + 16)); } - return xs; + return messageBlocks; } +// concatenate bytes into a 32bit word using bit shifting function concat(xs: UInt8[]) { let target = new Field(0); xs.forEach((x) => { @@ -101,31 +67,44 @@ function concat(xs: UInt8[]) { return new UInt32(target); } +// decompose a 32bit word into 4 bytes +function decomposeToBytes(a: UInt32) { + let field = a.value; + let ys = []; + for (let i = 0; i < 4; i++) { + let { quotient, remainder } = Gadgets.divMod32(field.mul(1n << 8n)); + field = remainder; + ys.push(quotient); + } + + // UInt8.from does a rangeCheck8 + return ys.map(UInt8.from); +} + +// constants §4.2.2 +const K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, +].map((k) => UInt32.from(k)); + const SHA256 = { hash(data: FlexibleBytes) { - const message = Bytes.from(data); - // constants §4.2.2 - const K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, - ].map((k) => UInt32.from(k)); - // initial hash values §5.3.3 const H = [ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, ].map((h) => UInt32.from(h)); - // TODO: correct dynamic preprocessing §6.2 + // preprocessing §6.2 // padding the message $5.1.1 into blocks that are a multiple of 512 let messageBlocks = padding(data); @@ -192,9 +171,13 @@ const SHA256 = { H[7] = H[7].addMod32(h); } - return H; + let ys: UInt8[] = []; + H.forEach((x) => { + ys.push(...decomposeToBytes(x)); + }); + + return Bytes.from(ys); }, - processStringToMessageBlocks: processStringToMessageBlocks, }; function Ch(x: UInt32, y: UInt32, z: UInt32) { diff --git a/src/lib/gadgets/sha256.unit-test.ts b/src/lib/gadgets/sha256.unit-test.ts new file mode 100644 index 0000000000..e91f1b1411 --- /dev/null +++ b/src/lib/gadgets/sha256.unit-test.ts @@ -0,0 +1,19 @@ +import { Field } from '../field.js'; +import { ZkProgram } from '../proof_system.js'; +import { Bytes } from '../provable-types/provable-types.js'; +import { Gadgets } from './gadgets.js'; +import { sha256 as nobleSha256 } from '@noble/hashes/sha256'; +import { bytes } from './test-utils.js'; +import { equivalent } from '../testing/equivalent.js'; +import { Random, sample } from '../testing/random.js'; + +sample(Random.nat(100), 10).forEach((preimageLength) => { + let inputBytes = bytes(preimageLength); + let outputBytes = bytes(256 / 8); + + equivalent({ from: [inputBytes], to: outputBytes, verbose: true })( + (x) => nobleSha256(x), + (x) => Gadgets.SHA256.hash(x), + `sha256 preimage length ${preimageLength}` + ); +}); From dc4ddb97d442057a81b4f85328f8089e9bea72ef Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 19 Dec 2023 15:21:45 +0100 Subject: [PATCH 028/524] fix block size, add tests --- src/lib/gadgets/sha256.ts | 4 ++++ src/lib/gadgets/sha256.unit-test.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index b466993088..33eb392c56 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -22,6 +22,10 @@ function padding(data: FlexibleBytes): UInt32[][] { let l = data.length * 8; // length in bits let k = (448 - (l + 1)) % 512; + + // pad for new blog size + if (k < 0) k += 512; + let totalPaddingLength = (k + 1 + 64) / 8; // total length of the padding let padding = Provable.witness( diff --git a/src/lib/gadgets/sha256.unit-test.ts b/src/lib/gadgets/sha256.unit-test.ts index e91f1b1411..5a7b8104fc 100644 --- a/src/lib/gadgets/sha256.unit-test.ts +++ b/src/lib/gadgets/sha256.unit-test.ts @@ -7,7 +7,7 @@ import { bytes } from './test-utils.js'; import { equivalent } from '../testing/equivalent.js'; import { Random, sample } from '../testing/random.js'; -sample(Random.nat(100), 10).forEach((preimageLength) => { +sample(Random.nat(400), 25).forEach((preimageLength) => { let inputBytes = bytes(preimageLength); let outputBytes = bytes(256 / 8); From c49e07bad5e44868637aa313e8d2ec9d75332094 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 19 Dec 2023 16:05:24 +0100 Subject: [PATCH 029/524] add tests --- src/lib/gadgets/sha256.ts | 33 +++++++-------- src/lib/gadgets/sha256.unit-test.ts | 63 +++++++++++++++++++++++++++-- 2 files changed, 77 insertions(+), 19 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 33eb392c56..13933a7da5 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -1,5 +1,5 @@ // https://csrc.nist.gov/pubs/fips/180-4/upd1/final -import { Field } from '../field.js'; +import { Field } from '../core.js'; import { UInt32, UInt8 } from '../int.js'; import { Bytes, FlexibleBytes, createBytes } from '../provable-types/bytes.js'; import { Provable } from '../provable.js'; @@ -85,21 +85,6 @@ function decomposeToBytes(a: UInt32) { return ys.map(UInt8.from); } -// constants §4.2.2 -const K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, -].map((k) => UInt32.from(k)); - const SHA256 = { hash(data: FlexibleBytes) { // initial hash values §5.3.3 @@ -108,6 +93,22 @@ const SHA256 = { 0x1f83d9ab, 0x5be0cd19, ].map((h) => UInt32.from(h)); + // I dont like to have this in here but UInt32 isn't initialized if used at top level + // constants §4.2.2 + const K = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + ].map((k) => UInt32.from(k)); + // preprocessing §6.2 // padding the message $5.1.1 into blocks that are a multiple of 512 let messageBlocks = padding(data); diff --git a/src/lib/gadgets/sha256.unit-test.ts b/src/lib/gadgets/sha256.unit-test.ts index 5a7b8104fc..c2ade3f822 100644 --- a/src/lib/gadgets/sha256.unit-test.ts +++ b/src/lib/gadgets/sha256.unit-test.ts @@ -1,13 +1,13 @@ -import { Field } from '../field.js'; import { ZkProgram } from '../proof_system.js'; import { Bytes } from '../provable-types/provable-types.js'; import { Gadgets } from './gadgets.js'; import { sha256 as nobleSha256 } from '@noble/hashes/sha256'; import { bytes } from './test-utils.js'; -import { equivalent } from '../testing/equivalent.js'; +import { equivalent, equivalentAsync } from '../testing/equivalent.js'; import { Random, sample } from '../testing/random.js'; +import { expect } from 'expect'; -sample(Random.nat(400), 25).forEach((preimageLength) => { +sample(Random.nat(400), 10).forEach((preimageLength) => { let inputBytes = bytes(preimageLength); let outputBytes = bytes(256 / 8); @@ -17,3 +17,60 @@ sample(Random.nat(400), 25).forEach((preimageLength) => { `sha256 preimage length ${preimageLength}` ); }); + +const Sha256Program = ZkProgram({ + name: `sha256`, + publicOutput: Bytes(32).provable, + methods: { + sha256: { + privateInputs: [Bytes(192).provable], + method(preImage: Bytes) { + return Gadgets.SHA256.hash(preImage); + }, + }, + }, +}); + +const RUNS = 5; + +await Sha256Program.compile(); + +await equivalentAsync( + { + from: [bytes(192)], + to: bytes(32), + }, + { runs: RUNS } +)(nobleSha256, async (x) => { + const proof = await Sha256Program.sha256(x); + await Sha256Program.verify(proof); + return proof.publicOutput; +}); + +for (let { preimage, hash } of testVectors()) { + class BytesN extends Bytes(preimage.length) {} + let actual = Gadgets.SHA256.hash(BytesN.fromString(preimage)); + expect(actual.toHex()).toEqual(hash); +} + +function testVectors() { + return [ + { + preimage: 'abc', + hash: 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', + }, + { + preimage: 'a'.repeat(1000000), + hash: 'cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0', + }, + { + preimage: '', + hash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', + }, + { + preimage: + 'de188941a3375d3a8a061e67576e926dc71a7fa3f0cceb97452b4d3227965f9ea8cc75076d9fb9c5417aa5cb30fc22198b34982dbb629e', + hash: '70b6ee0dd06c26d51177d5bb1de954d6d50aa9f7b771b4401415d43da40605ad', + }, + ]; +} From ea83f74350fe8a2f32aba8d6036b394b0c15a3d6 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 19 Dec 2023 16:08:43 +0100 Subject: [PATCH 030/524] add trivial example --- src/examples/sha256.ts | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/examples/sha256.ts b/src/examples/sha256.ts index f6c694952d..fcda4698dd 100644 --- a/src/examples/sha256.ts +++ b/src/examples/sha256.ts @@ -1,15 +1,7 @@ -import { - Bytes, - Field, - Gadgets, - Provable, - UInt32, - UInt64, - UInt8, - ZkProgram, -} from 'o1js'; +import { Bytes, Gadgets, ZkProgram } from 'o1js'; + +class Bytes12 extends Bytes(12) {} -/* let SHA256 = ZkProgram({ name: 'sha256', publicOutput: Bytes(32).provable, @@ -23,17 +15,16 @@ let SHA256 = ZkProgram({ }, }); -await SHA256.compile(); */ -class BytesN extends Bytes(58) {} - -let preimage = BytesN.fromString('hello world!'); +await SHA256.compile(); +let preimage = Bytes12.fromString('hello world!'); -Gadgets.SHA256.hash(preimage); +let proof = await SHA256.sha256(preimage); -function toHex(xs: UInt32[]) { - let hex = ''; - for (let h = 0; h < xs.length; h++) - hex = hex + ('00000000' + xs[h].toBigint().toString(16)).slice(-8); +let isValid = await SHA256.verify(proof); - return hex; -} +if ( + proof.publicOutput.toHex() !== + '7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9' +) + throw new Error('Invalid sha256 digest!'); +if (!isValid) throw new Error('Invalid proof'); From 9b61ead488fee31e80aaf174574af432606d6bce Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 19 Dec 2023 16:14:22 +0100 Subject: [PATCH 031/524] move example, dump VKs --- src/examples/{ => crypto}/sha256.ts | 10 ++++++---- tests/vk-regression/vk-regression.json | 13 +++++++++++++ tests/vk-regression/vk-regression.ts | 2 ++ 3 files changed, 21 insertions(+), 4 deletions(-) rename src/examples/{ => crypto}/sha256.ts (74%) diff --git a/src/examples/sha256.ts b/src/examples/crypto/sha256.ts similarity index 74% rename from src/examples/sha256.ts rename to src/examples/crypto/sha256.ts index fcda4698dd..1b79085058 100644 --- a/src/examples/sha256.ts +++ b/src/examples/crypto/sha256.ts @@ -1,8 +1,10 @@ import { Bytes, Gadgets, ZkProgram } from 'o1js'; +export { SHA256Program }; + class Bytes12 extends Bytes(12) {} -let SHA256 = ZkProgram({ +let SHA256Program = ZkProgram({ name: 'sha256', publicOutput: Bytes(32).provable, methods: { @@ -15,12 +17,12 @@ let SHA256 = ZkProgram({ }, }); -await SHA256.compile(); +await SHA256Program.compile(); let preimage = Bytes12.fromString('hello world!'); -let proof = await SHA256.sha256(preimage); +let proof = await SHA256Program.sha256(preimage); -let isValid = await SHA256.verify(proof); +let isValid = await SHA256Program.verify(proof); if ( proof.publicOutput.toHex() !== diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index a7ff2ecb38..74c98a7c4d 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -264,5 +264,18 @@ "data": "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", "hash": "24853426911779666983119365864639019692354940105641954611250992067926077919455" } + }, + "sha256": { + "digest": "94f9e7bfe7224549a32ebf3e2f65e5d848e9feebd4c79a5d966a084c28b41fe", + "methods": { + "sha256": { + "rows": 5957, + "digest": "ea7ff27557e7c6270c200f2feb62a3a7" + } + }, + "verificationKey": { + "data": "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", + "hash": "5242875480670941030919675131962218019722619843591732956374487945418325506772" + } } } \ No newline at end of file diff --git a/tests/vk-regression/vk-regression.ts b/tests/vk-regression/vk-regression.ts index 9c63c7e38e..bc6a644934 100644 --- a/tests/vk-regression/vk-regression.ts +++ b/tests/vk-regression/vk-regression.ts @@ -7,6 +7,7 @@ import { ecdsa, keccakAndEcdsa, } from '../../src/examples/crypto/ecdsa/ecdsa.js'; +import { SHA256Program } from '../../src/examples/crypto/sha256.js'; import { GroupCS, BitwiseCS, HashCS } from './plain-constraint-system.js'; // toggle this for quick iteration when debugging vk regressions @@ -45,6 +46,7 @@ const ConstraintSystems: MinimumConstraintSystem[] = [ HashCS, ecdsa, keccakAndEcdsa, + SHA256Program, ]; let filePath = jsonPath ? jsonPath : './tests/vk-regression/vk-regression.json'; From 9e9a3cb70bd4ae5f75fc87c6ef94baa868375889 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 19 Dec 2023 17:11:41 +0100 Subject: [PATCH 032/524] add SHA256 to Hash namespace --- src/lib/hashes-combined.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib/hashes-combined.ts b/src/lib/hashes-combined.ts index 8440a25b32..edd64f8355 100644 --- a/src/lib/hashes-combined.ts +++ b/src/lib/hashes-combined.ts @@ -1,3 +1,4 @@ +import { Gadgets } from './gadgets/gadgets.js'; import { Poseidon } from './hash.js'; import { Keccak } from './keccak.js'; import { Bytes } from './provable-types/provable-types.js'; @@ -35,6 +36,19 @@ const Hash = { */ Poseidon, + /** + * The SHA2 hash function with an output length of 256 bits. + */ + SHA2_256: { + /** + * Hashes the given bytes using SHA2-256. + * + * This is an alias for `Gadgets.SHA256.hash(bytes)`.\ + * See {@link Gadgets.SHA256.hash} for details and usage examples. + */ + hash: Gadgets.SHA256.hash, + }, + /** * The SHA3 hash function with an output length of 256 bits. */ From a6252bdb970d217a62926f863bf86275efe11478 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 20 Dec 2023 13:10:09 +0100 Subject: [PATCH 033/524] make witness constant --- src/lib/gadgets/sha256.ts | 44 +++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 13933a7da5..f489181b09 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -1,7 +1,7 @@ // https://csrc.nist.gov/pubs/fips/180-4/upd1/final import { Field } from '../core.js'; import { UInt32, UInt8 } from '../int.js'; -import { Bytes, FlexibleBytes, createBytes } from '../provable-types/bytes.js'; +import { Bytes, FlexibleBytes } from '../provable-types/bytes.js'; import { Provable } from '../provable.js'; import { TupleN } from '../util/types.js'; import { bitSlice, exists } from './common.js'; @@ -11,37 +11,29 @@ export { SHA256 }; function padding(data: FlexibleBytes): UInt32[][] { // pad the data with zeros to reach the desired length + // this is because we need to inherit to a static sized Bytes array. unrelated to sha256 let zeroPadded = Bytes.from(data); // now pad the data to reach the format expected by sha256 // pad 1 bit, followed by k zero bits where k is the smallest non-negative solution to // l + 1 + k = 448 mod 512 // then append a 64bit block containing the length of the original message in bits - // TODO: question, most values are witnessed because we dont need to prove the padding. - // The user is incentivized to provide the correct padding because otherwise the hash will be wrong. - let l = data.length * 8; // length in bits + let l = zeroPadded.length * 8; // length in bits let k = (448 - (l + 1)) % 512; - // pad for new blog size + // pad message exceeds 512bit and needs a new block if (k < 0) k += 512; - let totalPaddingLength = (k + 1 + 64) / 8; // total length of the padding + let paddingBits = ( + '1' + // append 1 bit + '0'.repeat(k) + // append k zero bits + '0'.repeat(64 - l.toString(2).length) + // append 64bit containing the length of the original message + l.toString(2) + ).match(/.{1,8}/g)!; // this should always be devisable by 8 - let padding = Provable.witness( - Provable.Array(UInt8, totalPaddingLength), - () => { - let padding = ( - '1' + // append 1 bit - '0'.repeat(k) + // append k zero bits - '0'.repeat(64 - l.toString(2).length) + // append 64bit containing the length of the original message - l.toString(2) - ).match(/.{1,8}/g)!; - - let xs = padding.map((x) => UInt8.from(BigInt('0b' + x))); - return xs; - } - ); + // map the padding bit string to UInt8 elements + let padding = paddingBits.map((x) => UInt8.from(BigInt('0b' + x))); // concatenate the padding with the original padded data let paddedMessage = zeroPadded.bytes.concat(padding); @@ -50,10 +42,12 @@ function padding(data: FlexibleBytes): UInt32[][] { let chunks: UInt32[] = []; for (let i = 0; i < paddedMessage.length; i += 4) { - chunks.push(concat(paddedMessage.slice(i, i + 4))); + // chunk 4 bytes into one UInt32, as expected by SHA256 + chunks.push(concatToUInt32(paddedMessage.slice(i, i + 4))); } // split message into 16 element sized message blocks + // SHA256 expects n-blocks of 512bit each, 16*32bit = 512bit let messageBlocks: UInt32[][] = []; for (let i = 0; i < chunks.length; i += 16) { messageBlocks.push(chunks.slice(i, i + 16)); @@ -62,11 +56,13 @@ function padding(data: FlexibleBytes): UInt32[][] { } // concatenate bytes into a 32bit word using bit shifting -function concat(xs: UInt8[]) { +function concatToUInt32(xs: UInt8[]) { let target = new Field(0); xs.forEach((x) => { + // for each element we shift the target by 8 bits and add the element target = Gadgets.leftShift32(target, 8).add(x.value); }); + // making sure its actually 32bit Gadgets.rangeCheck32(target); return new UInt32(target); } @@ -76,12 +72,14 @@ function decomposeToBytes(a: UInt32) { let field = a.value; let ys = []; for (let i = 0; i < 4; i++) { + // for each byte we rotate the element and get the excess bits (8 at a time) and construct a UInt8 of it let { quotient, remainder } = Gadgets.divMod32(field.mul(1n << 8n)); + // "shift" the element by 8 bit to get the next byte sequence during the next iteration field = remainder; ys.push(quotient); } - // UInt8.from does a rangeCheck8 + // UInt8.from does a rangeCheck8 for Field elements return ys.map(UInt8.from); } From 3ba3824261e3f14f8f8405e6967e627f80fd790c Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 20 Dec 2023 13:16:36 +0100 Subject: [PATCH 034/524] simplify --- src/lib/gadgets/sha256.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index f489181b09..b922fb4b73 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -121,10 +121,12 @@ const SHA256 = { // prepare message block for (let t = 0; t <= 15; t++) W[t] = M[t]; for (let t = 16; t <= 63; t++) { + // the field element is unreduced and not proven to be 32bit, we will do this later to save constraints let unreduced = DeltaOne(W[t - 2]) .value.add(W[t - 7].value) .add(DeltaZero(W[t - 15]).value.add(W[t - 16].value)); + // mod 32bit the unreduced field element W[t] = UInt32.from(Gadgets.divMod32(unreduced, 16).remainder); } @@ -140,12 +142,14 @@ const SHA256 = { // main loop for (let t = 0; t <= 63; t++) { + // T1 is unreduced and not proven to be 32bit, we will do this later to save constraints const unreducedT1 = h.value .add(SigmaOne(e).value) .add(Ch(e, f, g).value) .add(K[t].value) .add(W[t].value); + // T2 is also unreduced const unreducedT2 = SigmaZero(a).value.add(Maj(a, b, c).value); h = g; @@ -153,13 +157,13 @@ const SHA256 = { f = e; e = UInt32.from( Gadgets.divMod32(d.value.add(unreducedT1), 16).remainder - ); + ); // mod 32bit the unreduced field element d = c; c = b; b = a; a = UInt32.from( Gadgets.divMod32(unreducedT2.add(unreducedT1), 16).remainder - ); + ); // mod 32bit } // new intermediate hash value @@ -174,12 +178,15 @@ const SHA256 = { H[7] = H[7].addMod32(h); } + /* let ys: UInt8[] = []; H.forEach((x) => { ys.push(...decomposeToBytes(x)); }); + */ - return Bytes.from(ys); + // the working variables H[i] are 32bit, however we want to decompose them into bytes to be more compatible + return Bytes.from(H.map((x) => decomposeToBytes(x)).flat()); }, }; From 719742456a0be94a717191c022debaf9dbef7bb5 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 20 Dec 2023 13:25:51 +0100 Subject: [PATCH 035/524] add doc comments --- src/lib/gadgets/gadgets.ts | 20 +++++++++++++++++++- src/lib/gadgets/sha256.ts | 8 -------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/lib/gadgets/gadgets.ts b/src/lib/gadgets/gadgets.ts index 74c95cb812..18954d4080 100644 --- a/src/lib/gadgets/gadgets.ts +++ b/src/lib/gadgets/gadgets.ts @@ -798,7 +798,25 @@ const Gadgets = { * */ addMod32, - // TODO: everything + /** + * Implementation of the [SHA256 hash function.](https://en.wikipedia.org/wiki/SHA-2) Hash function with 256bit output. + * + * Applies the SHA2-256 hash function to a list of big-endian byte-sized elements. + * + * The function accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). + * Alternatively, you can pass plain `number[]`, `bigint[]` or `Uint8Array` to perform a hash outside provable code. + * + * Produces an output of {@link Bytes} that conforms to the chosen bit length. + * Both input and output bytes are big-endian. + * + * @param data - Big-endian {@link Bytes} representing the message to hash. + * + * ```ts + * let preimage = Bytes.fromString("hello world"); + * let digest = Gadgets.SHA256.hash(preimage); + * ``` + * + */ SHA256: SHA256, }; diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index b922fb4b73..e428826921 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -167,7 +167,6 @@ const SHA256 = { } // new intermediate hash value - H[0] = H[0].addMod32(a); H[1] = H[1].addMod32(b); H[2] = H[2].addMod32(c); @@ -178,13 +177,6 @@ const SHA256 = { H[7] = H[7].addMod32(h); } - /* - let ys: UInt8[] = []; - H.forEach((x) => { - ys.push(...decomposeToBytes(x)); - }); - */ - // the working variables H[i] are 32bit, however we want to decompose them into bytes to be more compatible return Bytes.from(H.map((x) => decomposeToBytes(x)).flat()); }, From 498ab345562484b9d26782fff33d464423b9d928 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 20 Dec 2023 13:31:49 +0100 Subject: [PATCH 036/524] Update sha256.ts --- src/lib/gadgets/sha256.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index e428826921..f877e3fc8e 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -2,7 +2,6 @@ import { Field } from '../core.js'; import { UInt32, UInt8 } from '../int.js'; import { Bytes, FlexibleBytes } from '../provable-types/bytes.js'; -import { Provable } from '../provable.js'; import { TupleN } from '../util/types.js'; import { bitSlice, exists } from './common.js'; import { Gadgets } from './gadgets.js'; From ebe202869378dd8be99fc5b79900fe28e66166c8 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 20 Dec 2023 13:33:40 +0100 Subject: [PATCH 037/524] undo accidental commit --- src/lib/gadgets/arithmetic.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/lib/gadgets/arithmetic.ts b/src/lib/gadgets/arithmetic.ts index 31350bfa6a..414ddfe814 100644 --- a/src/lib/gadgets/arithmetic.ts +++ b/src/lib/gadgets/arithmetic.ts @@ -1,13 +1,12 @@ -import { Bool } from '../bool.js'; import { provableTuple } from '../circuit_value.js'; import { Field } from '../core.js'; import { assert } from '../errors.js'; import { Provable } from '../provable.js'; -import { rangeCheck32, rangeCheckN } from './range-check.js'; +import { rangeCheck32 } from './range-check.js'; export { divMod32, addMod32 }; -function divMod32(n: Field, quotientBits = 32) { +function divMod32(n: Field) { if (n.isConstant()) { assert( n.toBigInt() < 1n << 64n, @@ -33,11 +32,7 @@ function divMod32(n: Field, quotientBits = 32) { } ); - if (quotientBits === 1) { - Bool.check(Bool.Unsafe.ofField(quotient)); - } else { - rangeCheckN(quotientBits, quotient); - } + rangeCheck32(quotient); rangeCheck32(remainder); n.assertEquals(quotient.mul(1n << 32n).add(remainder)); @@ -49,5 +44,5 @@ function divMod32(n: Field, quotientBits = 32) { } function addMod32(x: Field, y: Field) { - return divMod32(x.add(y), 1).remainder; + return divMod32(x.add(y)).remainder; } From 68b6d1a0176caffaadadc549fc87c8dafea8080d Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 20 Dec 2023 13:38:43 +0100 Subject: [PATCH 038/524] add commit back :X --- src/lib/gadgets/arithmetic.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/lib/gadgets/arithmetic.ts b/src/lib/gadgets/arithmetic.ts index 414ddfe814..31350bfa6a 100644 --- a/src/lib/gadgets/arithmetic.ts +++ b/src/lib/gadgets/arithmetic.ts @@ -1,12 +1,13 @@ +import { Bool } from '../bool.js'; import { provableTuple } from '../circuit_value.js'; import { Field } from '../core.js'; import { assert } from '../errors.js'; import { Provable } from '../provable.js'; -import { rangeCheck32 } from './range-check.js'; +import { rangeCheck32, rangeCheckN } from './range-check.js'; export { divMod32, addMod32 }; -function divMod32(n: Field) { +function divMod32(n: Field, quotientBits = 32) { if (n.isConstant()) { assert( n.toBigInt() < 1n << 64n, @@ -32,7 +33,11 @@ function divMod32(n: Field) { } ); - rangeCheck32(quotient); + if (quotientBits === 1) { + Bool.check(Bool.Unsafe.ofField(quotient)); + } else { + rangeCheckN(quotientBits, quotient); + } rangeCheck32(remainder); n.assertEquals(quotient.mul(1n << 32n).add(remainder)); @@ -44,5 +49,5 @@ function divMod32(n: Field) { } function addMod32(x: Field, y: Field) { - return divMod32(x.add(y)).remainder; + return divMod32(x.add(y), 1).remainder; } From 175228b65166914fc8e4712cc51dec4cf268f04d Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Wed, 20 Dec 2023 18:23:28 +0100 Subject: [PATCH 039/524] add flow comments --- src/examples/crypto/sha256.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/examples/crypto/sha256.ts b/src/examples/crypto/sha256.ts index 1b79085058..bc973499d9 100644 --- a/src/examples/crypto/sha256.ts +++ b/src/examples/crypto/sha256.ts @@ -17,13 +17,21 @@ let SHA256Program = ZkProgram({ }, }); +console.time('compile'); await SHA256Program.compile(); +console.timeEnd('compile'); + let preimage = Bytes12.fromString('hello world!'); -let proof = await SHA256Program.sha256(preimage); +console.log('sha256 rows:', SHA256Program.analyzeMethods().sha256.rows); +console.time('prove'); +let proof = await SHA256Program.sha256(preimage); +console.timeEnd('prove'); let isValid = await SHA256Program.verify(proof); +console.log('digest:', proof.publicOutput.toHex()); + if ( proof.publicOutput.toHex() !== '7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9' From 06afa39e09cb643707f3f5e4c233ad604686591a Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Wed, 20 Dec 2023 18:28:15 +0100 Subject: [PATCH 040/524] dump vks --- tests/vk-regression/vk-regression.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 329b1f1f40..74c98a7c4d 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -236,33 +236,33 @@ } }, "ecdsa-only": { - "digest": "1e6bef24a2e02b573363f3512822d401d53ec7220c8d5837cd49691c19723028", + "digest": "2113edb508f10afee42dd48aec81ac7d06805d76225b0b97300501136486bb30", "methods": { "verifySignedHash": { - "rows": 30680, - "digest": "5fe00efee7ecfb82b3ca69c8dd23d07f" + "rows": 38888, + "digest": "f75dd9e49c88eb6097a7f3abbe543467" } }, "verificationKey": { - "data": "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", - "hash": "13090090603196708539583054794074329820941412942119534377592583560995629520780" + "data": "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", + "hash": "10504586047480864396273137275551599454708712068910013426206550544367939284599" } }, "ecdsa": { - "digest": "334c2efc6a82e87319cadb7f3e6f9edb55f8f2eab71e0bcf2451f3d5536de5fe", + "digest": "baf90de64c1b6b5b5938a0b80a8fdb70bdbbc84a292dd7eccfdbce470c10b4c", "methods": { "sha3": { "rows": 14494, "digest": "949539824d56622702d9ac048e8111e9" }, "verifyEcdsa": { - "rows": 45178, - "digest": "0b6ce4cc658bcca79b1b1373569aa9b9" + "rows": 53386, + "digest": "5a234cff8ea48ce653cbd7efa2e1c241" } }, "verificationKey": { - "data": "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", - "hash": "18381574995221799963215366500837755447342811019610547066832598459350935665488" + "data": "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", + "hash": "24853426911779666983119365864639019692354940105641954611250992067926077919455" } }, "sha256": { From c1e14dcc5840231d2ce64b7c238de04d5fe68ccb Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 21 Dec 2023 09:27:50 +0100 Subject: [PATCH 041/524] remove redundant keccak-only circuit from ecdsa example --- src/examples/crypto/ecdsa/ecdsa.ts | 8 -------- src/examples/crypto/ecdsa/run.ts | 13 ++++--------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/examples/crypto/ecdsa/ecdsa.ts b/src/examples/crypto/ecdsa/ecdsa.ts index 45639c41cb..bebe6484fb 100644 --- a/src/examples/crypto/ecdsa/ecdsa.ts +++ b/src/examples/crypto/ecdsa/ecdsa.ts @@ -27,14 +27,6 @@ const keccakAndEcdsa = ZkProgram({ return signature.verify(message, publicKey); }, }, - - sha3: { - privateInputs: [], - method(message: Bytes32) { - Keccak.nistSha3(256, message); - return Bool(true); - }, - }, }, }); diff --git a/src/examples/crypto/ecdsa/run.ts b/src/examples/crypto/ecdsa/run.ts index 2a497de373..c3acaa19c2 100644 --- a/src/examples/crypto/ecdsa/run.ts +++ b/src/examples/crypto/ecdsa/run.ts @@ -13,19 +13,14 @@ let signature = Ecdsa.sign(message.toBytes(), privateKey.toBigInt()); // investigate the constraint system generated by ECDSA verify console.time('ecdsa verify only (build constraint system)'); -let csEcdsa = ecdsa.analyzeMethods().verifySignedHash; +let csEcdsa = ecdsa.analyzeMethods(); console.timeEnd('ecdsa verify only (build constraint system)'); -console.log(csEcdsa.summary()); - -console.time('keccak only (build constraint system)'); -let csKeccak = keccakAndEcdsa.analyzeMethods().sha3; -console.timeEnd('keccak only (build constraint system)'); -console.log(csKeccak.summary()); +console.log(csEcdsa.verifySignedHash.summary()); console.time('keccak + ecdsa verify (build constraint system)'); -let cs = keccakAndEcdsa.analyzeMethods().verifyEcdsa; +let cs = keccakAndEcdsa.analyzeMethods(); console.timeEnd('keccak + ecdsa verify (build constraint system)'); -console.log(cs.summary()); +console.log(cs.verifyEcdsa.summary()); // compile and prove From 0a60312534fd5e50efa71cbded0541ddcfae699d Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 12:04:21 +0100 Subject: [PATCH 042/524] move example --- .../crypto/{sha256.ts => sha256/run.ts} | 19 ++----------------- src/examples/crypto/sha256/sha256.ts | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 17 deletions(-) rename src/examples/crypto/{sha256.ts => sha256/run.ts} (63%) create mode 100644 src/examples/crypto/sha256/sha256.ts diff --git a/src/examples/crypto/sha256.ts b/src/examples/crypto/sha256/run.ts similarity index 63% rename from src/examples/crypto/sha256.ts rename to src/examples/crypto/sha256/run.ts index bc973499d9..4cb750cabc 100644 --- a/src/examples/crypto/sha256.ts +++ b/src/examples/crypto/sha256/run.ts @@ -1,21 +1,6 @@ -import { Bytes, Gadgets, ZkProgram } from 'o1js'; +import { Bytes12, SHA256Program } from './sha256.js'; -export { SHA256Program }; - -class Bytes12 extends Bytes(12) {} - -let SHA256Program = ZkProgram({ - name: 'sha256', - publicOutput: Bytes(32).provable, - methods: { - sha256: { - privateInputs: [Bytes12.provable], - method(xs: Bytes12) { - return Gadgets.SHA256.hash(xs); - }, - }, - }, -}); +console.log('sha256 rows:', SHA256Program.analyzeMethods().sha256.rows); console.time('compile'); await SHA256Program.compile(); diff --git a/src/examples/crypto/sha256/sha256.ts b/src/examples/crypto/sha256/sha256.ts new file mode 100644 index 0000000000..a85357bda6 --- /dev/null +++ b/src/examples/crypto/sha256/sha256.ts @@ -0,0 +1,18 @@ +import { Bytes, Gadgets, ZkProgram } from 'o1js'; + +export { SHA256Program, Bytes12 }; + +class Bytes12 extends Bytes(12) {} + +let SHA256Program = ZkProgram({ + name: 'sha256', + publicOutput: Bytes(32).provable, + methods: { + sha256: { + privateInputs: [Bytes12.provable], + method(xs: Bytes12) { + return Gadgets.SHA256.hash(xs); + }, + }, + }, +}); From f789d5a4c3b57f0c2378b8e7eadda1dcbc4fe516 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 12:06:21 +0100 Subject: [PATCH 043/524] dump vks --- src/examples/crypto/sha256/run.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/examples/crypto/sha256/run.ts b/src/examples/crypto/sha256/run.ts index 4cb750cabc..b1d72e3fa6 100644 --- a/src/examples/crypto/sha256/run.ts +++ b/src/examples/crypto/sha256/run.ts @@ -1,7 +1,5 @@ import { Bytes12, SHA256Program } from './sha256.js'; -console.log('sha256 rows:', SHA256Program.analyzeMethods().sha256.rows); - console.time('compile'); await SHA256Program.compile(); console.timeEnd('compile'); From ca4c7c9f6403d7aa3657956ad47f200fb0d20793 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 12:06:32 +0100 Subject: [PATCH 044/524] dump vks --- tests/vk-regression/vk-regression.json | 30 +++++++++++++------------- tests/vk-regression/vk-regression.ts | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 74c98a7c4d..ec705078d8 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -236,46 +236,46 @@ } }, "ecdsa-only": { - "digest": "2113edb508f10afee42dd48aec81ac7d06805d76225b0b97300501136486bb30", + "digest": "1e6bef24a2e02b573363f3512822d401d53ec7220c8d5837cd49691c19723028", "methods": { "verifySignedHash": { - "rows": 38888, - "digest": "f75dd9e49c88eb6097a7f3abbe543467" + "rows": 30680, + "digest": "5fe00efee7ecfb82b3ca69c8dd23d07f" } }, "verificationKey": { - "data": "AAAdmtvKeZvyx7UyPW6rIhb96GnTZEEywf8pGpbkt+QXNIm7oWxIDWYa4EWTiadEqzk8WXg3wiZmbXWcqBQU+uIoTiBnYTRcd7RsaAjbdbIbQJ9EuopFRFewZRx9qeQeEibNeMRcRMP4LdfS3AQRxhFZzN4HFa4MbtGs+Aja820cI9VFULH2/7BvD6JjpVWjVLvvo6zhO3S5axqfDh7QqtkPo3TLpand9OVvMHhTVlz/AV7rus5E/+0cv50MaEJ/wBfUh5XNLAlGgVi7FfWR6p9P72AAymyD3lUdecJyZmCREiVgPrTdFppkp45TefJWNTySkV9c5YzpNxQoXedZDvYP/5s4KBkfIeK+zB2yJC9eZ1ZDYfM88shGDYxmBtur9AkQ49QGquR+kYUI0lpXtuNMG+ZRy0FRJ8ci/TE+PIPIFnSiGcSOA3YM2G171LYf89abU2QUoQRHSP3PmmAOy/8CoRLVro7Nl6z/Ou0oZzX7RjOEo//LBqcSWa2S9X8TQz0R3uivbovTdq0rrba56SbEnK6LWItmBc6CubYWL7UzDbD3RZM6iRz1hqTHDzDz7UIWOzHgLqW9rjnZllQCyfsSAHV05f+qac/ZBDmwqzaprv0J0hiho1m+s3yNkKQVSOkFyy3T9xnMBFjK62dF1KOp2k1Uvadd2KRyqwXiGN7JtQwgKzcNZhhPW5VfbcSYDpx5nVaU5pTEFl+2+RlcuhBpG1ksAWbD64AUKDjdyTWIC5Wn68AagPtG65V13eFS5LgkSfVtNXxGodg7SdP4AJmXpBgZfzMg4RW6Qje5ZFfrwRzoHPo0y7nO1hkaNLGV3Wvd3/pYiebXvyo+DdTZmaMbJpJaGSCysnovOrVUIpcn4h1hvA12jztQFQcbNHoVeZgslPxA54y9ynjhN7VZfT8lNXXIrRCpmPaxZW6Bw6Op/g6P1Y8pKZHixBy1UrxqWGI+49oRtRFGw9CWS21EekuBFeu9RKI6yZLDiyRC2b3koFG+Kp6oq5Ej6Q8uargE09Ag9D9DKKoexOqr3N/Z3GGptvh3qvOPyxcWf475b+B/fTIwTQQC8ykkZ35HAVW3ZT6XDz0QFSmB0NJ8A+lkaTa0JF46ddCU9VJ1JmYsYa+MYEgKjZCvABbX9AEY2ggMr1cHaA49GrGul+Sj6pAvz4oyzaR8m7WAPMDuBtVwdbDtfju3CVbhX15uBoKhuYWQgLr2rnVJ5SOZoDvlwJtcK2izLMYVAasejw4fvsehYGb88wvDbFxS6sM9gDSgTlavZRs95Qf+c1KpYf/jb8BxYNrwrqy8F++c1APDzfzQ/IbVLiaL28wkEy412qmXSjM+9hErKXFy8JIT/WBOIWMMg/7mMu1Al7Tt/kOZrDznlS/szLlpAp2jISa8VWCmlAEPrustdNqQvptSsF6hikzXZVXg5f8pU4Gpa0TP0TRFvIYfmTyl8HpdFOG7vpQavC600YgzS2YGtY7K2WQ5GtN5ZTZBHPsUSir2yKSo9Le9CWXbDtn3SBDepWypwDa3YWKtNog+y10VmpL1N+RG3u1DXSuY7y9WZgkQ7tdvyx/Gjr91kjF0s3bt7vHIAZCtzNlRlWDBz3og0cSnEucCEuKR6dL2Mz+RuF1GmLoXZXapUjVG/82BjdAMAOxPlE67lEs+JWgnrVrA5NLJoL4DZ6+fhQKpNfk0uOrEfZIWR9Sau0IBwBxu6IYVm5/XAB19dt8MAuVcRdN/JGGzo0Hr3WVJuKzbAhuFwJZzcd1J1n4xO09ECT5NQdFSFXGsy8kIFjRNEOkLl+bAExePtGCt0w6cYqB0uCeX3lTI7ugIEgdStMtHFiWngJ218l8CuVrkwTJ7ZqHLtuJDiNqlLptkHWChDfw+IgDwz85dZrfBBzQrMRWranxQmisM+wx3vC+pLURRQHZJEasGCAElj0lTColrqQ/cXS7cBaqs1tBsQDGzKYMCMwsqL53fyxGCljVvljBa99+FpYfoUK+Fi0z6uEbem+luXRScr2yPB5I08lnBY23RmBb/pfSyBfbcmnmF5BkRlJTJKY7fQL/t9bFfywoquQe9e7OQvIjppA/FO7HmZS6hoOU+eS8+W94fEF2gvrowpTeqQHM6hLN9Qzl8niwZWUIyRCfyuzQnuSz/VP1K2sMFBKnZZNDcuBh1/xSFymOH6LfNKostvc6qHTIxrTjlH6952bo1bQl+mVvBUaJuRkYh12QbcyIyzcBFUYwaFazzkHXMof0O30oL3Q6wegTvJxTSZD5VCr5D26Myzoa0JBpqL0st9/MNGZe5a/+HW1qan/VtGA5nYkJcUzwKVqqlmZeuOZekFLGxlfp0lv9IQUQWtiU5uvd5HVoolEc/teUnx/IxYe01IDxX9cbmPMJnLYXJGSY=", - "hash": "10504586047480864396273137275551599454708712068910013426206550544367939284599" + "data": "AAAdmtvKeZvyx7UyPW6rIhb96GnTZEEywf8pGpbkt+QXNIm7oWxIDWYa4EWTiadEqzk8WXg3wiZmbXWcqBQU+uIoTiBnYTRcd7RsaAjbdbIbQJ9EuopFRFewZRx9qeQeEibNeMRcRMP4LdfS3AQRxhFZzN4HFa4MbtGs+Aja820cI9VFULH2/7BvD6JjpVWjVLvvo6zhO3S5axqfDh7QqtkPo3TLpand9OVvMHhTVlz/AV7rus5E/+0cv50MaEJ/wBfUh5XNLAlGgVi7FfWR6p9P72AAymyD3lUdecJyZmCREiVgPrTdFppkp45TefJWNTySkV9c5YzpNxQoXedZDvYP/5s4KBkfIeK+zB2yJC9eZ1ZDYfM88shGDYxmBtur9AkQ49QGquR+kYUI0lpXtuNMG+ZRy0FRJ8ci/TE+PIPIFnSiGcSOA3YM2G171LYf89abU2QUoQRHSP3PmmAOy/8CoRLVro7Nl6z/Ou0oZzX7RjOEo//LBqcSWa2S9X8TQz0R3uivbovTdq0rrba56SbEnK6LWItmBc6CubYWL7UzDbD3RZM6iRz1hqTHDzDz7UIWOzHgLqW9rjnZllQCyfsSAEce2RV0gBkOlsJXf/A50Yo1Y+0y0ZMB/g8wkRIs0p8RIff5piGXJPfSak+7+oCoV/CQoa0RkkegIKmjsjOyMAAgKzcNZhhPW5VfbcSYDpx5nVaU5pTEFl+2+RlcuhBpG1ksAWbD64AUKDjdyTWIC5Wn68AagPtG65V13eFS5LgkSfVtNXxGodg7SdP4AJmXpBgZfzMg4RW6Qje5ZFfrwRzoHPo0y7nO1hkaNLGV3Wvd3/pYiebXvyo+DdTZmaMbJpJaGSCysnovOrVUIpcn4h1hvA12jztQFQcbNHoVeZgslPxA54y9ynjhN7VZfT8lNXXIrRCpmPaxZW6Bw6Op/g6FGIs8TlruzZJRhz1lOLvl2FPvrUsFtz4yWTPjbT+VGsKuJFPvMuYybxq8pGyWVQN023uObDel47krlcQoH4MAggd39s50O0IaSbMgpJUWQVx+sxoXepe26SF5LQjWRDf7usrBVYTYoI9gDkVXMxLRmNnjQsKjg65fnQdhdLHyE/DR5DLlenSa0wQ3PXdv/C9LpDvkzJOLZs+/ZePd4YMI0+WuP2+6Xas4aNM+4JkNuHF5uMDcxgWID4TUy7Vdlzm3CVbhX15uBoKhuYWQgLr2rnVJ5SOZoDvlwJtcK2izLMYVAasejw4fvsehYGb88wvDbFxS6sM9gDSgTlavZRs95Qf+c1KpYf/jb8BxYNrwrqy8F++c1APDzfzQ/IbVLiaL28wkEy412qmXSjM+9hErKXFy8JIT/WBOIWMMg/7mMjvxngHnci+aYJZ6J+Lszh5zgo708vzO7fwaxC0wgd8anH3gFrbFnOg1hkmmoUEIgIwXh+ynuoZPOaoKNXNm1jOl8HpdFOG7vpQavC600YgzS2YGtY7K2WQ5GtN5ZTZBHPsUSir2yKSo9Le9CWXbDtn3SBDepWypwDa3YWKtNog+y10VmpL1N+RG3u1DXSuY7y9WZgkQ7tdvyx/Gjr91kjF0s3bt7vHIAZCtzNlRlWDBz3og0cSnEucCEuKR6dL2Mz+RuF1GmLoXZXapUjVG/82BjdAMAOxPlE67lEs+JWgnrVrA5NLJoL4DZ6+fhQKpNfk0uOrEfZIWR9Sau0IBwBxu6IYVm5/XAB19dt8MAuVcRdN/JGGzo0Hr3WVJuKzbAhuFwJZzcd1J1n4xO09ECT5NQdFSFXGsy8kIFjRNEOkLl+bAExePtGCt0w6cYqB0uCeX3lTI7ugIEgdStMtHFiWngJ218l8CuVrkwTJ7ZqHLtuJDiNqlLptkHWChDfw+IgDwz85dZrfBBzQrMRWranxQmisM+wx3vC+pLURRQHZJEasGCAElj0lTColrqQ/cXS7cBaqs1tBsQDGzKYMCMwsqL53fyxGCljVvljBa99+FpYfoUK+Fi0z6uEbem+luXRScr2yPB5I08lnBY23RmBb/pfSyBfbcmnmF5BkRlJTJKY7fQL/t9bFfywoquQe9e7OQvIjppA/FO7HmZS6hoOU+eS8+W94fEF2gvrowpTeqQHM6hLN9Qzl8niwZWUIyRCfyuzQnuSz/VP1K2sMFBKnZZNDcuBh1/xSFymOH6LfNKostvc6qHTIxrTjlH6952bo1bQl+mVvBUaJuRkYh12QbcyIyzcBFUYwaFazzkHXMof0O30oL3Q6wegTvJxTSZD5VCr5D26Myzoa0JBpqL0st9/MNGZe5a/+HW1qan/VtGA5nYkJcUzwKVqqlmZeuOZekFLGxlfp0lv9IQUQWtiU5uvd5HVoolEc/teUnx/IxYe01IDxX9cbmPMJnLYXJGSY=", + "hash": "13090090603196708539583054794074329820941412942119534377592583560995629520780" } }, "ecdsa": { - "digest": "baf90de64c1b6b5b5938a0b80a8fdb70bdbbc84a292dd7eccfdbce470c10b4c", + "digest": "334c2efc6a82e87319cadb7f3e6f9edb55f8f2eab71e0bcf2451f3d5536de5fe", "methods": { "sha3": { "rows": 14494, "digest": "949539824d56622702d9ac048e8111e9" }, "verifyEcdsa": { - "rows": 53386, - "digest": "5a234cff8ea48ce653cbd7efa2e1c241" + "rows": 45178, + "digest": "0b6ce4cc658bcca79b1b1373569aa9b9" } }, "verificationKey": { - "data": "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", - "hash": "24853426911779666983119365864639019692354940105641954611250992067926077919455" + "data": "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", + "hash": "18381574995221799963215366500837755447342811019610547066832598459350935665488" } }, "sha256": { - "digest": "94f9e7bfe7224549a32ebf3e2f65e5d848e9feebd4c79a5d966a084c28b41fe", + "digest": "15bce9c541c0619f3b3587d7c06918e1423c5d1d4efe6ba6e0c789987e41b512", "methods": { "sha256": { - "rows": 5957, - "digest": "ea7ff27557e7c6270c200f2feb62a3a7" + "rows": 5314, + "digest": "bfad0408f0baab2e3db346a3e9997455" } }, "verificationKey": { - "data": "AACa0OPxg0UDEf5DBLZf/TtdB4TzIAMNQC467+/R1yGnL1tRXJnn0BDcLG0fGWdqFcWK1q2zKdAYfORKVOHgvKEwxtxZYv4CjM7SwlG09G52oNZmOgGCilD0ntmby4rzJTaoyCimx5ynQ/oYjslJgSM/1DTAkpW6IIdFjjkmjlOSHqRNRAMtNtl44D9lbhci4blHbDvzQnlYynwRh58jAzoDj3bCkPsByviAyFBoPhUx2M13h0/VPK1ND/69djzZgi9lQKaON74XvbnvJdSAYLQSIBbOE0yo7tS1vGTPqDqJGzc1f0+2QhZttE2UEUV4PPEGB6LUFEuQPXK8lXyXHVQTOU+omjpvKHLLs/dQZLTSvvZ3UFqxUvxjSEG9eTPo3Cyt4wXcPEi1b993ePSSxP6njj1SoPBA4BvLCCcvaxz5DegUu7nZDKkC8tuaoNbqYcayaf/8+oZ+dA+Ek1Xe08og1Hz9gB9cfPvgI9EiL2Na2cgiOF2klHHEAj3fORN8UQVzs0ioqLcQeq2tH2UMMtZS4JNcohHUah1T0jKmS3eZDqe+aMuf5qzKA5oKRF2ejRwmsxI161YlgXXpmHs+kCQGAMfWLQPS0/gbveVfQ3HLmsXF+Hvfl3J3IBdUECqVnbsExIc5Pacvo8DgeGxJObnGn5pp6P761+YUr/WuUqVyYSP4U20AHCGYSahuSFbBO6dDhRCPGRCJJgxktY+CSO+3B9mLtCe2X23FeFsUnaAFlzmsWeKR1tMyPupNsUXPFugubYZYd1EwxUDeBIOLeahcqX78yDDMOdsr0QHNG8XteyXcRXGFUSzTTKNEXdD+BOMY6FfMroKO4y3FffyrZef1PRsu/kVwaZb4UthvhBwet8DBcMa26kISlsI1ItcvsdwST5x1+iLCM4hBcFB0ly3N18uR7+MLHU0AdV13INFo8indyz1XEZCHlm+tKF3LX8Z7G0v68uRmjKRgy/S9NjKPA+M3rbj4pDU+jS4EKN++nKYxXjwdKJXu4XvbUU9ITKM7N7nZr0RbamEC3Mi3j7dV69psfJY41NUnqTvf3N6dGxdZ7zyPZ5YRyP7VMZMroxw+31ZYUyTYYQ4Kjb6mXDTbP00OzQ8/vAiYAdVmcHuS+M1etSdnWerxU4E3vC2odvcjNq2yh/VyODIwPt/UPYT1soPpv6M3XSyvpE1kVb0TmD91v6mXvfq23wSDn7ndgLx52m+CGnEGzA/OwwVQnZaFNq+sZjKjOa8ALFNcjyS8IuYEz4XYMiSy/wCl2n2AHVIc6djnQZySvECly6HHJSpWpWv8zvNfLUiWozg4ActV4QzQsd2nagCedaw1z82uWcKLraPboPGke+1dGOqg8OFiBJUBoW/bROBgw2H8WnPIhcofMwGOCrXpMGvA4LKnn3okzY3hTNfex1t7zCpxQC2YRBN0Ze8qOELHTYvOlSwG1AQqZDhC//VDGFvvQ1ZgzsmpnbyNGTGMaCKRMGI0evgxD6Ziitu5k9ogVW6dGSR9cBM0NryOxQl6JcwUXd0twqsLI0pbH7cjlGzWsylGuufx/m77GPRqnysk6r+ibZ4fDBFDumJFxxbS4AqzXLR+nNg42hJbvuxsyZnCRpkB2N9xH3VX8/Il8BX40HdEE/08l3JmL3jn8Bznqwj8z3TqBFMdRGOOq2/5DjwcjaNh9pYRn6bDl/1qVPrJkUExPECrQRymHQ3JERZjm1G+a3bhAJFeb+Ak4D8PlK2RgPcMCS7bBXWuPRh0z4FKGnhZnecNmuWMSvUFsSZcoaFKfyNanX8qMsu+KWtWEbDwwQ49NrfCmg45/WAOQxX8LKMYgUrDpSVdE/bM+JqYpq0AmOHAhoIdlOC7jVMIPI6LEAVJC1PrFQBS3HbH+u5IMQ684sJehPFtd1pjpfboDrnbgfhnjFf9HqS5bG1sR1Dh2mXGXpQ+ni+O3FvEYCEZY+UU9d9XCGwL2OZIhtMi6M6qcnrn11w2MyaZ8U6aZxVTMFvKQ2JLtwVGVnMDuSkNC+iN711acwssgbTpsgHLdVbtFR1oYcbpJHe6a9SFquG+q9qfzT15IzKpBzxn6BVXfhhFGpJRAbU0bXbjOpeceg7vaV7RykTzBoIzAe5aUVAdKNM6fzGlPw16xx7QeOW+LFlOm6HJyxYAZfbpB/BLza4ZhoqmVx+ALUXHFIztgGQK9rzm+jBwiuwuLqdD2W00cbTZcbgKTo48XD6NJ+8T4J9B3rPzht3qbgpN//TyYkfrzAercAa/HCvFeBNXl1slCj8cF/EO6iX/NnIxBkuqmXfQnGUfcFK0LZPsvd7RInaLEYTeA4ZDfChiuw+5nTmrJFOywwOYdIA+NiMfCh24dPYAAwEGb9KLEP9u7/Rp5uPi0S3tuTw67yg=", - "hash": "5242875480670941030919675131962218019722619843591732956374487945418325506772" + "data": "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", + "hash": "801809578510365639167868048464129098535493687043603299559390072759232736708" } } } \ No newline at end of file diff --git a/tests/vk-regression/vk-regression.ts b/tests/vk-regression/vk-regression.ts index bc6a644934..cccf97e4bc 100644 --- a/tests/vk-regression/vk-regression.ts +++ b/tests/vk-regression/vk-regression.ts @@ -7,7 +7,7 @@ import { ecdsa, keccakAndEcdsa, } from '../../src/examples/crypto/ecdsa/ecdsa.js'; -import { SHA256Program } from '../../src/examples/crypto/sha256.js'; +import { SHA256Program } from '../../src/examples/crypto/sha256/sha256.js'; import { GroupCS, BitwiseCS, HashCS } from './plain-constraint-system.js'; // toggle this for quick iteration when debugging vk regressions From a816814bd2277d29af6dc7a59c6a7361c4540d6c Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 12:10:31 +0100 Subject: [PATCH 045/524] improve doc comment --- src/lib/gadgets/gadgets.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib/gadgets/gadgets.ts b/src/lib/gadgets/gadgets.ts index 18954d4080..fedbc8b89b 100644 --- a/src/lib/gadgets/gadgets.ts +++ b/src/lib/gadgets/gadgets.ts @@ -801,15 +801,14 @@ const Gadgets = { /** * Implementation of the [SHA256 hash function.](https://en.wikipedia.org/wiki/SHA-2) Hash function with 256bit output. * - * Applies the SHA2-256 hash function to a list of big-endian byte-sized elements. + * Applies the SHA2-256 hash function to a list of byte-sized elements. * * The function accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). * Alternatively, you can pass plain `number[]`, `bigint[]` or `Uint8Array` to perform a hash outside provable code. * * Produces an output of {@link Bytes} that conforms to the chosen bit length. - * Both input and output bytes are big-endian. * - * @param data - Big-endian {@link Bytes} representing the message to hash. + * @param data - {@link Bytes} representing the message to hash. * * ```ts * let preimage = Bytes.fromString("hello world"); From 86930e8a9c9a150922e43d869a5cfb7997b26ec8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 21 Dec 2023 12:14:37 +0100 Subject: [PATCH 046/524] bindings and rename parse hex --- src/bindings | 2 +- src/lib/provable-context.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bindings b/src/bindings index 84483cd12a..5f650144e1 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 84483cd12abca5a8d2b16c03083976cecced721f +Subproject commit 5f650144e14dd937c0038cf30b0923958282f453 diff --git a/src/lib/provable-context.ts b/src/lib/provable-context.ts index 3b79562889..c285ee81f8 100644 --- a/src/lib/provable-context.ts +++ b/src/lib/provable-context.ts @@ -1,6 +1,6 @@ import { Context } from './global-context.js'; import { Gate, GateType, JsonGate, Snarky } from '../snarky.js'; -import { parseHexString } from '../bindings/crypto/bigint-helpers.js'; +import { parseHexString32 } from '../bindings/crypto/bigint-helpers.js'; import { prettifyStacktrace } from './errors.js'; import { Fp } from '../bindings/crypto/finite_field.js'; @@ -126,7 +126,7 @@ function constraintSystem(f: () => T) { function gatesFromJson(cs: { gates: JsonGate[]; public_input_size: number }) { let gates: Gate[] = cs.gates.map(({ typ, wires, coeffs: hexCoeffs }) => { - let coeffs = hexCoeffs.map((hex) => parseHexString(hex).toString()); + let coeffs = hexCoeffs.map((hex) => parseHexString32(hex).toString()); return { type: typ, wires, coeffs }; }); return { publicInputSize: cs.public_input_size, gates }; From 7747e75fd102cd3dcd17be0e2bc1b5e6cd8a82cd Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 12:46:51 +0100 Subject: [PATCH 047/524] address part of feedback --- src/lib/gadgets/sha256.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index f877e3fc8e..7878d01075 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -1,7 +1,10 @@ // https://csrc.nist.gov/pubs/fips/180-4/upd1/final +import { mod } from '../../bindings/crypto/finite_field.js'; import { Field } from '../core.js'; import { UInt32, UInt8 } from '../int.js'; -import { Bytes, FlexibleBytes } from '../provable-types/bytes.js'; +import { FlexibleBytes } from '../provable-types/bytes.js'; +import { Bytes } from '../provable-types/provable-types.js'; +import { chunk } from '../util/arrays.js'; import { TupleN } from '../util/types.js'; import { bitSlice, exists } from './common.js'; import { Gadgets } from './gadgets.js'; @@ -9,20 +12,17 @@ import { Gadgets } from './gadgets.js'; export { SHA256 }; function padding(data: FlexibleBytes): UInt32[][] { - // pad the data with zeros to reach the desired length - // this is because we need to inherit to a static sized Bytes array. unrelated to sha256 - let zeroPadded = Bytes.from(data); + // create a provable Bytes instance from the input data + // the Bytes class will be static sized according to the length of the input data + let message = Bytes.from(data); // now pad the data to reach the format expected by sha256 // pad 1 bit, followed by k zero bits where k is the smallest non-negative solution to // l + 1 + k = 448 mod 512 // then append a 64bit block containing the length of the original message in bits - let l = zeroPadded.length * 8; // length in bits - let k = (448 - (l + 1)) % 512; - - // pad message exceeds 512bit and needs a new block - if (k < 0) k += 512; + let l = message.length * 8; // length in bits + let k = Number(mod(448n - (BigInt(l) + 1n), 512n)); let paddingBits = ( '1' + // append 1 bit @@ -35,7 +35,7 @@ function padding(data: FlexibleBytes): UInt32[][] { let padding = paddingBits.map((x) => UInt8.from(BigInt('0b' + x))); // concatenate the padding with the original padded data - let paddedMessage = zeroPadded.bytes.concat(padding); + let paddedMessage = message.bytes.concat(padding); // split the message into 32bit chunks let chunks: UInt32[] = []; @@ -47,11 +47,7 @@ function padding(data: FlexibleBytes): UInt32[][] { // split message into 16 element sized message blocks // SHA256 expects n-blocks of 512bit each, 16*32bit = 512bit - let messageBlocks: UInt32[][] = []; - for (let i = 0; i < chunks.length; i += 16) { - messageBlocks.push(chunks.slice(i, i + 16)); - } - return messageBlocks; + return chunk(chunks, 16); } // concatenate bytes into a 32bit word using bit shifting From 198ecb830cca33c3b4048d1a96e29978e67be3ea Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 12:56:27 +0100 Subject: [PATCH 048/524] move constants --- src/lib/gadgets/sha256.ts | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 7878d01075..763df489ac 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -8,9 +8,32 @@ import { chunk } from '../util/arrays.js'; import { TupleN } from '../util/types.js'; import { bitSlice, exists } from './common.js'; import { Gadgets } from './gadgets.js'; +import { rangeCheck16 } from './range-check.js'; export { SHA256 }; +const SHA256Constants = { + // constants §4.2.2 + K: [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, + 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, + 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, + 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, + 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + ], + // initial hash values §5.3.3 + H: [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, + 0x1f83d9ab, 0x5be0cd19, + ], +}; + function padding(data: FlexibleBytes): UInt32[][] { // create a provable Bytes instance from the input data // the Bytes class will be static sized according to the length of the input data @@ -80,32 +103,13 @@ function decomposeToBytes(a: UInt32) { const SHA256 = { hash(data: FlexibleBytes) { - // initial hash values §5.3.3 - const H = [ - 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, - 0x1f83d9ab, 0x5be0cd19, - ].map((h) => UInt32.from(h)); - - // I dont like to have this in here but UInt32 isn't initialized if used at top level - // constants §4.2.2 - const K = [ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, - ].map((k) => UInt32.from(k)); - // preprocessing §6.2 // padding the message $5.1.1 into blocks that are a multiple of 512 let messageBlocks = padding(data); + const H = SHA256Constants.H.map((x) => UInt32.from(x)); + const K = SHA256Constants.K.map((x) => UInt32.from(x)); + const N = messageBlocks.length; for (let i = 0; i < N; i++) { @@ -296,7 +300,3 @@ function sigma(u: UInt32, bits: TupleN, firstShifted = false) { return UInt32.from(xRotR0).xor(new UInt32(xRotR1)).xor(new UInt32(xRotR2)); } - -function rangeCheck16(x: Field) { - x.rangeCheckHelper(16).assertEquals(x); -} From 98bed632f587d349845c3fa81c88ab6bda54a1b2 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 12:56:33 +0100 Subject: [PATCH 049/524] improve tests --- src/lib/gadgets/sha256.unit-test.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/gadgets/sha256.unit-test.ts b/src/lib/gadgets/sha256.unit-test.ts index c2ade3f822..f17c7ad51e 100644 --- a/src/lib/gadgets/sha256.unit-test.ts +++ b/src/lib/gadgets/sha256.unit-test.ts @@ -3,15 +3,15 @@ import { Bytes } from '../provable-types/provable-types.js'; import { Gadgets } from './gadgets.js'; import { sha256 as nobleSha256 } from '@noble/hashes/sha256'; import { bytes } from './test-utils.js'; -import { equivalent, equivalentAsync } from '../testing/equivalent.js'; +import { equivalentAsync, equivalentProvable } from '../testing/equivalent.js'; import { Random, sample } from '../testing/random.js'; import { expect } from 'expect'; -sample(Random.nat(400), 10).forEach((preimageLength) => { +sample(Random.nat(400), 5).forEach((preimageLength) => { let inputBytes = bytes(preimageLength); let outputBytes = bytes(256 / 8); - equivalent({ from: [inputBytes], to: outputBytes, verbose: true })( + equivalentProvable({ from: [inputBytes], to: outputBytes, verbose: true })( (x) => nobleSha256(x), (x) => Gadgets.SHA256.hash(x), `sha256 preimage length ${preimageLength}` @@ -48,8 +48,7 @@ await equivalentAsync( }); for (let { preimage, hash } of testVectors()) { - class BytesN extends Bytes(preimage.length) {} - let actual = Gadgets.SHA256.hash(BytesN.fromString(preimage)); + let actual = Gadgets.SHA256.hash(Bytes.fromString(preimage)); expect(actual.toHex()).toEqual(hash); } From e98fae941ce4d3c2ddfbaff18e049303cfdfc288 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 13:52:40 +0100 Subject: [PATCH 050/524] move keccak helper functions --- src/lib/gadgets/common.ts | 50 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts index 9da4490723..190124d802 100644 --- a/src/lib/gadgets/common.ts +++ b/src/lib/gadgets/common.ts @@ -2,6 +2,9 @@ import { Field, FieldConst, FieldVar, VarField } from '../field.js'; import { Tuple, TupleN } from '../util/types.js'; import { Snarky } from '../../snarky.js'; import { MlArray } from '../ml/base.js'; +import { UInt8 } from '../int.js'; +import { Provable } from '../provable.js'; +import { chunk } from '../util/arrays.js'; const MAX_BITS = 64 as const; @@ -15,6 +18,10 @@ export { assert, bitSlice, divideWithRemainder, + bytesToWord, + bytesToWords, + wordsToBytes, + wordToBytes, }; function existsOne(compute: () => bigint) { @@ -77,3 +84,46 @@ function divideWithRemainder(numerator: bigint, denominator: bigint) { const remainder = numerator - denominator * quotient; return { quotient, remainder }; } + +/** + * Convert an array of UInt8 to a Field element. Expects little endian representation. + */ +function bytesToWord(wordBytes: UInt8[]): Field { + return wordBytes.reduce((acc, byte, idx) => { + const shift = 1n << BigInt(8 * idx); + return acc.add(byte.value.mul(shift)); + }, Field.from(0)); +} + +/** + * Convert a Field element to an array of UInt8. Expects little endian representation. + * @param bytesPerWord number of bytes per word + */ +function wordToBytes(word: Field, bytesPerWord = 8): UInt8[] { + let bytes = Provable.witness(Provable.Array(UInt8, bytesPerWord), () => { + let w = word.toBigInt(); + return Array.from({ length: bytesPerWord }, (_, k) => + UInt8.from((w >> BigInt(8 * k)) & 0xffn) + ); + }); + + // check decomposition + bytesToWord(bytes).assertEquals(word); + + return bytes; +} + +/** + * Convert an array of Field elements to an array of UInt8. Expects little endian representation. + * @param bytesPerWord number of bytes per word + */ +function wordsToBytes(words: Field[], bytesPerWord = 8): UInt8[] { + return words.flatMap((w) => wordToBytes(w, bytesPerWord)); +} +/** + * Convert an array of UInt8 to an array of Field elements. Expects little endian representation. + * @param bytesPerWord number of bytes per word + */ +function bytesToWords(bytes: UInt8[], bytesPerWord = 8): Field[] { + return chunk(bytes, bytesPerWord).map(bytesToWord); +} From a1aa7c8ba559a80566bea5a929c6c233238cb4d8 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 13:52:46 +0100 Subject: [PATCH 051/524] move helpers --- src/lib/keccak.ts | 36 +----------------------------------- 1 file changed, 1 insertion(+), 35 deletions(-) diff --git a/src/lib/keccak.ts b/src/lib/keccak.ts index ccc8c45548..e4e2e4aec1 100644 --- a/src/lib/keccak.ts +++ b/src/lib/keccak.ts @@ -1,11 +1,10 @@ import { Field } from './field.js'; import { Gadgets } from './gadgets/gadgets.js'; import { assert } from './errors.js'; -import { Provable } from './provable.js'; -import { chunk } from './util/arrays.js'; import { FlexibleBytes } from './provable-types/bytes.js'; import { UInt8 } from './int.js'; import { Bytes } from './provable-types/provable-types.js'; +import { bytesToWords, wordsToBytes } from './gadgets/common.js'; export { Keccak }; @@ -508,39 +507,6 @@ const BytesOfBitlength = { 512: Bytes64, }; -// AUXILARY FUNCTIONS - -// Auxiliary functions to check the composition of 8 byte values (LE) into a 64-bit word and create constraints for it - -function bytesToWord(wordBytes: UInt8[]): Field { - return wordBytes.reduce((acc, byte, idx) => { - const shift = 1n << BigInt(8 * idx); - return acc.add(byte.value.mul(shift)); - }, Field.from(0)); -} - -function wordToBytes(word: Field): UInt8[] { - let bytes = Provable.witness(Provable.Array(UInt8, BYTES_PER_WORD), () => { - let w = word.toBigInt(); - return Array.from({ length: BYTES_PER_WORD }, (_, k) => - UInt8.from((w >> BigInt(8 * k)) & 0xffn) - ); - }); - - // check decomposition - bytesToWord(bytes).assertEquals(word); - - return bytes; -} - -function bytesToWords(bytes: UInt8[]): Field[] { - return chunk(bytes, BYTES_PER_WORD).map(bytesToWord); -} - -function wordsToBytes(words: Field[]): UInt8[] { - return words.flatMap(wordToBytes); -} - // xor which avoids doing anything on 0 inputs // (but doesn't range-check the other input in that case) function xor(x: Field, y: Field): Field { From 015db6b1d1ebd2b87f51b6d9442e2316826d3d29 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Thu, 21 Dec 2023 13:52:56 +0100 Subject: [PATCH 052/524] simply decomposition of variables --- src/lib/gadgets/sha256.ts | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 763df489ac..60e1cfdb8d 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -4,9 +4,10 @@ import { Field } from '../core.js'; import { UInt32, UInt8 } from '../int.js'; import { FlexibleBytes } from '../provable-types/bytes.js'; import { Bytes } from '../provable-types/provable-types.js'; +import { Provable } from '../provable.js'; import { chunk } from '../util/arrays.js'; import { TupleN } from '../util/types.js'; -import { bitSlice, exists } from './common.js'; +import { bitSlice, bytesToWord, exists, wordToBytes } from './common.js'; import { Gadgets } from './gadgets.js'; import { rangeCheck16 } from './range-check.js'; @@ -65,7 +66,10 @@ function padding(data: FlexibleBytes): UInt32[][] { for (let i = 0; i < paddedMessage.length; i += 4) { // chunk 4 bytes into one UInt32, as expected by SHA256 - chunks.push(concatToUInt32(paddedMessage.slice(i, i + 4))); + // bytesToWord expects little endian, so we reverse the bytes + chunks.push( + UInt32.from(bytesToWord(paddedMessage.slice(i, i + 4).reverse())) + ); } // split message into 16 element sized message blocks @@ -73,18 +77,6 @@ function padding(data: FlexibleBytes): UInt32[][] { return chunk(chunks, 16); } -// concatenate bytes into a 32bit word using bit shifting -function concatToUInt32(xs: UInt8[]) { - let target = new Field(0); - xs.forEach((x) => { - // for each element we shift the target by 8 bits and add the element - target = Gadgets.leftShift32(target, 8).add(x.value); - }); - // making sure its actually 32bit - Gadgets.rangeCheck32(target); - return new UInt32(target); -} - // decompose a 32bit word into 4 bytes function decomposeToBytes(a: UInt32) { let field = a.value; @@ -177,7 +169,8 @@ const SHA256 = { } // the working variables H[i] are 32bit, however we want to decompose them into bytes to be more compatible - return Bytes.from(H.map((x) => decomposeToBytes(x)).flat()); + // wordToBytes expects little endian, so we reverse the bytes + return Bytes.from(H.map((x) => wordToBytes(x.value, 4).reverse()).flat()); }, }; From a3d1c81beea2383856488d5a3de8b7e29c8a8572 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 21 Dec 2023 14:09:06 +0100 Subject: [PATCH 053/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 5f650144e1..7fcd4cd6f5 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 5f650144e14dd937c0038cf30b0923958282f453 +Subproject commit 7fcd4cd6f5b90c7c5c50b15377777528d8b35e41 From 5655928d38341290d73e5860435195fa44b61ad4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 21 Dec 2023 14:28:18 +0100 Subject: [PATCH 054/524] dump vks --- tests/vk-regression/vk-regression.json | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 9a183bc35e..2f69465d55 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -249,20 +249,16 @@ } }, "ecdsa": { - "digest": "334c2efc6a82e87319cadb7f3e6f9edb55f8f2eab71e0bcf2451f3d5536de5fe", + "digest": "3e80a93d93a93f6152d70a9c21a2979ff0094c2d648a67c6d2daa4b2b0d18309", "methods": { - "sha3": { - "rows": 14494, - "digest": "949539824d56622702d9ac048e8111e9" - }, "verifyEcdsa": { "rows": 45178, "digest": "0b6ce4cc658bcca79b1b1373569aa9b9" } }, "verificationKey": { - "data": "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", - "hash": "18381574995221799963215366500837755447342811019610547066832598459350935665488" + "data": "AACzYt9qtBkn6y40KDH0lkzRSMBh3W41urWE6j0PSP2KB9GxsBAHAI3uzay1Vqyc+LMXeANSzNcoYSYnZts9Pk0nFNjCZ84EnGkie609NhFB8tU9k5Vkoqw3jihdsoJEUy6GK0H30dl/7H1rGxsx6Ec05aaFhiPw6t0jLxF1kj4uIeipqOScf8snKuzywk02FqvRxSHlk9pkEsUOvpNIwywxzhvHjWgXEQzROQF8v6q5R/1aJk3swpM1iRct9URLIjdin4GWyDB9279EZ6D6avFW2l7WuMJG++xBqGsNKZUgNM4WkUGNfCd+m42hJgt46eOy89db672su0n24IZG9tAsgQl8vPsVKfsTvTWlMj6/jISm7Dcctr1rZpSb8hRPsQstlfqMw3q6qijtTkFiMsdGRwJ6LNukSFUxOarhVsfREQngJufm4IxFpJJMR5F1DFSDPiOPuylEqXzke+j078Y4vr+QRo07YRlsoEv4a6ChcxMd3uu5Oami+D747/YVaS8kLd/3bO+WFpubID5fv4F7/JO4Fy/O7n1waPpNnzi/PZRlHVlwzNVAs09OmTmgzNM4/jAJBO9lRgCFA1SW0BADAGT9gdb9h2XRFwVa1hFKtWIWgyAp4WKhGZR+Zdfdtrws2CHK+lFtQqWcUvdCxgJs3DGRHI8701bibYD9aj9UNyjPFNzYqZw3swyXzQ3nvZqWU2ARuzo1BgMrvnDgW1H+AMbKbNGU7IYXIYaLfTR9S7qrUbHESHac4wo9J9HmRiU1/IQdyr5LldYkzYtZOrjM4SzBkYYVtpSH7Sopij/TTy0U9CXNle7iCnZQS/72C8kwyJ+BGqpULLkSWhHoj+U9GSW9UgDHZ62jRTzvuZz5QaX/hYOmpNChNMFS1zoDYVE7ZIzVQKX03IDkzHAVJCXggwhQO3NK6OGhlP7A/heM6zgiR3/LlOa8uW4fcow50XC3280SDziK0Uczab3zlYXPPH6KqGPJfnftgwuvcHsRgddOWDVfEH3Q9mAj0y1R1FopyO7bDhkxQK8xD4eSKZFfAJ199/XuQin4Z0LCRBhZIjKnbEk7Y4jD2SMQWP79+5uKBfXEpSzKoaV6DIZDMTSLOg2qDXacvJQHRIiBHfPZ3G52Z2lTf6OGg/elBurqGhA2wdDAQrBIWJwiTClONbV+8yR/4Md7aPi44E4XICLpHhE5hzko7ePy9cwh3oXy3btBt0urRwrl4d/jhHvoYt1eE2inNWEOYdlkXFUDlDErwOpFVsyQon0G25zNLAcVaZgdJLWueU1y3G0XkfHRqMZ8eV1iNAegPCCNRCvJ6SVsSwcQ67s45a8VqFxSSW0F65bDCI6Ue3Hwpb1RFKbfSIJbPyUrVSq5K99wUJ01O93Kn8LQlrAbjHWo5Za+tW0a/+Qlbr5E2eSEge+ldnbMbA9rcJwZf4bT457dBXMdlD7mECIDZtD8M/KLeyzMEinDzPfqnwZjU2ifxs6gaJPXOQAWPzbCm/z2vGlRbXDGZF6yTbLTdjzviuPhVtb7bzsZW2AYC+TlZqb4qm9MAVsH5rX3OZmvvmw5oRKeSj+FFD7uSRwfutDGC99i93uptU8syL/8Tr8xU3atxITlSqHqG+rVGWdLO9i3iq38zXgXbvZacrc3CMF5QBIM8yZXNslXH5k39D5SqubSHBWTqAJ1I0heOjaIHQGLROBYLn178tckBxfKQ2UpyfkvMw1Waw+fp5f64Ce+5bmYyZr6Dhmw/xcoAihjUsEqoecrLuGPp6qI4hQt9qOnVrAxHzwwtJGxcqoiCbe1mgz0fxMCt/i0z3ygdqAn20DKPHuBdqgVUFwx2T7Ac9fUCf3RHMq34onrr2nLHc038GYedmlFjoUZStujGwA8tSwLWyuWZTDVV+ZaW92qkhmrACog6NwhR6SEjQgsMRCVBQZzYirZxyulYmcNWH6BUmnLLFsn3GbS40xUr70gujEPnjZUK/ExGRfUPOfrYYb8mAciE9nP8OeK/UI+zjJy6Qp8mMroFw7gVHCfDtKTeQFt4JV3zubGsD7jypquHKCqPewhgn9tZ1UIsKIQB7+hBwDHzhlOZ2FfR4eLwQkO8sz275tpjHDAqX/TBWWRVg/yBDii0CWN4bP8UuX36jZKZboJUxIkM1xThiGZM2/oMbe5cZyjgrBR3P21wiDHAAlsHkaMfJgkVLqvZOw8hflKRIMa2dEYo5voD6aV30sATHQLoV0o+MlV3WA38RA+23Jqt1g+UZ7ReAuDP88jXhqWFcIvWHrJG0oy+rpAPQU/38vhIxbl//lirsirdVK2LrU47CC1f9/pRi07vTnvAm+n02dhwriqpwOmI2o2OU4mO0q96pCueKjAttkXgz+NSIJzcwprvNyE9UtKWswmIQg=", + "hash": "25447212082831819715054236631079960883754611880602728284997977929479384060913" } } } \ No newline at end of file From 82e0fcc38711fab4232d7057a2e60b47d631bd6e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 21 Dec 2023 15:33:30 +0100 Subject: [PATCH 055/524] fix hash example --- src/bindings | 2 +- src/examples/zkapps/hashing/hash.ts | 11 ++++++----- src/examples/zkapps/hashing/run.ts | 4 ++-- src/lib/hash.ts | 5 +++-- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/bindings b/src/bindings index 7fcd4cd6f5..a884dc593d 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 7fcd4cd6f5b90c7c5c50b15377777528d8b35e41 +Subproject commit a884dc593dbab69e55ab9602b998ec12dfc3a288 diff --git a/src/examples/zkapps/hashing/hash.ts b/src/examples/zkapps/hashing/hash.ts index 9ad9947dfa..e762863c85 100644 --- a/src/examples/zkapps/hashing/hash.ts +++ b/src/examples/zkapps/hashing/hash.ts @@ -9,7 +9,8 @@ import { Bytes, } from 'o1js'; -let initialCommitment: Field = Field(0); +let initialCommitment = Field(0); +class Bytes32 extends Bytes(32) {} export class HashStorage extends SmartContract { @state(Field) commitment = State(); @@ -23,25 +24,25 @@ export class HashStorage extends SmartContract { this.commitment.set(initialCommitment); } - @method SHA256(xs: Bytes) { + @method SHA256(xs: Bytes32) { const shaHash = Hash.SHA3_256.hash(xs); const commitment = Hash.hash(shaHash.toFields()); this.commitment.set(commitment); } - @method SHA384(xs: Bytes) { + @method SHA384(xs: Bytes32) { const shaHash = Hash.SHA3_384.hash(xs); const commitment = Hash.hash(shaHash.toFields()); this.commitment.set(commitment); } - @method SHA512(xs: Bytes) { + @method SHA512(xs: Bytes32) { const shaHash = Hash.SHA3_512.hash(xs); const commitment = Hash.hash(shaHash.toFields()); this.commitment.set(commitment); } - @method Keccak256(xs: Bytes) { + @method Keccak256(xs: Bytes32) { const shaHash = Hash.Keccak256.hash(xs); const commitment = Hash.hash(shaHash.toFields()); this.commitment.set(commitment); diff --git a/src/examples/zkapps/hashing/run.ts b/src/examples/zkapps/hashing/run.ts index 9350211d68..e1568fa95d 100644 --- a/src/examples/zkapps/hashing/run.ts +++ b/src/examples/zkapps/hashing/run.ts @@ -9,7 +9,7 @@ Mina.setActiveInstance(Local); if (proofsEnabled) { console.log('Proofs enabled'); - HashStorage.compile(); + await HashStorage.compile(); } // test accounts that pays all the fees, and puts additional funds into the zkapp @@ -20,7 +20,7 @@ const zkAppPrivateKey = PrivateKey.random(); const zkAppAddress = zkAppPrivateKey.toPublicKey(); const zkAppInstance = new HashStorage(zkAppAddress); -// 0, 1, 2, 3, ..., 32 +// 0, 1, 2, 3, ..., 31 const hashData = Bytes.from(Array.from({ length: 32 }, (_, i) => i)); console.log('Deploying Hash Example....'); diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 8f51d97d0f..1a55da5181 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -7,6 +7,7 @@ import { MlFieldArray } from './ml/fields.js'; import { Poseidon as PoseidonBigint } from '../bindings/crypto/poseidon.js'; import { assert } from './errors.js'; import { rangeCheckN } from './gadgets/range-check.js'; +import { TupleN } from './util/types.js'; // external API export { Poseidon, TokenSymbol }; @@ -45,13 +46,13 @@ const Poseidon = { if (isConstant(input)) { return Field(PoseidonBigint.hash(toBigints(input))); } - return Poseidon.update(this.initialState(), input)[0]; + return Poseidon.update(Poseidon.initialState(), input)[0]; }, update(state: [Field, Field, Field], input: Field[]) { if (isConstant(state) && isConstant(input)) { let newState = PoseidonBigint.update(toBigints(state), toBigints(input)); - return newState.map(Field); + return TupleN.fromArray(3, newState.map(Field)); } let newState = Snarky.poseidon.update( From 8ed2556b7c7278dc210f1daa609c0f4427e3cd86 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 21 Dec 2023 15:39:16 +0100 Subject: [PATCH 056/524] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b30d0e852..781c521b38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/19115a159...HEAD) +### Fixed + +- Fix bug in `Hash.hash()` which always resulted in an error https://github.com/o1-labs/o1js/pull/1346 + ## [0.15.1](https://github.com/o1-labs/o1js/compare/1ad7333e9e...19115a159) ### Breaking changes From 08ba27329e1d2750566d9dcbf584bec9c2f306e8 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 26 Dec 2023 00:04:35 +0000 Subject: [PATCH 057/524] 0.15.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 24af729ad1..e0edcd8ffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "o1js", - "version": "0.15.1", + "version": "0.15.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "o1js", - "version": "0.15.1", + "version": "0.15.2", "license": "Apache-2.0", "dependencies": { "blakejs": "1.2.1", diff --git a/package.json b/package.json index ac4a5736e9..9402f07279 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "o1js", "description": "TypeScript framework for zk-SNARKs and zkApps", - "version": "0.15.1", + "version": "0.15.2", "license": "Apache-2.0", "homepage": "https://github.com/o1-labs/o1js/", "keywords": [ From f39c5e882e6c87e549def393f40b86b47605dfb6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 26 Dec 2023 00:04:43 +0000 Subject: [PATCH 058/524] Update CHANGELOG for new version v0.15.2 --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 781c521b38..2e4d07a15d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm _Security_ in case of vulnerabilities. --> -## [Unreleased](https://github.com/o1-labs/o1js/compare/19115a159...HEAD) +## [Unreleased](https://github.com/o1-labs/o1js/compare/08ba27329...HEAD) + +## [0.15.2](https://github.com/o1-labs/o1js/compare/1ad7333e9e...08ba27329) ### Fixed From a477a0caa918ff988daadf86aa838e525e22553f Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 2 Jan 2024 12:25:43 +0100 Subject: [PATCH 059/524] fix dependency hell --- src/lib/gadgets/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts index 190124d802..8198884be2 100644 --- a/src/lib/gadgets/common.ts +++ b/src/lib/gadgets/common.ts @@ -2,7 +2,7 @@ import { Field, FieldConst, FieldVar, VarField } from '../field.js'; import { Tuple, TupleN } from '../util/types.js'; import { Snarky } from '../../snarky.js'; import { MlArray } from '../ml/base.js'; -import { UInt8 } from '../int.js'; +import { UInt8 } from '../../index.js'; import { Provable } from '../provable.js'; import { chunk } from '../util/arrays.js'; From e318e3ea6b94e28d7370f6b3d046a5e91a558029 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 2 Jan 2024 13:08:13 +0100 Subject: [PATCH 060/524] add assert to export --- CHANGELOG.md | 1 + src/index.ts | 2 ++ src/lib/gadgets/common.ts | 12 +++++++++--- src/lib/gadgets/sha256.ts | 1 - 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b30d0e852..20ca16e89e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - bitwise AND via `{UInt32, UInt64}.and()` - Example for using actions to store a map data structure https://github.com/o1-labs/o1js/pull/1300 - `Provable.constraintSystem()` and `{ZkProgram,SmartContract}.analyzeMethods()` return a `summary()` method to return a summary of the constraints used by a method https://github.com/o1-labs/o1js/pull/1007 +- `assert()` asserts that a given statement is true https://github.com/o1-labs/o1js/pull/1285 ### Fixed diff --git a/src/index.ts b/src/index.ts index 243fe7dbfa..47d35c815a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,6 +13,8 @@ export { Poseidon, TokenSymbol } from './lib/hash.js'; export { Keccak } from './lib/keccak.js'; export { Hash } from './lib/hashes-combined.js'; +export { assert } from './lib/gadgets/common.js'; + export * from './lib/signature.js'; export type { ProvableExtended, diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts index 8198884be2..27c1b49ff0 100644 --- a/src/lib/gadgets/common.ts +++ b/src/lib/gadgets/common.ts @@ -2,7 +2,7 @@ import { Field, FieldConst, FieldVar, VarField } from '../field.js'; import { Tuple, TupleN } from '../util/types.js'; import { Snarky } from '../../snarky.js'; import { MlArray } from '../ml/base.js'; -import { UInt8 } from '../../index.js'; +import { Bool, UInt8 } from '../../index.js'; import { Provable } from '../provable.js'; import { chunk } from '../util/arrays.js'; @@ -69,8 +69,14 @@ function toVars>( return Tuple.map(fields, toVar); } -function assert(stmt: boolean, message?: string): asserts stmt { - if (!stmt) { +/** + * Assert that a statement is true. If the statement is false, throws an error with the given message. + * Can be used in provable code. + */ +function assert(stmt: boolean | Bool, message?: string): asserts stmt { + if (stmt instanceof Bool) { + stmt.assertTrue(message ?? 'Assertion failed'); + } else if (!stmt) { throw Error(message ?? 'Assertion failed'); } } diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 60e1cfdb8d..644cfb8f45 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -4,7 +4,6 @@ import { Field } from '../core.js'; import { UInt32, UInt8 } from '../int.js'; import { FlexibleBytes } from '../provable-types/bytes.js'; import { Bytes } from '../provable-types/provable-types.js'; -import { Provable } from '../provable.js'; import { chunk } from '../util/arrays.js'; import { TupleN } from '../util/types.js'; import { bitSlice, bytesToWord, exists, wordToBytes } from './common.js'; From d0212addc2dc5d1e6be759ca70241006dbe7ecde Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 2 Jan 2024 15:17:35 +0100 Subject: [PATCH 061/524] trigger CI --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e4d07a15d..bd76e3380e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed -- Fix bug in `Hash.hash()` which always resulted in an error https://github.com/o1-labs/o1js/pull/1346 +- Fix bug in `Hash.hash()` which always resulted in an error. https://github.com/o1-labs/o1js/pull/1346 ## [0.15.1](https://github.com/o1-labs/o1js/compare/1ad7333e9e...19115a159) From a7de65a96ecab9de52fd93b180c9e3b1f0fe409f Mon Sep 17 00:00:00 2001 From: Barrie Byron Date: Tue, 2 Jan 2024 13:11:40 -0500 Subject: [PATCH 062/524] fix typo in README.md --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3da515ed33..637a852691 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,26 @@ # o1js   [![npm version](https://img.shields.io/npm/v/o1js.svg?style=flat)](https://www.npmjs.com/package/o1js) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/o1-labs/o1js/blob/main/CONTRIBUTING.md) -ℹ️ **o1js** is an evolution of [SnarkyJS](https://www.npmjs.com/package/snarkyjs) which saw: -49 updated versions over 2 years of development with 43,141 downloads +ℹ️ **o1js** is an evolution of [SnarkyJS](https://www.npmjs.com/package/snarkyjs) which saw +49 updated versions over two years of development with 43,141 downloads. -This name change reflects the evolution of our vision for the premiere toolkit used by developers to build zero knowledge-enabled applications, while paying homage to our technology's recursive proof generation capabilities. +This name change to o1js reflects the evolution of our vision for the premiere toolkit used by developers to build zero knowledge-enabled applications, while paying homage to our technology's recursive proof generation capabilities. Your favorite functionality stays the same and transitioning to o1js is a quick and easy process: - To update zkApp-cli, run the following command: + `npm i -g zkapp-cli@latest` -- To remove the now-deprecated SnarkyJs package and install o1js, run the following command: + +- To remove the now-deprecated SnarkyJS package and install o1js, run the following command: + `npm remove snarkyjs && npm install o1js` + - For existing zkApps, make sure to update your imports from `snarkyjs` to `o1js` - No need to redeploy, you are good to go! ## o1js -o1js helps developers build apps powered by zero-knowledge (zk) cryptography. +o1js helps developers build apps powered by zero knowledge (zk) cryptography. The easiest way to write zk programs is using o1js. @@ -42,6 +46,10 @@ o1js is an open source project. We appreciate all community contributions to o1j See the [Contributing guidelines](https://github.com/o1-labs/o1js/blob/main/CONTRIBUTING.md) for ways you can contribute. +## Development Workflow + +For guidance on building o1js from source and understanding the development workflow, see [o1js README-dev](https://github.com/o1-labs/o1js/blob/main/README-dev.md). + ## Community Packages High-quality community packages from open source developers are available for your project. From f2b6cf49f63c421a521102cb44e046d27051fa85 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 13:14:24 -0800 Subject: [PATCH 063/524] Update submodule URL for o1js-bindings --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 76a36100bc..9e993ea759 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "src/snarkyjs-bindings"] +[submodule "src/o1js-bindings"] path = src/bindings - url = https://github.com/o1-labs/snarkyjs-bindings.git + url = https://github.com/o1-labs/o1js-bindings.git [submodule "src/mina"] path = src/mina url = https://github.com/MinaProtocol/mina.git From b3af8910c17553ccbd0e7e6324c0acf1e0a99b1a Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 14:01:45 -0800 Subject: [PATCH 064/524] Update subproject commit and error handling --- src/bindings | 2 +- src/lib/errors.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index a884dc593d..bcb5f4fb20 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a884dc593dbab69e55ab9602b998ec12dfc3a288 +Subproject commit bcb5f4fb200287427d23bdbc495a57f8f32f8b96 diff --git a/src/lib/errors.ts b/src/lib/errors.ts index b7e65c0338..3fe584eac2 100644 --- a/src/lib/errors.ts +++ b/src/lib/errors.ts @@ -99,7 +99,7 @@ function handleResult(result: any) { * A list of keywords used to filter out unwanted lines from the error stack trace. */ const lineRemovalKeywords = [ - 'snarky_js_node.bc.cjs', + 'o1js_node.bc.cjs', '/builtin/', 'CatchAndPrettifyStacktrace', // Decorator name to remove from stacktrace (covers both class and method decorator) ] as const; From ca4d9bbd39f47ab72cffd8f1ced25c24fce51ea7 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 14:11:52 -0800 Subject: [PATCH 065/524] Update build scripts for o1js bindings --- package.json | 4 ++-- src/bindings | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 9402f07279..ee1d7b87f2 100644 --- a/package.json +++ b/package.json @@ -44,8 +44,8 @@ "scripts": { "dev": "npx tsc -p tsconfig.test.json && node src/build/copy-to-dist.js", "build": "node src/build/copy-artifacts.js && rimraf ./dist/node && npm run dev && node src/build/buildNode.js", - "build:bindings": "./src/bindings/scripts/build-snarkyjs-node.sh", - "build:update-bindings": "./src/bindings/scripts/update-snarkyjs-bindings.sh", + "build:bindings": "./src/bindings/scripts/build-o1js-node.sh", + "build:update-bindings": "./src/bindings/scripts/update-o1js-bindings.sh", "build:wasm": "./src/bindings/scripts/update-wasm-and-types.sh", "build:web": "rimraf ./dist/web && node src/build/buildWeb.js", "build:examples": "npm run build && rimraf ./dist/examples && npx tsc -p tsconfig.examples.json", diff --git a/src/bindings b/src/bindings index bcb5f4fb20..263bea2c09 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit bcb5f4fb200287427d23bdbc495a57f8f32f8b96 +Subproject commit 263bea2c09f626717f239e150735294e9f72471a From 43f84138401e9f53f182d27c48d6c4238fae3c10 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 14:20:23 -0800 Subject: [PATCH 066/524] Update import statements to use o1js.js instead of snarky.js --- src/build/buildWeb.js | 2 +- src/build/copy-to-dist.js | 2 +- src/examples/benchmarks/import.ts | 2 +- src/index.ts | 4 ++-- src/lib/account_update.ts | 2 +- src/lib/account_update.unit-test.ts | 2 +- src/lib/base58.unit-test.ts | 2 +- src/lib/bool.ts | 2 +- src/lib/circuit.ts | 2 +- src/lib/circuit_value.ts | 2 +- src/lib/field.ts | 2 +- src/lib/field.unit-test.ts | 2 +- src/lib/foreign-field.unit-test.ts | 2 +- src/lib/gadgets/basic.ts | 2 +- src/lib/gadgets/bitwise.unit-test.ts | 2 +- src/lib/gadgets/common.ts | 2 +- src/lib/gadgets/foreign-field.unit-test.ts | 2 +- src/lib/gadgets/range-check.ts | 2 +- src/lib/gates.ts | 2 +- src/lib/group.ts | 2 +- src/lib/hash-input.unit-test.ts | 2 +- src/lib/hash.ts | 2 +- src/lib/mina.ts | 2 +- src/lib/ml/consistency.unit-test.ts | 2 +- src/lib/ml/conversion.ts | 2 +- src/lib/proof-system/prover-keys.ts | 2 +- src/lib/proof_system.ts | 2 +- src/lib/proof_system.unit-test.ts | 2 +- src/lib/provable-context.ts | 2 +- src/lib/provable.ts | 2 +- src/lib/scalar.ts | 2 +- src/lib/state.ts | 2 +- src/lib/testing/constraint-system.ts | 2 +- src/lib/zkapp.ts | 2 +- .../src/sign-zkapp-command.unit-test.ts | 2 +- src/mina-signer/src/signature.unit-test.ts | 2 +- src/mina-signer/src/transaction-hash.unit-test.ts | 14 +++++++++++--- src/{snarky.d.ts => o1js.d.ts} | 0 src/{snarky.js => o1js.js} | 0 tsconfig.node.json | 2 +- tsconfig.test.json | 2 +- tsconfig.web.json | 2 +- typedoc.json | 2 +- 43 files changed, 52 insertions(+), 44 deletions(-) rename src/{snarky.d.ts => o1js.d.ts} (100%) rename src/{snarky.js => o1js.js} (100%) diff --git a/src/build/buildWeb.js b/src/build/buildWeb.js index b2a4929859..a446954377 100644 --- a/src/build/buildWeb.js +++ b/src/build/buildWeb.js @@ -51,7 +51,7 @@ async function buildWeb({ production }) { // copy over pure js files let copyPromise = copy({ './src/bindings/compiled/web_bindings/': './dist/web/web_bindings/', - './src/snarky.d.ts': './dist/web/snarky.d.ts', + './src/o1js.d.ts': './dist/web/o1js.d.ts', './src/bindings/js/wrapper.web.js': './dist/web/bindings/js/wrapper.js', './src/bindings/js/web/': './dist/web/bindings/js/web/', }); diff --git a/src/build/copy-to-dist.js b/src/build/copy-to-dist.js index 6218414713..c2bc1ee08e 100644 --- a/src/build/copy-to-dist.js +++ b/src/build/copy-to-dist.js @@ -3,7 +3,7 @@ import { copyFromTo } from './utils.js'; await copyFromTo( [ - 'src/snarky.d.ts', + 'src/o1js.d.ts', 'src/bindings/compiled/_node_bindings', 'src/bindings/compiled/node_bindings/plonk_wasm.d.cts', ], diff --git a/src/examples/benchmarks/import.ts b/src/examples/benchmarks/import.ts index fdd4340209..c985abf5ba 100644 --- a/src/examples/benchmarks/import.ts +++ b/src/examples/benchmarks/import.ts @@ -1,5 +1,5 @@ let start = performance.now(); -await import('../../snarky.js'); +await import('../../o1js.js'); let time = performance.now() - start; console.log(`import jsoo: ${time.toFixed(0)}ms`); diff --git a/src/index.ts b/src/index.ts index 243fe7dbfa..02690046c2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ -export type { ProvablePure } from './snarky.js'; -export { Ledger } from './snarky.js'; +export type { ProvablePure } from './o1js.js'; +export { Ledger } from './o1js.js'; export { Field, Bool, Group, Scalar } from './lib/core.js'; export { createForeignField, diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 23f5e66c08..00557cad7c 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -6,7 +6,7 @@ import { } from './circuit_value.js'; import { memoizationContext, memoizeWitness, Provable } from './provable.js'; import { Field, Bool } from './core.js'; -import { Pickles, Test } from '../snarky.js'; +import { Pickles, Test } from '../o1js.js'; import { jsLayout } from '../bindings/mina-transaction/gen/js-layout.js'; import { Types, diff --git a/src/lib/account_update.unit-test.ts b/src/lib/account_update.unit-test.ts index a280b22712..cfd4d3564c 100644 --- a/src/lib/account_update.unit-test.ts +++ b/src/lib/account_update.unit-test.ts @@ -8,7 +8,7 @@ import { Types, Provable, } from '../index.js'; -import { Test } from '../snarky.js'; +import { Test } from '../o1js.js'; import { expect } from 'expect'; let address = PrivateKey.random().toPublicKey(); diff --git a/src/lib/base58.unit-test.ts b/src/lib/base58.unit-test.ts index 2d09307da6..b4ff845f57 100644 --- a/src/lib/base58.unit-test.ts +++ b/src/lib/base58.unit-test.ts @@ -1,5 +1,5 @@ import { fromBase58Check, toBase58Check } from './base58.js'; -import { Test } from '../snarky.js'; +import { Test } from '../o1js.js'; import { expect } from 'expect'; import { test, Random, withHardCoded } from './testing/property.js'; diff --git a/src/lib/bool.ts b/src/lib/bool.ts index 8957e89d53..eecf0423b7 100644 --- a/src/lib/bool.ts +++ b/src/lib/bool.ts @@ -1,4 +1,4 @@ -import { Snarky } from '../snarky.js'; +import { Snarky } from '../o1js.js'; import { Field, FieldConst, diff --git a/src/lib/circuit.ts b/src/lib/circuit.ts index dd697cc967..294b43e90e 100644 --- a/src/lib/circuit.ts +++ b/src/lib/circuit.ts @@ -1,5 +1,5 @@ import 'reflect-metadata'; -import { ProvablePure, Snarky } from '../snarky.js'; +import { ProvablePure, Snarky } from '../o1js.js'; import { MlFieldArray, MlFieldConstArray } from './ml/fields.js'; import { withThreadPool } from '../bindings/js/wrapper.js'; import { Provable } from './provable.js'; diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index d42f267cdf..4f256f10d9 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -1,5 +1,5 @@ import 'reflect-metadata'; -import { ProvablePure, Snarky } from '../snarky.js'; +import { ProvablePure, Snarky } from '../o1js.js'; import { Field, Bool, Scalar, Group } from './core.js'; import { provable, diff --git a/src/lib/field.ts b/src/lib/field.ts index 891390619d..c93981ff17 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -1,4 +1,4 @@ -import { Snarky, Provable } from '../snarky.js'; +import { Snarky, Provable } from '../o1js.js'; import { Field as Fp } from '../provable/field-bigint.js'; import { defineBinable } from '../bindings/lib/binable.js'; import type { NonNegativeInteger } from '../bindings/crypto/non-negative.js'; diff --git a/src/lib/field.unit-test.ts b/src/lib/field.unit-test.ts index 8f7d8843f5..ef5a45e7a1 100644 --- a/src/lib/field.unit-test.ts +++ b/src/lib/field.unit-test.ts @@ -1,4 +1,4 @@ -import { ProvablePure } from '../snarky.js'; +import { ProvablePure } from '../o1js.js'; import { Field } from './core.js'; import { Field as Fp } from '../provable/field-bigint.js'; import { test, Random } from './testing/property.js'; diff --git a/src/lib/foreign-field.unit-test.ts b/src/lib/foreign-field.unit-test.ts index 26f85a5699..4c84399853 100644 --- a/src/lib/foreign-field.unit-test.ts +++ b/src/lib/foreign-field.unit-test.ts @@ -1,4 +1,4 @@ -import { ProvablePure } from '../snarky.js'; +import { ProvablePure } from '../o1js.js'; import { Field, Group } from './core.js'; import { ForeignField, createForeignField } from './foreign-field.js'; import { Scalar as Fq, Group as G } from '../provable/curve-bigint.js'; diff --git a/src/lib/gadgets/basic.ts b/src/lib/gadgets/basic.ts index 8f6f218935..dec349015a 100644 --- a/src/lib/gadgets/basic.ts +++ b/src/lib/gadgets/basic.ts @@ -6,7 +6,7 @@ import type { Field, VarField } from '../field.js'; import { existsOne, toVar } from './common.js'; import { Gates } from '../gates.js'; import { TupleN } from '../util/types.js'; -import { Snarky } from '../../snarky.js'; +import { Snarky } from '../../o1js.js'; export { assertBoolean, arrayGet, assertOneOf }; diff --git a/src/lib/gadgets/bitwise.unit-test.ts b/src/lib/gadgets/bitwise.unit-test.ts index f6f186e941..1b8e897ecd 100644 --- a/src/lib/gadgets/bitwise.unit-test.ts +++ b/src/lib/gadgets/bitwise.unit-test.ts @@ -18,7 +18,7 @@ import { and, withoutGenerics, } from '../testing/constraint-system.js'; -import { GateType } from '../../snarky.js'; +import { GateType } from '../../o1js.js'; const maybeField = { ...field, diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts index 9da4490723..540ce07215 100644 --- a/src/lib/gadgets/common.ts +++ b/src/lib/gadgets/common.ts @@ -1,6 +1,6 @@ import { Field, FieldConst, FieldVar, VarField } from '../field.js'; import { Tuple, TupleN } from '../util/types.js'; -import { Snarky } from '../../snarky.js'; +import { Snarky } from '../../o1js.js'; import { MlArray } from '../ml/base.js'; const MAX_BITS = 64 as const; diff --git a/src/lib/gadgets/foreign-field.unit-test.ts b/src/lib/gadgets/foreign-field.unit-test.ts index 135fca61a1..c2c1864b91 100644 --- a/src/lib/gadgets/foreign-field.unit-test.ts +++ b/src/lib/gadgets/foreign-field.unit-test.ts @@ -26,7 +26,7 @@ import { repeat, withoutGenerics, } from '../testing/constraint-system.js'; -import { GateType } from '../../snarky.js'; +import { GateType } from '../../o1js.js'; import { AnyTuple } from '../util/types.js'; import { foreignField, diff --git a/src/lib/gadgets/range-check.ts b/src/lib/gadgets/range-check.ts index 1d33dae0f8..9cfd693575 100644 --- a/src/lib/gadgets/range-check.ts +++ b/src/lib/gadgets/range-check.ts @@ -1,4 +1,4 @@ -import { Snarky } from '../../snarky.js'; +import { Snarky } from '../../o1js.js'; import { Fp } from '../../bindings/crypto/finite_field.js'; import { Field as FieldProvable } from '../../provable/field-bigint.js'; import { Field } from '../field.js'; diff --git a/src/lib/gates.ts b/src/lib/gates.ts index a8900cfe49..2a7bfcd85e 100644 --- a/src/lib/gates.ts +++ b/src/lib/gates.ts @@ -1,4 +1,4 @@ -import { Snarky } from '../snarky.js'; +import { Snarky } from '../o1js.js'; import { FieldConst, type Field } from './field.js'; import { exists } from './gadgets/common.js'; import { MlArray, MlTuple } from './ml/base.js'; diff --git a/src/lib/group.ts b/src/lib/group.ts index 67b021097e..ecd685da5c 100644 --- a/src/lib/group.ts +++ b/src/lib/group.ts @@ -1,6 +1,6 @@ import { Field, FieldVar } from './field.js'; import { Scalar } from './scalar.js'; -import { Snarky } from '../snarky.js'; +import { Snarky } from '../o1js.js'; import { Field as Fp } from '../provable/field-bigint.js'; import { GroupAffine, Pallas } from '../bindings/crypto/elliptic_curve.js'; import { Provable } from './provable.js'; diff --git a/src/lib/hash-input.unit-test.ts b/src/lib/hash-input.unit-test.ts index 35b5436dd6..9df80d3e54 100644 --- a/src/lib/hash-input.unit-test.ts +++ b/src/lib/hash-input.unit-test.ts @@ -16,7 +16,7 @@ import { packToFields } from './hash.js'; import { Random, test } from './testing/property.js'; import { MlHashInput } from './ml/conversion.js'; import { MlFieldConstArray } from './ml/fields.js'; -import { Test } from '../snarky.js'; +import { Test } from '../o1js.js'; let { hashInputFromJson } = Test; diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 1a55da5181..1b145e5e66 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -1,5 +1,5 @@ import { HashInput, ProvableExtended, Struct } from './circuit_value.js'; -import { Snarky } from '../snarky.js'; +import { Snarky } from '../o1js.js'; import { Field } from './core.js'; import { createHashHelpers } from './hash-generic.js'; import { Provable } from './provable.js'; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index e3b5823a9c..992c0c83c7 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -1,4 +1,4 @@ -import { Ledger } from '../snarky.js'; +import { Ledger } from '../o1js.js'; import { Field } from './core.js'; import { UInt32, UInt64 } from './int.js'; import { PrivateKey, PublicKey } from './signature.js'; diff --git a/src/lib/ml/consistency.unit-test.ts b/src/lib/ml/consistency.unit-test.ts index d0b78d0a03..5fc13e1671 100644 --- a/src/lib/ml/consistency.unit-test.ts +++ b/src/lib/ml/consistency.unit-test.ts @@ -1,4 +1,4 @@ -import { Ledger, Test } from '../../snarky.js'; +import { Ledger, Test } from '../../o1js.js'; import { Random, test } from '../testing/property.js'; import { Field, Bool } from '../core.js'; import { PrivateKey, PublicKey } from '../signature.js'; diff --git a/src/lib/ml/conversion.ts b/src/lib/ml/conversion.ts index de65656cce..fb636db51f 100644 --- a/src/lib/ml/conversion.ts +++ b/src/lib/ml/conversion.ts @@ -2,7 +2,7 @@ * this file contains conversion functions between JS and OCaml */ -import type { MlPublicKey, MlPublicKeyVar } from '../../snarky.js'; +import type { MlPublicKey, MlPublicKeyVar } from '../../o1js.js'; import { HashInput } from '../circuit_value.js'; import { Bool, Field } from '../core.js'; import { FieldConst, FieldVar } from '../field.js'; diff --git a/src/lib/proof-system/prover-keys.ts b/src/lib/proof-system/prover-keys.ts index 18b68f27b9..f4157ae5b4 100644 --- a/src/lib/proof-system/prover-keys.ts +++ b/src/lib/proof-system/prover-keys.ts @@ -9,7 +9,7 @@ import { WasmPastaFpPlonkIndex, WasmPastaFqPlonkIndex, } from '../../bindings/compiled/node_bindings/plonk_wasm.cjs'; -import { Pickles, getWasm } from '../../snarky.js'; +import { Pickles, getWasm } from '../../o1js.js'; import { VerifierIndex } from '../../bindings/crypto/bindings/kimchi-types.js'; import { getRustConversion } from '../../bindings/crypto/bindings.js'; import { MlString } from '../ml/base.js'; diff --git a/src/lib/proof_system.ts b/src/lib/proof_system.ts index 59ebf57ec4..ae730fe6a5 100644 --- a/src/lib/proof_system.ts +++ b/src/lib/proof_system.ts @@ -11,7 +11,7 @@ import { MlFeatureFlags, Gate, GateType, -} from '../snarky.js'; +} from '../o1js.js'; import { Field, Bool } from './core.js'; import { FlexibleProvable, diff --git a/src/lib/proof_system.unit-test.ts b/src/lib/proof_system.unit-test.ts index 3aab1dc9a5..1d16a8e55b 100644 --- a/src/lib/proof_system.unit-test.ts +++ b/src/lib/proof_system.unit-test.ts @@ -10,7 +10,7 @@ import { sortMethodArguments, } from './proof_system.js'; import { expect } from 'expect'; -import { Pickles, ProvablePure, Snarky } from '../snarky.js'; +import { Pickles, ProvablePure, Snarky } from '../o1js.js'; import { AnyFunction } from './util/types.js'; import { snarkContext } from './provable-context.js'; import { it } from 'node:test'; diff --git a/src/lib/provable-context.ts b/src/lib/provable-context.ts index c285ee81f8..4886873106 100644 --- a/src/lib/provable-context.ts +++ b/src/lib/provable-context.ts @@ -1,5 +1,5 @@ import { Context } from './global-context.js'; -import { Gate, GateType, JsonGate, Snarky } from '../snarky.js'; +import { Gate, GateType, JsonGate, Snarky } from '../o1js.js'; import { parseHexString32 } from '../bindings/crypto/bigint-helpers.js'; import { prettifyStacktrace } from './errors.js'; import { Fp } from '../bindings/crypto/finite_field.js'; diff --git a/src/lib/provable.ts b/src/lib/provable.ts index 8094bcf89e..2781026213 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -4,7 +4,7 @@ * - the main interface for types that can be used in provable code */ import { Field, Bool } from './core.js'; -import { Provable as Provable_, Snarky } from '../snarky.js'; +import { Provable as Provable_, Snarky } from '../o1js.js'; import type { FlexibleProvable, ProvableExtended } from './circuit_value.js'; import { Context } from './global-context.js'; import { diff --git a/src/lib/scalar.ts b/src/lib/scalar.ts index d54f20c209..a2208a99fa 100644 --- a/src/lib/scalar.ts +++ b/src/lib/scalar.ts @@ -1,4 +1,4 @@ -import { Snarky, Provable } from '../snarky.js'; +import { Snarky, Provable } from '../o1js.js'; import { Scalar as Fq } from '../provable/curve-bigint.js'; import { Field, FieldConst, FieldVar } from './field.js'; import { MlArray } from './ml/base.js'; diff --git a/src/lib/state.ts b/src/lib/state.ts index dc848a1996..a7ff6f877f 100644 --- a/src/lib/state.ts +++ b/src/lib/state.ts @@ -1,4 +1,4 @@ -import { ProvablePure } from '../snarky.js'; +import { ProvablePure } from '../o1js.js'; import { FlexibleProvablePure } from './circuit_value.js'; import { AccountUpdate, TokenId } from './account_update.js'; import { PublicKey } from './signature.js'; diff --git a/src/lib/testing/constraint-system.ts b/src/lib/testing/constraint-system.ts index 1f8ad9ba52..24751275f1 100644 --- a/src/lib/testing/constraint-system.ts +++ b/src/lib/testing/constraint-system.ts @@ -4,7 +4,7 @@ * An essential feature is that `constraintSystem()` automatically generates a * variety of fieldvar types for the inputs: constants, variables, and combinators. */ -import { Gate, GateType } from '../../snarky.js'; +import { Gate, GateType } from '../../o1js.js'; import { randomBytes } from '../../bindings/crypto/random.js'; import { Field, FieldType, FieldVar } from '../field.js'; import { Provable } from '../provable.js'; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 9346973f72..243da1099c 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1,4 +1,4 @@ -import { Gate, Pickles, ProvablePure } from '../snarky.js'; +import { Gate, Pickles, ProvablePure } from '../o1js.js'; import { Field, Bool } from './core.js'; import { AccountUpdate, diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index e400f9c8d4..97e9f2a3dc 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -1,5 +1,5 @@ import { expect } from 'expect'; -import { Ledger, Test, Pickles } from '../../snarky.js'; +import { Ledger, Test, Pickles } from '../../o1js.js'; import { PrivateKey as PrivateKeySnarky, PublicKey as PublicKeySnarky, diff --git a/src/mina-signer/src/signature.unit-test.ts b/src/mina-signer/src/signature.unit-test.ts index 9743bda23b..e13e1cb9ff 100644 --- a/src/mina-signer/src/signature.unit-test.ts +++ b/src/mina-signer/src/signature.unit-test.ts @@ -7,7 +7,7 @@ import { verify, verifyFieldElement, } from './signature.js'; -import { Ledger, Test } from '../../snarky.js'; +import { Ledger, Test } from '../../o1js.js'; import { Field as FieldSnarky } from '../../lib/core.js'; import { Field } from '../../provable/field-bigint.js'; import { PrivateKey, PublicKey } from '../../provable/curve-bigint.js'; diff --git a/src/mina-signer/src/transaction-hash.unit-test.ts b/src/mina-signer/src/transaction-hash.unit-test.ts index 955f451cd2..d8faaca2e2 100644 --- a/src/mina-signer/src/transaction-hash.unit-test.ts +++ b/src/mina-signer/src/transaction-hash.unit-test.ts @@ -1,4 +1,4 @@ -import { Ledger, Test } from '../../snarky.js'; +import { Ledger, Test } from '../../o1js.js'; import { Common, hashPayment, @@ -180,7 +180,12 @@ function paymentToOcamlV1({ common: commonToOcamlV1(common), body: [ 'Payment', - { source_pk: common.feePayer, receiver_pk: receiver, amount, token_id: '1' }, + { + source_pk: common.feePayer, + receiver_pk: receiver, + amount, + token_id: '1', + }, ], }, signer: common.feePayer, @@ -220,7 +225,10 @@ function delegationToOcamlV1({ common: commonToOcamlV1(common), body: [ 'Stake_delegation', - ['Set_delegate', { delegator: common.feePayer, new_delegate: newDelegate }], + [ + 'Set_delegate', + { delegator: common.feePayer, new_delegate: newDelegate }, + ], ], }, signer: common.feePayer, diff --git a/src/snarky.d.ts b/src/o1js.d.ts similarity index 100% rename from src/snarky.d.ts rename to src/o1js.d.ts diff --git a/src/snarky.js b/src/o1js.js similarity index 100% rename from src/snarky.js rename to src/o1js.js diff --git a/tsconfig.node.json b/tsconfig.node.json index 7b5d2daf33..0708208757 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -2,7 +2,7 @@ "extends": "./tsconfig.json", "include": [ "./src/index.ts", - "./src/snarky.js", + "./src/o1js.js", "./src/bindings/js/wrapper.js", "./src/mina-signer/src", "./src/mina-signer/MinaSigner.ts", diff --git a/tsconfig.test.json b/tsconfig.test.json index 85717a9f8c..c3c247bc89 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -2,7 +2,7 @@ "extends": "./tsconfig.json", "include": [ "./src/**/*.unit-test.ts", - "./src/snarky.js", + "./src/o1js.js", "./src/bindings/js/wrapper.js" ], "compilerOptions": { diff --git a/tsconfig.web.json b/tsconfig.web.json index 022d6abd3e..365e1acee5 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -1,6 +1,6 @@ { "extends": "./tsconfig.json", - "include": ["./src/index.ts", "./src/snarky.js", "./src/**/*.web.ts"], + "include": ["./src/index.ts", "./src/o1js.js", "./src/**/*.web.ts"], "compilerOptions": { "outDir": "dist/web" } diff --git a/typedoc.json b/typedoc.json index f670c1036e..e87c4e1f8b 100644 --- a/typedoc.json +++ b/typedoc.json @@ -7,7 +7,7 @@ "exclude": ["dist/**/*", "src/mina-signer/**/*", "src/examples/**/*"], "entryPoints": [ "src/index.ts", - "src/snarky.d.ts", + "src/o1js.d.ts", "src/lib/field.ts", "src/lib/group.ts", "src/lib/bool.ts" From 3355064f181e2277ce96ad60e04f709ae6454134 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 14:26:53 -0800 Subject: [PATCH 067/524] Update subproject commit reference --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 263bea2c09..8d062145d9 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 263bea2c09f626717f239e150735294e9f72471a +Subproject commit 8d062145d9ecacca03f1f103ec8ee502a24c6042 From 8f9eaacc1eed50abd0713a79a2ef0d3e6fc0a7b7 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 14:27:11 -0800 Subject: [PATCH 068/524] Update o1js build process and bindings --- README-dev.md | 8 ++++---- src/bindings | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README-dev.md b/README-dev.md index 24de5f1c4e..0e9dbbea45 100644 --- a/README-dev.md +++ b/README-dev.md @@ -37,7 +37,7 @@ This will compile the TypeScript source files, making it ready for use. The comp If you need to regenerate the OCaml and WebAssembly artifacts, you can do so within the o1js repo. The [bindings](https://github.com/o1-labs/o1js-bindings) and [Mina](https://github.com/MinaProtocol/mina) repos are both submodules of o1js, so you can build them from within the o1js repo. -o1js depends on OCaml code that is transpiled to JavaScript using [Js_of_ocaml](https://github.com/ocsigen/js_of_ocaml), and Rust code that is transpiled to WebAssembly using [wasm-pack](https://github.com/rustwasm/wasm-pack). These artifacts allow o1js to call into [Pickles](https://github.com/o1-labs/snarkyhttps://github.com/MinaProtocol/mina/blob/develop/src/lib/pickles/README.md), [snarky](https://github.com/o1-labs/snarky), and [Kimchi](https://github.com/o1-labs/proof-systems) to write zk-SNARKs and zkApps. +o1js depends on OCaml code that is transpiled to JavaScript using [Js_of_ocaml](https://github.com/ocsigen/js_of_ocaml), and Rust code that is transpiled to WebAssembly using [wasm-pack](https://github.com/rustwasm/wasm-pack). These artifacts allow o1js to call into [Pickles](https://github.com/MinaProtocol/mina/blob/develop/src/lib/pickles/README.md), [snarky](https://github.com/o1-labs/snarky), and [Kimchi](https://github.com/o1-labs/proof-systems) to write zk-SNARKs and zkApps. The compiled artifacts are stored under `src/bindings/compiled`, and are version-controlled to simplify the build process for end-users. @@ -51,13 +51,13 @@ This will build the OCaml and Rust artifacts, and copy them to the `src/bindings ### Build Scripts -The root build script which kicks off the build process is under `src/bindings/scripts/update-snarkyjs-bindings.sh`. This script is responsible for building the Node.js and web artifacts for o1js, and places them under `src/bindings/compiled`, to be used by o1js. +The root build script which kicks off the build process is under `src/bindings/scripts/update-o1js-bindings.sh`. This script is responsible for building the Node.js and web artifacts for o1js, and places them under `src/bindings/compiled`, to be used by o1js. ### OCaml Bindings o1js depends on Pickles, snarky, and parts of the Mina transaction logic, all of which are compiled to JavaScript and stored as artifacts to be used by o1js natively. The OCaml bindings are located under `src/bindings`. See the [OCaml Bindings README](https://github.com/o1-labs/o1js-bindings/blob/main/README.md) for more information. -To compile the OCaml code, a build tool called Dune is used. Dune is a build system for OCaml projects, and is used in addition with Js_of_ocaml to compile the OCaml code to JavaScript. The dune file that is responsible for compiling the OCaml code is located under `src/bindings/ocaml/dune`. There are two build targets: `snarky_js_node` and `snarky_js_web`, which compile the Mina dependencies as well as link the wasm artifacts to build the Node.js and web artifacts, respectively. The output file is `snark_js_node.bc.js`, which is used by o1js. +To compile the OCaml code, a build tool called Dune is used. Dune is a build system for OCaml projects, and is used in addition with Js_of_ocaml to compile the OCaml code to JavaScript. The dune file that is responsible for compiling the OCaml code is located under `src/bindings/ocaml/dune`. There are two build targets: `o1js_node` and `o1js_web`, which compile the Mina dependencies as well as link the wasm artifacts to build the Node.js and web artifacts, respectively. The output file is `o1js_node.bc.js`, which is used by o1js. ### WebAssembly Bindings @@ -74,7 +74,7 @@ For the wasm build, the output files are: ### Generated Constant Types -In addition to building the OCaml and Rust code, the build script also generates TypeScript types for constants used in the Mina protocol. These types are generated from the OCaml source files, and are located under `src/bindings/crypto/constants.ts` and `src/bindings/mina-transaction/gen`. When building the bindings, these constants are auto-generated by Dune. If you wish to add a new constant, you can edit the `src/bindings/ocaml/snarky_js_constants` file, and then run `npm run build:bindings` to regenerate the TypeScript files. +In addition to building the OCaml and Rust code, the build script also generates TypeScript types for constants used in the Mina protocol. These types are generated from the OCaml source files, and are located under `src/bindings/crypto/constants.ts` and `src/bindings/mina-transaction/gen`. When building the bindings, these constants are auto-generated by Dune. If you wish to add a new constant, you can edit the `src/bindings/ocaml/o1js_constants` file, and then run `npm run build:bindings` to regenerate the TypeScript files. These types are used by o1js to ensure that the constants used in the protocol are consistent with the OCaml source files. diff --git a/src/bindings b/src/bindings index 8d062145d9..4a730a0a19 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 8d062145d9ecacca03f1f103ec8ee502a24c6042 +Subproject commit 4a730a0a19cd31743e3c686393a7c2b94d927bbd From b07cc569ccb4278fde4e1cd13c38b738743af2df Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 14:40:37 -0800 Subject: [PATCH 069/524] rename 'hello-snarkyjs' to 'hello-o1js' and update importmap to reflect the new library name 'o1js' --- src/examples/plain-html/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/examples/plain-html/index.html b/src/examples/plain-html/index.html index 0678fde35a..656bbec220 100644 --- a/src/examples/plain-html/index.html +++ b/src/examples/plain-html/index.html @@ -2,9 +2,9 @@ - hello-snarkyjs + hello-o1js From 47a83d75af3c9ebe973128eb29b2c48472483c8a Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 14:53:38 -0800 Subject: [PATCH 070/524] Update file paths in buildWeb.js --- src/bindings | 2 +- src/build/buildWeb.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index 4a730a0a19..cc82f2ea2c 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 4a730a0a19cd31743e3c686393a7c2b94d927bbd +Subproject commit cc82f2ea2ca6450a632c89738be1c21bca57b58d diff --git a/src/build/buildWeb.js b/src/build/buildWeb.js index a446954377..ce9fd663c6 100644 --- a/src/build/buildWeb.js +++ b/src/build/buildWeb.js @@ -59,7 +59,7 @@ async function buildWeb({ production }) { await Promise.all([tscPromise, copyPromise]); if (minify) { - let o1jsWebPath = './dist/web/web_bindings/snarky_js_web.bc.js'; + let o1jsWebPath = './dist/web/web_bindings/o1js_web.bc.js'; let o1jsWeb = await readFile(o1jsWebPath, 'utf8'); let { code } = await esbuild.transform(o1jsWeb, { target, From 78ad0b92167a8d053aa781b997cc263cb485e77f Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 15:15:32 -0800 Subject: [PATCH 071/524] Update bindings and import statements --- src/bindings | 2 +- src/o1js.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bindings b/src/bindings index cc82f2ea2c..ba9b30230b 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit cc82f2ea2ca6450a632c89738be1c21bca57b58d +Subproject commit ba9b30230bc1add0c8b1a8d32edd5f4adcfb665f diff --git a/src/o1js.js b/src/o1js.js index b80fa8e6bd..8487cbf7de 100644 --- a/src/o1js.js +++ b/src/o1js.js @@ -1,5 +1,5 @@ import './bindings/crypto/bindings.js'; -import { getSnarky, getWasm, withThreadPool } from './bindings/js/wrapper.js'; +import { getO1js, getWasm, withThreadPool } from './bindings/js/wrapper.js'; import snarkySpec from './bindings/js/snarky-class-spec.js'; import { proxyClasses } from './bindings/js/proxy.js'; @@ -8,7 +8,7 @@ let isReadyBoolean = true; let isItReady = () => isReadyBoolean; let { Snarky, Ledger, Pickles, Test } = proxyClasses( - getSnarky, + getO1js, isItReady, snarkySpec ); From ce3a4de48bf3717ba828d26c17e56127470a11f7 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 15:52:17 -0800 Subject: [PATCH 072/524] refactor(release.yml): simplify job termination on odd weeks --- .github/workflows/release.yml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3aa4226dd..7901f04858 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ name: Version Bump on: workflow_dispatch: # Allow to manually trigger the workflow schedule: - - cron: "0 0 * * 2" # At 00:00 UTC every Tuesday + - cron: '0 0 * * 2' # At 00:00 UTC every Tuesday jobs: version-bump: @@ -23,10 +23,9 @@ jobs: - name: Check if it's an even week run: | WEEK_NUM=$(date +'%V') - if [ $((WEEK_NUM % 2)) -eq 0 ]; then - echo "RUN_JOB=true" >> $GITHUB_ENV - else - echo "RUN_JOB=false" >> $GITHUB_ENV + if [ $((WEEK_NUM % 2)) -ne 0 ]; then + echo "Odd week number ($WEEK_NUM), terminating job." + exit 1 fi - name: Check out the repository @@ -35,7 +34,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v4 with: - node-version: "18" + node-version: '18' - name: Configure Git run: | @@ -43,7 +42,6 @@ jobs: git config --local user.name "GitHub Action" - name: Bump patch version - if: ${{ env.RUN_JOB }} == 'true' run: | git fetch --prune --unshallow NEW_VERSION=$(npm version patch) @@ -51,18 +49,15 @@ jobs: echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV - name: Install npm dependencies - if: ${{ env.RUN_JOB }} == 'true' run: npm install - name: Update CHANGELOG.md - if: ${{ env.RUN_JOB }} == 'true' run: | npm run update-changelog git add CHANGELOG.md git commit -m "Update CHANGELOG for new version $NEW_VERSION" - name: Create new release branch - if: ${{ env.RUN_JOB }} == 'true' run: | NEW_BRANCH="release/${NEW_VERSION}" git checkout -b $NEW_BRANCH From 71f7a86b65c49cf50e2813bb298e0e4fe63b4e30 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 16:02:27 -0800 Subject: [PATCH 073/524] Update bindings and import in bytes.ts --- src/bindings | 2 +- src/lib/circuit_value.ts | 4 ++-- src/lib/foreign-curve.ts | 2 +- src/lib/foreign-ecdsa.ts | 2 +- src/lib/gadgets/foreign-field.ts | 2 +- src/lib/provable-types/bytes.ts | 2 +- src/lib/provable.ts | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bindings b/src/bindings index ba9b30230b..b1715c92b9 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit ba9b30230bc1add0c8b1a8d32edd5f4adcfb665f +Subproject commit b1715c92b9f7cf0f79f7c979674480ffd17274e8 diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 4f256f10d9..c0f7a2fbaf 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -7,13 +7,13 @@ import { provableTuple, HashInput, NonMethods, -} from '../bindings/lib/provable-snarky.js'; +} from '../bindings/lib/provable-o1js.js'; import type { InferJson, InferProvable, InferredProvable, IsPure, -} from '../bindings/lib/provable-snarky.js'; +} from '../bindings/lib/provable-o1js.js'; import { Provable } from './provable.js'; import { assert } from './errors.js'; import { inCheckedComputation } from './provable-context.js'; diff --git a/src/lib/foreign-curve.ts b/src/lib/foreign-curve.ts index 08fb733bfd..cc34bfc0db 100644 --- a/src/lib/foreign-curve.ts +++ b/src/lib/foreign-curve.ts @@ -10,7 +10,7 @@ import { EllipticCurve, Point } from './gadgets/elliptic-curve.js'; import { Field3 } from './gadgets/foreign-field.js'; import { assert } from './gadgets/common.js'; import { Provable } from './provable.js'; -import { provableFromClass } from '../bindings/lib/provable-snarky.js'; +import { provableFromClass } from '../bindings/lib/provable-o1js.js'; // external API export { createForeignCurve, ForeignCurve }; diff --git a/src/lib/foreign-ecdsa.ts b/src/lib/foreign-ecdsa.ts index bccbaa77ab..5331c6e5b7 100644 --- a/src/lib/foreign-ecdsa.ts +++ b/src/lib/foreign-ecdsa.ts @@ -1,4 +1,4 @@ -import { provableFromClass } from '../bindings/lib/provable-snarky.js'; +import { provableFromClass } from '../bindings/lib/provable-o1js.js'; import { CurveParams } from '../bindings/crypto/elliptic_curve.js'; import { ProvablePureExtended } from './circuit_value.js'; import { diff --git a/src/lib/gadgets/foreign-field.ts b/src/lib/gadgets/foreign-field.ts index 51b815dfc8..527bda3193 100644 --- a/src/lib/gadgets/foreign-field.ts +++ b/src/lib/gadgets/foreign-field.ts @@ -5,7 +5,7 @@ import { inverse as modInverse, mod, } from '../../bindings/crypto/finite_field.js'; -import { provableTuple } from '../../bindings/lib/provable-snarky.js'; +import { provableTuple } from '../../bindings/lib/provable-o1js.js'; import { Bool } from '../bool.js'; import { Unconstrained } from '../circuit_value.js'; import { Field } from '../field.js'; diff --git a/src/lib/provable-types/bytes.ts b/src/lib/provable-types/bytes.ts index 9bde301e69..fdea69dd7b 100644 --- a/src/lib/provable-types/bytes.ts +++ b/src/lib/provable-types/bytes.ts @@ -1,4 +1,4 @@ -import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; +import { provableFromClass } from '../../bindings/lib/provable-o1js.js'; import { ProvablePureExtended } from '../circuit_value.js'; import { assert } from '../gadgets/common.js'; import { chunkString } from '../util/arrays.js'; diff --git a/src/lib/provable.ts b/src/lib/provable.ts index 2781026213..f9542b90eb 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -12,7 +12,7 @@ import { InferJson, InferProvable, InferredProvable, -} from '../bindings/lib/provable-snarky.js'; +} from '../bindings/lib/provable-o1js.js'; import { inCheckedComputation, inProver, From 4850afdbe6b8ade4c5c0b489b32338130ec766d8 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 2 Jan 2024 16:17:20 -0800 Subject: [PATCH 074/524] pdate subproject commit hash to 9a097f13b0ef3f7430dd20bb0e4442bff02d7113 --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index b1715c92b9..9a097f13b0 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit b1715c92b9f7cf0f79f7c979674480ffd17274e8 +Subproject commit 9a097f13b0ef3f7430dd20bb0e4442bff02d7113 From 5a4f5711f36b1c47036fab4be4c8cd5105e6dddc Mon Sep 17 00:00:00 2001 From: yanziseeker <153156292+AdventureSeeker987@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:28:04 +0000 Subject: [PATCH 075/524] fix typo in field.ts --- src/lib/field.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index 891390619d..e36cdb0e49 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -297,7 +297,7 @@ class Field { * const sum = x.add(Field(-7)); * * // If you try to print sum - `console.log(sum.toBigInt())` - you will realize that it prints a very big integer because this is modular arithmetic, and 1 + (-7) circles around the field to become p - 6. - * // You can use the reverse operation of addition (substraction) to prove the sum is calculated correctly. + * // You can use the reverse operation of addition (subtraction) to prove the sum is calculated correctly. * * sum.sub(x).assertEquals(Field(-7)); * sum.sub(Field(-7)).assertEquals(x); @@ -345,7 +345,7 @@ class Field { } /** - * Substract another "field-like" value from this {@link Field} element. + * subtract another "field-like" value from this {@link Field} element. * * @example * ```ts @@ -355,7 +355,7 @@ class Field { * difference.assertEquals(Field(-2)); * ``` * - * **Warning**: This is a modular substraction in the pasta field. + * **Warning**: This is a modular subtraction in the pasta field. * * @example * ```ts @@ -363,11 +363,11 @@ class Field { * const difference = x.sub(Field(2)); * * // If you try to print difference - `console.log(difference.toBigInt())` - you will realize that it prints a very big integer because this is modular arithmetic, and 1 - 2 circles around the field to become p - 1. - * // You can use the reverse operation of substraction (addition) to prove the difference is calculated correctly. + * // You can use the reverse operation of subtraction (addition) to prove the difference is calculated correctly. * difference.add(Field(2)).assertEquals(x); * ``` * - * @param value - a "field-like" value to substract from the {@link Field}. + * @param value - a "field-like" value to subtract from the {@link Field}. * * @return A {@link Field} element equivalent to the modular difference of the two value. */ From b974a27773d5f0b053c6e494b0d755c70c1e147e Mon Sep 17 00:00:00 2001 From: yanziseeker <153156292+AdventureSeeker987@users.noreply.github.com> Date: Thu, 4 Jan 2024 00:36:47 +0800 Subject: [PATCH 076/524] docs: correct Pickles link path --- README-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README-dev.md b/README-dev.md index 24de5f1c4e..56887893bd 100644 --- a/README-dev.md +++ b/README-dev.md @@ -37,7 +37,7 @@ This will compile the TypeScript source files, making it ready for use. The comp If you need to regenerate the OCaml and WebAssembly artifacts, you can do so within the o1js repo. The [bindings](https://github.com/o1-labs/o1js-bindings) and [Mina](https://github.com/MinaProtocol/mina) repos are both submodules of o1js, so you can build them from within the o1js repo. -o1js depends on OCaml code that is transpiled to JavaScript using [Js_of_ocaml](https://github.com/ocsigen/js_of_ocaml), and Rust code that is transpiled to WebAssembly using [wasm-pack](https://github.com/rustwasm/wasm-pack). These artifacts allow o1js to call into [Pickles](https://github.com/o1-labs/snarkyhttps://github.com/MinaProtocol/mina/blob/develop/src/lib/pickles/README.md), [snarky](https://github.com/o1-labs/snarky), and [Kimchi](https://github.com/o1-labs/proof-systems) to write zk-SNARKs and zkApps. +o1js depends on OCaml code that is transpiled to JavaScript using [Js_of_ocaml](https://github.com/ocsigen/js_of_ocaml), and Rust code that is transpiled to WebAssembly using [wasm-pack](https://github.com/rustwasm/wasm-pack). These artifacts allow o1js to call into [Pickles](https://github.com/MinaProtocol/mina/blob/develop/src/lib/pickles/README.md), [snarky](https://github.com/o1-labs/snarky), and [Kimchi](https://github.com/o1-labs/proof-systems) to write zk-SNARKs and zkApps. The compiled artifacts are stored under `src/bindings/compiled`, and are version-controlled to simplify the build process for end-users. From 9587669cda84f5b2c3381889e1b33fc3b75e90f8 Mon Sep 17 00:00:00 2001 From: Yoni Mekuria Date: Thu, 4 Jan 2024 00:43:55 -0700 Subject: [PATCH 077/524] Update field.ts --- src/lib/field.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index e36cdb0e49..843110d526 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -345,7 +345,7 @@ class Field { } /** - * subtract another "field-like" value from this {@link Field} element. + * Subtract another "field-like" value from this {@link Field} element. * * @example * ```ts From 94a7d32acdb1f52e4d0fd0fdb66eabcf3e86923d Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 4 Jan 2024 10:44:07 -0800 Subject: [PATCH 078/524] docs(README-dev.md): update o1js-main branch description for clarity Add more detailed explanation about the relationship between o1js-main and berkeley branches. Also, provide a branching structure for better understanding of the merge direction. --- README-dev.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README-dev.md b/README-dev.md index 56887893bd..c81b561d08 100644 --- a/README-dev.md +++ b/README-dev.md @@ -96,7 +96,9 @@ The other base branches (`berkeley`, `develop`) are only used in specific scenar | | berkeley -> berkeley -> berkeley | | | develop -> develop -> develop | -- `o1js-main`: The o1js-main branch in the Mina repository corresponds to the main branch in both o1js and o1js-bindings repositories. This is where stable releases and ramp-up features are maintained. +- `o1js-main`: The o1js-main branch in the Mina repository corresponds to the main branch in both o1js and o1js-bindings repositories. This is where stable releases and ramp-up features are maintained. The o1js-main branch runs in parallel to the Mina `berkeley` branch and does not have a subset or superset relationship with it. The branching structure is as follows (<- means direction to merge): + + - `develop` <- `o1js-main` <- `current testnet` - Typically, the current testnet often corresponds to the rampup branch. - `berkeley`: The berkeley branch is maintained across all three repositories. This branch is used for features and updates specific to the Berkeley release of the project. From 16e66f9fba51ac4832691c3fc7a52bae60a08fd7 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 4 Jan 2024 10:52:11 -0800 Subject: [PATCH 079/524] Revert "Update import statements to use o1js.js instead of snarky.js" This reverts commit 43f84138401e9f53f182d27c48d6c4238fae3c10. --- src/build/buildWeb.js | 2 +- src/build/copy-to-dist.js | 2 +- src/examples/benchmarks/import.ts | 2 +- src/index.ts | 4 ++-- src/lib/account_update.ts | 2 +- src/lib/account_update.unit-test.ts | 2 +- src/lib/base58.unit-test.ts | 2 +- src/lib/bool.ts | 2 +- src/lib/circuit.ts | 2 +- src/lib/circuit_value.ts | 2 +- src/lib/field.ts | 2 +- src/lib/field.unit-test.ts | 2 +- src/lib/foreign-field.unit-test.ts | 2 +- src/lib/gadgets/basic.ts | 2 +- src/lib/gadgets/bitwise.unit-test.ts | 2 +- src/lib/gadgets/common.ts | 2 +- src/lib/gadgets/foreign-field.unit-test.ts | 2 +- src/lib/gadgets/range-check.ts | 2 +- src/lib/gates.ts | 2 +- src/lib/group.ts | 2 +- src/lib/hash-input.unit-test.ts | 2 +- src/lib/hash.ts | 2 +- src/lib/mina.ts | 2 +- src/lib/ml/consistency.unit-test.ts | 2 +- src/lib/ml/conversion.ts | 2 +- src/lib/proof-system/prover-keys.ts | 2 +- src/lib/proof_system.ts | 2 +- src/lib/proof_system.unit-test.ts | 2 +- src/lib/provable-context.ts | 2 +- src/lib/provable.ts | 2 +- src/lib/scalar.ts | 2 +- src/lib/state.ts | 2 +- src/lib/testing/constraint-system.ts | 2 +- src/lib/zkapp.ts | 2 +- .../src/sign-zkapp-command.unit-test.ts | 2 +- src/mina-signer/src/signature.unit-test.ts | 2 +- src/mina-signer/src/transaction-hash.unit-test.ts | 14 +++----------- src/{o1js.d.ts => snarky.d.ts} | 0 src/{o1js.js => snarky.js} | 0 tsconfig.node.json | 2 +- tsconfig.test.json | 2 +- tsconfig.web.json | 2 +- typedoc.json | 2 +- 43 files changed, 44 insertions(+), 52 deletions(-) rename src/{o1js.d.ts => snarky.d.ts} (100%) rename src/{o1js.js => snarky.js} (100%) diff --git a/src/build/buildWeb.js b/src/build/buildWeb.js index ce9fd663c6..b0455e62b5 100644 --- a/src/build/buildWeb.js +++ b/src/build/buildWeb.js @@ -51,7 +51,7 @@ async function buildWeb({ production }) { // copy over pure js files let copyPromise = copy({ './src/bindings/compiled/web_bindings/': './dist/web/web_bindings/', - './src/o1js.d.ts': './dist/web/o1js.d.ts', + './src/snarky.d.ts': './dist/web/snarky.d.ts', './src/bindings/js/wrapper.web.js': './dist/web/bindings/js/wrapper.js', './src/bindings/js/web/': './dist/web/bindings/js/web/', }); diff --git a/src/build/copy-to-dist.js b/src/build/copy-to-dist.js index c2bc1ee08e..6218414713 100644 --- a/src/build/copy-to-dist.js +++ b/src/build/copy-to-dist.js @@ -3,7 +3,7 @@ import { copyFromTo } from './utils.js'; await copyFromTo( [ - 'src/o1js.d.ts', + 'src/snarky.d.ts', 'src/bindings/compiled/_node_bindings', 'src/bindings/compiled/node_bindings/plonk_wasm.d.cts', ], diff --git a/src/examples/benchmarks/import.ts b/src/examples/benchmarks/import.ts index c985abf5ba..fdd4340209 100644 --- a/src/examples/benchmarks/import.ts +++ b/src/examples/benchmarks/import.ts @@ -1,5 +1,5 @@ let start = performance.now(); -await import('../../o1js.js'); +await import('../../snarky.js'); let time = performance.now() - start; console.log(`import jsoo: ${time.toFixed(0)}ms`); diff --git a/src/index.ts b/src/index.ts index 02690046c2..243fe7dbfa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ -export type { ProvablePure } from './o1js.js'; -export { Ledger } from './o1js.js'; +export type { ProvablePure } from './snarky.js'; +export { Ledger } from './snarky.js'; export { Field, Bool, Group, Scalar } from './lib/core.js'; export { createForeignField, diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 00557cad7c..23f5e66c08 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -6,7 +6,7 @@ import { } from './circuit_value.js'; import { memoizationContext, memoizeWitness, Provable } from './provable.js'; import { Field, Bool } from './core.js'; -import { Pickles, Test } from '../o1js.js'; +import { Pickles, Test } from '../snarky.js'; import { jsLayout } from '../bindings/mina-transaction/gen/js-layout.js'; import { Types, diff --git a/src/lib/account_update.unit-test.ts b/src/lib/account_update.unit-test.ts index cfd4d3564c..a280b22712 100644 --- a/src/lib/account_update.unit-test.ts +++ b/src/lib/account_update.unit-test.ts @@ -8,7 +8,7 @@ import { Types, Provable, } from '../index.js'; -import { Test } from '../o1js.js'; +import { Test } from '../snarky.js'; import { expect } from 'expect'; let address = PrivateKey.random().toPublicKey(); diff --git a/src/lib/base58.unit-test.ts b/src/lib/base58.unit-test.ts index b4ff845f57..2d09307da6 100644 --- a/src/lib/base58.unit-test.ts +++ b/src/lib/base58.unit-test.ts @@ -1,5 +1,5 @@ import { fromBase58Check, toBase58Check } from './base58.js'; -import { Test } from '../o1js.js'; +import { Test } from '../snarky.js'; import { expect } from 'expect'; import { test, Random, withHardCoded } from './testing/property.js'; diff --git a/src/lib/bool.ts b/src/lib/bool.ts index eecf0423b7..8957e89d53 100644 --- a/src/lib/bool.ts +++ b/src/lib/bool.ts @@ -1,4 +1,4 @@ -import { Snarky } from '../o1js.js'; +import { Snarky } from '../snarky.js'; import { Field, FieldConst, diff --git a/src/lib/circuit.ts b/src/lib/circuit.ts index 294b43e90e..dd697cc967 100644 --- a/src/lib/circuit.ts +++ b/src/lib/circuit.ts @@ -1,5 +1,5 @@ import 'reflect-metadata'; -import { ProvablePure, Snarky } from '../o1js.js'; +import { ProvablePure, Snarky } from '../snarky.js'; import { MlFieldArray, MlFieldConstArray } from './ml/fields.js'; import { withThreadPool } from '../bindings/js/wrapper.js'; import { Provable } from './provable.js'; diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index c0f7a2fbaf..81b8cf2ad5 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -1,5 +1,5 @@ import 'reflect-metadata'; -import { ProvablePure, Snarky } from '../o1js.js'; +import { ProvablePure, Snarky } from '../snarky.js'; import { Field, Bool, Scalar, Group } from './core.js'; import { provable, diff --git a/src/lib/field.ts b/src/lib/field.ts index c93981ff17..891390619d 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -1,4 +1,4 @@ -import { Snarky, Provable } from '../o1js.js'; +import { Snarky, Provable } from '../snarky.js'; import { Field as Fp } from '../provable/field-bigint.js'; import { defineBinable } from '../bindings/lib/binable.js'; import type { NonNegativeInteger } from '../bindings/crypto/non-negative.js'; diff --git a/src/lib/field.unit-test.ts b/src/lib/field.unit-test.ts index ef5a45e7a1..8f7d8843f5 100644 --- a/src/lib/field.unit-test.ts +++ b/src/lib/field.unit-test.ts @@ -1,4 +1,4 @@ -import { ProvablePure } from '../o1js.js'; +import { ProvablePure } from '../snarky.js'; import { Field } from './core.js'; import { Field as Fp } from '../provable/field-bigint.js'; import { test, Random } from './testing/property.js'; diff --git a/src/lib/foreign-field.unit-test.ts b/src/lib/foreign-field.unit-test.ts index 4c84399853..26f85a5699 100644 --- a/src/lib/foreign-field.unit-test.ts +++ b/src/lib/foreign-field.unit-test.ts @@ -1,4 +1,4 @@ -import { ProvablePure } from '../o1js.js'; +import { ProvablePure } from '../snarky.js'; import { Field, Group } from './core.js'; import { ForeignField, createForeignField } from './foreign-field.js'; import { Scalar as Fq, Group as G } from '../provable/curve-bigint.js'; diff --git a/src/lib/gadgets/basic.ts b/src/lib/gadgets/basic.ts index dec349015a..8f6f218935 100644 --- a/src/lib/gadgets/basic.ts +++ b/src/lib/gadgets/basic.ts @@ -6,7 +6,7 @@ import type { Field, VarField } from '../field.js'; import { existsOne, toVar } from './common.js'; import { Gates } from '../gates.js'; import { TupleN } from '../util/types.js'; -import { Snarky } from '../../o1js.js'; +import { Snarky } from '../../snarky.js'; export { assertBoolean, arrayGet, assertOneOf }; diff --git a/src/lib/gadgets/bitwise.unit-test.ts b/src/lib/gadgets/bitwise.unit-test.ts index 1b8e897ecd..f6f186e941 100644 --- a/src/lib/gadgets/bitwise.unit-test.ts +++ b/src/lib/gadgets/bitwise.unit-test.ts @@ -18,7 +18,7 @@ import { and, withoutGenerics, } from '../testing/constraint-system.js'; -import { GateType } from '../../o1js.js'; +import { GateType } from '../../snarky.js'; const maybeField = { ...field, diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts index 540ce07215..9da4490723 100644 --- a/src/lib/gadgets/common.ts +++ b/src/lib/gadgets/common.ts @@ -1,6 +1,6 @@ import { Field, FieldConst, FieldVar, VarField } from '../field.js'; import { Tuple, TupleN } from '../util/types.js'; -import { Snarky } from '../../o1js.js'; +import { Snarky } from '../../snarky.js'; import { MlArray } from '../ml/base.js'; const MAX_BITS = 64 as const; diff --git a/src/lib/gadgets/foreign-field.unit-test.ts b/src/lib/gadgets/foreign-field.unit-test.ts index c2c1864b91..135fca61a1 100644 --- a/src/lib/gadgets/foreign-field.unit-test.ts +++ b/src/lib/gadgets/foreign-field.unit-test.ts @@ -26,7 +26,7 @@ import { repeat, withoutGenerics, } from '../testing/constraint-system.js'; -import { GateType } from '../../o1js.js'; +import { GateType } from '../../snarky.js'; import { AnyTuple } from '../util/types.js'; import { foreignField, diff --git a/src/lib/gadgets/range-check.ts b/src/lib/gadgets/range-check.ts index 9cfd693575..1d33dae0f8 100644 --- a/src/lib/gadgets/range-check.ts +++ b/src/lib/gadgets/range-check.ts @@ -1,4 +1,4 @@ -import { Snarky } from '../../o1js.js'; +import { Snarky } from '../../snarky.js'; import { Fp } from '../../bindings/crypto/finite_field.js'; import { Field as FieldProvable } from '../../provable/field-bigint.js'; import { Field } from '../field.js'; diff --git a/src/lib/gates.ts b/src/lib/gates.ts index 2a7bfcd85e..a8900cfe49 100644 --- a/src/lib/gates.ts +++ b/src/lib/gates.ts @@ -1,4 +1,4 @@ -import { Snarky } from '../o1js.js'; +import { Snarky } from '../snarky.js'; import { FieldConst, type Field } from './field.js'; import { exists } from './gadgets/common.js'; import { MlArray, MlTuple } from './ml/base.js'; diff --git a/src/lib/group.ts b/src/lib/group.ts index ecd685da5c..67b021097e 100644 --- a/src/lib/group.ts +++ b/src/lib/group.ts @@ -1,6 +1,6 @@ import { Field, FieldVar } from './field.js'; import { Scalar } from './scalar.js'; -import { Snarky } from '../o1js.js'; +import { Snarky } from '../snarky.js'; import { Field as Fp } from '../provable/field-bigint.js'; import { GroupAffine, Pallas } from '../bindings/crypto/elliptic_curve.js'; import { Provable } from './provable.js'; diff --git a/src/lib/hash-input.unit-test.ts b/src/lib/hash-input.unit-test.ts index 9df80d3e54..35b5436dd6 100644 --- a/src/lib/hash-input.unit-test.ts +++ b/src/lib/hash-input.unit-test.ts @@ -16,7 +16,7 @@ import { packToFields } from './hash.js'; import { Random, test } from './testing/property.js'; import { MlHashInput } from './ml/conversion.js'; import { MlFieldConstArray } from './ml/fields.js'; -import { Test } from '../o1js.js'; +import { Test } from '../snarky.js'; let { hashInputFromJson } = Test; diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 1b145e5e66..1a55da5181 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -1,5 +1,5 @@ import { HashInput, ProvableExtended, Struct } from './circuit_value.js'; -import { Snarky } from '../o1js.js'; +import { Snarky } from '../snarky.js'; import { Field } from './core.js'; import { createHashHelpers } from './hash-generic.js'; import { Provable } from './provable.js'; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 992c0c83c7..e3b5823a9c 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -1,4 +1,4 @@ -import { Ledger } from '../o1js.js'; +import { Ledger } from '../snarky.js'; import { Field } from './core.js'; import { UInt32, UInt64 } from './int.js'; import { PrivateKey, PublicKey } from './signature.js'; diff --git a/src/lib/ml/consistency.unit-test.ts b/src/lib/ml/consistency.unit-test.ts index 5fc13e1671..d0b78d0a03 100644 --- a/src/lib/ml/consistency.unit-test.ts +++ b/src/lib/ml/consistency.unit-test.ts @@ -1,4 +1,4 @@ -import { Ledger, Test } from '../../o1js.js'; +import { Ledger, Test } from '../../snarky.js'; import { Random, test } from '../testing/property.js'; import { Field, Bool } from '../core.js'; import { PrivateKey, PublicKey } from '../signature.js'; diff --git a/src/lib/ml/conversion.ts b/src/lib/ml/conversion.ts index fb636db51f..de65656cce 100644 --- a/src/lib/ml/conversion.ts +++ b/src/lib/ml/conversion.ts @@ -2,7 +2,7 @@ * this file contains conversion functions between JS and OCaml */ -import type { MlPublicKey, MlPublicKeyVar } from '../../o1js.js'; +import type { MlPublicKey, MlPublicKeyVar } from '../../snarky.js'; import { HashInput } from '../circuit_value.js'; import { Bool, Field } from '../core.js'; import { FieldConst, FieldVar } from '../field.js'; diff --git a/src/lib/proof-system/prover-keys.ts b/src/lib/proof-system/prover-keys.ts index f4157ae5b4..18b68f27b9 100644 --- a/src/lib/proof-system/prover-keys.ts +++ b/src/lib/proof-system/prover-keys.ts @@ -9,7 +9,7 @@ import { WasmPastaFpPlonkIndex, WasmPastaFqPlonkIndex, } from '../../bindings/compiled/node_bindings/plonk_wasm.cjs'; -import { Pickles, getWasm } from '../../o1js.js'; +import { Pickles, getWasm } from '../../snarky.js'; import { VerifierIndex } from '../../bindings/crypto/bindings/kimchi-types.js'; import { getRustConversion } from '../../bindings/crypto/bindings.js'; import { MlString } from '../ml/base.js'; diff --git a/src/lib/proof_system.ts b/src/lib/proof_system.ts index ae730fe6a5..59ebf57ec4 100644 --- a/src/lib/proof_system.ts +++ b/src/lib/proof_system.ts @@ -11,7 +11,7 @@ import { MlFeatureFlags, Gate, GateType, -} from '../o1js.js'; +} from '../snarky.js'; import { Field, Bool } from './core.js'; import { FlexibleProvable, diff --git a/src/lib/proof_system.unit-test.ts b/src/lib/proof_system.unit-test.ts index 1d16a8e55b..3aab1dc9a5 100644 --- a/src/lib/proof_system.unit-test.ts +++ b/src/lib/proof_system.unit-test.ts @@ -10,7 +10,7 @@ import { sortMethodArguments, } from './proof_system.js'; import { expect } from 'expect'; -import { Pickles, ProvablePure, Snarky } from '../o1js.js'; +import { Pickles, ProvablePure, Snarky } from '../snarky.js'; import { AnyFunction } from './util/types.js'; import { snarkContext } from './provable-context.js'; import { it } from 'node:test'; diff --git a/src/lib/provable-context.ts b/src/lib/provable-context.ts index 4886873106..c285ee81f8 100644 --- a/src/lib/provable-context.ts +++ b/src/lib/provable-context.ts @@ -1,5 +1,5 @@ import { Context } from './global-context.js'; -import { Gate, GateType, JsonGate, Snarky } from '../o1js.js'; +import { Gate, GateType, JsonGate, Snarky } from '../snarky.js'; import { parseHexString32 } from '../bindings/crypto/bigint-helpers.js'; import { prettifyStacktrace } from './errors.js'; import { Fp } from '../bindings/crypto/finite_field.js'; diff --git a/src/lib/provable.ts b/src/lib/provable.ts index f9542b90eb..02f26eea2f 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -4,7 +4,7 @@ * - the main interface for types that can be used in provable code */ import { Field, Bool } from './core.js'; -import { Provable as Provable_, Snarky } from '../o1js.js'; +import { Provable as Provable_, Snarky } from '../snarky.js'; import type { FlexibleProvable, ProvableExtended } from './circuit_value.js'; import { Context } from './global-context.js'; import { diff --git a/src/lib/scalar.ts b/src/lib/scalar.ts index a2208a99fa..d54f20c209 100644 --- a/src/lib/scalar.ts +++ b/src/lib/scalar.ts @@ -1,4 +1,4 @@ -import { Snarky, Provable } from '../o1js.js'; +import { Snarky, Provable } from '../snarky.js'; import { Scalar as Fq } from '../provable/curve-bigint.js'; import { Field, FieldConst, FieldVar } from './field.js'; import { MlArray } from './ml/base.js'; diff --git a/src/lib/state.ts b/src/lib/state.ts index a7ff6f877f..dc848a1996 100644 --- a/src/lib/state.ts +++ b/src/lib/state.ts @@ -1,4 +1,4 @@ -import { ProvablePure } from '../o1js.js'; +import { ProvablePure } from '../snarky.js'; import { FlexibleProvablePure } from './circuit_value.js'; import { AccountUpdate, TokenId } from './account_update.js'; import { PublicKey } from './signature.js'; diff --git a/src/lib/testing/constraint-system.ts b/src/lib/testing/constraint-system.ts index 24751275f1..1f8ad9ba52 100644 --- a/src/lib/testing/constraint-system.ts +++ b/src/lib/testing/constraint-system.ts @@ -4,7 +4,7 @@ * An essential feature is that `constraintSystem()` automatically generates a * variety of fieldvar types for the inputs: constants, variables, and combinators. */ -import { Gate, GateType } from '../../o1js.js'; +import { Gate, GateType } from '../../snarky.js'; import { randomBytes } from '../../bindings/crypto/random.js'; import { Field, FieldType, FieldVar } from '../field.js'; import { Provable } from '../provable.js'; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 243da1099c..9346973f72 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1,4 +1,4 @@ -import { Gate, Pickles, ProvablePure } from '../o1js.js'; +import { Gate, Pickles, ProvablePure } from '../snarky.js'; import { Field, Bool } from './core.js'; import { AccountUpdate, diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 97e9f2a3dc..e400f9c8d4 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -1,5 +1,5 @@ import { expect } from 'expect'; -import { Ledger, Test, Pickles } from '../../o1js.js'; +import { Ledger, Test, Pickles } from '../../snarky.js'; import { PrivateKey as PrivateKeySnarky, PublicKey as PublicKeySnarky, diff --git a/src/mina-signer/src/signature.unit-test.ts b/src/mina-signer/src/signature.unit-test.ts index e13e1cb9ff..9743bda23b 100644 --- a/src/mina-signer/src/signature.unit-test.ts +++ b/src/mina-signer/src/signature.unit-test.ts @@ -7,7 +7,7 @@ import { verify, verifyFieldElement, } from './signature.js'; -import { Ledger, Test } from '../../o1js.js'; +import { Ledger, Test } from '../../snarky.js'; import { Field as FieldSnarky } from '../../lib/core.js'; import { Field } from '../../provable/field-bigint.js'; import { PrivateKey, PublicKey } from '../../provable/curve-bigint.js'; diff --git a/src/mina-signer/src/transaction-hash.unit-test.ts b/src/mina-signer/src/transaction-hash.unit-test.ts index d8faaca2e2..955f451cd2 100644 --- a/src/mina-signer/src/transaction-hash.unit-test.ts +++ b/src/mina-signer/src/transaction-hash.unit-test.ts @@ -1,4 +1,4 @@ -import { Ledger, Test } from '../../o1js.js'; +import { Ledger, Test } from '../../snarky.js'; import { Common, hashPayment, @@ -180,12 +180,7 @@ function paymentToOcamlV1({ common: commonToOcamlV1(common), body: [ 'Payment', - { - source_pk: common.feePayer, - receiver_pk: receiver, - amount, - token_id: '1', - }, + { source_pk: common.feePayer, receiver_pk: receiver, amount, token_id: '1' }, ], }, signer: common.feePayer, @@ -225,10 +220,7 @@ function delegationToOcamlV1({ common: commonToOcamlV1(common), body: [ 'Stake_delegation', - [ - 'Set_delegate', - { delegator: common.feePayer, new_delegate: newDelegate }, - ], + ['Set_delegate', { delegator: common.feePayer, new_delegate: newDelegate }], ], }, signer: common.feePayer, diff --git a/src/o1js.d.ts b/src/snarky.d.ts similarity index 100% rename from src/o1js.d.ts rename to src/snarky.d.ts diff --git a/src/o1js.js b/src/snarky.js similarity index 100% rename from src/o1js.js rename to src/snarky.js diff --git a/tsconfig.node.json b/tsconfig.node.json index 0708208757..7b5d2daf33 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -2,7 +2,7 @@ "extends": "./tsconfig.json", "include": [ "./src/index.ts", - "./src/o1js.js", + "./src/snarky.js", "./src/bindings/js/wrapper.js", "./src/mina-signer/src", "./src/mina-signer/MinaSigner.ts", diff --git a/tsconfig.test.json b/tsconfig.test.json index c3c247bc89..85717a9f8c 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -2,7 +2,7 @@ "extends": "./tsconfig.json", "include": [ "./src/**/*.unit-test.ts", - "./src/o1js.js", + "./src/snarky.js", "./src/bindings/js/wrapper.js" ], "compilerOptions": { diff --git a/tsconfig.web.json b/tsconfig.web.json index 365e1acee5..022d6abd3e 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -1,6 +1,6 @@ { "extends": "./tsconfig.json", - "include": ["./src/index.ts", "./src/o1js.js", "./src/**/*.web.ts"], + "include": ["./src/index.ts", "./src/snarky.js", "./src/**/*.web.ts"], "compilerOptions": { "outDir": "dist/web" } diff --git a/typedoc.json b/typedoc.json index e87c4e1f8b..f670c1036e 100644 --- a/typedoc.json +++ b/typedoc.json @@ -7,7 +7,7 @@ "exclude": ["dist/**/*", "src/mina-signer/**/*", "src/examples/**/*"], "entryPoints": [ "src/index.ts", - "src/o1js.d.ts", + "src/snarky.d.ts", "src/lib/field.ts", "src/lib/group.ts", "src/lib/bool.ts" From 41d39dde66ecc0bcac78766343028e6b0141a1a7 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 4 Jan 2024 11:03:25 -0800 Subject: [PATCH 080/524] Update bindings to use provable-snarky.js --- src/bindings | 2 +- src/lib/circuit_value.ts | 4 ++-- src/lib/foreign-curve.ts | 2 +- src/lib/foreign-ecdsa.ts | 2 +- src/lib/gadgets/foreign-field.ts | 2 +- src/lib/provable-types/bytes.ts | 2 +- src/lib/provable.ts | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bindings b/src/bindings index 9a097f13b0..6e3476e833 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 9a097f13b0ef3f7430dd20bb0e4442bff02d7113 +Subproject commit 6e3476e833821adbad460b01741e0320fcf90b19 diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 81b8cf2ad5..d42f267cdf 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -7,13 +7,13 @@ import { provableTuple, HashInput, NonMethods, -} from '../bindings/lib/provable-o1js.js'; +} from '../bindings/lib/provable-snarky.js'; import type { InferJson, InferProvable, InferredProvable, IsPure, -} from '../bindings/lib/provable-o1js.js'; +} from '../bindings/lib/provable-snarky.js'; import { Provable } from './provable.js'; import { assert } from './errors.js'; import { inCheckedComputation } from './provable-context.js'; diff --git a/src/lib/foreign-curve.ts b/src/lib/foreign-curve.ts index cc34bfc0db..08fb733bfd 100644 --- a/src/lib/foreign-curve.ts +++ b/src/lib/foreign-curve.ts @@ -10,7 +10,7 @@ import { EllipticCurve, Point } from './gadgets/elliptic-curve.js'; import { Field3 } from './gadgets/foreign-field.js'; import { assert } from './gadgets/common.js'; import { Provable } from './provable.js'; -import { provableFromClass } from '../bindings/lib/provable-o1js.js'; +import { provableFromClass } from '../bindings/lib/provable-snarky.js'; // external API export { createForeignCurve, ForeignCurve }; diff --git a/src/lib/foreign-ecdsa.ts b/src/lib/foreign-ecdsa.ts index 5331c6e5b7..bccbaa77ab 100644 --- a/src/lib/foreign-ecdsa.ts +++ b/src/lib/foreign-ecdsa.ts @@ -1,4 +1,4 @@ -import { provableFromClass } from '../bindings/lib/provable-o1js.js'; +import { provableFromClass } from '../bindings/lib/provable-snarky.js'; import { CurveParams } from '../bindings/crypto/elliptic_curve.js'; import { ProvablePureExtended } from './circuit_value.js'; import { diff --git a/src/lib/gadgets/foreign-field.ts b/src/lib/gadgets/foreign-field.ts index 527bda3193..51b815dfc8 100644 --- a/src/lib/gadgets/foreign-field.ts +++ b/src/lib/gadgets/foreign-field.ts @@ -5,7 +5,7 @@ import { inverse as modInverse, mod, } from '../../bindings/crypto/finite_field.js'; -import { provableTuple } from '../../bindings/lib/provable-o1js.js'; +import { provableTuple } from '../../bindings/lib/provable-snarky.js'; import { Bool } from '../bool.js'; import { Unconstrained } from '../circuit_value.js'; import { Field } from '../field.js'; diff --git a/src/lib/provable-types/bytes.ts b/src/lib/provable-types/bytes.ts index fdea69dd7b..9bde301e69 100644 --- a/src/lib/provable-types/bytes.ts +++ b/src/lib/provable-types/bytes.ts @@ -1,4 +1,4 @@ -import { provableFromClass } from '../../bindings/lib/provable-o1js.js'; +import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; import { ProvablePureExtended } from '../circuit_value.js'; import { assert } from '../gadgets/common.js'; import { chunkString } from '../util/arrays.js'; diff --git a/src/lib/provable.ts b/src/lib/provable.ts index 02f26eea2f..8094bcf89e 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -12,7 +12,7 @@ import { InferJson, InferProvable, InferredProvable, -} from '../bindings/lib/provable-o1js.js'; +} from '../bindings/lib/provable-snarky.js'; import { inCheckedComputation, inProver, From 21ef418fbf85b641d2d3fa368a026fa255643055 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 5 Jan 2024 16:29:55 +0200 Subject: [PATCH 081/524] Exercise more complicated zkApps with CI checks. --- .github/actions/live-tests-shared/action.yml | 3 +- run-ci-live-tests.sh | 45 ++++++++++++++++ run-ci-tests.sh | 5 -- .../zkapps/dex/arbitrary_token_interaction.ts | 21 ++------ src/examples/zkapps/dex/dex-with-actions.ts | 28 +++++----- src/examples/zkapps/dex/dex.ts | 21 ++++---- .../zkapps/dex/happy-path-with-actions.ts | 12 ++--- .../zkapps/dex/happy-path-with-proofs.ts | 9 ++-- src/examples/zkapps/dex/run.ts | 17 +----- .../dex/{run-berkeley.ts => run_live.ts} | 52 +++++++++++++------ src/examples/zkapps/dex/upgradability.ts | 20 ++----- 11 files changed, 120 insertions(+), 113 deletions(-) create mode 100755 run-ci-live-tests.sh rename src/examples/zkapps/dex/{run-berkeley.ts => run_live.ts} (87%) diff --git a/.github/actions/live-tests-shared/action.yml b/.github/actions/live-tests-shared/action.yml index a84bad475a..6807ecd9a9 100644 --- a/.github/actions/live-tests-shared/action.yml +++ b/.github/actions/live-tests-shared/action.yml @@ -19,14 +19,13 @@ runs: node-version: '20' - name: Build o1js and execute tests env: - TEST_TYPE: 'Live integration tests' USE_CUSTOM_LOCAL_NETWORK: 'true' run: | git submodule update --init --recursive npm ci npm run build touch profiling.md - sh run-ci-tests.sh + sh run-ci-live-tests.sh cat profiling.md >> $GITHUB_STEP_SUMMARY shell: bash - name: Upload Mina logs diff --git a/run-ci-live-tests.sh b/run-ci-live-tests.sh new file mode 100755 index 0000000000..ba881bb5c7 --- /dev/null +++ b/run-ci-live-tests.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -o pipefail + +# Function to add a prefix to each direct line of output +add_prefix() { + local prefix=$1 + while IFS= read -r line; do + echo "$prefix : $line" + done +} + +echo "" +echo "Running integration tests against the real Mina network." +echo "" + +./run src/examples/zkapps/hello_world/run_live.ts --bundle | add_prefix "HELLO_WORLD" & +HELLO_WORLD_PROC=$! +./run src/examples/zkapps/dex/run_live.ts --bundle | add_prefix "DEX" & +DEX_PROC=$! + +# Wait for each process and capture their exit statuses +FAILURE=0 +wait $HELLO_WORLD_PROC +if [ $? -ne 0 ]; then + echo "" + echo "HELLO_WORLD test failed." + echo "" + FAILURE=1 +fi +wait $DEX_PROC +if [ $? -ne 0 ]; then + echo "" + echo "DEX test failed." + echo "" + FAILURE=1 +fi + +# Exit with failure if any process failed +if [ $FAILURE -ne 0 ]; then + exit 1 +fi + +echo "" +echo "All tests completed successfully." +echo "" diff --git a/run-ci-tests.sh b/run-ci-tests.sh index 4b19ebd25d..d59c3cd09b 100755 --- a/run-ci-tests.sh +++ b/run-ci-tests.sh @@ -27,11 +27,6 @@ case $TEST_TYPE in ./run src/examples/zkapps/dex/happy-path-with-proofs.ts --bundle ;; -"Live integration tests") - echo "Running integration tests against real Mina network" - ./run src/examples/zkapps/hello_world/run_live.ts --bundle - ;; - "Unit tests") echo "Running unit tests" cd src/mina-signer diff --git a/src/examples/zkapps/dex/arbitrary_token_interaction.ts b/src/examples/zkapps/dex/arbitrary_token_interaction.ts index cf75347c40..aaf7d81555 100644 --- a/src/examples/zkapps/dex/arbitrary_token_interaction.ts +++ b/src/examples/zkapps/dex/arbitrary_token_interaction.ts @@ -1,16 +1,7 @@ -import { - isReady, - Mina, - AccountUpdate, - UInt64, - shutdown, - TokenId, -} from 'o1js'; +import { AccountUpdate, Mina, TokenId, UInt64 } from 'o1js'; import { TokenContract, addresses, keys, tokenIds } from './dex.js'; -await isReady; let doProofs = true; - let Local = Mina.LocalBlockchain({ proofsEnabled: doProofs }); Mina.setActiveInstance(Local); let accountFee = Mina.accountCreationFee(); @@ -32,9 +23,9 @@ await TokenContract.compile(); let tokenX = new TokenContract(addresses.tokenX); console.log('deploy & init token contracts...'); -tx = await Mina.transaction(userKey, () => { +tx = await Mina.transaction(userAddress, () => { // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves - let feePayerUpdate = AccountUpdate.createSigned(userKey); + let feePayerUpdate = AccountUpdate.createSigned(userAddress); feePayerUpdate.balance.subInPlace(accountFee.mul(1)); tokenX.deploy(); }); @@ -43,9 +34,9 @@ tx.sign([keys.tokenX]); await tx.send(); console.log('arbitrary token minting...'); -tx = await Mina.transaction(userKey, () => { +tx = await Mina.transaction(userAddress, () => { // pay fees for creating user's token X account - AccountUpdate.createSigned(userKey).balance.subInPlace(accountFee.mul(1)); + AccountUpdate.createSigned(userAddress).balance.subInPlace(accountFee.mul(1)); // 😈😈😈 mint any number of tokens to our account 😈😈😈 let tokenContract = new TokenContract(addresses.tokenX); tokenContract.token.mint({ @@ -61,5 +52,3 @@ console.log( 'User tokens: ', Mina.getBalance(userAddress, tokenIds.X).value.toBigInt() ); - -shutdown(); diff --git a/src/examples/zkapps/dex/dex-with-actions.ts b/src/examples/zkapps/dex/dex-with-actions.ts index 34353a2c4d..145197319b 100644 --- a/src/examples/zkapps/dex/dex-with-actions.ts +++ b/src/examples/zkapps/dex/dex-with-actions.ts @@ -5,27 +5,26 @@ */ import { Account, - method, AccountUpdate, + Field, + InferProvable, + Mina, + Permissions, + Provable, PublicKey, + Reducer, SmartContract, - UInt64, - Struct, State, - state, + Struct, TokenId, - Reducer, - Field, - Permissions, - isReady, - Mina, - InferProvable, - Provable, + UInt64, + method, + state, } from 'o1js'; import { TokenContract, randomAccounts } from './dex.js'; -export { Dex, DexTokenHolder, addresses, keys, tokenIds, getTokenBalances }; +export { Dex, DexTokenHolder, addresses, getTokenBalances, keys, tokenIds }; class RedeemAction extends Struct({ address: PublicKey, dl: UInt64 }) {} @@ -133,7 +132,7 @@ class Dex extends SmartContract { // calculate dy outside circuit let x = Account(this.address, TokenId.derive(this.tokenX)).balance.get(); let y = Account(this.address, TokenId.derive(this.tokenY)).balance.get(); - if (x.value.isConstant() && x.value.isZero().toBoolean()) { + if (x.value.isConstant() && x.value.equals(0).toBoolean()) { throw Error( 'Cannot call `supplyLiquidity` when reserves are zero. Use `supplyLiquidityBase`.' ); @@ -314,9 +313,8 @@ class DexTokenHolder extends SmartContract { } } -await isReady; let { keys, addresses } = randomAccounts( - false, + process.env.USE_CUSTOM_LOCAL_NETWORK === 'true', 'tokenX', 'tokenY', 'dex', diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index f9906fb96e..8bf5a1ea8f 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -1,29 +1,27 @@ import { Account, + AccountUpdate, Bool, - Circuit, DeployArgs, Field, Int64, - isReady, - method, Mina, - AccountUpdate, Permissions, PrivateKey, + Provable, PublicKey, SmartContract, + State, + Struct, + TokenId, + UInt32, UInt64, VerificationKey, - Struct, - State, + method, state, - UInt32, - TokenId, - Provable, } from 'o1js'; -export { createDex, TokenContract, keys, addresses, tokenIds, randomAccounts }; +export { TokenContract, addresses, createDex, keys, randomAccounts, tokenIds }; class UInt64x2 extends Struct([UInt64, UInt64]) {} @@ -493,9 +491,8 @@ const savedKeys = [ 'EKEyPVU37EGw8CdGtUYnfDcBT2Eu7B6rSdy64R68UHYbrYbVJett', ]; -await isReady; let { keys, addresses } = randomAccounts( - false, + process.env.USE_CUSTOM_LOCAL_NETWORK === 'true', 'tokenX', 'tokenY', 'dex', diff --git a/src/examples/zkapps/dex/happy-path-with-actions.ts b/src/examples/zkapps/dex/happy-path-with-actions.ts index 52eab4f2a0..16ea8fddcb 100644 --- a/src/examples/zkapps/dex/happy-path-with-actions.ts +++ b/src/examples/zkapps/dex/happy-path-with-actions.ts @@ -1,23 +1,19 @@ -import { isReady, Mina, AccountUpdate, UInt64 } from 'o1js'; +import { expect } from 'expect'; +import { AccountUpdate, Mina, UInt64 } from 'o1js'; +import { tic, toc } from '../../utils/tic-toc.node.js'; import { Dex, DexTokenHolder, addresses, + getTokenBalances, keys, tokenIds, - getTokenBalances, } from './dex-with-actions.js'; import { TokenContract } from './dex.js'; -import { expect } from 'expect'; -import { tic, toc } from '../../utils/tic-toc.node.js'; - -await isReady; let proofsEnabled = true; - tic('Happy path with actions'); console.log(); - let Local = Mina.LocalBlockchain({ proofsEnabled, enforceTransactionLimits: true, diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index f58f478fcf..4727851012 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -1,10 +1,8 @@ -import { isReady, Mina, AccountUpdate, UInt64 } from 'o1js'; -import { createDex, TokenContract, addresses, keys, tokenIds } from './dex.js'; import { expect } from 'expect'; -import { tic, toc } from '../../utils/tic-toc.node.js'; +import { AccountUpdate, Mina, UInt64 } from 'o1js'; import { getProfiler } from '../../utils/profiler.js'; - -await isReady; +import { tic, toc } from '../../utils/tic-toc.node.js'; +import { TokenContract, addresses, createDex, keys, tokenIds } from './dex.js'; const TokenProfiler = getProfiler('Token with Proofs'); TokenProfiler.start('Token with proofs test flow'); @@ -12,7 +10,6 @@ let proofsEnabled = true; tic('Happy path with proofs'); console.log(); - let Local = Mina.LocalBlockchain({ proofsEnabled, enforceTransactionLimits: false, diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index 4899e3fdb2..e9f15fad22 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -1,20 +1,9 @@ -import { - isReady, - Mina, - AccountUpdate, - UInt64, - shutdown, - Permissions, - TokenId, -} from 'o1js'; -import { createDex, TokenContract, addresses, keys, tokenIds } from './dex.js'; import { expect } from 'expect'; - +import { AccountUpdate, Mina, Permissions, TokenId, UInt64 } from 'o1js'; import { getProfiler } from '../../utils/profiler.js'; +import { TokenContract, addresses, createDex, keys, tokenIds } from './dex.js'; -await isReady; let proofsEnabled = false; - let Local = Mina.LocalBlockchain({ proofsEnabled, enforceTransactionLimits: false, @@ -536,5 +525,3 @@ async function main({ withVesting }: { withVesting: boolean }) { DexProfiler.stop().store(); } - -shutdown(); diff --git a/src/examples/zkapps/dex/run-berkeley.ts b/src/examples/zkapps/dex/run_live.ts similarity index 87% rename from src/examples/zkapps/dex/run-berkeley.ts rename to src/examples/zkapps/dex/run_live.ts index 540382bd67..f675e545fa 100644 --- a/src/examples/zkapps/dex/run-berkeley.ts +++ b/src/examples/zkapps/dex/run_live.ts @@ -1,11 +1,14 @@ +import { expect } from 'expect'; import { - isReady, - Mina, AccountUpdate, - UInt64, + Lightnet, + Mina, PrivateKey, + UInt64, fetchAccount, } from 'o1js'; +import os from 'os'; +import { tic, toc } from '../../utils/tic-toc.node.js'; import { Dex, DexTokenHolder, @@ -14,30 +17,36 @@ import { tokenIds, } from './dex-with-actions.js'; import { TokenContract } from './dex.js'; -import { expect } from 'expect'; -import { tic, toc } from '../../utils/tic-toc.node.js'; - -await isReady; +const useCustomLocalNetwork = process.env.USE_CUSTOM_LOCAL_NETWORK === 'true'; // setting this to a higher number allows you to skip a few transactions, to pick up after an error const successfulTransactions = 0; -tic('Run DEX with actions, happy path, on Berkeley'); +tic('Run DEX with actions, happy path, against real network.'); console.log(); - -let Berkeley = Mina.Network({ - mina: 'https://berkeley.minascan.io/graphql', - archive: 'https://archive-node-api.p42.xyz', +const network = Mina.Network({ + mina: useCustomLocalNetwork + ? 'http://localhost:8080/graphql' + : 'https://berkeley.minascan.io/graphql', + archive: useCustomLocalNetwork + ? 'http://localhost:8282' + : 'https://api.minascan.io/archive/berkeley/v1/graphql', + lightnetAccountManager: 'http://localhost:8181', }); -Mina.setActiveInstance(Berkeley); +Mina.setActiveInstance(network); let accountFee = Mina.accountCreationFee(); let tx, pendingTx: Mina.TransactionId, balances, oldBalances; // compile contracts & wait for fee payer to be funded -let { sender, senderKey } = await ensureFundedAccount( - 'EKDrVGPC6iVRqB2bMMakNBTdEi8M1TqMn5TViLe9bafcpEExPYui' -); +const senderKey = useCustomLocalNetwork + ? (await Lightnet.acquireKeyPair()).privateKey + : PrivateKey.random(); +const sender = senderKey.toPublicKey(); +if (!useCustomLocalNetwork) { + console.log(`Funding the fee payer account.`); + await ensureFundedAccount(senderKey.toBase58()); +} TokenContract.analyzeMethods(); DexTokenHolder.analyzeMethods(); @@ -255,6 +264,12 @@ if (successfulTransactions <= 8) { toc(); console.log('dex happy path with actions was successful! 🎉'); +console.log(); +// Tear down +const keyPairReleaseMessage = await Lightnet.releaseKeyPair({ + publicKey: sender.toBase58(), +}); +if (keyPairReleaseMessage) console.info(keyPairReleaseMessage); async function ensureFundedAccount(privateKeyBase58: string) { let senderKey = PrivateKey.fromBase58(privateKeyBase58); @@ -271,7 +286,10 @@ async function ensureFundedAccount(privateKeyBase58: string) { function logPendingTransaction(pendingTx: Mina.TransactionId) { if (!pendingTx.isSuccess) throw Error('transaction failed'); console.log( - `tx sent: https://berkeley.minaexplorer.com/transaction/${pendingTx.hash()}` + 'tx sent: ' + + (useCustomLocalNetwork + ? `file:///${os.homedir()}/.cache/zkapp-cli/lightnet/explorer//index.html?target=transaction&hash=${pendingTx.hash()}` + : `https://minascan.io/berkeley/tx/${pendingTx.hash()}?type=zk-tx`) ); } diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 02f4f2e78d..273431c903 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -1,32 +1,18 @@ -import { - AccountUpdate, - Mina, - isReady, - Permissions, - PrivateKey, - UInt64, -} from 'o1js'; -import { createDex, TokenContract, addresses, keys, tokenIds } from './dex.js'; import { expect } from 'expect'; +import { AccountUpdate, Mina, Permissions, PrivateKey, UInt64 } from 'o1js'; import { getProfiler } from '../../utils/profiler.js'; - -await isReady; +import { TokenContract, addresses, createDex, keys, tokenIds } from './dex.js'; let proofsEnabled = false; - console.log('starting upgradeability tests'); - await upgradeabilityTests({ withVesting: false, }); console.log('all upgradeability tests were successful! 🎉'); - console.log('starting atomic actions tests'); - await atomicActionsTest({ withVesting: false, }); - console.log('all atomic actions tests were successful!'); async function atomicActionsTest({ withVesting }: { withVesting: boolean }) { @@ -310,7 +296,7 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { console.log('deploy dex contracts...'); - tx = await Mina.transaction(feePayerKey, () => { + tx = await Mina.transaction(feePayerAddress, () => { // pay fees for creating 3 dex accounts AccountUpdate.fundNewAccount(feePayerAddress, 3); dex.deploy(); From fb9e61bc566c054b54f312516914997d767e3c5e Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 5 Jan 2024 16:40:31 +0200 Subject: [PATCH 082/524] Use Bash explicitely to make CI happy. --- .github/actions/live-tests-shared/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/live-tests-shared/action.yml b/.github/actions/live-tests-shared/action.yml index 6807ecd9a9..8bd6d166af 100644 --- a/.github/actions/live-tests-shared/action.yml +++ b/.github/actions/live-tests-shared/action.yml @@ -25,7 +25,7 @@ runs: npm ci npm run build touch profiling.md - sh run-ci-live-tests.sh + bash run-ci-live-tests.sh cat profiling.md >> $GITHUB_STEP_SUMMARY shell: bash - name: Upload Mina logs From 6c5934f33a7b37f138e76946b5a91813ee0a8b32 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 5 Jan 2024 17:56:07 +0200 Subject: [PATCH 083/524] Missed archive node api port exposure. --- .github/workflows/live-tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/live-tests.yml b/.github/workflows/live-tests.yml index d5d00b7615..d6c3f72ff4 100644 --- a/.github/workflows/live-tests.yml +++ b/.github/workflows/live-tests.yml @@ -28,6 +28,7 @@ jobs: - 5432:5432 - 8080:8080 - 8181:8181 + - 8282:8282 volumes: - /tmp:/root/logs steps: @@ -53,6 +54,7 @@ jobs: - 5432:5432 - 8080:8080 - 8181:8181 + - 8282:8282 volumes: - /tmp:/root/logs steps: @@ -78,6 +80,7 @@ jobs: - 5432:5432 - 8080:8080 - 8181:8181 + - 8282:8282 volumes: - /tmp:/root/logs steps: From 8f757ee805aeec207c78c976128e2d8caa9c9dd7 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 5 Jan 2024 18:14:10 +0200 Subject: [PATCH 084/524] Extra slash in file link removed. --- src/examples/zkapps/dex/run_live.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/run_live.ts b/src/examples/zkapps/dex/run_live.ts index f675e545fa..db9336fcc4 100644 --- a/src/examples/zkapps/dex/run_live.ts +++ b/src/examples/zkapps/dex/run_live.ts @@ -288,7 +288,7 @@ function logPendingTransaction(pendingTx: Mina.TransactionId) { console.log( 'tx sent: ' + (useCustomLocalNetwork - ? `file:///${os.homedir()}/.cache/zkapp-cli/lightnet/explorer//index.html?target=transaction&hash=${pendingTx.hash()}` + ? `file://${os.homedir()}/.cache/zkapp-cli/lightnet/explorer//index.html?target=transaction&hash=${pendingTx.hash()}` : `https://minascan.io/berkeley/tx/${pendingTx.hash()}?type=zk-tx`) ); } From 93070076aed0486a122a88b9fe6c7f2fdb5ee936 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 8 Jan 2024 12:37:13 +0100 Subject: [PATCH 085/524] fix import cycles by moving high-level dependencies out of gadgets/common, and gadgets out of int.ts --- src/lib/gadgets/bit-slices.ts | 54 +++++++++++++++++++ src/lib/gadgets/common.ts | 51 +----------------- src/lib/gadgets/sha256.ts | 3 +- src/lib/int.ts | 93 +++++++++++++++++---------------- src/lib/keccak.ts | 2 +- src/lib/provable-types/bytes.ts | 8 ++- 6 files changed, 112 insertions(+), 99 deletions(-) create mode 100644 src/lib/gadgets/bit-slices.ts diff --git a/src/lib/gadgets/bit-slices.ts b/src/lib/gadgets/bit-slices.ts new file mode 100644 index 0000000000..b6b3d7acfe --- /dev/null +++ b/src/lib/gadgets/bit-slices.ts @@ -0,0 +1,54 @@ +/** + * Gadgets for converting between field elements and bit slices of various lengths + */ +import { Field } from '../field.js'; +import { UInt8 } from '../int.js'; +import { Provable } from '../provable.js'; +import { chunk } from '../util/arrays.js'; + +export { bytesToWord, wordToBytes, wordsToBytes, bytesToWords }; + +// conversion between bytes and multi-byte words + +/** + * Convert an array of UInt8 to a Field element. Expects little endian representation. + */ +function bytesToWord(wordBytes: UInt8[]): Field { + return wordBytes.reduce((acc, byte, idx) => { + const shift = 1n << BigInt(8 * idx); + return acc.add(byte.value.mul(shift)); + }, Field.from(0)); +} + +/** + * Convert a Field element to an array of UInt8. Expects little endian representation. + * @param bytesPerWord number of bytes per word + */ +function wordToBytes(word: Field, bytesPerWord = 8): UInt8[] { + let bytes = Provable.witness(Provable.Array(UInt8, bytesPerWord), () => { + let w = word.toBigInt(); + return Array.from({ length: bytesPerWord }, (_, k) => + UInt8.from((w >> BigInt(8 * k)) & 0xffn) + ); + }); + + // check decomposition + bytesToWord(bytes).assertEquals(word); + + return bytes; +} + +/** + * Convert an array of Field elements to an array of UInt8. Expects little endian representation. + * @param bytesPerWord number of bytes per word + */ +function wordsToBytes(words: Field[], bytesPerWord = 8): UInt8[] { + return words.flatMap((w) => wordToBytes(w, bytesPerWord)); +} +/** + * Convert an array of UInt8 to an array of Field elements. Expects little endian representation. + * @param bytesPerWord number of bytes per word + */ +function bytesToWords(bytes: UInt8[], bytesPerWord = 8): Field[] { + return chunk(bytes, bytesPerWord).map(bytesToWord); +} diff --git a/src/lib/gadgets/common.ts b/src/lib/gadgets/common.ts index 27c1b49ff0..3a1a36b5a5 100644 --- a/src/lib/gadgets/common.ts +++ b/src/lib/gadgets/common.ts @@ -2,9 +2,7 @@ import { Field, FieldConst, FieldVar, VarField } from '../field.js'; import { Tuple, TupleN } from '../util/types.js'; import { Snarky } from '../../snarky.js'; import { MlArray } from '../ml/base.js'; -import { Bool, UInt8 } from '../../index.js'; -import { Provable } from '../provable.js'; -import { chunk } from '../util/arrays.js'; +import { Bool } from '../bool.js'; const MAX_BITS = 64 as const; @@ -18,10 +16,6 @@ export { assert, bitSlice, divideWithRemainder, - bytesToWord, - bytesToWords, - wordsToBytes, - wordToBytes, }; function existsOne(compute: () => bigint) { @@ -90,46 +84,3 @@ function divideWithRemainder(numerator: bigint, denominator: bigint) { const remainder = numerator - denominator * quotient; return { quotient, remainder }; } - -/** - * Convert an array of UInt8 to a Field element. Expects little endian representation. - */ -function bytesToWord(wordBytes: UInt8[]): Field { - return wordBytes.reduce((acc, byte, idx) => { - const shift = 1n << BigInt(8 * idx); - return acc.add(byte.value.mul(shift)); - }, Field.from(0)); -} - -/** - * Convert a Field element to an array of UInt8. Expects little endian representation. - * @param bytesPerWord number of bytes per word - */ -function wordToBytes(word: Field, bytesPerWord = 8): UInt8[] { - let bytes = Provable.witness(Provable.Array(UInt8, bytesPerWord), () => { - let w = word.toBigInt(); - return Array.from({ length: bytesPerWord }, (_, k) => - UInt8.from((w >> BigInt(8 * k)) & 0xffn) - ); - }); - - // check decomposition - bytesToWord(bytes).assertEquals(word); - - return bytes; -} - -/** - * Convert an array of Field elements to an array of UInt8. Expects little endian representation. - * @param bytesPerWord number of bytes per word - */ -function wordsToBytes(words: Field[], bytesPerWord = 8): UInt8[] { - return words.flatMap((w) => wordToBytes(w, bytesPerWord)); -} -/** - * Convert an array of UInt8 to an array of Field elements. Expects little endian representation. - * @param bytesPerWord number of bytes per word - */ -function bytesToWords(bytes: UInt8[], bytesPerWord = 8): Field[] { - return chunk(bytes, bytesPerWord).map(bytesToWord); -} diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 644cfb8f45..8cac25b58c 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -6,7 +6,8 @@ import { FlexibleBytes } from '../provable-types/bytes.js'; import { Bytes } from '../provable-types/provable-types.js'; import { chunk } from '../util/arrays.js'; import { TupleN } from '../util/types.js'; -import { bitSlice, bytesToWord, exists, wordToBytes } from './common.js'; +import { bytesToWord, wordToBytes } from './bit-slices.js'; +import { bitSlice, exists } from './common.js'; import { Gadgets } from './gadgets.js'; import { rangeCheck16 } from './range-check.js'; diff --git a/src/lib/int.ts b/src/lib/int.ts index a98ff8cf4b..73ff036ed8 100644 --- a/src/lib/int.ts +++ b/src/lib/int.ts @@ -3,9 +3,12 @@ import { AnyConstructor, CircuitValue, Struct, prop } from './circuit_value.js'; import { Types } from '../bindings/mina-transaction/types.js'; import { HashInput } from './hash.js'; import { Provable } from './provable.js'; -import { Gadgets } from './gadgets/gadgets.js'; - +import * as RangeCheck from './gadgets/range-check.js'; +import * as Bitwise from './gadgets/bitwise.js'; +import { addMod32 } from './gadgets/arithmetic.js'; +import type { Gadgets } from './gadgets/gadgets.js'; import { FieldVar, withMessage } from './field.js'; + // external API export { UInt8, UInt32, UInt64, Int64, Sign }; @@ -74,7 +77,7 @@ class UInt64 extends CircuitValue { } static check(x: UInt64) { - Gadgets.rangeCheckN(UInt64.NUM_BITS, x.value); + RangeCheck.rangeCheckN(UInt64.NUM_BITS, x.value); } static toInput(x: UInt64): HashInput { @@ -149,11 +152,11 @@ class UInt64 extends CircuitValue { () => new Field(x.toBigInt() / y_.toBigInt()) ); - Gadgets.rangeCheckN(UInt64.NUM_BITS, q); + RangeCheck.rangeCheckN(UInt64.NUM_BITS, q); // TODO: Could be a bit more efficient let r = x.sub(q.mul(y_)).seal(); - Gadgets.rangeCheckN(UInt64.NUM_BITS, r); + RangeCheck.rangeCheckN(UInt64.NUM_BITS, r); let r_ = new UInt64(r); let q_ = new UInt64(q); @@ -189,7 +192,7 @@ class UInt64 extends CircuitValue { */ mul(y: UInt64 | number) { let z = this.value.mul(UInt64.from(y).value); - Gadgets.rangeCheckN(UInt64.NUM_BITS, z); + RangeCheck.rangeCheckN(UInt64.NUM_BITS, z); return new UInt64(z); } @@ -198,7 +201,7 @@ class UInt64 extends CircuitValue { */ add(y: UInt64 | number) { let z = this.value.add(UInt64.from(y).value); - Gadgets.rangeCheckN(UInt64.NUM_BITS, z); + RangeCheck.rangeCheckN(UInt64.NUM_BITS, z); return new UInt64(z); } @@ -207,7 +210,7 @@ class UInt64 extends CircuitValue { */ sub(y: UInt64 | number) { let z = this.value.sub(UInt64.from(y).value); - Gadgets.rangeCheckN(UInt64.NUM_BITS, z); + RangeCheck.rangeCheckN(UInt64.NUM_BITS, z); return new UInt64(z); } @@ -231,7 +234,7 @@ class UInt64 extends CircuitValue { * ``` */ xor(x: UInt64) { - return new UInt64(Gadgets.xor(this.value, x.value, UInt64.NUM_BITS)); + return new UInt64(Bitwise.xor(this.value, x.value, UInt64.NUM_BITS)); } /** @@ -264,7 +267,7 @@ class UInt64 extends CircuitValue { * */ not() { - return new UInt64(Gadgets.not(this.value, UInt64.NUM_BITS, false)); + return new UInt64(Bitwise.not(this.value, UInt64.NUM_BITS, false)); } /** @@ -296,7 +299,7 @@ class UInt64 extends CircuitValue { * ``` */ rotate(bits: number, direction: 'left' | 'right' = 'left') { - return new UInt64(Gadgets.rotate64(this.value, bits, direction)); + return new UInt64(Bitwise.rotate64(this.value, bits, direction)); } /** @@ -317,7 +320,7 @@ class UInt64 extends CircuitValue { * ``` */ leftShift(bits: number) { - return new UInt64(Gadgets.leftShift64(this.value, bits)); + return new UInt64(Bitwise.leftShift64(this.value, bits)); } /** @@ -338,7 +341,7 @@ class UInt64 extends CircuitValue { * ``` */ rightShift(bits: number) { - return new UInt64(Gadgets.leftShift64(this.value, bits)); + return new UInt64(Bitwise.leftShift64(this.value, bits)); } /** @@ -367,7 +370,7 @@ class UInt64 extends CircuitValue { * ``` */ and(x: UInt64) { - return new UInt64(Gadgets.and(this.value, x.value, UInt64.NUM_BITS)); + return new UInt64(Bitwise.and(this.value, x.value, UInt64.NUM_BITS)); } /** @@ -382,9 +385,9 @@ class UInt64 extends CircuitValue { let xMinusY = this.value.sub(y.value).seal(); let yMinusX = xMinusY.neg(); - let xMinusYFits = Gadgets.isInRangeN(UInt64.NUM_BITS, xMinusY); + let xMinusYFits = RangeCheck.isInRangeN(UInt64.NUM_BITS, xMinusY); - let yMinusXFits = Gadgets.isInRangeN(UInt64.NUM_BITS, yMinusX); + let yMinusXFits = RangeCheck.isInRangeN(UInt64.NUM_BITS, yMinusX); xMinusYFits.or(yMinusXFits).assertEquals(true); // x <= y if y - x fits in 64 bits @@ -402,9 +405,9 @@ class UInt64 extends CircuitValue { let xMinusY = this.value.sub(y.value).seal(); let yMinusX = xMinusY.neg(); - let xMinusYFits = Gadgets.isInRangeN(UInt64.NUM_BITS, xMinusY); + let xMinusYFits = RangeCheck.isInRangeN(UInt64.NUM_BITS, xMinusY); - let yMinusXFits = Gadgets.isInRangeN(UInt64.NUM_BITS, yMinusX); + let yMinusXFits = RangeCheck.isInRangeN(UInt64.NUM_BITS, yMinusX); xMinusYFits.or(yMinusXFits).assertEquals(true); // x <= y if y - x fits in 64 bits @@ -435,7 +438,7 @@ class UInt64 extends CircuitValue { return; } let yMinusX = y.value.sub(this.value).seal(); - Gadgets.rangeCheckN(UInt64.NUM_BITS, yMinusX, message); + RangeCheck.rangeCheckN(UInt64.NUM_BITS, yMinusX, message); } /** @@ -583,7 +586,7 @@ class UInt32 extends CircuitValue { } static check(x: UInt32) { - Gadgets.rangeCheck32(x.value); + RangeCheck.rangeCheck32(x.value); } static toInput(x: UInt32): HashInput { return { packed: [[x.value, 32]] }; @@ -633,7 +636,7 @@ class UInt32 extends CircuitValue { * Addition modulo 2^32. Check {@link Gadgets.addMod32} for a detailed description. */ addMod32(y: UInt32) { - return new UInt32(Gadgets.addMod32(this.value, y.value)); + return new UInt32(addMod32(this.value, y.value)); } /** @@ -663,11 +666,11 @@ class UInt32 extends CircuitValue { () => new Field(x.toBigInt() / y_.toBigInt()) ); - Gadgets.rangeCheck32(q); + RangeCheck.rangeCheck32(q); // TODO: Could be a bit more efficient let r = x.sub(q.mul(y_)).seal(); - Gadgets.rangeCheck32(r); + RangeCheck.rangeCheck32(r); let r_ = new UInt32(r); let q_ = new UInt32(q); @@ -700,7 +703,7 @@ class UInt32 extends CircuitValue { */ mul(y: UInt32 | number) { let z = this.value.mul(UInt32.from(y).value); - Gadgets.rangeCheck32(z); + RangeCheck.rangeCheck32(z); return new UInt32(z); } /** @@ -708,7 +711,7 @@ class UInt32 extends CircuitValue { */ add(y: UInt32 | number) { let z = this.value.add(UInt32.from(y).value); - Gadgets.rangeCheck32(z); + RangeCheck.rangeCheck32(z); return new UInt32(z); } /** @@ -716,7 +719,7 @@ class UInt32 extends CircuitValue { */ sub(y: UInt32 | number) { let z = this.value.sub(UInt32.from(y).value); - Gadgets.rangeCheck32(z); + RangeCheck.rangeCheck32(z); return new UInt32(z); } @@ -740,7 +743,7 @@ class UInt32 extends CircuitValue { * ``` */ xor(x: UInt32) { - return new UInt32(Gadgets.xor(this.value, x.value, UInt32.NUM_BITS)); + return new UInt32(Bitwise.xor(this.value, x.value, UInt32.NUM_BITS)); } /** @@ -771,7 +774,7 @@ class UInt32 extends CircuitValue { * @param a - The value to apply NOT to. */ not() { - return new UInt32(Gadgets.not(this.value, UInt32.NUM_BITS, false)); + return new UInt32(Bitwise.not(this.value, UInt32.NUM_BITS, false)); } /** @@ -803,7 +806,7 @@ class UInt32 extends CircuitValue { * ``` */ rotate(bits: number, direction: 'left' | 'right' = 'left') { - return new UInt32(Gadgets.rotate32(this.value, bits, direction)); + return new UInt32(Bitwise.rotate32(this.value, bits, direction)); } /** @@ -826,7 +829,7 @@ class UInt32 extends CircuitValue { * ``` */ leftShift(bits: number) { - return new UInt32(Gadgets.leftShift32(this.value, bits)); + return new UInt32(Bitwise.leftShift32(this.value, bits)); } /** @@ -849,7 +852,7 @@ class UInt32 extends CircuitValue { * ``` */ rightShift(bits: number) { - return new UInt32(Gadgets.rightShift64(this.value, bits)); + return new UInt32(Bitwise.rightShift64(this.value, bits)); } /** @@ -878,7 +881,7 @@ class UInt32 extends CircuitValue { * ``` */ and(x: UInt32) { - return new UInt32(Gadgets.and(this.value, x.value, UInt32.NUM_BITS)); + return new UInt32(Bitwise.and(this.value, x.value, UInt32.NUM_BITS)); } /** @@ -892,8 +895,8 @@ class UInt32 extends CircuitValue { } else { let xMinusY = this.value.sub(y.value).seal(); let yMinusX = xMinusY.neg(); - let xMinusYFits = Gadgets.isInRangeN(UInt32.NUM_BITS, xMinusY); - let yMinusXFits = Gadgets.isInRangeN(UInt32.NUM_BITS, yMinusX); + let xMinusYFits = RangeCheck.isInRangeN(UInt32.NUM_BITS, xMinusY); + let yMinusXFits = RangeCheck.isInRangeN(UInt32.NUM_BITS, yMinusX); xMinusYFits.or(yMinusXFits).assertEquals(true); // x <= y if y - x fits in 64 bits return yMinusXFits; @@ -909,8 +912,8 @@ class UInt32 extends CircuitValue { } else { let xMinusY = this.value.sub(y.value).seal(); let yMinusX = xMinusY.neg(); - let xMinusYFits = Gadgets.isInRangeN(UInt32.NUM_BITS, xMinusY); - let yMinusXFits = Gadgets.isInRangeN(UInt32.NUM_BITS, yMinusX); + let xMinusYFits = RangeCheck.isInRangeN(UInt32.NUM_BITS, xMinusY); + let yMinusXFits = RangeCheck.isInRangeN(UInt32.NUM_BITS, yMinusX); xMinusYFits.or(yMinusXFits).assertEquals(true); // x <= y if y - x fits in 64 bits return yMinusXFits; @@ -940,7 +943,7 @@ class UInt32 extends CircuitValue { return; } let yMinusX = y.value.sub(this.value).seal(); - Gadgets.rangeCheckN(UInt32.NUM_BITS, yMinusX, message); + RangeCheck.rangeCheckN(UInt32.NUM_BITS, yMinusX, message); } /** @@ -1340,7 +1343,7 @@ class UInt8 extends Struct({ */ add(y: UInt8 | bigint | number) { let z = this.value.add(UInt8.from(y).value); - Gadgets.rangeCheck8(z); + RangeCheck.rangeCheck8(z); return UInt8.Unsafe.fromField(z); } @@ -1358,7 +1361,7 @@ class UInt8 extends Struct({ */ sub(y: UInt8 | bigint | number) { let z = this.value.sub(UInt8.from(y).value); - Gadgets.rangeCheck8(z); + RangeCheck.rangeCheck8(z); return UInt8.Unsafe.fromField(z); } @@ -1376,7 +1379,7 @@ class UInt8 extends Struct({ */ mul(y: UInt8 | bigint | number) { let z = this.value.mul(UInt8.from(y).value); - Gadgets.rangeCheck8(z); + RangeCheck.rangeCheck8(z); return UInt8.Unsafe.fromField(z); } @@ -1436,8 +1439,8 @@ class UInt8 extends Struct({ // q, r being 16 bits is enough for them to be 8 bits, // thanks to the === x check and the r < y check below - Gadgets.rangeCheck16(q); - Gadgets.rangeCheck16(r); + RangeCheck.rangeCheck16(q); + RangeCheck.rangeCheck16(r); let remainder = UInt8.Unsafe.fromField(r); let quotient = UInt8.Unsafe.fromField(q); @@ -1526,7 +1529,7 @@ class UInt8 extends Struct({ try { // x <= y <=> y - x >= 0 which is implied by y - x in [0, 2^16) let yMinusX = y_.value.sub(this.value).seal(); - Gadgets.rangeCheck16(yMinusX); + RangeCheck.rangeCheck16(yMinusX); } catch (err) { throw withMessage(err, message); } @@ -1630,7 +1633,7 @@ class UInt8 extends Struct({ */ static check(x: { value: Field } | Field) { if (x instanceof Field) x = { value: x }; - Gadgets.rangeCheck8(x.value); + RangeCheck.rangeCheck8(x.value); } static toInput(x: { value: Field }): HashInput { @@ -1674,6 +1677,6 @@ class UInt8 extends Struct({ private static checkConstant(x: Field) { if (!x.isConstant()) return; - Gadgets.rangeCheck8(x); + RangeCheck.rangeCheck8(x); } } diff --git a/src/lib/keccak.ts b/src/lib/keccak.ts index e4e2e4aec1..1d1f0e40a9 100644 --- a/src/lib/keccak.ts +++ b/src/lib/keccak.ts @@ -4,7 +4,7 @@ import { assert } from './errors.js'; import { FlexibleBytes } from './provable-types/bytes.js'; import { UInt8 } from './int.js'; import { Bytes } from './provable-types/provable-types.js'; -import { bytesToWords, wordsToBytes } from './gadgets/common.js'; +import { bytesToWords, wordsToBytes } from './gadgets/bit-slices.js'; export { Keccak }; diff --git a/src/lib/provable-types/bytes.ts b/src/lib/provable-types/bytes.ts index 992e10bd1f..811e93981c 100644 --- a/src/lib/provable-types/bytes.ts +++ b/src/lib/provable-types/bytes.ts @@ -1,11 +1,15 @@ import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; -import { ProvablePureExtended } from '../circuit_value.js'; +import type { ProvablePureExtended } from '../circuit_value.js'; import { assert } from '../gadgets/common.js'; import { chunkString } from '../util/arrays.js'; import { Provable } from '../provable.js'; import { UInt8 } from '../int.js'; -export { Bytes, createBytes, FlexibleBytes }; +// external API +export { Bytes }; + +// internal API +export { createBytes, FlexibleBytes }; type FlexibleBytes = Bytes | (UInt8 | bigint | number)[] | Uint8Array; From dd7b88ab3aec7e772fdd4d1e77e1abac66777c35 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 8 Jan 2024 12:50:30 +0100 Subject: [PATCH 086/524] move other bit slicing gadget to bit slicing file --- src/lib/gadgets/bit-slices.ts | 104 +++++++++++++++++++++++++++++- src/lib/gadgets/elliptic-curve.ts | 101 +---------------------------- 2 files changed, 106 insertions(+), 99 deletions(-) diff --git a/src/lib/gadgets/bit-slices.ts b/src/lib/gadgets/bit-slices.ts index b6b3d7acfe..0672e1aeb5 100644 --- a/src/lib/gadgets/bit-slices.ts +++ b/src/lib/gadgets/bit-slices.ts @@ -1,12 +1,17 @@ /** * Gadgets for converting between field elements and bit slices of various lengths */ +import { bigIntToBits } from '../../bindings/crypto/bigint-helpers.js'; +import { Bool } from '../bool.js'; import { Field } from '../field.js'; import { UInt8 } from '../int.js'; import { Provable } from '../provable.js'; import { chunk } from '../util/arrays.js'; +import { assert, exists } from './common.js'; +import type { Field3 } from './foreign-field.js'; +import { l } from './range-check.js'; -export { bytesToWord, wordToBytes, wordsToBytes, bytesToWords }; +export { bytesToWord, wordToBytes, wordsToBytes, bytesToWords, sliceField3 }; // conversion between bytes and multi-byte words @@ -52,3 +57,100 @@ function wordsToBytes(words: Field[], bytesPerWord = 8): UInt8[] { function bytesToWords(bytes: UInt8[], bytesPerWord = 8): Field[] { return chunk(bytes, bytesPerWord).map(bytesToWord); } + +// conversion between 3-limb foreign fields and arbitrary bit slices + +/** + * Provable method for slicing a 3x88-bit bigint into smaller bit chunks of length `chunkSize` + * + * This serves as a range check that the input is in [0, 2^maxBits) + */ +function sliceField3( + [x0, x1, x2]: Field3, + { maxBits, chunkSize }: { maxBits: number; chunkSize: number } +) { + let l_ = Number(l); + assert(maxBits <= 3 * l_, `expected max bits <= 3*${l_}, got ${maxBits}`); + + // first limb + let result0 = sliceField(x0, Math.min(l_, maxBits), chunkSize); + if (maxBits <= l_) return result0.chunks; + maxBits -= l_; + + // second limb + let result1 = sliceField(x1, Math.min(l_, maxBits), chunkSize, result0); + if (maxBits <= l_) return result0.chunks.concat(result1.chunks); + maxBits -= l_; + + // third limb + let result2 = sliceField(x2, maxBits, chunkSize, result1); + return result0.chunks.concat(result1.chunks, result2.chunks); +} + +/** + * Provable method for slicing a field element into smaller bit chunks of length `chunkSize`. + * + * This serves as a range check that the input is in [0, 2^maxBits) + * + * If `chunkSize` does not divide `maxBits`, the last chunk will be smaller. + * We return the number of free bits in the last chunk, and optionally accept such a result from a previous call, + * so that this function can be used to slice up a bigint of multiple limbs into homogeneous chunks. + * + * TODO: atm this uses expensive boolean checks for each bit. + * For larger chunks, we should use more efficient range checks. + */ +function sliceField( + x: Field, + maxBits: number, + chunkSize: number, + leftover?: { chunks: Field[]; leftoverSize: number } +) { + let bits = exists(maxBits, () => { + let bits = bigIntToBits(x.toBigInt()); + // normalize length + if (bits.length > maxBits) bits = bits.slice(0, maxBits); + if (bits.length < maxBits) + bits = bits.concat(Array(maxBits - bits.length).fill(false)); + return bits.map(BigInt); + }); + + let chunks = []; + let sum = Field.from(0n); + + // if there's a leftover chunk from a previous sliceField() call, we complete it + if (leftover !== undefined) { + let { chunks: previous, leftoverSize: size } = leftover; + let remainingChunk = Field.from(0n); + for (let i = 0; i < size; i++) { + let bit = bits[i]; + Bool.check(Bool.Unsafe.ofField(bit)); + remainingChunk = remainingChunk.add(bit.mul(1n << BigInt(i))); + } + sum = remainingChunk = remainingChunk.seal(); + let chunk = previous[previous.length - 1]; + previous[previous.length - 1] = chunk.add( + remainingChunk.mul(1n << BigInt(chunkSize - size)) + ); + } + + let i = leftover?.leftoverSize ?? 0; + for (; i < maxBits; i += chunkSize) { + // prove that chunk has `chunkSize` bits + // TODO: this inner sum should be replaced with a more efficient range check when possible + let chunk = Field.from(0n); + let size = Math.min(maxBits - i, chunkSize); // last chunk might be smaller + for (let j = 0; j < size; j++) { + let bit = bits[i + j]; + Bool.check(Bool.Unsafe.ofField(bit)); + chunk = chunk.add(bit.mul(1n << BigInt(j))); + } + chunk = chunk.seal(); + // prove that chunks add up to x + sum = sum.add(chunk.mul(1n << BigInt(i))); + chunks.push(chunk); + } + sum.assertEquals(x); + + let leftoverSize = i - maxBits; + return { chunks, leftoverSize } as const; +} diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts index d2ee6eefc8..78ca4dbdff 100644 --- a/src/lib/gadgets/elliptic-curve.ts +++ b/src/lib/gadgets/elliptic-curve.ts @@ -3,10 +3,9 @@ import { Field } from '../field.js'; import { Provable } from '../provable.js'; import { assert, exists } from './common.js'; import { Field3, ForeignField, split, weakBound } from './foreign-field.js'; -import { l, l2, multiRangeCheck } from './range-check.js'; +import { l2, multiRangeCheck } from './range-check.js'; import { sha256 } from 'js-sha256'; import { - bigIntToBits, bigIntToBytes, bytesToBigInt, } from '../../bindings/crypto/bigint-helpers.js'; @@ -19,6 +18,7 @@ import { Bool } from '../bool.js'; import { provable } from '../circuit_value.js'; import { assertPositiveInteger } from '../../bindings/crypto/non-negative.js'; import { arrayGet, assertBoolean } from './basic.js'; +import { sliceField3 } from './bit-slices.js'; // external API export { EllipticCurve, Point, Ecdsa }; @@ -410,7 +410,7 @@ function multiScalarMul( // slice scalars let scalarChunks = scalars.map((s, i) => - slice(s, { maxBits, chunkSize: windowSizes[i] }) + sliceField3(s, { maxBits, chunkSize: windowSizes[i] }) ); ia ??= initialAggregator(Curve); @@ -625,101 +625,6 @@ function simpleMapToCurve(x: bigint, Curve: CurveAffine) { return p; } -/** - * Provable method for slicing a 3x88-bit bigint into smaller bit chunks of length `chunkSize` - * - * This serves as a range check that the input is in [0, 2^maxBits) - */ -function slice( - [x0, x1, x2]: Field3, - { maxBits, chunkSize }: { maxBits: number; chunkSize: number } -) { - let l_ = Number(l); - assert(maxBits <= 3 * l_, `expected max bits <= 3*${l_}, got ${maxBits}`); - - // first limb - let result0 = sliceField(x0, Math.min(l_, maxBits), chunkSize); - if (maxBits <= l_) return result0.chunks; - maxBits -= l_; - - // second limb - let result1 = sliceField(x1, Math.min(l_, maxBits), chunkSize, result0); - if (maxBits <= l_) return result0.chunks.concat(result1.chunks); - maxBits -= l_; - - // third limb - let result2 = sliceField(x2, maxBits, chunkSize, result1); - return result0.chunks.concat(result1.chunks, result2.chunks); -} - -/** - * Provable method for slicing a field element into smaller bit chunks of length `chunkSize`. - * - * This serves as a range check that the input is in [0, 2^maxBits) - * - * If `chunkSize` does not divide `maxBits`, the last chunk will be smaller. - * We return the number of free bits in the last chunk, and optionally accept such a result from a previous call, - * so that this function can be used to slice up a bigint of multiple limbs into homogeneous chunks. - * - * TODO: atm this uses expensive boolean checks for each bit. - * For larger chunks, we should use more efficient range checks. - */ -function sliceField( - x: Field, - maxBits: number, - chunkSize: number, - leftover?: { chunks: Field[]; leftoverSize: number } -) { - let bits = exists(maxBits, () => { - let bits = bigIntToBits(x.toBigInt()); - // normalize length - if (bits.length > maxBits) bits = bits.slice(0, maxBits); - if (bits.length < maxBits) - bits = bits.concat(Array(maxBits - bits.length).fill(false)); - return bits.map(BigInt); - }); - - let chunks = []; - let sum = Field.from(0n); - - // if there's a leftover chunk from a previous sliceField() call, we complete it - if (leftover !== undefined) { - let { chunks: previous, leftoverSize: size } = leftover; - let remainingChunk = Field.from(0n); - for (let i = 0; i < size; i++) { - let bit = bits[i]; - Bool.check(Bool.Unsafe.ofField(bit)); - remainingChunk = remainingChunk.add(bit.mul(1n << BigInt(i))); - } - sum = remainingChunk = remainingChunk.seal(); - let chunk = previous[previous.length - 1]; - previous[previous.length - 1] = chunk.add( - remainingChunk.mul(1n << BigInt(chunkSize - size)) - ); - } - - let i = leftover?.leftoverSize ?? 0; - for (; i < maxBits; i += chunkSize) { - // prove that chunk has `chunkSize` bits - // TODO: this inner sum should be replaced with a more efficient range check when possible - let chunk = Field.from(0n); - let size = Math.min(maxBits - i, chunkSize); // last chunk might be smaller - for (let j = 0; j < size; j++) { - let bit = bits[i + j]; - Bool.check(Bool.Unsafe.ofField(bit)); - chunk = chunk.add(bit.mul(1n << BigInt(j))); - } - chunk = chunk.seal(); - // prove that chunks add up to x - sum = sum.add(chunk.mul(1n << BigInt(i))); - chunks.push(chunk); - } - sum.assertEquals(x); - - let leftoverSize = i - maxBits; - return { chunks, leftoverSize } as const; -} - /** * Get value from array in O(n) constraints. * From c202d9d5eeb2c58a9711ed51c601982553b9661f Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 8 Jan 2024 12:53:13 +0100 Subject: [PATCH 087/524] remove cyclic top-level gadgets dep from sha256.ts --- src/lib/gadgets/sha256.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 8cac25b58c..aa98f6e46e 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -6,9 +6,9 @@ import { FlexibleBytes } from '../provable-types/bytes.js'; import { Bytes } from '../provable-types/provable-types.js'; import { chunk } from '../util/arrays.js'; import { TupleN } from '../util/types.js'; +import { divMod32 } from './arithmetic.js'; import { bytesToWord, wordToBytes } from './bit-slices.js'; import { bitSlice, exists } from './common.js'; -import { Gadgets } from './gadgets.js'; import { rangeCheck16 } from './range-check.js'; export { SHA256 }; @@ -83,7 +83,7 @@ function decomposeToBytes(a: UInt32) { let ys = []; for (let i = 0; i < 4; i++) { // for each byte we rotate the element and get the excess bits (8 at a time) and construct a UInt8 of it - let { quotient, remainder } = Gadgets.divMod32(field.mul(1n << 8n)); + let { quotient, remainder } = divMod32(field.mul(1n << 8n)); // "shift" the element by 8 bit to get the next byte sequence during the next iteration field = remainder; ys.push(quotient); @@ -118,7 +118,7 @@ const SHA256 = { .add(DeltaZero(W[t - 15]).value.add(W[t - 16].value)); // mod 32bit the unreduced field element - W[t] = UInt32.from(Gadgets.divMod32(unreduced, 16).remainder); + W[t] = UInt32.from(divMod32(unreduced, 16).remainder); } // initialize working variables @@ -146,15 +146,11 @@ const SHA256 = { h = g; g = f; f = e; - e = UInt32.from( - Gadgets.divMod32(d.value.add(unreducedT1), 16).remainder - ); // mod 32bit the unreduced field element + e = UInt32.from(divMod32(d.value.add(unreducedT1), 16).remainder); // mod 32bit the unreduced field element d = c; c = b; b = a; - a = UInt32.from( - Gadgets.divMod32(unreducedT2.add(unreducedT1), 16).remainder - ); // mod 32bit + a = UInt32.from(divMod32(unreducedT2.add(unreducedT1), 16).remainder); // mod 32bit } // new intermediate hash value From 1f7ebb29f6c760aefc4441ca2f92102385bcc820 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 8 Jan 2024 12:53:32 +0100 Subject: [PATCH 088/524] remove unused function --- src/lib/gadgets/sha256.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index aa98f6e46e..6d55273347 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -77,22 +77,6 @@ function padding(data: FlexibleBytes): UInt32[][] { return chunk(chunks, 16); } -// decompose a 32bit word into 4 bytes -function decomposeToBytes(a: UInt32) { - let field = a.value; - let ys = []; - for (let i = 0; i < 4; i++) { - // for each byte we rotate the element and get the excess bits (8 at a time) and construct a UInt8 of it - let { quotient, remainder } = divMod32(field.mul(1n << 8n)); - // "shift" the element by 8 bit to get the next byte sequence during the next iteration - field = remainder; - ys.push(quotient); - } - - // UInt8.from does a rangeCheck8 for Field elements - return ys.map(UInt8.from); -} - const SHA256 = { hash(data: FlexibleBytes) { // preprocessing §6.2 From f077283029744f63594715184a4d951bb350e609 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 8 Jan 2024 13:02:13 +0100 Subject: [PATCH 089/524] dump vks --- tests/vk-regression/vk-regression.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index ec705078d8..7a84e7fc0f 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -266,16 +266,16 @@ } }, "sha256": { - "digest": "15bce9c541c0619f3b3587d7c06918e1423c5d1d4efe6ba6e0c789987e41b512", + "digest": "2253f1a883d78886858530b473943e685f302d489a509a9af105ccf3621584bf", "methods": { "sha256": { - "rows": 5314, - "digest": "bfad0408f0baab2e3db346a3e9997455" + "rows": 5125, + "digest": "5a7c5b22395d328f5ca9e3242669b478" } }, "verificationKey": { - "data": "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", - "hash": "801809578510365639167868048464129098535493687043603299559390072759232736708" + "data": "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", + "hash": "4802323592772073249618921425353059023939321317450660242786504849922261724135" } } } \ No newline at end of file From 6b8d48a7af93fc74c2bea224fb73cc1002e9c885 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 8 Jan 2024 13:06:03 +0100 Subject: [PATCH 090/524] save a few constraints with seal() --- src/lib/gadgets/sha256.ts | 3 ++- tests/vk-regression/vk-regression.json | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 6d55273347..9cca97c7b4 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -122,7 +122,8 @@ const SHA256 = { .add(SigmaOne(e).value) .add(Ch(e, f, g).value) .add(K[t].value) - .add(W[t].value); + .add(W[t].value) + .seal(); // T2 is also unreduced const unreducedT2 = SigmaZero(a).value.add(Maj(a, b, c).value); diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 7a84e7fc0f..1c1dca92f3 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -266,16 +266,16 @@ } }, "sha256": { - "digest": "2253f1a883d78886858530b473943e685f302d489a509a9af105ccf3621584bf", + "digest": "38eaa6264b1e4c1c0b5787c06efd92aa7855890aeeafed91818d466f1b109ca5", "methods": { "sha256": { - "rows": 5125, - "digest": "5a7c5b22395d328f5ca9e3242669b478" + "rows": 5036, + "digest": "c3952f2bd0dbb6b16ed43add7c57c9c5" } }, "verificationKey": { - "data": "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", - "hash": "4802323592772073249618921425353059023939321317450660242786504849922261724135" + "data": "AACa0OPxg0UDEf5DBLZf/TtdB4TzIAMNQC467+/R1yGnL1tRXJnn0BDcLG0fGWdqFcWK1q2zKdAYfORKVOHgvKEwxtxZYv4CjM7SwlG09G52oNZmOgGCilD0ntmby4rzJTaoyCimx5ynQ/oYjslJgSM/1DTAkpW6IIdFjjkmjlOSHqRNRAMtNtl44D9lbhci4blHbDvzQnlYynwRh58jAzoDj3bCkPsByviAyFBoPhUx2M13h0/VPK1ND/69djzZgi9lQKaON74XvbnvJdSAYLQSIBbOE0yo7tS1vGTPqDqJGzc1f0+2QhZttE2UEUV4PPEGB6LUFEuQPXK8lXyXHVQTOU+omjpvKHLLs/dQZLTSvvZ3UFqxUvxjSEG9eTPo3Cyt4wXcPEi1b993ePSSxP6njj1SoPBA4BvLCCcvaxz5DegUu7nZDKkC8tuaoNbqYcayaf/8+oZ+dA+Ek1Xe08og1Hz9gB9cfPvgI9EiL2Na2cgiOF2klHHEAj3fORN8UQVzs0ioqLcQeq2tH2UMMtZS4JNcohHUah1T0jKmS3eZDqe+aMuf5qzKA5oKRF2ejRwmsxI161YlgXXpmHs+kCQGAPvX5+/WvgVyy1uIX6426oQoB+0Ni/lWEhFNY+MvNVUGIgPAyvpEafTl8DMfPQkUf0yQCPxRg7d/lRCRfYqHRT74U20AHCGYSahuSFbBO6dDhRCPGRCJJgxktY+CSO+3B9mLtCe2X23FeFsUnaAFlzmsWeKR1tMyPupNsUXPFugubYZYd1EwxUDeBIOLeahcqX78yDDMOdsr0QHNG8XteyXcRXGFUSzTTKNEXdD+BOMY6FfMroKO4y3FffyrZef1PRsu/kVwaZb4UthvhBwet8DBcMa26kISlsI1ItcvsdwST5x1+iLCM4hBcFB0ly3N18uR7+MLHU0AdV13INFo8indyz1XEZCHlm+tKF3LX8Z7G0v68uRmjKRgy/S9NjKPA+M3rbj4pDU+jS4EKN++nKYxXjwdKJXu4XvbUU9ITKM7hwGRsMOqcMyUTbyXukWv+k1tSf9MbOH0BCVgQBzj2SiljlEYePMzBGm1WXhnj34Y+c3l038Kgr/LhS0/P33yPk0OzQ8/vAiYAdVmcHuS+M1etSdnWerxU4E3vC2odvcjNq2yh/VyODIwPt/UPYT1soPpv6M3XSyvpE1kVb0TmD91v6mXvfq23wSDn7ndgLx52m+CGnEGzA/OwwVQnZaFNq+sZjKjOa8ALFNcjyS8IuYEz4XYMiSy/wCl2n2AHVIc6djnQZySvECly6HHJSpWpWv8zvNfLUiWozg4ActV4QzQsd2nagCedaw1z82uWcKLraPboPGke+1dGOqg8OFiBJUBoW/bROBgw2H8WnPIhcofMwGOCrXpMGvA4LKnn3okzY3hTNfex1t7zCpxQC2YRBN0Ze8qOELHTYvOlSwG1AQqZDhC//VDGFvvQ1ZgzsmpnbyNGTGMaCKRMGI0evgxD6Ziitu5k9ogVW6dGSR9cBM0NryOxQl6JcwUXd0twqsLI0pbH7cjlGzWsylGuufx/m77GPRqnysk6r+ibZ4fDBFDumJFxxbS4AqzXLR+nNg42hJbvuxsyZnCRpkB2N9xH3VX8/Il8BX40HdEE/08l3JmL3jn8Bznqwj8z3TqBFMdRGOOq2/5DjwcjaNh9pYRn6bDl/1qVPrJkUExPECrQRymHQ3JERZjm1G+a3bhAJFeb+Ak4D8PlK2RgPcMCS7bBXWuPRh0z4FKGnhZnecNmuWMSvUFsSZcoaFKfyNanX8qMsu+KWtWEbDwwQ49NrfCmg45/WAOQxX8LKMYgUrDpSVdE/bM+JqYpq0AmOHAhoIdlOC7jVMIPI6LEAVJC1PrFQBS3HbH+u5IMQ684sJehPFtd1pjpfboDrnbgfhnjFf9HqS5bG1sR1Dh2mXGXpQ+ni+O3FvEYCEZY+UU9d9XCGwL2OZIhtMi6M6qcnrn11w2MyaZ8U6aZxVTMFvKQ2JLtwVGVnMDuSkNC+iN711acwssgbTpsgHLdVbtFR1oYcbpJHe6a9SFquG+q9qfzT15IzKpBzxn6BVXfhhFGpJRAbU0bXbjOpeceg7vaV7RykTzBoIzAe5aUVAdKNM6fzGlPw16xx7QeOW+LFlOm6HJyxYAZfbpB/BLza4ZhoqmVx+ALUXHFIztgGQK9rzm+jBwiuwuLqdD2W00cbTZcbgKTo48XD6NJ+8T4J9B3rPzht3qbgpN//TyYkfrzAercAa/HCvFeBNXl1slCj8cF/EO6iX/NnIxBkuqmXfQnGUfcFK0LZPsvd7RInaLEYTeA4ZDfChiuw+5nTmrJFOywwOYdIA+NiMfCh24dPYAAwEGb9KLEP9u7/Rp5uPi0S3tuTw67yg=", + "hash": "22296391645667701199385692837408020819294441951376164803693884547686842878882" } } } \ No newline at end of file From d71872dee0e387a9e7c886d6ab59d1d4f78ed878 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 8 Jan 2024 13:08:13 +0100 Subject: [PATCH 091/524] add comments --- src/lib/gadgets/sha256.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 9cca97c7b4..73cec23804 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -272,5 +272,7 @@ function sigma(u: UInt32, bits: TupleN, firstShifted = false) { let xRotR2 = x3.add(x012.mul(1 << d3)).seal(); // ^ proves that 2^(32-d2)*x2 < xRotR2 => x2 < 2^d2 if we check xRotR2 < 2^32 later + // since xor() is implicitly range-checking both of its inputs, this provides the missing + // proof that xRotR0, xRotR1, xRotR2 < 2^32, which implies x0 < 2^d0, x1 < 2^d1, x2 < 2^d2 return UInt32.from(xRotR0).xor(new UInt32(xRotR1)).xor(new UInt32(xRotR2)); } From 4414a59569e1fd233ed0345308495ca8cdc192a5 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 28 Sep 2023 13:48:08 -0300 Subject: [PATCH 092/524] feat: add ocaml build directory to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index fcb737b159..9246171b93 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ tests/test-artifacts/ profiling.md snarkyjs-reference *.tmp.js +_build/ +src/config/ +src/config.mlh From 3b98082c04b54b20a48a6a8f2134bdf103592df9 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 28 Sep 2023 13:48:36 -0300 Subject: [PATCH 093/524] feat: add dune-project file to build bindings from o1js root directory --- dune-project | 1 + 1 file changed, 1 insertion(+) create mode 100644 dune-project diff --git a/dune-project b/dune-project new file mode 100644 index 0000000000..7b17fb2d30 --- /dev/null +++ b/dune-project @@ -0,0 +1 @@ +(lang dune 3.3) From d20f6f6a84a09324e2d87304ed7a39af885a9c93 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Fri, 27 Oct 2023 12:36:26 -0700 Subject: [PATCH 094/524] fix(package.json): update 'make' script to use local script Co-authored-by: Gregor Mitscha-Baude --- README-dev.md | 8 +++----- package.json | 11 ++++------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/README-dev.md b/README-dev.md index 42a6771b5e..2925218bc5 100644 --- a/README-dev.md +++ b/README-dev.md @@ -14,9 +14,7 @@ npm run build ## Build and run the web version ```sh -npm install -npm run build:web -npm run serve:web +npm run build:bindings ``` To see the test running in a web browser, go to `http://localhost:8000/`. @@ -50,9 +48,9 @@ To see the test running in a web browser, go to `http://localhost:8000/`. ## Branch Compatibility -SnarkyJS is mostly used to write Mina Smart Contracts and must be compatible with the latest Berkeley Testnet (or soon Mainnet). +SnarkyJS is mostly used to write Mina Smart Contracts and must be compatible with the latest Berkeley Testnet (or soon Mainnet). -The OCaml code is in the snarkyjs-bindings repository, not directly in SnarkyJS. +The OCaml code is in the snarkyjs-bindings repository, not directly in SnarkyJS. To maintain compatibility between the repositories and build SnarkyJS from the [Mina repository](https://github.com/MinaProtocol/mina), make changes to its core, such as the OCaml-bindings in the [snarkyjs-bindings repository](https://github.com/o1-labs/snarkyjs-bindings), you must follow a certain branch compatibility pattern: diff --git a/package.json b/package.json index a489e8e919..5d5c9d157d 100644 --- a/package.json +++ b/package.json @@ -43,14 +43,11 @@ "node": ">=16.4.0" }, "scripts": { - "type-check": "tsc --noEmit", - "dev": "npx tsc -p tsconfig.node.json && node src/build/copy-to-dist.js", - "make": "make -C ../../.. snarkyjs", - "make:no-types": "npm run clean && make -C ../../.. snarkyjs_no_types", - "bindings": "cd ../../.. && ./scripts/update-snarkyjs-bindings.sh && cd src/lib/snarkyjs", + "dev": "npx tsc -p tsconfig.test.json && node src/build/copy-to-dist.js", "build": "node src/build/copy-artifacts.js && rimraf ./dist/node && npm run dev && node src/build/buildNode.js", - "build:test": "npx tsc -p tsconfig.test.json && cp src/snarky.d.ts dist/node/snarky.d.ts", - "build:node": "npm run build", + "build:bindings": "./src/bindings/scripts/build-snarkyjs-node.sh", + "build:update-bindings": "./src/bindings/scripts/update-snarkyjs-bindings.sh", + "build:wasm": "./src/bindings/scripts/update-wasm-and-types.sh", "build:web": "rimraf ./dist/web && node src/build/buildWeb.js", "build:examples": "rimraf ./dist/examples && npx tsc -p tsconfig.examples.json || exit 0", "build:docs": "npx typedoc", From 63a252895c76e8db4074506b2253022a70caaa4b Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Fri, 27 Oct 2023 12:36:57 -0700 Subject: [PATCH 095/524] fix(tsconfig.json): add './src/mina' to exclude list to prevent unnecessary compilation of mina directory --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 268b0f8b3a..367f1cc41d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "include": ["./src/**/*.ts"], - "exclude": ["./src/**/*.bc.js", "./src/build", "./src/examples"], + "exclude": ["./src/**/*.bc.js", "./src/build", "./src/examples", "./src/mina"], "compilerOptions": { "rootDir": "./src", "outDir": "dist", From 2835ff74022c97bf0a514f7e815eedfbd97e3dcb Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Fri, 27 Oct 2023 16:13:16 -0700 Subject: [PATCH 096/524] fix(.gitmodules): change MinaProtocol submodule URL from SSH to HTTPS --- .gitmodules | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitmodules b/.gitmodules index b343059624..76a36100bc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "src/snarkyjs-bindings"] path = src/bindings url = https://github.com/o1-labs/snarkyjs-bindings.git +[submodule "src/mina"] + path = src/mina + url = https://github.com/MinaProtocol/mina.git From 854f9fd7c45014946c6b0e34045db42c6474eda9 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Fri, 27 Oct 2023 16:47:05 -0700 Subject: [PATCH 097/524] feat(jest.config.js): add 'src/mina/' to modulePathIgnorePatterns to prevent Jest from running tests in this directory --- jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/jest.config.js b/jest.config.js index b24c895d96..7f6d944074 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,6 +3,7 @@ export default { testEnvironment: 'node', extensionsToTreatAsEsm: ['.ts'], transformIgnorePatterns: ['node_modules/', 'dist/node/'], + modulePathIgnorePatterns: ['src/mina/'], globals: { 'ts-jest': { useESM: true, From 087659544b7461df3ace6391e827c365e1e6d4b8 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Sun, 29 Oct 2023 20:22:32 -0700 Subject: [PATCH 098/524] docs(README-dev.md): update build instructions and add details about build scripts - Replace opam with Dune in the list of required tools to reflect changes in the build process - Add a new section about build scripts, explaining the role of update-snarkyjs-bindings.sh - Expand on the OCaml bindings section, detailing the use of Dune and Js_of_ocaml in the build process - Add information about the WebAssembly bindings build process, including the output files - Introduce a section about generated constant types, explaining how they are created and their role in ensuring protocol consistency Co-authored-by: Gregor Mitscha-Baude --- README-dev.md | 195 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 159 insertions(+), 36 deletions(-) diff --git a/README-dev.md b/README-dev.md index 2925218bc5..c81b561d08 100644 --- a/README-dev.md +++ b/README-dev.md @@ -1,63 +1,186 @@ -# How to contribute to the SnarkyJS codebase +# o1js README-dev -This README includes information that is helpful for SnarkyJS core contributors. +o1js is a TypeScript framework designed for zk-SNARKs and zkApps on the Mina blockchain. -## Run examples using Node.js +- [zkApps Overview](https://docs.minaprotocol.com/zkapps) +- [Mina README](/src/mina/README.md) + +For more information on our development process and how to contribute, see [CONTRIBUTING.md](https://github.com/o1-labs/o1js/blob/main/CONTRIBUTING.md). This document is meant to guide you through building o1js from source and understanding the development workflow. + +## Prerequisites + +Before starting, ensure you have the following tools installed: + +- [Git](https://git-scm.com/) +- [Node.js and npm](https://nodejs.org/) +- [Dune](https://github.com/ocaml/dune) (only needed when compiling o1js from source) +- [Cargo](https://www.rust-lang.org/learn/get-started) (only needed when compiling o1js from source) + +After cloning the repository, you need to fetch the submodules: + +```sh +git submodule update --init --recursive +``` + +## Building o1js + +For most users, building o1js is as simple as running: ```sh npm install npm run build +``` + +This will compile the TypeScript source files, making it ready for use. The compiled OCaml and WebAssembly artifacts are version-controlled to simplify the build process for end-users. These artifacts are stored under `src/bindings/compiled`, and contain the artifacts needed for both node and web builds. These files do not have to be regenerated unless there are changes to the OCaml or Rust source files. + +## Building Bindings + +If you need to regenerate the OCaml and WebAssembly artifacts, you can do so within the o1js repo. The [bindings](https://github.com/o1-labs/o1js-bindings) and [Mina](https://github.com/MinaProtocol/mina) repos are both submodules of o1js, so you can build them from within the o1js repo. + +o1js depends on OCaml code that is transpiled to JavaScript using [Js_of_ocaml](https://github.com/ocsigen/js_of_ocaml), and Rust code that is transpiled to WebAssembly using [wasm-pack](https://github.com/rustwasm/wasm-pack). These artifacts allow o1js to call into [Pickles](https://github.com/MinaProtocol/mina/blob/develop/src/lib/pickles/README.md), [snarky](https://github.com/o1-labs/snarky), and [Kimchi](https://github.com/o1-labs/proof-systems) to write zk-SNARKs and zkApps. -./run src/examples/api_exploration.ts +The compiled artifacts are stored under `src/bindings/compiled`, and are version-controlled to simplify the build process for end-users. + +If you wish to rebuild the OCaml and Rust artifacts, you must be able to build the Mina repo before building the bindings. See the [Mina Dev Readme](https://github.com/MinaProtocol/mina/blob/develop/README-dev.md) for more information. Once you have configured your environment to build Mina, you can build the bindings: + +```sh +npm run build:update-bindings ``` -## Build and run the web version +This will build the OCaml and Rust artifacts, and copy them to the `src/bindings/compiled` directory. + +### Build Scripts + +The root build script which kicks off the build process is under `src/bindings/scripts/update-snarkyjs-bindings.sh`. This script is responsible for building the Node.js and web artifacts for o1js, and places them under `src/bindings/compiled`, to be used by o1js. + +### OCaml Bindings + +o1js depends on Pickles, snarky, and parts of the Mina transaction logic, all of which are compiled to JavaScript and stored as artifacts to be used by o1js natively. The OCaml bindings are located under `src/bindings`. See the [OCaml Bindings README](https://github.com/o1-labs/o1js-bindings/blob/main/README.md) for more information. + +To compile the OCaml code, a build tool called Dune is used. Dune is a build system for OCaml projects, and is used in addition with Js_of_ocaml to compile the OCaml code to JavaScript. The dune file that is responsible for compiling the OCaml code is located under `src/bindings/ocaml/dune`. There are two build targets: `snarky_js_node` and `snarky_js_web`, which compile the Mina dependencies as well as link the wasm artifacts to build the Node.js and web artifacts, respectively. The output file is `snark_js_node.bc.js`, which is used by o1js. + +### WebAssembly Bindings + +o1js additionally depends on Kimchi, which is compiled to WebAssembly. Kimchi is located in the Mina repo, under `src/mina`. See the [Kimchi README](https://github.com/o1-labs/proof-systems/blob/master/README.md) for more information. + +To compile the wasm code, a combination of Cargo and Dune is used. Both build files are located under `src/mina/src/lib/crypto/kimchi`, where the `wasm` folder contains the Rust code which is compiled to wasm, and the `js` folder which contains a wrapper around the wasm code which allows Js_of_ocaml to compile against the wasm backend. + +For the wasm build, the output files are: + +- `plonk_wasm_bg.wasm`: The compiled WebAssembly binary. +- `plonk_wasm_bg.wasm.d.ts`: TypeScript definition files describing the types of .wasm or .js files. +- `plonk_wasm.js`: JavaScript file that wraps the WASM code for use in Node.js. +- `plonk_wasm.d.ts`: TypeScript definition file for plonk_wasm.js. + +### Generated Constant Types + +In addition to building the OCaml and Rust code, the build script also generates TypeScript types for constants used in the Mina protocol. These types are generated from the OCaml source files, and are located under `src/bindings/crypto/constants.ts` and `src/bindings/mina-transaction/gen`. When building the bindings, these constants are auto-generated by Dune. If you wish to add a new constant, you can edit the `src/bindings/ocaml/snarky_js_constants` file, and then run `npm run build:bindings` to regenerate the TypeScript files. + +These types are used by o1js to ensure that the constants used in the protocol are consistent with the OCaml source files. + +## Development + +### Branch Compatibility + +If you work on o1js, create a feature branch off of one of these base branches. It's encouraged to submit your work-in-progress as a draft PR to raise visibility! When working with submodules and various interconnected parts of the stack, ensure you are on the correct branches that are compatible with each other. + +#### How to Use the Branches + +**Default to `main` as the base branch**. + +The other base branches (`berkeley`, `develop`) are only used in specific scenarios where you want to adapt o1js to changes in the sibling repos on those other branches. Even then, consider whether it is feasible to land your changes to `main` and merge to `berkeley` and `develop` afterwards. Only changes in `main` will ever be released, so anything in the other branches has to be backported and reconciled with `main` eventually. + +| Repository | mina -> o1js -> o1js-bindings | +| ---------- | -------------------------------- | +| Branches | o1js-main -> main -> main | +| | berkeley -> berkeley -> berkeley | +| | develop -> develop -> develop | + +- `o1js-main`: The o1js-main branch in the Mina repository corresponds to the main branch in both o1js and o1js-bindings repositories. This is where stable releases and ramp-up features are maintained. The o1js-main branch runs in parallel to the Mina `berkeley` branch and does not have a subset or superset relationship with it. The branching structure is as follows (<- means direction to merge): + + - `develop` <- `o1js-main` <- `current testnet` - Typically, the current testnet often corresponds to the rampup branch. + +- `berkeley`: The berkeley branch is maintained across all three repositories. This branch is used for features and updates specific to the Berkeley release of the project. + +- `develop`: The develop branch is also maintained across all three repositories. It is used for ongoing development, testing new features, and integration work. + +### Running Tests + +To ensure your changes don't break existing functionality, run the test suite: + +```sh +npm run test +npm run test:unit +``` + +This will run all the unit tests and provide you with a summary of the test results. + +You can additionally run integration tests by running: + +```sh +npm run test:integration +``` + +Finally, we have a set of end-to-end tests that run against the browser. These tests are not run by default, but you can run them by running: ```sh -npm run build:bindings +npm install +npm run e2e:install +npm run build:web + +npm run e2e:prepare-server +npm run test:e2e +npm run e2e:show-report ``` -To see the test running in a web browser, go to `http://localhost:8000/`. +### Run the GitHub actions locally -## Run tests + -- Unit tests +You can execute the CI locally by using [act](https://github.com/nektos/act). First generate a GitHub token and use: - ```sh - npm run test - npm run test:unit - ``` +```sh +act -j Build-And-Test-Server --matrix test_type:"Simple integration tests" -s $GITHUB_TOKEN +``` -- Integration tests +### Releasing - ```sh - npm run test:integration - ``` +To release a new version of o1js, you must first update the version number in `package.json`. Then, you can create a new pull request to merge your changes into the main branch. Once the pull request is merged, a CI job will automatically publish the new version to npm. -- E2E tests +## Test zkApps against the local blockchain network - ```sh - npm install - npm run e2e:install - npm run build:web +In order to be able to test zkApps against the local blockchain network, you need to spin up such a network first. +You can do so in several ways. - npm run e2e:prepare-server - npm run test:e2e - npm run e2e:show-report - ``` +1. Using [zkapp-cli](https://www.npmjs.com/package/zkapp-cli)'s sub commands: -## Branch Compatibility + ```shell + zk lightnet start # start the local network + # Do your tests and other interactions with the network + zk lightnet logs # manage the logs of the local network + zk lightnet explorer # visualize the local network state + zk lightnet stop # stop the local network + ``` -SnarkyJS is mostly used to write Mina Smart Contracts and must be compatible with the latest Berkeley Testnet (or soon Mainnet). + Please refer to `zk lightnet --help` for more information. -The OCaml code is in the snarkyjs-bindings repository, not directly in SnarkyJS. +2. Using the corresponding [Docker image](https://hub.docker.com/r/o1labs/mina-local-network) manually: -To maintain compatibility between the repositories and build SnarkyJS from the [Mina repository](https://github.com/MinaProtocol/mina), make changes to its core, such as the OCaml-bindings in the [snarkyjs-bindings repository](https://github.com/o1-labs/snarkyjs-bindings), you must follow a certain branch compatibility pattern: + ```shell + docker run --rm --pull=missing -it \ + --env NETWORK_TYPE="single-node" \ + --env PROOF_LEVEL="none" \ + --env LOG_LEVEL="Trace" \ + -p 3085:3085 \ + -p 5432:5432 \ + -p 8080:8080 \ + -p 8181:8181 \ + -p 8282:8282 \ + o1labs/mina-local-network:o1js-main-latest-lightnet + ``` -The following branches are compatible: + Please refer to the [Docker Hub repository](https://hub.docker.com/r/o1labs/mina-local-network) for more information. -| repository | mina -> snarkyjs -> snarkyjs-bindings | -| ---------- | ------------------------------------- | -| branches | rampup -> main -> main | -| | berkeley -> berkeley -> berkeley | -| | develop -> develop -> develop | +Next up, you will need the Mina blockchain accounts information in order to be used in your zkApp. +Once the local network is up and running, you can use the [Lightnet](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/lib/fetch.ts#L1012) `o1js API namespace` to get the accounts information. +The corresponding example can be found here: [src/examples/zkapps/hello_world/run_live.ts](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/examples/zkapps/hello_world/run_live.ts) From 6aa979923760678437730452933de67db9f97db9 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 14 Nov 2023 13:58:50 -0800 Subject: [PATCH 099/524] fix(.prettierignore): update path for kimchi js files to reflect new directory structure, ensuring correct files are ignored by Prettier --- .prettierignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.prettierignore b/.prettierignore index 099c3fecca..fe67df0c2c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,4 @@ src/bindings/compiled/web_bindings/**/plonk_wasm* src/bindings/compiled/web_bindings/**/*.bc.js src/bindings/compiled/node_bindings/* dist/**/* -src/bindings/kimchi/js/**/*.js +src/mina/src/lib/crypto/kimchi/js/**/*.js From b913ba9197481fa424d5f320caf887c1f512d2c2 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Mon, 8 Jan 2024 16:14:32 -0800 Subject: [PATCH 100/524] chore(bindings): update subproject commit hash to 870d7eddfa1504a45de7286e51efa00ad37932d9 for latest changes --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 5f9f3a3a30..870d7eddfa 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 5f9f3a3a30d3e98e7f42764ddf4d5c3b9db45a9e +Subproject commit 870d7eddfa1504a45de7286e51efa00ad37932d9 From e5d93a778384925491234bd17a20f62cb5685d79 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 28 Sep 2023 13:46:46 -0300 Subject: [PATCH 101/524] feat: add mina submodule to o1js --- src/mina | 1 + 1 file changed, 1 insertion(+) create mode 160000 src/mina diff --git a/src/mina b/src/mina new file mode 160000 index 0000000000..396ae46c0f --- /dev/null +++ b/src/mina @@ -0,0 +1 @@ +Subproject commit 396ae46c0f301f697c91771bc6780571a7656a45 From fccf6e333652ed351911f167e744edf150aad206 Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:43:22 +0800 Subject: [PATCH 102/524] fix typo --- run-ci-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run-ci-tests.sh b/run-ci-tests.sh index d59c3cd09b..14f444a2a5 100755 --- a/run-ci-tests.sh +++ b/run-ci-tests.sh @@ -47,7 +47,7 @@ case $TEST_TYPE in ;; *) - echo "ERROR: Invalid enviroment variable, not clear what tests to run! $CI_NODE_INDEX" + echo "ERROR: Invalid environment variable, not clear what tests to run! $CI_NODE_INDEX" exit 1 ;; esac From 6d6f8b4052b272eb59ce3e756865718f12b0d3bc Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:43:34 +0800 Subject: [PATCH 103/524] fix typo --- src/lib/field.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/field.ts b/src/lib/field.ts index 843110d526..0e643cb2a5 100644 --- a/src/lib/field.ts +++ b/src/lib/field.ts @@ -118,7 +118,7 @@ type VarField = Field & { value: VarFieldVar }; * You can create a new Field from everything "field-like" (`bigint`, integer `number`, decimal `string`, `Field`). * @example * ``` - * Field(10n); // Field contruction from a big integer + * Field(10n); // Field construction from a big integer * Field(100); // Field construction from a number * Field("1"); // Field construction from a decimal string * ``` From 8e2a365f0ab1e554b0885970a4d60470969bec0e Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:43:39 +0800 Subject: [PATCH 104/524] fix typo --- src/lib/int.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/int.ts b/src/lib/int.ts index 9bae3afdf1..9e4f680e9a 100644 --- a/src/lib/int.ts +++ b/src/lib/int.ts @@ -1280,7 +1280,7 @@ class Int64 extends CircuitValue implements BalanceChange { this.toField().assertEquals(y_.toField(), message); } /** - * Checks if the value is postive. + * Checks if the value is positive. */ isPositive() { return this.sgn.isPositive(); From 3606ca1209736c1129a0d305504744b67d3b30a9 Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:43:44 +0800 Subject: [PATCH 105/524] fix typo --- src/lib/keccak.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/keccak.ts b/src/lib/keccak.ts index ccc8c45548..efc82342c7 100644 --- a/src/lib/keccak.ts +++ b/src/lib/keccak.ts @@ -508,7 +508,7 @@ const BytesOfBitlength = { 512: Bytes64, }; -// AUXILARY FUNCTIONS +// AUXILIARY FUNCTIONS // Auxiliary functions to check the composition of 8 byte values (LE) into a 64-bit word and create constraints for it From de1bd7132cca83d004fe4c5a5574d04d35180b38 Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:43:50 +0800 Subject: [PATCH 106/524] fix typo --- src/lib/testing/random.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/testing/random.ts b/src/lib/testing/random.ts index 8dd41ac38f..358b0ac206 100644 --- a/src/lib/testing/random.ts +++ b/src/lib/testing/random.ts @@ -356,7 +356,7 @@ function generatorFromLayout( return array(element, size); }, reduceObject(keys, object) { - // hack to not sample invalid vk hashes (because vk hash is correlated with other fields, and has to be overriden) + // hack to not sample invalid vk hashes (because vk hash is correlated with other fields, and has to be overridden) if (keys.includes('verificationKeyHash')) { (object as any).verificationKeyHash = noInvalid( (object as any).verificationKeyHash From 80e5683429a9867053c700906fbaee4847cd41c7 Mon Sep 17 00:00:00 2001 From: vuittont60 <81072379+vuittont60@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:43:55 +0800 Subject: [PATCH 107/524] fix typo --- src/lib/token.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/token.test.ts b/src/lib/token.test.ts index a8b6428af1..038686ee32 100644 --- a/src/lib/token.test.ts +++ b/src/lib/token.test.ts @@ -343,7 +343,7 @@ describe('Token', () => { token contract can transfer tokens with a signature tested cases: - sends tokens and updates the balance of the receiver - - fails if no account creation fee is payed for the new token account + - fails if no account creation fee is paid for the new token account - fails if we transfer more than the balance amount */ describe('Transfer', () => { From 3694d358332efc2e14f7fc7d9df694ab19fcae9c Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 8 Jan 2024 16:35:34 +0100 Subject: [PATCH 108/524] start debugging dex example --- src/examples/zkapps/dex/dex.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 8bf5a1ea8f..0f81f0a0c3 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -441,16 +441,16 @@ class TokenContract extends SmartContract { amount: UInt64 ) { // TODO: THIS IS INSECURE. The proper version has a prover error (compile != prove) that must be fixed - this.approve(zkappUpdate, AccountUpdate.Layout.AnyChildren); + // this.approve(zkappUpdate, AccountUpdate.Layout.AnyChildren); // THIS IS HOW IT SHOULD BE DONE: - // // approve a layout of two grandchildren, both of which can't inherit the token permission - // let { StaticChildren, AnyChildren } = AccountUpdate.Layout; - // this.approve(zkappUpdate, StaticChildren(AnyChildren, AnyChildren)); - // zkappUpdate.body.mayUseToken.parentsOwnToken.assertTrue(); - // let [grandchild1, grandchild2] = zkappUpdate.children.accountUpdates; - // grandchild1.body.mayUseToken.inheritFromParent.assertFalse(); - // grandchild2.body.mayUseToken.inheritFromParent.assertFalse(); + // approve a layout of two grandchildren, both of which can't inherit the token permission + let { StaticChildren, AnyChildren } = AccountUpdate.Layout; + this.approve(zkappUpdate, StaticChildren(AnyChildren, AnyChildren)); + zkappUpdate.body.mayUseToken.parentsOwnToken.assertTrue(); + let [grandchild1, grandchild2] = zkappUpdate.children.accountUpdates; + grandchild1.body.mayUseToken.inheritFromParent.assertFalse(); + grandchild2.body.mayUseToken.inheritFromParent.assertFalse(); // see if balance change cancels the amount sent let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); From 1e06cfcb76a4a9faf4d1ea333e8cbf0a4bd57439 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 11 Jan 2024 12:53:56 +0100 Subject: [PATCH 109/524] helper to print account update layout --- src/lib/account_update.ts | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 23f5e66c08..f04bcbdab7 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1096,11 +1096,37 @@ class AccountUpdate implements Types.AccountUpdate { return { accountUpdate, calls }; } + toPrettyLayout() { + let indent = 0; + let layout = ''; + let i = 0; + + let print = (a: AccountUpdate) => { + layout += + ' '.repeat(indent) + + `AccountUpdate(${i}, ${a.label || ''}, ${ + a.children.callsType.type + })` + + '\n'; + i++; + indent += 2; + for (let child of a.children.accountUpdates) { + print(child); + } + indent -= 2; + }; + + print(this); + return layout; + } + static defaultAccountUpdate(address: PublicKey, tokenId?: Field) { return new AccountUpdate(Body.keepAll(address, tokenId)); } static dummy() { - return new AccountUpdate(Body.dummy()); + let dummy = new AccountUpdate(Body.dummy()); + dummy.label = 'Dummy'; + return dummy; } isDummy() { return this.body.publicKey.isEmpty(); From 0612d09d49a0a19ac2783355545731a655401b07 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 11 Jan 2024 12:55:46 +0100 Subject: [PATCH 110/524] fix the damn bug --- src/lib/account_update.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index f04bcbdab7..b24b32354b 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1347,6 +1347,7 @@ class AccountUpdate implements Types.AccountUpdate { accountUpdate.body.mayUseToken.inheritFromParent.assertFalse(); return; } + accountUpdate.children.callsType = { type: 'None' }; let childArray: AccountUpdatesLayout[] = typeof childLayout === 'number' ? Array(childLayout).fill(AccountUpdate.Layout.NoChildren) From 4eda5c910f016d88bf64ce1543935bdd5ef30b2d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 11 Jan 2024 13:00:28 +0100 Subject: [PATCH 111/524] [dex] remove obsolete scare comments --- src/examples/zkapps/dex/dex.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 0f81f0a0c3..44ac6b5178 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -440,10 +440,6 @@ class TokenContract extends SmartContract { to: PublicKey, amount: UInt64 ) { - // TODO: THIS IS INSECURE. The proper version has a prover error (compile != prove) that must be fixed - // this.approve(zkappUpdate, AccountUpdate.Layout.AnyChildren); - - // THIS IS HOW IT SHOULD BE DONE: // approve a layout of two grandchildren, both of which can't inherit the token permission let { StaticChildren, AnyChildren } = AccountUpdate.Layout; this.approve(zkappUpdate, StaticChildren(AnyChildren, AnyChildren)); From ed42a3ff49de63c289a16f3a8620ba11fbbfb67e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 11 Jan 2024 13:00:37 +0100 Subject: [PATCH 112/524] minor example tweak --- src/examples/simple_zkapp.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/examples/simple_zkapp.ts b/src/examples/simple_zkapp.ts index 26f379770d..bd17d6eae4 100644 --- a/src/examples/simple_zkapp.ts +++ b/src/examples/simple_zkapp.ts @@ -91,6 +91,8 @@ if (doProofs) { console.time('compile'); await SimpleZkapp.compile(); console.timeEnd('compile'); +} else { + SimpleZkapp.analyzeMethods(); } console.log('deploy'); From 68417419e9e5b387473526245f28df32a5943fd3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 11 Jan 2024 13:21:40 +0100 Subject: [PATCH 113/524] changelog --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd76e3380e..cd308145b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/08ba27329...HEAD) +### Fixed + +- Fix approving of complex account update layouts https://github.com/o1-labs/o1js/pull/1364 + ## [0.15.2](https://github.com/o1-labs/o1js/compare/1ad7333e9e...08ba27329) ### Fixed -- Fix bug in `Hash.hash()` which always resulted in an error. https://github.com/o1-labs/o1js/pull/1346 +- Fix bug in `Hash.hash()` which always resulted in an error https://github.com/o1-labs/o1js/pull/1346 ## [0.15.1](https://github.com/o1-labs/o1js/compare/1ad7333e9e...19115a159) From 370a43c79d185dbe6eb80e75770ecf9edb5e6fd8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 11 Jan 2024 13:26:44 +0100 Subject: [PATCH 114/524] vk regression --- tests/vk-regression/vk-regression.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 2f69465d55..2d93692c67 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -63,7 +63,7 @@ } }, "TokenContract": { - "digest": "346c5ce0416c2479d962f0868825b4bcbf68f5beac5e7a93632013a6c57d1be8", + "digest": "f97dbbb67a72f01d956539b9f510633c0f88057f5dfbad8493e8d08dc4ca049", "methods": { "init": { "rows": 655, @@ -86,8 +86,8 @@ "digest": "240aada76b79de1ca67ecbe455621378" }, "approveUpdateAndSend": { - "rows": 2321, - "digest": "b1cff49cdc3cc751f802b4b5aee53383" + "rows": 3102, + "digest": "77efc708b9517c16722bad9cca54eb9c" }, "transferToAddress": { "rows": 1044, @@ -103,8 +103,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "13796172868423455932596117465273580383420853883879480382066094121613342871544" + "data": "AAAVRdJJF0DehjdPSA0kYGZTkzSfoEaHqDprP5lbtp+BLeGqblAzBabKYB+hRBo7ijFWFnIHV4LwvOlCtrAhNtk/Ae0EY5Tlufvf2snnstKNDXVgcRc/zNAaS5iW43PYqQnEYsaesXs/y5DeeEaFxwdyujsHSK/UaltNLsCc34RKG71O/TGRVVX/eYb8saPPV9W5YjPHLQdhqcHRU6Qq7hMEI1ejTXMokQcurz7jtYU/P56OYekAREejgrEV38U82BbgJigOmh5NhgGTBSAhJ35c9XCsJldUMd5xZiua9cWxGOHm0r7TkcCrV9CEPm5sT7sP7IYQ5dnSdPoi/sy7moUPRitxw7iGvewRVXro6rIemmbxNSzKXWprnl6ewrB2HTppMUEZRp7zYkFIaNDHpvdw4dvjX6K/i527/jwX0JL4BideRc+z3FNhj1VBSHhhvMzwFW6aUwSmWC4UCuwDBokkkBtUE0YYH8kwFnMoWWAlDzHekrxaVmxWRS0lvkr8IDlsR5kyq8SMXFLgKJjoFr6HZWE4tkO/abEgrsK1A3c9F5r/G2yUdMQZu8JMwxUY5qw7D09IPsUQ63c5/CJpea8PAHbUlzRl2KhAhm58JzY0th81wwK0uXhv2e0aXMoEpM0YViAu+c/32zmBe6xl97uBNmNWwlWOLEpHakq46OzONidU3betWNXGJbS4dC4hTNfWM956bK+fwkIlwhM3BC+wOai+M0+y9/y/RSI8qJkSU3MqOF9+nrifKRyNQ3KILqIyR7LjE0/Z/4NzH7eF3uZTBlqfLdf8WhXdwvOPoP1dCx1shF6g4Hh9V4myikRZBtkix1cO5FLUNLNAFw+glg1PB1eA+4ATFuFcfMjxDpDjxqCFCyuQ5TaLuNfYMA7fiO0vB6yqtWgSmCOlD/MQqAhHYRMq4PXk3TUQSle8XBZ67T0+gENjIJleTRgZFG6PgIEwHXcsKIvfFAPklTlnY+5sNVw8yBisVaFgw36DrHWNavWvsZM5HwD0h1Wk0hkavjEIb8rcV72g0u/pc/DJiHd3yJ8v6/HRt37apY8PaEibaDNWXXbSE2vRZQmtCUuAgHpuZ4168hKslBTR55TIuZp9AVdRTanQ73mLIz80yky+lCNkLWHmZtyWjtMsDFNgupc+yc+FvFNjJM/ea6u3PROtSyU3rAlmchkKvxO4qfrd0iqav/WbabGDMJhbugO4TNu1/i5omH8pbsjGGHQXk1UYPoP1SnMVPZ9RXPoWHJn/kePU9QqGxETHF4T7b2Ov7CcZDLuz147VCknmGiziHzbmYJleu4tzSlFsxHPkp2d9JiDUbO7X66Dh/+84gc5KWpMnEIAF9gITi3cXUglZTjWaASaXcpgHXXGZHZJcrG2VfPNjgTKJ1+CbvyXlvuhvX+0E2oaPB+BoP0i2iTXQHPNhOY/Gg2h6uKvE5fSSiYC7Rws2TGF1aEM54wX3Ti1qA1cAiNG5y8yk1YMGCk3TPqs9MRp0qjgjJbbvFlbgPkkqz5o6c7g8gfhIa4VEJyyI2joqJeIc7vMZFWhquSFHNs0TZKvKLiSAsyNDrpWZb/1PHxziswKvisk296AJi7hmlM1pKx6S4LlbT2OKLXbgq5HUKfe8QhxG4aOsPSSiVGwvnCrIPdSxLq77M27UWXnXHC8mmJmOsGUFj+bdX/u6AgrBhw/w74dDbuNEpC80PbJTuglF/TeDryYsFWCrBnF/WPstgzy3zDDTZ3DXHVYVxOEvErIynlQEY9Cv9QSxRI3dA+hLtob/L78ZeJSU4Al+Qv0QGZTOxQORosVshOP2eFQ1VMKGWOpCVvyi8QE4fa+gOgYT0JRm4rkQBZ5WDlYGkamD3euC92Kd7Z39G89h/AqeFACahkAW1a78SzLW69mZ+CDLfKp/xQsi2TWgJqGh7QNOEtMnn/2owLzLWd071mvUtT0484Eqx6hUqLJMH70p8oUjQIMsh0mvp1BWSU8XC6z+UZIpVm2CERrV8BMLmTLOgTNJlEIJQR7zzpJCDFNNOI+Y2ZtdcuU8XHgcsQhQ3PgCACFAWN3rO+goXoTWdYR/LcqszKzPnMArmPIHWkRM6Mkm13OsHXCVudUbqQjC/pNQZH1VW+RMXnre1vQVb3fnCy5h28Dce3Q2WzjBSZFhe3iADZpo7gWHM/sqe+Mbnbn8A+RRWVNbtjss9376jN73zV4xPH3un3VjTxrzCluqR8MbH8t7mhPBqV5CslmSIbDNruVXtwCf4VS1nssw63PfLzeOSvzhTTsg82rna/+TKl1RIwhD8VFnCDq/Rk8fdy/+K5qP6GcSTbh6J8ERx4jOOukL9TUCpJkhvo/3ED8GOewmWAwzL8avXuf9AFvhwH3ENp5v4IIGBljuDJ77vckGmTI=", + "hash": "20215142001741862869723990740096021895498505655157925381074115836190155964997" } }, "Dex": { From 841a79c46ddf303331ee391456fbab64c0ef8a80 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 11 Jan 2024 20:20:46 +0200 Subject: [PATCH 115/524] Use actual network genesis constants (timestamp). --- src/lib/fetch.ts | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib/mina.ts | 15 +++++++------- 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index 88b87c7e87..edda889e93 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -31,6 +31,7 @@ export { getCachedAccount, getCachedNetwork, getCachedActions, + getCachedGenesisConstants, addCachedAccount, networkConfig, setGraphqlEndpoint, @@ -216,6 +217,12 @@ type FetchError = { type ActionStatesStringified = { [K in keyof ActionStates]: string; }; +type GenesisConstants = { + genesisTimestamp: string; + coinbase: number; + accountCreationFee: number; +}; + // Specify 5min as the default timeout const defaultTimeout = 5 * 60 * 1000; @@ -257,6 +264,7 @@ let actionsToFetch = {} as Record< graphqlEndpoint: string; } >; +let genesisConstantsCache = {} as Record; function markAccountToBeFetched( publicKey: PublicKey, @@ -363,6 +371,23 @@ function getCachedActions( ?.actions; } +function getCachedGenesisConstants( + graphqlEndpoint = networkConfig.minaEndpoint +): GenesisConstants { + let genesisConstants = genesisConstantsCache[graphqlEndpoint]; + if (genesisConstants === undefined) { + fetchGenesisConstants(graphqlEndpoint) + .then((fetchedGenesisConstants) => { + genesisConstantsCache[graphqlEndpoint] = fetchedGenesisConstants; + genesisConstants = fetchedGenesisConstants; + }) + .catch((error) => { + throw Error(error.message); + }); + } + return genesisConstants; +} + /** * Adds an account to the local cache, indexed by a GraphQL endpoint. */ @@ -819,6 +844,13 @@ const getActionsQuery = ( } }`; }; +const genesisConstantsQuery = `{ + genesisConstants { + genesisTimestamp + coinbase + accountCreationFee + } + }`; /** * Asynchronously fetches event data for an account from the Mina Archive Node GraphQL API. @@ -1009,6 +1041,25 @@ async function fetchActions( return actionsList; } +/** + * Fetches genesis constants. + */ +async function fetchGenesisConstants( + graphqlEndpoint = networkConfig.minaEndpoint +): Promise { + let [resp, error] = await makeGraphqlRequest( + genesisConstantsQuery, + graphqlEndpoint, + networkConfig.minaFallbackEndpoints + ); + if (error) throw Error(error.statusText); + const genesisConstants = resp?.data?.genesisConstants; + if (genesisConstants === undefined) { + throw Error('Failed to fetch genesis constants.'); + } + return genesisConstants as GenesisConstants; +} + namespace Lightnet { /** * Gets random key pair (public and private keys) from account manager diff --git a/src/lib/mina.ts b/src/lib/mina.ts index e3b5823a9c..d240dd134d 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -343,7 +343,7 @@ interface Mina { getNetworkConstants(): { genesisTimestamp: UInt64; /** - * Duration of 1 slot in millisecondw + * Duration of 1 slot in milliseconds */ slotTime: UInt64; accountCreationFee: UInt64; @@ -737,19 +737,18 @@ function Network( ); } - // copied from mina/genesis_ledgers/berkeley.json - // TODO fetch from graphql instead of hardcoding - const genesisTimestampString = '2023-02-23T20:00:01Z'; - const genesisTimestamp = UInt64.from( - Date.parse(genesisTimestampString.slice(0, -1) + '+00:00') - ); // TODO also fetch from graphql const slotTime = UInt64.from(3 * 60 * 1000); return { accountCreationFee: () => accountCreationFee, getNetworkConstants() { return { - genesisTimestamp, + genesisTimestamp: UInt64.from( + Date.parse( + Fetch.getCachedGenesisConstants(minaGraphqlEndpoint) + .genesisTimestamp + ) + ), slotTime, accountCreationFee, }; From ae52dc11e10f1885824726243b14f9857dd9226f Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 13 Jan 2024 13:48:21 +0200 Subject: [PATCH 116/524] Refactoring. --- run-ci-live-tests.sh | 9 +++++ src/examples/fetch_live.ts | 73 ++++++++++++++++++++++++++++++++++++++ src/lib/fetch.ts | 46 ++++++++++++++++-------- src/lib/mina.ts | 70 +++++++++++++++++++++++++----------- 4 files changed, 162 insertions(+), 36 deletions(-) create mode 100644 src/examples/fetch_live.ts diff --git a/run-ci-live-tests.sh b/run-ci-live-tests.sh index ba881bb5c7..192041b540 100755 --- a/run-ci-live-tests.sh +++ b/run-ci-live-tests.sh @@ -17,6 +17,8 @@ echo "" HELLO_WORLD_PROC=$! ./run src/examples/zkapps/dex/run_live.ts --bundle | add_prefix "DEX" & DEX_PROC=$! +./run src/examples/fetch_live.ts --bundle | add_prefix "FETCH" & +FETCH_PROC=$! # Wait for each process and capture their exit statuses FAILURE=0 @@ -34,6 +36,13 @@ if [ $? -ne 0 ]; then echo "" FAILURE=1 fi +wait $FETCH_PROC +if [ $? -ne 0 ]; then + echo "" + echo "FETCH test failed." + echo "" + FAILURE=1 +fi # Exit with failure if any process failed if [ $FAILURE -ne 0 ]; then diff --git a/src/examples/fetch_live.ts b/src/examples/fetch_live.ts new file mode 100644 index 0000000000..f42b187504 --- /dev/null +++ b/src/examples/fetch_live.ts @@ -0,0 +1,73 @@ +import { expect } from 'expect'; +import { Lightnet, Mina, PrivateKey, UInt64, fetchAccount } from 'o1js'; + +const useCustomLocalNetwork = process.env.USE_CUSTOM_LOCAL_NETWORK === 'true'; +const minaGraphQlEndpoint = useCustomLocalNetwork + ? 'http://localhost:8080/graphql' + : 'https://berkeley.minascan.io/graphql'; +const network = Mina.Network({ + mina: minaGraphQlEndpoint, + archive: useCustomLocalNetwork + ? 'http://localhost:8282' + : 'https://api.minascan.io/archive/berkeley/v1/graphql', + lightnetAccountManager: 'http://localhost:8181', +}); +Mina.setActiveInstance(network); +const transactionFee = 100_000_000; + +// Fee payer setup +console.log(''); +const senderKey = useCustomLocalNetwork + ? (await Lightnet.acquireKeyPair()).privateKey + : PrivateKey.random(); +const sender = senderKey.toPublicKey(); +if (!useCustomLocalNetwork) { + console.log(`Funding the fee payer account.`); + await Mina.faucet(sender); +} +console.log(`Fetching the fee payer account information.`); +const accountDetails = (await fetchAccount({ publicKey: sender })).account; +console.log( + `Using the fee payer account ${sender.toBase58()} with nonce: ${ + accountDetails?.nonce + } and balance: ${accountDetails?.balance}.` +); + +console.log(''); +console.log( + "Check that network constants CAN'T be fetched outside of a transaction." +); +const getNetworkConstants = () => { + Mina.activeInstance.getNetworkConstants(); +}; +expect(getNetworkConstants).toThrow( + `getNetworkConstants: Could not fetch network constants from graphql endpoint ${minaGraphQlEndpoint} outside of a transaction.` +); + +console.log(''); +console.log( + 'Check that network constants CAN be fetched within the transaction.' +); +let slotTime: UInt64 | undefined; +let genesisTimestamp: UInt64 | undefined; +await Mina.transaction({ sender, fee: transactionFee }, () => { + const networkConstants = Mina.activeInstance.getNetworkConstants(); + slotTime = networkConstants.slotTime; + genesisTimestamp = networkConstants.genesisTimestamp; +}); + +expect(slotTime).not.toBeUndefined(); +expect(genesisTimestamp).not.toBeUndefined(); + +console.log(`Slot time: ${slotTime}`); +console.log(`Genesis timestamp: ${genesisTimestamp}`); +console.log( + `Genesis date: ${new Date(Number(genesisTimestamp?.toString() ?? '0'))}` +); + +// Tear down +console.log(''); +const keyPairReleaseMessage = await Lightnet.releaseKeyPair({ + publicKey: sender.toBase58(), +}); +if (keyPairReleaseMessage) console.info(keyPairReleaseMessage); diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index edda889e93..f1d39ef8b8 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -19,6 +19,7 @@ import { export { fetchAccount, fetchLastBlock, + fetchGenesisConstants, checkZkappTransaction, parseFetchedAccount, markAccountToBeFetched, @@ -45,7 +46,8 @@ export { removeJsonQuotes, fetchEvents, fetchActions, - Lightnet + Lightnet, + type GenesisConstants, }; type NetworkConfig = { @@ -221,6 +223,10 @@ type GenesisConstants = { genesisTimestamp: string; coinbase: number; accountCreationFee: number; + epochDuration: number; + k: number; + slotDuration: number; + slotsPerEpoch: number; }; // Specify 5min as the default timeout @@ -341,6 +347,7 @@ async function fetchMissingData( (async () => { try { await fetchLastBlock(graphqlEndpoint); + await fetchGenesisConstants(graphqlEndpoint); delete networksToFetch[network[0]]; } catch {} })() @@ -374,18 +381,7 @@ function getCachedActions( function getCachedGenesisConstants( graphqlEndpoint = networkConfig.minaEndpoint ): GenesisConstants { - let genesisConstants = genesisConstantsCache[graphqlEndpoint]; - if (genesisConstants === undefined) { - fetchGenesisConstants(graphqlEndpoint) - .then((fetchedGenesisConstants) => { - genesisConstantsCache[graphqlEndpoint] = fetchedGenesisConstants; - genesisConstants = fetchedGenesisConstants; - }) - .catch((error) => { - throw Error(error.message); - }); - } - return genesisConstants; + return genesisConstantsCache[graphqlEndpoint]; } /** @@ -850,6 +846,14 @@ const genesisConstantsQuery = `{ coinbase accountCreationFee } + daemonStatus { + consensusConfiguration { + epochDuration + k + slotDuration + slotsPerEpoch + } + } }`; /** @@ -1054,10 +1058,22 @@ async function fetchGenesisConstants( ); if (error) throw Error(error.statusText); const genesisConstants = resp?.data?.genesisConstants; - if (genesisConstants === undefined) { + const consensusConfiguration = + resp?.data?.daemonStatus?.consensusConfiguration; + if (genesisConstants === undefined || consensusConfiguration === undefined) { throw Error('Failed to fetch genesis constants.'); } - return genesisConstants as GenesisConstants; + const data = { + genesisTimestamp: genesisConstants.genesisTimestamp, + coinbase: Number(genesisConstants.coinbase), + accountCreationFee: Number(genesisConstants.accountCreationFee), + epochDuration: Number(consensusConfiguration.epochDuration), + k: Number(consensusConfiguration.k), + slotDuration: Number(consensusConfiguration.slotDuration), + slotsPerEpoch: Number(consensusConfiguration.slotsPerEpoch), + }; + genesisConstantsCache[graphqlEndpoint] = data; + return data as GenesisConstants; } namespace Lightnet { diff --git a/src/lib/mina.ts b/src/lib/mina.ts index d240dd134d..c984ee174c 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -65,6 +65,7 @@ export { // for internal testing only filterGroups, }; + interface TransactionId { isSuccess: boolean; wait(options?: { maxAttempts?: number; interval?: number }): Promise; @@ -163,6 +164,15 @@ type ActionStates = { endActionState?: Field; }; +type NetworkConstants = { + genesisTimestamp: UInt64; + /** + * Duration of 1 slot in milliseconds + */ + slotTime: UInt64; + accountCreationFee: UInt64; +}; + function reportGetAccountError(publicKey: string, tokenId: string) { if (tokenId === TokenId.toBase58(TokenId.default)) { return `getAccount: Could not find account for public key ${publicKey}`; @@ -340,14 +350,7 @@ interface Mina { hasAccount(publicKey: PublicKey, tokenId?: Field): boolean; getAccount(publicKey: PublicKey, tokenId?: Field): Account; getNetworkState(): NetworkValue; - getNetworkConstants(): { - genesisTimestamp: UInt64; - /** - * Duration of 1 slot in milliseconds - */ - slotTime: UInt64; - accountCreationFee: UInt64; - }; + getNetworkConstants(): NetworkConstants; accountCreationFee(): UInt64; sendTransaction(transaction: Transaction): Promise; fetchEvents: ( @@ -369,6 +372,11 @@ interface Mina { } const defaultAccountCreationFee = 1_000_000_000; +const defaultNetworkConstants = { + genesisTimestamp: UInt64.from(0), + slotTime: UInt64.from(3 * 60 * 1000), + accountCreationFee: UInt64.from(defaultAccountCreationFee), +}; /** * A mock Mina blockchain running locally and useful for testing. @@ -737,21 +745,29 @@ function Network( ); } - // TODO also fetch from graphql - const slotTime = UInt64.from(3 * 60 * 1000); return { accountCreationFee: () => accountCreationFee, getNetworkConstants() { - return { - genesisTimestamp: UInt64.from( - Date.parse( - Fetch.getCachedGenesisConstants(minaGraphqlEndpoint) - .genesisTimestamp - ) - ), - slotTime, - accountCreationFee, - }; + if (currentTransaction()?.fetchMode === 'test') { + Fetch.markNetworkToBeFetched(minaGraphqlEndpoint); + const genesisConstants = + Fetch.getCachedGenesisConstants(minaGraphqlEndpoint); + return genesisConstants !== undefined + ? genesisToNetworkConstants(genesisConstants) + : defaultNetworkConstants; + } + if ( + !currentTransaction.has() || + currentTransaction.get().fetchMode === 'cached' + ) { + const genesisConstants = + Fetch.getCachedGenesisConstants(minaGraphqlEndpoint); + if (genesisConstants !== undefined) + return genesisToNetworkConstants(genesisConstants); + } + throw Error( + `getNetworkConstants: Could not fetch network constants from graphql endpoint ${minaGraphqlEndpoint} outside of a transaction.` + ); }, currentSlot() { throw Error( @@ -813,7 +829,7 @@ function Network( if (network !== undefined) return network; } throw Error( - `getNetworkState: Could not fetch network state from graphql endpoint ${minaGraphqlEndpoint}` + `getNetworkState: Could not fetch network state from graphql endpoint ${minaGraphqlEndpoint} outside of a transaction.` ); }, async sendTransaction(txn: Transaction) { @@ -1591,3 +1607,15 @@ async function faucet(pub: PublicKey, network: string = 'berkeley-qanet') { } await waitForFunding(address); } + +function genesisToNetworkConstants( + genesisConstants: Fetch.GenesisConstants +): NetworkConstants { + return { + genesisTimestamp: UInt64.from( + Date.parse(genesisConstants.genesisTimestamp) + ), + slotTime: UInt64.from(genesisConstants.slotDuration), + accountCreationFee: UInt64.from(genesisConstants.accountCreationFee), + }; +} From e5ec5ae7e93269a0eb24a6777c903ea0a67a9959 Mon Sep 17 00:00:00 2001 From: Bork Bork <107079055+BorkBorked@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:42:47 +0100 Subject: [PATCH 117/524] Update int.ts --- src/lib/int.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/int.ts b/src/lib/int.ts index 9e4f680e9a..3306d59f3b 100644 --- a/src/lib/int.ts +++ b/src/lib/int.ts @@ -178,7 +178,7 @@ class UInt64 extends CircuitValue { * Integer remainder. * * `x.mod(y)` returns the value `z` such that `0 <= z < y` and - * `x - z` is divisble by `y`. + * `x - z` is divisible by `y`. */ mod(y: UInt64 | number) { return this.divMod(y).rest; @@ -683,7 +683,7 @@ class UInt32 extends CircuitValue { * Integer remainder. * * `x.mod(y)` returns the value `z` such that `0 <= z < y` and - * `x - z` is divisble by `y`. + * `x - z` is divisible by `y`. */ mod(y: UInt32 | number) { return this.divMod(y).rest; @@ -1253,7 +1253,7 @@ class Int64 extends CircuitValue implements BalanceChange { * Integer remainder. * * `x.mod(y)` returns the value `z` such that `0 <= z < y` and - * `x - z` is divisble by `y`. + * `x - z` is divisible by `y`. */ mod(y: UInt64 | number | string | bigint | UInt32) { let y_ = UInt64.from(y); From d1ac5ef6ba6873eb148422e6ad55f2a41919272c Mon Sep 17 00:00:00 2001 From: Bork Bork <107079055+BorkBorked@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:43:16 +0100 Subject: [PATCH 118/524] Update string.ts --- src/lib/string.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/string.ts b/src/lib/string.ts index 8045dd3318..360f66465f 100644 --- a/src/lib/string.ts +++ b/src/lib/string.ts @@ -102,7 +102,7 @@ class CircuitString extends CircuitValue { .slice(0, length) .concat(otherChars.slice(0, n - length)); } - // compute the actual result, by always picking the char which correponds to the actual length + // compute the actual result, by always picking the char which corresponds to the actual length let result: Character[] = []; let mask = this.lengthMask(); for (let i = 0; i < n; i++) { From b64e72acc8d6f400a10c8c669f11cd12c7ddbfe3 Mon Sep 17 00:00:00 2001 From: Bork Bork <107079055+BorkBorked@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:44:13 +0100 Subject: [PATCH 119/524] Update precondition.ts --- src/lib/precondition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index 25b10b9031..628f42a915 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -416,7 +416,7 @@ function assertPreconditionInvariants(accountUpdate: AccountUpdate) { let self = context.isSelf ? 'this' : 'accountUpdate'; let dummyPreconditions = Preconditions.ignoreAll(); for (let preconditionPath of context.read) { - // check if every precondition that was read was also contrained + // check if every precondition that was read was also constrained if (context.constrained.has(preconditionPath)) continue; // check if the precondition was modified manually, which is also a valid way of avoiding an error From 24ead4bbb4f010699231e2014e79d10884cf3812 Mon Sep 17 00:00:00 2001 From: Bork Bork <107079055+BorkBorked@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:44:51 +0100 Subject: [PATCH 120/524] Update circuit_value.ts --- src/lib/circuit_value.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index d42f267cdf..f3f0705a60 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -576,7 +576,7 @@ function cloneCircuitValue(obj: T): T { // primitive JS types and functions aren't cloned if (typeof obj !== 'object' || obj === null) return obj; - // HACK: callbacks, account udpates + // HACK: callbacks, account updates if ( obj.constructor?.name.includes('GenericArgument') || obj.constructor?.name.includes('Callback') From 1bcdc1ffe595237dd4f2b927b010c8ef0fb248be Mon Sep 17 00:00:00 2001 From: Bork Bork <107079055+BorkBorked@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:45:23 +0100 Subject: [PATCH 121/524] Update state.ts --- src/lib/state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/state.ts b/src/lib/state.ts index dc848a1996..bf6a534ec6 100644 --- a/src/lib/state.ts +++ b/src/lib/state.ts @@ -409,7 +409,7 @@ const reservedPropNames = new Set(['_methods', '_']); function assertStatePrecondition(sc: SmartContract) { try { for (let [key, context] of getStateContexts(sc)) { - // check if every state that was read was also contrained + // check if every state that was read was also constrained if (!context?.wasRead || context.wasConstrained) continue; // we accessed a precondition field but not constrained it explicitly - throw an error let errorMessage = `You used \`this.${key}.get()\` without adding a precondition that links it to the actual on-chain state. From b16d146dc0adaed1227b0c1d04a5c985beafda53 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 16 Jan 2024 09:54:24 +0300 Subject: [PATCH 122/524] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6868af6bd1..dadf1323e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/08ba27329...HEAD) +### Added + +- **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 + ### Fixed - Fix approving of complex account update layouts https://github.com/o1-labs/o1js/pull/1364 From c189f2be73561218ddfb3cd99d6db4dda4371c35 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 16 Jan 2024 10:39:37 +0100 Subject: [PATCH 123/524] adapt tictoc and examples build --- src/examples/utils/tic-toc.node.ts | 12 ++++++------ src/examples/utils/tic-toc.ts | 8 ++++---- tsconfig.examples.json | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/examples/utils/tic-toc.node.ts b/src/examples/utils/tic-toc.node.ts index 9e8d50634a..ea6c4d012d 100644 --- a/src/examples/utils/tic-toc.node.ts +++ b/src/examples/utils/tic-toc.node.ts @@ -1,21 +1,21 @@ /** * Helper for printing timings, in the spirit of Python's `tic` and `toc`. * - * This is a slightly nicer version of './tic-tic.ts' which only works in Node. + * This is a slightly nicer version of './tic-toc.ts' which only works in Node. */ export { tic, toc }; -let timingStack: [string, number][] = []; -let i = 0; +let timingStack: [string | undefined, number][] = []; -function tic(label = `Run command ${i++}`) { - process.stdout.write(`${label}... `); +function tic(label?: string) { + if (label) process.stdout.write(`${label}... `); timingStack.push([label, performance.now()]); } function toc() { let [label, start] = timingStack.pop()!; let time = (performance.now() - start) / 1000; - process.stdout.write(`\r${label}... ${time.toFixed(3)} sec\n`); + if (label) process.stdout.write(`\r${label}... ${time.toFixed(3)} sec\n`); + return time; } diff --git a/src/examples/utils/tic-toc.ts b/src/examples/utils/tic-toc.ts index 4b66514d8d..32de080832 100644 --- a/src/examples/utils/tic-toc.ts +++ b/src/examples/utils/tic-toc.ts @@ -4,17 +4,17 @@ export { tic, toc }; -let timingStack: [string, number][] = []; +let timingStack: [string | undefined, number][] = []; let i = 0; -function tic(label = `Run command ${i++}`) { - console.log(`${label}... `); +function tic(label?: string) { + if (label) console.log(`${label}... `); timingStack.push([label, performance.now()]); } function toc() { let [label, start] = timingStack.pop()!; let time = (performance.now() - start) / 1000; - console.log(`\r${label}... ${time.toFixed(3)} sec\n`); + if (label) console.log(`${label}... ${time.toFixed(3)} sec`); return time; } diff --git a/tsconfig.examples.json b/tsconfig.examples.json index 9d9e39d1eb..f76d1764b4 100644 --- a/tsconfig.examples.json +++ b/tsconfig.examples.json @@ -3,7 +3,7 @@ "include": ["./src/examples/**/*.ts"], "exclude": [], "compilerOptions": { - "outDir": "dist/", + "outDir": "dist/node", "importHelpers": false } } From 9732d5800b21d3284e03cb030649274e504288e4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 16 Jan 2024 16:06:55 +0100 Subject: [PATCH 124/524] add assert() in more basic module --- src/lib/util/assert.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/lib/util/assert.ts diff --git a/src/lib/util/assert.ts b/src/lib/util/assert.ts new file mode 100644 index 0000000000..df3f5a6749 --- /dev/null +++ b/src/lib/util/assert.ts @@ -0,0 +1,7 @@ +export { assert }; + +function assert(stmt: boolean, message?: string): asserts stmt { + if (!stmt) { + throw Error(message ?? 'Assertion failed'); + } +} From 65affe67e5e604f000522019f63af12f565cfc1f Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 16 Jan 2024 16:20:42 +0100 Subject: [PATCH 125/524] submodules --- src/bindings | 2 +- src/mina | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index a884dc593d..4a53d10476 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a884dc593dbab69e55ab9602b998ec12dfc3a288 +Subproject commit 4a53d10476eef30016941e54d57a047a521cc3f6 diff --git a/src/mina b/src/mina index 2a968c8347..00e3aa4ef3 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 2a968c83477ed9f9e3b30a02cc357e541b76dcac +Subproject commit 00e3aa4ef3aaaa13822a714f32926c824daae487 From eee19aa61dd051944a726a51113740b609c5aac4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 16 Jan 2024 18:03:33 +0100 Subject: [PATCH 126/524] revert examples build change --- tsconfig.examples.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.examples.json b/tsconfig.examples.json index f76d1764b4..9d9e39d1eb 100644 --- a/tsconfig.examples.json +++ b/tsconfig.examples.json @@ -3,7 +3,7 @@ "include": ["./src/examples/**/*.ts"], "exclude": [], "compilerOptions": { - "outDir": "dist/node", + "outDir": "dist/", "importHelpers": false } } From db6c2a086869f482e9972a78d2ca35e5a797108c Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 16 Jan 2024 18:06:26 +0100 Subject: [PATCH 127/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 4a53d10476..163af623b2 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 4a53d10476eef30016941e54d57a047a521cc3f6 +Subproject commit 163af623b2a0ac533b2004e783bf0e8865356a74 From 23d096c923431662220a5052e54b92f6fa39c296 Mon Sep 17 00:00:00 2001 From: Florian Date: Wed, 17 Jan 2024 10:55:41 +0300 Subject: [PATCH 128/524] address feedback --- src/lib/gadgets/sha256.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index 73cec23804..ed69db8325 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -48,12 +48,14 @@ function padding(data: FlexibleBytes): UInt32[][] { let l = message.length * 8; // length in bits let k = Number(mod(448n - (BigInt(l) + 1n), 512n)); + let lBinary = l.toString(2); + let paddingBits = ( '1' + // append 1 bit '0'.repeat(k) + // append k zero bits - '0'.repeat(64 - l.toString(2).length) + // append 64bit containing the length of the original message - l.toString(2) - ).match(/.{1,8}/g)!; // this should always be devisable by 8 + '0'.repeat(64 - lBinary.length) + // append 64bit containing the length of the original message + lBinary + ).match(/.{1,8}/g)!; // this should always be divisible by 8 // map the padding bit string to UInt8 elements let padding = paddingBits.map((x) => UInt8.from(BigInt('0b' + x))); From 226ab3157fa53ff6e20864614372a877dd334350 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 17 Jan 2024 09:38:30 +0100 Subject: [PATCH 129/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 163af623b2..309cac009f 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 163af623b2a0ac533b2004e783bf0e8865356a74 +Subproject commit 309cac009f42055567737ba77fc978e859e30c3b From bd501fb6793fb3442f417b23331977cbbf5653c9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 17 Jan 2024 09:47:40 +0100 Subject: [PATCH 130/524] fixup bench script in bindings --- src/bindings | 2 +- src/examples/utils/tic-toc.ts | 1 - src/lib/util/tic-toc.ts | 18 ++++++++++++++++++ tsconfig.test.json | 1 + 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/lib/util/tic-toc.ts diff --git a/src/bindings b/src/bindings index 309cac009f..df4ca0e729 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 309cac009f42055567737ba77fc978e859e30c3b +Subproject commit df4ca0e7291d7eb0af5034726d8eb975e359c62d diff --git a/src/examples/utils/tic-toc.ts b/src/examples/utils/tic-toc.ts index 32de080832..a7d2ab20b8 100644 --- a/src/examples/utils/tic-toc.ts +++ b/src/examples/utils/tic-toc.ts @@ -5,7 +5,6 @@ export { tic, toc }; let timingStack: [string | undefined, number][] = []; -let i = 0; function tic(label?: string) { if (label) console.log(`${label}... `); diff --git a/src/lib/util/tic-toc.ts b/src/lib/util/tic-toc.ts new file mode 100644 index 0000000000..b9dfd61771 --- /dev/null +++ b/src/lib/util/tic-toc.ts @@ -0,0 +1,18 @@ +/** + * Helper for printing timings, in the spirit of Python's `tic` and `toc`. + */ + +export { tic, toc }; + +let timingStack: [string | undefined, number][] = []; + +function tic(label?: string) { + timingStack.push([label, performance.now()]); +} + +function toc() { + let [label, start] = timingStack.pop()!; + let time = (performance.now() - start) / 1000; + if (label) console.log(`${label}... ${time.toFixed(3)} sec`); + return time; +} diff --git a/tsconfig.test.json b/tsconfig.test.json index 85717a9f8c..bd3694b834 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "include": [ "./src/**/*.unit-test.ts", + "./src/lib/**/*.ts", "./src/snarky.js", "./src/bindings/js/wrapper.js" ], From dcc6eceb216e07039505602174f52a361d3e6952 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Mon, 8 Jan 2024 16:21:23 -0800 Subject: [PATCH 131/524] chore(mina): update mina submodule to f445fd --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 396ae46c0f..f445fdd2fb 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 396ae46c0f301f697c91771bc6780571a7656a45 +Subproject commit f445fdd2fbd756d3437bf501dfd199a046d20965 From 8cda3aeba5ab8e9b165231ab137ce5c3e690b784 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Wed, 17 Jan 2024 10:43:36 -0800 Subject: [PATCH 132/524] chore(bindings, mina): update mina to 69770 and bindings to ba1607 --- src/bindings | 2 +- src/mina | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index 870d7eddfa..ba1607a552 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 870d7eddfa1504a45de7286e51efa00ad37932d9 +Subproject commit ba1607a552b40024aff4dce095ad3448993f13af diff --git a/src/mina b/src/mina index f445fdd2fb..697709dd3c 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit f445fdd2fbd756d3437bf501dfd199a046d20965 +Subproject commit 697709dd3cf1d3626d084edff5523c6e41a3f2ef From 8732c3d5aedbab55fa835926b9d5a7413407e255 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 5 Dec 2023 10:26:46 +0100 Subject: [PATCH 133/524] remove use of build:node --- .github/workflows/build-action.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-action.yml b/.github/workflows/build-action.yml index f2945f9bec..f042184e0f 100644 --- a/.github/workflows/build-action.yml +++ b/.github/workflows/build-action.yml @@ -43,7 +43,7 @@ jobs: run: | git submodule update --init --recursive npm ci - npm run build:node + npm run build touch profiling.md sh run-ci-tests.sh cat profiling.md >> $GITHUB_STEP_SUMMARY @@ -93,7 +93,7 @@ jobs: run: | git submodule update --init --recursive npm ci - npm run build:node + npm run build - name: Publish to NPM if version has changed uses: JS-DevTools/npm-publish@v1 if: github.ref == 'refs/heads/main' From 8a69267ac4c2200851f94d4500beee90cd7ca196 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 5 Dec 2023 08:35:26 +0100 Subject: [PATCH 134/524] simplify build scripts --- run-unit-tests.sh | 3 +-- src/bindings | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/run-unit-tests.sh b/run-unit-tests.sh index 44881eca5d..3e39307f36 100755 --- a/run-unit-tests.sh +++ b/run-unit-tests.sh @@ -2,8 +2,7 @@ set -e shopt -s globstar # to expand '**' into nested directories./ -# run the build:test -npm run build:test +npm run build # find all unit tests in dist/node and run them # TODO it would be nice to make this work on Mac diff --git a/src/bindings b/src/bindings index ba1607a552..ecc3519753 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit ba1607a552b40024aff4dce095ad3448993f13af +Subproject commit ecc3519753aa9cc7aa88697520bd89a97ab5f8ec From 3be4d7fbcc5af9799919300c48342677bc16806e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 09:09:40 +0100 Subject: [PATCH 135/524] start packed provable type --- src/lib/circuit_value.ts | 8 +++++- src/lib/provable-types/fields.ts | 44 +++++++++++++++++++++++++++++++ src/lib/provable-types/packed.ts | 45 ++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/lib/provable-types/fields.ts create mode 100644 src/lib/provable-types/packed.ts diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index d42f267cdf..6720c343cb 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -555,12 +555,18 @@ and Provable.asProver() blocks, which execute outside the proof. ); } - static provable: Provable> = { + static provable: Provable> & { + toInput: (x: Unconstrained) => { + fields?: Field[]; + packed?: [Field, number][]; + }; + } = { sizeInFields: () => 0, toFields: () => [], toAuxiliary: (t?: any) => [t ?? new Unconstrained(false)], fromFields: (_, [t]) => t, check: () => {}, + toInput: () => ({}), }; } diff --git a/src/lib/provable-types/fields.ts b/src/lib/provable-types/fields.ts new file mode 100644 index 0000000000..385c7d0cae --- /dev/null +++ b/src/lib/provable-types/fields.ts @@ -0,0 +1,44 @@ +import { ProvablePureExtended } from '../circuit_value.js'; +import { Field } from '../field.js'; + +export { modifiedField, fields }; + +const zero = new Field(0); + +// provable for a single field element + +const ProvableField: ProvablePureExtended = { + sizeInFields: () => 1, + toFields: (x) => [x], + toAuxiliary: () => [], + fromFields: ([x]) => x, + check: () => {}, + toInput: (x) => ({ fields: [x] }), + toJSON: Field.toJSON, + fromJSON: Field.fromJSON, + empty: () => zero, +}; + +function modifiedField( + methods: Partial> +): ProvablePureExtended { + return Object.assign({}, ProvableField, methods); +} + +// provable for a fixed-size array of field elements + +let id = (t: T) => t; + +function fields(length: number): ProvablePureExtended { + return { + sizeInFields: () => length, + toFields: id, + toAuxiliary: () => [], + fromFields: id, + check: () => {}, + toInput: (x) => ({ fields: x }), + toJSON: (x) => x.map(Field.toJSON), + fromJSON: (x) => x.map(Field.fromJSON), + empty: () => new Array(length).fill(zero), + }; +} diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts new file mode 100644 index 0000000000..f4ee1d8603 --- /dev/null +++ b/src/lib/provable-types/packed.ts @@ -0,0 +1,45 @@ +import { + HashInput, + InferProvable, + ProvableExtended, + Unconstrained, + provable, +} from '../circuit_value.js'; +import { Field } from '../field.js'; +import { Provable } from '../provable.js'; +import { fields } from './fields.js'; + +export { provablePacked, Packed }; + +type Packed = { packed: Field[]; value: Unconstrained }; + +type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; + +function provablePacked>( + type: A +): ProvableHashable>> { + // compute packed size + let input = type.toInput(type.empty()); + let packedSize = countFields(input); + + return provable({ + packed: fields(packedSize), + value: Unconstrained.provable, + }); +} + +function countFields(input: HashInput) { + let n = input.fields?.length ?? 0; + let pendingBits = 0; + + for (let [, bits] of input.packed ?? []) { + pendingBits += bits; + if (pendingBits >= Field.sizeInBits) { + n++; + pendingBits = bits; + } + } + if (pendingBits > 0) n++; + + return n; +} From b8aea8d3a6cfa26073d44b13f6127d3f5d23903a Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 09:11:10 +0100 Subject: [PATCH 136/524] foreign field to input --- src/lib/gadgets/foreign-field.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/gadgets/foreign-field.ts b/src/lib/gadgets/foreign-field.ts index 51b815dfc8..ce90b7947d 100644 --- a/src/lib/gadgets/foreign-field.ts +++ b/src/lib/gadgets/foreign-field.ts @@ -10,6 +10,7 @@ import { Bool } from '../bool.js'; import { Unconstrained } from '../circuit_value.js'; import { Field } from '../field.js'; import { Gates, foreignFieldAdd } from '../gates.js'; +import { modifiedField } from '../provable-types/fields.js'; import { Tuple, TupleN } from '../util/types.js'; import { assertOneOf } from './basic.js'; import { assert, bitSlice, exists, toVar, toVars } from './common.js'; @@ -427,6 +428,12 @@ function equals(x: Field3, c: bigint, f: bigint) { } } +const provableLimb = modifiedField({ + toInput(x) { + return { packed: [[x, Number(l)]] }; + }, +}); + const Field3 = { /** * Turn a bigint into a 3-tuple of Fields @@ -462,7 +469,7 @@ const Field3 = { * Note: Witnessing this creates a plain tuple of field elements without any implicit * range checks. */ - provable: provableTuple([Field, Field, Field]), + provable: provableTuple([provableLimb, provableLimb, provableLimb]), }; type Field2 = [Field, Field]; From a05c9dc6f0ba5d0af4e160f56437864d9679ab9e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 10:02:21 +0100 Subject: [PATCH 137/524] flesh out Packed class --- src/lib/provable-types/packed.ts | 48 ++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index f4ee1d8603..d0e6a01c3e 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -6,18 +6,62 @@ import { provable, } from '../circuit_value.js'; import { Field } from '../field.js'; +import { Poseidon, packToFields } from '../hash.js'; import { Provable } from '../provable.js'; import { fields } from './fields.js'; export { provablePacked, Packed }; -type Packed = { packed: Field[]; value: Unconstrained }; +class Packed { + packed: Field[]; + value: Unconstrained; + type: ProvableExtended; + + private constructor( + packed: Field[], + value: Unconstrained, + type: ProvableExtended + ) { + this.packed = packed; + this.value = value; + this.type = type; + } + + static pack(type: ProvableExtended, x: T): Packed { + let input = type.toInput(x); + let packed = packToFields(input); + return new Packed(packed, Unconstrained.from(x), type); + } + + unpack(): T { + let value = Provable.witness(this.type, () => this.value.get()); + + // prove that the value packs to the packed fields + let input = this.type.toInput(value); + let packed = packToFields(input); + for (let i = 0; i < this.packed.length; i++) { + this.packed[i].assertEquals(packed[i]); + } + + return value; + } + + toFields(): Field[] { + return this.packed; + } + + hash() { + return Poseidon.hash(this.packed); + } +} + +type PackedBase = { packed: Field[]; value: Unconstrained }; type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; function provablePacked>( type: A -): ProvableHashable>> { +): ProvableHashable>> { // compute packed size let input = type.toInput(type.empty()); let packedSize = countFields(input); From 6ea442bd364cd81cec17946c77efcea97e757a88 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 10:42:45 +0100 Subject: [PATCH 138/524] further tweaks to packed --- src/lib/provable-types/packed.ts | 74 +++++++++++++++++++------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index d0e6a01c3e..fb7db86e0e 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -1,43 +1,39 @@ +import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; import { HashInput, - InferProvable, ProvableExtended, Unconstrained, - provable, } from '../circuit_value.js'; import { Field } from '../field.js'; +import { assert } from '../gadgets/common.js'; import { Poseidon, packToFields } from '../hash.js'; import { Provable } from '../provable.js'; import { fields } from './fields.js'; -export { provablePacked, Packed }; +export { Packed }; class Packed { packed: Field[]; value: Unconstrained; - type: ProvableExtended; - private constructor( - packed: Field[], - value: Unconstrained, - type: ProvableExtended - ) { + constructor(packed: Field[], value: Unconstrained) { this.packed = packed; this.value = value; - this.type = type; } - static pack(type: ProvableExtended, x: T): Packed { - let input = type.toInput(x); + static pack(x: T): Packed { + let input = this.innerProvable.toInput(x); let packed = packToFields(input); - return new Packed(packed, Unconstrained.from(x), type); + return new this(packed, Unconstrained.from(x)); } unpack(): T { - let value = Provable.witness(this.type, () => this.value.get()); + let value = Provable.witness(this.Constructor.innerProvable, () => + this.value.get() + ); // prove that the value packs to the packed fields - let input = this.type.toInput(value); + let input = this.Constructor.innerProvable.toInput(value); let packed = packToFields(input); for (let i = 0; i < this.packed.length; i++) { this.packed[i].assertEquals(packed[i]); @@ -53,25 +49,45 @@ class Packed { hash() { return Poseidon.hash(this.packed); } -} -type PackedBase = { packed: Field[]; value: Unconstrained }; + static createProvable( + type: ProvableExtended + ): ProvableHashable> { + let input = type.toInput(type.empty()); + let packedSize = countFields(input); + + return provableFromClass(this, { + packed: fields(packedSize), + value: Unconstrained.provable, + }) as ProvableHashable>; + } -type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; + static _provable: ProvableHashable> | undefined; + static _innerProvable: ProvableExtended | undefined; + + get Constructor(): typeof Packed { + return this.constructor as typeof Packed; + } -function provablePacked>( - type: A -): ProvableHashable>> { - // compute packed size - let input = type.toInput(type.empty()); - let packedSize = countFields(input); - - return provable({ - packed: fields(packedSize), - value: Unconstrained.provable, - }); + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'Packed not initialized'); + return this._provable; + } + static get innerProvable(): ProvableExtended { + assert(this._innerProvable !== undefined, 'Packed not initialized'); + return this._innerProvable; + } + + static create(type: ProvableExtended): typeof Packed { + return class Packed_ extends Packed { + static _provable = Packed_.createProvable(type); + static _innerProvable = type; + }; + } } +type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; + function countFields(input: HashInput) { let n = input.fields?.length ?? 0; let pendingBits = 0; From 08981587b6e996d587107497c56da7528659a7cb Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 10:56:55 +0100 Subject: [PATCH 139/524] document Packed --- src/lib/provable-types/packed.ts | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index fb7db86e0e..52d52604af 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -12,6 +12,39 @@ import { fields } from './fields.js'; export { Packed }; +/** + * Packed is a "packed" representation of any type T. + * + * "Packed" means that field elements which take up fewer than 254 bits are packed together into + * as few field elements as possible. + * + * For example, you can pack several Bools (1 bit) or UInt32s (32 bits) into a single field element. + * + * Using a packed representation can make sense in provable code where the number of constraints + * depends on the number of field elements per value. + * + * For example, `Provable.if(bool, x, y)` takes O(n) constraints, where n is the number of field + * elements in x and y. + * + * Usage: + * + * ```ts + * // define a packed type from a type + * let PackedType = Packed.create(MyType); + * + * // pack a value + * let packed = PackedType.pack(value); + * + * // ... operations on packed values, more efficient than on plain values ... + * + * // unpack a value + * let value = packed.unpack(); + * ``` + * + * **Warning**: Packing only makes sense where packing actually reduces the number of field elements. + * For example, it doesn't make sense to pack a _single_ Bool, because it will be 1 field element before + * and after packing. On the other hand, it does makes sense to pack a type that holds 10 or 20 Bools. + */ class Packed { packed: Field[]; value: Unconstrained; From 60e43ba96ee64392d91d73ad1691b69911ee57f5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 10:59:01 +0100 Subject: [PATCH 140/524] use packing in ecdsa to save 2k constraints --- src/lib/gadgets/elliptic-curve.ts | 15 ++++++++++++++- tests/vk-regression/vk-regression.json | 20 ++++++++++---------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts index 78ca4dbdff..865113fc2f 100644 --- a/src/lib/gadgets/elliptic-curve.ts +++ b/src/lib/gadgets/elliptic-curve.ts @@ -19,6 +19,7 @@ import { provable } from '../circuit_value.js'; import { assertPositiveInteger } from '../../bindings/crypto/non-negative.js'; import { arrayGet, assertBoolean } from './basic.js'; import { sliceField3 } from './bit-slices.js'; +import { Packed } from '../provable-types/packed.js'; // external API export { EllipticCurve, Point, Ecdsa }; @@ -413,6 +414,14 @@ function multiScalarMul( sliceField3(s, { maxBits, chunkSize: windowSizes[i] }) ); + // pack points to make array access more efficient + // a Point is 6 x 88-bit field elements, which are packed into 3 field elements + const PackedPoint = Packed.create(Point.provable); + + let packedTables = tables.map((table) => + table.map((point) => PackedPoint.pack(point)) + ); + ia ??= initialAggregator(Curve); let sum = Point.from(ia); @@ -426,7 +435,11 @@ function multiScalarMul( let sjP = windowSize === 1 ? points[j] - : arrayGetGeneric(Point.provable, tables[j], sj); + : arrayGetGeneric( + PackedPoint.provable, + packedTables[j], + sj + ).unpack(); // ec addition let added = add(sum, sjP, Curve); diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index b112f092a7..acadee1f6b 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -236,29 +236,29 @@ } }, "ecdsa-only": { - "digest": "1e6bef24a2e02b573363f3512822d401d53ec7220c8d5837cd49691c19723028", + "digest": "529de0fe555c4359b6a25097cc0c873736532f64c045155f68aa868517632e4", "methods": { "verifySignedHash": { - "rows": 30680, - "digest": "5fe00efee7ecfb82b3ca69c8dd23d07f" + "rows": 28334, + "digest": "e8bcb74aa4278de6e4c31d9fec724f81" } }, "verificationKey": { - "data": "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", - "hash": "13090090603196708539583054794074329820941412942119534377592583560995629520780" + "data": "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", + "hash": "15135058674307280144061130356720654447302609112077173286437081384990231021958" } }, "ecdsa": { - "digest": "3e80a93d93a93f6152d70a9c21a2979ff0094c2d648a67c6d2daa4b2b0d18309", + "digest": "20942077320476aefd62a3271f60caf17cd442039d7572529bb77315db971d17", "methods": { "verifyEcdsa": { - "rows": 45178, - "digest": "0b6ce4cc658bcca79b1b1373569aa9b9" + "rows": 42832, + "digest": "29cb625b78e1ccf5dc4e897ecf08aca1" } }, "verificationKey": { - "data": "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", - "hash": "25447212082831819715054236631079960883754611880602728284997977929479384060913" + "data": "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", + "hash": "6055195771858836384417585817623837831345819726617689986064045716966540411450" } }, "sha256": { From 44f44c7160f34117804330e8b3acca0bdade272d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 11:02:00 +0100 Subject: [PATCH 141/524] export packed --- CHANGELOG.md | 1 + src/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dadf1323e4..d6795f5e86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 +- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1375 ### Fixed diff --git a/src/index.ts b/src/index.ts index 47d35c815a..57cf62e9ae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,6 +36,7 @@ export { Provable } from './lib/provable.js'; export { Circuit, Keypair, public_, circuitMain } from './lib/circuit.js'; export { UInt32, UInt64, Int64, Sign, UInt8 } from './lib/int.js'; export { Bytes } from './lib/provable-types/provable-types.js'; +export { Packed } from './lib/provable-types/packed.js'; export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; From 7ae44dc120a88b9c91a8366a3f665be9f4d5ac18 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 11:22:49 +0100 Subject: [PATCH 142/524] simplify, prettify --- src/lib/provable-types/packed.ts | 47 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index 52d52604af..2fcb67280d 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -49,17 +49,40 @@ class Packed { packed: Field[]; value: Unconstrained; + /** + * Create a packed representation of `type`. You can then use `PackedType.pack(x)` to pack a value. + */ + static create(type: ProvableExtended): typeof Packed { + // compute size of packed representation + let input = type.toInput(type.empty()); + let packedSize = countFields(input); + + return class Packed_ extends Packed { + static _innerProvable = type; + static _provable = provableFromClass(Packed_, { + packed: fields(packedSize), + value: Unconstrained.provable, + }) as ProvableHashable>; + }; + } + constructor(packed: Field[], value: Unconstrained) { this.packed = packed; this.value = value; } + /** + * Pack a value. + */ static pack(x: T): Packed { let input = this.innerProvable.toInput(x); let packed = packToFields(input); return new this(packed, Unconstrained.from(x)); } + /** + * Unpack a value. + */ unpack(): T { let value = Provable.witness(this.Constructor.innerProvable, () => this.value.get() @@ -79,22 +102,7 @@ class Packed { return this.packed; } - hash() { - return Poseidon.hash(this.packed); - } - - static createProvable( - type: ProvableExtended - ): ProvableHashable> { - let input = type.toInput(type.empty()); - let packedSize = countFields(input); - - return provableFromClass(this, { - packed: fields(packedSize), - value: Unconstrained.provable, - }) as ProvableHashable>; - } - + // dynamic subclassing infra static _provable: ProvableHashable> | undefined; static _innerProvable: ProvableExtended | undefined; @@ -110,13 +118,6 @@ class Packed { assert(this._innerProvable !== undefined, 'Packed not initialized'); return this._innerProvable; } - - static create(type: ProvableExtended): typeof Packed { - return class Packed_ extends Packed { - static _provable = Packed_.createProvable(type); - static _innerProvable = type; - }; - } } type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; From 0df7242bc7c7bb1f4009f19f79ae45773b42e73d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 11:26:26 +0100 Subject: [PATCH 143/524] it's a breaking change actually --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6795f5e86..a080488d7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/08ba27329...HEAD) +### Breaking changes + +- Reduce number of constraints of ECDSA verification by 5%, which breaks deployed contracts using ECDSA https://github.com/o1-labs/o1js/pull/1376 + ### Added - **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 From eb8854c8cc97918ca0c2721938f2b0a2be375a96 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 12:53:04 +0100 Subject: [PATCH 144/524] changelog fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a080488d7a..9d8107dab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 -- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1375 +- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 ### Fixed From 9560cb8c5252345fd2875125d27d5dfa1102745c Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 12:54:44 +0100 Subject: [PATCH 145/524] add `Hashed`, which is analogous to `Packed` --- CHANGELOG.md | 1 + src/index.ts | 2 +- src/lib/provable-types/packed.ts | 100 ++++++++++++++++++++++++++++++- 3 files changed, 101 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d8107dab0..18eb7bca0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 - Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 +- Provable type `Hashed` to represent provable types by their hash https://github.com/o1-labs/o1js/pull/1377 ### Fixed diff --git a/src/index.ts b/src/index.ts index 57cf62e9ae..8fb4e0c9ef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -36,7 +36,7 @@ export { Provable } from './lib/provable.js'; export { Circuit, Keypair, public_, circuitMain } from './lib/circuit.js'; export { UInt32, UInt64, Int64, Sign, UInt8 } from './lib/int.js'; export { Bytes } from './lib/provable-types/provable-types.js'; -export { Packed } from './lib/provable-types/packed.js'; +export { Packed, Hashed } from './lib/provable-types/packed.js'; export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index 2fcb67280d..d3ff8bbd31 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -10,7 +10,7 @@ import { Poseidon, packToFields } from '../hash.js'; import { Provable } from '../provable.js'; import { fields } from './fields.js'; -export { Packed }; +export { Packed, Hashed }; /** * Packed is a "packed" representation of any type T. @@ -137,3 +137,101 @@ function countFields(input: HashInput) { return n; } + +/** + * Hashed represents a type T by its hash. + * + * Since a hash is only a single field element, this can be more efficient in provable code + * where the number of constraints depends on the number of field elements per value. + * + * For example, `Provable.if(bool, x, y)` takes O(n) constraints, where n is the number of field + * elements in x and y. With Hashed, this is reduced to O(1). + * + * The downside is that you will pay the overhead of hashing your values, so it helps to experiment + * in which parts of your code a hashed representation is beneficial. + * + * Usage: + * + * ```ts + * // define a hashed type from a type + * let HashedType = Hashed.create(MyType); + * + * // hash a value + * let hashed = HashedType.hash(value); + * + * // ... operations on hashes, more efficient than on plain values ... + * + * // unhash to get the original value + * let value = hashed.unhash(); + * ``` + */ +class Hashed { + hash: Field; + value: Unconstrained; + + /** + * Create a hashed representation of `type`. You can then use `HashedType.hash(x)` to wrap a value in a `Hashed`. + */ + static create(type: ProvableExtended): typeof Hashed { + return class Hashed_ extends Hashed { + static _innerProvable = type; + static _provable = provableFromClass(Hashed_, { + hash: Field, + value: Unconstrained.provable, + }) as ProvableHashable>; + }; + } + + constructor(hash: Field, value: Unconstrained) { + this.hash = hash; + this.value = value; + } + + /** + * Wrap a value, and represent it by its hash in provable code. + */ + static hash(x: T): Hashed { + let input = this.innerProvable.toInput(x); + let packed = packToFields(input); + let hash = Poseidon.hash(packed); + return new this(hash, Unconstrained.from(x)); + } + + /** + * Unwrap a value from its hashed variant. + */ + unhash(): T { + let value = Provable.witness(this.Constructor.innerProvable, () => + this.value.get() + ); + + // prove that the value hashes to the hash + let input = this.Constructor.innerProvable.toInput(value); + let packed = packToFields(input); + let hash = Poseidon.hash(packed); + this.hash.assertEquals(hash); + + return value; + } + + toFields(): Field[] { + return [this.hash]; + } + + // dynamic subclassing infra + static _provable: ProvableHashable> | undefined; + static _innerProvable: ProvableExtended | undefined; + + get Constructor(): typeof Hashed { + return this.constructor as typeof Hashed; + } + + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'Hashed not initialized'); + return this._provable; + } + static get innerProvable(): ProvableExtended { + assert(this._innerProvable !== undefined, 'Hashed not initialized'); + return this._innerProvable; + } +} From 66680a875c83cd8b7e96ff6d41ff496b31589e98 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 12:57:43 +0100 Subject: [PATCH 146/524] use hashed instead of packed in ecdsa for tiny improvement --- src/lib/gadgets/ecdsa.unit-test.ts | 2 +- src/lib/gadgets/elliptic-curve.ts | 16 ++++++++-------- tests/vk-regression/vk-regression.json | 20 ++++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index b03266b1a2..250b088709 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -129,7 +129,7 @@ let msgHash = ); const ia = initialAggregator(Secp256k1); -const config = { G: { windowSize: 4 }, P: { windowSize: 3 }, ia }; +const config = { G: { windowSize: 4 }, P: { windowSize: 4 }, ia }; let program = ZkProgram({ name: 'ecdsa', diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts index 865113fc2f..9dbce8e544 100644 --- a/src/lib/gadgets/elliptic-curve.ts +++ b/src/lib/gadgets/elliptic-curve.ts @@ -19,7 +19,7 @@ import { provable } from '../circuit_value.js'; import { assertPositiveInteger } from '../../bindings/crypto/non-negative.js'; import { arrayGet, assertBoolean } from './basic.js'; import { sliceField3 } from './bit-slices.js'; -import { Packed } from '../provable-types/packed.js'; +import { Hashed } from '../provable-types/packed.js'; // external API export { EllipticCurve, Point, Ecdsa }; @@ -228,7 +228,7 @@ function verifyEcdsa( G?: { windowSize: number; multiples?: Point[] }; P?: { windowSize: number; multiples?: Point[] }; ia?: point; - } = { G: { windowSize: 4 }, P: { windowSize: 3 } } + } = { G: { windowSize: 4 }, P: { windowSize: 4 } } ) { // constant case if ( @@ -416,10 +416,10 @@ function multiScalarMul( // pack points to make array access more efficient // a Point is 6 x 88-bit field elements, which are packed into 3 field elements - const PackedPoint = Packed.create(Point.provable); + const HashedPoint = Hashed.create(Point.provable); - let packedTables = tables.map((table) => - table.map((point) => PackedPoint.pack(point)) + let hashedTables = tables.map((table) => + table.map((point) => HashedPoint.hash(point)) ); ia ??= initialAggregator(Curve); @@ -436,10 +436,10 @@ function multiScalarMul( windowSize === 1 ? points[j] : arrayGetGeneric( - PackedPoint.provable, - packedTables[j], + HashedPoint.provable, + hashedTables[j], sj - ).unpack(); + ).unhash(); // ec addition let added = add(sum, sjP, Curve); diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index acadee1f6b..2697fed274 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -236,29 +236,29 @@ } }, "ecdsa-only": { - "digest": "529de0fe555c4359b6a25097cc0c873736532f64c045155f68aa868517632e4", + "digest": "2a2beadf929015559514abb88dd77694dbe62a6a5904e5a9d05ab21a3f293c0c", "methods": { "verifySignedHash": { - "rows": 28334, - "digest": "e8bcb74aa4278de6e4c31d9fec724f81" + "rows": 28186, + "digest": "dd43cd30a8277b5af02fd85a4762a488" } }, "verificationKey": { - "data": "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", - "hash": "15135058674307280144061130356720654447302609112077173286437081384990231021958" + "data": "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", + "hash": "16626558875595050675741741208497473516532634598762284173771479275503819571624" } }, "ecdsa": { - "digest": "20942077320476aefd62a3271f60caf17cd442039d7572529bb77315db971d17", + "digest": "2f78a9c0307e6f9c6872499c3ca7c412f5771a7ac7e7e88c351ed1b62d6ac408", "methods": { "verifyEcdsa": { - "rows": 42832, - "digest": "29cb625b78e1ccf5dc4e897ecf08aca1" + "rows": 42684, + "digest": "2bb042b0fa7bbbb32f7e77d392f43d2c" } }, "verificationKey": { - "data": "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", - "hash": "6055195771858836384417585817623837831345819726617689986064045716966540411450" + "data": "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", + "hash": "13907522711254991952405567976395529829664716172124500348498751038796408381729" } }, "sha256": { From 75696efac59e72551d24c767769187e79dd16a45 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 18 Jan 2024 13:06:02 +0100 Subject: [PATCH 147/524] minor --- src/examples/crypto/ecdsa/ecdsa.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/examples/crypto/ecdsa/ecdsa.ts b/src/examples/crypto/ecdsa/ecdsa.ts index bebe6484fb..d681a8037e 100644 --- a/src/examples/crypto/ecdsa/ecdsa.ts +++ b/src/examples/crypto/ecdsa/ecdsa.ts @@ -4,7 +4,6 @@ import { createEcdsa, createForeignCurve, Bool, - Keccak, Bytes, } from 'o1js'; From 0a069c6d90cc03e12a6c20f6a1f363f65aa60016 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 18 Jan 2024 08:52:43 -0800 Subject: [PATCH 148/524] chore(bindings): update submodule to commit 5ed3f4 --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index ecc3519753..5ed3f47ab9 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit ecc3519753aa9cc7aa88697520bd89a97ab5f8ec +Subproject commit 5ed3f47ab9446422280aa04fe748bc72828bc0f1 From bb29f333b275e569febffb83d6a02936fbe75e43 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 18 Jan 2024 15:56:33 -0800 Subject: [PATCH 149/524] chore(mina): update submodule to commit 94ae65 --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 2a968c8347..94ae65b1d4 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 2a968c83477ed9f9e3b30a02cc357e541b76dcac +Subproject commit 94ae65b1d4d25459412fa34e7d1b51a7b991a307 From 50d250413fe102e8fff9fd7f30b5a23285a3f776 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 18 Jan 2024 16:46:48 -0800 Subject: [PATCH 150/524] docs(README-dev.md): restructure testing section and add debugging instructions - Refactor 'Test zkApps against the local blockchain network' section under a new 'Testing and Debugging' section for better organization. - Add 'Profiling o1js' section with instructions on how to use the 'run-debug' script and Chrome Debugger for performance profiling and debugging. This provides developers with more tools and guidance for testing and optimizing their code. --- README-dev.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README-dev.md b/README-dev.md index c81b561d08..2c459de44c 100644 --- a/README-dev.md +++ b/README-dev.md @@ -147,7 +147,9 @@ act -j Build-And-Test-Server --matrix test_type:"Simple integration tests" -s $G To release a new version of o1js, you must first update the version number in `package.json`. Then, you can create a new pull request to merge your changes into the main branch. Once the pull request is merged, a CI job will automatically publish the new version to npm. -## Test zkApps against the local blockchain network +## Testing and Debugging + +### Test zkApps against the local blockchain network In order to be able to test zkApps against the local blockchain network, you need to spin up such a network first. You can do so in several ways. @@ -184,3 +186,19 @@ You can do so in several ways. Next up, you will need the Mina blockchain accounts information in order to be used in your zkApp. Once the local network is up and running, you can use the [Lightnet](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/lib/fetch.ts#L1012) `o1js API namespace` to get the accounts information. The corresponding example can be found here: [src/examples/zkapps/hello_world/run_live.ts](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/examples/zkapps/hello_world/run_live.ts) + +### Profiling o1js + +To enhance the development experience and optimize the performance of o1js, we provide a setup that allows you to use the Chrome Debugger alongside Node.js. This setup is particularly useful when you want to profile the performance of your zkApp or o1js. + +#### Using the `run-debug` script + +We have included a script named `run-debug` to facilitate this process. To use this script, run: + +```sh +./run-debug --bundle +``` + +This script initializes a Node.js process with the `--inspect-brk` flag, which starts the Node.js inspector and breaks before the user script starts (i.e., it pauses execution until a debugger is attached). The `--enable-source-maps` flag ensures that source maps are utilized, allowing easy debugging of o1js code directly. + +Once the Node.js process is running, you can open the Chrome browser and navigate to `chrome://inspect` to attach the Chrome Debugger to the Node.js process. This will allow you to set breakpoints, inspect variables, and profile the performance of your zkApp or o1js. For more information on how to use the Chrome Debugger, see the [official documentation](https://developer.chrome.com/docs/devtools/). From b2fadfe2f8bfc38f90ede5bb0adfbca35a69e328 Mon Sep 17 00:00:00 2001 From: barriebyron Date: Fri, 19 Jan 2024 09:07:11 -0500 Subject: [PATCH 151/524] edits for Lightnet terminology and other style fixes --- README-dev.md | 75 +++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/README-dev.md b/README-dev.md index 2c459de44c..2201c5d2c0 100644 --- a/README-dev.md +++ b/README-dev.md @@ -31,52 +31,52 @@ npm install npm run build ``` -This will compile the TypeScript source files, making it ready for use. The compiled OCaml and WebAssembly artifacts are version-controlled to simplify the build process for end-users. These artifacts are stored under `src/bindings/compiled`, and contain the artifacts needed for both node and web builds. These files do not have to be regenerated unless there are changes to the OCaml or Rust source files. +This command compiles the TypeScript source files, making them ready for use. The compiled OCaml and WebAssembly artifacts are version-controlled to simplify the build process for end users. These artifacts are stored under `src/bindings/compiled` and contain the artifacts needed for both node and web builds. These files only have to be regenerated if there are changes to the OCaml or Rust source files. ## Building Bindings -If you need to regenerate the OCaml and WebAssembly artifacts, you can do so within the o1js repo. The [bindings](https://github.com/o1-labs/o1js-bindings) and [Mina](https://github.com/MinaProtocol/mina) repos are both submodules of o1js, so you can build them from within the o1js repo. +To regenerate the OCaml and WebAssembly artifacts, you can do so within the o1js repo. The [bindings](https://github.com/o1-labs/o1js-bindings) and [Mina](https://github.com/MinaProtocol/mina) repos are both submodules of o1js so you can build them from within the o1js repo. -o1js depends on OCaml code that is transpiled to JavaScript using [Js_of_ocaml](https://github.com/ocsigen/js_of_ocaml), and Rust code that is transpiled to WebAssembly using [wasm-pack](https://github.com/rustwasm/wasm-pack). These artifacts allow o1js to call into [Pickles](https://github.com/MinaProtocol/mina/blob/develop/src/lib/pickles/README.md), [snarky](https://github.com/o1-labs/snarky), and [Kimchi](https://github.com/o1-labs/proof-systems) to write zk-SNARKs and zkApps. +o1js depends on OCaml code that is transpiled to JavaScript using [Js_of_ocaml](https://github.com/ocsigen/js_of_ocaml) and Rust code that is transpiled to WebAssembly using [wasm-pack](https://github.com/rustwasm/wasm-pack). These artifacts allow o1js to call into [Pickles](https://github.com/MinaProtocol/mina/blob/develop/src/lib/pickles/README.md), [snarky](https://github.com/o1-labs/snarky), and [Kimchi](https://github.com/o1-labs/proof-systems) to write zk-SNARKs and zkApps. -The compiled artifacts are stored under `src/bindings/compiled`, and are version-controlled to simplify the build process for end-users. +The compiled artifacts are stored under `src/bindings/compiled` and are version-controlled to simplify the build process for end-users. -If you wish to rebuild the OCaml and Rust artifacts, you must be able to build the Mina repo before building the bindings. See the [Mina Dev Readme](https://github.com/MinaProtocol/mina/blob/develop/README-dev.md) for more information. Once you have configured your environment to build Mina, you can build the bindings: +If you want to rebuild the OCaml and Rust artifacts, you must be able to build the mina repo before building the bindings. See the [Mina Dev Readme](https://github.com/MinaProtocol/mina/blob/develop/README-dev.md) for more information. After you have configured your environment to build mina, you can build the bindings: ```sh npm run build:update-bindings ``` -This will build the OCaml and Rust artifacts, and copy them to the `src/bindings/compiled` directory. +This command builds the OCaml and Rust artifacts and copies them to the `src/bindings/compiled` directory. ### Build Scripts -The root build script which kicks off the build process is under `src/bindings/scripts/update-snarkyjs-bindings.sh`. This script is responsible for building the Node.js and web artifacts for o1js, and places them under `src/bindings/compiled`, to be used by o1js. +The root build script which kicks off the build process is under `src/bindings/scripts/update-snarkyjs-bindings.sh`. This script is responsible for building the Node.js and web artifacts for o1js and places them under `src/bindings/compiled` to be used by o1js. ### OCaml Bindings o1js depends on Pickles, snarky, and parts of the Mina transaction logic, all of which are compiled to JavaScript and stored as artifacts to be used by o1js natively. The OCaml bindings are located under `src/bindings`. See the [OCaml Bindings README](https://github.com/o1-labs/o1js-bindings/blob/main/README.md) for more information. -To compile the OCaml code, a build tool called Dune is used. Dune is a build system for OCaml projects, and is used in addition with Js_of_ocaml to compile the OCaml code to JavaScript. The dune file that is responsible for compiling the OCaml code is located under `src/bindings/ocaml/dune`. There are two build targets: `snarky_js_node` and `snarky_js_web`, which compile the Mina dependencies as well as link the wasm artifacts to build the Node.js and web artifacts, respectively. The output file is `snark_js_node.bc.js`, which is used by o1js. +To compile the OCaml code, a build tool called Dune is used. Dune is a build system for OCaml projects and is used with Js_of_ocaml to compile the OCaml code to JavaScript. The Dune file responsible for compiling the OCaml code is located under `src/bindings/ocaml/dune`. Two build targets, `snarky_js_node` and `snarky_js_web`, compile the Mina dependencies and link the Wasm artifacts to build the Node.js and web artifacts, respectively. The output file is `snark_js_node.bc.js`, which is used by o1js. ### WebAssembly Bindings -o1js additionally depends on Kimchi, which is compiled to WebAssembly. Kimchi is located in the Mina repo, under `src/mina`. See the [Kimchi README](https://github.com/o1-labs/proof-systems/blob/master/README.md) for more information. +o1js additionally depends on Kimchi, which is compiled to WebAssembly. Kimchi is located in the Mina repo under `src/mina`. See the [Kimchi README](https://github.com/o1-labs/proof-systems/blob/master/README.md) for more information. -To compile the wasm code, a combination of Cargo and Dune is used. Both build files are located under `src/mina/src/lib/crypto/kimchi`, where the `wasm` folder contains the Rust code which is compiled to wasm, and the `js` folder which contains a wrapper around the wasm code which allows Js_of_ocaml to compile against the wasm backend. +To compile the Wasm code, a combination of Cargo and Dune is used. Both build files are located under `src/mina/src/lib/crypto/kimchi`, where the `wasm` folder contains the Rust code that is compiled to Wasm, and the `js` folder that contains a wrapper around the Wasm code which allows Js_of_ocaml to compile against the Wasm backend. -For the wasm build, the output files are: +For the Wasm build, the output files are: - `plonk_wasm_bg.wasm`: The compiled WebAssembly binary. - `plonk_wasm_bg.wasm.d.ts`: TypeScript definition files describing the types of .wasm or .js files. -- `plonk_wasm.js`: JavaScript file that wraps the WASM code for use in Node.js. +- `plonk_wasm.js`: JavaScript file that wraps the Wasm code for use in Node.js. - `plonk_wasm.d.ts`: TypeScript definition file for plonk_wasm.js. ### Generated Constant Types -In addition to building the OCaml and Rust code, the build script also generates TypeScript types for constants used in the Mina protocol. These types are generated from the OCaml source files, and are located under `src/bindings/crypto/constants.ts` and `src/bindings/mina-transaction/gen`. When building the bindings, these constants are auto-generated by Dune. If you wish to add a new constant, you can edit the `src/bindings/ocaml/snarky_js_constants` file, and then run `npm run build:bindings` to regenerate the TypeScript files. +In addition to building the OCaml and Rust code, the build script also generates TypeScript types for constants used in the Mina protocol. These types are generated from the OCaml source files and are located under `src/bindings/crypto/constants.ts` and `src/bindings/mina-transaction/gen`. When building the bindings, these constants are auto-generated by Dune. If you want to add a new constant, edit the `src/bindings/ocaml/snarky_js_constants` file and then run `npm run build:bindings` to regenerate the TypeScript files. -These types are used by o1js to ensure that the constants used in the protocol are consistent with the OCaml source files. +o1js uses these types to ensure that the constants used in the protocol are consistent with the OCaml source files. ## Development @@ -88,7 +88,7 @@ If you work on o1js, create a feature branch off of one of these base branches. **Default to `main` as the base branch**. -The other base branches (`berkeley`, `develop`) are only used in specific scenarios where you want to adapt o1js to changes in the sibling repos on those other branches. Even then, consider whether it is feasible to land your changes to `main` and merge to `berkeley` and `develop` afterwards. Only changes in `main` will ever be released, so anything in the other branches has to be backported and reconciled with `main` eventually. +The other base branches (`berkeley` and `develop`) are used only in specific scenarios where you want to adapt o1js to changes in the sibling repos on those other branches. Even then, consider whether it is feasible to land your changes to `main` and merge to `berkeley` and `develop` afterwards. Only changes in `main` will ever be released, so anything in the other branches has to be backported and reconciled with `main` eventually. | Repository | mina -> o1js -> o1js-bindings | | ---------- | -------------------------------- | @@ -96,13 +96,13 @@ The other base branches (`berkeley`, `develop`) are only used in specific scenar | | berkeley -> berkeley -> berkeley | | | develop -> develop -> develop | -- `o1js-main`: The o1js-main branch in the Mina repository corresponds to the main branch in both o1js and o1js-bindings repositories. This is where stable releases and ramp-up features are maintained. The o1js-main branch runs in parallel to the Mina `berkeley` branch and does not have a subset or superset relationship with it. The branching structure is as follows (<- means direction to merge): +- `o1js-main`: The `o1js-main` branch in the Mina repository corresponds to the `main` branch in both o1js and o1js-bindings repositories. This branch is where stable releases and ramp-up features are maintained. The `o1js-main` branch runs in parallel to the Mina `berkeley` branch and does not have a subset or superset relationship with it. The branching structure is as follows (<- means direction to merge): - - `develop` <- `o1js-main` <- `current testnet` - Typically, the current testnet often corresponds to the rampup branch. + - `develop` <- `o1js-main` <- `current testnet` - Typically, the current Testnet often corresponds to the rampup branch. -- `berkeley`: The berkeley branch is maintained across all three repositories. This branch is used for features and updates specific to the Berkeley release of the project. +- `berkeley`: The `berkeley` branch is maintained across all three repositories. This branch is used for features and updates specific to the Berkeley release of the project. -- `develop`: The develop branch is also maintained across all three repositories. It is used for ongoing development, testing new features, and integration work. +- `develop`: The `develop` branch is also maintained across all three repositories. It is used for ongoing development, testing new features, and integration work. ### Running Tests @@ -113,15 +113,15 @@ npm run test npm run test:unit ``` -This will run all the unit tests and provide you with a summary of the test results. +This runs all the unit tests and provides you with a summary of the test results. -You can additionally run integration tests by running: +You can also run integration tests by running: ```sh npm run test:integration ``` -Finally, we have a set of end-to-end tests that run against the browser. These tests are not run by default, but you can run them by running: +Finally, a set of end-to-end tests are run against the browser. These tests are not run by default, but you can run them by running: ```sh npm install @@ -137,7 +137,7 @@ npm run e2e:show-report -You can execute the CI locally by using [act](https://github.com/nektos/act). First generate a GitHub token and use: +You can execute the CI locally by using [act](https://github.com/nektos/act). First, generate a GitHub token and use: ```sh act -j Build-And-Test-Server --matrix test_type:"Simple integration tests" -s $GITHUB_TOKEN @@ -145,16 +145,15 @@ act -j Build-And-Test-Server --matrix test_type:"Simple integration tests" -s $G ### Releasing -To release a new version of o1js, you must first update the version number in `package.json`. Then, you can create a new pull request to merge your changes into the main branch. Once the pull request is merged, a CI job will automatically publish the new version to npm. +To release a new version of o1js, you must first update the version number in `package.json`. Then, you can create a new pull request to merge your changes into the main branch. After the pull request is merged, a CI job automatically publishes the new version to npm. ## Testing and Debugging -### Test zkApps against the local blockchain network +### Test zkApps against Lightnet network -In order to be able to test zkApps against the local blockchain network, you need to spin up such a network first. -You can do so in several ways. +Use the lightweight Mina blockchain network (Lightnet) to test on a local blockchain before you test with a live network. To test zkApps against the local blockchain, first spin up Lightnet. -1. Using [zkapp-cli](https://www.npmjs.com/package/zkapp-cli)'s sub commands: +The easiest way is to use [zkApp CLI](https://www.npmjs.com/package/zkapp-cli) sub-commands: ```shell zk lightnet start # start the local network @@ -164,9 +163,9 @@ You can do so in several ways. zk lightnet stop # stop the local network ``` - Please refer to `zk lightnet --help` for more information. + Use `zk lightnet --help` for more information. -2. Using the corresponding [Docker image](https://hub.docker.com/r/o1labs/mina-local-network) manually: +You can also use the corresponding [Docker image](https://hub.docker.com/r/o1labs/mina-local-network) manually: ```shell docker run --rm --pull=missing -it \ @@ -181,24 +180,24 @@ You can do so in several ways. o1labs/mina-local-network:o1js-main-latest-lightnet ``` - Please refer to the [Docker Hub repository](https://hub.docker.com/r/o1labs/mina-local-network) for more information. + See the [Docker Hub repository](https://hub.docker.com/r/o1labs/mina-local-network) for more information. -Next up, you will need the Mina blockchain accounts information in order to be used in your zkApp. -Once the local network is up and running, you can use the [Lightnet](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/lib/fetch.ts#L1012) `o1js API namespace` to get the accounts information. -The corresponding example can be found here: [src/examples/zkapps/hello_world/run_live.ts](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/examples/zkapps/hello_world/run_live.ts) +Next up, get the Mina blockchain accounts information to be used in your zkApp. +After the local network is up and running, you can use the [Lightnet](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/lib/fetch.ts#L1012) `o1js API namespace` to get the accounts information. +See the corresponding example in [src/examples/zkapps/hello_world/run_live.ts](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/examples/zkapps/hello_world/run_live.ts). ### Profiling o1js -To enhance the development experience and optimize the performance of o1js, we provide a setup that allows you to use the Chrome Debugger alongside Node.js. This setup is particularly useful when you want to profile the performance of your zkApp or o1js. +To enhance the development experience and optimize the performance of o1js, use the Chrome Debugger alongside Node.js. This setup is particularly useful when you want to profile the performance of your zkApp or o1js. #### Using the `run-debug` script -We have included a script named `run-debug` to facilitate this process. To use this script, run: +To facilitate this process, use the provided script named `run-debug`. To use this script, run: ```sh ./run-debug --bundle ``` -This script initializes a Node.js process with the `--inspect-brk` flag, which starts the Node.js inspector and breaks before the user script starts (i.e., it pauses execution until a debugger is attached). The `--enable-source-maps` flag ensures that source maps are utilized, allowing easy debugging of o1js code directly. +This script initializes a Node.js process with the `--inspect-brk` flag that starts the Node.js inspector and breaks before the user script starts (i.e., it pauses execution until a debugger is attached). The `--enable-source-maps` flag ensures that source maps are used to allow easy debugging of o1js code directly. -Once the Node.js process is running, you can open the Chrome browser and navigate to `chrome://inspect` to attach the Chrome Debugger to the Node.js process. This will allow you to set breakpoints, inspect variables, and profile the performance of your zkApp or o1js. For more information on how to use the Chrome Debugger, see the [official documentation](https://developer.chrome.com/docs/devtools/). +After the Node.js process is running, open the Chrome browser and navigate to `chrome://inspect` to attach the Chrome Debugger to the Node.js process. You can set breakpoints, inspect variables, and profile the performance of your zkApp or o1js. For more information on using the Chrome Debugger, see the [DevTools documentation](https://developer.chrome.com/docs/devtools/). From 1cdaa3882d3f495c467e9af771f5507fe6a3d0ab Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Fri, 19 Jan 2024 09:38:36 -0800 Subject: [PATCH 152/524] chore(mina): update minda to 4d8161 --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 697709dd3c..4d81614a94 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 697709dd3cf1d3626d084edff5523c6e41a3f2ef +Subproject commit 4d81614a94ee68ae9afe350e46dbb878852695a7 From e565337e083f024552cbc781a6f0f17661ebcd4b Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 20 Jan 2024 14:11:22 +0200 Subject: [PATCH 153/524] Refactoring. --- src/examples/fetch_live.ts | 141 ++++++++++-------- .../zkapps/dex/arbitrary_token_interaction.ts | 9 +- src/examples/zkapps/dex/dex.ts | 4 +- src/examples/zkapps/dex/erc20.ts | 2 +- .../zkapps/dex/happy-path-with-actions.ts | 4 +- .../zkapps/dex/happy-path-with-proofs.ts | 4 +- src/examples/zkapps/dex/run.ts | 16 +- src/examples/zkapps/dex/run_live.ts | 6 +- src/examples/zkapps/dex/upgradability.ts | 18 ++- src/lib/account_update.ts | 2 +- src/lib/caller.unit-test.ts | 2 +- src/lib/mina.ts | 49 +++--- src/lib/precondition.ts | 8 +- src/lib/token.test.ts | 6 +- 14 files changed, 162 insertions(+), 109 deletions(-) diff --git a/src/examples/fetch_live.ts b/src/examples/fetch_live.ts index f42b187504..85ce4b8d4d 100644 --- a/src/examples/fetch_live.ts +++ b/src/examples/fetch_live.ts @@ -2,72 +2,91 @@ import { expect } from 'expect'; import { Lightnet, Mina, PrivateKey, UInt64, fetchAccount } from 'o1js'; const useCustomLocalNetwork = process.env.USE_CUSTOM_LOCAL_NETWORK === 'true'; -const minaGraphQlEndpoint = useCustomLocalNetwork - ? 'http://localhost:8080/graphql' - : 'https://berkeley.minascan.io/graphql'; -const network = Mina.Network({ - mina: minaGraphQlEndpoint, - archive: useCustomLocalNetwork - ? 'http://localhost:8282' - : 'https://api.minascan.io/archive/berkeley/v1/graphql', - lightnetAccountManager: 'http://localhost:8181', -}); -Mina.setActiveInstance(network); +Mina.setActiveInstance(configureMinaNetwork()); const transactionFee = 100_000_000; +let defaultNetworkConstants: Mina.NetworkConstants = { + genesisTimestamp: UInt64.from(0), + slotTime: UInt64.from(3 * 60 * 1000), + accountCreationFee: UInt64.from(1_000_000_000), +}; +const { sender } = await configureFeePayer(); + +await checkDefaultNetworkConstantsFetching(); +await checkActualNetworkConstantsFetching(); + +await tearDown(); -// Fee payer setup -console.log(''); -const senderKey = useCustomLocalNetwork - ? (await Lightnet.acquireKeyPair()).privateKey - : PrivateKey.random(); -const sender = senderKey.toPublicKey(); -if (!useCustomLocalNetwork) { - console.log(`Funding the fee payer account.`); - await Mina.faucet(sender); +async function checkDefaultNetworkConstantsFetching() { + console.log( + '\nCase #1: check that default network constants can be fetched outside of the transaction scope.' + ); + const networkConstants = Mina.getNetworkConstants(); + expect(defaultNetworkConstants).toEqual(networkConstants); + logNetworkConstants(networkConstants); } -console.log(`Fetching the fee payer account information.`); -const accountDetails = (await fetchAccount({ publicKey: sender })).account; -console.log( - `Using the fee payer account ${sender.toBase58()} with nonce: ${ - accountDetails?.nonce - } and balance: ${accountDetails?.balance}.` -); -console.log(''); -console.log( - "Check that network constants CAN'T be fetched outside of a transaction." -); -const getNetworkConstants = () => { - Mina.activeInstance.getNetworkConstants(); -}; -expect(getNetworkConstants).toThrow( - `getNetworkConstants: Could not fetch network constants from graphql endpoint ${minaGraphQlEndpoint} outside of a transaction.` -); +async function checkActualNetworkConstantsFetching() { + console.log( + '\nCase #2: check that actual network constants can be fetched within the transaction scope.' + ); + let networkConstants: Mina.NetworkConstants | undefined; + await Mina.transaction({ sender, fee: transactionFee }, () => { + networkConstants = Mina.getNetworkConstants(); + }); + expect(networkConstants?.slotTime).not.toBeUndefined(); + expect(networkConstants?.genesisTimestamp).not.toBeUndefined(); + expect(defaultNetworkConstants).not.toEqual(networkConstants); + logNetworkConstants(networkConstants); +} -console.log(''); -console.log( - 'Check that network constants CAN be fetched within the transaction.' -); -let slotTime: UInt64 | undefined; -let genesisTimestamp: UInt64 | undefined; -await Mina.transaction({ sender, fee: transactionFee }, () => { - const networkConstants = Mina.activeInstance.getNetworkConstants(); - slotTime = networkConstants.slotTime; - genesisTimestamp = networkConstants.genesisTimestamp; -}); +function configureMinaNetwork() { + const minaGraphQlEndpoint = useCustomLocalNetwork + ? 'http://localhost:8080/graphql' + : 'https://berkeley.minascan.io/graphql'; + return Mina.Network({ + mina: minaGraphQlEndpoint, + archive: useCustomLocalNetwork + ? 'http://localhost:8282' + : 'https://api.minascan.io/archive/berkeley/v1/graphql', + lightnetAccountManager: 'http://localhost:8181', + }); +} -expect(slotTime).not.toBeUndefined(); -expect(genesisTimestamp).not.toBeUndefined(); +async function configureFeePayer() { + const senderKey = useCustomLocalNetwork + ? (await Lightnet.acquireKeyPair()).privateKey + : PrivateKey.random(); + const sender = senderKey.toPublicKey(); + if (!useCustomLocalNetwork) { + console.log(`\nFunding the fee payer account.`); + await Mina.faucet(sender); + } + console.log(`\nFetching the fee payer account information.`); + const accountDetails = (await fetchAccount({ publicKey: sender })).account; + console.log( + `Using the fee payer account ${sender.toBase58()} with nonce: ${ + accountDetails?.nonce + } and balance: ${accountDetails?.balance}.` + ); + return { sender, senderKey }; +} -console.log(`Slot time: ${slotTime}`); -console.log(`Genesis timestamp: ${genesisTimestamp}`); -console.log( - `Genesis date: ${new Date(Number(genesisTimestamp?.toString() ?? '0'))}` -); +async function tearDown() { + const keyPairReleaseMessage = await Lightnet.releaseKeyPair({ + publicKey: sender.toBase58(), + }); + if (keyPairReleaseMessage) console.info('\n' + keyPairReleaseMessage); +} -// Tear down -console.log(''); -const keyPairReleaseMessage = await Lightnet.releaseKeyPair({ - publicKey: sender.toBase58(), -}); -if (keyPairReleaseMessage) console.info(keyPairReleaseMessage); +function logNetworkConstants( + networkConstants: Mina.NetworkConstants | undefined +) { + console.log(`Account creation fee: ${networkConstants?.accountCreationFee}`); + console.log(`Slot time: ${networkConstants?.slotTime}`); + console.log(`Genesis timestamp: ${networkConstants?.genesisTimestamp}`); + console.log( + `Genesis date: ${new Date( + Number(networkConstants?.genesisTimestamp.toString() ?? '0') + )}` + ); +} diff --git a/src/examples/zkapps/dex/arbitrary_token_interaction.ts b/src/examples/zkapps/dex/arbitrary_token_interaction.ts index aaf7d81555..182beb95fa 100644 --- a/src/examples/zkapps/dex/arbitrary_token_interaction.ts +++ b/src/examples/zkapps/dex/arbitrary_token_interaction.ts @@ -4,7 +4,6 @@ import { TokenContract, addresses, keys, tokenIds } from './dex.js'; let doProofs = true; let Local = Mina.LocalBlockchain({ proofsEnabled: doProofs }); Mina.setActiveInstance(Local); -let accountFee = Mina.accountCreationFee(); let [{ privateKey: userKey, publicKey: userAddress }] = Local.testAccounts; let tx; @@ -26,7 +25,9 @@ console.log('deploy & init token contracts...'); tx = await Mina.transaction(userAddress, () => { // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves let feePayerUpdate = AccountUpdate.createSigned(userAddress); - feePayerUpdate.balance.subInPlace(accountFee.mul(1)); + feePayerUpdate.balance.subInPlace( + Mina.getNetworkConstants().accountCreationFee.mul(1) + ); tokenX.deploy(); }); await tx.prove(); @@ -36,7 +37,9 @@ await tx.send(); console.log('arbitrary token minting...'); tx = await Mina.transaction(userAddress, () => { // pay fees for creating user's token X account - AccountUpdate.createSigned(userAddress).balance.subInPlace(accountFee.mul(1)); + AccountUpdate.createSigned(userAddress).balance.subInPlace( + Mina.getNetworkConstants().accountCreationFee.mul(1) + ); // 😈😈😈 mint any number of tokens to our account 😈😈😈 let tokenContract = new TokenContract(addresses.tokenX); tokenContract.token.mint({ diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 8bf5a1ea8f..0db07a690a 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -394,7 +394,7 @@ class TokenContract extends SmartContract { // assert that the receiving account is new, so this can be only done once receiver.account.isNew.requireEquals(Bool(true)); // pay fees for opened account - this.balance.subInPlace(Mina.accountCreationFee()); + this.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); } /** @@ -410,7 +410,7 @@ class TokenContract extends SmartContract { // assert that the receiving account is new, so this can be only done once receiver.account.isNew.requireEquals(Bool(true)); // pay fees for opened account - this.balance.subInPlace(Mina.accountCreationFee()); + this.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); } // this is a very standardized deploy method. instead, we could also take the account update from a callback diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index b18ba43dea..9648640f8a 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -85,7 +85,7 @@ class TrivialCoin extends SmartContract implements Erc20 { // assert that the receiving account is new, so this can be only done once receiver.account.isNew.requireEquals(Bool(true)); // pay fees for opened account - this.balance.subInPlace(Mina.accountCreationFee()); + this.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); // since this is the only method of this zkApp that resets the entire state, provedState: true implies // that this function was run. Since it can be run only once, this implies it was run exactly once diff --git a/src/examples/zkapps/dex/happy-path-with-actions.ts b/src/examples/zkapps/dex/happy-path-with-actions.ts index 16ea8fddcb..f7effd3360 100644 --- a/src/examples/zkapps/dex/happy-path-with-actions.ts +++ b/src/examples/zkapps/dex/happy-path-with-actions.ts @@ -19,7 +19,6 @@ let Local = Mina.LocalBlockchain({ enforceTransactionLimits: true, }); Mina.setActiveInstance(Local); -let accountFee = Mina.accountCreationFee(); let [{ privateKey: feePayerKey, publicKey: feePayerAddress }] = Local.testAccounts; let tx, balances, oldBalances; @@ -44,6 +43,7 @@ let dexTokenHolderY = new DexTokenHolder(addresses.dex, tokenIds.Y); tic('deploy & init token contracts'); tx = await Mina.transaction(feePayerAddress, () => { + const accountFee = Mina.getNetworkConstants().accountCreationFee; // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves let feePayerUpdate = AccountUpdate.createSigned(feePayerAddress); feePayerUpdate.balance.subInPlace(accountFee.mul(2)); @@ -61,7 +61,7 @@ tic('deploy dex contracts'); tx = await Mina.transaction(feePayerAddress, () => { // pay fees for creating 3 dex accounts AccountUpdate.createSigned(feePayerAddress).balance.subInPlace( - accountFee.mul(3) + Mina.getNetworkConstants().accountCreationFee.mul(3) ); dex.deploy(); dexTokenHolderX.deploy(); diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index 4727851012..3922f82c39 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -15,7 +15,6 @@ let Local = Mina.LocalBlockchain({ enforceTransactionLimits: false, }); Mina.setActiveInstance(Local); -let accountFee = Mina.accountCreationFee(); let [{ privateKey: feePayerKey, publicKey: feePayerAddress }] = Local.testAccounts; let tx, balances, oldBalances; @@ -46,6 +45,7 @@ let dexTokenHolderY = new DexTokenHolder(addresses.dex, tokenIds.Y); tic('deploy & init token contracts'); tx = await Mina.transaction(feePayerAddress, () => { + const accountFee = Mina.getNetworkConstants().accountCreationFee; // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves let feePayerUpdate = AccountUpdate.createSigned(feePayerAddress); feePayerUpdate.balance.subInPlace(accountFee.mul(2)); @@ -63,7 +63,7 @@ tic('deploy dex contracts'); tx = await Mina.transaction(feePayerAddress, () => { // pay fees for creating 3 dex accounts AccountUpdate.createSigned(feePayerAddress).balance.subInPlace( - accountFee.mul(3) + Mina.getNetworkConstants().accountCreationFee.mul(3) ); dex.deploy(); dexTokenHolderX.deploy(); diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index e9f15fad22..bb50b2c119 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -9,7 +9,6 @@ let Local = Mina.LocalBlockchain({ enforceTransactionLimits: false, }); Mina.setActiveInstance(Local); -let accountFee = Mina.accountCreationFee(); let [{ privateKey: feePayerKey, publicKey: feePayerAddress }] = Local.testAccounts; let tx, balances, oldBalances; @@ -77,6 +76,7 @@ async function main({ withVesting }: { withVesting: boolean }) { console.log('deploy & init token contracts...'); tx = await Mina.transaction(feePayerAddress, () => { + const accountFee = Mina.getNetworkConstants().accountCreationFee; // pay fees for creating 2 token contract accounts, and fund them so each can create 2 accounts themselves let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee.mul(2) }); @@ -110,7 +110,10 @@ async function main({ withVesting }: { withVesting: boolean }) { console.log('transfer tokens to user'); tx = await Mina.transaction( - { sender: feePayerAddress, fee: accountFee.mul(1) }, + { + sender: feePayerAddress, + fee: Mina.getNetworkConstants().accountCreationFee.mul(1), + }, () => { let feePayer = AccountUpdate.fundNewAccount(feePayerAddress, 4); feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees @@ -133,7 +136,10 @@ async function main({ withVesting }: { withVesting: boolean }) { // supply the initial liquidity where the token ratio can be arbitrary console.log('supply liquidity -- base'); tx = await Mina.transaction( - { sender: feePayerAddress, fee: accountFee }, + { + sender: feePayerAddress, + fee: Mina.getNetworkConstants().accountCreationFee, + }, () => { AccountUpdate.fundNewAccount(feePayerAddress); dex.supplyLiquidityBase(UInt64.from(10_000), UInt64.from(10_000)); @@ -437,7 +443,7 @@ async function main({ withVesting }: { withVesting: boolean }) { ); tx = await Mina.transaction(addresses.user2, () => { AccountUpdate.createSigned(addresses.user2).balance.subInPlace( - accountFee.mul(2) + Mina.getNetworkConstants().accountCreationFee.mul(2) ); dex.redeemLiquidity(UInt64.from(USER_DL)); dex.redeemLiquidity(UInt64.from(USER_DL)); @@ -451,7 +457,7 @@ async function main({ withVesting }: { withVesting: boolean }) { console.log('user2 redeem liquidity'); tx = await Mina.transaction(addresses.user2, () => { AccountUpdate.createSigned(addresses.user2).balance.subInPlace( - accountFee.mul(2) + Mina.getNetworkConstants().accountCreationFee.mul(2) ); dex.redeemLiquidity(UInt64.from(USER_DL)); }); diff --git a/src/examples/zkapps/dex/run_live.ts b/src/examples/zkapps/dex/run_live.ts index db9336fcc4..256ba5f87d 100644 --- a/src/examples/zkapps/dex/run_live.ts +++ b/src/examples/zkapps/dex/run_live.ts @@ -34,7 +34,6 @@ const network = Mina.Network({ lightnetAccountManager: 'http://localhost:8181', }); Mina.setActiveInstance(network); -let accountFee = Mina.accountCreationFee(); let tx, pendingTx: Mina.TransactionId, balances, oldBalances; @@ -74,6 +73,7 @@ let userSpec = { sender: addresses.user, fee: 0.1e9 }; if (successfulTransactions <= 0) { tic('deploy & init token contracts'); tx = await Mina.transaction(senderSpec, () => { + const accountFee = Mina.getNetworkConstants().accountCreationFee; // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves let feePayerUpdate = AccountUpdate.createSigned(sender); feePayerUpdate.balance.subInPlace(accountFee.mul(2)); @@ -97,7 +97,9 @@ if (successfulTransactions <= 1) { tic('deploy dex contracts'); tx = await Mina.transaction(senderSpec, () => { // pay fees for creating 3 dex accounts - AccountUpdate.createSigned(sender).balance.subInPlace(accountFee.mul(3)); + AccountUpdate.createSigned(sender).balance.subInPlace( + Mina.getNetworkConstants().accountCreationFee.mul(3) + ); dex.deploy(); dexTokenHolderX.deploy(); tokenX.approveUpdate(dexTokenHolderX.self); diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 273431c903..bf44ac97e5 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -23,7 +23,6 @@ async function atomicActionsTest({ withVesting }: { withVesting: boolean }) { enforceTransactionLimits: false, }); Mina.setActiveInstance(Local); - let accountFee = Mina.accountCreationFee(); let [{ privateKey: feePayerKey, publicKey: feePayerAddress }] = Local.testAccounts; let tx, balances; @@ -51,6 +50,7 @@ async function atomicActionsTest({ withVesting }: { withVesting: boolean }) { console.log('deploy & init token contracts...'); tx = await Mina.transaction(feePayerAddress, () => { + const accountFee = Mina.getNetworkConstants().accountCreationFee; // pay fees for creating 2 token contract accounts, and fund them so each can create 2 accounts themselves let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee.mul(2) }); @@ -227,7 +227,6 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { enforceTransactionLimits: false, }); Mina.setActiveInstance(Local); - let accountFee = Mina.accountCreationFee(); let [{ privateKey: feePayerKey, publicKey: feePayerAddress }] = Local.testAccounts; let tx, balances, oldBalances; @@ -259,6 +258,7 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { console.log('deploy & init token contracts...'); tx = await Mina.transaction(feePayerAddress, () => { + const accountFee = Mina.getNetworkConstants().accountCreationFee; // pay fees for creating 2 token contract accounts, and fund them so each can create 2 accounts themselves let feePayerUpdate = AccountUpdate.createSigned(feePayerAddress); feePayerUpdate.balance.subInPlace(accountFee.mul(2)); @@ -309,10 +309,15 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { console.log('transfer tokens to user'); tx = await Mina.transaction( - { sender: feePayerAddress, fee: accountFee.mul(1) }, + { + sender: feePayerAddress, + fee: Mina.getNetworkConstants().accountCreationFee.mul(1), + }, () => { let feePayer = AccountUpdate.createSigned(feePayerAddress); - feePayer.balance.subInPlace(Mina.accountCreationFee().mul(4)); + feePayer.balance.subInPlace( + Mina.getNetworkConstants().accountCreationFee.mul(4) + ); feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees feePayer.send({ to: addresses.user2, amount: 20e9 }); // transfer to fee payer so they can provide initial liquidity @@ -364,7 +369,10 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { console.log('supply liquidity -- base'); tx = await Mina.transaction( - { sender: feePayerAddress, fee: accountFee.mul(1) }, + { + sender: feePayerAddress, + fee: Mina.getNetworkConstants().accountCreationFee.mul(1), + }, () => { AccountUpdate.fundNewAccount(feePayerAddress); modifiedDex.supplyLiquidityBase(UInt64.from(10_000), UInt64.from(10_000)); diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 23f5e66c08..1077a0ae36 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1247,7 +1247,7 @@ class AccountUpdate implements Types.AccountUpdate { ) { let accountUpdate = AccountUpdate.createSigned(feePayer as PrivateKey); accountUpdate.label = 'AccountUpdate.fundNewAccount()'; - let fee = Mina.accountCreationFee(); + let fee = Mina.getNetworkConstants().accountCreationFee; numberOfAccounts ??= 1; if (typeof numberOfAccounts === 'number') fee = fee.mul(numberOfAccounts); else fee = fee.add(UInt64.from(numberOfAccounts.initialBalance ?? 0)); diff --git a/src/lib/caller.unit-test.ts b/src/lib/caller.unit-test.ts index 1c6508dcd6..47e242de6f 100644 --- a/src/lib/caller.unit-test.ts +++ b/src/lib/caller.unit-test.ts @@ -17,7 +17,7 @@ let parentId = TokenId.derive(publicKey); let tx = await Mina.transaction(privateKey, () => { let parent = AccountUpdate.defaultAccountUpdate(publicKey); parent.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; - parent.balance.subInPlace(Mina.accountCreationFee()); + parent.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); let child = AccountUpdate.defaultAccountUpdate(publicKey, parentId); child.body.mayUseToken = AccountUpdate.MayUseToken.ParentsOwnToken; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index c984ee174c..bcc2573803 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -51,6 +51,7 @@ export { getAccount, hasAccount, getBalance, + getNetworkConstants, getNetworkState, accountCreationFee, sendTransaction, @@ -64,6 +65,7 @@ export { getProofsEnabled, // for internal testing only filterGroups, + type NetworkConstants }; interface TransactionId { @@ -351,6 +353,9 @@ interface Mina { getAccount(publicKey: PublicKey, tokenId?: Field): Account; getNetworkState(): NetworkValue; getNetworkConstants(): NetworkConstants; + /** + * @deprecated use {@link getNetworkConstants} + */ accountCreationFee(): UInt64; sendTransaction(transaction: Transaction): Promise; fetchEvents: ( @@ -372,7 +377,7 @@ interface Mina { } const defaultAccountCreationFee = 1_000_000_000; -const defaultNetworkConstants = { +const defaultNetworkConstants: NetworkConstants = { genesisTimestamp: UInt64.from(0), slotTime: UInt64.from(3 * 60 * 1000), accountCreationFee: UInt64.from(defaultAccountCreationFee), @@ -382,16 +387,13 @@ const defaultNetworkConstants = { * A mock Mina blockchain running locally and useful for testing. */ function LocalBlockchain({ - accountCreationFee = defaultAccountCreationFee as string | number, proofsEnabled = true, enforceTransactionLimits = true, } = {}) { const slotTime = 3 * 60 * 1000; const startTime = Date.now(); const genesisTimestamp = UInt64.from(startTime); - const ledger = Ledger.create(); - let networkState = defaultNetworkState(); function addAccount(publicKey: PublicKey, balance: string) { @@ -420,12 +422,14 @@ function LocalBlockchain({ return { proofsEnabled, - accountCreationFee: () => UInt64.from(accountCreationFee), + /** + * @deprecated use {@link Mina.getNetworkConstants} + */ + accountCreationFee: () => defaultNetworkConstants.accountCreationFee, getNetworkConstants() { return { + ...defaultNetworkConstants, genesisTimestamp, - accountCreationFee: UInt64.from(accountCreationFee), - slotTime: UInt64.from(slotTime), }; }, currentSlot() { @@ -498,7 +502,7 @@ function LocalBlockchain({ try { ledger.applyJsonTransaction( JSON.stringify(zkappCommandJson), - String(accountCreationFee), + defaultNetworkConstants.accountCreationFee.toString(), JSON.stringify(networkState) ); } catch (err: any) { @@ -507,7 +511,7 @@ function LocalBlockchain({ // TODO: label updates, and try to give precise explanations about what went wrong let errors = JSON.parse(err.message); err.message = invalidTransactionError(txn.transaction, errors, { - accountCreationFee, + accountCreationFee: defaultNetworkConstants.accountCreationFee.toString(), }); } finally { throw err; @@ -609,7 +613,7 @@ function LocalBlockchain({ applyJsonTransaction(json: string) { return ledger.applyJsonTransaction( json, - String(accountCreationFee), + defaultNetworkConstants.accountCreationFee.toString(), JSON.stringify(networkState) ); }, @@ -699,7 +703,6 @@ function Network( } | string ): Mina { - let accountCreationFee = UInt64.from(defaultAccountCreationFee); let minaGraphqlEndpoint: string; let archiveEndpoint: string; let lightnetAccountManagerEndpoint: string; @@ -746,7 +749,10 @@ function Network( } return { - accountCreationFee: () => accountCreationFee, + /** + * @deprecated use {@link Mina.getNetworkConstants} + */ + accountCreationFee: () => defaultNetworkConstants.accountCreationFee, getNetworkConstants() { if (currentTransaction()?.fetchMode === 'test') { Fetch.markNetworkToBeFetched(minaGraphqlEndpoint); @@ -765,9 +771,7 @@ function Network( if (genesisConstants !== undefined) return genesisToNetworkConstants(genesisConstants); } - throw Error( - `getNetworkConstants: Could not fetch network constants from graphql endpoint ${minaGraphqlEndpoint} outside of a transaction.` - ); + return defaultNetworkConstants; }, currentSlot() { throw Error( @@ -1009,9 +1013,12 @@ function BerkeleyQANet(graphqlEndpoint: string) { } let activeInstance: Mina = { - accountCreationFee: () => UInt64.from(defaultAccountCreationFee), + /** + * @deprecated use {@link Mina.getNetworkConstants} + */ + accountCreationFee: () => defaultNetworkConstants.accountCreationFee, getNetworkConstants() { - throw new Error('must call Mina.setActiveInstance first'); + return defaultNetworkConstants; }, currentSlot: () => { throw new Error('must call Mina.setActiveInstance first'); @@ -1192,6 +1199,13 @@ function hasAccount(publicKey: PublicKey, tokenId?: Field): boolean { return activeInstance.hasAccount(publicKey, tokenId); } +/** + * @return Data associated with the current Mina network constants. + */ +function getNetworkConstants() { + return activeInstance.getNetworkConstants(); +} + /** * @return Data associated with the current state of the Mina network. */ @@ -1208,6 +1222,7 @@ function getBalance(publicKey: PublicKey, tokenId?: Field) { /** * Returns the default account creation fee. + * @deprecated use {@link Mina.getNetworkConstants} */ function accountCreationFee() { return activeInstance.accountCreationFee(); diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index 25b10b9031..ac5fd8f8aa 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -58,7 +58,7 @@ function Network(accountUpdate: AccountUpdate): Network { }, requireEquals(value: UInt64) { let { genesisTimestamp, slotTime } = - Mina.activeInstance.getNetworkConstants(); + Mina.getNetworkConstants(); let slot = timestampToGlobalSlot( value, `Timestamp precondition unsatisfied: the timestamp can only equal numbers of the form ${genesisTimestamp} + k*${slotTime},\n` + @@ -319,12 +319,12 @@ function getVariable( function globalSlotToTimestamp(slot: UInt32) { let { genesisTimestamp, slotTime } = - Mina.activeInstance.getNetworkConstants(); + Mina.getNetworkConstants(); return UInt64.from(slot).mul(slotTime).add(genesisTimestamp); } function timestampToGlobalSlot(timestamp: UInt64, message: string) { let { genesisTimestamp, slotTime } = - Mina.activeInstance.getNetworkConstants(); + Mina.getNetworkConstants(); let { quotient: slot, rest } = timestamp .sub(genesisTimestamp) .divMod(slotTime); @@ -340,7 +340,7 @@ function timestampToGlobalSlotRange( // so we have to make the range smaller -- round up `tsLower` and round down `tsUpper` // also, we should clamp to the UInt32 max range [0, 2**32-1] let { genesisTimestamp, slotTime } = - Mina.activeInstance.getNetworkConstants(); + Mina.getNetworkConstants(); let tsLowerInt = Int64.from(tsLower) .sub(genesisTimestamp) .add(slotTime) diff --git a/src/lib/token.test.ts b/src/lib/token.test.ts index a8b6428af1..6f711f0a6a 100644 --- a/src/lib/token.test.ts +++ b/src/lib/token.test.ts @@ -44,7 +44,7 @@ class TokenContract extends SmartContract { amount: this.SUPPLY, }); receiver.account.isNew.assertEquals(Bool(true)); - this.balance.subInPlace(Mina.accountCreationFee()); + this.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); this.totalAmountInCirculation.set(this.SUPPLY.sub(100_000_000)); this.account.permissions.set({ ...Permissions.default(), @@ -172,7 +172,7 @@ async function setupLocal() { let feePayerUpdate = AccountUpdate.fundNewAccount(feePayer); feePayerUpdate.send({ to: tokenZkappAddress, - amount: Mina.accountCreationFee(), + amount: Mina.getNetworkConstants().accountCreationFee, }); tokenZkapp.deploy(); }); @@ -189,7 +189,7 @@ async function setupLocalProofs() { let feePayerUpdate = AccountUpdate.fundNewAccount(feePayer, 3); feePayerUpdate.send({ to: tokenZkappAddress, - amount: Mina.accountCreationFee(), + amount: Mina.getNetworkConstants().accountCreationFee, }); tokenZkapp.deploy(); tokenZkapp.deployZkapp(zkAppBAddress, ZkAppB._verificationKey!); From 0cdac60734d1c03343c0707bb422253662dcd00f Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sun, 21 Jan 2024 10:25:55 +0200 Subject: [PATCH 154/524] Changelog entry. --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd76e3380e..e8f5d6c62f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/08ba27329...HEAD) +### Breaking changes + +- `Mina.accountCreationFee()` is deprecated in favor of `Mina.getNetworkConstants().accountCreationFee`. https://github.com/o1-labs/o1js/pull/1367 + - `Mina.getNetworkConstants()` returns: + - [default](https://github.com/o1-labs/o1js/pull/1367/files#diff-ef2c3547d64a8eaa8253cd82b3623288f3271e14f1dc893a0a3ddc1ff4b9688fR7) network constants if used outside of the transaction scope. + - [actual](https://github.com/o1-labs/o1js/pull/1367/files#diff-437f2c15df7c90ad8154c5de1677ec0838d51859bcc0a0cefd8a0424b5736f31R1051) network constants if used within the transaction scope. + ## [0.15.2](https://github.com/o1-labs/o1js/compare/1ad7333e9e...08ba27329) ### Fixed From 4644fb523eca1a915dd7ab95bfd9af7c978be25a Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 19:00:47 +0100 Subject: [PATCH 155/524] merkle list --- src/examples/zkapps/token/merkle-list.ts | 68 ++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/examples/zkapps/token/merkle-list.ts diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts new file mode 100644 index 0000000000..7a1e696884 --- /dev/null +++ b/src/examples/zkapps/token/merkle-list.ts @@ -0,0 +1,68 @@ +import { Field, Poseidon, Provable, Struct, Unconstrained } from 'o1js'; + +export { MerkleList }; + +class Element extends Struct({ previousHash: Field, element: Field }) {} + +const emptyHash = Field(0); +const dummyElement = Field(0); + +class MerkleList { + hash: Field; + value: Unconstrained; + + private constructor(hash: Field, value: Element[]) { + this.hash = hash; + this.value = Unconstrained.from(value); + } + + isEmpty() { + return this.hash.equals(emptyHash); + } + + static create(): MerkleList { + return new MerkleList(emptyHash, []); + } + + push(element: Field) { + let previousHash = this.hash; + this.hash = Poseidon.hash([previousHash, element]); + Provable.asProver(() => { + this.value.set([...this.value.get(), { previousHash, element }]); + }); + } + + private popWitness() { + return Provable.witness(Element, () => { + let value = this.value.get(); + let head = value.at(-1) ?? { + previousHash: emptyHash, + element: dummyElement, + }; + this.value.set(value.slice(0, -1)); + return head; + }); + } + + pop(): Field { + let { previousHash, element } = this.popWitness(); + + let requiredHash = Poseidon.hash([previousHash, element]); + this.hash.assertEquals(requiredHash); + + this.hash = previousHash; + return element; + } + + popOrDummy(): Field { + let { previousHash, element } = this.popWitness(); + + let isEmpty = this.isEmpty(); + let correctHash = Poseidon.hash([previousHash, element]); + let requiredHash = Provable.if(isEmpty, emptyHash, correctHash); + this.hash.assertEquals(requiredHash); + + this.hash = Provable.if(isEmpty, emptyHash, previousHash); + return Provable.if(isEmpty, dummyElement, element); + } +} From 302c01dc7efe18013acb31dbdd044691139ad4b7 Mon Sep 17 00:00:00 2001 From: Barrie Byron Date: Mon, 22 Jan 2024 14:23:38 -0500 Subject: [PATCH 156/524] Create README.md --- src/examples/zkapps/hashing/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/examples/zkapps/hashing/README.md diff --git a/src/examples/zkapps/hashing/README.md b/src/examples/zkapps/hashing/README.md new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/examples/zkapps/hashing/README.md @@ -0,0 +1 @@ + From 90261ffab32082bbc6643a7e42fc119b371f03e0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:48:50 +0100 Subject: [PATCH 157/524] export unconstrained properly --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 8fb4e0c9ef..8b8aa0672e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,6 @@ export type { FlexibleProvable, FlexibleProvablePure, InferProvable, - Unconstrained, } from './lib/circuit_value.js'; export { CircuitValue, @@ -31,6 +30,7 @@ export { provable, provablePure, Struct, + Unconstrained, } from './lib/circuit_value.js'; export { Provable } from './lib/provable.js'; export { Circuit, Keypair, public_, circuitMain } from './lib/circuit.js'; From c91e52c4966f0fab2dfeb361f654835fafc1587f Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:49:04 +0100 Subject: [PATCH 158/524] export hash with prefix --- src/lib/hash.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 1a55da5181..3b24556b51 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -62,6 +62,17 @@ const Poseidon = { return MlFieldArray.from(newState) as [Field, Field, Field]; }, + hashWithPrefix(prefix: string, input: Field[]) { + let init = Poseidon.update(Poseidon.initialState(), [ + prefixToField(prefix), + ]); + return Poseidon.update(init, input)[0]; + }, + + initialState(): [Field, Field, Field] { + return [Field(0), Field(0), Field(0)]; + }, + hashToGroup(input: Field[]) { if (isConstant(input)) { let result = PoseidonBigint.hashToGroup(toBigints(input)); @@ -96,10 +107,6 @@ const Poseidon = { return { x, y: { x0, x1 } }; }, - initialState(): [Field, Field, Field] { - return [Field(0), Field(0), Field(0)]; - }, - Sponge, }; From 57c720b61a0f1dc1c2732589364cbda11084e7e4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:49:19 +0100 Subject: [PATCH 159/524] generic merkle tree --- src/examples/zkapps/token/merkle-list.ts | 52 +++++++++++++++++------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 7a1e696884..3cefc1e941 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -4,65 +4,87 @@ export { MerkleList }; class Element extends Struct({ previousHash: Field, element: Field }) {} +type WithHash = { previousHash: Field; element: T }; +function WithHash(type: ProvableHashable): Provable> { + return Struct({ previousHash: Field, element: type }); +} + const emptyHash = Field(0); -const dummyElement = Field(0); -class MerkleList { +class MerkleList { hash: Field; - value: Unconstrained; + value: Unconstrained[]>; + provable: ProvableHashable; + nextHash: (hash: Field, t: T) => Field; - private constructor(hash: Field, value: Element[]) { + private constructor( + hash: Field, + value: WithHash[], + provable: ProvableHashable, + nextHash: (hash: Field, t: T) => Field + ) { this.hash = hash; this.value = Unconstrained.from(value); + this.provable = provable; + this.nextHash = nextHash; } isEmpty() { return this.hash.equals(emptyHash); } - static create(): MerkleList { - return new MerkleList(emptyHash, []); + static create( + provable: ProvableHashable, + nextHash: (hash: Field, t: T) => Field + ): MerkleList { + return new MerkleList(emptyHash, [], provable, nextHash); } - push(element: Field) { + push(element: T) { let previousHash = this.hash; - this.hash = Poseidon.hash([previousHash, element]); + this.hash = this.nextHash(previousHash, element); Provable.asProver(() => { this.value.set([...this.value.get(), { previousHash, element }]); }); } private popWitness() { - return Provable.witness(Element, () => { + return Provable.witness(WithHash(this.provable), () => { let value = this.value.get(); let head = value.at(-1) ?? { previousHash: emptyHash, - element: dummyElement, + element: this.provable.empty(), }; this.value.set(value.slice(0, -1)); return head; }); } - pop(): Field { + pop(): T { let { previousHash, element } = this.popWitness(); - let requiredHash = Poseidon.hash([previousHash, element]); + let requiredHash = this.nextHash(previousHash, element); this.hash.assertEquals(requiredHash); this.hash = previousHash; return element; } - popOrDummy(): Field { + popOrDummy(): T { let { previousHash, element } = this.popWitness(); let isEmpty = this.isEmpty(); - let correctHash = Poseidon.hash([previousHash, element]); + let correctHash = this.nextHash(previousHash, element); let requiredHash = Provable.if(isEmpty, emptyHash, correctHash); this.hash.assertEquals(requiredHash); this.hash = Provable.if(isEmpty, emptyHash, previousHash); - return Provable.if(isEmpty, dummyElement, element); + return Provable.if(isEmpty, this.provable, this.provable.empty(), element); } } + +type HashInput = { fields?: Field[]; packed?: [Field, number][] }; +type ProvableHashable = Provable & { + toInput: (x: T) => HashInput; + empty: () => T; +}; From 872a4ddec5bfde2edebc81a846d15a54eb6edd63 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:49:30 +0100 Subject: [PATCH 160/524] start call forest --- src/examples/zkapps/token/call-forest.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/examples/zkapps/token/call-forest.ts diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts new file mode 100644 index 0000000000..4384f51c54 --- /dev/null +++ b/src/examples/zkapps/token/call-forest.ts @@ -0,0 +1,17 @@ +import { AccountUpdate, Field, Hashed } from 'o1js'; +import { MerkleList } from './merkle-list.js'; + +export { CallForest }; + +class CallTree { + accountUpdate: Hashed; + calls: CallTree[]; +} + +// basically a MerkleList if MerkleList were generic +type CallForest = MerkleList[]; + +class PartialCallForest { + forest: Hashed; + pendingForests: MerkleList; +} From 7880ad3e40f86cab77a6e47f7318b51416aac982 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:49:51 +0100 Subject: [PATCH 161/524] dump initial token contract code (copied) --- src/examples/zkapps/token/token-contract.ts | 109 ++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/examples/zkapps/token/token-contract.ts diff --git a/src/examples/zkapps/token/token-contract.ts b/src/examples/zkapps/token/token-contract.ts new file mode 100644 index 0000000000..e5c484089d --- /dev/null +++ b/src/examples/zkapps/token/token-contract.ts @@ -0,0 +1,109 @@ +import { + AccountUpdate, + Bool, + DeployArgs, + Int64, + method, + Mina, + Permissions, + PublicKey, + SmartContract, + UInt64, + VerificationKey, +} from 'o1js'; + +/** + * Simple token with API flexible enough to handle all our use cases + */ +class TokenContract extends SmartContract { + // constant supply + SUPPLY = UInt64.from(10n ** 18n); + + deploy(args?: DeployArgs) { + super.deploy(args); + this.account.permissions.set({ + ...Permissions.default(), + access: Permissions.proofOrSignature(), + }); + } + + @method init() { + super.init(); + + // mint the entire supply to the token account with the same address as this contract + let receiver = this.token.mint({ + address: this.address, + amount: this.SUPPLY, + }); + + // assert that the receiving account is new, so this can be only done once + receiver.account.isNew.requireEquals(Bool(true)); + + // pay fees for opened account + this.balance.subInPlace(Mina.accountCreationFee()); + } + + // this is a very standardized deploy method. instead, we could also take the account update from a callback + // => need callbacks for signatures + @method deployZkapp(address: PublicKey, verificationKey: VerificationKey) { + let tokenId = this.token.id; + let zkapp = AccountUpdate.create(address, tokenId); + zkapp.account.permissions.set(Permissions.default()); + zkapp.account.verificationKey.set(verificationKey); + zkapp.requireSignature(); + } + + @method approveUpdate(zkappUpdate: AccountUpdate) { + this.approve(zkappUpdate); + let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); + balanceChange.assertEquals(Int64.from(0)); + } + + // FIXME: remove this + @method approveAny(zkappUpdate: AccountUpdate) { + this.approve(zkappUpdate, AccountUpdate.Layout.AnyChildren); + } + + // let a zkapp send tokens to someone, provided the token supply stays constant + @method approveUpdateAndSend( + zkappUpdate: AccountUpdate, + to: PublicKey, + amount: UInt64 + ) { + // approve a layout of two grandchildren, both of which can't inherit the token permission + let { StaticChildren, AnyChildren } = AccountUpdate.Layout; + this.approve(zkappUpdate, StaticChildren(AnyChildren, AnyChildren)); + zkappUpdate.body.mayUseToken.parentsOwnToken.assertTrue(); + let [grandchild1, grandchild2] = zkappUpdate.children.accountUpdates; + grandchild1.body.mayUseToken.inheritFromParent.assertFalse(); + grandchild2.body.mayUseToken.inheritFromParent.assertFalse(); + + // see if balance change cancels the amount sent + let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); + balanceChange.assertEquals(Int64.from(amount).neg()); + // add same amount of tokens to the receiving address + this.token.mint({ address: to, amount }); + } + + transfer(from: PublicKey, to: PublicKey | AccountUpdate, amount: UInt64) { + if (to instanceof PublicKey) + return this.transferToAddress(from, to, amount); + if (to instanceof AccountUpdate) + return this.transferToUpdate(from, to, amount); + } + @method transferToAddress(from: PublicKey, to: PublicKey, value: UInt64) { + this.token.send({ from, to, amount: value }); + } + @method transferToUpdate(from: PublicKey, to: AccountUpdate, value: UInt64) { + this.token.send({ from, to, amount: value }); + } + + @method getBalance(publicKey: PublicKey): UInt64 { + let accountUpdate = AccountUpdate.create(publicKey, this.token.id); + let balance = accountUpdate.account.balance.get(); + accountUpdate.account.balance.requireEquals( + accountUpdate.account.balance.get() + ); + return balance; + } +} From c8a2943963eb14a85ec24e0f1ae65fe918753211 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Mon, 22 Jan 2024 14:33:42 -0800 Subject: [PATCH 162/524] refactor(snarky): revert 'o1js' obj to 'snarky' --- src/bindings | 2 +- src/snarky.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bindings b/src/bindings index 6e3476e833..e8fec4c64c 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 6e3476e833821adbad460b01741e0320fcf90b19 +Subproject commit e8fec4c64cedab7095c8ede90d56d0973be86fb1 diff --git a/src/snarky.js b/src/snarky.js index 8487cbf7de..b80fa8e6bd 100644 --- a/src/snarky.js +++ b/src/snarky.js @@ -1,5 +1,5 @@ import './bindings/crypto/bindings.js'; -import { getO1js, getWasm, withThreadPool } from './bindings/js/wrapper.js'; +import { getSnarky, getWasm, withThreadPool } from './bindings/js/wrapper.js'; import snarkySpec from './bindings/js/snarky-class-spec.js'; import { proxyClasses } from './bindings/js/proxy.js'; @@ -8,7 +8,7 @@ let isReadyBoolean = true; let isItReady = () => isReadyBoolean; let { Snarky, Ledger, Pickles, Test } = proxyClasses( - getO1js, + getSnarky, isItReady, snarkySpec ); From f5a581dc4ae11a49d342dcaf248178c866a09af1 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 00:55:16 +0100 Subject: [PATCH 163/524] hashed can take an alternative hash function --- src/lib/provable-types/packed.ts | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index d3ff8bbd31..a41080bfec 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -172,13 +172,18 @@ class Hashed { /** * Create a hashed representation of `type`. You can then use `HashedType.hash(x)` to wrap a value in a `Hashed`. */ - static create(type: ProvableExtended): typeof Hashed { + static create( + type: ProvableExtended, + hash?: (t: T) => Field + ): typeof Hashed { return class Hashed_ extends Hashed { static _innerProvable = type; static _provable = provableFromClass(Hashed_, { hash: Field, value: Unconstrained.provable, }) as ProvableHashable>; + + static _hash = (hash ?? Hashed._hash) satisfies (t: T) => Field; }; } @@ -187,13 +192,17 @@ class Hashed { this.value = value; } + static _hash(t: any) { + let input = this.innerProvable.toInput(t); + let packed = packToFields(input); + return Poseidon.hash(packed); + } + /** * Wrap a value, and represent it by its hash in provable code. */ static hash(x: T): Hashed { - let input = this.innerProvable.toInput(x); - let packed = packToFields(input); - let hash = Poseidon.hash(packed); + let hash = this._hash(x); return new this(hash, Unconstrained.from(x)); } @@ -206,9 +215,7 @@ class Hashed { ); // prove that the value hashes to the hash - let input = this.Constructor.innerProvable.toInput(value); - let packed = packToFields(input); - let hash = Poseidon.hash(packed); + let hash = this.Constructor._hash(value); this.hash.assertEquals(hash); return value; From 64c650fd902cd3b0668f88db953b44433c0fecbb Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 00:55:24 +0100 Subject: [PATCH 164/524] call forest logic --- src/examples/zkapps/token/call-forest.ts | 69 +++++++++++++++++++++--- src/examples/zkapps/token/merkle-list.ts | 24 +++++---- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 4384f51c54..0c431bb991 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -1,17 +1,72 @@ -import { AccountUpdate, Field, Hashed } from 'o1js'; -import { MerkleList } from './merkle-list.js'; +import { AccountUpdate, Field, Hashed, Poseidon, Unconstrained } from 'o1js'; +import { MerkleList, WithStackHash, emptyHash } from './merkle-list.js'; export { CallForest }; -class CallTree { +class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => + a.hash() +) {} + +type CallTree = { accountUpdate: Hashed; - calls: CallTree[]; -} + calls: CallForest; +}; + +type CallForest = WithStackHash; + +const CallForest = { + fromAccountUpdates(updates: AccountUpdate[]): CallForest { + let forest = CallForest.empty(); + + for (let update of [...updates].reverse()) { + let accountUpdate = HashedAccountUpdate.hash(update); + let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); + CallForest.cons(forest, { accountUpdate, calls }); + } + return forest; + }, + + empty(): CallForest { + return { hash: emptyHash, stack: Unconstrained.from([]) }; + }, -// basically a MerkleList if MerkleList were generic -type CallForest = MerkleList[]; + cons(forest: CallForest, tree: CallTree) { + let node = { previousHash: forest.hash, element: tree }; + let nodeHash = CallForest.hashNode(tree); + + forest.stack.set([...forest.stack.get(), node]); + forest.hash = CallForest.hashCons(forest, nodeHash); + }, + + hashNode(tree: CallTree) { + return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ + tree.accountUpdate.hash, + tree.calls.hash, + ]); + }, + hashCons(forest: CallForest, nodeHash: Field) { + return Poseidon.hashWithPrefix('MinaAcctUpdateCons**', [ + forest.hash, + nodeHash, + ]); + }, + + provable: WithStackHash(), +}; + +const CallForestHashed = Hashed.create( + CallForest.provable, + (forest) => forest.hash +); class PartialCallForest { forest: Hashed; pendingForests: MerkleList; + + constructor(forest: CallForest) { + this.forest = CallForestHashed.hash(forest); + this.pendingForests = MerkleList.create(CallForest.provable, (hash, t) => + Poseidon.hash([hash, t.hash]) + ); + } } diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 3cefc1e941..1fd6b09c2c 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -1,19 +1,25 @@ -import { Field, Poseidon, Provable, Struct, Unconstrained } from 'o1js'; +import { Field, Provable, ProvableExtended, Struct, Unconstrained } from 'o1js'; -export { MerkleList }; - -class Element extends Struct({ previousHash: Field, element: Field }) {} +export { MerkleList, WithHash, WithStackHash, emptyHash }; type WithHash = { previousHash: Field; element: T }; function WithHash(type: ProvableHashable): Provable> { return Struct({ previousHash: Field, element: type }); } +type WithStackHash = { + hash: Field; + stack: Unconstrained[]>; +}; +function WithStackHash(): ProvableExtended> { + return Struct({ hash: Field, stack: Unconstrained.provable }); +} + const emptyHash = Field(0); class MerkleList { hash: Field; - value: Unconstrained[]>; + stack: Unconstrained[]>; provable: ProvableHashable; nextHash: (hash: Field, t: T) => Field; @@ -24,7 +30,7 @@ class MerkleList { nextHash: (hash: Field, t: T) => Field ) { this.hash = hash; - this.value = Unconstrained.from(value); + this.stack = Unconstrained.from(value); this.provable = provable; this.nextHash = nextHash; } @@ -44,18 +50,18 @@ class MerkleList { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); Provable.asProver(() => { - this.value.set([...this.value.get(), { previousHash, element }]); + this.stack.set([...this.stack.get(), { previousHash, element }]); }); } private popWitness() { return Provable.witness(WithHash(this.provable), () => { - let value = this.value.get(); + let value = this.stack.get(); let head = value.at(-1) ?? { previousHash: emptyHash, element: this.provable.empty(), }; - this.value.set(value.slice(0, -1)); + this.stack.set(value.slice(0, -1)); return head; }); } From be748e42eba00bab84e1f321591f77deae77dabb Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 23 Jan 2024 00:04:47 +0000 Subject: [PATCH 165/524] 0.15.3 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e0edcd8ffa..d7e23d73cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "o1js", - "version": "0.15.2", + "version": "0.15.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "o1js", - "version": "0.15.2", + "version": "0.15.3", "license": "Apache-2.0", "dependencies": { "blakejs": "1.2.1", diff --git a/package.json b/package.json index ee1d7b87f2..affed9efdc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "o1js", "description": "TypeScript framework for zk-SNARKs and zkApps", - "version": "0.15.2", + "version": "0.15.3", "license": "Apache-2.0", "homepage": "https://github.com/o1-labs/o1js/", "keywords": [ From 84ed7d2294f87e8acde6d999b40c00e470792f46 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 23 Jan 2024 00:04:55 +0000 Subject: [PATCH 166/524] Update CHANGELOG for new version v0.15.3 --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a198341af1..d26e29efff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm _Security_ in case of vulnerabilities. --> -## [Unreleased](https://github.com/o1-labs/o1js/compare/08ba27329...HEAD) +## [Unreleased](https://github.com/o1-labs/o1js/compare/be748e42e...HEAD) + +## [0.15.3](https://github.com/o1-labs/o1js/compare/1ad7333e9e...be748e42e) ### Breaking changes From b1b23b989cef8222d6d631d03da12d8cf945f507 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 01:37:19 +0100 Subject: [PATCH 167/524] properly generic merkle list --- src/examples/zkapps/token/merkle-list.ts | 88 ++++++++++++++++++------ 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 1fd6b09c2c..b58599fb15 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -1,6 +1,14 @@ -import { Field, Provable, ProvableExtended, Struct, Unconstrained } from 'o1js'; - -export { MerkleList, WithHash, WithStackHash, emptyHash }; +import { + Field, + Provable, + ProvableExtended, + Struct, + Unconstrained, + assert, +} from 'o1js'; +import { provableFromClass } from 'src/bindings/lib/provable-snarky.js'; + +export { MerkleList, WithHash, WithStackHash, emptyHash, ProvableHashable }; type WithHash = { previousHash: Field; element: T }; function WithHash(type: ProvableHashable): Provable> { @@ -20,30 +28,18 @@ const emptyHash = Field(0); class MerkleList { hash: Field; stack: Unconstrained[]>; - provable: ProvableHashable; - nextHash: (hash: Field, t: T) => Field; - private constructor( - hash: Field, - value: WithHash[], - provable: ProvableHashable, - nextHash: (hash: Field, t: T) => Field - ) { + constructor(hash: Field, value: WithHash[]) { this.hash = hash; this.stack = Unconstrained.from(value); - this.provable = provable; - this.nextHash = nextHash; } isEmpty() { return this.hash.equals(emptyHash); } - static create( - provable: ProvableHashable, - nextHash: (hash: Field, t: T) => Field - ): MerkleList { - return new MerkleList(emptyHash, [], provable, nextHash); + static empty(): MerkleList { + return new this(emptyHash, []); } push(element: T) { @@ -55,11 +51,11 @@ class MerkleList { } private popWitness() { - return Provable.witness(WithHash(this.provable), () => { + return Provable.witness(WithHash(this.innerProvable), () => { let value = this.stack.get(); let head = value.at(-1) ?? { previousHash: emptyHash, - element: this.provable.empty(), + element: this.innerProvable.empty(), }; this.stack.set(value.slice(0, -1)); return head; @@ -85,7 +81,57 @@ class MerkleList { this.hash.assertEquals(requiredHash); this.hash = Provable.if(isEmpty, emptyHash, previousHash); - return Provable.if(isEmpty, this.provable, this.provable.empty(), element); + let provable = this.innerProvable; + return Provable.if(isEmpty, provable, provable.empty(), element); + } + + /** + * Create a Merkle list type + */ + static create( + type: ProvableHashable, + nextHash: (hash: Field, t: T) => Field + ): typeof MerkleList { + return class MerkleList_ extends MerkleList { + static _innerProvable = type; + + static _provable = provableFromClass(MerkleList_, { + hash: Field, + stack: Unconstrained.provable, + }) as ProvableHashable>; + + static _nextHash = nextHash; + }; + } + + // dynamic subclassing infra + static _nextHash: ((hash: Field, t: any) => Field) | undefined; + + static _provable: ProvableHashable> | undefined; + static _innerProvable: ProvableHashable | undefined; + + get Constructor() { + return this.constructor as typeof MerkleList; + } + + nextHash(hash: Field, t: T): Field { + assert( + this.Constructor._nextHash !== undefined, + 'MerkleList not initialized' + ); + return this.Constructor._nextHash(hash, t); + } + + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'MerkleList not initialized'); + return this._provable; + } + get innerProvable(): ProvableHashable { + assert( + this.Constructor._innerProvable !== undefined, + 'MerkleList not initialized' + ); + return this.Constructor._innerProvable; } } From 3ef39b0b7ee059ad9a2864fd43b9ee90dfe279b9 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 01:37:36 +0100 Subject: [PATCH 168/524] tweak hased input type --- src/lib/provable-types/packed.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index a41080bfec..2fdfd68988 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -173,7 +173,7 @@ class Hashed { * Create a hashed representation of `type`. You can then use `HashedType.hash(x)` to wrap a value in a `Hashed`. */ static create( - type: ProvableExtended, + type: ProvableHashable, hash?: (t: T) => Field ): typeof Hashed { return class Hashed_ extends Hashed { @@ -227,7 +227,7 @@ class Hashed { // dynamic subclassing infra static _provable: ProvableHashable> | undefined; - static _innerProvable: ProvableExtended | undefined; + static _innerProvable: ProvableHashable | undefined; get Constructor(): typeof Hashed { return this.constructor as typeof Hashed; @@ -237,7 +237,7 @@ class Hashed { assert(this._provable !== undefined, 'Hashed not initialized'); return this._provable; } - static get innerProvable(): ProvableExtended { + static get innerProvable(): ProvableHashable { assert(this._innerProvable !== undefined, 'Hashed not initialized'); return this._innerProvable; } From afc5d5a01f90b196981ffed3d298a8b717f28f95 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 01:37:58 +0100 Subject: [PATCH 169/524] make callforest a merkle list --- src/examples/zkapps/token/call-forest.ts | 82 +++++++++++------------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 0c431bb991..1646d3e476 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -1,5 +1,5 @@ -import { AccountUpdate, Field, Hashed, Poseidon, Unconstrained } from 'o1js'; -import { MerkleList, WithStackHash, emptyHash } from './merkle-list.js'; +import { AccountUpdate, Field, Hashed, Poseidon, Struct } from 'o1js'; +import { MerkleList, ProvableHashable, WithStackHash } from './merkle-list.js'; export { CallForest }; @@ -9,64 +9,58 @@ class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => type CallTree = { accountUpdate: Hashed; - calls: CallForest; + calls: WithStackHash; }; +const CallTree: ProvableHashable = Struct({ + accountUpdate: HashedAccountUpdate.provable, + calls: WithStackHash(), +}); -type CallForest = WithStackHash; +class CallForest extends MerkleList.create(CallTree, function (hash, tree) { + return hashCons(hash, hashNode(tree)); +}) { + static empty(): CallForest { + return super.empty(); + } -const CallForest = { - fromAccountUpdates(updates: AccountUpdate[]): CallForest { + static fromAccountUpdates(updates: AccountUpdate[]): CallForest { let forest = CallForest.empty(); for (let update of [...updates].reverse()) { let accountUpdate = HashedAccountUpdate.hash(update); let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); - CallForest.cons(forest, { accountUpdate, calls }); + forest.push({ accountUpdate, calls }); } - return forest; - }, - - empty(): CallForest { - return { hash: emptyHash, stack: Unconstrained.from([]) }; - }, - - cons(forest: CallForest, tree: CallTree) { - let node = { previousHash: forest.hash, element: tree }; - let nodeHash = CallForest.hashNode(tree); - forest.stack.set([...forest.stack.get(), node]); - forest.hash = CallForest.hashCons(forest, nodeHash); - }, - - hashNode(tree: CallTree) { - return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ - tree.accountUpdate.hash, - tree.calls.hash, - ]); - }, - hashCons(forest: CallForest, nodeHash: Field) { - return Poseidon.hashWithPrefix('MinaAcctUpdateCons**', [ - forest.hash, - nodeHash, - ]); - }, - - provable: WithStackHash(), -}; + return forest; + } +} -const CallForestHashed = Hashed.create( - CallForest.provable, - (forest) => forest.hash +const PendingForests = MerkleList.create(CallForest.provable, (hash, t) => + Poseidon.hash([hash, t.hash]) ); class PartialCallForest { - forest: Hashed; + forest: CallForest; pendingForests: MerkleList; constructor(forest: CallForest) { - this.forest = CallForestHashed.hash(forest); - this.pendingForests = MerkleList.create(CallForest.provable, (hash, t) => - Poseidon.hash([hash, t.hash]) - ); + this.forest = forest; + this.pendingForests = PendingForests.empty(); } } + +// how to hash a forest + +function hashNode(tree: CallTree) { + return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ + tree.accountUpdate.hash, + tree.calls.hash, + ]); +} +function hashCons(forestHash: Field, nodeHash: Field) { + return Poseidon.hashWithPrefix('MinaAcctUpdateCons**', [ + forestHash, + nodeHash, + ]); +} From 79fd9ec8243a3f7cc5c8a858a18ec9dfc6cc8efc Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 02:33:22 +0100 Subject: [PATCH 170/524] correct dummy hash --- src/lib/provable-types/packed.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index 2fdfd68988..ed78bd2b5a 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -8,7 +8,7 @@ import { Field } from '../field.js'; import { assert } from '../gadgets/common.js'; import { Poseidon, packToFields } from '../hash.js'; import { Provable } from '../provable.js'; -import { fields } from './fields.js'; +import { fields, modifiedField } from './fields.js'; export { Packed, Hashed }; @@ -120,7 +120,10 @@ class Packed { } } -type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; +type ProvableHashable = Provable & { + toInput: (x: T) => HashInput; + empty: () => T; +}; function countFields(input: HashInput) { let n = input.fields?.length ?? 0; @@ -176,10 +179,13 @@ class Hashed { type: ProvableHashable, hash?: (t: T) => Field ): typeof Hashed { + hash ??= Hashed._hash; + let dummyHash = hash(type.empty()); + return class Hashed_ extends Hashed { static _innerProvable = type; static _provable = provableFromClass(Hashed_, { - hash: Field, + hash: modifiedField({ empty: () => dummyHash }), value: Unconstrained.provable, }) as ProvableHashable>; From c68cf5bf0ac275c6a7092391dbf1702ef4830c23 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 02:33:46 +0100 Subject: [PATCH 171/524] start writing pop account update --- src/examples/zkapps/token/call-forest.ts | 29 +++++++++++++++++++--- src/examples/zkapps/token/merkle-list.ts | 31 +++++++++++++++++++----- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 1646d3e476..fa476bf727 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -16,12 +16,13 @@ const CallTree: ProvableHashable = Struct({ calls: WithStackHash(), }); -class CallForest extends MerkleList.create(CallTree, function (hash, tree) { - return hashCons(hash, hashNode(tree)); -}) { +class CallForest extends MerkleList.create(CallTree, nextHash) { static empty(): CallForest { return super.empty(); } + static from(value: WithStackHash): CallForest { + return new this(value.hash, value.stack); + } static fromAccountUpdates(updates: AccountUpdate[]): CallForest { let forest = CallForest.empty(); @@ -34,6 +35,11 @@ class CallForest extends MerkleList.create(CallTree, function (hash, tree) { return forest; } + + pop() { + let { accountUpdate, calls } = super.pop(); + return { accountUpdate, calls: CallForest.from(calls) }; + } } const PendingForests = MerkleList.create(CallForest.provable, (hash, t) => @@ -48,10 +54,27 @@ class PartialCallForest { this.forest = forest; this.pendingForests = PendingForests.empty(); } + + popAccountUpdate() { + let { accountUpdate, calls: forest } = this.forest.pop(); + let restOfForest = this.forest; + + this.pendingForests.pushIf(restOfForest.isEmpty().not(), restOfForest); + this.forest = forest; + + // TODO add a notion of 'current token' to partial call forest, + // or as input to this method + // TODO replace forest with empty forest if account update can't access current token + let update = accountUpdate.unhash(); + } } // how to hash a forest +function nextHash(forestHash: Field, tree: CallTree) { + return hashCons(forestHash, hashNode(tree)); +} + function hashNode(tree: CallTree) { return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ tree.accountUpdate.hash, diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index b58599fb15..16e5f0390f 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -1,4 +1,5 @@ import { + Bool, Field, Provable, ProvableExtended, @@ -15,23 +16,27 @@ function WithHash(type: ProvableHashable): Provable> { return Struct({ previousHash: Field, element: type }); } +const emptyHash = Field(0); + type WithStackHash = { hash: Field; stack: Unconstrained[]>; }; function WithStackHash(): ProvableExtended> { - return Struct({ hash: Field, stack: Unconstrained.provable }); + return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { + static empty(): WithStackHash { + return { hash: emptyHash, stack: Unconstrained.from([]) }; + } + }; } -const emptyHash = Field(0); - class MerkleList { hash: Field; stack: Unconstrained[]>; - constructor(hash: Field, value: WithHash[]) { + constructor(hash: Field, value: Unconstrained[]>) { this.hash = hash; - this.stack = Unconstrained.from(value); + this.stack = value; } isEmpty() { @@ -39,7 +44,7 @@ class MerkleList { } static empty(): MerkleList { - return new this(emptyHash, []); + return new this(emptyHash, Unconstrained.from([])); } push(element: T) { @@ -50,6 +55,20 @@ class MerkleList { }); } + pushIf(condition: Bool, element: T) { + let previousHash = this.hash; + this.hash = Provable.if( + condition, + this.nextHash(previousHash, element), + previousHash + ); + Provable.asProver(() => { + if (condition.toBoolean()) { + this.stack.set([...this.stack.get(), { previousHash, element }]); + } + }); + } + private popWitness() { return Provable.witness(WithHash(this.innerProvable), () => { let value = this.stack.get(); From cdb62ffeae949e13a5580bbcad00ab66abc3cb77 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 02:51:01 +0100 Subject: [PATCH 172/524] finish core pop account update logic --- src/examples/zkapps/token/call-forest.ts | 24 ++++++++++++++++++++++-- src/examples/zkapps/token/merkle-list.ts | 9 +++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index fa476bf727..274d13fe8a 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -1,4 +1,4 @@ -import { AccountUpdate, Field, Hashed, Poseidon, Struct } from 'o1js'; +import { AccountUpdate, Field, Hashed, Poseidon, Provable, Struct } from 'o1js'; import { MerkleList, ProvableHashable, WithStackHash } from './merkle-list.js'; export { CallForest }; @@ -60,12 +60,32 @@ class PartialCallForest { let restOfForest = this.forest; this.pendingForests.pushIf(restOfForest.isEmpty().not(), restOfForest); - this.forest = forest; // TODO add a notion of 'current token' to partial call forest, // or as input to this method // TODO replace forest with empty forest if account update can't access current token let update = accountUpdate.unhash(); + + let currentIsEmpty = forest.isEmpty(); + + let pendingForests = this.pendingForests.clone(); + let nextForest = this.pendingForests.pop(); + let newPendingForests = this.pendingForests; + + this.forest = Provable.if( + currentIsEmpty, + CallForest.provable, + nextForest, + forest + ); + this.pendingForests = Provable.if( + currentIsEmpty, + PendingForests.provable, + newPendingForests, + pendingForests + ); + + return update; } } diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 16e5f0390f..b79402ee9a 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -81,7 +81,7 @@ class MerkleList { }); } - pop(): T { + popExn(): T { let { previousHash, element } = this.popWitness(); let requiredHash = this.nextHash(previousHash, element); @@ -91,7 +91,7 @@ class MerkleList { return element; } - popOrDummy(): T { + pop(): T { let { previousHash, element } = this.popWitness(); let isEmpty = this.isEmpty(); @@ -104,6 +104,11 @@ class MerkleList { return Provable.if(isEmpty, provable, provable.empty(), element); } + clone(): MerkleList { + let stack = Unconstrained.witness(() => [...this.stack.get()]); + return new this.Constructor(this.hash, stack); + } + /** * Create a Merkle list type */ From df2067a40d89b850552897fc5d27581413c17471 Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 23 Jan 2024 10:45:03 +0300 Subject: [PATCH 173/524] Trigger Build From 76007d0f10ad1e1837357449194f459dc600f6c1 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 12:11:17 +0100 Subject: [PATCH 174/524] finish pop account update & refactor partial call forest --- src/examples/zkapps/token/call-forest.ts | 112 ++++++++++++++--------- src/examples/zkapps/token/merkle-list.ts | 73 +++++++++------ 2 files changed, 115 insertions(+), 70 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 274d13fe8a..504618408b 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -1,7 +1,15 @@ -import { AccountUpdate, Field, Hashed, Poseidon, Provable, Struct } from 'o1js'; +import { + AccountUpdate, + Field, + Hashed, + Poseidon, + Provable, + Struct, + TokenId, +} from 'o1js'; import { MerkleList, ProvableHashable, WithStackHash } from './merkle-list.js'; -export { CallForest }; +export { CallForest, PartialCallForest }; class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => a.hash() @@ -16,15 +24,8 @@ const CallTree: ProvableHashable = Struct({ calls: WithStackHash(), }); -class CallForest extends MerkleList.create(CallTree, nextHash) { - static empty(): CallForest { - return super.empty(); - } - static from(value: WithStackHash): CallForest { - return new this(value.hash, value.stack); - } - - static fromAccountUpdates(updates: AccountUpdate[]): CallForest { +class CallForest extends MerkleList.create(CallTree, merkleListHash) { + static fromAccountUpdates(updates: AccountUpdate[]) { let forest = CallForest.empty(); for (let update of [...updates].reverse()) { @@ -35,63 +36,90 @@ class CallForest extends MerkleList.create(CallTree, nextHash) { return forest; } - - pop() { - let { accountUpdate, calls } = super.pop(); - return { accountUpdate, calls: CallForest.from(calls) }; - } } -const PendingForests = MerkleList.create(CallForest.provable, (hash, t) => - Poseidon.hash([hash, t.hash]) -); +class ForestMayUseToken extends Struct({ + forest: CallForest.provable, + mayUseToken: AccountUpdate.MayUseToken.type, +}) {} +const PendingForests = MerkleList.create(ForestMayUseToken); + +type MayUseToken = AccountUpdate['body']['mayUseToken']; +const MayUseToken = AccountUpdate.MayUseToken; class PartialCallForest { - forest: CallForest; - pendingForests: MerkleList; + current: ForestMayUseToken; + pending: MerkleList; - constructor(forest: CallForest) { - this.forest = forest; - this.pendingForests = PendingForests.empty(); + constructor(forest: CallForest, mayUseToken: MayUseToken) { + this.current = { forest, mayUseToken }; + this.pending = PendingForests.empty(); } - popAccountUpdate() { - let { accountUpdate, calls: forest } = this.forest.pop(); - let restOfForest = this.forest; + popAccountUpdate(selfToken: Field) { + // get next account update from the current forest (might be a dummy) + let { accountUpdate, calls } = this.current.forest.pop(); + let forest = new CallForest(calls); + let restOfForest = this.current.forest; - this.pendingForests.pushIf(restOfForest.isEmpty().not(), restOfForest); + this.pending.pushIf(restOfForest.notEmpty(), { + forest: restOfForest, + mayUseToken: this.current.mayUseToken, + }); - // TODO add a notion of 'current token' to partial call forest, - // or as input to this method - // TODO replace forest with empty forest if account update can't access current token + // check if this account update / it's children can use the token let update = accountUpdate.unhash(); + let canAccessThisToken = Provable.equal( + MayUseToken.type, + update.body.mayUseToken, + this.current.mayUseToken + ); + let isSelf = TokenId.derive(update.publicKey, update.tokenId).equals( + selfToken + ); + + let usesThisToken = update.tokenId + .equals(selfToken) + .and(canAccessThisToken); + + // if we don't have to check the children, replace forest with an empty one + let checkSubtree = canAccessThisToken.and(isSelf.not()); + forest = Provable.if( + checkSubtree, + CallForest.provable, + forest, + CallForest.empty() + ); + + // if the current forest is empty, switch to the next pending forest + let current = { forest, mayUseToken: MayUseToken.InheritFromParent }; let currentIsEmpty = forest.isEmpty(); - let pendingForests = this.pendingForests.clone(); - let nextForest = this.pendingForests.pop(); - let newPendingForests = this.pendingForests; + let pendingForests = this.pending.clone(); + let next = this.pending.pop(); + let nextPendingForests = this.pending; - this.forest = Provable.if( + this.current = Provable.if( currentIsEmpty, - CallForest.provable, - nextForest, - forest + ForestMayUseToken, + next, + current ); - this.pendingForests = Provable.if( + this.pending = Provable.if( currentIsEmpty, PendingForests.provable, - newPendingForests, + nextPendingForests, pendingForests ); - return update; + return { update, usesThisToken }; } } // how to hash a forest -function nextHash(forestHash: Field, tree: CallTree) { +function merkleListHash(forestHash: Field, tree: CallTree) { return hashCons(forestHash, hashNode(tree)); } diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index b79402ee9a..be02ccfa79 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -1,6 +1,7 @@ import { Bool, Field, + Poseidon, Provable, ProvableExtended, Struct, @@ -8,25 +9,15 @@ import { assert, } from 'o1js'; import { provableFromClass } from 'src/bindings/lib/provable-snarky.js'; +import { packToFields } from 'src/lib/hash.js'; export { MerkleList, WithHash, WithStackHash, emptyHash, ProvableHashable }; -type WithHash = { previousHash: Field; element: T }; -function WithHash(type: ProvableHashable): Provable> { - return Struct({ previousHash: Field, element: type }); -} - -const emptyHash = Field(0); - -type WithStackHash = { - hash: Field; - stack: Unconstrained[]>; -}; -function WithStackHash(): ProvableExtended> { - return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { - static empty(): WithStackHash { - return { hash: emptyHash, stack: Unconstrained.from([]) }; - } +function merkleListHash(provable: ProvableHashable, prefix = '') { + return function nextHash(hash: Field, value: T) { + let input = provable.toInput(value); + let packed = packToFields(input); + return Poseidon.hashWithPrefix(prefix, [hash, ...packed]); }; } @@ -34,17 +25,16 @@ class MerkleList { hash: Field; stack: Unconstrained[]>; - constructor(hash: Field, value: Unconstrained[]>) { + constructor({ hash, stack }: WithStackHash) { this.hash = hash; - this.stack = value; + this.stack = stack; } isEmpty() { return this.hash.equals(emptyHash); } - - static empty(): MerkleList { - return new this(emptyHash, Unconstrained.from([])); + notEmpty() { + return this.hash.equals(emptyHash).not(); } push(element: T) { @@ -106,7 +96,7 @@ class MerkleList { clone(): MerkleList { let stack = Unconstrained.witness(() => [...this.stack.get()]); - return new this.Constructor(this.hash, stack); + return new this.Constructor({ hash: this.hash, stack }); } /** @@ -114,8 +104,11 @@ class MerkleList { */ static create( type: ProvableHashable, - nextHash: (hash: Field, t: T) => Field - ): typeof MerkleList { + nextHash: (hash: Field, value: T) => Field = merkleListHash(type) + ): typeof MerkleList & { + empty: () => MerkleList; + provable: ProvableHashable>; + } { return class MerkleList_ extends MerkleList { static _innerProvable = type; @@ -125,6 +118,15 @@ class MerkleList { }) as ProvableHashable>; static _nextHash = nextHash; + + static empty(): MerkleList { + return new this({ hash: emptyHash, stack: Unconstrained.from([]) }); + } + + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'MerkleList not initialized'); + return this._provable; + } }; } @@ -146,10 +148,6 @@ class MerkleList { return this.Constructor._nextHash(hash, t); } - static get provable(): ProvableHashable> { - assert(this._provable !== undefined, 'MerkleList not initialized'); - return this._provable; - } get innerProvable(): ProvableHashable { assert( this.Constructor._innerProvable !== undefined, @@ -159,6 +157,25 @@ class MerkleList { } } +type WithHash = { previousHash: Field; element: T }; +function WithHash(type: ProvableHashable): Provable> { + return Struct({ previousHash: Field, element: type }); +} + +const emptyHash = Field(0); + +type WithStackHash = { + hash: Field; + stack: Unconstrained[]>; +}; +function WithStackHash(): ProvableExtended> { + return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { + static empty(): WithStackHash { + return { hash: emptyHash, stack: Unconstrained.from([]) }; + } + }; +} + type HashInput = { fields?: Field[]; packed?: [Field, number][] }; type ProvableHashable = Provable & { toInput: (x: T) => HashInput; From 962482e87665d19317c946f9d8d13c0cfaf27e59 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 12:25:59 +0100 Subject: [PATCH 175/524] improve variable naming --- src/examples/zkapps/token/call-forest.ts | 50 ++++++++++++------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 504618408b..34a845391d 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -38,33 +38,33 @@ class CallForest extends MerkleList.create(CallTree, merkleListHash) { } } -class ForestMayUseToken extends Struct({ +class Layer extends Struct({ forest: CallForest.provable, mayUseToken: AccountUpdate.MayUseToken.type, }) {} -const PendingForests = MerkleList.create(ForestMayUseToken); +const ParentLayers = MerkleList.create(Layer); type MayUseToken = AccountUpdate['body']['mayUseToken']; const MayUseToken = AccountUpdate.MayUseToken; class PartialCallForest { - current: ForestMayUseToken; - pending: MerkleList; + currentLayer: Layer; + nonEmptyParentLayers: MerkleList; constructor(forest: CallForest, mayUseToken: MayUseToken) { - this.current = { forest, mayUseToken }; - this.pending = PendingForests.empty(); + this.currentLayer = { forest, mayUseToken }; + this.nonEmptyParentLayers = ParentLayers.empty(); } popAccountUpdate(selfToken: Field) { // get next account update from the current forest (might be a dummy) - let { accountUpdate, calls } = this.current.forest.pop(); + let { accountUpdate, calls } = this.currentLayer.forest.pop(); let forest = new CallForest(calls); - let restOfForest = this.current.forest; + let restOfForest = this.currentLayer.forest; - this.pending.pushIf(restOfForest.notEmpty(), { + this.nonEmptyParentLayers.pushIf(restOfForest.notEmpty(), { forest: restOfForest, - mayUseToken: this.current.mayUseToken, + mayUseToken: this.currentLayer.mayUseToken, }); // check if this account update / it's children can use the token @@ -73,7 +73,7 @@ class PartialCallForest { let canAccessThisToken = Provable.equal( MayUseToken.type, update.body.mayUseToken, - this.current.mayUseToken + this.currentLayer.mayUseToken ); let isSelf = TokenId.derive(update.publicKey, update.tokenId).equals( selfToken @@ -92,25 +92,27 @@ class PartialCallForest { CallForest.empty() ); - // if the current forest is empty, switch to the next pending forest - let current = { forest, mayUseToken: MayUseToken.InheritFromParent }; + // if the current forest is empty, step up to the next non-empty parent layer + // invariant: the current layer will _never_ be empty _except_ at the point where we stepped + // through the entire forest and there are no remaining parent layers + let currentLayer = { forest, mayUseToken: MayUseToken.InheritFromParent }; let currentIsEmpty = forest.isEmpty(); - let pendingForests = this.pending.clone(); - let next = this.pending.pop(); - let nextPendingForests = this.pending; + let parentLayers = this.nonEmptyParentLayers.clone(); + let nextParentLayer = this.nonEmptyParentLayers.pop(); + let parentLayersIfSteppingUp = this.nonEmptyParentLayers; - this.current = Provable.if( + this.currentLayer = Provable.if( currentIsEmpty, - ForestMayUseToken, - next, - current + Layer, + nextParentLayer, + currentLayer ); - this.pending = Provable.if( + this.nonEmptyParentLayers = Provable.if( currentIsEmpty, - PendingForests.provable, - nextPendingForests, - pendingForests + ParentLayers.provable, + parentLayersIfSteppingUp, + parentLayers ); return { update, usesThisToken }; From a24e092f6a7438df62bb191e455d5d8686e10bfa Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 13:49:04 +0100 Subject: [PATCH 176/524] merkle array type --- src/examples/zkapps/token/merkle-list.ts | 165 ++++++++++++++++++++++- src/lib/circuit_value.ts | 7 + src/lib/provable.ts | 2 - 3 files changed, 171 insertions(+), 3 deletions(-) diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index be02ccfa79..3e0ecf7122 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -11,7 +11,14 @@ import { import { provableFromClass } from 'src/bindings/lib/provable-snarky.js'; import { packToFields } from 'src/lib/hash.js'; -export { MerkleList, WithHash, WithStackHash, emptyHash, ProvableHashable }; +export { + MerkleArray, + MerkleList, + WithHash, + WithStackHash, + emptyHash, + ProvableHashable, +}; function merkleListHash(provable: ProvableHashable, prefix = '') { return function nextHash(hash: Field, value: T) { @@ -181,3 +188,159 @@ type ProvableHashable = Provable & { toInput: (x: T) => HashInput; empty: () => T; }; + +// merkle array + +type MerkleArrayBase = { + readonly array: Unconstrained[]>; + readonly fullHash: Field; + + currentHash: Field; + currentIndex: Unconstrained; +}; + +/** + * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, + * instead of needlessly mutating itself / throwing away context while stepping through it. + * + * We maintain two commitments, both of which are equivalent to a Merkle list hash starting _from the end_ of the array: + * - One to the entire array, to prove that we start iterating at the beginning. + * - One to the array from the current index until the end, to efficiently step forward. + */ +class MerkleArray implements MerkleArrayBase { + // fixed parts + readonly array: Unconstrained[]>; + readonly fullHash: Field; + + // mutable parts + currentHash: Field; + currentIndex: Unconstrained; + + constructor(value: MerkleArrayBase) { + Object.assign(this, value); + } + + isEmpty() { + return this.fullHash.equals(emptyHash); + } + isAtEnd() { + return this.currentHash.equals(emptyHash); + } + assertAtStart() { + return this.currentHash.assertEquals(this.fullHash); + } + + next() { + // next corresponds to `pop()` in MerkleList + // it returns a dummy element if we're at the end of the array + let index = Unconstrained.witness(() => this.currentIndex.get() + 1); + + let { previousHash, element } = Provable.witness( + WithHash(this.innerProvable), + () => + this.array.get()[index.get()] ?? { + previousHash: this.fullHash, + element: this.innerProvable.empty(), + } + ); + + let isDummy = this.isAtEnd(); + let correctHash = this.nextHash(previousHash, element); + let requiredHash = Provable.if(isDummy, emptyHash, correctHash); + this.currentHash.assertEquals(requiredHash); + + this.currentIndex.setTo(index); + this.currentHash = Provable.if(isDummy, emptyHash, previousHash); + + return Provable.if( + isDummy, + this.innerProvable, + this.innerProvable.empty(), + element + ); + } + + clone(): MerkleArray { + let array = Unconstrained.witness(() => [...this.array.get()]); + let currentIndex = Unconstrained.witness(() => this.currentIndex.get()); + return new this.Constructor({ + array, + fullHash: this.fullHash, + currentHash: this.currentHash, + currentIndex, + }); + } + + /** + * Create a Merkle list type + */ + static create( + type: ProvableHashable, + nextHash: (hash: Field, value: T) => Field = merkleListHash(type) + ): typeof MerkleArray & { + from: (array: T[]) => MerkleArray; + provable: ProvableHashable>; + } { + return class MerkleArray_ extends MerkleArray { + static _innerProvable = type; + + static _provable = provableFromClass(MerkleArray_, { + array: Unconstrained.provable, + fullHash: Field, + currentHash: Field, + currentIndex: Unconstrained.provable, + }) as ProvableHashable>; + + static _nextHash = nextHash; + + static from(array: T[]): MerkleArray { + let n = array.length; + let arrayWithHashes = Array>(n); + let currentHash = emptyHash; + + for (let i = n - 1; i >= 0; i--) { + arrayWithHashes[i] = { previousHash: currentHash, element: array[i] }; + currentHash = nextHash(currentHash, array[i]); + } + + return new this({ + array: Unconstrained.from(arrayWithHashes), + fullHash: currentHash, + currentHash: currentHash, + currentIndex: Unconstrained.from(0), + }); + } + + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'MerkleArray not initialized'); + return this._provable; + } + }; + } + + // dynamic subclassing infra + static _nextHash: ((hash: Field, t: any) => Field) | undefined; + + static _provable: ProvableHashable> | undefined; + static _innerProvable: ProvableHashable | undefined; + + get Constructor() { + return this.constructor as typeof MerkleArray; + } + + nextHash(hash: Field, t: T): Field { + assert( + this.Constructor._nextHash !== undefined, + 'MerkleArray not initialized' + ); + return this.Constructor._nextHash(hash, t); + } + + get innerProvable(): ProvableHashable { + assert( + this.Constructor._innerProvable !== undefined, + 'MerkleArray not initialized' + ); + return this.Constructor._innerProvable; + } +} diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 6720c343cb..adccc9a249 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -538,6 +538,13 @@ and Provable.asProver() blocks, which execute outside the proof. this.option = { isSome: true, value }; } + /** + * Set the unconstrained value to the same as another `Unconstrained`. + */ + setTo(value: Unconstrained) { + this.option = value.option; + } + /** * Create an `Unconstrained` with the given `value`. */ diff --git a/src/lib/provable.ts b/src/lib/provable.ts index 8094bcf89e..961f723451 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -341,8 +341,6 @@ function ifImplicit(condition: Bool, x: T, y: T): T { `If x, y are Structs or other custom types, you can use the following:\n` + `Provable.if(bool, MyType, x, y)` ); - // TODO remove second condition once we have consolidated field class back into one - // if (type !== y.constructor) { if (type !== y.constructor) { throw Error( 'Provable.if: Mismatched argument types. Try using an explicit type argument:\n' + From 3dd1880efb362abfbb083e03790f990946f576e6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 14:28:00 +0100 Subject: [PATCH 177/524] make call forest a merkle array, iteration --- src/examples/zkapps/token/call-forest.ts | 72 ++++++++++--------- src/examples/zkapps/token/merkle-list.ts | 89 ++++++++++++++++++++---- 2 files changed, 109 insertions(+), 52 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 34a845391d..938cb5e1b1 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -7,7 +7,12 @@ import { Struct, TokenId, } from 'o1js'; -import { MerkleList, ProvableHashable, WithStackHash } from './merkle-list.js'; +import { + MerkleArray, + MerkleArrayBase, + MerkleList, + ProvableHashable, +} from './merkle-list.js'; export { CallForest, PartialCallForest }; @@ -17,24 +22,22 @@ class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => type CallTree = { accountUpdate: Hashed; - calls: WithStackHash; + calls: MerkleArrayBase; }; const CallTree: ProvableHashable = Struct({ accountUpdate: HashedAccountUpdate.provable, - calls: WithStackHash(), + calls: MerkleArrayBase(), }); -class CallForest extends MerkleList.create(CallTree, merkleListHash) { - static fromAccountUpdates(updates: AccountUpdate[]) { - let forest = CallForest.empty(); - - for (let update of [...updates].reverse()) { +class CallForest extends MerkleArray.create(CallTree, merkleListHash) { + static fromAccountUpdates(updates: AccountUpdate[]): CallForest { + let nodes = updates.map((update) => { let accountUpdate = HashedAccountUpdate.hash(update); let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); - forest.push({ accountUpdate, calls }); - } + return { accountUpdate, calls }; + }); - return forest; + return CallForest.from(nodes); } } @@ -49,21 +52,21 @@ const MayUseToken = AccountUpdate.MayUseToken; class PartialCallForest { currentLayer: Layer; - nonEmptyParentLayers: MerkleList; + unfinishedParentLayers: MerkleList; constructor(forest: CallForest, mayUseToken: MayUseToken) { this.currentLayer = { forest, mayUseToken }; - this.nonEmptyParentLayers = ParentLayers.empty(); + this.unfinishedParentLayers = ParentLayers.empty(); } - popAccountUpdate(selfToken: Field) { + nextAccountUpdate(selfToken: Field) { // get next account update from the current forest (might be a dummy) - let { accountUpdate, calls } = this.currentLayer.forest.pop(); - let forest = new CallForest(calls); - let restOfForest = this.currentLayer.forest; + let { accountUpdate, calls } = this.currentLayer.forest.next(); + let forest = CallForest.startIterating(calls); + let parentLayer = this.currentLayer.forest; - this.nonEmptyParentLayers.pushIf(restOfForest.notEmpty(), { - forest: restOfForest, + this.unfinishedParentLayers.pushIf(parentLayer.isAtEnd(), { + forest: parentLayer, mayUseToken: this.currentLayer.mayUseToken, }); @@ -83,33 +86,28 @@ class PartialCallForest { .equals(selfToken) .and(canAccessThisToken); - // if we don't have to check the children, replace forest with an empty one - let checkSubtree = canAccessThisToken.and(isSelf.not()); - forest = Provable.if( - checkSubtree, - CallForest.provable, - forest, - CallForest.empty() - ); + // if we don't have to check the children, ignore the forest by jumping to its end + let skipSubtree = canAccessThisToken.not().or(isSelf); + forest.jumpToEndIf(skipSubtree); - // if the current forest is empty, step up to the next non-empty parent layer - // invariant: the current layer will _never_ be empty _except_ at the point where we stepped - // through the entire forest and there are no remaining parent layers + // if we're at the end of the current layer, step up to the next unfinished parent layer + // invariant: the new current layer will _never_ be finished _except_ at the point where we stepped + // through the entire forest and there are no remaining parent layers to finish let currentLayer = { forest, mayUseToken: MayUseToken.InheritFromParent }; - let currentIsEmpty = forest.isEmpty(); + let currentIsFinished = forest.isAtEnd(); - let parentLayers = this.nonEmptyParentLayers.clone(); - let nextParentLayer = this.nonEmptyParentLayers.pop(); - let parentLayersIfSteppingUp = this.nonEmptyParentLayers; + let parentLayers = this.unfinishedParentLayers.clone(); + let nextParentLayer = this.unfinishedParentLayers.pop(); + let parentLayersIfSteppingUp = this.unfinishedParentLayers; this.currentLayer = Provable.if( - currentIsEmpty, + currentIsFinished, Layer, nextParentLayer, currentLayer ); - this.nonEmptyParentLayers = Provable.if( - currentIsEmpty, + this.unfinishedParentLayers = Provable.if( + currentIsFinished, ParentLayers.provable, parentLayersIfSteppingUp, parentLayers diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 3e0ecf7122..a82c4f0e56 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -3,7 +3,6 @@ import { Field, Poseidon, Provable, - ProvableExtended, Struct, Unconstrained, assert, @@ -13,6 +12,8 @@ import { packToFields } from 'src/lib/hash.js'; export { MerkleArray, + MerkleArrayIterator, + MerkleArrayBase, MerkleList, WithHash, WithStackHash, @@ -175,7 +176,7 @@ type WithStackHash = { hash: Field; stack: Unconstrained[]>; }; -function WithStackHash(): ProvableExtended> { +function WithStackHash(): ProvableHashable> { return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { static empty(): WithStackHash { return { hash: emptyHash, stack: Unconstrained.from([]) }; @@ -193,12 +194,43 @@ type ProvableHashable = Provable & { type MerkleArrayBase = { readonly array: Unconstrained[]>; - readonly fullHash: Field; + readonly hash: Field; +}; + +function MerkleArrayBase(): ProvableHashable> { + return class extends Struct({ array: Unconstrained.provable, hash: Field }) { + static empty(): MerkleArrayBase { + return { array: Unconstrained.from([]), hash: emptyHash }; + } + }; +} + +type MerkleArrayIterator = { + readonly array: Unconstrained[]>; + readonly hash: Field; currentHash: Field; currentIndex: Unconstrained; }; +function MerkleArrayIterator(): ProvableHashable> { + return class extends Struct({ + array: Unconstrained.provable, + hash: Field, + currentHash: Field, + currentIndex: Unconstrained.provable, + }) { + static empty(): MerkleArrayIterator { + return { + array: Unconstrained.from([]), + hash: emptyHash, + currentHash: emptyHash, + currentIndex: Unconstrained.from(0), + }; + } + }; +} + /** * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, * instead of needlessly mutating itself / throwing away context while stepping through it. @@ -207,27 +239,47 @@ type MerkleArrayBase = { * - One to the entire array, to prove that we start iterating at the beginning. * - One to the array from the current index until the end, to efficiently step forward. */ -class MerkleArray implements MerkleArrayBase { +class MerkleArray implements MerkleArrayIterator { // fixed parts readonly array: Unconstrained[]>; - readonly fullHash: Field; + readonly hash: Field; // mutable parts currentHash: Field; currentIndex: Unconstrained; - constructor(value: MerkleArrayBase) { + constructor(value: MerkleArrayIterator) { Object.assign(this, value); } - isEmpty() { - return this.fullHash.equals(emptyHash); + static startIterating({ array, hash }: MerkleArrayBase) { + return new this({ + array, + hash, + currentHash: hash, + currentIndex: Unconstrained.from(0), + }); + } + assertAtStart() { + return this.currentHash.assertEquals(this.hash); } + isAtEnd() { return this.currentHash.equals(emptyHash); } - assertAtStart() { - return this.currentHash.assertEquals(this.fullHash); + jumpToEnd() { + this.currentIndex.setTo( + Unconstrained.witness(() => this.array.get().length) + ); + this.currentHash = emptyHash; + } + jumpToEndIf(condition: Bool) { + Provable.asProver(() => { + if (condition.toBoolean()) { + this.currentIndex.set(this.array.get().length); + } + }); + this.currentHash = Provable.if(condition, emptyHash, this.currentHash); } next() { @@ -239,7 +291,7 @@ class MerkleArray implements MerkleArrayBase { WithHash(this.innerProvable), () => this.array.get()[index.get()] ?? { - previousHash: this.fullHash, + previousHash: this.hash, element: this.innerProvable.empty(), } ); @@ -265,7 +317,7 @@ class MerkleArray implements MerkleArrayBase { let currentIndex = Unconstrained.witness(() => this.currentIndex.get()); return new this.Constructor({ array, - fullHash: this.fullHash, + hash: this.hash, currentHash: this.currentHash, currentIndex, }); @@ -279,6 +331,7 @@ class MerkleArray implements MerkleArrayBase { nextHash: (hash: Field, value: T) => Field = merkleListHash(type) ): typeof MerkleArray & { from: (array: T[]) => MerkleArray; + empty: () => MerkleArray; provable: ProvableHashable>; } { return class MerkleArray_ extends MerkleArray { @@ -286,10 +339,12 @@ class MerkleArray implements MerkleArrayBase { static _provable = provableFromClass(MerkleArray_, { array: Unconstrained.provable, - fullHash: Field, + hash: Field, currentHash: Field, currentIndex: Unconstrained.provable, - }) as ProvableHashable>; + }) satisfies ProvableHashable> as ProvableHashable< + MerkleArray + >; static _nextHash = nextHash; @@ -305,12 +360,16 @@ class MerkleArray implements MerkleArrayBase { return new this({ array: Unconstrained.from(arrayWithHashes), - fullHash: currentHash, + hash: currentHash, currentHash: currentHash, currentIndex: Unconstrained.from(0), }); } + static empty(): MerkleArray { + return this.from([]); + } + static get provable(): ProvableHashable> { assert(this._provable !== undefined, 'MerkleArray not initialized'); return this._provable; From 503fdba7721acde530df652b372ce2f04bdfad32 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 16:11:06 +0100 Subject: [PATCH 178/524] tweaks, doccomments --- src/examples/zkapps/token/call-forest.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 938cb5e1b1..84e6320a22 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -59,14 +59,24 @@ class PartialCallForest { this.unfinishedParentLayers = ParentLayers.empty(); } + /** + * Make a single step through a tree of account updates. + * + * This function will visit each account update in the tree exactly once when called repeatedly, + * and the internal state of `PartialCallForest` represents the work still to be done. + * + * Makes a best effort to avoid visiting account updates that are not using the token and in particular, to avoid returning dummy updates + * -- but both can't be ruled out, so we're returning { update, usesThisToken } and let the caller handle the irrelevant case. + */ nextAccountUpdate(selfToken: Field) { // get next account update from the current forest (might be a dummy) + // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); let forest = CallForest.startIterating(calls); - let parentLayer = this.currentLayer.forest; + let parentForest = this.currentLayer.forest; - this.unfinishedParentLayers.pushIf(parentLayer.isAtEnd(), { - forest: parentLayer, + this.unfinishedParentLayers.pushIf(parentForest.isAtEnd().not(), { + forest: parentForest, mayUseToken: this.currentLayer.mayUseToken, }); From 5e4f58fbc8aa36e28cc9594ce7667c9ee775d2d6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 16:16:07 +0100 Subject: [PATCH 179/524] improve comment --- src/examples/zkapps/token/call-forest.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 84e6320a22..090ed664dd 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -60,13 +60,18 @@ class PartialCallForest { } /** - * Make a single step through a tree of account updates. + * Make a single step along a tree of account updates. * - * This function will visit each account update in the tree exactly once when called repeatedly, - * and the internal state of `PartialCallForest` represents the work still to be done. + * This function is guaranteed to visit each account update in the tree that uses the token + * exactly once, when called repeatedly. * - * Makes a best effort to avoid visiting account updates that are not using the token and in particular, to avoid returning dummy updates - * -- but both can't be ruled out, so we're returning { update, usesThisToken } and let the caller handle the irrelevant case. + * The internal state of `PartialCallForest` represents the work still to be done, and + * can be passed from one proof to the next. + * + * The method makes a best effort to avoid visiting account updates that are not using the token, + * and in particular, to avoid returning dummy updates. + * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the + * caller handle the irrelevant case where `usesThisToken` is false. */ nextAccountUpdate(selfToken: Field) { // get next account update from the current forest (might be a dummy) From 9d9ed56e80f15de8c079abfae9bc5e0094630f21 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 16:31:09 +0100 Subject: [PATCH 180/524] move files for now --- src/{examples/zkapps => lib/mina}/token/call-forest.ts | 0 src/{examples/zkapps => lib/mina}/token/merkle-list.ts | 0 src/{examples/zkapps => lib/mina}/token/token-contract.ts | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/{examples/zkapps => lib/mina}/token/call-forest.ts (100%) rename src/{examples/zkapps => lib/mina}/token/merkle-list.ts (100%) rename src/{examples/zkapps => lib/mina}/token/token-contract.ts (100%) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/lib/mina/token/call-forest.ts similarity index 100% rename from src/examples/zkapps/token/call-forest.ts rename to src/lib/mina/token/call-forest.ts diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts similarity index 100% rename from src/examples/zkapps/token/merkle-list.ts rename to src/lib/mina/token/merkle-list.ts diff --git a/src/examples/zkapps/token/token-contract.ts b/src/lib/mina/token/token-contract.ts similarity index 100% rename from src/examples/zkapps/token/token-contract.ts rename to src/lib/mina/token/token-contract.ts From b49f9175fcdf6a48f29065552da86ec1dc95383b Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 17:14:55 +0100 Subject: [PATCH 181/524] start creating test --- src/lib/mina/token/call-forest.ts | 17 +++++++-- src/lib/mina/token/call-forest.unit-test.ts | 40 +++++++++++++++++++++ src/lib/mina/token/merkle-list.ts | 15 ++++++-- src/lib/testing/random.ts | 3 +- tsconfig.test.json | 1 + 5 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 src/lib/mina/token/call-forest.unit-test.ts diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 090ed664dd..d5e00897ae 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,3 +1,4 @@ +import { prefixes } from '../../../provable/poseidon-bigint.js'; import { AccountUpdate, Field, @@ -6,18 +7,20 @@ import { Provable, Struct, TokenId, -} from 'o1js'; +} from '../../../index.js'; import { MerkleArray, MerkleArrayBase, MerkleList, ProvableHashable, + genericHash, } from './merkle-list.js'; export { CallForest, PartialCallForest }; -class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => - a.hash() +class HashedAccountUpdate extends Hashed.create( + AccountUpdate, + hashAccountUpdate ) {} type CallTree = { @@ -59,6 +62,10 @@ class PartialCallForest { this.unfinishedParentLayers = ParentLayers.empty(); } + static create(forest: CallForest) { + return new PartialCallForest(forest, MayUseToken.ParentsOwnToken); + } + /** * Make a single step along a tree of account updates. * @@ -150,3 +157,7 @@ function hashCons(forestHash: Field, nodeHash: Field) { nodeHash, ]); } + +function hashAccountUpdate(update: AccountUpdate) { + return genericHash(AccountUpdate, prefixes.body, update); +} diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts new file mode 100644 index 0000000000..73521b60a9 --- /dev/null +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -0,0 +1,40 @@ +import { Random, test } from '../../testing/property.js'; +import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; +import { CallForest, PartialCallForest } from './call-forest.js'; +import { AccountUpdate, ZkappCommand } from '../../account_update.js'; +import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; +import { Pickles } from '../../../snarky.js'; +import { Field } from '../../../lib/core.js'; + +// rng for account updates + +let [, data, hashMl] = Pickles.dummyVerificationKey(); +let verificationKey = { data, hash: Field(hashMl) }; + +const accountUpdates = Random.map( + RandomTransaction.zkappCommand, + (txBigint) => { + // bigint to json, then to provable + let txJson = TypesBigint.ZkappCommand.toJSON(txBigint); + + let accountUpdates = txJson.accountUpdates.map((aJson) => { + let a = AccountUpdate.fromJSON(aJson); + + // fix verification key + if (a.body.update.verificationKey.isSome) { + a.body.update.verificationKey.value = verificationKey; + } + return a; + }); + + return accountUpdates; + } +); + +// tests begin here + +test.custom({ timeBudget: 10000 })(accountUpdates, (updates) => { + console.log({ length: updates.length }); + + CallForest.fromAccountUpdates(updates); +}); diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index a82c4f0e56..edd1577079 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -7,8 +7,8 @@ import { Unconstrained, assert, } from 'o1js'; -import { provableFromClass } from 'src/bindings/lib/provable-snarky.js'; -import { packToFields } from 'src/lib/hash.js'; +import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; +import { packToFields } from '../../hash.js'; export { MerkleArray, @@ -19,8 +19,19 @@ export { WithStackHash, emptyHash, ProvableHashable, + genericHash, }; +function genericHash( + provable: ProvableHashable, + prefix: string, + value: T +) { + let input = provable.toInput(value); + let packed = packToFields(input); + return Poseidon.hashWithPrefix(prefix, packed); +} + function merkleListHash(provable: ProvableHashable, prefix = '') { return function nextHash(hash: Field, value: T) { let input = provable.toInput(value); diff --git a/src/lib/testing/random.ts b/src/lib/testing/random.ts index 358b0ac206..12b82802ff 100644 --- a/src/lib/testing/random.ts +++ b/src/lib/testing/random.ts @@ -162,7 +162,8 @@ const accountUpdate = mapWithInvalid( a.body.authorizationKind.isProved = Bool(false); } if (!a.body.authorizationKind.isProved) { - a.body.authorizationKind.verificationKeyHash = Field(0); + a.body.authorizationKind.verificationKeyHash = + VerificationKeyHash.empty(); } // ensure mayUseToken is valid let { inheritFromParent, parentsOwnToken } = a.body.mayUseToken; diff --git a/tsconfig.test.json b/tsconfig.test.json index 85717a9f8c..bd3694b834 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -2,6 +2,7 @@ "extends": "./tsconfig.json", "include": [ "./src/**/*.unit-test.ts", + "./src/lib/**/*.ts", "./src/snarky.js", "./src/bindings/js/wrapper.js" ], From cde3325184c3d3c8c02b1cf37d84c3a46c2323eb Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 17:39:58 +0100 Subject: [PATCH 182/524] compute stack hash that is equivalent between provable & mins-aigner, but not yet the new implementation --- src/lib/mina/token/call-forest.unit-test.ts | 73 +++++++++++++++++---- src/mina-signer/src/sign-zkapp-command.ts | 1 + 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 73521b60a9..9387ff8ba6 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,40 +1,85 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; import { CallForest, PartialCallForest } from './call-forest.js'; -import { AccountUpdate, ZkappCommand } from '../../account_update.js'; +import { + AccountUpdate, + CallForest as ProvableCallForest, +} from '../../account_update.js'; import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; import { Pickles } from '../../../snarky.js'; -import { Field } from '../../../lib/core.js'; +import { + accountUpdatesToCallForest, + callForestHash, + CallForest as SimpleCallForest, +} from '../../../mina-signer/src/sign-zkapp-command.js'; // rng for account updates let [, data, hashMl] = Pickles.dummyVerificationKey(); -let verificationKey = { data, hash: Field(hashMl) }; +let verificationKey = { data, hash: hashMl[1] }; -const accountUpdates = Random.map( +const callForest: Random = Random.map( RandomTransaction.zkappCommand, (txBigint) => { - // bigint to json, then to provable - let txJson = TypesBigint.ZkappCommand.toJSON(txBigint); - - let accountUpdates = txJson.accountUpdates.map((aJson) => { - let a = AccountUpdate.fromJSON(aJson); - + let flatUpdates = txBigint.accountUpdates.map((a) => { // fix verification key if (a.body.update.verificationKey.isSome) { a.body.update.verificationKey.value = verificationKey; } return a; }); - - return accountUpdates; + return accountUpdatesToCallForest(flatUpdates); } ); // tests begin here -test.custom({ timeBudget: 10000 })(accountUpdates, (updates) => { +test.custom({ timeBudget: 10000 })(callForest, (forestBigint) => { + // reference: bigint callforest hash from mina-signer + let stackHash = callForestHash(forestBigint); + + let updates = callForestToNestedArray( + mapCallForest(forestBigint, accountUpdateFromBigint) + ); + console.log({ length: updates.length }); - CallForest.fromAccountUpdates(updates); + let dummyParent = AccountUpdate.dummy(); + dummyParent.children.accountUpdates = updates; + + let hash = ProvableCallForest.hashChildren(dummyParent); + hash.assertEquals(stackHash); + + let forest = CallForest.fromAccountUpdates(updates); }); + +// call forest helpers + +type AbstractSimpleCallForest = { + accountUpdate: A; + children: AbstractSimpleCallForest; +}[]; + +function mapCallForest( + forest: AbstractSimpleCallForest, + mapOne: (a: A) => B +): AbstractSimpleCallForest { + return forest.map(({ accountUpdate, children }) => ({ + accountUpdate: mapOne(accountUpdate), + children: mapCallForest(children, mapOne), + })); +} + +function accountUpdateFromBigint(a: TypesBigint.AccountUpdate): AccountUpdate { + // bigint to json, then to provable + return AccountUpdate.fromJSON(TypesBigint.AccountUpdate.toJSON(a)); +} + +function callForestToNestedArray( + forest: AbstractSimpleCallForest +): AccountUpdate[] { + return forest.map(({ accountUpdate, children }) => { + accountUpdate.children.accountUpdates = callForestToNestedArray(children); + return accountUpdate; + }); +} diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 07d8a37c62..8f810ff96b 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -33,6 +33,7 @@ export { createFeePayer, accountUpdateFromFeePayer, isCallDepthValid, + CallForest, }; function signZkappCommand( From acd039cce26141caa849454d6f6505090a66433b Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 17:48:00 +0100 Subject: [PATCH 183/524] test on wider/deeper trees --- src/lib/mina/token/call-forest.unit-test.ts | 13 ++++++++++--- src/mina-signer/src/random-transaction.ts | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 9387ff8ba6..2d06732feb 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -18,16 +18,23 @@ import { let [, data, hashMl] = Pickles.dummyVerificationKey(); let verificationKey = { data, hash: hashMl[1] }; +let accountUpdates = Random.array( + RandomTransaction.accountUpdateWithCallDepth, + Random.int(0, 50), + { reset: true } +); + const callForest: Random = Random.map( - RandomTransaction.zkappCommand, - (txBigint) => { - let flatUpdates = txBigint.accountUpdates.map((a) => { + accountUpdates, + (accountUpdates) => { + let flatUpdates = accountUpdates.map((a) => { // fix verification key if (a.body.update.verificationKey.isSome) { a.body.update.verificationKey.value = verificationKey; } return a; }); + console.log({ totalLength: flatUpdates.length }); return accountUpdatesToCallForest(flatUpdates); } ); diff --git a/src/mina-signer/src/random-transaction.ts b/src/mina-signer/src/random-transaction.ts index 80ecf9d7f6..18e80bb0a4 100644 --- a/src/mina-signer/src/random-transaction.ts +++ b/src/mina-signer/src/random-transaction.ts @@ -141,4 +141,5 @@ const RandomTransaction = { zkappCommandAndFeePayerKey, zkappCommandJson, networkId: Random.oneOf('testnet', 'mainnet'), + accountUpdateWithCallDepth: accountUpdate, }; From 60e21eb7f00e7715141effbde936f7e0eedb3f2d Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 21:38:10 +0100 Subject: [PATCH 184/524] debugging --- src/lib/mina/token/call-forest.ts | 2 ++ src/lib/mina/token/call-forest.unit-test.ts | 40 +++++++++++++-------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index d5e00897ae..6354199c37 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -18,6 +18,8 @@ import { export { CallForest, PartialCallForest }; +export { HashedAccountUpdate }; + class HashedAccountUpdate extends Hashed.create( AccountUpdate, hashAccountUpdate diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 2d06732feb..538f101344 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,6 +1,6 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { CallForest, PartialCallForest } from './call-forest.js'; +import { CallForest, HashedAccountUpdate } from './call-forest.js'; import { AccountUpdate, CallForest as ProvableCallForest, @@ -41,24 +41,36 @@ const callForest: Random = Random.map( // tests begin here -test.custom({ timeBudget: 10000 })(callForest, (forestBigint) => { - // reference: bigint callforest hash from mina-signer - let stackHash = callForestHash(forestBigint); +test.custom({ timeBudget: 10000, logFailures: false })( + callForest, + (forestBigint) => { + // reference: bigint callforest hash from mina-signer + let stackHash = callForestHash(forestBigint); - let updates = callForestToNestedArray( - mapCallForest(forestBigint, accountUpdateFromBigint) - ); + let updates = callForestToNestedArray( + mapCallForest(forestBigint, accountUpdateFromBigint) + ); - console.log({ length: updates.length }); + console.log({ length: updates.length }); - let dummyParent = AccountUpdate.dummy(); - dummyParent.children.accountUpdates = updates; + let dummyParent = AccountUpdate.dummy(); + dummyParent.children.accountUpdates = updates; - let hash = ProvableCallForest.hashChildren(dummyParent); - hash.assertEquals(stackHash); + let hash = ProvableCallForest.hashChildren(dummyParent); + hash.assertEquals(stackHash); - let forest = CallForest.fromAccountUpdates(updates); -}); + let nodes = updates.map((update) => { + let accountUpdate = HashedAccountUpdate.hash(update); + let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); + return { accountUpdate, calls }; + }); + + console.log({ nodes: nodes.map((n) => n.calls.hash.toBigInt()) }); + + let forest = CallForest.fromAccountUpdates(updates); + forest.hash.assertEquals(stackHash); + } +); // call forest helpers From 8318ed720e59294defeafe352185c50dde11cd22 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 21:38:14 +0100 Subject: [PATCH 185/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index a884dc593d..339a03c984 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a884dc593dbab69e55ab9602b998ec12dfc3a288 +Subproject commit 339a03c984140c10dd8c4e1f3b26438d38550c6a From d36c79f1b10936def78c1cf8e2097bf35ccec61f Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 10:12:13 +0100 Subject: [PATCH 186/524] fix test by fixing order in hashCons --- src/lib/mina/token/call-forest.ts | 6 ++--- src/lib/mina/token/call-forest.unit-test.ts | 27 ++++++--------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 6354199c37..9a4a540388 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -148,15 +148,15 @@ function merkleListHash(forestHash: Field, tree: CallTree) { } function hashNode(tree: CallTree) { - return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ + return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ tree.accountUpdate.hash, tree.calls.hash, ]); } function hashCons(forestHash: Field, nodeHash: Field) { - return Poseidon.hashWithPrefix('MinaAcctUpdateCons**', [ - forestHash, + return Poseidon.hashWithPrefix(prefixes.accountUpdateCons, [ nodeHash, + forestHash, ]); } diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 538f101344..782a4e7d9e 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -39,40 +39,27 @@ const callForest: Random = Random.map( } ); -// tests begin here +// TESTS + +// correctly hashes a call forest test.custom({ timeBudget: 10000, logFailures: false })( callForest, (forestBigint) => { // reference: bigint callforest hash from mina-signer - let stackHash = callForestHash(forestBigint); + let expectedHash = callForestHash(forestBigint); + // convert to o1js-style list of nested `AccountUpdate`s let updates = callForestToNestedArray( mapCallForest(forestBigint, accountUpdateFromBigint) ); - console.log({ length: updates.length }); - - let dummyParent = AccountUpdate.dummy(); - dummyParent.children.accountUpdates = updates; - - let hash = ProvableCallForest.hashChildren(dummyParent); - hash.assertEquals(stackHash); - - let nodes = updates.map((update) => { - let accountUpdate = HashedAccountUpdate.hash(update); - let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); - return { accountUpdate, calls }; - }); - - console.log({ nodes: nodes.map((n) => n.calls.hash.toBigInt()) }); - let forest = CallForest.fromAccountUpdates(updates); - forest.hash.assertEquals(stackHash); + forest.hash.assertEquals(expectedHash); } ); -// call forest helpers +// HELPERS type AbstractSimpleCallForest = { accountUpdate: A; From b0f6c947e2076f9dc226615b3cc0d0f03d8a66db Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 12:08:01 +0100 Subject: [PATCH 187/524] fix hash.empty() and merkleArray.next() --- src/lib/mina/token/merkle-list.ts | 8 ++++---- src/lib/provable-types/packed.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index edd1577079..b5cca46e5a 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -268,7 +268,7 @@ class MerkleArray implements MerkleArrayIterator { array, hash, currentHash: hash, - currentIndex: Unconstrained.from(0), + currentIndex: Unconstrained.from(-1), }); } assertAtStart() { @@ -302,7 +302,7 @@ class MerkleArray implements MerkleArrayIterator { WithHash(this.innerProvable), () => this.array.get()[index.get()] ?? { - previousHash: this.hash, + previousHash: emptyHash, element: this.innerProvable.empty(), } ); @@ -372,8 +372,8 @@ class MerkleArray implements MerkleArrayIterator { return new this({ array: Unconstrained.from(arrayWithHashes), hash: currentHash, - currentHash: currentHash, - currentIndex: Unconstrained.from(0), + currentHash, + currentIndex: Unconstrained.from(-1), }); } diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index ed78bd2b5a..8abbc310f8 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -63,6 +63,10 @@ class Packed { packed: fields(packedSize), value: Unconstrained.provable, }) as ProvableHashable>; + + static empty(): Packed { + return Packed_.pack(type.empty()); + } }; } @@ -190,6 +194,10 @@ class Hashed { }) as ProvableHashable>; static _hash = (hash ?? Hashed._hash) satisfies (t: T) => Field; + + static empty(): Hashed { + return new this(dummyHash, Unconstrained.from(type.empty())); + } }; } From 37f2ba705b3c10767e9980bf1424a0b7cbf3ed27 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 12:08:15 +0100 Subject: [PATCH 188/524] make some code generic for reuse --- src/mina-signer/src/sign-zkapp-command.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 8f810ff96b..c2031731a3 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -123,16 +123,22 @@ function transactionCommitments(zkappCommand: ZkappCommand) { return { commitment, fullCommitment }; } -type CallTree = { accountUpdate: AccountUpdate; children: CallForest }; -type CallForest = CallTree[]; +type CallTree = { + accountUpdate: AccountUpdate; + children: CallForest; +}; +type CallForest = CallTree[]; /** * Turn flat list into a hierarchical structure (forest) by letting the callDepth * determine parent-child relationships */ -function accountUpdatesToCallForest(updates: AccountUpdate[], callDepth = 0) { +function accountUpdatesToCallForest( + updates: A[], + callDepth = 0 +) { let remainingUpdates = callDepth > 0 ? updates : [...updates]; - let forest: CallForest = []; + let forest: CallForest = []; while (remainingUpdates.length > 0) { let accountUpdate = remainingUpdates[0]; if (accountUpdate.body.callDepth < callDepth) return forest; @@ -150,7 +156,7 @@ function accountUpdateHash(update: AccountUpdate) { return hashWithPrefix(prefixes.body, fields); } -function callForestHash(forest: CallForest): Field { +function callForestHash(forest: CallForest): Field { let stackHash = 0n; for (let callTree of [...forest].reverse()) { let calls = callForestHash(callTree.children); From d561ee0e957fd105df5067b910ae6f49042a888b Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 12:42:32 +0100 Subject: [PATCH 189/524] refactor, start test that traverses the tree --- src/lib/mina/token/call-forest.ts | 22 ++-- src/lib/mina/token/call-forest.unit-test.ts | 111 +++++++++++++------- 2 files changed, 85 insertions(+), 48 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 9a4a540388..a60a93ea1b 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -16,7 +16,7 @@ import { genericHash, } from './merkle-list.js'; -export { CallForest, PartialCallForest }; +export { CallForest, PartialCallForest, hashAccountUpdate }; export { HashedAccountUpdate }; @@ -58,14 +58,20 @@ const MayUseToken = AccountUpdate.MayUseToken; class PartialCallForest { currentLayer: Layer; unfinishedParentLayers: MerkleList; + selfToken: Field; - constructor(forest: CallForest, mayUseToken: MayUseToken) { + constructor(forest: CallForest, mayUseToken: MayUseToken, selfToken: Field) { this.currentLayer = { forest, mayUseToken }; this.unfinishedParentLayers = ParentLayers.empty(); + this.selfToken = selfToken; } - static create(forest: CallForest) { - return new PartialCallForest(forest, MayUseToken.ParentsOwnToken); + static create(forest: CallForest, selfToken: Field) { + return new PartialCallForest( + forest, + MayUseToken.ParentsOwnToken, + selfToken + ); } /** @@ -82,7 +88,7 @@ class PartialCallForest { * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the * caller handle the irrelevant case where `usesThisToken` is false. */ - nextAccountUpdate(selfToken: Field) { + next() { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); @@ -103,11 +109,11 @@ class PartialCallForest { this.currentLayer.mayUseToken ); let isSelf = TokenId.derive(update.publicKey, update.tokenId).equals( - selfToken + this.selfToken ); let usesThisToken = update.tokenId - .equals(selfToken) + .equals(this.selfToken) .and(canAccessThisToken); // if we don't have to check the children, ignore the forest by jumping to its end @@ -137,7 +143,7 @@ class PartialCallForest { parentLayers ); - return { update, usesThisToken }; + return { accountUpdate: update, usesThisToken }; } } diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 782a4e7d9e..0d3d792bf9 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,9 +1,15 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { CallForest, HashedAccountUpdate } from './call-forest.js'; +import { + CallForest, + HashedAccountUpdate, + PartialCallForest, + hashAccountUpdate, +} from './call-forest.js'; import { AccountUpdate, CallForest as ProvableCallForest, + TokenId, } from '../../account_update.js'; import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; import { Pickles } from '../../../snarky.js'; @@ -12,46 +18,47 @@ import { callForestHash, CallForest as SimpleCallForest, } from '../../../mina-signer/src/sign-zkapp-command.js'; +import assert from 'assert'; +import { Field } from '../../field.js'; -// rng for account updates +// RANDOM NUMBER GENERATORS for account updates let [, data, hashMl] = Pickles.dummyVerificationKey(); -let verificationKey = { data, hash: hashMl[1] }; +let dummyVerificationKey = { data, hash: hashMl[1] }; -let accountUpdates = Random.array( +const accountUpdateBigint = Random.map( RandomTransaction.accountUpdateWithCallDepth, - Random.int(0, 50), - { reset: true } -); - -const callForest: Random = Random.map( - accountUpdates, - (accountUpdates) => { - let flatUpdates = accountUpdates.map((a) => { - // fix verification key - if (a.body.update.verificationKey.isSome) { - a.body.update.verificationKey.value = verificationKey; - } - return a; - }); - console.log({ totalLength: flatUpdates.length }); - return accountUpdatesToCallForest(flatUpdates); + (a) => { + // fix verification key + if (a.body.update.verificationKey.isSome) + a.body.update.verificationKey.value = dummyVerificationKey; + return a; } ); +const accountUpdate = Random.map(accountUpdateBigint, accountUpdateFromBigint); + +const arrayOf = (x: Random) => + // `reset: true` to start callDepth at 0 for each array + Random.array(x, Random.int(20, 50), { reset: true }); + +const flatAccountUpdatesBigint = arrayOf(accountUpdateBigint); +const flatAccountUpdates = arrayOf(accountUpdate); // TESTS // correctly hashes a call forest -test.custom({ timeBudget: 10000, logFailures: false })( - callForest, - (forestBigint) => { +test.custom({ timeBudget: 1000, logFailures: false })( + flatAccountUpdatesBigint, + (flatUpdatesBigint) => { // reference: bigint callforest hash from mina-signer + let forestBigint = accountUpdatesToCallForest(flatUpdatesBigint); let expectedHash = callForestHash(forestBigint); // convert to o1js-style list of nested `AccountUpdate`s + let flatUpdates = flatUpdatesBigint.map(accountUpdateFromBigint); let updates = callForestToNestedArray( - mapCallForest(forestBigint, accountUpdateFromBigint) + accountUpdatesToCallForest(flatUpdates) ); let forest = CallForest.fromAccountUpdates(updates); @@ -59,22 +66,46 @@ test.custom({ timeBudget: 10000, logFailures: false })( } ); -// HELPERS +// traverses a call forest in correct depth-first order -type AbstractSimpleCallForest = { - accountUpdate: A; - children: AbstractSimpleCallForest; -}[]; - -function mapCallForest( - forest: AbstractSimpleCallForest, - mapOne: (a: A) => B -): AbstractSimpleCallForest { - return forest.map(({ accountUpdate, children }) => ({ - accountUpdate: mapOne(accountUpdate), - children: mapCallForest(children, mapOne), - })); -} +test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( + flatAccountUpdates, + (flatUpdates) => { + // convert to o1js-style list of nested `AccountUpdate`s + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + + let forest = CallForest.fromAccountUpdates(updates); + let tokenId = Field.random(); + let partialForest = PartialCallForest.create(forest, tokenId); + + let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + + let n = flatUpdates.length; + for (let i = 0; i < n; i++) { + assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); + + let expected = flatUpdates[i]; + let actual = partialForest.next().accountUpdate; + + console.log( + 'expected: ', + expected.body.callDepth, + expected.body.publicKey.toBase58(), + hashAccountUpdate(expected).toBigInt() + ); + console.log( + 'actual: ', + actual.body.callDepth, + actual.body.publicKey.toBase58(), + hashAccountUpdate(actual).toBigInt() + ); + } + } +); + +// HELPERS function accountUpdateFromBigint(a: TypesBigint.AccountUpdate): AccountUpdate { // bigint to json, then to provable @@ -82,7 +113,7 @@ function accountUpdateFromBigint(a: TypesBigint.AccountUpdate): AccountUpdate { } function callForestToNestedArray( - forest: AbstractSimpleCallForest + forest: SimpleCallForest ): AccountUpdate[] { return forest.map(({ accountUpdate, children }) => { accountUpdate.children.accountUpdates = callForestToNestedArray(children); From 0550703beacf2d9b2d6ac0c8ad884ee031e24f79 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 13:07:08 +0100 Subject: [PATCH 190/524] confirm that callforest.next() works --- src/lib/mina/token/call-forest.unit-test.ts | 63 ++++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 0d3d792bf9..291d84660d 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -39,7 +39,7 @@ const accountUpdate = Random.map(accountUpdateBigint, accountUpdateFromBigint); const arrayOf = (x: Random) => // `reset: true` to start callDepth at 0 for each array - Random.array(x, Random.int(20, 50), { reset: true }); + Random.array(x, 10, { reset: true }); const flatAccountUpdatesBigint = arrayOf(accountUpdateBigint); const flatAccountUpdates = arrayOf(accountUpdate); @@ -66,6 +66,31 @@ test.custom({ timeBudget: 1000, logFailures: false })( } ); +// traverses the top level of a call forest in correct order +// i.e., CallForest.next() works + +test.custom({ timeBudget: 10000, logFailures: false })( + flatAccountUpdates, + (flatUpdates) => { + // convert to o1js-style list of nested `AccountUpdate`s + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + let forest = CallForest.fromAccountUpdates(updates); + + let n = updates.length; + for (let i = 0; i < n; i++) { + let expected = updates[i]; + let actual = forest.next().accountUpdate.value.get(); + assertEqual(actual, expected); + } + + // doing next again should return a dummy + let actual = forest.next().accountUpdate.value.get(); + assertEqual(actual, AccountUpdate.dummy()); + } +); + // traverses a call forest in correct depth-first order test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( @@ -76,6 +101,10 @@ test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( accountUpdatesToCallForest(flatUpdates) ); + let dummyParent = AccountUpdate.dummy(); + dummyParent.children.accountUpdates = updates; + console.log(dummyParent.toPrettyLayout()); + let forest = CallForest.fromAccountUpdates(updates); let tokenId = Field.random(); let partialForest = PartialCallForest.create(forest, tokenId); @@ -87,20 +116,31 @@ test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); let expected = flatUpdates[i]; + let expectedHash = hashAccountUpdate(expected).toBigInt(); let actual = partialForest.next().accountUpdate; + let actualHash = hashAccountUpdate(actual).toBigInt(); + + let isCorrect = String(expectedHash === actualHash).padStart(5, ' '); + console.log( + 'actual: ', + actual.body.callDepth, + isCorrect, + actual.body.publicKey.toBase58(), + actualHash + ); + } + console.log(); + + for (let i = 0; i < n; i++) { + let expected = flatUpdates[i]; console.log( 'expected: ', expected.body.callDepth, + ' true', expected.body.publicKey.toBase58(), hashAccountUpdate(expected).toBigInt() ); - console.log( - 'actual: ', - actual.body.callDepth, - actual.body.publicKey.toBase58(), - hashAccountUpdate(actual).toBigInt() - ); } } ); @@ -120,3 +160,12 @@ function callForestToNestedArray( return accountUpdate; }); } + +function assertEqual(actual: AccountUpdate, expected: AccountUpdate) { + let actualHash = hashAccountUpdate(actual).toBigInt(); + let expectedHash = hashAccountUpdate(expected).toBigInt(); + + assert.deepStrictEqual(actual.body, expected.body); + assert.deepStrictEqual(actual.authorization, expected.authorization); + assert.deepStrictEqual(actualHash, expectedHash); +} From eea09cdffbe56403f002c38d94a5f4cbeeb77160 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 14:04:46 +0100 Subject: [PATCH 191/524] it actually works when not skipping subtrees --- src/lib/mina/token/call-forest.ts | 8 ++-- src/lib/mina/token/call-forest.unit-test.ts | 52 ++++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index a60a93ea1b..607a1c365a 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,7 +1,6 @@ import { prefixes } from '../../../provable/poseidon-bigint.js'; import { AccountUpdate, - Field, Hashed, Poseidon, Provable, @@ -15,6 +14,7 @@ import { ProvableHashable, genericHash, } from './merkle-list.js'; +import { Field, Bool } from '../../core.js'; export { CallForest, PartialCallForest, hashAccountUpdate }; @@ -88,7 +88,7 @@ class PartialCallForest { * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the * caller handle the irrelevant case where `usesThisToken` is false. */ - next() { + next({ skipSubtrees = true } = {}) { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); @@ -117,7 +117,9 @@ class PartialCallForest { .and(canAccessThisToken); // if we don't have to check the children, ignore the forest by jumping to its end - let skipSubtree = canAccessThisToken.not().or(isSelf); + let skipSubtree = skipSubtrees + ? canAccessThisToken.not().or(isSelf) + : new Bool(false); forest.jumpToEndIf(skipSubtree); // if we're at the end of the current layer, step up to the next unfinished parent layer diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 291d84660d..ae5f79abb6 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -39,7 +39,7 @@ const accountUpdate = Random.map(accountUpdateBigint, accountUpdateFromBigint); const arrayOf = (x: Random) => // `reset: true` to start callDepth at 0 for each array - Random.array(x, 10, { reset: true }); + Random.array(x, Random.int(10, 40), { reset: true }); const flatAccountUpdatesBigint = arrayOf(accountUpdateBigint); const flatAccountUpdates = arrayOf(accountUpdate); @@ -69,7 +69,7 @@ test.custom({ timeBudget: 1000, logFailures: false })( // traverses the top level of a call forest in correct order // i.e., CallForest.next() works -test.custom({ timeBudget: 10000, logFailures: false })( +test.custom({ timeBudget: 1000, logFailures: false })( flatAccountUpdates, (flatUpdates) => { // convert to o1js-style list of nested `AccountUpdate`s @@ -85,13 +85,46 @@ test.custom({ timeBudget: 10000, logFailures: false })( assertEqual(actual, expected); } - // doing next again should return a dummy + // doing next() again should return a dummy let actual = forest.next().accountUpdate.value.get(); assertEqual(actual, AccountUpdate.dummy()); } ); -// traverses a call forest in correct depth-first order +// traverses a call forest in correct depth-first order, +// assuming we don't skip any subtrees + +test.custom({ timeBudget: 10000, logFailures: false })( + flatAccountUpdates, + (flatUpdates) => { + // convert to o1js-style list of nested `AccountUpdate`s + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + + let forest = CallForest.fromAccountUpdates(updates); + let tokenId = Field.random(); + let partialForest = PartialCallForest.create(forest, tokenId); + + let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + + let n = flatUpdates.length; + for (let i = 0; i < n; i++) { + assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); + + let expected = flatUpdates[i]; + let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; + assertEqual(actual, expected); + } + + // doing next() again should return a dummy + let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; + assertEqual(actual, AccountUpdate.dummy()); + } +); + +// TODO +// traverses a call forest in correct depth-first order, when skipping subtrees test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( flatAccountUpdates, @@ -117,7 +150,7 @@ test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( let expected = flatUpdates[i]; let expectedHash = hashAccountUpdate(expected).toBigInt(); - let actual = partialForest.next().accountUpdate; + let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; let actualHash = hashAccountUpdate(actual).toBigInt(); let isCorrect = String(expectedHash === actualHash).padStart(5, ' '); @@ -166,6 +199,13 @@ function assertEqual(actual: AccountUpdate, expected: AccountUpdate) { let expectedHash = hashAccountUpdate(expected).toBigInt(); assert.deepStrictEqual(actual.body, expected.body); - assert.deepStrictEqual(actual.authorization, expected.authorization); + assert.deepStrictEqual( + actual.authorization.proof, + expected.authorization.proof + ); + assert.deepStrictEqual( + actual.authorization.signature, + expected.authorization.signature + ); assert.deepStrictEqual(actualHash, expectedHash); } From 2471c1006e47bf36bb4077074fc52701df82f2b4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 16:35:01 +0100 Subject: [PATCH 192/524] finish unit tests for call forest iteration --- src/lib/mina/token/call-forest.ts | 6 +- src/lib/mina/token/call-forest.unit-test.ts | 214 ++++++++++++-------- 2 files changed, 137 insertions(+), 83 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 607a1c365a..146f97d1ff 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -88,7 +88,7 @@ class PartialCallForest { * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the * caller handle the irrelevant case where `usesThisToken` is false. */ - next({ skipSubtrees = true } = {}) { + next() { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); @@ -117,9 +117,7 @@ class PartialCallForest { .and(canAccessThisToken); // if we don't have to check the children, ignore the forest by jumping to its end - let skipSubtree = skipSubtrees - ? canAccessThisToken.not().or(isSelf) - : new Bool(false); + let skipSubtree = canAccessThisToken.not().or(isSelf); forest.jumpToEndIf(skipSubtree); // if we're at the end of the current layer, step up to the next unfinished parent layer diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index ae5f79abb6..de62ceb354 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -2,8 +2,7 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; import { CallForest, - HashedAccountUpdate, - PartialCallForest, + PartialCallForest as CallForestIterator, hashAccountUpdate, } from './call-forest.js'; import { @@ -19,7 +18,9 @@ import { CallForest as SimpleCallForest, } from '../../../mina-signer/src/sign-zkapp-command.js'; import assert from 'assert'; -import { Field } from '../../field.js'; +import { Field, Bool } from '../../core.js'; +import { Bool as BoolB } from '../../../provable/field-bigint.js'; +import { PublicKey } from '../../signature.js'; // RANDOM NUMBER GENERATORS for account updates @@ -32,6 +33,12 @@ const accountUpdateBigint = Random.map( // fix verification key if (a.body.update.verificationKey.isSome) a.body.update.verificationKey.value = dummyVerificationKey; + + // ensure that, by default, all account updates are token-accessible + a.body.mayUseToken = + a.body.callDepth === 0 + ? { parentsOwnToken: BoolB(true), inheritFromParent: BoolB(false) } + : { parentsOwnToken: BoolB(false), inheritFromParent: BoolB(true) }; return a; } ); @@ -48,7 +55,7 @@ const flatAccountUpdates = arrayOf(accountUpdate); // correctly hashes a call forest -test.custom({ timeBudget: 1000, logFailures: false })( +test.custom({ timeBudget: 1000 })( flatAccountUpdatesBigint, (flatUpdatesBigint) => { // reference: bigint callforest hash from mina-signer @@ -66,114 +73,163 @@ test.custom({ timeBudget: 1000, logFailures: false })( } ); +// can recover flat account updates from nested updates +// this is here to assert that we compute `updates` correctly in the other tests + +test(flatAccountUpdates, (flatUpdates) => { + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + let n = flatUpdates.length; + for (let i = 0; i < n; i++) { + assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); + } +}); + // traverses the top level of a call forest in correct order // i.e., CallForest.next() works -test.custom({ timeBudget: 1000, logFailures: false })( - flatAccountUpdates, - (flatUpdates) => { - // convert to o1js-style list of nested `AccountUpdate`s - let updates = callForestToNestedArray( - accountUpdatesToCallForest(flatUpdates) - ); - let forest = CallForest.fromAccountUpdates(updates); - - let n = updates.length; - for (let i = 0; i < n; i++) { - let expected = updates[i]; - let actual = forest.next().accountUpdate.value.get(); - assertEqual(actual, expected); - } +test.custom({ timeBudget: 1000 })(flatAccountUpdates, (flatUpdates) => { + // prepare call forest from flat account updates + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + let forest = CallForest.fromAccountUpdates(updates); - // doing next() again should return a dummy + // step through top-level by calling forest.next() repeatedly + let n = updates.length; + for (let i = 0; i < n; i++) { + let expected = updates[i]; let actual = forest.next().accountUpdate.value.get(); - assertEqual(actual, AccountUpdate.dummy()); + assertEqual(actual, expected); } -); + + // doing next() again should return a dummy + let actual = forest.next().accountUpdate.value.get(); + assertEqual(actual, AccountUpdate.dummy()); +}); // traverses a call forest in correct depth-first order, -// assuming we don't skip any subtrees +// when no subtrees are skipped + +test.custom({ timeBudget: 5000 })(flatAccountUpdates, (flatUpdates) => { + // with default token id, no subtrees will be skipped + let tokenId = TokenId.default; -test.custom({ timeBudget: 10000, logFailures: false })( + // prepare forest iterator from flat account updates + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + let forest = CallForest.fromAccountUpdates(updates); + let forestIterator = CallForestIterator.create(forest, tokenId); + + // step through forest iterator and compare against expected updates + let expectedUpdates = flatUpdates; + + let n = flatUpdates.length; + for (let i = 0; i < n; i++) { + let expected = expectedUpdates[i]; + let actual = forestIterator.next().accountUpdate; + assertEqual(actual, expected); + } + + // doing next() again should return a dummy + let actual = forestIterator.next().accountUpdate; + assertEqual(actual, AccountUpdate.dummy()); +}); + +// correctly skips subtrees for various reasons + +// in this test, we make all updates inaccessible by setting the top level to `no` or `inherit`, or to the token owner +// this means we wouldn't need to traverse any update in the whole tree +// but we only notice inaccessibility when we have already traversed the inaccessible update +// so, the result should be that we traverse the top level and nothing else +test.custom({ timeBudget: 5000 })( flatAccountUpdates, - (flatUpdates) => { - // convert to o1js-style list of nested `AccountUpdate`s + Random.publicKey, + (flatUpdates, publicKey) => { + // create token owner and derive token id + let tokenOwner = PublicKey.fromObject({ + x: Field.from(publicKey.x), + isOdd: Bool(!!publicKey.isOdd), + }); + let tokenId = TokenId.derive(tokenOwner); + + // prepare forest iterator from flat account updates let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); + // make all top-level updates inaccessible + updates.forEach((u, i) => { + if (i % 3 === 0) { + u.body.mayUseToken = AccountUpdate.MayUseToken.No; + } else if (i % 3 === 1) { + u.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; + } else { + u.body.publicKey = tokenOwner; + u.body.tokenId = TokenId.default; + } + }); + let forest = CallForest.fromAccountUpdates(updates); - let tokenId = Field.random(); - let partialForest = PartialCallForest.create(forest, tokenId); + let forestIterator = CallForestIterator.create(forest, tokenId); - let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + // step through forest iterator and compare against expected updates + let expectedUpdates = updates; let n = flatUpdates.length; for (let i = 0; i < n; i++) { - assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); - - let expected = flatUpdates[i]; - let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; + let expected = expectedUpdates[i] ?? AccountUpdate.dummy(); + let actual = forestIterator.next().accountUpdate; assertEqual(actual, expected); } - - // doing next() again should return a dummy - let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; - assertEqual(actual, AccountUpdate.dummy()); } ); -// TODO -// traverses a call forest in correct depth-first order, when skipping subtrees - -test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( +// similar to the test before, but now we make all updates in the second layer inaccessible +// so the iteration should walk through the first and second layer +test.custom({ timeBudget: 5000 })( flatAccountUpdates, - (flatUpdates) => { - // convert to o1js-style list of nested `AccountUpdate`s + Random.publicKey, + (flatUpdates, publicKey) => { + // create token owner and derive token id + let tokenOwner = PublicKey.fromObject({ + x: Field.from(publicKey.x), + isOdd: Bool(!!publicKey.isOdd), + }); + let tokenId = TokenId.derive(tokenOwner); + + // make all second-level updates inaccessible + flatUpdates + .filter((u) => u.body.callDepth === 1) + .forEach((u, i) => { + if (i % 3 === 0) { + u.body.mayUseToken = AccountUpdate.MayUseToken.No; + } else if (i % 3 === 1) { + u.body.mayUseToken = AccountUpdate.MayUseToken.ParentsOwnToken; + } else { + u.body.publicKey = tokenOwner; + u.body.tokenId = TokenId.default; + } + }); + + // prepare forest iterator from flat account updates let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - - let dummyParent = AccountUpdate.dummy(); - dummyParent.children.accountUpdates = updates; - console.log(dummyParent.toPrettyLayout()); - let forest = CallForest.fromAccountUpdates(updates); - let tokenId = Field.random(); - let partialForest = PartialCallForest.create(forest, tokenId); + let forestIterator = CallForestIterator.create(forest, tokenId); - let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + // step through forest iterator and compare against expected updates + let expectedUpdates = flatUpdates.filter((u) => u.body.callDepth <= 1); let n = flatUpdates.length; for (let i = 0; i < n; i++) { - assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); - - let expected = flatUpdates[i]; - let expectedHash = hashAccountUpdate(expected).toBigInt(); - let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; - let actualHash = hashAccountUpdate(actual).toBigInt(); - - let isCorrect = String(expectedHash === actualHash).padStart(5, ' '); - - console.log( - 'actual: ', - actual.body.callDepth, - isCorrect, - actual.body.publicKey.toBase58(), - actualHash - ); - } - console.log(); - - for (let i = 0; i < n; i++) { - let expected = flatUpdates[i]; - console.log( - 'expected: ', - expected.body.callDepth, - ' true', - expected.body.publicKey.toBase58(), - hashAccountUpdate(expected).toBigInt() - ); + let expected = expectedUpdates[i] ?? AccountUpdate.dummy(); + let actual = forestIterator.next().accountUpdate; + assertEqual(actual, expected); } } ); From b3ca62e624809e600a87f68bc7e043795dcc1034 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 18:16:02 +0100 Subject: [PATCH 193/524] rename, doccomments --- src/lib/mina/token/call-forest.ts | 37 +++++++++++++++------ src/lib/mina/token/call-forest.unit-test.ts | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 146f97d1ff..8b2d953d65 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -14,9 +14,9 @@ import { ProvableHashable, genericHash, } from './merkle-list.js'; -import { Field, Bool } from '../../core.js'; +import { Field } from '../../core.js'; -export { CallForest, PartialCallForest, hashAccountUpdate }; +export { CallForest, CallForestIterator, hashAccountUpdate }; export { HashedAccountUpdate }; @@ -55,7 +55,13 @@ const ParentLayers = MerkleList.create(Layer); type MayUseToken = AccountUpdate['body']['mayUseToken']; const MayUseToken = AccountUpdate.MayUseToken; -class PartialCallForest { +/** + * Data structure to represent a forest tree of account updates that is being iterated over. + * + * Important: Since this is to be used for token manager contracts to process it's entire subtree + * of account updates, the iterator skips subtrees that don't inherit token permissions. + */ +class CallForestIterator { currentLayer: Layer; unfinishedParentLayers: MerkleList; selfToken: Field; @@ -67,7 +73,7 @@ class PartialCallForest { } static create(forest: CallForest, selfToken: Field) { - return new PartialCallForest( + return new CallForestIterator( forest, MayUseToken.ParentsOwnToken, selfToken @@ -80,9 +86,6 @@ class PartialCallForest { * This function is guaranteed to visit each account update in the tree that uses the token * exactly once, when called repeatedly. * - * The internal state of `PartialCallForest` represents the work still to be done, and - * can be passed from one proof to the next. - * * The method makes a best effort to avoid visiting account updates that are not using the token, * and in particular, to avoid returning dummy updates. * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the @@ -112,9 +115,7 @@ class PartialCallForest { this.selfToken ); - let usesThisToken = update.tokenId - .equals(this.selfToken) - .and(canAccessThisToken); + let usesThisToken = update.tokenId.equals(this.selfToken); // if we don't have to check the children, ignore the forest by jumping to its end let skipSubtree = canAccessThisToken.not().or(isSelf); @@ -147,6 +148,22 @@ class PartialCallForest { } } +// helper class to represent the position in a tree = the last visited node + +// every entry in the array is a layer +// so if there are two entries, we last visited a node in the second layer +// this index is the index of the node in that layer +type TreePosition = { index: number; isDone: boolean }[]; +// const TreePosition = { +// stepDown(position: TreePosition, numberOfChildren: number) { +// position.push({ index: 0, isDone: false }); +// }, +// stepUp(position: TreePosition) { +// position.pop(); +// position[position.length - 1].index++; +// }, +// }; + // how to hash a forest function merkleListHash(forestHash: Field, tree: CallTree) { diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index de62ceb354..11b1ea3592 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -2,7 +2,7 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; import { CallForest, - PartialCallForest as CallForestIterator, + CallForestIterator, hashAccountUpdate, } from './call-forest.js'; import { From 2b0dbf5c283cafd1eebbd50522e9199bc09cd1c9 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Thu, 18 Jan 2024 16:19:18 -0800 Subject: [PATCH 194/524] chore(bindings): update submodule to commit 29167d --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index a884dc593d..29167d5395 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a884dc593dbab69e55ab9602b998ec12dfc3a288 +Subproject commit 29167d53953ee2134b8bf764a56d1cb5ada2726d From 2ed13d9a06299b243b18cce919d02307bcd8a4c8 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Fri, 19 Jan 2024 09:44:17 -0800 Subject: [PATCH 195/524] docs(CHANGELOG.md): update changelog with performance improvement details for Poseidon hashing --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dadf1323e4..0e8c745f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix approving of complex account update layouts https://github.com/o1-labs/o1js/pull/1364 +### Changed + +- Improve performance of Poseidon hashing by a factor of 13x https://github.com/o1-labs/o1js/pull/1378 + ## [0.15.2](https://github.com/o1-labs/o1js/compare/1ad7333e9e...08ba27329) ### Fixed From 88998ceab0518baeab111b1c4f261a1baad7cd8d Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Wed, 24 Jan 2024 10:48:30 -0800 Subject: [PATCH 196/524] docs(CHANGELOG.md): move Poseidon hashing performance improvement note to 'Unreleased' section This change is made to correctly reflect the status of the improvement, as it has not been released yet. --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 998774da6f..0594d6dc9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/be748e42e...HEAD) +### Changed + +- Improve performance of Poseidon hashing by a factor of 13x https://github.com/o1-labs/o1js/pull/1378 + ## [0.15.3](https://github.com/o1-labs/o1js/compare/1ad7333e9e...be748e42e) ### Breaking changes @@ -34,10 +38,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix approving of complex account update layouts https://github.com/o1-labs/o1js/pull/1364 -### Changed - -- Improve performance of Poseidon hashing by a factor of 13x https://github.com/o1-labs/o1js/pull/1378 - ## [0.15.2](https://github.com/o1-labs/o1js/compare/1ad7333e9e...08ba27329) ### Fixed From d967878eac0cb04f4e5de3715f118ca5086d9bd6 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Wed, 24 Jan 2024 10:54:18 -0800 Subject: [PATCH 197/524] chore(bindings): update submodule to commit 6df837 --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 29167d5395..6df837ccf6 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 29167d53953ee2134b8bf764a56d1cb5ada2726d +Subproject commit 6df837ccf654d3c1cc027a78718cca0eb2299e96 From cc0e391ed7b33c88fb2e8c31c5de35198b0fe24d Mon Sep 17 00:00:00 2001 From: Barrie Byron Date: Wed, 24 Jan 2024 14:36:07 -0500 Subject: [PATCH 198/524] describe hashing example and link to docs --- src/examples/zkapps/hashing/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/examples/zkapps/hashing/README.md b/src/examples/zkapps/hashing/README.md index 8b13789179..0193400352 100644 --- a/src/examples/zkapps/hashing/README.md +++ b/src/examples/zkapps/hashing/README.md @@ -1 +1,5 @@ +Hash functions under the `Hash` namespace in o1js require binary arithmetic and operate over binary data. +This example shows how to extend the `Bytes` class and specify the length of bytes. + +See the [Keccak](https://docs.minaprotocol.com/zkapps/o1js/keccak) and [SHA-256](https://docs.minaprotocol.com/zkapps/o1js/sha256) documentation for o1js. From 1a52cc0c60c6e76e33af0fe0044b4239058c8799 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 11:09:41 +0100 Subject: [PATCH 199/524] unify base types of merklelist/array --- src/lib/mina/token/call-forest.ts | 6 +- src/lib/mina/token/merkle-list.ts | 164 +++++++++++++++--------------- 2 files changed, 83 insertions(+), 87 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 8b2d953d65..36d0840578 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -9,7 +9,7 @@ import { } from '../../../index.js'; import { MerkleArray, - MerkleArrayBase, + MerkleListBase, MerkleList, ProvableHashable, genericHash, @@ -27,11 +27,11 @@ class HashedAccountUpdate extends Hashed.create( type CallTree = { accountUpdate: Hashed; - calls: MerkleArrayBase; + calls: MerkleListBase; }; const CallTree: ProvableHashable = Struct({ accountUpdate: HashedAccountUpdate.provable, - calls: MerkleArrayBase(), + calls: MerkleListBase(), }); class CallForest extends MerkleArray.create(CallTree, merkleListHash) { diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index b5cca46e5a..ae0164f5c3 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -11,42 +11,49 @@ import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; import { packToFields } from '../../hash.js'; export { - MerkleArray, - MerkleArrayIterator, - MerkleArrayBase, + MerkleListBase, MerkleList, + MerkleListIterator, + MerkleArray, WithHash, - WithStackHash, emptyHash, ProvableHashable, genericHash, + merkleListHash, }; -function genericHash( - provable: ProvableHashable, - prefix: string, - value: T -) { - let input = provable.toInput(value); - let packed = packToFields(input); - return Poseidon.hashWithPrefix(prefix, packed); +// common base types for both MerkleList and MerkleArray + +const emptyHash = Field(0); + +type WithHash = { previousHash: Field; element: T }; + +function WithHash(type: ProvableHashable): ProvableHashable> { + return Struct({ previousHash: Field, element: type }); } -function merkleListHash(provable: ProvableHashable, prefix = '') { - return function nextHash(hash: Field, value: T) { - let input = provable.toInput(value); - let packed = packToFields(input); - return Poseidon.hashWithPrefix(prefix, [hash, ...packed]); +type MerkleListBase = { + hash: Field; + data: Unconstrained[]>; +}; + +function MerkleListBase(): ProvableHashable> { + return class extends Struct({ hash: Field, data: Unconstrained.provable }) { + static empty(): MerkleListBase { + return { hash: emptyHash, data: Unconstrained.from([]) }; + } }; } -class MerkleList { +// merkle list + +class MerkleList implements MerkleListBase { hash: Field; - stack: Unconstrained[]>; + data: Unconstrained[]>; - constructor({ hash, stack }: WithStackHash) { + constructor({ hash, data }: MerkleListBase) { this.hash = hash; - this.stack = stack; + this.data = data; } isEmpty() { @@ -60,7 +67,7 @@ class MerkleList { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); Provable.asProver(() => { - this.stack.set([...this.stack.get(), { previousHash, element }]); + this.data.set([...this.data.get(), { previousHash, element }]); }); } @@ -73,19 +80,19 @@ class MerkleList { ); Provable.asProver(() => { if (condition.toBoolean()) { - this.stack.set([...this.stack.get(), { previousHash, element }]); + this.data.set([...this.data.get(), { previousHash, element }]); } }); } private popWitness() { return Provable.witness(WithHash(this.innerProvable), () => { - let value = this.stack.get(); + let value = this.data.get(); let head = value.at(-1) ?? { previousHash: emptyHash, element: this.innerProvable.empty(), }; - this.stack.set(value.slice(0, -1)); + this.data.set(value.slice(0, -1)); return head; }); } @@ -114,8 +121,8 @@ class MerkleList { } clone(): MerkleList { - let stack = Unconstrained.witness(() => [...this.stack.get()]); - return new this.Constructor({ hash: this.hash, stack }); + let data = Unconstrained.witness(() => [...this.data.get()]); + return new this.Constructor({ hash: this.hash, data }); } /** @@ -125,6 +132,7 @@ class MerkleList { type: ProvableHashable, nextHash: (hash: Field, value: T) => Field = merkleListHash(type) ): typeof MerkleList & { + // override static methods with strict types empty: () => MerkleList; provable: ProvableHashable>; } { @@ -133,13 +141,13 @@ class MerkleList { static _provable = provableFromClass(MerkleList_, { hash: Field, - stack: Unconstrained.provable, + data: Unconstrained.provable, }) as ProvableHashable>; static _nextHash = nextHash; static empty(): MerkleList { - return new this({ hash: emptyHash, stack: Unconstrained.from([]) }); + return new this({ hash: emptyHash, data: Unconstrained.from([]) }); } static get provable(): ProvableHashable> { @@ -176,25 +184,6 @@ class MerkleList { } } -type WithHash = { previousHash: Field; element: T }; -function WithHash(type: ProvableHashable): Provable> { - return Struct({ previousHash: Field, element: type }); -} - -const emptyHash = Field(0); - -type WithStackHash = { - hash: Field; - stack: Unconstrained[]>; -}; -function WithStackHash(): ProvableHashable> { - return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { - static empty(): WithStackHash { - return { hash: emptyHash, stack: Unconstrained.from([]) }; - } - }; -} - type HashInput = { fields?: Field[]; packed?: [Field, number][] }; type ProvableHashable = Provable & { toInput: (x: T) => HashInput; @@ -203,38 +192,25 @@ type ProvableHashable = Provable & { // merkle array -type MerkleArrayBase = { - readonly array: Unconstrained[]>; - readonly hash: Field; -}; - -function MerkleArrayBase(): ProvableHashable> { - return class extends Struct({ array: Unconstrained.provable, hash: Field }) { - static empty(): MerkleArrayBase { - return { array: Unconstrained.from([]), hash: emptyHash }; - } - }; -} - -type MerkleArrayIterator = { - readonly array: Unconstrained[]>; +type MerkleListIterator = { readonly hash: Field; + readonly data: Unconstrained[]>; currentHash: Field; currentIndex: Unconstrained; }; -function MerkleArrayIterator(): ProvableHashable> { +function MerkleListIterator(): ProvableHashable> { return class extends Struct({ - array: Unconstrained.provable, hash: Field, + data: Unconstrained.provable, currentHash: Field, currentIndex: Unconstrained.provable, }) { - static empty(): MerkleArrayIterator { + static empty(): MerkleListIterator { return { - array: Unconstrained.from([]), hash: emptyHash, + data: Unconstrained.from([]), currentHash: emptyHash, currentIndex: Unconstrained.from(0), }; @@ -244,28 +220,28 @@ function MerkleArrayIterator(): ProvableHashable> { /** * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, - * instead of needlessly mutating itself / throwing away context while stepping through it. + * instead of mutating itself / throwing away context while stepping through it. * * We maintain two commitments, both of which are equivalent to a Merkle list hash starting _from the end_ of the array: * - One to the entire array, to prove that we start iterating at the beginning. * - One to the array from the current index until the end, to efficiently step forward. */ -class MerkleArray implements MerkleArrayIterator { +class MerkleArray implements MerkleListIterator { // fixed parts - readonly array: Unconstrained[]>; + readonly data: Unconstrained[]>; readonly hash: Field; // mutable parts currentHash: Field; currentIndex: Unconstrained; - constructor(value: MerkleArrayIterator) { + constructor(value: MerkleListIterator) { Object.assign(this, value); } - static startIterating({ array, hash }: MerkleArrayBase) { + static startIterating({ data, hash }: MerkleListBase) { return new this({ - array, + data, hash, currentHash: hash, currentIndex: Unconstrained.from(-1), @@ -280,14 +256,14 @@ class MerkleArray implements MerkleArrayIterator { } jumpToEnd() { this.currentIndex.setTo( - Unconstrained.witness(() => this.array.get().length) + Unconstrained.witness(() => this.data.get().length) ); this.currentHash = emptyHash; } jumpToEndIf(condition: Bool) { Provable.asProver(() => { if (condition.toBoolean()) { - this.currentIndex.set(this.array.get().length); + this.currentIndex.set(this.data.get().length); } }); this.currentHash = Provable.if(condition, emptyHash, this.currentHash); @@ -301,7 +277,7 @@ class MerkleArray implements MerkleArrayIterator { let { previousHash, element } = Provable.witness( WithHash(this.innerProvable), () => - this.array.get()[index.get()] ?? { + this.data.get()[index.get()] ?? { previousHash: emptyHash, element: this.innerProvable.empty(), } @@ -324,10 +300,10 @@ class MerkleArray implements MerkleArrayIterator { } clone(): MerkleArray { - let array = Unconstrained.witness(() => [...this.array.get()]); + let data = Unconstrained.witness(() => [...this.data.get()]); let currentIndex = Unconstrained.witness(() => this.currentIndex.get()); return new this.Constructor({ - array, + data, hash: this.hash, currentHash: this.currentHash, currentIndex, @@ -335,7 +311,7 @@ class MerkleArray implements MerkleArrayIterator { } /** - * Create a Merkle list type + * Create a Merkle array type */ static create( type: ProvableHashable, @@ -349,8 +325,8 @@ class MerkleArray implements MerkleArrayIterator { static _innerProvable = type; static _provable = provableFromClass(MerkleArray_, { - array: Unconstrained.provable, hash: Field, + data: Unconstrained.provable, currentHash: Field, currentIndex: Unconstrained.provable, }) satisfies ProvableHashable> as ProvableHashable< @@ -370,7 +346,7 @@ class MerkleArray implements MerkleArrayIterator { } return new this({ - array: Unconstrained.from(arrayWithHashes), + data: Unconstrained.from(arrayWithHashes), hash: currentHash, currentHash, currentIndex: Unconstrained.from(-1), @@ -389,7 +365,7 @@ class MerkleArray implements MerkleArrayIterator { } // dynamic subclassing infra - static _nextHash: ((hash: Field, t: any) => Field) | undefined; + static _nextHash: ((hash: Field, value: any) => Field) | undefined; static _provable: ProvableHashable> | undefined; static _innerProvable: ProvableHashable | undefined; @@ -398,12 +374,12 @@ class MerkleArray implements MerkleArrayIterator { return this.constructor as typeof MerkleArray; } - nextHash(hash: Field, t: T): Field { + nextHash(hash: Field, value: T): Field { assert( this.Constructor._nextHash !== undefined, 'MerkleArray not initialized' ); - return this.Constructor._nextHash(hash, t); + return this.Constructor._nextHash(hash, value); } get innerProvable(): ProvableHashable { @@ -414,3 +390,23 @@ class MerkleArray implements MerkleArrayIterator { return this.Constructor._innerProvable; } } + +// hash helpers + +function genericHash( + provable: ProvableHashable, + prefix: string, + value: T +) { + let input = provable.toInput(value); + let packed = packToFields(input); + return Poseidon.hashWithPrefix(prefix, packed); +} + +function merkleListHash(provable: ProvableHashable, prefix = '') { + return function nextHash(hash: Field, value: T) { + let input = provable.toInput(value); + let packed = packToFields(input); + return Poseidon.hashWithPrefix(prefix, [hash, ...packed]); + }; +} From 598ba4419b238b661fa47232b5febbf36ab5112f Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 11:35:41 +0100 Subject: [PATCH 200/524] simple way to update unconstrained --- src/lib/circuit_value.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index adccc9a249..67bc6515af 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -562,6 +562,16 @@ and Provable.asProver() blocks, which execute outside the proof. ); } + /** + * Update an `Unconstrained` by a witness computation. + */ + updateAsProver(compute: (value: T) => T) { + return Provable.asProver(() => { + let value = this.get(); + this.set(compute(value)); + }); + } + static provable: Provable> & { toInput: (x: Unconstrained) => { fields?: Field[]; From 832f123c1493ec90929f8d85d0308b5eb8fc2930 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 11:38:21 +0100 Subject: [PATCH 201/524] change merkle array start index from -1 to 0 --- src/lib/mina/token/merkle-list.ts | 52 ++++++++++++------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index ae0164f5c3..0e621f7279 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -66,9 +66,7 @@ class MerkleList implements MerkleListBase { push(element: T) { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); - Provable.asProver(() => { - this.data.set([...this.data.get(), { previousHash, element }]); - }); + this.data.updateAsProver((data) => [...data, { previousHash, element }]); } pushIf(condition: Bool, element: T) { @@ -78,11 +76,9 @@ class MerkleList implements MerkleListBase { this.nextHash(previousHash, element), previousHash ); - Provable.asProver(() => { - if (condition.toBoolean()) { - this.data.set([...this.data.get(), { previousHash, element }]); - } - }); + this.data.updateAsProver((data) => + condition.toBoolean() ? [...data, { previousHash, element }] : data + ); } private popWitness() { @@ -196,28 +192,20 @@ type MerkleListIterator = { readonly hash: Field; readonly data: Unconstrained[]>; + /** + * The merkle list hash of `[data[currentIndex], ..., data[length-1]]` (when hashing from right to left). + * + * For example: + * - If `currentIndex === 0`, then `currentHash === this.hash` is the hash of the entire array. + * - If `currentIndex === length`, then `currentHash === emptyHash` is the hash of an empty array. + */ currentHash: Field; + /** + * The index of the element that will be returned by the next call to `next()`. + */ currentIndex: Unconstrained; }; -function MerkleListIterator(): ProvableHashable> { - return class extends Struct({ - hash: Field, - data: Unconstrained.provable, - currentHash: Field, - currentIndex: Unconstrained.provable, - }) { - static empty(): MerkleListIterator { - return { - hash: emptyHash, - data: Unconstrained.from([]), - currentHash: emptyHash, - currentIndex: Unconstrained.from(0), - }; - } - }; -} - /** * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, * instead of mutating itself / throwing away context while stepping through it. @@ -244,7 +232,7 @@ class MerkleArray implements MerkleListIterator { data, hash, currentHash: hash, - currentIndex: Unconstrained.from(-1), + currentIndex: Unconstrained.from(0), }); } assertAtStart() { @@ -272,12 +260,10 @@ class MerkleArray implements MerkleListIterator { next() { // next corresponds to `pop()` in MerkleList // it returns a dummy element if we're at the end of the array - let index = Unconstrained.witness(() => this.currentIndex.get() + 1); - let { previousHash, element } = Provable.witness( WithHash(this.innerProvable), () => - this.data.get()[index.get()] ?? { + this.data.get()[this.currentIndex.get()] ?? { previousHash: emptyHash, element: this.innerProvable.empty(), } @@ -288,7 +274,9 @@ class MerkleArray implements MerkleListIterator { let requiredHash = Provable.if(isDummy, emptyHash, correctHash); this.currentHash.assertEquals(requiredHash); - this.currentIndex.setTo(index); + this.currentIndex.updateAsProver((i) => + Math.min(i + 1, this.data.get().length) + ); this.currentHash = Provable.if(isDummy, emptyHash, previousHash); return Provable.if( @@ -349,7 +337,7 @@ class MerkleArray implements MerkleListIterator { data: Unconstrained.from(arrayWithHashes), hash: currentHash, currentHash, - currentIndex: Unconstrained.from(-1), + currentIndex: Unconstrained.from(0), }); } From 3e03b273b975082731653d0deb39aae3388e9d2a Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 12:37:26 +0100 Subject: [PATCH 202/524] invert internal order in merkle list --- src/lib/hash.ts | 6 +++ src/lib/mina/token/merkle-list.ts | 70 ++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 3b24556b51..ab3dc883ca 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -14,6 +14,7 @@ export { Poseidon, TokenSymbol }; // internal API export { + ProvableHashable, HashInput, HashHelpers, emptyHashWithPrefix, @@ -24,6 +25,11 @@ export { hashConstant, }; +type ProvableHashable = Provable & { + toInput: (x: T) => HashInput; + empty: () => T; +}; + class Sponge { #sponge: unknown; diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index 0e621f7279..a3ded82ab3 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -6,9 +6,9 @@ import { Struct, Unconstrained, assert, -} from 'o1js'; +} from '../../../index.js'; import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; -import { packToFields } from '../../hash.js'; +import { packToFields, ProvableHashable } from '../../hash.js'; export { MerkleListBase, @@ -32,6 +32,9 @@ function WithHash(type: ProvableHashable): ProvableHashable> { return Struct({ previousHash: Field, element: type }); } +/** + * Common base type for {@link MerkleList} and {@link MerkleArray} + */ type MerkleListBase = { hash: Field; data: Unconstrained[]>; @@ -47,6 +50,25 @@ function MerkleListBase(): ProvableHashable> { // merkle list +/** + * Dynamic-length list which is represented as a single hash + * + * Supported operations are {@link push()} and {@link pop()} and some variants thereof. + * + * **Important:** `push()` adds elements to the _start_ of the internal array and `pop()` removes them from the start. + * This is so that the hash which represents the list is consistent with {@link MerkleArray}, + * and so a `MerkleList` can be used as input to `MerkleArray.startIterating(list)` (which will then iterate starting from the last pushed element). + * + * A Merkle list is generic over its element types, so before using it you must create a subclass for your element type: + * + * ```ts + * class MyList extends MerkleList.create(MyType) {} + * + * // now use it + * let list = MyList.empty(); + * list.push(new MyType(...)); + * ``` + */ class MerkleList implements MerkleListBase { hash: Field; data: Unconstrained[]>; @@ -59,14 +81,11 @@ class MerkleList implements MerkleListBase { isEmpty() { return this.hash.equals(emptyHash); } - notEmpty() { - return this.hash.equals(emptyHash).not(); - } push(element: T) { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); - this.data.updateAsProver((data) => [...data, { previousHash, element }]); + this.data.updateAsProver((data) => [{ previousHash, element }, ...data]); } pushIf(condition: Bool, element: T) { @@ -77,18 +96,18 @@ class MerkleList implements MerkleListBase { previousHash ); this.data.updateAsProver((data) => - condition.toBoolean() ? [...data, { previousHash, element }] : data + condition.toBoolean() ? [{ previousHash, element }, ...data] : data ); } private popWitness() { return Provable.witness(WithHash(this.innerProvable), () => { - let value = this.data.get(); - let head = value.at(-1) ?? { + let [value, ...data] = this.data.get(); + let head = value ?? { previousHash: emptyHash, element: this.innerProvable.empty(), }; - this.data.set(value.slice(0, -1)); + this.data.set(data); return head; }); } @@ -96,8 +115,8 @@ class MerkleList implements MerkleListBase { popExn(): T { let { previousHash, element } = this.popWitness(); - let requiredHash = this.nextHash(previousHash, element); - this.hash.assertEquals(requiredHash); + let currentHash = this.nextHash(previousHash, element); + this.hash.assertEquals(currentHash); this.hash = previousHash; return element; @@ -105,11 +124,11 @@ class MerkleList implements MerkleListBase { pop(): T { let { previousHash, element } = this.popWitness(); - let isEmpty = this.isEmpty(); - let correctHash = this.nextHash(previousHash, element); - let requiredHash = Provable.if(isEmpty, emptyHash, correctHash); - this.hash.assertEquals(requiredHash); + + let currentHash = this.nextHash(previousHash, element); + currentHash = Provable.if(isEmpty, emptyHash, currentHash); + this.hash.assertEquals(currentHash); this.hash = Provable.if(isEmpty, emptyHash, previousHash); let provable = this.innerProvable; @@ -123,6 +142,15 @@ class MerkleList implements MerkleListBase { /** * Create a Merkle list type + * + * Optionally, you can tell `create()` how to do the hash that pushed a new list element, by passing a `nextHash` function. + * + * @example + * ```ts + * class MyList extends MerkleList.create(Field, (hash, x) => + * Poseidon.hashWithPrefix('custom', [hash, x]) + * ) {} + * ``` */ static create( type: ProvableHashable, @@ -163,12 +191,12 @@ class MerkleList implements MerkleListBase { return this.constructor as typeof MerkleList; } - nextHash(hash: Field, t: T): Field { + nextHash(hash: Field, value: T): Field { assert( this.Constructor._nextHash !== undefined, 'MerkleList not initialized' ); - return this.Constructor._nextHash(hash, t); + return this.Constructor._nextHash(hash, value); } get innerProvable(): ProvableHashable { @@ -180,12 +208,6 @@ class MerkleList implements MerkleListBase { } } -type HashInput = { fields?: Field[]; packed?: [Field, number][] }; -type ProvableHashable = Provable & { - toInput: (x: T) => HashInput; - empty: () => T; -}; - // merkle array type MerkleListIterator = { From f3a4a29b5e6535ac7515bde647d6ea583e38dcac Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 13:51:04 +0100 Subject: [PATCH 203/524] make merkle list the main callforest intf --- src/lib/mina/token/call-forest.ts | 18 ++++-- src/lib/mina/token/call-forest.unit-test.ts | 8 +-- src/lib/mina/token/merkle-list.ts | 61 ++++++++++++++------- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 36d0840578..a4b070481d 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -16,7 +16,7 @@ import { } from './merkle-list.js'; import { Field } from '../../core.js'; -export { CallForest, CallForestIterator, hashAccountUpdate }; +export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; export { HashedAccountUpdate }; @@ -34,7 +34,7 @@ const CallTree: ProvableHashable = Struct({ calls: MerkleListBase(), }); -class CallForest extends MerkleArray.create(CallTree, merkleListHash) { +class CallForest extends MerkleList.create(CallTree, merkleListHash) { static fromAccountUpdates(updates: AccountUpdate[]): CallForest { let nodes = updates.map((update) => { let accountUpdate = HashedAccountUpdate.hash(update); @@ -46,8 +46,10 @@ class CallForest extends MerkleArray.create(CallTree, merkleListHash) { } } +class CallForestArray extends MerkleArray.createFromList(CallForest) {} + class Layer extends Struct({ - forest: CallForest.provable, + forest: CallForestArray.provable, mayUseToken: AccountUpdate.MayUseToken.type, }) {} const ParentLayers = MerkleList.create(Layer); @@ -66,7 +68,11 @@ class CallForestIterator { unfinishedParentLayers: MerkleList; selfToken: Field; - constructor(forest: CallForest, mayUseToken: MayUseToken, selfToken: Field) { + constructor( + forest: CallForestArray, + mayUseToken: MayUseToken, + selfToken: Field + ) { this.currentLayer = { forest, mayUseToken }; this.unfinishedParentLayers = ParentLayers.empty(); this.selfToken = selfToken; @@ -74,7 +80,7 @@ class CallForestIterator { static create(forest: CallForest, selfToken: Field) { return new CallForestIterator( - forest, + CallForestArray.startIterating(forest), MayUseToken.ParentsOwnToken, selfToken ); @@ -95,7 +101,7 @@ class CallForestIterator { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); - let forest = CallForest.startIterating(calls); + let forest = CallForestArray.startIterating(calls); let parentForest = this.currentLayer.forest; this.unfinishedParentLayers.pushIf(parentForest.isAtEnd().not(), { diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 11b1ea3592..a9ca56bc56 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -88,25 +88,25 @@ test(flatAccountUpdates, (flatUpdates) => { }); // traverses the top level of a call forest in correct order -// i.e., CallForest.next() works +// i.e., CallForestArray works test.custom({ timeBudget: 1000 })(flatAccountUpdates, (flatUpdates) => { // prepare call forest from flat account updates let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - let forest = CallForest.fromAccountUpdates(updates); + let forest = CallForest.fromAccountUpdates(updates).startIterating(); // step through top-level by calling forest.next() repeatedly let n = updates.length; for (let i = 0; i < n; i++) { let expected = updates[i]; - let actual = forest.next().accountUpdate.value.get(); + let actual = forest.next().accountUpdate.unhash(); assertEqual(actual, expected); } // doing next() again should return a dummy - let actual = forest.next().accountUpdate.value.get(); + let actual = forest.next().accountUpdate.unhash(); assertEqual(actual, AccountUpdate.dummy()); }); diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index a3ded82ab3..60139dca77 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -140,6 +140,11 @@ class MerkleList implements MerkleListBase { return new this.Constructor({ hash: this.hash, data }); } + startIterating(): MerkleArray { + let merkleArray = MerkleArray.createFromList(this.Constructor); + return merkleArray.startIterating(this); + } + /** * Create a Merkle list type * @@ -158,6 +163,7 @@ class MerkleList implements MerkleListBase { ): typeof MerkleList & { // override static methods with strict types empty: () => MerkleList; + from: (array: T[]) => MerkleList; provable: ProvableHashable>; } { return class MerkleList_ extends MerkleList { @@ -174,6 +180,11 @@ class MerkleList implements MerkleListBase { return new this({ hash: emptyHash, data: Unconstrained.from([]) }); } + static from(array: T[]): MerkleList { + let { hash, data } = withHashes(array, nextHash); + return new this({ data: Unconstrained.from(data), hash }); + } + static get provable(): ProvableHashable> { assert(this._provable !== undefined, 'MerkleList not initialized'); return this._provable; @@ -249,14 +260,6 @@ class MerkleArray implements MerkleListIterator { Object.assign(this, value); } - static startIterating({ data, hash }: MerkleListBase) { - return new this({ - data, - hash, - currentHash: hash, - currentIndex: Unconstrained.from(0), - }); - } assertAtStart() { return this.currentHash.assertEquals(this.hash); } @@ -328,6 +331,7 @@ class MerkleArray implements MerkleListIterator { nextHash: (hash: Field, value: T) => Field = merkleListHash(type) ): typeof MerkleArray & { from: (array: T[]) => MerkleArray; + startIterating: (list: MerkleListBase) => MerkleArray; empty: () => MerkleArray; provable: ProvableHashable>; } { @@ -346,19 +350,15 @@ class MerkleArray implements MerkleListIterator { static _nextHash = nextHash; static from(array: T[]): MerkleArray { - let n = array.length; - let arrayWithHashes = Array>(n); - let currentHash = emptyHash; - - for (let i = n - 1; i >= 0; i--) { - arrayWithHashes[i] = { previousHash: currentHash, element: array[i] }; - currentHash = nextHash(currentHash, array[i]); - } + let { hash, data } = withHashes(array, nextHash); + return this.startIterating({ data: Unconstrained.from(data), hash }); + } + static startIterating({ data, hash }: MerkleListBase): MerkleArray { return new this({ - data: Unconstrained.from(arrayWithHashes), - hash: currentHash, - currentHash, + data, + hash, + currentHash: hash, currentIndex: Unconstrained.from(0), }); } @@ -374,6 +374,13 @@ class MerkleArray implements MerkleListIterator { }; } + static createFromList(merkleList: typeof MerkleList) { + return this.create( + merkleList.prototype.innerProvable, + merkleList._nextHash + ); + } + // dynamic subclassing infra static _nextHash: ((hash: Field, value: any) => Field) | undefined; @@ -420,3 +427,19 @@ function merkleListHash(provable: ProvableHashable, prefix = '') { return Poseidon.hashWithPrefix(prefix, [hash, ...packed]); }; } + +function withHashes( + data: T[], + nextHash: (hash: Field, value: T) => Field +): { data: WithHash[]; hash: Field } { + let n = data.length; + let arrayWithHashes = Array>(n); + let currentHash = emptyHash; + + for (let i = n - 1; i >= 0; i--) { + arrayWithHashes[i] = { previousHash: currentHash, element: data[i] }; + currentHash = nextHash(currentHash, data[i]); + } + + return { data: arrayWithHashes, hash: currentHash }; +} From d232aa91c527395c18c00d262960c782c89ff015 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 14:49:43 +0100 Subject: [PATCH 204/524] token contract using call forest iterator --- src/lib/account_update.ts | 20 +++- src/lib/mina/token/call-forest.ts | 5 + src/lib/mina/token/token-contract.ts | 145 ++++++++++++++------------- 3 files changed, 101 insertions(+), 69 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index b24b32354b..3b61e3a98d 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -671,7 +671,8 @@ class AccountUpdate implements Types.AccountUpdate { callsType: | { type: 'None' } | { type: 'Witness' } - | { type: 'Equals'; value: Field }; + | { type: 'Equals'; value: Field } + | { type: 'WitnessEquals'; value: Field }; accountUpdates: AccountUpdate[]; } = { callsType: { type: 'None' }, @@ -874,6 +875,13 @@ class AccountUpdate implements Types.AccountUpdate { AccountUpdate.witnessChildren(childUpdate, layout, { skipCheck: true }); } + /** + * Makes an {@link AccountUpdate} a child-{@link AccountUpdate} of this. + */ + adopt(childUpdate: AccountUpdate) { + makeChildAccountUpdate(this, childUpdate); + } + get balance() { let accountUpdate = this; @@ -889,6 +897,13 @@ class AccountUpdate implements Types.AccountUpdate { }; } + get balanceChange() { + return Int64.fromObject(this.body.balanceChange); + } + set balanceChange(x: Int64) { + this.body.balanceChange = x; + } + get update(): Update { return this.body.update; } @@ -1611,6 +1626,9 @@ const CallForest = { if (callsType.type === 'Witness') { return Provable.witness(Field, () => CallForest.hashChildrenBase(update)); } + if (callsType.type === 'WitnessEquals') { + return callsType.value; + } let calls = CallForest.hashChildrenBase(update); if (callsType.type === 'Equals' && Provable.inCheckedComputation()) { calls.assertEquals(callsType.value); diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index a4b070481d..b56664a195 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -6,6 +6,7 @@ import { Provable, Struct, TokenId, + assert, } from '../../../index.js'; import { MerkleArray, @@ -152,6 +153,10 @@ class CallForestIterator { return { accountUpdate: update, usesThisToken }; } + + assertFinished() { + assert(this.currentLayer.forest.isAtEnd(), 'CallForest not finished'); + } } // helper class to represent the position in a tree = the last visited node diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index e5c484089d..5f587be223 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -6,16 +6,89 @@ import { method, Mina, Permissions, + Provable, PublicKey, SmartContract, UInt64, - VerificationKey, -} from 'o1js'; +} from '../../../index.js'; +import { CallForest, CallForestIterator } from './call-forest.js'; + +export { TransferableTokenContract }; + +// it's fine to have this restriction, because the protocol also has a limit of ~20 +// TODO find out precise protocol limit +const MAX_ACCOUNT_UPDATES = 20; /** - * Simple token with API flexible enough to handle all our use cases + * Attempt at a standardized token contract which seemlessly supports all of the following model use cases: + * + * **Transfer** { from, to, amount }, supporting the various configurations: + * + * - from `send: signature` to `receive: none` (classical end-user transfer) + * - from `send: signature` to `receive: signature` (atypical end-user transfer) + * - from `send: signature` to `receive: proof` (deposit into zkapp w/ strict setup) + * + * - from `send: proof` to `receive: none` (typical transfer from zkapp) + * - from `send: proof` to `receive: signature` (transfer from zkapp to atypical end-user) + * - from `send: proof` to `receive: proof` (transfer from zkapp to zkapp w/ strict setup) */ -class TokenContract extends SmartContract { +class TransferableTokenContract extends SmartContract { + // APPROVABLE API + + @method + approveUpdates(updatesList: CallForest) { + let forest = CallForestIterator.create(updatesList, this.token.id); + let totalBalanceChange = Int64.zero; + + // iterate through the forest and accumulate balance changes + for (let i = 0; i < MAX_ACCOUNT_UPDATES; i++) { + let { accountUpdate, usesThisToken } = forest.next(); + + totalBalanceChange = totalBalanceChange.add( + Provable.if(usesThisToken, accountUpdate.balanceChange, Int64.zero) + ); + + this.self.adopt(accountUpdate); + } + + // prove that we checked all updates + forest.assertFinished(); + + // prove that the total balance change is zero + totalBalanceChange.assertEquals(0); + + // skip hashing our child account updates in the method wrapper + // since we just did that in the loop above + this.self.children.callsType = { + type: 'WitnessEquals', + value: updatesList.hash, + }; + } + + // TRANSFERABLE API - simple wrapper around Approvable API + + transfer( + from: PublicKey | AccountUpdate, + to: PublicKey | AccountUpdate, + amount: UInt64 + ) { + // coerce the inputs to AccountUpdate and pass to `approveUpdates()` + let tokenId = this.token.id; + if (from instanceof PublicKey) { + from = AccountUpdate.defaultAccountUpdate(from, tokenId); + } + if (to instanceof PublicKey) { + to = AccountUpdate.defaultAccountUpdate(to, tokenId); + } + from.balance.subInPlace(amount); + to.balance.addInPlace(amount); + + let forest = CallForest.fromAccountUpdates([from, to]); + this.approveUpdates(forest); + } + + // BELOW: example implementation specific to this token + // constant supply SUPPLY = UInt64.from(10n ** 18n); @@ -42,68 +115,4 @@ class TokenContract extends SmartContract { // pay fees for opened account this.balance.subInPlace(Mina.accountCreationFee()); } - - // this is a very standardized deploy method. instead, we could also take the account update from a callback - // => need callbacks for signatures - @method deployZkapp(address: PublicKey, verificationKey: VerificationKey) { - let tokenId = this.token.id; - let zkapp = AccountUpdate.create(address, tokenId); - zkapp.account.permissions.set(Permissions.default()); - zkapp.account.verificationKey.set(verificationKey); - zkapp.requireSignature(); - } - - @method approveUpdate(zkappUpdate: AccountUpdate) { - this.approve(zkappUpdate); - let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); - balanceChange.assertEquals(Int64.from(0)); - } - - // FIXME: remove this - @method approveAny(zkappUpdate: AccountUpdate) { - this.approve(zkappUpdate, AccountUpdate.Layout.AnyChildren); - } - - // let a zkapp send tokens to someone, provided the token supply stays constant - @method approveUpdateAndSend( - zkappUpdate: AccountUpdate, - to: PublicKey, - amount: UInt64 - ) { - // approve a layout of two grandchildren, both of which can't inherit the token permission - let { StaticChildren, AnyChildren } = AccountUpdate.Layout; - this.approve(zkappUpdate, StaticChildren(AnyChildren, AnyChildren)); - zkappUpdate.body.mayUseToken.parentsOwnToken.assertTrue(); - let [grandchild1, grandchild2] = zkappUpdate.children.accountUpdates; - grandchild1.body.mayUseToken.inheritFromParent.assertFalse(); - grandchild2.body.mayUseToken.inheritFromParent.assertFalse(); - - // see if balance change cancels the amount sent - let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); - balanceChange.assertEquals(Int64.from(amount).neg()); - // add same amount of tokens to the receiving address - this.token.mint({ address: to, amount }); - } - - transfer(from: PublicKey, to: PublicKey | AccountUpdate, amount: UInt64) { - if (to instanceof PublicKey) - return this.transferToAddress(from, to, amount); - if (to instanceof AccountUpdate) - return this.transferToUpdate(from, to, amount); - } - @method transferToAddress(from: PublicKey, to: PublicKey, value: UInt64) { - this.token.send({ from, to, amount: value }); - } - @method transferToUpdate(from: PublicKey, to: AccountUpdate, value: UInt64) { - this.token.send({ from, to, amount: value }); - } - - @method getBalance(publicKey: PublicKey): UInt64 { - let accountUpdate = AccountUpdate.create(publicKey, this.token.id); - let balance = accountUpdate.account.balance.get(); - accountUpdate.account.balance.requireEquals( - accountUpdate.account.balance.get() - ); - return balance; - } } From ff799dec1eb5aef7e407b7854d527365f01de13f Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 15:30:13 +0100 Subject: [PATCH 205/524] start testing --- src/lib/mina/token/token-contract.ts | 8 ++++++++ src/lib/zkapp.ts | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 5f587be223..ffd8ee31f0 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -116,3 +116,11 @@ class TransferableTokenContract extends SmartContract { this.balance.subInPlace(Mina.accountCreationFee()); } } + +// TESTS + +TransferableTokenContract.analyzeMethods({ printSummary: true }); + +console.time('compile'); +await TransferableTokenContract.compile(); +console.timeEnd('compile'); diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 9346973f72..c317b92cdc 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1164,7 +1164,7 @@ super.init(); * - `actions` the number of actions the method dispatches * - `gates` the constraint system, represented as an array of gates */ - static analyzeMethods() { + static analyzeMethods({ printSummary = false } = {}) { let ZkappClass = this as typeof SmartContract; let methodMetadata = (ZkappClass._methodMetadata ??= {}); let methodIntfs = ZkappClass._methods ?? []; @@ -1186,7 +1186,7 @@ super.init(); try { for (let methodIntf of methodIntfs) { let accountUpdate: AccountUpdate; - let { rows, digest, result, gates } = analyzeMethod( + let { rows, digest, result, gates, summary } = analyzeMethod( ZkappPublicInput, methodIntf, (publicInput, publicKey, tokenId, ...args) => { @@ -1206,6 +1206,7 @@ super.init(); hasReturn: result !== undefined, gates, }; + if (printSummary) console.log(methodIntf.methodName, summary()); } } finally { if (insideSmartContract) smartContractContext.leave(id!); From 83a4d9f5681b2945ece8cbe408825e813a648b6d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 20:47:03 +0100 Subject: [PATCH 206/524] only zkapps with proof authorization can be called --- src/lib/zkapp.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index c317b92cdc..9933d9db20 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -56,6 +56,7 @@ import { snarkContext, } from './provable-context.js'; import { Cache } from './proof-system/cache.js'; +import { assert } from './gadgets/common.js'; // external API export { @@ -473,6 +474,11 @@ function wrapMethod( accountUpdate.body.publicKey.assertEquals(this.address); accountUpdate.body.tokenId.assertEquals(this.self.body.tokenId); + // assert that the callee account update has proof authorization. everything else would have much worse security trade-offs, + // because a one-time change of the callee semantics by using a signature could go unnoticed even if we monitor the callee's + // onchain verification key + assert(accountUpdate.body.authorizationKind.isProved, 'callee is proved'); + // assert that the inputs & outputs we have match what the callee put on its callData let callDataFields = computeCallData( methodIntf, From 1b54524ccb851ad4feb6ee150247a6212ccdc3d0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:05:25 +0100 Subject: [PATCH 207/524] lower level deps for merkle list --- src/lib/mina/token/merkle-list.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index 60139dca77..7fc72b1592 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -1,14 +1,9 @@ -import { - Bool, - Field, - Poseidon, - Provable, - Struct, - Unconstrained, - assert, -} from '../../../index.js'; +import { Bool, Field } from '../../core.js'; +import { Provable } from '../../provable.js'; +import { Struct, Unconstrained } from '../../circuit_value.js'; +import { assert } from '../../gadgets/common.js'; import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; -import { packToFields, ProvableHashable } from '../../hash.js'; +import { Poseidon, packToFields, ProvableHashable } from '../../hash.js'; export { MerkleListBase, From ca471a4351f78435cd0badc16e1a8d3aefb06962 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:06:32 +0100 Subject: [PATCH 208/524] move merkle list --- src/lib/mina/token/call-forest.ts | 2 +- .../{mina/token => provable-types}/merkle-list.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) rename src/lib/{mina/token => provable-types}/merkle-list.ts (97%) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index b56664a195..00799ccc5d 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -14,7 +14,7 @@ import { MerkleList, ProvableHashable, genericHash, -} from './merkle-list.js'; +} from '../../provable-types/merkle-list.js'; import { Field } from '../../core.js'; export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/provable-types/merkle-list.ts similarity index 97% rename from src/lib/mina/token/merkle-list.ts rename to src/lib/provable-types/merkle-list.ts index 7fc72b1592..3e299f8c0c 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -1,9 +1,9 @@ -import { Bool, Field } from '../../core.js'; -import { Provable } from '../../provable.js'; -import { Struct, Unconstrained } from '../../circuit_value.js'; -import { assert } from '../../gadgets/common.js'; -import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; -import { Poseidon, packToFields, ProvableHashable } from '../../hash.js'; +import { Bool, Field } from '../core.js'; +import { Provable } from '../provable.js'; +import { Struct, Unconstrained } from '../circuit_value.js'; +import { assert } from '../gadgets/common.js'; +import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; +import { Poseidon, packToFields, ProvableHashable } from '../hash.js'; export { MerkleListBase, From 491b93b511da1f2cc6464d1cebbb81f965593132 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:06:40 +0100 Subject: [PATCH 209/524] expose merkle list/array --- src/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/index.ts b/src/index.ts index 8b8aa0672e..81f3bb4193 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,6 +40,12 @@ export { Packed, Hashed } from './lib/provable-types/packed.js'; export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; +export { + MerkleList, + MerkleArray, + ProvableHashable, +} from './lib/provable-types/merkle-list.js'; + export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; export { From 29829d4f8b280e6476ab56120d27152004eaf352 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:17:24 +0100 Subject: [PATCH 210/524] remove unnecessary code --- src/lib/mina/token/call-forest.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 00799ccc5d..0e3d869d99 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -159,22 +159,6 @@ class CallForestIterator { } } -// helper class to represent the position in a tree = the last visited node - -// every entry in the array is a layer -// so if there are two entries, we last visited a node in the second layer -// this index is the index of the node in that layer -type TreePosition = { index: number; isDone: boolean }[]; -// const TreePosition = { -// stepDown(position: TreePosition, numberOfChildren: number) { -// position.push({ index: 0, isDone: false }); -// }, -// stepUp(position: TreePosition) { -// position.pop(); -// position[position.length - 1].index++; -// }, -// }; - // how to hash a forest function merkleListHash(forestHash: Field, tree: CallTree) { From 5465321f8ba1a423e957e8be30b3cf1288751659 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:36:33 +0100 Subject: [PATCH 211/524] split out general base contract which example contract is implemented on --- src/lib/mina/token/call-forest.ts | 7 +- src/lib/mina/token/token-contract.ts | 104 ++++++------------ .../mina/token/token-contract.unit-test.ts | 64 +++++++++++ 3 files changed, 105 insertions(+), 70 deletions(-) create mode 100644 src/lib/mina/token/token-contract.unit-test.ts diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 0e3d869d99..d8804de9da 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -154,8 +154,11 @@ class CallForestIterator { return { accountUpdate: update, usesThisToken }; } - assertFinished() { - assert(this.currentLayer.forest.isAtEnd(), 'CallForest not finished'); + assertFinished(message?: string) { + assert( + this.currentLayer.forest.isAtEnd(), + message ?? 'CallForest not finished' + ); } } diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index ffd8ee31f0..5ad25da313 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -2,66 +2,71 @@ import { AccountUpdate, Bool, DeployArgs, - Int64, - method, - Mina, Permissions, - Provable, PublicKey, SmartContract, UInt64, } from '../../../index.js'; import { CallForest, CallForestIterator } from './call-forest.js'; -export { TransferableTokenContract }; +export { TokenContract }; // it's fine to have this restriction, because the protocol also has a limit of ~20 // TODO find out precise protocol limit const MAX_ACCOUNT_UPDATES = 20; /** - * Attempt at a standardized token contract which seemlessly supports all of the following model use cases: - * - * **Transfer** { from, to, amount }, supporting the various configurations: - * - * - from `send: signature` to `receive: none` (classical end-user transfer) - * - from `send: signature` to `receive: signature` (atypical end-user transfer) - * - from `send: signature` to `receive: proof` (deposit into zkapp w/ strict setup) - * - * - from `send: proof` to `receive: none` (typical transfer from zkapp) - * - from `send: proof` to `receive: signature` (transfer from zkapp to atypical end-user) - * - from `send: proof` to `receive: proof` (transfer from zkapp to zkapp w/ strict setup) + * Base token contract which + * - implements the `Approvable` API with some easy bit left to be defined by subclasses + * - implements the `Transferable` API as a wrapper around the `Approvable` API */ -class TransferableTokenContract extends SmartContract { - // APPROVABLE API +class TokenContract extends SmartContract { + // change default permissions - important that token contracts use an access permission - @method - approveUpdates(updatesList: CallForest) { - let forest = CallForestIterator.create(updatesList, this.token.id); - let totalBalanceChange = Int64.zero; + deploy(args?: DeployArgs) { + super.deploy(args); + this.account.permissions.set({ + ...Permissions.default(), + access: Permissions.proofOrSignature(), + }); + } + + // APPROVABLE API has to be specified by subclasses, + // but the hard part is `approveEachUpdate()` - // iterate through the forest and accumulate balance changes + approveUpdates(_: CallForest) { + throw Error( + 'TokenContract.approveUpdates() must be implemented by subclasses' + ); + } + + forEachUpdate( + updates: CallForest, + callback: (update: AccountUpdate, usesToken: Bool) => void + ) { + let forest = CallForestIterator.create(updates, this.token.id); + + // iterate through the forest and apply user-defined logc for (let i = 0; i < MAX_ACCOUNT_UPDATES; i++) { let { accountUpdate, usesThisToken } = forest.next(); - totalBalanceChange = totalBalanceChange.add( - Provable.if(usesThisToken, accountUpdate.balanceChange, Int64.zero) - ); + callback(accountUpdate, usesThisToken); + // add update to our children this.self.adopt(accountUpdate); } // prove that we checked all updates - forest.assertFinished(); - - // prove that the total balance change is zero - totalBalanceChange.assertEquals(0); + forest.assertFinished( + `Number of account updates to approve exceed ` + + `the supported limit of ${MAX_ACCOUNT_UPDATES}.\n` + ); // skip hashing our child account updates in the method wrapper // since we just did that in the loop above this.self.children.callsType = { type: 'WitnessEquals', - value: updatesList.hash, + value: updates.hash, }; } @@ -86,41 +91,4 @@ class TransferableTokenContract extends SmartContract { let forest = CallForest.fromAccountUpdates([from, to]); this.approveUpdates(forest); } - - // BELOW: example implementation specific to this token - - // constant supply - SUPPLY = UInt64.from(10n ** 18n); - - deploy(args?: DeployArgs) { - super.deploy(args); - this.account.permissions.set({ - ...Permissions.default(), - access: Permissions.proofOrSignature(), - }); - } - - @method init() { - super.init(); - - // mint the entire supply to the token account with the same address as this contract - let receiver = this.token.mint({ - address: this.address, - amount: this.SUPPLY, - }); - - // assert that the receiving account is new, so this can be only done once - receiver.account.isNew.requireEquals(Bool(true)); - - // pay fees for opened account - this.balance.subInPlace(Mina.accountCreationFee()); - } } - -// TESTS - -TransferableTokenContract.analyzeMethods({ printSummary: true }); - -console.time('compile'); -await TransferableTokenContract.compile(); -console.timeEnd('compile'); diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts new file mode 100644 index 0000000000..ff5a3c4b6d --- /dev/null +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -0,0 +1,64 @@ +import { Bool, Int64, method, Mina, Provable, UInt64 } from '../../../index.js'; +import { CallForest } from './call-forest.js'; +import { TokenContract } from './token-contract.js'; + +/** + * Attempt at a standardized token contract which seemlessly supports all of the following model use cases: + * + * **Transfer** { from, to, amount }, supporting the various configurations: + * + * - from `send: signature` to `receive: none` (classical end-user transfer) + * - from `send: signature` to `receive: signature` (atypical end-user transfer) + * - from `send: signature` to `receive: proof` (deposit into zkapp w/ strict setup) + * + * - from `send: proof` to `receive: none` (typical transfer from zkapp) + * - from `send: proof` to `receive: signature` (transfer from zkapp to atypical end-user) + * - from `send: proof` to `receive: proof` (transfer from zkapp to zkapp w/ strict setup) + */ +class ExampleTokenContract extends TokenContract { + // APPROVABLE API + + @method + approveUpdates(updates: CallForest) { + let totalBalanceChange = Int64.zero; + + this.forEachUpdate(updates, (accountUpdate, usesToken) => { + totalBalanceChange = totalBalanceChange.add( + Provable.if(usesToken, accountUpdate.balanceChange, Int64.zero) + ); + }); + + // prove that the total balance change is zero + totalBalanceChange.assertEquals(0); + } + + // BELOW: example implementation specific to this token + + // constant supply + SUPPLY = UInt64.from(10n ** 18n); + + @method + init() { + super.init(); + + // mint the entire supply to the token account with the same address as this contract + let receiver = this.token.mint({ + address: this.address, + amount: this.SUPPLY, + }); + + // assert that the receiving account is new, so this can be only done once + receiver.account.isNew.requireEquals(Bool(true)); + + // pay fees for opened account + this.balance.subInPlace(Mina.accountCreationFee()); + } +} + +// TESTS + +ExampleTokenContract.analyzeMethods({ printSummary: true }); + +console.time('compile'); +await ExampleTokenContract.compile(); +console.timeEnd('compile'); From b1ce65844ec1cc3e75fe2b2df9825e79757775f0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:41:20 +0100 Subject: [PATCH 212/524] fix dependencies --- src/lib/mina/token/call-forest.ts | 18 +++++++----------- src/lib/mina/token/token-contract.ts | 14 +++++--------- src/lib/provable-types/merkle-list.ts | 1 - 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index d8804de9da..ccf3ccec9a 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,21 +1,17 @@ import { prefixes } from '../../../provable/poseidon-bigint.js'; -import { - AccountUpdate, - Hashed, - Poseidon, - Provable, - Struct, - TokenId, - assert, -} from '../../../index.js'; +import { AccountUpdate, TokenId } from '../../account_update.js'; +import { Field } from '../../core.js'; +import { Provable } from '../../provable.js'; +import { Struct } from '../../circuit_value.js'; +import { assert } from '../../gadgets/common.js'; +import { Poseidon, ProvableHashable } from '../../hash.js'; +import { Hashed } from '../../provable-types/packed.js'; import { MerkleArray, MerkleListBase, MerkleList, - ProvableHashable, genericHash, } from '../../provable-types/merkle-list.js'; -import { Field } from '../../core.js'; export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 5ad25da313..af0301bf3e 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -1,12 +1,8 @@ -import { - AccountUpdate, - Bool, - DeployArgs, - Permissions, - PublicKey, - SmartContract, - UInt64, -} from '../../../index.js'; +import { Bool } from '../../core.js'; +import { UInt64 } from '../../int.js'; +import { PublicKey } from '../../signature.js'; +import { AccountUpdate, Permissions } from '../../account_update.js'; +import { DeployArgs, SmartContract } from '../../zkapp.js'; import { CallForest, CallForestIterator } from './call-forest.js'; export { TokenContract }; diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index 3e299f8c0c..72dee6d025 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -12,7 +12,6 @@ export { MerkleArray, WithHash, emptyHash, - ProvableHashable, genericHash, merkleListHash, }; From 407d31af5ff778f050ca2e2a0bc4bdbf8b2b6478 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:42:11 +0100 Subject: [PATCH 213/524] fix build --- src/index.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 81f3bb4193..d18500bfb7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ export { } from './lib/foreign-field.js'; export { createForeignCurve, ForeignCurve } from './lib/foreign-curve.js'; export { createEcdsa, EcdsaSignature } from './lib/foreign-ecdsa.js'; -export { Poseidon, TokenSymbol } from './lib/hash.js'; +export { Poseidon, TokenSymbol, ProvableHashable } from './lib/hash.js'; export { Keccak } from './lib/keccak.js'; export { Hash } from './lib/hashes-combined.js'; @@ -40,11 +40,7 @@ export { Packed, Hashed } from './lib/provable-types/packed.js'; export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; -export { - MerkleList, - MerkleArray, - ProvableHashable, -} from './lib/provable-types/merkle-list.js'; +export { MerkleList, MerkleArray } from './lib/provable-types/merkle-list.js'; export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; From ebca276e82ed0971e5705591c9c0129ce5c8d1d6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 26 Jan 2024 08:55:54 +0100 Subject: [PATCH 214/524] submodules --- src/bindings | 2 +- src/mina | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index df4ca0e729..14bddb6f69 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit df4ca0e7291d7eb0af5034726d8eb975e359c62d +Subproject commit 14bddb6f69cd0240b81a9d74488a5a6e10ad319f diff --git a/src/mina b/src/mina index 00e3aa4ef3..f4e67fe467 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 00e3aa4ef3aaaa13822a714f32926c824daae487 +Subproject commit f4e67fe4672762b327026614882a2e8847287688 From b8e90d5417208109078b854ce4b2daa463323774 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 26 Jan 2024 12:11:51 +0100 Subject: [PATCH 215/524] changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0594d6dc9e..36b3109217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Changed -- Improve performance of Poseidon hashing by a factor of 13x https://github.com/o1-labs/o1js/pull/1378 +- Improve performance of Wasm Poseidon hashing by a factor of 13x https://github.com/o1-labs/o1js/pull/1378 + - Speeds up local blockchain tests without proving by ~40% +- Improve performance of Field inverse https://github.com/o1-labs/o1js/pull/1373 + - Speeds up proving by ~2-4% ## [0.15.3](https://github.com/o1-labs/o1js/compare/1ad7333e9e...be748e42e) From f1e5b9b11b17237880abfa3918932693f97f9a39 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 26 Jan 2024 21:35:00 +0200 Subject: [PATCH 216/524] Add support for mainnet. --- src/lib/account_update.ts | 4 +-- src/lib/mina.ts | 31 ++++++++++++------- src/lib/signature.ts | 11 +++++-- src/mina-signer/MinaSigner.ts | 10 +++--- src/mina-signer/src/TSTypes.ts | 2 +- src/mina-signer/src/random-transaction.ts | 3 +- src/mina-signer/src/sign-legacy.ts | 2 +- src/mina-signer/src/sign-legacy.unit-test.ts | 3 +- src/mina-signer/src/sign-zkapp-command.ts | 2 +- .../src/sign-zkapp-command.unit-test.ts | 2 +- src/mina-signer/src/signature.ts | 4 +-- 11 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 714fba42c5..f5f6c3a015 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1934,7 +1934,7 @@ function addMissingSignatures( let signature = signFieldElement( fullCommitment, privateKey.toBigInt(), - 'testnet' + Mina.networkId ); return { body, authorization: Signature.toBase58(signature) }; } @@ -1967,7 +1967,7 @@ function addMissingSignatures( let signature = signFieldElement( transactionCommitment, privateKey.toBigInt(), - 'testnet' + Mina.networkId ); Authorization.setSignature(accountUpdate, Signature.toBase58(signature)); return accountUpdate as AccountUpdate & { lazyAuthorization: undefined }; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index bcc2573803..2d7905405f 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -33,6 +33,7 @@ import { transactionCommitments, verifyAccountUpdateSignature, } from '../mina-signer/src/sign-zkapp-command.js'; +import { NetworkId } from 'src/mina-signer/src/TSTypes.js'; export { createTransaction, @@ -65,9 +66,12 @@ export { getProofsEnabled, // for internal testing only filterGroups, - type NetworkConstants + type NetworkConstants, + networkId }; +let networkId: NetworkId = 'testnet'; + interface TransactionId { isSuccess: boolean; wait(options?: { maxAttempts?: number; interval?: number }): Promise; @@ -685,22 +689,21 @@ function LocalBlockchain({ // assert type compatibility without preventing LocalBlockchain to return additional properties / methods LocalBlockchain satisfies (...args: any) => Mina; +type MinaNetworkEndpoints = { + mina: string | string[]; + archive?: string | string[]; + lightnetAccountManager?: string; +}; + /** * Represents the Mina blockchain running on a real network */ function Network(graphqlEndpoint: string): Mina; -function Network(endpoints: { - mina: string | string[]; - archive?: string | string[]; - lightnetAccountManager?: string; -}): Mina; +function Network(endpoints: MinaNetworkEndpoints): Mina; function Network( input: - | { - mina: string | string[]; - archive?: string | string[]; - lightnetAccountManager?: string; - } + | { networkId: NetworkId; endpoints: MinaNetworkEndpoints } + | MinaNetworkEndpoints | string ): Mina { let minaGraphqlEndpoint: string; @@ -711,6 +714,10 @@ function Network( minaGraphqlEndpoint = input; Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); } else if (input && typeof input === 'object') { + if ('endpoints' in input) { + networkId = input.networkId; + input = input.endpoints; + } if (!input.mina) throw new Error( "Network: malformed input. Please provide an object with 'mina' endpoint." @@ -1406,7 +1413,7 @@ async function verifyAccountUpdate( isValidSignature = verifyAccountUpdateSignature( TypesBigint.AccountUpdate.fromJSON(accountUpdateJson), transactionCommitments, - 'testnet' + networkId ); } catch (error) { errorTrace += '\n\n' + (error as Error).message; diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 58d68ccef0..63100dcb05 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -14,6 +14,7 @@ import { import { prefixes } from '../bindings/crypto/constants.js'; import { constantScalarToBigint } from './scalar.js'; import { toConstantField } from './field.js'; +import { networkId } from './mina.js'; // external API export { PrivateKey, PublicKey, Signature }; @@ -243,13 +244,15 @@ class Signature extends CircuitValue { { fields: msg.map((f) => f.toBigInt()) }, { x: publicKey.x.toBigInt(), y: publicKey.y.toBigInt() }, BigInt(d.toJSON()), - 'testnet' + networkId ) ); let { x: r, y: ry } = Group.generator.scale(kPrime); const k = ry.toBits()[0].toBoolean() ? kPrime.neg() : kPrime; let h = hashWithPrefix( - prefixes.signatureTestnet, + networkId === 'mainnet' + ? prefixes.signatureMainnet + : prefixes.signatureTestnet, msg.concat([publicKey.x, publicKey.y, r]) ); // TODO: Scalar.fromBits interprets the input as a "shifted scalar" @@ -266,7 +269,9 @@ class Signature extends CircuitValue { verify(publicKey: PublicKey, msg: Field[]): Bool { const point = publicKey.toGroup(); let h = hashWithPrefix( - prefixes.signatureTestnet, + networkId === 'mainnet' + ? prefixes.signatureMainnet + : prefixes.signatureTestnet, msg.concat([point.x, point.y, this.r]) ); // TODO: Scalar.fromBits interprets the input as a "shifted scalar" diff --git a/src/mina-signer/MinaSigner.ts b/src/mina-signer/MinaSigner.ts index d4dce0e2df..b79859a8ea 100644 --- a/src/mina-signer/MinaSigner.ts +++ b/src/mina-signer/MinaSigner.ts @@ -1,6 +1,6 @@ import { PrivateKey, PublicKey } from '../provable/curve-bigint.js'; import * as Json from './src/TSTypes.js'; -import type { SignedLegacy, Signed, Network } from './src/TSTypes.js'; +import type { SignedLegacy, Signed, NetworkId } from './src/TSTypes.js'; import { isPayment, @@ -39,9 +39,9 @@ export { Client as default }; const defaultValidUntil = '4294967295'; class Client { - private network: Network; + private network: NetworkId; // TODO: Rename to "networkId" for consistency with remaining codebase. - constructor(options: { network: Network }) { + constructor(options: { network: NetworkId }) { if (!options?.network) { throw Error('Invalid Specified Network'); } @@ -124,7 +124,7 @@ class Client { */ signFields(fields: bigint[], privateKey: Json.PrivateKey): Signed { let privateKey_ = PrivateKey.fromBase58(privateKey); - let signature = sign({ fields }, privateKey_, 'testnet'); + let signature = sign({ fields }, privateKey_, this.network); return { signature: Signature.toBase58(signature), publicKey: PublicKey.toBase58(PrivateKey.toPublicKey(privateKey_)), @@ -144,7 +144,7 @@ class Client { Signature.fromBase58(signature), { fields: data }, PublicKey.fromBase58(publicKey), - 'testnet' + this.network ); } diff --git a/src/mina-signer/src/TSTypes.ts b/src/mina-signer/src/TSTypes.ts index 732b85845d..d0e2c6991a 100644 --- a/src/mina-signer/src/TSTypes.ts +++ b/src/mina-signer/src/TSTypes.ts @@ -9,7 +9,7 @@ export type Field = number | bigint | string; export type PublicKey = string; export type PrivateKey = string; export type Signature = SignatureJson; -export type Network = 'mainnet' | 'testnet'; +export type NetworkId = 'mainnet' | 'testnet'; export type Keypair = { readonly privateKey: PrivateKey; diff --git a/src/mina-signer/src/random-transaction.ts b/src/mina-signer/src/random-transaction.ts index 80ecf9d7f6..8b58405ae3 100644 --- a/src/mina-signer/src/random-transaction.ts +++ b/src/mina-signer/src/random-transaction.ts @@ -6,7 +6,8 @@ import { ZkappCommand, } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; import { PrivateKey } from '../../provable/curve-bigint.js'; -import { NetworkId, Signature } from './signature.js'; +import { Signature } from './signature.js'; +import { NetworkId } from './TSTypes.js'; export { RandomTransaction }; diff --git a/src/mina-signer/src/sign-legacy.ts b/src/mina-signer/src/sign-legacy.ts index 2cc71b9e98..11c262c308 100644 --- a/src/mina-signer/src/sign-legacy.ts +++ b/src/mina-signer/src/sign-legacy.ts @@ -4,13 +4,13 @@ import { HashInputLegacy } from '../../provable/poseidon-bigint.js'; import { Memo } from './memo.js'; import { SignatureJson, - NetworkId, Signature, signLegacy, verifyLegacy, } from './signature.js'; import { Json } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; import { bytesToBits, stringToBytes } from '../../bindings/lib/binable.js'; +import { NetworkId } from './TSTypes.js'; export { signPayment, diff --git a/src/mina-signer/src/sign-legacy.unit-test.ts b/src/mina-signer/src/sign-legacy.unit-test.ts index f414f1c7fa..e5d6eb64ea 100644 --- a/src/mina-signer/src/sign-legacy.unit-test.ts +++ b/src/mina-signer/src/sign-legacy.unit-test.ts @@ -14,12 +14,13 @@ import { verifyStakeDelegation, verifyStringSignature, } from './sign-legacy.js'; -import { NetworkId, Signature, SignatureJson } from './signature.js'; +import { Signature, SignatureJson } from './signature.js'; import { expect } from 'expect'; import { PublicKey, Scalar } from '../../provable/curve-bigint.js'; import { Field } from '../../provable/field-bigint.js'; import { Random, test } from '../../lib/testing/property.js'; import { RandomTransaction } from './random-transaction.js'; +import { NetworkId } from './TSTypes.js'; let { privateKey, publicKey } = keypair; let networks: NetworkId[] = ['testnet', 'mainnet']; diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 07d8a37c62..7644c2ee32 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -12,12 +12,12 @@ import { } from '../../provable/poseidon-bigint.js'; import { Memo } from './memo.js'; import { - NetworkId, Signature, signFieldElement, verifyFieldElement, } from './signature.js'; import { mocks } from '../../bindings/crypto/constants.js'; +import { NetworkId } from './TSTypes.js'; // external API export { signZkappCommand, verifyZkappCommandSignature }; diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index e400f9c8d4..49677e6ca9 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -34,7 +34,6 @@ import { import { packToFields as packToFieldsSnarky } from '../../lib/hash.js'; import { Memo } from './memo.js'; import { - NetworkId, Signature, signFieldElement, verifyFieldElement, @@ -44,6 +43,7 @@ import { RandomTransaction } from './random-transaction.js'; import { Ml, MlHashInput } from '../../lib/ml/conversion.js'; import { FieldConst } from '../../lib/field.js'; import { mocks } from '../../bindings/crypto/constants.js'; +import { NetworkId } from './TSTypes.js'; // monkey-patch bigint to json (BigInt.prototype as any).toJSON = function () { diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 2731b7874e..9e719cebdd 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -27,6 +27,7 @@ import { import { base58 } from '../../lib/base58.js'; import { versionBytes } from '../../bindings/crypto/constants.js'; import { Pallas } from '../../bindings/crypto/elliptic_curve.js'; +import { NetworkId } from './TSTypes.js'; export { sign, @@ -35,7 +36,6 @@ export { verifyFieldElement, Signature, SignatureJson, - NetworkId, signLegacy, verifyLegacy, deriveNonce, @@ -43,7 +43,7 @@ export { const networkIdMainnet = 0x01n; const networkIdTestnet = 0x00n; -type NetworkId = 'mainnet' | 'testnet'; + type Signature = { r: Field; s: Scalar }; type SignatureJson = { field: string; scalar: string }; From 88025a4b6a3d00e45a19a6712f0c5f60fab5b557 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 27 Jan 2024 11:04:23 +0200 Subject: [PATCH 217/524] Add support for mainnet. --- src/index.ts | 1 + src/lib/account_update.ts | 5 +++-- src/lib/mina.ts | 10 ++++------ src/lib/mina/config.ts | 11 +++++++++++ src/lib/signature.ts | 8 ++++---- 5 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 src/lib/mina/config.ts diff --git a/src/index.ts b/src/index.ts index 47d35c815a..fe7c0f23d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,6 +40,7 @@ export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; export * as Mina from './lib/mina.js'; +export * as Config from './lib/mina/config.js'; export type { DeployArgs } from './lib/zkapp.js'; export { SmartContract, diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index f5f6c3a015..c38675e55c 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -33,6 +33,7 @@ import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; +import * as Config from './mina/config.js'; // external API export { AccountUpdate, Permissions, ZkappPublicInput }; @@ -1934,7 +1935,7 @@ function addMissingSignatures( let signature = signFieldElement( fullCommitment, privateKey.toBigInt(), - Mina.networkId + Config.getNetworkId() ); return { body, authorization: Signature.toBase58(signature) }; } @@ -1967,7 +1968,7 @@ function addMissingSignatures( let signature = signFieldElement( transactionCommitment, privateKey.toBigInt(), - Mina.networkId + Config.getNetworkId() ); Authorization.setSignature(accountUpdate, Signature.toBase58(signature)); return accountUpdate as AccountUpdate & { lazyAuthorization: undefined }; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 2d7905405f..ea4729a6f8 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -33,7 +33,8 @@ import { transactionCommitments, verifyAccountUpdateSignature, } from '../mina-signer/src/sign-zkapp-command.js'; -import { NetworkId } from 'src/mina-signer/src/TSTypes.js'; +import { NetworkId } from '../mina-signer/src/TSTypes.js'; +import * as Config from './mina/config.js'; export { createTransaction, @@ -67,11 +68,8 @@ export { // for internal testing only filterGroups, type NetworkConstants, - networkId }; -let networkId: NetworkId = 'testnet'; - interface TransactionId { isSuccess: boolean; wait(options?: { maxAttempts?: number; interval?: number }): Promise; @@ -715,7 +713,7 @@ function Network( Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); } else if (input && typeof input === 'object') { if ('endpoints' in input) { - networkId = input.networkId; + Config.setNetworkId(input.networkId); input = input.endpoints; } if (!input.mina) @@ -1413,7 +1411,7 @@ async function verifyAccountUpdate( isValidSignature = verifyAccountUpdateSignature( TypesBigint.AccountUpdate.fromJSON(accountUpdateJson), transactionCommitments, - networkId + Config.getNetworkId() ); } catch (error) { errorTrace += '\n\n' + (error as Error).message; diff --git a/src/lib/mina/config.ts b/src/lib/mina/config.ts new file mode 100644 index 0000000000..c3faba480e --- /dev/null +++ b/src/lib/mina/config.ts @@ -0,0 +1,11 @@ +import { NetworkId } from '../../mina-signer/src/TSTypes.js'; + +let networkId: NetworkId = 'testnet'; + +export function setNetworkId(id: NetworkId) { + networkId = id; +} + +export function getNetworkId() { + return networkId; +} diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 63100dcb05..d58d9fb137 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -14,7 +14,7 @@ import { import { prefixes } from '../bindings/crypto/constants.js'; import { constantScalarToBigint } from './scalar.js'; import { toConstantField } from './field.js'; -import { networkId } from './mina.js'; +import * as Config from './mina/config.js'; // external API export { PrivateKey, PublicKey, Signature }; @@ -244,13 +244,13 @@ class Signature extends CircuitValue { { fields: msg.map((f) => f.toBigInt()) }, { x: publicKey.x.toBigInt(), y: publicKey.y.toBigInt() }, BigInt(d.toJSON()), - networkId + Config.getNetworkId() ) ); let { x: r, y: ry } = Group.generator.scale(kPrime); const k = ry.toBits()[0].toBoolean() ? kPrime.neg() : kPrime; let h = hashWithPrefix( - networkId === 'mainnet' + Config.getNetworkId() === 'mainnet' ? prefixes.signatureMainnet : prefixes.signatureTestnet, msg.concat([publicKey.x, publicKey.y, r]) @@ -269,7 +269,7 @@ class Signature extends CircuitValue { verify(publicKey: PublicKey, msg: Field[]): Bool { const point = publicKey.toGroup(); let h = hashWithPrefix( - networkId === 'mainnet' + Config.getNetworkId() === 'mainnet' ? prefixes.signatureMainnet : prefixes.signatureTestnet, msg.concat([point.x, point.y, this.r]) From dfbea2222149e332f30453f1a891e4dfd8c08127 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 27 Jan 2024 15:22:19 +0200 Subject: [PATCH 218/524] Rename Mina.Network::input to options. --- src/lib/mina.ts | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index ea4729a6f8..1c370a9a4d 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -699,7 +699,7 @@ type MinaNetworkEndpoints = { function Network(graphqlEndpoint: string): Mina; function Network(endpoints: MinaNetworkEndpoints): Mina; function Network( - input: + options: | { networkId: NetworkId; endpoints: MinaNetworkEndpoints } | MinaNetworkEndpoints | string @@ -708,43 +708,43 @@ function Network( let archiveEndpoint: string; let lightnetAccountManagerEndpoint: string; - if (input && typeof input === 'string') { - minaGraphqlEndpoint = input; + if (options && typeof options === 'string') { + minaGraphqlEndpoint = options; Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); - } else if (input && typeof input === 'object') { - if ('endpoints' in input) { - Config.setNetworkId(input.networkId); - input = input.endpoints; + } else if (options && typeof options === 'object') { + if ('endpoints' in options) { + Config.setNetworkId(options.networkId); + options = options.endpoints; } - if (!input.mina) + if (!options.mina) throw new Error( "Network: malformed input. Please provide an object with 'mina' endpoint." ); - if (Array.isArray(input.mina) && input.mina.length !== 0) { - minaGraphqlEndpoint = input.mina[0]; + if (Array.isArray(options.mina) && options.mina.length !== 0) { + minaGraphqlEndpoint = options.mina[0]; Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); - Fetch.setMinaGraphqlFallbackEndpoints(input.mina.slice(1)); - } else if (typeof input.mina === 'string') { - minaGraphqlEndpoint = input.mina; + Fetch.setMinaGraphqlFallbackEndpoints(options.mina.slice(1)); + } else if (typeof options.mina === 'string') { + minaGraphqlEndpoint = options.mina; Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); } - if (input.archive !== undefined) { - if (Array.isArray(input.archive) && input.archive.length !== 0) { - archiveEndpoint = input.archive[0]; + if (options.archive !== undefined) { + if (Array.isArray(options.archive) && options.archive.length !== 0) { + archiveEndpoint = options.archive[0]; Fetch.setArchiveGraphqlEndpoint(archiveEndpoint); - Fetch.setArchiveGraphqlFallbackEndpoints(input.archive.slice(1)); - } else if (typeof input.archive === 'string') { - archiveEndpoint = input.archive; + Fetch.setArchiveGraphqlFallbackEndpoints(options.archive.slice(1)); + } else if (typeof options.archive === 'string') { + archiveEndpoint = options.archive; Fetch.setArchiveGraphqlEndpoint(archiveEndpoint); } } if ( - input.lightnetAccountManager !== undefined && - typeof input.lightnetAccountManager === 'string' + options.lightnetAccountManager !== undefined && + typeof options.lightnetAccountManager === 'string' ) { - lightnetAccountManagerEndpoint = input.lightnetAccountManager; + lightnetAccountManagerEndpoint = options.lightnetAccountManager; Fetch.setLightnetAccountManagerEndpoint(lightnetAccountManagerEndpoint); } } else { From 8a6e3c3090c8aba47bba2cbed053be5ae9b7a440 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 27 Jan 2024 17:43:24 +0200 Subject: [PATCH 219/524] Updating the tests. --- src/index.ts | 2 +- src/lib/account_update.ts | 6 +++--- src/lib/mina.ts | 6 +++--- src/lib/signature.ts | 8 ++++---- src/mina-signer/tests/verify-in-snark.unit-test.ts | 2 ++ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/index.ts b/src/index.ts index fe7c0f23d7..3aa1f4a29f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,7 +40,7 @@ export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; export * as Mina from './lib/mina.js'; -export * as Config from './lib/mina/config.js'; +export * as MinaConfig from './lib/mina/config.js'; export type { DeployArgs } from './lib/zkapp.js'; export { SmartContract, diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index c38675e55c..7436fb1ff4 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -33,7 +33,7 @@ import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; -import * as Config from './mina/config.js'; +import * as MinaConfig from './mina/config.js'; // external API export { AccountUpdate, Permissions, ZkappPublicInput }; @@ -1935,7 +1935,7 @@ function addMissingSignatures( let signature = signFieldElement( fullCommitment, privateKey.toBigInt(), - Config.getNetworkId() + MinaConfig.getNetworkId() ); return { body, authorization: Signature.toBase58(signature) }; } @@ -1968,7 +1968,7 @@ function addMissingSignatures( let signature = signFieldElement( transactionCommitment, privateKey.toBigInt(), - Config.getNetworkId() + MinaConfig.getNetworkId() ); Authorization.setSignature(accountUpdate, Signature.toBase58(signature)); return accountUpdate as AccountUpdate & { lazyAuthorization: undefined }; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 1c370a9a4d..faec79115f 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -34,7 +34,7 @@ import { verifyAccountUpdateSignature, } from '../mina-signer/src/sign-zkapp-command.js'; import { NetworkId } from '../mina-signer/src/TSTypes.js'; -import * as Config from './mina/config.js'; +import * as MinaConfig from './mina/config.js'; export { createTransaction, @@ -713,7 +713,7 @@ function Network( Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); } else if (options && typeof options === 'object') { if ('endpoints' in options) { - Config.setNetworkId(options.networkId); + MinaConfig.setNetworkId(options.networkId); options = options.endpoints; } if (!options.mina) @@ -1411,7 +1411,7 @@ async function verifyAccountUpdate( isValidSignature = verifyAccountUpdateSignature( TypesBigint.AccountUpdate.fromJSON(accountUpdateJson), transactionCommitments, - Config.getNetworkId() + MinaConfig.getNetworkId() ); } catch (error) { errorTrace += '\n\n' + (error as Error).message; diff --git a/src/lib/signature.ts b/src/lib/signature.ts index d58d9fb137..99e9bcba34 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -14,7 +14,7 @@ import { import { prefixes } from '../bindings/crypto/constants.js'; import { constantScalarToBigint } from './scalar.js'; import { toConstantField } from './field.js'; -import * as Config from './mina/config.js'; +import * as MinaConfig from './mina/config.js'; // external API export { PrivateKey, PublicKey, Signature }; @@ -244,13 +244,13 @@ class Signature extends CircuitValue { { fields: msg.map((f) => f.toBigInt()) }, { x: publicKey.x.toBigInt(), y: publicKey.y.toBigInt() }, BigInt(d.toJSON()), - Config.getNetworkId() + MinaConfig.getNetworkId() ) ); let { x: r, y: ry } = Group.generator.scale(kPrime); const k = ry.toBits()[0].toBoolean() ? kPrime.neg() : kPrime; let h = hashWithPrefix( - Config.getNetworkId() === 'mainnet' + MinaConfig.getNetworkId() === 'mainnet' ? prefixes.signatureMainnet : prefixes.signatureTestnet, msg.concat([publicKey.x, publicKey.y, r]) @@ -269,7 +269,7 @@ class Signature extends CircuitValue { verify(publicKey: PublicKey, msg: Field[]): Bool { const point = publicKey.toGroup(); let h = hashWithPrefix( - Config.getNetworkId() === 'mainnet' + MinaConfig.getNetworkId() === 'mainnet' ? prefixes.signatureMainnet : prefixes.signatureTestnet, msg.concat([point.x, point.y, this.r]) diff --git a/src/mina-signer/tests/verify-in-snark.unit-test.ts b/src/mina-signer/tests/verify-in-snark.unit-test.ts index c32180cff0..3b3f765eef 100644 --- a/src/mina-signer/tests/verify-in-snark.unit-test.ts +++ b/src/mina-signer/tests/verify-in-snark.unit-test.ts @@ -4,11 +4,13 @@ import Client from '../MinaSigner.js'; import { PrivateKey, Signature } from '../../lib/signature.js'; import { expect } from 'expect'; import { Provable } from '../../lib/provable.js'; +import * as MinaConfig from '../../lib/mina/config.js'; let fields = [10n, 20n, 30n, 340817401n, 2091283n, 1n, 0n]; let privateKey = 'EKENaWFuAiqktsnWmxq8zaoR8bSgVdscsghJE5tV6hPoNm8qBKWM'; // sign with mina-signer +MinaConfig.setNetworkId('mainnet'); let client = new Client({ network: 'mainnet' }); let signed = client.signFields(fields, privateKey); From 91dbd97ffed953bc49db3aba3d140727e760ac30 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 27 Jan 2024 18:03:02 +0100 Subject: [PATCH 220/524] add edge case to investigate --- src/lib/gadgets/ecdsa.unit-test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index 250b088709..f21adb722f 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -43,6 +43,17 @@ for (let Curve of curves) { msg: scalar, publicKey: record({ x: field, y: field }), }); + badSignature.rng = Random.withHardCoded(badSignature.rng, { + signature: { + r: 3243632040670678816425112099743675011873398345579979202080647260629177216981n, + s: 0n, + }, + msg: 0n, + publicKey: { + x: 28948022309329048855892746252171976963363056481941560715954676764349967630336n, + y: 2n, + }, + }); let signatureInputs = record({ privateKey, msg: scalar }); From 7a8597e6f491c3df7726412e6201eff25a1209b3 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jan 2024 10:23:43 +0200 Subject: [PATCH 221/524] Addressing review comments. --- src/index.ts | 1 - src/lib/account_update.ts | 5 +- src/lib/mina.ts | 64 ++++++++++++++----- src/lib/mina/config.ts | 11 ---- src/lib/signature.ts | 12 ++-- src/mina-signer/MinaSigner.ts | 2 +- .../tests/verify-in-snark.unit-test.ts | 2 - 7 files changed, 57 insertions(+), 40 deletions(-) delete mode 100644 src/lib/mina/config.ts diff --git a/src/index.ts b/src/index.ts index 3aa1f4a29f..47d35c815a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,7 +40,6 @@ export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; export * as Mina from './lib/mina.js'; -export * as MinaConfig from './lib/mina/config.js'; export type { DeployArgs } from './lib/zkapp.js'; export { SmartContract, diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 7436fb1ff4..f585fed1cd 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -33,7 +33,6 @@ import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; -import * as MinaConfig from './mina/config.js'; // external API export { AccountUpdate, Permissions, ZkappPublicInput }; @@ -1935,7 +1934,7 @@ function addMissingSignatures( let signature = signFieldElement( fullCommitment, privateKey.toBigInt(), - MinaConfig.getNetworkId() + Mina.getNetworkId() ); return { body, authorization: Signature.toBase58(signature) }; } @@ -1968,7 +1967,7 @@ function addMissingSignatures( let signature = signFieldElement( transactionCommitment, privateKey.toBigInt(), - MinaConfig.getNetworkId() + Mina.getNetworkId() ); Authorization.setSignature(accountUpdate, Signature.toBase58(signature)); return accountUpdate as AccountUpdate & { lazyAuthorization: undefined }; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index faec79115f..da54abf118 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -34,7 +34,6 @@ import { verifyAccountUpdateSignature, } from '../mina-signer/src/sign-zkapp-command.js'; import { NetworkId } from '../mina-signer/src/TSTypes.js'; -import * as MinaConfig from './mina/config.js'; export { createTransaction, @@ -53,6 +52,7 @@ export { getAccount, hasAccount, getBalance, + getNetworkId, getNetworkConstants, getNetworkState, accountCreationFee, @@ -376,6 +376,7 @@ interface Mina { tokenId?: Field ) => { hash: string; actions: string[][] }[]; proofsEnabled: boolean; + getNetworkId(): NetworkId; } const defaultAccountCreationFee = 1_000_000_000; @@ -391,12 +392,23 @@ const defaultNetworkConstants: NetworkConstants = { function LocalBlockchain({ proofsEnabled = true, enforceTransactionLimits = true, + networkId = 'testnet' as NetworkId, } = {}) { const slotTime = 3 * 60 * 1000; const startTime = Date.now(); const genesisTimestamp = UInt64.from(startTime); const ledger = Ledger.create(); let networkState = defaultNetworkState(); + let minaNetworkId: NetworkId = 'testnet'; + + if (networkId) { + if (networkId !== 'mainnet' && networkId !== 'testnet') { + throw Error( + "Invalid network id specified. Please use 'mainnet' or 'testnet'" + ); + } + minaNetworkId = networkId; + } function addAccount(publicKey: PublicKey, balance: string) { ledger.addAccount(Ml.fromPublicKey(publicKey), balance); @@ -423,6 +435,7 @@ function LocalBlockchain({ > = {}; return { + getNetworkId: () => minaNetworkId, proofsEnabled, /** * @deprecated use {@link Mina.getNetworkConstants} @@ -496,7 +509,8 @@ function LocalBlockchain({ account, update, commitments, - this.proofsEnabled + this.proofsEnabled, + this.getNetworkId() ); } } @@ -687,23 +701,27 @@ function LocalBlockchain({ // assert type compatibility without preventing LocalBlockchain to return additional properties / methods LocalBlockchain satisfies (...args: any) => Mina; -type MinaNetworkEndpoints = { - mina: string | string[]; - archive?: string | string[]; - lightnetAccountManager?: string; -}; - /** * Represents the Mina blockchain running on a real network */ function Network(graphqlEndpoint: string): Mina; -function Network(endpoints: MinaNetworkEndpoints): Mina; +function Network(options: { + networkId?: NetworkId; + mina: string | string[]; + archive?: string | string[]; + lightnetAccountManager?: string; +}): Mina; function Network( options: - | { networkId: NetworkId; endpoints: MinaNetworkEndpoints } - | MinaNetworkEndpoints + | { + networkId?: NetworkId; + mina: string | string[]; + archive?: string | string[]; + lightnetAccountManager?: string; + } | string ): Mina { + let minaNetworkId: NetworkId = 'testnet'; let minaGraphqlEndpoint: string; let archiveEndpoint: string; let lightnetAccountManagerEndpoint: string; @@ -712,9 +730,13 @@ function Network( minaGraphqlEndpoint = options; Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); } else if (options && typeof options === 'object') { - if ('endpoints' in options) { - MinaConfig.setNetworkId(options.networkId); - options = options.endpoints; + if (options.networkId) { + if (options.networkId !== 'mainnet' && options.networkId !== 'testnet') { + throw Error( + "Invalid network id specified. Please use 'mainnet' or 'testnet'" + ); + } + minaNetworkId = options.networkId; } if (!options.mina) throw new Error( @@ -754,6 +776,7 @@ function Network( } return { + getNetworkId: () => minaNetworkId, /** * @deprecated use {@link Mina.getNetworkConstants} */ @@ -1018,6 +1041,7 @@ function BerkeleyQANet(graphqlEndpoint: string) { } let activeInstance: Mina = { + getNetworkId: () => 'testnet', /** * @deprecated use {@link Mina.getNetworkConstants} */ @@ -1204,6 +1228,13 @@ function hasAccount(publicKey: PublicKey, tokenId?: Field): boolean { return activeInstance.hasAccount(publicKey, tokenId); } +/** + * @return The current Mina network ID. + */ +function getNetworkId() { + return activeInstance.getNetworkId(); +} + /** * @return Data associated with the current Mina network constants. */ @@ -1303,7 +1334,8 @@ async function verifyAccountUpdate( account: Account, accountUpdate: AccountUpdate, transactionCommitments: { commitment: bigint; fullCommitment: bigint }, - proofsEnabled: boolean + proofsEnabled: boolean, + networkId: NetworkId ): Promise { // check that that top-level updates have mayUseToken = No // (equivalent check exists in the Mina node) @@ -1411,7 +1443,7 @@ async function verifyAccountUpdate( isValidSignature = verifyAccountUpdateSignature( TypesBigint.AccountUpdate.fromJSON(accountUpdateJson), transactionCommitments, - MinaConfig.getNetworkId() + networkId ); } catch (error) { errorTrace += '\n\n' + (error as Error).message; diff --git a/src/lib/mina/config.ts b/src/lib/mina/config.ts deleted file mode 100644 index c3faba480e..0000000000 --- a/src/lib/mina/config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NetworkId } from '../../mina-signer/src/TSTypes.js'; - -let networkId: NetworkId = 'testnet'; - -export function setNetworkId(id: NetworkId) { - networkId = id; -} - -export function getNetworkId() { - return networkId; -} diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 99e9bcba34..0adced0ff8 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -14,7 +14,7 @@ import { import { prefixes } from '../bindings/crypto/constants.js'; import { constantScalarToBigint } from './scalar.js'; import { toConstantField } from './field.js'; -import * as MinaConfig from './mina/config.js'; +import { NetworkId } from 'src/mina-signer/src/TSTypes.js'; // external API export { PrivateKey, PublicKey, Signature }; @@ -236,7 +236,7 @@ class Signature extends CircuitValue { * Signs a message using a {@link PrivateKey}. * @returns a {@link Signature} */ - static create(privKey: PrivateKey, msg: Field[]): Signature { + static create(privKey: PrivateKey, msg: Field[], networkId: NetworkId = 'testnet'): Signature { const publicKey = PublicKey.fromPrivateKey(privKey).toGroup(); const d = privKey.s; const kPrime = Scalar.fromBigInt( @@ -244,13 +244,13 @@ class Signature extends CircuitValue { { fields: msg.map((f) => f.toBigInt()) }, { x: publicKey.x.toBigInt(), y: publicKey.y.toBigInt() }, BigInt(d.toJSON()), - MinaConfig.getNetworkId() + networkId ) ); let { x: r, y: ry } = Group.generator.scale(kPrime); const k = ry.toBits()[0].toBoolean() ? kPrime.neg() : kPrime; let h = hashWithPrefix( - MinaConfig.getNetworkId() === 'mainnet' + networkId === 'mainnet' ? prefixes.signatureMainnet : prefixes.signatureTestnet, msg.concat([publicKey.x, publicKey.y, r]) @@ -266,10 +266,10 @@ class Signature extends CircuitValue { * Verifies the {@link Signature} using a message and the corresponding {@link PublicKey}. * @returns a {@link Bool} */ - verify(publicKey: PublicKey, msg: Field[]): Bool { + verify(publicKey: PublicKey, msg: Field[], networkId: NetworkId = 'testnet'): Bool { const point = publicKey.toGroup(); let h = hashWithPrefix( - MinaConfig.getNetworkId() === 'mainnet' + networkId === 'mainnet' ? prefixes.signatureMainnet : prefixes.signatureTestnet, msg.concat([point.x, point.y, this.r]) diff --git a/src/mina-signer/MinaSigner.ts b/src/mina-signer/MinaSigner.ts index b79859a8ea..ef73961c11 100644 --- a/src/mina-signer/MinaSigner.ts +++ b/src/mina-signer/MinaSigner.ts @@ -124,7 +124,7 @@ class Client { */ signFields(fields: bigint[], privateKey: Json.PrivateKey): Signed { let privateKey_ = PrivateKey.fromBase58(privateKey); - let signature = sign({ fields }, privateKey_, this.network); + let signature = sign({ fields }, privateKey_, 'testnet'); return { signature: Signature.toBase58(signature), publicKey: PublicKey.toBase58(PrivateKey.toPublicKey(privateKey_)), diff --git a/src/mina-signer/tests/verify-in-snark.unit-test.ts b/src/mina-signer/tests/verify-in-snark.unit-test.ts index 3b3f765eef..c32180cff0 100644 --- a/src/mina-signer/tests/verify-in-snark.unit-test.ts +++ b/src/mina-signer/tests/verify-in-snark.unit-test.ts @@ -4,13 +4,11 @@ import Client from '../MinaSigner.js'; import { PrivateKey, Signature } from '../../lib/signature.js'; import { expect } from 'expect'; import { Provable } from '../../lib/provable.js'; -import * as MinaConfig from '../../lib/mina/config.js'; let fields = [10n, 20n, 30n, 340817401n, 2091283n, 1n, 0n]; let privateKey = 'EKENaWFuAiqktsnWmxq8zaoR8bSgVdscsghJE5tV6hPoNm8qBKWM'; // sign with mina-signer -MinaConfig.setNetworkId('mainnet'); let client = new Client({ network: 'mainnet' }); let signed = client.signFields(fields, privateKey); From 04f1b20765d5039fd5bd0c4dc03a7d515dd5c6bc Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jan 2024 11:28:08 +0200 Subject: [PATCH 222/524] Missed revert item. --- src/mina-signer/MinaSigner.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mina-signer/MinaSigner.ts b/src/mina-signer/MinaSigner.ts index ef73961c11..4a1575ccb7 100644 --- a/src/mina-signer/MinaSigner.ts +++ b/src/mina-signer/MinaSigner.ts @@ -124,7 +124,7 @@ class Client { */ signFields(fields: bigint[], privateKey: Json.PrivateKey): Signed { let privateKey_ = PrivateKey.fromBase58(privateKey); - let signature = sign({ fields }, privateKey_, 'testnet'); + let signature = sign({ fields }, privateKey_, 'mainnet'); return { signature: Signature.toBase58(signature), publicKey: PublicKey.toBase58(PrivateKey.toPublicKey(privateKey_)), @@ -144,7 +144,7 @@ class Client { Signature.fromBase58(signature), { fields: data }, PublicKey.fromBase58(publicKey), - this.network + 'mainnet' ); } From 77fc82cb4a9a13999de069cf8a37f4ebd9753561 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jan 2024 12:19:59 +0200 Subject: [PATCH 223/524] Yse correct methods signature in tests. --- src/mina-signer/MinaSigner.ts | 9 +++++++++ .../tests/verify-in-snark.unit-test.ts | 18 +++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/mina-signer/MinaSigner.ts b/src/mina-signer/MinaSigner.ts index 4a1575ccb7..8671f5c92d 100644 --- a/src/mina-signer/MinaSigner.ts +++ b/src/mina-signer/MinaSigner.ts @@ -493,6 +493,15 @@ class Client { let sk = PrivateKey.fromBase58(privateKeyBase58); return createNullifier(message, sk); } + + /** + * Returns the network ID. + * + * @returns {NetworkId} The network ID. + */ + getNetworkId(): NetworkId { + return this.network; + } } function validNonNegative(n: number | string | bigint): string { diff --git a/src/mina-signer/tests/verify-in-snark.unit-test.ts b/src/mina-signer/tests/verify-in-snark.unit-test.ts index c32180cff0..4c1c84e555 100644 --- a/src/mina-signer/tests/verify-in-snark.unit-test.ts +++ b/src/mina-signer/tests/verify-in-snark.unit-test.ts @@ -19,14 +19,18 @@ expect(ok).toEqual(true); // sign with o1js and check that we get the same signature let fieldsSnarky = fields.map(Field); let privateKeySnarky = PrivateKey.fromBase58(privateKey); -let signatureSnarky = Signature.create(privateKeySnarky, fieldsSnarky); +let signatureSnarky = Signature.create( + privateKeySnarky, + fieldsSnarky, + client.getNetworkId() +); expect(signatureSnarky.toBase58()).toEqual(signed.signature); // verify out-of-snark with o1js let publicKey = privateKeySnarky.toPublicKey(); let signature = Signature.fromBase58(signed.signature); Provable.assertEqual(Signature, signature, signatureSnarky); -signature.verify(publicKey, fieldsSnarky).assertTrue(); +signature.verify(publicKey, fieldsSnarky, client.getNetworkId()).assertTrue(); // verify in-snark with o1js const Message = Provable.Array(Field, fields.length); @@ -37,7 +41,9 @@ const MyProgram = ZkProgram({ verifySignature: { privateInputs: [Signature, Message], method(signature: Signature, message: Field[]) { - signature.verify(publicKey, message).assertTrue(); + signature + .verify(publicKey, message, client.getNetworkId()) + .assertTrue(); }, }, }, @@ -55,7 +61,9 @@ let invalidSigned = client.signFields(fields, wrongKey); let invalidSignature = Signature.fromBase58(invalidSigned.signature); // can't verify out of snark -invalidSignature.verify(publicKey, fieldsSnarky).assertFalse(); +invalidSignature + .verify(publicKey, fieldsSnarky, client.getNetworkId()) + .assertFalse(); // can't verify in snark await expect(() => @@ -68,7 +76,7 @@ let wrongFields = [...fieldsSnarky]; wrongFields[0] = wrongFields[0].add(1); // can't verify out of snark -signature.verify(publicKey, wrongFields).assertFalse(); +signature.verify(publicKey, wrongFields, client.getNetworkId()).assertFalse(); // can't verify in snark await expect(() => From a3a25d0f560b3cb09364d8dc193a7694961f07dc Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 29 Jan 2024 12:13:41 +0100 Subject: [PATCH 224/524] export token contract --- src/index.ts | 5 ++++ .../mina/token/token-contract.unit-test.ts | 26 +++++++------------ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/index.ts b/src/index.ts index d18500bfb7..dd06c7ee65 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,6 +41,11 @@ export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; export { MerkleList, MerkleArray } from './lib/provable-types/merkle-list.js'; +export { + CallForest, + CallForestIterator, +} from './lib/mina/token/call-forest.js'; +export { TokenContract } from './lib/mina/token/token-contract.js'; export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts index ff5a3c4b6d..d3fe7968d0 100644 --- a/src/lib/mina/token/token-contract.unit-test.ts +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -1,20 +1,14 @@ -import { Bool, Int64, method, Mina, Provable, UInt64 } from '../../../index.js'; -import { CallForest } from './call-forest.js'; -import { TokenContract } from './token-contract.js'; +import { + Bool, + Int64, + method, + Mina, + Provable, + UInt64, + CallForest, + TokenContract, +} from '../../../index.js'; -/** - * Attempt at a standardized token contract which seemlessly supports all of the following model use cases: - * - * **Transfer** { from, to, amount }, supporting the various configurations: - * - * - from `send: signature` to `receive: none` (classical end-user transfer) - * - from `send: signature` to `receive: signature` (atypical end-user transfer) - * - from `send: signature` to `receive: proof` (deposit into zkapp w/ strict setup) - * - * - from `send: proof` to `receive: none` (typical transfer from zkapp) - * - from `send: proof` to `receive: signature` (transfer from zkapp to atypical end-user) - * - from `send: proof` to `receive: proof` (transfer from zkapp to zkapp w/ strict setup) - */ class ExampleTokenContract extends TokenContract { // APPROVABLE API From 14b7aed747effddafe171f69feba5db86840ccb3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 29 Jan 2024 12:29:57 +0100 Subject: [PATCH 225/524] more token contract methods --- src/lib/mina/token/token-contract.ts | 38 ++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index af0301bf3e..1659e506d9 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -1,5 +1,6 @@ import { Bool } from '../../core.js'; -import { UInt64 } from '../../int.js'; +import { UInt64, Int64 } from '../../int.js'; +import { Provable } from '../../provable.js'; import { PublicKey } from '../../signature.js'; import { AccountUpdate, Permissions } from '../../account_update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; @@ -28,7 +29,7 @@ class TokenContract extends SmartContract { } // APPROVABLE API has to be specified by subclasses, - // but the hard part is `approveEachUpdate()` + // but the hard part is `forEachUpdate()` approveUpdates(_: CallForest) { throw Error( @@ -36,6 +37,11 @@ class TokenContract extends SmartContract { ); } + /** + * Iterate through the account updates in `updates` and apply `callback` to each. + * + * This method is provable and is suitable as a base for implementing `approveUpdates()`. + */ forEachUpdate( updates: CallForest, callback: (update: AccountUpdate, usesToken: Bool) => void @@ -66,8 +72,36 @@ class TokenContract extends SmartContract { }; } + /** + * Use `forEachUpdate()` to prove that the total balance change of child account updates is zero. + * + * This is provided out of the box as it is both a good example, and probably the most common implementation, of `approveUpdates()`. + */ + checkZeroBalanceChange(updates: CallForest) { + let totalBalanceChange = Int64.zero; + + this.forEachUpdate(updates, (accountUpdate, usesToken) => { + totalBalanceChange = totalBalanceChange.add( + Provable.if(usesToken, accountUpdate.balanceChange, Int64.zero) + ); + }); + + // prove that the total balance change is zero + totalBalanceChange.assertEquals(0); + } + + /** + * Convenience method for approving a single account update. + */ + approveAccountUpdate(accountUpdate: AccountUpdate) { + this.approveUpdates(CallForest.fromAccountUpdates([accountUpdate])); + } + // TRANSFERABLE API - simple wrapper around Approvable API + /** + * Transfer `amount` of tokens from `from` to `to`. + */ transfer( from: PublicKey | AccountUpdate, to: PublicKey | AccountUpdate, From cface0f23943ecf0859f9f8540512526a5fb4a1e Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 29 Jan 2024 13:56:03 +0100 Subject: [PATCH 226/524] fix token contract child adopting --- src/lib/circuit_value.ts | 5 ++- src/lib/mina/token/token-contract.ts | 40 ++++++++++++------- .../mina/token/token-contract.unit-test.ts | 2 +- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 67bc6515af..a24e58e83a 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -599,14 +599,15 @@ function cloneCircuitValue(obj: T): T { // primitive JS types and functions aren't cloned if (typeof obj !== 'object' || obj === null) return obj; - // HACK: callbacks, account udpates + // HACK: callbacks if ( obj.constructor?.name.includes('GenericArgument') || obj.constructor?.name.includes('Callback') ) { return obj; } - if (obj.constructor?.name.includes('AccountUpdate')) { + // classes that define clone() are cloned using that method + if (obj.constructor !== undefined && 'clone' in obj.constructor) { return (obj as any).constructor.clone(obj); } diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 1659e506d9..3dfe8b38d6 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -17,7 +17,7 @@ const MAX_ACCOUNT_UPDATES = 20; * - implements the `Approvable` API with some easy bit left to be defined by subclasses * - implements the `Transferable` API as a wrapper around the `Approvable` API */ -class TokenContract extends SmartContract { +abstract class TokenContract extends SmartContract { // change default permissions - important that token contracts use an access permission deploy(args?: DeployArgs) { @@ -31,11 +31,7 @@ class TokenContract extends SmartContract { // APPROVABLE API has to be specified by subclasses, // but the hard part is `forEachUpdate()` - approveUpdates(_: CallForest) { - throw Error( - 'TokenContract.approveUpdates() must be implemented by subclasses' - ); - } + abstract approveBase(forest: CallForest): void; /** * Iterate through the account updates in `updates` and apply `callback` to each. @@ -51,11 +47,7 @@ class TokenContract extends SmartContract { // iterate through the forest and apply user-defined logc for (let i = 0; i < MAX_ACCOUNT_UPDATES; i++) { let { accountUpdate, usesThisToken } = forest.next(); - callback(accountUpdate, usesThisToken); - - // add update to our children - this.self.adopt(accountUpdate); } // prove that we checked all updates @@ -70,6 +62,13 @@ class TokenContract extends SmartContract { type: 'WitnessEquals', value: updates.hash, }; + + // make top-level updates our children + Provable.asProver(() => { + updates.data.get().forEach((update) => { + this.self.adopt(update.element.accountUpdate.value.get()); + }); + }); } /** @@ -91,10 +90,17 @@ class TokenContract extends SmartContract { } /** - * Convenience method for approving a single account update. + * Approve a single account update (with arbitrarily many children). */ approveAccountUpdate(accountUpdate: AccountUpdate) { - this.approveUpdates(CallForest.fromAccountUpdates([accountUpdate])); + this.approveBase(CallForest.fromAccountUpdates([accountUpdate])); + } + + /** + * Approve a list of account updates (with arbitrarily many children). + */ + approveAccountUpdates(accountUpdates: AccountUpdate[]) { + this.approveBase(CallForest.fromAccountUpdates(accountUpdates)); } // TRANSFERABLE API - simple wrapper around Approvable API @@ -111,14 +117,18 @@ class TokenContract extends SmartContract { let tokenId = this.token.id; if (from instanceof PublicKey) { from = AccountUpdate.defaultAccountUpdate(from, tokenId); + from.requireSignature(); + from.label = `${this.constructor.name}.transfer() (from)`; } if (to instanceof PublicKey) { to = AccountUpdate.defaultAccountUpdate(to, tokenId); + + to.label = `${this.constructor.name}.transfer() (to)`; } - from.balance.subInPlace(amount); - to.balance.addInPlace(amount); + from.balanceChange = Int64.from(amount).neg(); + to.balanceChange = Int64.from(amount); let forest = CallForest.fromAccountUpdates([from, to]); - this.approveUpdates(forest); + this.approveBase(forest); } } diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts index d3fe7968d0..974749736f 100644 --- a/src/lib/mina/token/token-contract.unit-test.ts +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -13,7 +13,7 @@ class ExampleTokenContract extends TokenContract { // APPROVABLE API @method - approveUpdates(updates: CallForest) { + approveBase(updates: CallForest) { let totalBalanceChange = Int64.zero; this.forEachUpdate(updates, (accountUpdate, usesToken) => { From 926901ad379a7a9fad87d2db6e424c76b6337994 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 29 Jan 2024 14:14:38 +0100 Subject: [PATCH 227/524] unlink account updates to prevent them from being in caller's public input in compile() --- src/lib/mina/token/token-contract.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 3dfe8b38d6..5837995e49 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -93,6 +93,7 @@ abstract class TokenContract extends SmartContract { * Approve a single account update (with arbitrarily many children). */ approveAccountUpdate(accountUpdate: AccountUpdate) { + AccountUpdate.unlink(accountUpdate); this.approveBase(CallForest.fromAccountUpdates([accountUpdate])); } @@ -100,6 +101,7 @@ abstract class TokenContract extends SmartContract { * Approve a list of account updates (with arbitrarily many children). */ approveAccountUpdates(accountUpdates: AccountUpdate[]) { + accountUpdates.forEach(AccountUpdate.unlink); this.approveBase(CallForest.fromAccountUpdates(accountUpdates)); } @@ -127,6 +129,8 @@ abstract class TokenContract extends SmartContract { } from.balanceChange = Int64.from(amount).neg(); to.balanceChange = Int64.from(amount); + AccountUpdate.unlink(from); + AccountUpdate.unlink(to); let forest = CallForest.fromAccountUpdates([from, to]); this.approveBase(forest); From 66a892aebc65e939614615436213682259456ce9 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jan 2024 15:16:30 +0200 Subject: [PATCH 228/524] Addressing review comments. --- src/lib/signature.ts | 15 +++++---------- src/mina-signer/MinaSigner.ts | 6 +++--- .../tests/verify-in-snark.unit-test.ts | 18 +++++------------- 3 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 0adced0ff8..58d68ccef0 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -14,7 +14,6 @@ import { import { prefixes } from '../bindings/crypto/constants.js'; import { constantScalarToBigint } from './scalar.js'; import { toConstantField } from './field.js'; -import { NetworkId } from 'src/mina-signer/src/TSTypes.js'; // external API export { PrivateKey, PublicKey, Signature }; @@ -236,7 +235,7 @@ class Signature extends CircuitValue { * Signs a message using a {@link PrivateKey}. * @returns a {@link Signature} */ - static create(privKey: PrivateKey, msg: Field[], networkId: NetworkId = 'testnet'): Signature { + static create(privKey: PrivateKey, msg: Field[]): Signature { const publicKey = PublicKey.fromPrivateKey(privKey).toGroup(); const d = privKey.s; const kPrime = Scalar.fromBigInt( @@ -244,15 +243,13 @@ class Signature extends CircuitValue { { fields: msg.map((f) => f.toBigInt()) }, { x: publicKey.x.toBigInt(), y: publicKey.y.toBigInt() }, BigInt(d.toJSON()), - networkId + 'testnet' ) ); let { x: r, y: ry } = Group.generator.scale(kPrime); const k = ry.toBits()[0].toBoolean() ? kPrime.neg() : kPrime; let h = hashWithPrefix( - networkId === 'mainnet' - ? prefixes.signatureMainnet - : prefixes.signatureTestnet, + prefixes.signatureTestnet, msg.concat([publicKey.x, publicKey.y, r]) ); // TODO: Scalar.fromBits interprets the input as a "shifted scalar" @@ -266,12 +263,10 @@ class Signature extends CircuitValue { * Verifies the {@link Signature} using a message and the corresponding {@link PublicKey}. * @returns a {@link Bool} */ - verify(publicKey: PublicKey, msg: Field[], networkId: NetworkId = 'testnet'): Bool { + verify(publicKey: PublicKey, msg: Field[]): Bool { const point = publicKey.toGroup(); let h = hashWithPrefix( - networkId === 'mainnet' - ? prefixes.signatureMainnet - : prefixes.signatureTestnet, + prefixes.signatureTestnet, msg.concat([point.x, point.y, this.r]) ); // TODO: Scalar.fromBits interprets the input as a "shifted scalar" diff --git a/src/mina-signer/MinaSigner.ts b/src/mina-signer/MinaSigner.ts index 8671f5c92d..559bcb27d3 100644 --- a/src/mina-signer/MinaSigner.ts +++ b/src/mina-signer/MinaSigner.ts @@ -124,7 +124,7 @@ class Client { */ signFields(fields: bigint[], privateKey: Json.PrivateKey): Signed { let privateKey_ = PrivateKey.fromBase58(privateKey); - let signature = sign({ fields }, privateKey_, 'mainnet'); + let signature = sign({ fields }, privateKey_, 'testnet'); return { signature: Signature.toBase58(signature), publicKey: PublicKey.toBase58(PrivateKey.toPublicKey(privateKey_)), @@ -144,7 +144,7 @@ class Client { Signature.fromBase58(signature), { fields: data }, PublicKey.fromBase58(publicKey), - 'mainnet' + 'testnet' ); } @@ -499,7 +499,7 @@ class Client { * * @returns {NetworkId} The network ID. */ - getNetworkId(): NetworkId { + get networkId(): NetworkId { return this.network; } } diff --git a/src/mina-signer/tests/verify-in-snark.unit-test.ts b/src/mina-signer/tests/verify-in-snark.unit-test.ts index 4c1c84e555..c32180cff0 100644 --- a/src/mina-signer/tests/verify-in-snark.unit-test.ts +++ b/src/mina-signer/tests/verify-in-snark.unit-test.ts @@ -19,18 +19,14 @@ expect(ok).toEqual(true); // sign with o1js and check that we get the same signature let fieldsSnarky = fields.map(Field); let privateKeySnarky = PrivateKey.fromBase58(privateKey); -let signatureSnarky = Signature.create( - privateKeySnarky, - fieldsSnarky, - client.getNetworkId() -); +let signatureSnarky = Signature.create(privateKeySnarky, fieldsSnarky); expect(signatureSnarky.toBase58()).toEqual(signed.signature); // verify out-of-snark with o1js let publicKey = privateKeySnarky.toPublicKey(); let signature = Signature.fromBase58(signed.signature); Provable.assertEqual(Signature, signature, signatureSnarky); -signature.verify(publicKey, fieldsSnarky, client.getNetworkId()).assertTrue(); +signature.verify(publicKey, fieldsSnarky).assertTrue(); // verify in-snark with o1js const Message = Provable.Array(Field, fields.length); @@ -41,9 +37,7 @@ const MyProgram = ZkProgram({ verifySignature: { privateInputs: [Signature, Message], method(signature: Signature, message: Field[]) { - signature - .verify(publicKey, message, client.getNetworkId()) - .assertTrue(); + signature.verify(publicKey, message).assertTrue(); }, }, }, @@ -61,9 +55,7 @@ let invalidSigned = client.signFields(fields, wrongKey); let invalidSignature = Signature.fromBase58(invalidSigned.signature); // can't verify out of snark -invalidSignature - .verify(publicKey, fieldsSnarky, client.getNetworkId()) - .assertFalse(); +invalidSignature.verify(publicKey, fieldsSnarky).assertFalse(); // can't verify in snark await expect(() => @@ -76,7 +68,7 @@ let wrongFields = [...fieldsSnarky]; wrongFields[0] = wrongFields[0].add(1); // can't verify out of snark -signature.verify(publicKey, wrongFields, client.getNetworkId()).assertFalse(); +signature.verify(publicKey, wrongFields).assertFalse(); // can't verify in snark await expect(() => From f7a1e8568969e8e5d908bdc3a4a3faa948f912e6 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jan 2024 16:18:23 +0200 Subject: [PATCH 229/524] Addressing review comments. --- src/lib/mina.ts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index da54abf118..fbac14657b 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -399,16 +399,7 @@ function LocalBlockchain({ const genesisTimestamp = UInt64.from(startTime); const ledger = Ledger.create(); let networkState = defaultNetworkState(); - let minaNetworkId: NetworkId = 'testnet'; - - if (networkId) { - if (networkId !== 'mainnet' && networkId !== 'testnet') { - throw Error( - "Invalid network id specified. Please use 'mainnet' or 'testnet'" - ); - } - minaNetworkId = networkId; - } + let minaNetworkId: NetworkId = networkId; function addAccount(publicKey: PublicKey, balance: string) { ledger.addAccount(Ml.fromPublicKey(publicKey), balance); @@ -731,11 +722,6 @@ function Network( Fetch.setGraphqlEndpoint(minaGraphqlEndpoint); } else if (options && typeof options === 'object') { if (options.networkId) { - if (options.networkId !== 'mainnet' && options.networkId !== 'testnet') { - throw Error( - "Invalid network id specified. Please use 'mainnet' or 'testnet'" - ); - } minaNetworkId = options.networkId; } if (!options.mina) From 429d2d25cbd06ecfdeadf5d942272f37ff64994a Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jan 2024 17:01:45 +0200 Subject: [PATCH 230/524] CHANGELOG entry. --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36b3109217..3ec9738103 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Improve performance of Field inverse https://github.com/o1-labs/o1js/pull/1373 - Speeds up proving by ~2-4% +### Added + +- Target `network ID` configuration. https://github.com/o1-labs/o1js/pull/1387 + - Defaults to the `testnet` (as it was before). + ## [0.15.3](https://github.com/o1-labs/o1js/compare/1ad7333e9e...be748e42e) ### Breaking changes From e5d1e0f72838ffa29de12fe534fc89f27917653f Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jan 2024 17:05:18 +0200 Subject: [PATCH 231/524] Minor version bump. --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d7e23d73cd..f6abce7a96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "o1js", - "version": "0.15.3", + "version": "0.15.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "o1js", - "version": "0.15.3", + "version": "0.15.4", "license": "Apache-2.0", "dependencies": { "blakejs": "1.2.1", diff --git a/package.json b/package.json index affed9efdc..59b5e2c705 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "o1js", "description": "TypeScript framework for zk-SNARKs and zkApps", - "version": "0.15.3", + "version": "0.15.4", "license": "Apache-2.0", "homepage": "https://github.com/o1-labs/o1js/", "keywords": [ From 61d85ad4764b96281df8af64e8360b5729a87faf Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Mon, 29 Jan 2024 20:47:32 +0200 Subject: [PATCH 232/524] Bump mina-signer and export NetworkId type. --- src/mina-signer/index.d.ts | 1 + src/mina-signer/package-lock.json | 4 ++-- src/mina-signer/package.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mina-signer/index.d.ts b/src/mina-signer/index.d.ts index d021add39e..9c3a5698e8 100644 --- a/src/mina-signer/index.d.ts +++ b/src/mina-signer/index.d.ts @@ -1,5 +1,6 @@ // this file is a wrapper for supporting types in both commonjs and esm projects import Client from './MinaSigner.js'; +export { type NetworkId } from './src/TSTypes.js'; export = Client; diff --git a/src/mina-signer/package-lock.json b/src/mina-signer/package-lock.json index b73b420ae1..325fd4e3e0 100644 --- a/src/mina-signer/package-lock.json +++ b/src/mina-signer/package-lock.json @@ -1,12 +1,12 @@ { "name": "mina-signer", - "version": "2.1.1", + "version": "2.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mina-signer", - "version": "2.1.1", + "version": "2.1.2", "license": "Apache-2.0", "dependencies": { "blakejs": "^1.2.1", diff --git a/src/mina-signer/package.json b/src/mina-signer/package.json index bb2dc803d7..1eda8112fd 100644 --- a/src/mina-signer/package.json +++ b/src/mina-signer/package.json @@ -1,7 +1,7 @@ { "name": "mina-signer", "description": "Node API for signing transactions on various networks for Mina Protocol", - "version": "2.1.1", + "version": "2.1.2", "type": "module", "scripts": { "build": "tsc -p ../../tsconfig.mina-signer.json", From a9bbee576fb46514a65c742da961c8c8c3ed3e7a Mon Sep 17 00:00:00 2001 From: cristiantroy <154241727+cristiantroy@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:42:50 +0800 Subject: [PATCH 233/524] fix typo in src/examples/api_exploration.ts --- src/examples/api_exploration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/api_exploration.ts b/src/examples/api_exploration.ts index 43e2284950..6ed949a862 100644 --- a/src/examples/api_exploration.ts +++ b/src/examples/api_exploration.ts @@ -86,7 +86,7 @@ const b3: Bool = b0.and(b1.not()).or(b1); ): T ``` - `Provable.if(b, x, y)` evaluates to `x` if `b` is true, and evalutes to `y` if `b` is false, + `Provable.if(b, x, y)` evaluates to `x` if `b` is true, and evaluates to `y` if `b` is false, so it works like a ternary if expression `b ? x : y`. The generic type T can be instantiated to primitive types like Bool, Field, or Group, or From 1a367a73ebe5c8d4330b2aa37a251be1cb369b8c Mon Sep 17 00:00:00 2001 From: cristiantroy <154241727+cristiantroy@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:43:06 +0800 Subject: [PATCH 234/524] fix typo in src/lib/circuit_value.ts --- src/lib/circuit_value.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index d42f267cdf..f3f0705a60 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -576,7 +576,7 @@ function cloneCircuitValue(obj: T): T { // primitive JS types and functions aren't cloned if (typeof obj !== 'object' || obj === null) return obj; - // HACK: callbacks, account udpates + // HACK: callbacks, account updates if ( obj.constructor?.name.includes('GenericArgument') || obj.constructor?.name.includes('Callback') From af7414fe8032cd4cb6396d6f0237387239d2afb5 Mon Sep 17 00:00:00 2001 From: cristiantroy <154241727+cristiantroy@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:43:20 +0800 Subject: [PATCH 235/524] fix typo in src/lib/hash-input.unit-test.ts --- src/lib/hash-input.unit-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/hash-input.unit-test.ts b/src/lib/hash-input.unit-test.ts index 35b5436dd6..5b5f357dca 100644 --- a/src/lib/hash-input.unit-test.ts +++ b/src/lib/hash-input.unit-test.ts @@ -46,7 +46,7 @@ let NetworkPrecondition = provableFromLayout( ); let Body = provableFromLayout(bodyLayout as any); -// test with random account udpates +// test with random account updates test(Random.json.accountUpdate, (accountUpdateJson) => { fixVerificationKey(accountUpdateJson); let accountUpdate = AccountUpdate.fromJSON(accountUpdateJson); From b878012bc0f94d58f2011c655aa8b6edbec4832b Mon Sep 17 00:00:00 2001 From: cristiantroy <154241727+cristiantroy@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:43:31 +0800 Subject: [PATCH 236/524] fix typo in src/lib/precondition.ts --- src/lib/precondition.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index ac5fd8f8aa..1badbf5c66 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -416,7 +416,7 @@ function assertPreconditionInvariants(accountUpdate: AccountUpdate) { let self = context.isSelf ? 'this' : 'accountUpdate'; let dummyPreconditions = Preconditions.ignoreAll(); for (let preconditionPath of context.read) { - // check if every precondition that was read was also contrained + // check if every precondition that was read was also constrained if (context.constrained.has(preconditionPath)) continue; // check if the precondition was modified manually, which is also a valid way of avoiding an error From 0a36b025b95c390a6f891555a52203a27451954f Mon Sep 17 00:00:00 2001 From: cristiantroy <154241727+cristiantroy@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:43:42 +0800 Subject: [PATCH 237/524] fix typo in src/lib/state.ts --- src/lib/state.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/state.ts b/src/lib/state.ts index dc848a1996..bf6a534ec6 100644 --- a/src/lib/state.ts +++ b/src/lib/state.ts @@ -409,7 +409,7 @@ const reservedPropNames = new Set(['_methods', '_']); function assertStatePrecondition(sc: SmartContract) { try { for (let [key, context] of getStateContexts(sc)) { - // check if every state that was read was also contrained + // check if every state that was read was also constrained if (!context?.wasRead || context.wasConstrained) continue; // we accessed a precondition field but not constrained it explicitly - throw an error let errorMessage = `You used \`this.${key}.get()\` without adding a precondition that links it to the actual on-chain state. From 67074b68e233c7a0e645b07ce365555fc3ab2ecf Mon Sep 17 00:00:00 2001 From: cristiantroy <154241727+cristiantroy@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:43:52 +0800 Subject: [PATCH 238/524] fix typo in src/lib/string.ts --- src/lib/string.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/string.ts b/src/lib/string.ts index 8045dd3318..360f66465f 100644 --- a/src/lib/string.ts +++ b/src/lib/string.ts @@ -102,7 +102,7 @@ class CircuitString extends CircuitValue { .slice(0, length) .concat(otherChars.slice(0, n - length)); } - // compute the actual result, by always picking the char which correponds to the actual length + // compute the actual result, by always picking the char which corresponds to the actual length let result: Character[] = []; let mask = this.lengthMask(); for (let i = 0; i < n; i++) { From 70ed6da1864dd24f95d75fe20509ccce010e8ff6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 29 Jan 2024 14:16:34 +0100 Subject: [PATCH 239/524] (wip) use base token contract for dex --- src/examples/zkapps/dex/dex-with-actions.ts | 4 +- src/examples/zkapps/dex/dex.ts | 130 ++++++++---------- .../zkapps/dex/happy-path-with-actions.ts | 8 +- .../zkapps/dex/happy-path-with-proofs.ts | 8 +- src/examples/zkapps/dex/run.ts | 8 +- src/examples/zkapps/dex/run_live.ts | 8 +- src/examples/zkapps/dex/upgradability.ts | 4 +- 7 files changed, 81 insertions(+), 89 deletions(-) diff --git a/src/examples/zkapps/dex/dex-with-actions.ts b/src/examples/zkapps/dex/dex-with-actions.ts index 145197319b..0a93ce572a 100644 --- a/src/examples/zkapps/dex/dex-with-actions.ts +++ b/src/examples/zkapps/dex/dex-with-actions.ts @@ -188,7 +188,7 @@ class Dex extends SmartContract { let tokenY = new TokenContract(this.tokenY); let dexY = new DexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.approveUpdateAndSend(dexY.self, this.sender, dy); + tokenY.transfer(dexY.self, this.sender, dy); return dy; } @@ -206,7 +206,7 @@ class Dex extends SmartContract { let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dx = dexX.swap(this.sender, dy, this.tokenY); - tokenX.approveUpdateAndSend(dexX.self, this.sender, dx); + tokenX.transfer(dexX.self, this.sender, dx); return dx; } diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 44ac6b5178..e10aa44668 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -2,9 +2,6 @@ import { Account, AccountUpdate, Bool, - DeployArgs, - Field, - Int64, Mina, Permissions, PrivateKey, @@ -19,6 +16,8 @@ import { VerificationKey, method, state, + TokenContract as BaseTokenContract, + CallForest, } from 'o1js'; export { TokenContract, addresses, createDex, keys, randomAccounts, tokenIds }; @@ -144,7 +143,7 @@ function createDex({ let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dxdy = dexX.redeemLiquidity(this.sender, dl, this.tokenY); let dx = dxdy[0]; - tokenX.approveUpdateAndSend(dexX.self, this.sender, dx); + tokenX.transfer(dexX.self, this.sender, dx); return dxdy; } @@ -159,7 +158,7 @@ function createDex({ let tokenY = new TokenContract(this.tokenY); let dexY = new DexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.approveUpdateAndSend(dexY.self, this.sender, dy); + tokenY.transfer(dexY.self, this.sender, dy); return dy; } @@ -174,7 +173,7 @@ function createDex({ let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dx = dexX.swap(this.sender, dy, this.tokenY); - tokenX.approveUpdateAndSend(dexX.self, this.sender, dx); + tokenX.transfer(dexX.self, this.sender, dx); return dx; } @@ -208,7 +207,7 @@ function createDex({ let tokenY = new TokenContract(this.tokenY); let dexY = new ModifiedDexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.approveUpdateAndSend(dexY.self, this.sender, dy); + tokenY.transfer(dexY.self, this.sender, dy); return dy; } } @@ -250,7 +249,7 @@ function createDex({ let result = dexY.redeemLiquidityPartial(user, dl); let l = result[0]; let dy = result[1]; - tokenY.approveUpdateAndSend(dexY.self, user, dy); + tokenY.transfer(dexY.self, user, dy); // in return for dl, we give back dx, the X token part let x = this.account.balance.get(); @@ -371,14 +370,7 @@ function createDex({ /** * Simple token with API flexible enough to handle all our use cases */ -class TokenContract extends SmartContract { - deploy(args?: DeployArgs) { - super.deploy(args); - this.account.permissions.set({ - ...Permissions.default(), - access: Permissions.proofOrSignature(), - }); - } +class TokenContract extends BaseTokenContract { @method init() { super.init(); // mint the entire supply to the token account with the same address as this contract @@ -413,6 +405,11 @@ class TokenContract extends SmartContract { this.balance.subInPlace(Mina.accountCreationFee()); } + @method + approveBase(forest: CallForest) { + this.checkZeroBalanceChange(forest); + } + // this is a very standardized deploy method. instead, we could also take the account update from a callback // => need callbacks for signatures @method deployZkapp(address: PublicKey, verificationKey: VerificationKey) { @@ -423,50 +420,50 @@ class TokenContract extends SmartContract { zkapp.requireSignature(); } - @method approveUpdate(zkappUpdate: AccountUpdate) { - this.approve(zkappUpdate); - let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); - balanceChange.assertEquals(Int64.from(0)); - } - - // FIXME: remove this - @method approveAny(zkappUpdate: AccountUpdate) { - this.approve(zkappUpdate, AccountUpdate.Layout.AnyChildren); - } - - // let a zkapp send tokens to someone, provided the token supply stays constant - @method approveUpdateAndSend( - zkappUpdate: AccountUpdate, - to: PublicKey, - amount: UInt64 - ) { - // approve a layout of two grandchildren, both of which can't inherit the token permission - let { StaticChildren, AnyChildren } = AccountUpdate.Layout; - this.approve(zkappUpdate, StaticChildren(AnyChildren, AnyChildren)); - zkappUpdate.body.mayUseToken.parentsOwnToken.assertTrue(); - let [grandchild1, grandchild2] = zkappUpdate.children.accountUpdates; - grandchild1.body.mayUseToken.inheritFromParent.assertFalse(); - grandchild2.body.mayUseToken.inheritFromParent.assertFalse(); - - // see if balance change cancels the amount sent - let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); - balanceChange.assertEquals(Int64.from(amount).neg()); - // add same amount of tokens to the receiving address - this.token.mint({ address: to, amount }); - } - - transfer(from: PublicKey, to: PublicKey | AccountUpdate, amount: UInt64) { - if (to instanceof PublicKey) - return this.transferToAddress(from, to, amount); - if (to instanceof AccountUpdate) - return this.transferToUpdate(from, to, amount); - } - @method transferToAddress(from: PublicKey, to: PublicKey, value: UInt64) { - this.token.send({ from, to, amount: value }); - } - @method transferToUpdate(from: PublicKey, to: AccountUpdate, value: UInt64) { - this.token.send({ from, to, amount: value }); - } + // @method _approveUpdate(zkappUpdate: AccountUpdate) { + // this.approve(zkappUpdate); + // let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); + // balanceChange.assertEquals(Int64.from(0)); + // } + + // // FIXME: remove this + // @method _approveAny(zkappUpdate: AccountUpdate) { + // this.approve(zkappUpdate, AccountUpdate.Layout.AnyChildren); + // } + + // // let a zkapp send tokens to someone, provided the token supply stays constant + // @method _approveUpdateAndSend( + // zkappUpdate: AccountUpdate, + // to: PublicKey, + // amount: UInt64 + // ) { + // // approve a layout of two grandchildren, both of which can't inherit the token permission + // let { StaticChildren, AnyChildren } = AccountUpdate.Layout; + // this.approve(zkappUpdate, StaticChildren(AnyChildren, AnyChildren)); + // zkappUpdate.body.mayUseToken.parentsOwnToken.assertTrue(); + // let [grandchild1, grandchild2] = zkappUpdate.children.accountUpdates; + // grandchild1.body.mayUseToken.inheritFromParent.assertFalse(); + // grandchild2.body.mayUseToken.inheritFromParent.assertFalse(); + + // // see if balance change cancels the amount sent + // let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); + // balanceChange.assertEquals(Int64.from(amount).neg()); + // // add same amount of tokens to the receiving address + // this.token.mint({ address: to, amount }); + // } + + // _transfer(from: PublicKey, to: PublicKey | AccountUpdate, amount: UInt64) { + // if (to instanceof PublicKey) + // return this._transferToAddress(from, to, amount); + // if (to instanceof AccountUpdate) + // return this._transferToUpdate(from, to, amount); + // } + // @method _transferToAddress(from: PublicKey, to: PublicKey, value: UInt64) { + // this.token.send({ from, to, amount: value }); + // } + // @method _transferToUpdate(from: PublicKey, to: AccountUpdate, value: UInt64) { + // this.token.send({ from, to, amount: value }); + // } @method getBalance(publicKey: PublicKey): UInt64 { let accountUpdate = AccountUpdate.create(publicKey, this.token.id); @@ -502,19 +499,6 @@ let tokenIds = { lqXY: TokenId.derive(addresses.dex), }; -/** - * Sum of balances of the account update and all its descendants - */ -function balanceSum(accountUpdate: AccountUpdate, tokenId: Field) { - let myTokenId = accountUpdate.body.tokenId; - let myBalance = Int64.fromObject(accountUpdate.body.balanceChange); - let balance = Provable.if(myTokenId.equals(tokenId), myBalance, Int64.zero); - for (let child of accountUpdate.children.accountUpdates) { - balance = balance.add(balanceSum(child, tokenId)); - } - return balance; -} - /** * Predefined accounts keys, labeled by the input strings. Useful for testing/debugging with consistent keys. */ diff --git a/src/examples/zkapps/dex/happy-path-with-actions.ts b/src/examples/zkapps/dex/happy-path-with-actions.ts index 16ea8fddcb..1e722e0785 100644 --- a/src/examples/zkapps/dex/happy-path-with-actions.ts +++ b/src/examples/zkapps/dex/happy-path-with-actions.ts @@ -65,9 +65,9 @@ tx = await Mina.transaction(feePayerAddress, () => { ); dex.deploy(); dexTokenHolderX.deploy(); - tokenX.approveUpdate(dexTokenHolderX.self); + tokenX.approveAccountUpdate(dexTokenHolderX.self); dexTokenHolderY.deploy(); - tokenY.approveUpdate(dexTokenHolderY.self); + tokenY.approveAccountUpdate(dexTokenHolderY.self); }); await tx.prove(); await tx.sign([feePayerKey, keys.dex]).send(); @@ -129,7 +129,7 @@ console.log(getTokenBalances()); tic('redeem liquidity, step 2a (get back token X)'); tx = await Mina.transaction(addresses.user, () => { dexTokenHolderX.redeemLiquidityFinalize(); - tokenX.approveAny(dexTokenHolderX.self); + tokenX.approveAccountUpdate(dexTokenHolderX.self); }); await tx.prove(); await tx.sign([keys.user]).send(); @@ -140,7 +140,7 @@ console.log(getTokenBalances()); tic('redeem liquidity, step 2b (get back token Y)'); tx = await Mina.transaction(addresses.user, () => { dexTokenHolderY.redeemLiquidityFinalize(); - tokenY.approveAny(dexTokenHolderY.self); + tokenY.approveAccountUpdate(dexTokenHolderY.self); }); await tx.prove(); await tx.sign([keys.user]).send(); diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index 4727851012..046fd46efc 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -67,9 +67,9 @@ tx = await Mina.transaction(feePayerAddress, () => { ); dex.deploy(); dexTokenHolderX.deploy(); - tokenX.approveUpdate(dexTokenHolderX.self); + tokenX.approveAccountUpdate(dexTokenHolderX.self); dexTokenHolderY.deploy(); - tokenY.approveUpdate(dexTokenHolderY.self); + tokenY.approveAccountUpdate(dexTokenHolderY.self); }); await tx.prove(); await tx.sign([feePayerKey, keys.dex]).send(); @@ -109,6 +109,10 @@ let USER_DL = 100n; tx = await Mina.transaction(addresses.user, () => { dex.redeemLiquidity(UInt64.from(USER_DL)); }); + +console.log(tx.transaction.accountUpdates[0].toPrettyLayout()); +console.log(tx.toPretty()); + await tx.prove(); await tx.sign([keys.user]).send(); toc(); diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index e9f15fad22..f6e0b67166 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -100,9 +100,9 @@ async function main({ withVesting }: { withVesting: boolean }) { AccountUpdate.fundNewAccount(feePayerAddress, 3); dex.deploy(); dexTokenHolderX.deploy(); - tokenX.approveUpdate(dexTokenHolderX.self); + tokenX.approveAccountUpdate(dexTokenHolderX.self); dexTokenHolderY.deploy(); - tokenY.approveUpdate(dexTokenHolderY.self); + tokenY.approveAccountUpdate(dexTokenHolderY.self); }); await tx.prove(); tx.sign([feePayerKey, keys.dex]); @@ -360,6 +360,10 @@ async function main({ withVesting }: { withVesting: boolean }) { }); await tx.prove(); tx.sign([keys.user]); + + console.log(tx.transaction.accountUpdates[0].toPrettyLayout()); + console.log(tx.toPretty()); + await tx.send(); [oldBalances, balances] = [balances, getTokenBalances()]; console.log('DEX liquidity (X, Y):', balances.dex.X, balances.dex.Y); diff --git a/src/examples/zkapps/dex/run_live.ts b/src/examples/zkapps/dex/run_live.ts index db9336fcc4..5627e4579d 100644 --- a/src/examples/zkapps/dex/run_live.ts +++ b/src/examples/zkapps/dex/run_live.ts @@ -100,9 +100,9 @@ if (successfulTransactions <= 1) { AccountUpdate.createSigned(sender).balance.subInPlace(accountFee.mul(3)); dex.deploy(); dexTokenHolderX.deploy(); - tokenX.approveUpdate(dexTokenHolderX.self); + tokenX.approveAccountUpdate(dexTokenHolderX.self); dexTokenHolderY.deploy(); - tokenY.approveUpdate(dexTokenHolderY.self); + tokenY.approveAccountUpdate(dexTokenHolderY.self); }); await tx.prove(); pendingTx = await tx.sign([senderKey, keys.dex]).send(); @@ -203,7 +203,7 @@ if (successfulTransactions <= 6) { tic('redeem liquidity, step 2a (get back token X)'); tx = await Mina.transaction(userSpec, () => { dexTokenHolderX.redeemLiquidityFinalize(); - tokenX.approveAny(dexTokenHolderX.self); + tokenX.approveAccountUpdate(dexTokenHolderX.self); }); await tx.prove(); pendingTx = await tx.sign([keys.user]).send(); @@ -222,7 +222,7 @@ if (successfulTransactions <= 7) { tic('redeem liquidity, step 2b (get back token Y)'); tx = await Mina.transaction(userSpec, () => { dexTokenHolderY.redeemLiquidityFinalize(); - tokenY.approveAny(dexTokenHolderY.self); + tokenY.approveAccountUpdate(dexTokenHolderY.self); }); await tx.prove(); pendingTx = await tx.sign([keys.user]).send(); diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 273431c903..5765a39a71 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -88,9 +88,9 @@ async function atomicActionsTest({ withVesting }: { withVesting: boolean }) { AccountUpdate.fundNewAccount(feePayerAddress, 3); dex.deploy(); dexTokenHolderX.deploy(); - tokenX.approveUpdate(dexTokenHolderX.self); + tokenX.approveAccountUpdate(dexTokenHolderX.self); dexTokenHolderY.deploy(); - tokenY.approveUpdate(dexTokenHolderY.self); + tokenY.approveAccountUpdate(dexTokenHolderY.self); console.log('manipulating setDelegate field to impossible...'); // setting the setDelegate permission field to impossible let dexAccount = AccountUpdate.create(addresses.dex); From b46bd14062154e5e1eb830ffbfd7c48a856a60aa Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 29 Jan 2024 22:03:02 +0100 Subject: [PATCH 240/524] wip debugging / start fixing --- src/examples/zkapps/dex/happy-path-with-proofs.ts | 4 ++++ src/lib/account_update.ts | 6 +++++- src/lib/zkapp.ts | 15 +++++---------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index 046fd46efc..71aff062c3 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -44,6 +44,8 @@ let dex = new Dex(addresses.dex); let dexTokenHolderX = new DexTokenHolder(addresses.dex, tokenIds.X); let dexTokenHolderY = new DexTokenHolder(addresses.dex, tokenIds.Y); +Local.setProofsEnabled(false); + tic('deploy & init token contracts'); tx = await Mina.transaction(feePayerAddress, () => { // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves @@ -104,6 +106,8 @@ console.log('account updates length', tx.transaction.accountUpdates.length); [oldBalances, balances] = [balances, getTokenBalances()]; expect(balances.user.X).toEqual(0n); +Local.setProofsEnabled(true); + tic('redeem liquidity'); let USER_DL = 100n; tx = await Mina.transaction(addresses.user, () => { diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 3b61e3a98d..d51d97ee59 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1620,6 +1620,10 @@ const CallForest = { // hashes a accountUpdate's children (and their children, and ...) to compute // the `calls` field of ZkappPublicInput hashChildren(update: AccountUpdate): Field { + if (!Provable.inCheckedComputation()) { + return CallForest.hashChildrenBase(update); + } + let { callsType } = update.children; // compute hash outside the circuit if callsType is "Witness" // i.e., allowing accountUpdates with arbitrary children @@ -1630,7 +1634,7 @@ const CallForest = { return callsType.value; } let calls = CallForest.hashChildrenBase(update); - if (callsType.type === 'Equals' && Provable.inCheckedComputation()) { + if (callsType.type === 'Equals') { calls.assertEquals(callsType.value); } return calls; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 9933d9db20..f167cf57e7 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -446,16 +446,11 @@ function wrapMethod( }; // we have to run the called contract inside a witness block, to not affect the caller's circuit - // however, if this is a nested call -- the caller is already called by another contract --, - // then we're already in a witness block, and shouldn't open another one - let { accountUpdate, result } = - methodCallDepth === 0 - ? AccountUpdate.witness( - returnType ?? provable(null), - runCalledContract, - { skipCheck: true } - ) - : runCalledContract(); + let { accountUpdate, result } = AccountUpdate.witness( + returnType ?? provable(null), + runCalledContract, + { skipCheck: true } + ); // we're back in the _caller's_ circuit now, where we assert stuff about the method call From ad8ce691b361017e5d892cf91252e73995c6917f Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 12:47:33 +0100 Subject: [PATCH 241/524] add data structure for in-circuit call forest construction which only persists through a contract call --- src/lib/account_update.ts | 2 + src/lib/mina/token/call-forest.ts | 96 ++++++++++++++++++++++++++- src/lib/provable-types/merkle-list.ts | 1 + src/lib/zkapp.ts | 3 + 4 files changed, 100 insertions(+), 2 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index d51d97ee59..e2fede277f 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -33,6 +33,7 @@ import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; +import { CallForestUnderConstruction } from './mina/token/call-forest.js'; // external API export { AccountUpdate, Permissions, ZkappPublicInput }; @@ -67,6 +68,7 @@ type SmartContractContext = { this: SmartContract; methodCallDepth: number; selfUpdate: AccountUpdate; + selfCalls: CallForestUnderConstruction; }; let smartContractContext = Context.create({ default: null, diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index ccf3ccec9a..63016c9130 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -2,7 +2,7 @@ import { prefixes } from '../../../provable/poseidon-bigint.js'; import { AccountUpdate, TokenId } from '../../account_update.js'; import { Field } from '../../core.js'; import { Provable } from '../../provable.js'; -import { Struct } from '../../circuit_value.js'; +import { Struct, Unconstrained } from '../../circuit_value.js'; import { assert } from '../../gadgets/common.js'; import { Poseidon, ProvableHashable } from '../../hash.js'; import { Hashed } from '../../provable-types/packed.js'; @@ -11,11 +11,12 @@ import { MerkleListBase, MerkleList, genericHash, + withHashes, } from '../../provable-types/merkle-list.js'; export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; -export { HashedAccountUpdate }; +export { HashedAccountUpdate, CallForestUnderConstruction }; class HashedAccountUpdate extends Hashed.create( AccountUpdate, @@ -180,3 +181,94 @@ function hashCons(forestHash: Field, nodeHash: Field) { function hashAccountUpdate(update: AccountUpdate) { return genericHash(AccountUpdate, prefixes.body, update); } + +/** + * Structure for constructing a call forest from a circuit. + * + * The circuit can mutate account updates and change their array of children, so here we can't hash + * everything immediately. Instead, we maintain a structure consisting of either hashes or full account + * updates that can be hashed into a final call forest at the end. + */ +type CallForestUnderConstruction = HashOrValue< + { + accountUpdate: HashOrValue; + calls: CallForestUnderConstruction; + }[] +>; + +type HashOrValue = + | { useHash: true; hash: Field; value: T } + | { useHash: false; value: T }; + +const CallForestUnderConstruction = { + empty(): CallForestUnderConstruction { + return { useHash: false, value: [] }; + }, + + setHash(forest: CallForestUnderConstruction, hash: Field) { + Object.assign(forest, { useHash: true, hash }); + }, + + witnessHash(forest: CallForestUnderConstruction) { + let hash = Provable.witness(Field, () => { + let nodes = forest.value.map(toCallTree); + return withHashes(nodes, merkleListHash).hash; + }); + CallForestUnderConstruction.setHash(forest, hash); + }, + + push( + forest: CallForestUnderConstruction, + accountUpdate: HashOrValue, + calls?: CallForestUnderConstruction + ) { + forest.value.push({ + accountUpdate, + calls: calls ?? CallForestUnderConstruction.empty(), + }); + }, + + remove(forest: CallForestUnderConstruction, accountUpdate: AccountUpdate) { + // find account update by .id + let index = forest.value.findIndex( + (node) => node.accountUpdate.value.id === accountUpdate.id + ); + + // nothing to do if it's not there + if (index === -1) return; + + // remove it + forest.value.splice(index, 1); + }, + + finalize(forest: CallForestUnderConstruction): CallForest { + if (forest.useHash) { + let data = Unconstrained.witness(() => { + let nodes = forest.value.map(toCallTree); + return withHashes(nodes, merkleListHash).data; + }); + return new CallForest({ hash: forest.hash, data }); + } + + // not using the hash means we calculate it in-circuit + let nodes = forest.value.map(toCallTree); + return CallForest.from(nodes); + }, +}; + +function toCallTree(node: { + accountUpdate: HashOrValue; + calls: CallForestUnderConstruction; +}): CallTree { + let accountUpdate = node.accountUpdate.useHash + ? new HashedAccountUpdate( + node.accountUpdate.hash, + Unconstrained.from(node.accountUpdate.value) + ) + : HashedAccountUpdate.hash(node.accountUpdate.value); + + return { + accountUpdate, + calls: CallForestUnderConstruction.finalize(node.calls), + }; +} diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index 72dee6d025..0ee9c1500d 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -14,6 +14,7 @@ export { emptyHash, genericHash, merkleListHash, + withHashes, }; // common base types for both MerkleList and MerkleArray diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index f167cf57e7..6140eca774 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -57,6 +57,7 @@ import { } from './provable-context.js'; import { Cache } from './proof-system/cache.js'; import { assert } from './gadgets/common.js'; +import { CallForestUnderConstruction } from './mina/token/call-forest.js'; // external API export { @@ -165,6 +166,7 @@ function wrapMethod( this: this, methodCallDepth: 0, selfUpdate: selfAccountUpdate(this, methodName), + selfCalls: CallForestUnderConstruction.empty(), }; let id = smartContractContext.enter(context); try { @@ -363,6 +365,7 @@ function wrapMethod( this: this, methodCallDepth: methodCallDepth + 1, selfUpdate: selfAccountUpdate(this, methodName), + selfCalls: CallForestUnderConstruction.empty(), }; let id = smartContractContext.enter(innerContext); try { From 8f45168c426ceca82d3d2e08ec71a063d2422c56 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 14:01:47 +0100 Subject: [PATCH 242/524] fixed Hashed default hash --- src/lib/provable-types/packed.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index 8abbc310f8..5edc947dfa 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -183,8 +183,16 @@ class Hashed { type: ProvableHashable, hash?: (t: T) => Field ): typeof Hashed { - hash ??= Hashed._hash; - let dummyHash = hash(type.empty()); + // default hash function + let _hash = + hash ?? + function hash(t: T) { + let input = type.toInput(t); + let packed = packToFields(input); + return Poseidon.hash(packed); + }; + + let dummyHash = _hash(type.empty()); return class Hashed_ extends Hashed { static _innerProvable = type; @@ -193,7 +201,7 @@ class Hashed { value: Unconstrained.provable, }) as ProvableHashable>; - static _hash = (hash ?? Hashed._hash) satisfies (t: T) => Field; + static _hash = _hash satisfies (t: T) => Field; static empty(): Hashed { return new this(dummyHash, Unconstrained.from(type.empty())); @@ -206,10 +214,8 @@ class Hashed { this.value = value; } - static _hash(t: any) { - let input = this.innerProvable.toInput(t); - let packed = packToFields(input); - return Poseidon.hash(packed); + static _hash(_: any): Field { + assert(false, 'Hashed not initialized'); } /** From 27bf6fa4ffb0e77af82b61b62461664bdb6fac4f Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 14:18:11 +0100 Subject: [PATCH 243/524] fix edge case in ecdsa unit test --- src/lib/gadgets/ecdsa.unit-test.ts | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index f21adb722f..a04ccc932c 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -43,17 +43,6 @@ for (let Curve of curves) { msg: scalar, publicKey: record({ x: field, y: field }), }); - badSignature.rng = Random.withHardCoded(badSignature.rng, { - signature: { - r: 3243632040670678816425112099743675011873398345579979202080647260629177216981n, - s: 0n, - }, - msg: 0n, - publicKey: { - x: 28948022309329048855892746252171976963363056481941560715954676764349967630336n, - y: 2n, - }, - }); let signatureInputs = record({ privateKey, msg: scalar }); @@ -70,14 +59,23 @@ for (let Curve of curves) { let noGlv = fromRandom(Random.map(Random.fraction(), (f) => f < 0.3)); // provable method we want to test - const verify = (s: Second, noGlv: boolean) => { + const verify = (sig: Second, noGlv: boolean) => { // invalid public key can lead to either a failing constraint, or verify() returning false - EllipticCurve.assertOnCurve(s.publicKey, Curve); + EllipticCurve.assertOnCurve(sig.publicKey, Curve); + + // additional checks which are inconsistent between constant and variable verification + let { s, r } = sig.signature; + if (Field3.isConstant(s)) { + assert(Field3.toBigint(s) !== 0n, 'invalid signature (s=0)'); + } + if (Field3.isConstant(r)) { + assert(Field3.toBigint(r) !== 0n, 'invalid signature (r=0)'); + } let hasGlv = Curve.hasEndomorphism; if (noGlv) Curve.hasEndomorphism = false; // hack to force non-GLV version try { - return Ecdsa.verify(Curve, s.signature, s.msg, s.publicKey); + return Ecdsa.verify(Curve, sig.signature, sig.msg, sig.publicKey); } finally { Curve.hasEndomorphism = hasGlv; } From c016072c84741f2ac9650388dafe524cecc04622 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 15:49:06 +0100 Subject: [PATCH 244/524] restructure code to fix import cycles in account_update.ts --- src/index.ts | 11 +- src/lib/account_update.ts | 211 ++++------------------------ src/lib/mina.ts | 182 ++---------------------- src/lib/mina/mina-instance.ts | 123 ++++++++++++++++ src/lib/mina/smart-contract-base.ts | 13 ++ src/lib/mina/token/call-forest.ts | 2 +- src/lib/mina/transaction-context.ts | 16 +++ src/lib/precondition.ts | 173 ++++++++++++++++++++--- src/lib/zkapp.ts | 65 ++++++++- 9 files changed, 415 insertions(+), 381 deletions(-) create mode 100644 src/lib/mina/mina-instance.ts create mode 100644 src/lib/mina/smart-contract-base.ts create mode 100644 src/lib/mina/transaction-context.ts diff --git a/src/index.ts b/src/index.ts index dd06c7ee65..94233cadda 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,11 +41,6 @@ export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; export { MerkleList, MerkleArray } from './lib/provable-types/merkle-list.js'; -export { - CallForest, - CallForestIterator, -} from './lib/mina/token/call-forest.js'; -export { TokenContract } from './lib/mina/token/token-contract.js'; export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; @@ -78,6 +73,12 @@ export { ZkappPublicInput, } from './lib/account_update.js'; +export { + CallForest, + CallForestIterator, +} from './lib/mina/token/call-forest.js'; +export { TokenContract } from './lib/mina/token/token-contract.js'; + export type { TransactionStatus } from './lib/fetch.js'; export { fetchAccount, diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index e2fede277f..1d4d25f239 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -15,9 +15,17 @@ import { } from '../bindings/mina-transaction/types.js'; import { PrivateKey, PublicKey } from './signature.js'; import { UInt64, UInt32, Int64, Sign } from './int.js'; -import * as Mina from './mina.js'; -import { SmartContract } from './zkapp.js'; -import * as Precondition from './precondition.js'; +import type { SmartContract } from './zkapp.js'; +import { + Preconditions, + Account, + Network, + CurrentSlot, + preconditions, + OrIgnore, + ClosedInterval, + getAccountPreconditions, +} from './precondition.js'; import { dummyBase64Proof, Empty, Proof, Prover } from './proof_system.js'; import { Memo } from '../mina-signer/src/memo.js'; import { @@ -28,12 +36,14 @@ import { TokenId as Base58TokenId } from './base58-encodings.js'; import { hashWithPrefix, packToFields } from './hash.js'; import { mocks, prefixes } from '../bindings/crypto/constants.js'; import { Context } from './global-context.js'; -import { assert } from './errors.js'; import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; -import { CallForestUnderConstruction } from './mina/token/call-forest.js'; +import type { CallForestUnderConstruction } from './mina/token/call-forest.js'; +import { currentTransaction } from './mina/transaction-context.js'; +import { isSmartContract } from './mina/smart-contract-base.js'; +import { activeInstance } from './mina/mina-instance.js'; // external API export { AccountUpdate, Permissions, ZkappPublicInput }; @@ -60,6 +70,7 @@ export { zkAppProver, SmartContractContext, dummySignature, + LazyProof, }; const ZkappStateLength = 8; @@ -88,11 +99,6 @@ type Update = AccountUpdateBody['update']; type MayUseToken = AccountUpdateBody['mayUseToken']; -/** - * Preconditions for the network and accounts - */ -type Preconditions = AccountUpdateBody['preconditions']; - /** * Either set a value or keep it the same. */ @@ -485,107 +491,6 @@ type FeePayerUnsigned = FeePayer & { lazyAuthorization?: LazySignature | undefined; }; -/** - * Either check a value or ignore it. - * - * Used within [[ AccountPredicate ]]s and [[ ProtocolStatePredicate ]]s. - */ -type OrIgnore = { isSome: Bool; value: T }; - -/** - * An interval representing all the values between `lower` and `upper` inclusive - * of both the `lower` and `upper` values. - * - * @typeParam A something with an ordering where one can quantify a lower and - * upper bound. - */ -type ClosedInterval = { lower: T; upper: T }; - -type NetworkPrecondition = Preconditions['network']; -let NetworkPrecondition = { - ignoreAll(): NetworkPrecondition { - let stakingEpochData = { - ledger: { hash: ignore(Field(0)), totalCurrency: ignore(uint64()) }, - seed: ignore(Field(0)), - startCheckpoint: ignore(Field(0)), - lockCheckpoint: ignore(Field(0)), - epochLength: ignore(uint32()), - }; - let nextEpochData = cloneCircuitValue(stakingEpochData); - return { - snarkedLedgerHash: ignore(Field(0)), - blockchainLength: ignore(uint32()), - minWindowDensity: ignore(uint32()), - totalCurrency: ignore(uint64()), - globalSlotSinceGenesis: ignore(uint32()), - stakingEpochData, - nextEpochData, - }; - }, -}; - -/** - * Ignores a `dummy` - * - * @param dummy The value to ignore - * @returns Always an ignored value regardless of the input. - */ -function ignore(dummy: T): OrIgnore { - return { isSome: Bool(false), value: dummy }; -} - -/** - * Ranges between all uint32 values - */ -const uint32 = () => ({ lower: UInt32.from(0), upper: UInt32.MAXINT() }); - -/** - * Ranges between all uint64 values - */ -const uint64 = () => ({ lower: UInt64.from(0), upper: UInt64.MAXINT() }); - -type AccountPrecondition = Preconditions['account']; -const AccountPrecondition = { - ignoreAll(): AccountPrecondition { - let appState: Array> = []; - for (let i = 0; i < ZkappStateLength; ++i) { - appState.push(ignore(Field(0))); - } - return { - balance: ignore(uint64()), - nonce: ignore(uint32()), - receiptChainHash: ignore(Field(0)), - delegate: ignore(PublicKey.empty()), - state: appState, - actionState: ignore(Actions.emptyActionState()), - provedState: ignore(Bool(false)), - isNew: ignore(Bool(false)), - }; - }, - nonce(nonce: UInt32): AccountPrecondition { - let p = AccountPrecondition.ignoreAll(); - AccountUpdate.assertEquals(p.nonce, nonce); - return p; - }, -}; - -type GlobalSlotPrecondition = Preconditions['validWhile']; -const GlobalSlotPrecondition = { - ignoreAll(): GlobalSlotPrecondition { - return ignore(uint32()); - }, -}; - -const Preconditions = { - ignoreAll(): Preconditions { - return { - account: AccountPrecondition.ignoreAll(), - network: NetworkPrecondition.ignoreAll(), - validWhile: GlobalSlotPrecondition.ignoreAll(), - }; - }, -}; - type Control = Types.AccountUpdate['authorization']; type LazyNone = { kind: 'lazy-none'; @@ -666,9 +571,9 @@ class AccountUpdate implements Types.AccountUpdate { authorization: Control; lazyAuthorization: LazySignature | LazyProof | LazyNone | undefined = undefined; - account: Precondition.Account; - network: Precondition.Network; - currentSlot: Precondition.CurrentSlot; + account: Account; + network: Network; + currentSlot: CurrentSlot; children: { callsType: | { type: 'None' } @@ -691,10 +596,7 @@ class AccountUpdate implements Types.AccountUpdate { this.id = Math.random(); this.body = body; this.authorization = authorization; - let { account, network, currentSlot } = Precondition.preconditions( - this, - isSelf - ); + let { account, network, currentSlot } = preconditions(this, isSelf); this.account = account; this.network = network; this.currentSlot = currentSlot; @@ -733,7 +635,7 @@ class AccountUpdate implements Types.AccountUpdate { accountLike: PublicKey | AccountUpdate | SmartContract, label: string ) { - if (accountLike instanceof SmartContract) { + if (isSmartContract(accountLike)) { accountLike = accountLike.self; } if (accountLike instanceof AccountUpdate) { @@ -845,7 +747,7 @@ class AccountUpdate implements Types.AccountUpdate { if (to instanceof AccountUpdate) { receiver = to; receiver.body.tokenId.assertEquals(this.body.tokenId); - } else if (to instanceof SmartContract) { + } else if (isSmartContract(to)) { receiver = to.self; receiver.body.tokenId.assertEquals(this.body.tokenId); } else { @@ -1049,13 +951,11 @@ class AccountUpdate implements Types.AccountUpdate { let publicKey = update.body.publicKey; let tokenId = update instanceof AccountUpdate ? update.body.tokenId : TokenId.default; - let nonce = Number( - Precondition.getAccountPreconditions(update.body).nonce.toString() - ); + let nonce = Number(getAccountPreconditions(update.body).nonce.toString()); // if the fee payer is the same account update as this one, we have to start // the nonce predicate at one higher, bc the fee payer already increases its // nonce - let isFeePayer = Mina.currentTransaction()?.sender?.equals(publicKey); + let isFeePayer = currentTransaction()?.sender?.equals(publicKey); let isSameAsFeePayer = !!isFeePayer ?.and(tokenId.equals(TokenId.default)) .toBoolean(); @@ -1063,7 +963,7 @@ class AccountUpdate implements Types.AccountUpdate { // now, we check how often this account update already updated its nonce in // this tx, and increase nonce from `getAccount` by that amount CallForest.forEachPredecessor( - Mina.currentTransaction.get().accountUpdates, + currentTransaction.get().accountUpdates, update as AccountUpdate, (otherUpdate) => { let shouldIncreaseNonce = otherUpdate.publicKey @@ -1180,7 +1080,7 @@ class AccountUpdate implements Types.AccountUpdate { self.label || 'Unlabeled' } > AccountUpdate.create()`; } else { - Mina.currentTransaction()?.accountUpdates.push(accountUpdate); + currentTransaction()?.accountUpdates.push(accountUpdate); accountUpdate.label = `Mina.transaction > AccountUpdate.create()`; } return accountUpdate; @@ -1199,8 +1099,8 @@ class AccountUpdate implements Types.AccountUpdate { if (selfUpdate === accountUpdate) return; insideContract.this.self.approve(accountUpdate); } else { - if (!Mina.currentTransaction.has()) return; - let updates = Mina.currentTransaction.get().accountUpdates; + if (!currentTransaction.has()) return; + let updates = currentTransaction.get().accountUpdates; if (!updates.find((update) => update.id === accountUpdate.id)) { updates.push(accountUpdate); } @@ -1212,7 +1112,7 @@ class AccountUpdate implements Types.AccountUpdate { static unlink(accountUpdate: AccountUpdate) { let siblings = accountUpdate.parent?.children.accountUpdates ?? - Mina.currentTransaction()?.accountUpdates; + currentTransaction()?.accountUpdates; if (siblings === undefined) return; let i = siblings?.findIndex((update) => update.id === accountUpdate.id); if (i !== undefined && i !== -1) { @@ -1290,7 +1190,7 @@ class AccountUpdate implements Types.AccountUpdate { ) { let accountUpdate = AccountUpdate.createSigned(feePayer as PrivateKey); accountUpdate.label = 'AccountUpdate.fundNewAccount()'; - let fee = Mina.accountCreationFee(); + let fee = activeInstance.getNetworkConstants().accountCreationFee; numberOfAccounts ??= 1; if (typeof numberOfAccounts === 'number') fee = fee.mul(numberOfAccounts); else fee = fee.add(UInt64.from(numberOfAccounts.initialBalance ?? 0)); @@ -1866,57 +1766,6 @@ const Authorization = { accountUpdate.authorization = {}; accountUpdate.lazyAuthorization = { ...signature, kind: 'lazy-signature' }; }, - setProofAuthorizationKind( - { body, id }: AccountUpdate, - priorAccountUpdates?: AccountUpdate[] - ) { - body.authorizationKind.isSigned = Bool(false); - body.authorizationKind.isProved = Bool(true); - let hash = Provable.witness(Field, () => { - let proverData = zkAppProver.getData(); - let isProver = proverData !== undefined; - assert( - isProver || priorAccountUpdates !== undefined, - 'Called `setProofAuthorizationKind()` outside the prover without passing in `priorAccountUpdates`.' - ); - let myAccountUpdateId = isProver ? proverData.accountUpdate.id : id; - priorAccountUpdates ??= proverData.transaction.accountUpdates; - priorAccountUpdates = priorAccountUpdates.filter( - (a) => a.id !== myAccountUpdateId - ); - let priorAccountUpdatesFlat = CallForest.toFlatList( - priorAccountUpdates, - false - ); - let accountUpdate = [...priorAccountUpdatesFlat] - .reverse() - .find((body_) => - body_.update.verificationKey.isSome - .and(body_.tokenId.equals(body.tokenId)) - .and(body_.publicKey.equals(body.publicKey)) - .toBoolean() - ); - if (accountUpdate !== undefined) { - return accountUpdate.body.update.verificationKey.value.hash; - } - try { - let account = Mina.getAccount(body.publicKey, body.tokenId); - return account.zkapp?.verificationKey?.hash ?? Field(0); - } catch { - return Field(0); - } - }); - body.authorizationKind.verificationKeyHash = hash; - }, - setLazyProof( - accountUpdate: AccountUpdate, - proof: Omit, - priorAccountUpdates: AccountUpdate[] - ) { - Authorization.setProofAuthorizationKind(accountUpdate, priorAccountUpdates); - accountUpdate.authorization = {}; - accountUpdate.lazyAuthorization = { ...proof, kind: 'lazy-proof' }; - }, setLazyNone(accountUpdate: AccountUpdate) { accountUpdate.body.authorizationKind.isSigned = Bool(false); accountUpdate.body.authorizationKind.isProved = Bool(false); diff --git a/src/lib/mina.ts b/src/lib/mina.ts index e3b5823a9c..579d8defa0 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -20,7 +20,6 @@ import * as Fetch from './fetch.js'; import { assertPreconditionInvariants, NetworkValue } from './precondition.js'; import { cloneCircuitValue, toConstant } from './circuit_value.js'; import { Empty, Proof, verify } from './proof_system.js'; -import { Context } from './global-context.js'; import { SmartContract } from './zkapp.js'; import { invalidTransactionError } from './mina/errors.js'; import { Types, TypesBigint } from '../bindings/mina-transaction/types.js'; @@ -33,6 +32,15 @@ import { transactionCommitments, verifyAccountUpdateSignature, } from '../mina-signer/src/sign-zkapp-command.js'; +import { FetchMode, currentTransaction } from './mina/transaction-context.js'; +import { + activeInstance, + setActiveInstance, + Mina, + FeePayerSpec, + DeprecatedFeePayerSpec, + ActionStates, +} from './mina/mina-instance.js'; export { createTransaction, @@ -40,7 +48,6 @@ export { Network, LocalBlockchain, currentTransaction, - CurrentTransaction, Transaction, TransactionId, activeInstance, @@ -115,54 +122,6 @@ const Transaction = { }, }; -type FetchMode = 'fetch' | 'cached' | 'test'; -type CurrentTransaction = { - sender?: PublicKey; - accountUpdates: AccountUpdate[]; - fetchMode: FetchMode; - isFinalRunOutsideCircuit: boolean; - numberOfRuns: 0 | 1 | undefined; -}; - -let currentTransaction = Context.create(); - -/** - * Allows you to specify information about the fee payer account and the transaction. - */ -type FeePayerSpec = - | PublicKey - | { - sender: PublicKey; - fee?: number | string | UInt64; - memo?: string; - nonce?: number; - } - | undefined; - -type DeprecatedFeePayerSpec = - | PublicKey - | PrivateKey - | (( - | { - feePayerKey: PrivateKey; - sender?: PublicKey; - } - | { - feePayerKey?: PrivateKey; - sender: PublicKey; - } - ) & { - fee?: number | string | UInt64; - memo?: string; - nonce?: number; - }) - | undefined; - -type ActionStates = { - fromActionState?: Field; - endActionState?: Field; -}; - function reportGetAccountError(publicKey: string, tokenId: string) { if (tokenId === TokenId.toBase58(TokenId.default)) { return `getAccount: Could not find account for public key ${publicKey}`; @@ -331,43 +290,6 @@ function newTransaction(transaction: ZkappCommand, proofsEnabled?: boolean) { return self; } -interface Mina { - transaction( - sender: DeprecatedFeePayerSpec, - f: () => void - ): Promise; - currentSlot(): UInt32; - hasAccount(publicKey: PublicKey, tokenId?: Field): boolean; - getAccount(publicKey: PublicKey, tokenId?: Field): Account; - getNetworkState(): NetworkValue; - getNetworkConstants(): { - genesisTimestamp: UInt64; - /** - * Duration of 1 slot in millisecondw - */ - slotTime: UInt64; - accountCreationFee: UInt64; - }; - accountCreationFee(): UInt64; - sendTransaction(transaction: Transaction): Promise; - fetchEvents: ( - publicKey: PublicKey, - tokenId?: Field, - filterOptions?: Fetch.EventActionFilterOptions - ) => ReturnType; - fetchActions: ( - publicKey: PublicKey, - actionStates?: ActionStates, - tokenId?: Field - ) => ReturnType; - getActions: ( - publicKey: PublicKey, - actionStates?: ActionStates, - tokenId?: Field - ) => { hash: string; actions: string[][] }[]; - proofsEnabled: boolean; -} - const defaultAccountCreationFee = 1_000_000_000; /** @@ -993,92 +915,6 @@ function BerkeleyQANet(graphqlEndpoint: string) { return Network(graphqlEndpoint); } -let activeInstance: Mina = { - accountCreationFee: () => UInt64.from(defaultAccountCreationFee), - getNetworkConstants() { - throw new Error('must call Mina.setActiveInstance first'); - }, - currentSlot: () => { - throw new Error('must call Mina.setActiveInstance first'); - }, - hasAccount(publicKey: PublicKey, tokenId: Field = TokenId.default) { - if ( - !currentTransaction.has() || - currentTransaction.get().fetchMode === 'cached' - ) { - return !!Fetch.getCachedAccount( - publicKey, - tokenId, - Fetch.networkConfig.minaEndpoint - ); - } - return false; - }, - getAccount(publicKey: PublicKey, tokenId: Field = TokenId.default) { - if (currentTransaction()?.fetchMode === 'test') { - Fetch.markAccountToBeFetched( - publicKey, - tokenId, - Fetch.networkConfig.minaEndpoint - ); - return dummyAccount(publicKey); - } - if ( - !currentTransaction.has() || - currentTransaction.get().fetchMode === 'cached' - ) { - let account = Fetch.getCachedAccount( - publicKey, - tokenId, - Fetch.networkConfig.minaEndpoint - ); - if (account === undefined) - throw Error( - `${reportGetAccountError( - publicKey.toBase58(), - TokenId.toBase58(tokenId) - )}\n\nEither call Mina.setActiveInstance first or explicitly add the account with addCachedAccount` - ); - return account; - } - throw new Error('must call Mina.setActiveInstance first'); - }, - getNetworkState() { - throw new Error('must call Mina.setActiveInstance first'); - }, - sendTransaction() { - throw new Error('must call Mina.setActiveInstance first'); - }, - async transaction(sender: DeprecatedFeePayerSpec, f: () => void) { - return createTransaction(sender, f, 0); - }, - fetchEvents(_publicKey: PublicKey, _tokenId: Field = TokenId.default) { - throw Error('must call Mina.setActiveInstance first'); - }, - fetchActions( - _publicKey: PublicKey, - _actionStates?: ActionStates, - _tokenId: Field = TokenId.default - ) { - throw Error('must call Mina.setActiveInstance first'); - }, - getActions( - _publicKey: PublicKey, - _actionStates?: ActionStates, - _tokenId: Field = TokenId.default - ) { - throw Error('must call Mina.setActiveInstance first'); - }, - proofsEnabled: true, -}; - -/** - * Set the currently used Mina instance. - */ -function setActiveInstance(m: Mina) { - activeInstance = m; -} - /** * Construct a smart contract transaction. Within the callback passed to this function, * you can call into the methods of smart contracts. diff --git a/src/lib/mina/mina-instance.ts b/src/lib/mina/mina-instance.ts new file mode 100644 index 0000000000..a64f82ec01 --- /dev/null +++ b/src/lib/mina/mina-instance.ts @@ -0,0 +1,123 @@ +/** + * This module holds the global Mina instance and its interface. + */ +import type { Field } from '../field.js'; +import { UInt64, UInt32 } from '../int.js'; +import type { PublicKey, PrivateKey } from '../signature.js'; +import type { Transaction, TransactionId } from '../mina.js'; +import type { Account } from './account.js'; +import type { NetworkValue } from '../precondition.js'; +import type * as Fetch from '../fetch.js'; + +export { + Mina, + FeePayerSpec, + DeprecatedFeePayerSpec, + ActionStates, + activeInstance, + setActiveInstance, + ZkappStateLength, +}; + +const defaultAccountCreationFee = 1_000_000_000; +const ZkappStateLength = 8; + +/** + * Allows you to specify information about the fee payer account and the transaction. + */ +type FeePayerSpec = + | PublicKey + | { + sender: PublicKey; + fee?: number | string | UInt64; + memo?: string; + nonce?: number; + } + | undefined; + +type DeprecatedFeePayerSpec = + | PublicKey + | PrivateKey + | (( + | { + feePayerKey: PrivateKey; + sender?: PublicKey; + } + | { + feePayerKey?: PrivateKey; + sender: PublicKey; + } + ) & { + fee?: number | string | UInt64; + memo?: string; + nonce?: number; + }) + | undefined; + +type ActionStates = { + fromActionState?: Field; + endActionState?: Field; +}; + +interface Mina { + transaction( + sender: DeprecatedFeePayerSpec, + f: () => void + ): Promise; + currentSlot(): UInt32; + hasAccount(publicKey: PublicKey, tokenId?: Field): boolean; + getAccount(publicKey: PublicKey, tokenId?: Field): Account; + getNetworkState(): NetworkValue; + getNetworkConstants(): { + genesisTimestamp: UInt64; + /** + * Duration of 1 slot in millisecondw + */ + slotTime: UInt64; + accountCreationFee: UInt64; + }; + accountCreationFee(): UInt64; + sendTransaction(transaction: Transaction): Promise; + fetchEvents: ( + publicKey: PublicKey, + tokenId?: Field, + filterOptions?: Fetch.EventActionFilterOptions + ) => ReturnType; + fetchActions: ( + publicKey: PublicKey, + actionStates?: ActionStates, + tokenId?: Field + ) => ReturnType; + getActions: ( + publicKey: PublicKey, + actionStates?: ActionStates, + tokenId?: Field + ) => { hash: string; actions: string[][] }[]; + proofsEnabled: boolean; +} + +let activeInstance: Mina = { + accountCreationFee: () => UInt64.from(defaultAccountCreationFee), + getNetworkConstants: noActiveInstance, + currentSlot: noActiveInstance, + hasAccount: noActiveInstance, + getAccount: noActiveInstance, + getNetworkState: noActiveInstance, + sendTransaction: noActiveInstance, + transaction: noActiveInstance, + fetchEvents: noActiveInstance, + fetchActions: noActiveInstance, + getActions: noActiveInstance, + proofsEnabled: true, +}; + +/** + * Set the currently used Mina instance. + */ +function setActiveInstance(m: Mina) { + activeInstance = m; +} + +function noActiveInstance(): never { + throw Error('Must call Mina.setActiveInstance first'); +} diff --git a/src/lib/mina/smart-contract-base.ts b/src/lib/mina/smart-contract-base.ts new file mode 100644 index 0000000000..37d54379e0 --- /dev/null +++ b/src/lib/mina/smart-contract-base.ts @@ -0,0 +1,13 @@ +/** + * This file exists to avoid an import cycle between code that just needs access + * to a smart contract base class, and the code that implements `SmartContract`. + */ +import type { SmartContract } from '../zkapp.js'; + +export { isSmartContract, SmartContractBase }; + +class SmartContractBase {} + +function isSmartContract(object: unknown): object is SmartContract { + return object instanceof SmartContractBase; +} diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 63016c9130..e5bed5328d 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -16,7 +16,7 @@ import { export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; -export { HashedAccountUpdate, CallForestUnderConstruction }; +export { CallForestUnderConstruction }; class HashedAccountUpdate extends Hashed.create( AccountUpdate, diff --git a/src/lib/mina/transaction-context.ts b/src/lib/mina/transaction-context.ts new file mode 100644 index 0000000000..a3bb040b1c --- /dev/null +++ b/src/lib/mina/transaction-context.ts @@ -0,0 +1,16 @@ +import type { AccountUpdate } from '../account_update.js'; +import type { PublicKey } from '../signature.js'; +import { Context } from '../global-context.js'; + +export { currentTransaction, CurrentTransaction, FetchMode }; + +type FetchMode = 'fetch' | 'cached' | 'test'; +type CurrentTransaction = { + sender?: PublicKey; + accountUpdates: AccountUpdate[]; + fetchMode: FetchMode; + isFinalRunOutsideCircuit: boolean; + numberOfRuns: 0 | 1 | undefined; +}; + +let currentTransaction = Context.create(); diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index 25b10b9031..eba86036bf 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -1,14 +1,19 @@ import { Bool, Field } from './core.js'; -import { circuitValueEquals } from './circuit_value.js'; +import { circuitValueEquals, cloneCircuitValue } from './circuit_value.js'; import { Provable } from './provable.js'; -import * as Mina from './mina.js'; -import { Actions, AccountUpdate, Preconditions } from './account_update.js'; +import { activeInstance as Mina } from './mina/mina-instance.js'; +import type { AccountUpdate } from './account_update.js'; import { Int64, UInt32, UInt64 } from './int.js'; import { Layout } from '../bindings/mina-transaction/gen/transaction.js'; import { jsLayout } from '../bindings/mina-transaction/gen/js-layout.js'; import { emptyReceiptChainHash, TokenSymbol } from './hash.js'; import { PublicKey } from './signature.js'; -import { ZkappUri } from '../bindings/mina-transaction/transaction-leaves.js'; +import { + Actions, + ZkappUri, +} from '../bindings/mina-transaction/transaction-leaves.js'; +import type { Types } from '../bindings/mina-transaction/types.js'; +import { ZkappStateLength } from './mina/mina-instance.js'; export { preconditions, @@ -20,6 +25,145 @@ export { AccountValue, NetworkValue, getAccountPreconditions, + Preconditions, + OrIgnore, + ClosedInterval, +}; + +type AccountUpdateBody = Types.AccountUpdate['body']; + +/** + * Preconditions for the network and accounts + */ +type Preconditions = AccountUpdateBody['preconditions']; + +/** + * Either check a value or ignore it. + * + * Used within [[ AccountPredicate ]]s and [[ ProtocolStatePredicate ]]s. + */ +type OrIgnore = { isSome: Bool; value: T }; + +/** + * An interval representing all the values between `lower` and `upper` inclusive + * of both the `lower` and `upper` values. + * + * @typeParam A something with an ordering where one can quantify a lower and + * upper bound. + */ +type ClosedInterval = { lower: T; upper: T }; + +type NetworkPrecondition = Preconditions['network']; +let NetworkPrecondition = { + ignoreAll(): NetworkPrecondition { + let stakingEpochData = { + ledger: { hash: ignore(Field(0)), totalCurrency: ignore(uint64()) }, + seed: ignore(Field(0)), + startCheckpoint: ignore(Field(0)), + lockCheckpoint: ignore(Field(0)), + epochLength: ignore(uint32()), + }; + let nextEpochData = cloneCircuitValue(stakingEpochData); + return { + snarkedLedgerHash: ignore(Field(0)), + blockchainLength: ignore(uint32()), + minWindowDensity: ignore(uint32()), + totalCurrency: ignore(uint64()), + globalSlotSinceGenesis: ignore(uint32()), + stakingEpochData, + nextEpochData, + }; + }, +}; + +/** + * Ignores a `dummy` + * + * @param dummy The value to ignore + * @returns Always an ignored value regardless of the input. + */ +function ignore(dummy: T): OrIgnore { + return { isSome: Bool(false), value: dummy }; +} + +/** + * Ranges between all uint32 values + */ +const uint32 = () => ({ lower: UInt32.from(0), upper: UInt32.MAXINT() }); + +/** + * Ranges between all uint64 values + */ +const uint64 = () => ({ lower: UInt64.from(0), upper: UInt64.MAXINT() }); + +/** + * Fix a property to a certain value. + * + * @param property The property to constrain + * @param value The value it is fixed to + * + * Example: To fix the account nonce of a SmartContract to 0, you can use + * + * ```ts + * \@method onlyRunsWhenNonceIsZero() { + * AccountUpdate.assertEquals(this.self.body.preconditions.account.nonce, UInt32.zero); + * // ... + * } + * ``` + */ +function assertEquals( + property: OrIgnore | T>, + value: T +) { + property.isSome = Bool(true); + if ('lower' in property.value && 'upper' in property.value) { + property.value.lower = value; + property.value.upper = value; + } else { + property.value = value; + } +} + +type AccountPrecondition = Preconditions['account']; +const AccountPrecondition = { + ignoreAll(): AccountPrecondition { + let appState: Array> = []; + for (let i = 0; i < ZkappStateLength; ++i) { + appState.push(ignore(Field(0))); + } + return { + balance: ignore(uint64()), + nonce: ignore(uint32()), + receiptChainHash: ignore(Field(0)), + delegate: ignore(PublicKey.empty()), + state: appState, + actionState: ignore(Actions.emptyActionState()), + provedState: ignore(Bool(false)), + isNew: ignore(Bool(false)), + }; + }, + nonce(nonce: UInt32): AccountPrecondition { + let p = AccountPrecondition.ignoreAll(); + assertEquals(p.nonce, nonce); + return p; + }, +}; + +type GlobalSlotPrecondition = Preconditions['validWhile']; +const GlobalSlotPrecondition = { + ignoreAll(): GlobalSlotPrecondition { + return ignore(uint32()); + }, +}; + +const Preconditions = { + ignoreAll(): Preconditions { + return { + account: AccountPrecondition.ignoreAll(), + network: NetworkPrecondition.ignoreAll(), + validWhile: GlobalSlotPrecondition.ignoreAll(), + }; + }, }; function preconditions(accountUpdate: AccountUpdate, isSelf: boolean) { @@ -57,8 +201,7 @@ function Network(accountUpdate: AccountUpdate): Network { return this.getAndRequireEquals(); }, requireEquals(value: UInt64) { - let { genesisTimestamp, slotTime } = - Mina.activeInstance.getNetworkConstants(); + let { genesisTimestamp, slotTime } = Mina.getNetworkConstants(); let slot = timestampToGlobalSlot( value, `Timestamp precondition unsatisfied: the timestamp can only equal numbers of the form ${genesisTimestamp} + k*${slotTime},\n` + @@ -318,13 +461,11 @@ function getVariable( } function globalSlotToTimestamp(slot: UInt32) { - let { genesisTimestamp, slotTime } = - Mina.activeInstance.getNetworkConstants(); + let { genesisTimestamp, slotTime } = Mina.getNetworkConstants(); return UInt64.from(slot).mul(slotTime).add(genesisTimestamp); } function timestampToGlobalSlot(timestamp: UInt64, message: string) { - let { genesisTimestamp, slotTime } = - Mina.activeInstance.getNetworkConstants(); + let { genesisTimestamp, slotTime } = Mina.getNetworkConstants(); let { quotient: slot, rest } = timestamp .sub(genesisTimestamp) .divMod(slotTime); @@ -339,8 +480,7 @@ function timestampToGlobalSlotRange( // we need `slotLower <= current slot <= slotUpper` to imply `tsLower <= current timestamp <= tsUpper` // so we have to make the range smaller -- round up `tsLower` and round down `tsUpper` // also, we should clamp to the UInt32 max range [0, 2**32-1] - let { genesisTimestamp, slotTime } = - Mina.activeInstance.getNetworkConstants(); + let { genesisTimestamp, slotTime } = Mina.getNetworkConstants(); let tsLowerInt = Int64.from(tsLower) .sub(genesisTimestamp) .add(slotTime) @@ -452,7 +592,6 @@ const preconditionContexts = new WeakMap(); // exported types -type NetworkPrecondition = Preconditions['network']; type NetworkValue = PreconditionBaseTypes; type RawNetwork = PreconditionClassType; type Network = RawNetwork & { @@ -461,9 +600,9 @@ type Network = RawNetwork & { // TODO: should we add account.state? // then can just use circuitArray(Field, 8) as the type -type AccountPrecondition = Omit; -type AccountValue = PreconditionBaseTypes; -type Account = PreconditionClassType & Update; +type AccountPreconditionNoState = Omit; +type AccountValue = PreconditionBaseTypes; +type Account = PreconditionClassType & Update; type CurrentSlotPrecondition = Preconditions['validWhile']; type CurrentSlot = { @@ -552,7 +691,7 @@ type PreconditionFlatEntry = T extends RangeCondition type FlatPreconditionValue = { [S in PreconditionFlatEntry as `network.${S[0]}`]: S[2]; } & { - [S in PreconditionFlatEntry as `account.${S[0]}`]: S[2]; + [S in PreconditionFlatEntry as `account.${S[0]}`]: S[2]; } & { validWhile: PreconditionFlatEntry[2] }; type LongKey = keyof FlatPreconditionValue; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 6140eca774..53be3bf380 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -16,6 +16,8 @@ import { ZkappPublicInput, ZkappStateLength, SmartContractContext, + LazyProof, + CallForest, } from './account_update.js'; import { cloneCircuitValue, @@ -58,6 +60,9 @@ import { import { Cache } from './proof-system/cache.js'; import { assert } from './gadgets/common.js'; import { CallForestUnderConstruction } from './mina/token/call-forest.js'; +import { SmartContractBase } from './mina/smart-contract-base.js'; + +console.log('importing zkapp'); // external API export { @@ -212,7 +217,7 @@ function wrapMethod( blindingValue ); accountUpdate.body.callData = Poseidon.hash(callDataFields); - Authorization.setProofAuthorizationKind(accountUpdate); + ProofAuthorization.setKind(accountUpdate); // TODO: currently commented out, but could come back in some form when we add caller to the public input // // compute `caller` field from `isDelegateCall` and a context determined by the transaction @@ -334,7 +339,7 @@ function wrapMethod( accountUpdate.body.callData = Poseidon.hash(callDataFields); if (!Authorization.hasAny(accountUpdate)) { - Authorization.setLazyProof( + ProofAuthorization.setLazyProof( accountUpdate, { methodName: methodIntf.methodName, @@ -429,7 +434,7 @@ function wrapMethod( accountUpdate.body.callData = hashConstant(callDataFields); if (!Authorization.hasAny(accountUpdate)) { - Authorization.setLazyProof( + ProofAuthorization.setLazyProof( accountUpdate, { methodName: methodIntf.methodName, @@ -601,7 +606,7 @@ class Callback extends GenericArgument { * ``` * */ -class SmartContract { +class SmartContract extends SmartContractBase { address: PublicKey; tokenId: Field; @@ -639,6 +644,7 @@ class SmartContract { } constructor(address: PublicKey, tokenId?: Field) { + super(); this.address = address; this.tokenId = tokenId ?? TokenId.default; Object.defineProperty(this, 'reducer', { @@ -1563,6 +1569,57 @@ const Reducer: (< { get: Actions.emptyActionState } ) as any; +const ProofAuthorization = { + setKind({ body, id }: AccountUpdate, priorAccountUpdates?: AccountUpdate[]) { + body.authorizationKind.isSigned = Bool(false); + body.authorizationKind.isProved = Bool(true); + let hash = Provable.witness(Field, () => { + let proverData = zkAppProver.getData(); + let isProver = proverData !== undefined; + assert( + isProver || priorAccountUpdates !== undefined, + 'Called `setProofAuthorizationKind()` outside the prover without passing in `priorAccountUpdates`.' + ); + let myAccountUpdateId = isProver ? proverData.accountUpdate.id : id; + priorAccountUpdates ??= proverData.transaction.accountUpdates; + priorAccountUpdates = priorAccountUpdates.filter( + (a) => a.id !== myAccountUpdateId + ); + let priorAccountUpdatesFlat = CallForest.toFlatList( + priorAccountUpdates, + false + ); + let accountUpdate = [...priorAccountUpdatesFlat] + .reverse() + .find((body_) => + body_.update.verificationKey.isSome + .and(body_.tokenId.equals(body.tokenId)) + .and(body_.publicKey.equals(body.publicKey)) + .toBoolean() + ); + if (accountUpdate !== undefined) { + return accountUpdate.body.update.verificationKey.value.hash; + } + try { + let account = Mina.getAccount(body.publicKey, body.tokenId); + return account.zkapp?.verificationKey?.hash ?? Field(0); + } catch { + return Field(0); + } + }); + body.authorizationKind.verificationKeyHash = hash; + }, + setLazyProof( + accountUpdate: AccountUpdate, + proof: Omit, + priorAccountUpdates: AccountUpdate[] + ) { + this.setKind(accountUpdate, priorAccountUpdates); + accountUpdate.authorization = {}; + accountUpdate.lazyAuthorization = { ...proof, kind: 'lazy-proof' }; + }, +}; + /** * this is useful to debug a very common error: when the consistency check between * -) the account update that went into the public input, and From fd6e36d5a69c5e3fc5e72c7d3a49dd226dc6ba09 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 16:04:02 +0100 Subject: [PATCH 245/524] fixup --- src/lib/mina.ts | 9 +++++++++ src/lib/zkapp.ts | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 579d8defa0..06095e6610 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -72,6 +72,15 @@ export { // for internal testing only filterGroups, }; + +// patch active instance so that we can still create basic transactions without giving Mina network details +setActiveInstance({ + ...activeInstance, + async transaction(sender: DeprecatedFeePayerSpec, f: () => void) { + return createTransaction(sender, f, 0); + }, +}); + interface TransactionId { isSuccess: boolean; wait(options?: { maxAttempts?: number; interval?: number }): Promise; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 53be3bf380..7b852f086b 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -62,8 +62,6 @@ import { assert } from './gadgets/common.js'; import { CallForestUnderConstruction } from './mina/token/call-forest.js'; import { SmartContractBase } from './mina/smart-contract-base.js'; -console.log('importing zkapp'); - // external API export { SmartContract, From d40a8defc8e667c8f7d56197909f4d215e5fa2c8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 16:09:01 +0100 Subject: [PATCH 246/524] fix proof systems unit test --- src/lib/proof_system.unit-test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/proof_system.unit-test.ts b/src/lib/proof_system.unit-test.ts index 3aab1dc9a5..5da3390e71 100644 --- a/src/lib/proof_system.unit-test.ts +++ b/src/lib/proof_system.unit-test.ts @@ -124,7 +124,6 @@ expect(emptyMethodsMetadata.run).toEqual( expect.objectContaining({ rows: 0, digest: '4f5ddea76d29cfcfd8c595f14e31f21b', - result: undefined, gates: [], publicInputSize: 0, }) From 8f5acbd946caa0da9ba34022a07a3b992db97070 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 31 Jan 2024 10:03:01 +0100 Subject: [PATCH 247/524] minor --- src/examples/zkapps/dex/dex.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index e10aa44668..073ce21de0 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -55,15 +55,13 @@ function createDex({ let tokenY = new TokenContract(this.tokenY); // get balances of X and Y token - // TODO: this creates extra account updates. we need to reuse these by passing them to or returning them from transfer() - // but for that, we need the @method argument generalization let dexXUpdate = AccountUpdate.create(this.address, tokenX.token.id); let dexXBalance = dexXUpdate.account.balance.getAndRequireEquals(); let dexYUpdate = AccountUpdate.create(this.address, tokenY.token.id); let dexYBalance = dexYUpdate.account.balance.getAndRequireEquals(); - // // assert dy === [dx * y/x], or x === 0 + // assert dy === [dx * y/x], or x === 0 let isXZero = dexXBalance.equals(UInt64.zero); let xSafe = Provable.if(isXZero, UInt64.one, dexXBalance); let isDyCorrect = dy.equals(dx.mul(dexYBalance).div(xSafe)); @@ -72,7 +70,7 @@ function createDex({ tokenX.transfer(user, dexXUpdate, dx); tokenY.transfer(user, dexYUpdate, dy); - // calculate liquidity token output simply as dl = dx + dx + // calculate liquidity token output simply as dl = dx + dy // => maintains ratio x/l, y/l let dl = dy.add(dx); let userUpdate = this.token.mint({ address: user, amount: dl }); From c6f32187662e9ddf494214c11c9091589908b9d0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 31 Jan 2024 10:07:12 +0100 Subject: [PATCH 248/524] remove unusedd code which just adds confusion --- src/lib/account_update.ts | 70 --------------------------------------- src/lib/zkapp.ts | 11 ------ 2 files changed, 81 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 1d4d25f239..b2aa86b689 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1484,12 +1484,6 @@ type AccountUpdatesLayout = | 'NoDelegation' | AccountUpdatesLayout[]; -type WithCallers = { - accountUpdate: AccountUpdate; - caller: Field; - children: WithCallers[]; -}; - const CallForest = { // similar to Mina_base.ZkappCommand.Call_forest.to_account_updates_list // takes a list of accountUpdates, which each can have children, so they form a "forest" (list of trees) @@ -1560,70 +1554,6 @@ const CallForest = { return stackHash; }, - // Mina_base.Zkapp_command.Call_forest.add_callers - // TODO: currently unused, but could come back when we add caller to the - // public input - addCallers( - updates: AccountUpdate[], - context: { self: Field; caller: Field } = { - self: TokenId.default, - caller: TokenId.default, - } - ): WithCallers[] { - let withCallers: WithCallers[] = []; - for (let update of updates) { - let { mayUseToken } = update.body; - let caller = Provable.if( - mayUseToken.parentsOwnToken, - context.self, - Provable.if( - mayUseToken.inheritFromParent, - context.caller, - TokenId.default - ) - ); - let self = TokenId.derive(update.body.publicKey, update.body.tokenId); - let childContext = { caller, self }; - withCallers.push({ - accountUpdate: update, - caller, - children: CallForest.addCallers( - update.children.accountUpdates, - childContext - ), - }); - } - return withCallers; - }, - /** - * Used in the prover to witness the context from which to compute its caller - * - * TODO: currently unused, but could come back when we add caller to the - * public input - */ - computeCallerContext(update: AccountUpdate) { - // compute the line of ancestors - let current = update; - let ancestors = []; - while (true) { - let parent = current.parent; - if (parent === undefined) break; - ancestors.unshift(parent); - current = parent; - } - let context = { self: TokenId.default, caller: TokenId.default }; - for (let update of ancestors) { - if (update.body.mayUseToken.parentsOwnToken.toBoolean()) { - context.caller = context.self; - } else if (!update.body.mayUseToken.inheritFromParent.toBoolean()) { - context.caller = TokenId.default; - } - context.self = TokenId.derive(update.body.publicKey, update.body.tokenId); - } - return context; - }, - callerContextType: provablePure({ self: Field, caller: Field }), - computeCallDepth(update: AccountUpdate) { for (let callDepth = 0; ; callDepth++) { if (update.parent === undefined) return callDepth; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 7b852f086b..01735b07ea 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -217,17 +217,6 @@ function wrapMethod( accountUpdate.body.callData = Poseidon.hash(callDataFields); ProofAuthorization.setKind(accountUpdate); - // TODO: currently commented out, but could come back in some form when we add caller to the public input - // // compute `caller` field from `isDelegateCall` and a context determined by the transaction - // let callerContext = Provable.witness( - // CallForest.callerContextType, - // () => { - // let { accountUpdate } = zkAppProver.getData(); - // return CallForest.computeCallerContext(accountUpdate); - // } - // ); - // CallForest.addCallers([accountUpdate], callerContext); - // connect the public input to the account update & child account updates we created if (DEBUG_PUBLIC_INPUT_CHECK) { Provable.asProver(() => { From a4dec71e5d1a009b723e472f8c6f90fdf645f04c Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 31 Jan 2024 10:18:29 +0100 Subject: [PATCH 249/524] move annoying debug code out of sight --- src/lib/zkapp.ts | 97 ++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 45 deletions(-) diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 01735b07ea..1efb6b45aa 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -217,51 +217,7 @@ function wrapMethod( accountUpdate.body.callData = Poseidon.hash(callDataFields); ProofAuthorization.setKind(accountUpdate); - // connect the public input to the account update & child account updates we created - if (DEBUG_PUBLIC_INPUT_CHECK) { - Provable.asProver(() => { - // TODO: print a nice diff string instead of the two objects - // something like `expect` or `json-diff`, but web-compatible - function diff(prover: any, input: any) { - delete prover.id; - delete prover.callDepth; - delete input.id; - delete input.callDepth; - if (JSON.stringify(prover) !== JSON.stringify(input)) { - console.log( - 'transaction:', - ZkappCommand.toPretty(transaction) - ); - console.log('index', index); - console.log('inconsistent account updates:'); - console.log('update created by the prover:'); - console.log(prover); - console.log('update created in transaction block:'); - console.log(input); - } - } - function diffRecursive( - prover: AccountUpdate, - input: AccountUpdate - ) { - diff(prover.toPretty(), input.toPretty()); - let nChildren = input.children.accountUpdates.length; - for (let i = 0; i < nChildren; i++) { - let inputChild = input.children.accountUpdates[i]; - let child = prover.children.accountUpdates[i]; - if (!inputChild || !child) return; - diffRecursive(child, inputChild); - } - } - - let { - accountUpdate: inputUpdate, - transaction, - index, - } = zkAppProver.getData(); - diffRecursive(accountUpdate, inputUpdate); - }); - } + debugPublicInput(accountUpdate); checkPublicInput(publicInput, accountUpdate); // check the self accountUpdate right after calling the method @@ -1618,3 +1574,54 @@ const ProofAuthorization = { * TODO find or write library that can print nice JS object diffs */ const DEBUG_PUBLIC_INPUT_CHECK = false; + +function debugPublicInput(accountUpdate: AccountUpdate) { + if (!DEBUG_PUBLIC_INPUT_CHECK) return; + + // connect the public input to the account update & child account updates we created + Provable.asProver(() => { + diffRecursive(accountUpdate, zkAppProver.getData()); + }); +} + +function diffRecursive( + prover: AccountUpdate, + inputData: { + transaction: ZkappCommand; + index: number; + accountUpdate: AccountUpdate; + } +) { + let { transaction, index, accountUpdate: input } = inputData; + diff(transaction, index, prover.toPretty(), input.toPretty()); + let nChildren = input.children.accountUpdates.length; + for (let i = 0; i < nChildren; i++) { + let inputChild = input.children.accountUpdates[i]; + let child = prover.children.accountUpdates[i]; + if (!inputChild || !child) return; + diffRecursive(child, { transaction, index, accountUpdate: inputChild }); + } +} + +// TODO: print a nice diff string instead of the two objects +// something like `expect` or `json-diff`, but web-compatible +function diff( + transaction: ZkappCommand, + index: number, + prover: any, + input: any +) { + delete prover.id; + delete prover.callDepth; + delete input.id; + delete input.callDepth; + if (JSON.stringify(prover) !== JSON.stringify(input)) { + console.log('transaction:', ZkappCommand.toPretty(transaction)); + console.log('index', index); + console.log('inconsistent account updates:'); + console.log('update created by the prover:'); + console.log(prover); + console.log('update created in transaction block:'); + console.log(input); + } +} From a3b2e97bd296bad4ccf3e71b97a5ac923a0d6b09 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 31 Jan 2024 12:05:05 +0100 Subject: [PATCH 250/524] move call forest code next to account update --- src/index.ts | 6 +- src/lib/account_update.ts | 193 ++++++++++++++++++-- src/lib/mina.ts | 9 +- src/lib/mina/token/call-forest.ts | 159 +--------------- src/lib/mina/token/call-forest.unit-test.ts | 12 +- src/lib/mina/token/token-contract.ts | 8 +- src/lib/zkapp.ts | 6 +- 7 files changed, 201 insertions(+), 192 deletions(-) diff --git a/src/index.ts b/src/index.ts index 94233cadda..5fe75fe101 100644 --- a/src/index.ts +++ b/src/index.ts @@ -71,12 +71,10 @@ export { AccountUpdate, Permissions, ZkappPublicInput, + CallForest, } from './lib/account_update.js'; -export { - CallForest, - CallForestIterator, -} from './lib/mina/token/call-forest.js'; +export { CallForestIterator } from './lib/mina/token/call-forest.js'; export { TokenContract } from './lib/mina/token/token-contract.js'; export type { TransactionStatus } from './lib/fetch.js'; diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index b2aa86b689..1a02a01fbc 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -3,6 +3,8 @@ import { FlexibleProvable, provable, provablePure, + Struct, + Unconstrained, } from './circuit_value.js'; import { memoizationContext, memoizeWitness, Provable } from './provable.js'; import { Field, Bool } from './core.js'; @@ -33,20 +35,31 @@ import { Actions, } from '../bindings/mina-transaction/transaction-leaves.js'; import { TokenId as Base58TokenId } from './base58-encodings.js'; -import { hashWithPrefix, packToFields } from './hash.js'; +import { + hashWithPrefix, + packToFields, + Poseidon, + ProvableHashable, +} from './hash.js'; import { mocks, prefixes } from '../bindings/crypto/constants.js'; import { Context } from './global-context.js'; import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; -import type { CallForestUnderConstruction } from './mina/token/call-forest.js'; import { currentTransaction } from './mina/transaction-context.js'; import { isSmartContract } from './mina/smart-contract-base.js'; import { activeInstance } from './mina/mina-instance.js'; +import { + genericHash, + MerkleList, + MerkleListBase, + withHashes, +} from './provable-types/merkle-list.js'; +import { Hashed } from './provable-types/packed.js'; // external API -export { AccountUpdate, Permissions, ZkappPublicInput }; +export { AccountUpdate, Permissions, ZkappPublicInput, CallForest }; // internal API export { smartContractContext, @@ -64,13 +77,16 @@ export { Actions, TokenId, Token, - CallForest, + CallForestHelpers, createChildAccountUpdate, AccountUpdatesLayout, zkAppProver, SmartContractContext, dummySignature, LazyProof, + CallTree, + CallForestUnderConstruction, + hashAccountUpdate, }; const ZkappStateLength = 8; @@ -962,7 +978,7 @@ class AccountUpdate implements Types.AccountUpdate { if (isSameAsFeePayer) nonce++; // now, we check how often this account update already updated its nonce in // this tx, and increase nonce from `getAccount` by that amount - CallForest.forEachPredecessor( + CallForestHelpers.forEachPredecessor( currentTransaction.get().accountUpdates, update as AccountUpdate, (otherUpdate) => { @@ -1009,7 +1025,7 @@ class AccountUpdate implements Types.AccountUpdate { toPublicInput(): ZkappPublicInput { let accountUpdate = this.hash(); - let calls = CallForest.hashChildren(this); + let calls = CallForestHelpers.hashChildren(this); return { accountUpdate, calls }; } @@ -1285,7 +1301,7 @@ class AccountUpdate implements Types.AccountUpdate { if (n === 0) { accountUpdate.children.callsType = { type: 'Equals', - value: CallForest.emptyHash(), + value: CallForestHelpers.emptyHash(), }; } } @@ -1484,7 +1500,148 @@ type AccountUpdatesLayout = | 'NoDelegation' | AccountUpdatesLayout[]; -const CallForest = { +// call forest stuff + +function hashAccountUpdate(update: AccountUpdate) { + return genericHash(AccountUpdate, prefixes.body, update); +} + +class HashedAccountUpdate extends Hashed.create( + AccountUpdate, + hashAccountUpdate +) {} + +type CallTree = { + accountUpdate: Hashed; + calls: MerkleListBase; +}; +const CallTree: ProvableHashable = Struct({ + accountUpdate: HashedAccountUpdate.provable, + calls: MerkleListBase(), +}); + +class CallForest extends MerkleList.create(CallTree, merkleListHash) { + static fromAccountUpdates(updates: AccountUpdate[]): CallForest { + let nodes = updates.map((update) => { + let accountUpdate = HashedAccountUpdate.hash(update); + let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); + return { accountUpdate, calls }; + }); + + return CallForest.from(nodes); + } +} + +// how to hash a forest + +function merkleListHash(forestHash: Field, tree: CallTree) { + return hashCons(forestHash, hashNode(tree)); +} +function hashNode(tree: CallTree) { + return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ + tree.accountUpdate.hash, + tree.calls.hash, + ]); +} +function hashCons(forestHash: Field, nodeHash: Field) { + return Poseidon.hashWithPrefix(prefixes.accountUpdateCons, [ + nodeHash, + forestHash, + ]); +} + +/** + * Structure for constructing a call forest from a circuit. + * + * The circuit can mutate account updates and change their array of children, so here we can't hash + * everything immediately. Instead, we maintain a structure consisting of either hashes or full account + * updates that can be hashed into a final call forest at the end. + */ +type CallForestUnderConstruction = HashOrValue< + { + accountUpdate: HashOrValue; + calls: CallForestUnderConstruction; + }[] +>; + +type HashOrValue = + | { useHash: true; hash: Field; value: T } + | { useHash: false; value: T }; + +const CallForestUnderConstruction = { + empty(): CallForestUnderConstruction { + return { useHash: false, value: [] }; + }, + + setHash(forest: CallForestUnderConstruction, hash: Field) { + Object.assign(forest, { useHash: true, hash }); + }, + + witnessHash(forest: CallForestUnderConstruction) { + let hash = Provable.witness(Field, () => { + let nodes = forest.value.map(toCallTree); + return withHashes(nodes, merkleListHash).hash; + }); + CallForestUnderConstruction.setHash(forest, hash); + }, + + push( + forest: CallForestUnderConstruction, + accountUpdate: HashOrValue, + calls?: CallForestUnderConstruction + ) { + forest.value.push({ + accountUpdate, + calls: calls ?? CallForestUnderConstruction.empty(), + }); + }, + + remove(forest: CallForestUnderConstruction, accountUpdate: AccountUpdate) { + // find account update by .id + let index = forest.value.findIndex( + (node) => node.accountUpdate.value.id === accountUpdate.id + ); + + // nothing to do if it's not there + if (index === -1) return; + + // remove it + forest.value.splice(index, 1); + }, + + finalize(forest: CallForestUnderConstruction): CallForest { + if (forest.useHash) { + let data = Unconstrained.witness(() => { + let nodes = forest.value.map(toCallTree); + return withHashes(nodes, merkleListHash).data; + }); + return new CallForest({ hash: forest.hash, data }); + } + + // not using the hash means we calculate it in-circuit + let nodes = forest.value.map(toCallTree); + return CallForest.from(nodes); + }, +}; + +function toCallTree(node: { + accountUpdate: HashOrValue; + calls: CallForestUnderConstruction; +}): CallTree { + let accountUpdate = node.accountUpdate.useHash + ? new HashedAccountUpdate( + node.accountUpdate.hash, + Unconstrained.from(node.accountUpdate.value) + ) + : HashedAccountUpdate.hash(node.accountUpdate.value); + + return { + accountUpdate, + calls: CallForestUnderConstruction.finalize(node.calls), + }; +} + +const CallForestHelpers = { // similar to Mina_base.ZkappCommand.Call_forest.to_account_updates_list // takes a list of accountUpdates, which each can have children, so they form a "forest" (list of trees) // returns a flattened list, with `accountUpdate.body.callDepth` specifying positions in the forest @@ -1501,7 +1658,7 @@ const CallForest = { let children = accountUpdate.children.accountUpdates; accountUpdates.push( accountUpdate, - ...CallForest.toFlatList(children, mutate, depth + 1) + ...CallForestHelpers.toFlatList(children, mutate, depth + 1) ); } return accountUpdates; @@ -1517,19 +1674,21 @@ const CallForest = { // the `calls` field of ZkappPublicInput hashChildren(update: AccountUpdate): Field { if (!Provable.inCheckedComputation()) { - return CallForest.hashChildrenBase(update); + return CallForestHelpers.hashChildrenBase(update); } let { callsType } = update.children; // compute hash outside the circuit if callsType is "Witness" // i.e., allowing accountUpdates with arbitrary children if (callsType.type === 'Witness') { - return Provable.witness(Field, () => CallForest.hashChildrenBase(update)); + return Provable.witness(Field, () => + CallForestHelpers.hashChildrenBase(update) + ); } if (callsType.type === 'WitnessEquals') { return callsType.value; } - let calls = CallForest.hashChildrenBase(update); + let calls = CallForestHelpers.hashChildrenBase(update); if (callsType.type === 'Equals') { calls.assertEquals(callsType.value); } @@ -1537,9 +1696,9 @@ const CallForest = { }, hashChildrenBase({ children }: AccountUpdate) { - let stackHash = CallForest.emptyHash(); + let stackHash = CallForestHelpers.emptyHash(); for (let accountUpdate of [...children.accountUpdates].reverse()) { - let calls = CallForest.hashChildren(accountUpdate); + let calls = CallForestHelpers.hashChildren(accountUpdate); let nodeHash = hashWithPrefix(prefixes.accountUpdateNode, [ accountUpdate.hash(), calls, @@ -1565,7 +1724,7 @@ const CallForest = { let newUpdates: AccountUpdate[] = []; for (let update of updates) { let newUpdate = map(update); - newUpdate.children.accountUpdates = CallForest.map( + newUpdate.children.accountUpdates = CallForestHelpers.map( update.children.accountUpdates, map ); @@ -1577,7 +1736,7 @@ const CallForest = { forEach(updates: AccountUpdate[], callback: (update: AccountUpdate) => void) { for (let update of updates) { callback(update); - CallForest.forEach(update.children.accountUpdates, callback); + CallForestHelpers.forEach(update.children.accountUpdates, callback); } }, @@ -1587,7 +1746,7 @@ const CallForest = { callback: (update: AccountUpdate) => void ) { let isPredecessor = true; - CallForest.forEach(updates, (otherUpdate) => { + CallForestHelpers.forEach(updates, (otherUpdate) => { if (otherUpdate.id === update.id) isPredecessor = false; if (isPredecessor) callback(otherUpdate); }); diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 06095e6610..1e9dca7568 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -10,7 +10,7 @@ import { AccountUpdate, ZkappPublicInput, TokenId, - CallForest, + CallForestHelpers, Authorization, Actions, Events, @@ -196,8 +196,9 @@ function createTransaction( f(); Provable.asProver(() => { let tx = currentTransaction.get(); - tx.accountUpdates = CallForest.map(tx.accountUpdates, (a) => - toConstant(AccountUpdate, a) + tx.accountUpdates = CallForestHelpers.map( + tx.accountUpdates, + (a) => toConstant(AccountUpdate, a) ); }); }); @@ -217,7 +218,7 @@ function createTransaction( let accountUpdates = currentTransaction.get().accountUpdates; // TODO: I'll be back // CallForest.addCallers(accountUpdates); - accountUpdates = CallForest.toFlatList(accountUpdates); + accountUpdates = CallForestHelpers.toFlatList(accountUpdates); try { // check that on-chain values weren't used without setting a precondition diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index e5bed5328d..89158e712a 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,48 +1,11 @@ -import { prefixes } from '../../../provable/poseidon-bigint.js'; -import { AccountUpdate, TokenId } from '../../account_update.js'; +import { AccountUpdate, CallForest, TokenId } from '../../account_update.js'; import { Field } from '../../core.js'; import { Provable } from '../../provable.js'; -import { Struct, Unconstrained } from '../../circuit_value.js'; +import { Struct } from '../../circuit_value.js'; import { assert } from '../../gadgets/common.js'; -import { Poseidon, ProvableHashable } from '../../hash.js'; -import { Hashed } from '../../provable-types/packed.js'; -import { - MerkleArray, - MerkleListBase, - MerkleList, - genericHash, - withHashes, -} from '../../provable-types/merkle-list.js'; +import { MerkleArray, MerkleList } from '../../provable-types/merkle-list.js'; -export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; - -export { CallForestUnderConstruction }; - -class HashedAccountUpdate extends Hashed.create( - AccountUpdate, - hashAccountUpdate -) {} - -type CallTree = { - accountUpdate: Hashed; - calls: MerkleListBase; -}; -const CallTree: ProvableHashable = Struct({ - accountUpdate: HashedAccountUpdate.provable, - calls: MerkleListBase(), -}); - -class CallForest extends MerkleList.create(CallTree, merkleListHash) { - static fromAccountUpdates(updates: AccountUpdate[]): CallForest { - let nodes = updates.map((update) => { - let accountUpdate = HashedAccountUpdate.hash(update); - let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); - return { accountUpdate, calls }; - }); - - return CallForest.from(nodes); - } -} +export { CallForestArray, CallForestIterator }; class CallForestArray extends MerkleArray.createFromList(CallForest) {} @@ -158,117 +121,3 @@ class CallForestIterator { ); } } - -// how to hash a forest - -function merkleListHash(forestHash: Field, tree: CallTree) { - return hashCons(forestHash, hashNode(tree)); -} - -function hashNode(tree: CallTree) { - return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ - tree.accountUpdate.hash, - tree.calls.hash, - ]); -} -function hashCons(forestHash: Field, nodeHash: Field) { - return Poseidon.hashWithPrefix(prefixes.accountUpdateCons, [ - nodeHash, - forestHash, - ]); -} - -function hashAccountUpdate(update: AccountUpdate) { - return genericHash(AccountUpdate, prefixes.body, update); -} - -/** - * Structure for constructing a call forest from a circuit. - * - * The circuit can mutate account updates and change their array of children, so here we can't hash - * everything immediately. Instead, we maintain a structure consisting of either hashes or full account - * updates that can be hashed into a final call forest at the end. - */ -type CallForestUnderConstruction = HashOrValue< - { - accountUpdate: HashOrValue; - calls: CallForestUnderConstruction; - }[] ->; - -type HashOrValue = - | { useHash: true; hash: Field; value: T } - | { useHash: false; value: T }; - -const CallForestUnderConstruction = { - empty(): CallForestUnderConstruction { - return { useHash: false, value: [] }; - }, - - setHash(forest: CallForestUnderConstruction, hash: Field) { - Object.assign(forest, { useHash: true, hash }); - }, - - witnessHash(forest: CallForestUnderConstruction) { - let hash = Provable.witness(Field, () => { - let nodes = forest.value.map(toCallTree); - return withHashes(nodes, merkleListHash).hash; - }); - CallForestUnderConstruction.setHash(forest, hash); - }, - - push( - forest: CallForestUnderConstruction, - accountUpdate: HashOrValue, - calls?: CallForestUnderConstruction - ) { - forest.value.push({ - accountUpdate, - calls: calls ?? CallForestUnderConstruction.empty(), - }); - }, - - remove(forest: CallForestUnderConstruction, accountUpdate: AccountUpdate) { - // find account update by .id - let index = forest.value.findIndex( - (node) => node.accountUpdate.value.id === accountUpdate.id - ); - - // nothing to do if it's not there - if (index === -1) return; - - // remove it - forest.value.splice(index, 1); - }, - - finalize(forest: CallForestUnderConstruction): CallForest { - if (forest.useHash) { - let data = Unconstrained.witness(() => { - let nodes = forest.value.map(toCallTree); - return withHashes(nodes, merkleListHash).data; - }); - return new CallForest({ hash: forest.hash, data }); - } - - // not using the hash means we calculate it in-circuit - let nodes = forest.value.map(toCallTree); - return CallForest.from(nodes); - }, -}; - -function toCallTree(node: { - accountUpdate: HashOrValue; - calls: CallForestUnderConstruction; -}): CallTree { - let accountUpdate = node.accountUpdate.useHash - ? new HashedAccountUpdate( - node.accountUpdate.hash, - Unconstrained.from(node.accountUpdate.value) - ) - : HashedAccountUpdate.hash(node.accountUpdate.value); - - return { - accountUpdate, - calls: CallForestUnderConstruction.finalize(node.calls), - }; -} diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index a9ca56bc56..fa5d46b508 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,14 +1,12 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { - CallForest, - CallForestIterator, - hashAccountUpdate, -} from './call-forest.js'; +import { CallForestIterator } from './call-forest.js'; import { AccountUpdate, - CallForest as ProvableCallForest, + CallForest, + CallForestHelpers, TokenId, + hashAccountUpdate, } from '../../account_update.js'; import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; import { Pickles } from '../../../snarky.js'; @@ -80,7 +78,7 @@ test(flatAccountUpdates, (flatUpdates) => { let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + let flatUpdates2 = CallForestHelpers.toFlatList(updates, false); let n = flatUpdates.length; for (let i = 0; i < n; i++) { assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 5837995e49..acb0908946 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -2,9 +2,13 @@ import { Bool } from '../../core.js'; import { UInt64, Int64 } from '../../int.js'; import { Provable } from '../../provable.js'; import { PublicKey } from '../../signature.js'; -import { AccountUpdate, Permissions } from '../../account_update.js'; +import { + AccountUpdate, + CallForest, + Permissions, +} from '../../account_update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; -import { CallForest, CallForestIterator } from './call-forest.js'; +import { CallForestIterator } from './call-forest.js'; export { TokenContract }; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 1efb6b45aa..8f653b97a2 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -17,7 +17,8 @@ import { ZkappStateLength, SmartContractContext, LazyProof, - CallForest, + CallForestHelpers, + CallForestUnderConstruction, } from './account_update.js'; import { cloneCircuitValue, @@ -59,7 +60,6 @@ import { } from './provable-context.js'; import { Cache } from './proof-system/cache.js'; import { assert } from './gadgets/common.js'; -import { CallForestUnderConstruction } from './mina/token/call-forest.js'; import { SmartContractBase } from './mina/smart-contract-base.js'; // external API @@ -1528,7 +1528,7 @@ const ProofAuthorization = { priorAccountUpdates = priorAccountUpdates.filter( (a) => a.id !== myAccountUpdateId ); - let priorAccountUpdatesFlat = CallForest.toFlatList( + let priorAccountUpdatesFlat = CallForestHelpers.toFlatList( priorAccountUpdates, false ); From f291743541e03a2296b81494cfcb502cfe89e675 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 31 Jan 2024 18:12:13 +0100 Subject: [PATCH 251/524] make token contract work w/ dex (hacky first iteration) --- src/lib/account_update.ts | 46 ++++++++++++++++++++++++++++ src/lib/mina/token/token-contract.ts | 41 ++++++++++++++++++++----- src/lib/zkapp.ts | 8 +++++ 3 files changed, 87 insertions(+), 8 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 1a02a01fbc..223d28b9f9 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -87,6 +87,7 @@ export { CallTree, CallForestUnderConstruction, hashAccountUpdate, + HashedAccountUpdate, }; const ZkappStateLength = 8; @@ -793,6 +794,15 @@ class AccountUpdate implements Types.AccountUpdate { ) { makeChildAccountUpdate(this, childUpdate); AccountUpdate.witnessChildren(childUpdate, layout, { skipCheck: true }); + + // TODO: this is not as general as approve suggests + let insideContract = smartContractContext.get(); + if (insideContract && insideContract.selfUpdate.id === this.id) { + CallForestUnderConstruction.push(insideContract.selfCalls, { + useHash: false, + value: childUpdate, + }); + } } /** @@ -800,6 +810,15 @@ class AccountUpdate implements Types.AccountUpdate { */ adopt(childUpdate: AccountUpdate) { makeChildAccountUpdate(this, childUpdate); + + // TODO: this is not as general as adopt suggests + let insideContract = smartContractContext.get(); + if (insideContract && insideContract.selfUpdate.id === this.id) { + CallForestUnderConstruction.push(insideContract.selfCalls, { + useHash: false, + value: childUpdate, + }); + } } get balance() { @@ -1126,6 +1145,14 @@ class AccountUpdate implements Types.AccountUpdate { * Disattach an account update from where it's currently located in the transaction */ static unlink(accountUpdate: AccountUpdate) { + // TODO duplicate logic + let insideContract = smartContractContext.get(); + if (insideContract) { + CallForestUnderConstruction.remove( + insideContract.selfCalls, + accountUpdate + ); + } let siblings = accountUpdate.parent?.children.accountUpdates ?? currentTransaction()?.accountUpdates; @@ -1585,6 +1612,25 @@ const CallForestUnderConstruction = { CallForestUnderConstruction.setHash(forest, hash); }, + fromAccountUpdates( + updates: AccountUpdate[], + useHash = false + ): CallForestUnderConstruction { + let nodes = updates.map((update) => { + let accountUpdate: HashOrValue = { + useHash: false, + value: update, + }; + let calls = CallForestUnderConstruction.fromAccountUpdates( + update.children.accountUpdates + ); + return { accountUpdate, calls }; + }); + let forest: CallForestUnderConstruction = { useHash: false, value: nodes }; + if (useHash) CallForestUnderConstruction.witnessHash(forest); + return forest; + }, + push( forest: CallForestUnderConstruction, accountUpdate: HashOrValue, diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index acb0908946..272145f25f 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -5,7 +5,11 @@ import { PublicKey } from '../../signature.js'; import { AccountUpdate, CallForest, + CallForestUnderConstruction, + CallTree, + HashedAccountUpdate, Permissions, + smartContractContext, } from '../../account_update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; import { CallForestIterator } from './call-forest.js'; @@ -97,16 +101,16 @@ abstract class TokenContract extends SmartContract { * Approve a single account update (with arbitrarily many children). */ approveAccountUpdate(accountUpdate: AccountUpdate) { - AccountUpdate.unlink(accountUpdate); - this.approveBase(CallForest.fromAccountUpdates([accountUpdate])); + let forest = finalizeAccountUpdates([accountUpdate]); + this.approveBase(forest); } /** * Approve a list of account updates (with arbitrarily many children). */ approveAccountUpdates(accountUpdates: AccountUpdate[]) { - accountUpdates.forEach(AccountUpdate.unlink); - this.approveBase(CallForest.fromAccountUpdates(accountUpdates)); + let forest = finalizeAccountUpdates(accountUpdates); + this.approveBase(forest); } // TRANSFERABLE API - simple wrapper around Approvable API @@ -128,15 +132,36 @@ abstract class TokenContract extends SmartContract { } if (to instanceof PublicKey) { to = AccountUpdate.defaultAccountUpdate(to, tokenId); - to.label = `${this.constructor.name}.transfer() (to)`; } + from.balanceChange = Int64.from(amount).neg(); to.balanceChange = Int64.from(amount); - AccountUpdate.unlink(from); - AccountUpdate.unlink(to); - let forest = CallForest.fromAccountUpdates([from, to]); + let forest = finalizeAccountUpdates([from, to]); this.approveBase(forest); } } + +function finalizeAccountUpdates(updates: AccountUpdate[]): CallForest { + let trees = updates.map(finalizeAccountUpdate); + return CallForest.from(trees); +} + +function finalizeAccountUpdate(update: AccountUpdate): CallTree { + let calls: CallForest; + + let insideContract = smartContractContext.get(); + if (insideContract) { + let node = insideContract.selfCalls.value.find( + (c) => c.accountUpdate.value.id === update.id + ); + if (node !== undefined) { + calls = CallForestUnderConstruction.finalize(node.calls); + } + } + calls ??= CallForest.fromAccountUpdates(update.children.accountUpdates); + AccountUpdate.unlink(update); + + return { accountUpdate: HashedAccountUpdate.hash(update), calls }; +} diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 8f653b97a2..267c625211 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -415,6 +415,14 @@ function wrapMethod( // nothing is asserted about them -- it's the callee's task to check their children accountUpdate.children.callsType = { type: 'Witness' }; parentAccountUpdate.children.accountUpdates.push(accountUpdate); + CallForestUnderConstruction.push( + insideContract.selfCalls, + { useHash: false, value: accountUpdate }, + CallForestUnderConstruction.fromAccountUpdates( + accountUpdate.children.accountUpdates, + true + ) + ); // assert that we really called the right zkapp accountUpdate.body.publicKey.assertEquals(this.address); From b8e43ccfe9f279f5c7a7d1d0b0347bcd8312657a Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 31 Jan 2024 18:33:16 +0100 Subject: [PATCH 252/524] dump vks --- tests/vk-regression/vk-regression.json | 82 +++++++++++--------------- 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 2697fed274..a22d774c87 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -1,22 +1,22 @@ { "Voting_": { - "digest": "3f56ff09ceba13daf64b20cd48419395a04aa0007cac20e6e9c5f9106f251c3a", + "digest": "22daa074b7870490b90b23421015e6d08e682f4ed5e622a9db2504dee5cd5671", "methods": { "voterRegistration": { "rows": 1258, - "digest": "5572b0d59feea6b199f3f45af7498d92" + "digest": "ae840e7007243291e18a1bd5216a49ed" }, "candidateRegistration": { "rows": 1258, - "digest": "07c8451f1c1ea4e9653548d411d5728c" + "digest": "e4f3b101d10ead3ce9cb2ccb37f6bc11" }, "approveRegistrations": { "rows": 1146, - "digest": "ec68c1d8ab22e779ccbd2659dd6b46cd" + "digest": "fe826d28b553da5307c15d28d0fd632d" }, "vote": { "rows": 1672, - "digest": "fa5671190ca2cc46084cae922a62288e" + "digest": "cfbc84a918636da1ca6afc95d02e014e" }, "countVotes": { "rows": 5796, @@ -24,8 +24,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "1740450553572902301764143810281331039416167348454304895395553400061364101079" + "data": "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", + "hash": "1875936070919967236715828287620658152293796457000976946864180886256059059334" } }, "Membership_": { @@ -63,7 +63,7 @@ } }, "TokenContract": { - "digest": "f97dbbb67a72f01d956539b9f510633c0f88057f5dfbad8493e8d08dc4ca049", + "digest": "20b64a7b0520ae119f8dc91a8ef10bb91b3f30ab8d6ade52b5b628c429b43d0b", "methods": { "init": { "rows": 655, @@ -73,54 +73,38 @@ "rows": 652, "digest": "1ebb84a10bafd30accfd3e8046d2e20d" }, + "approveBase": { + "rows": 13194, + "digest": "46100d5f715c68fc81f93c70e32c21f2" + }, "deployZkapp": { "rows": 702, "digest": "e5ac2667a79f44f1e7a65b12d8ac006c" }, - "approveUpdate": { - "rows": 1928, - "digest": "f8bd1807567dc405f841283227dfb158" - }, - "approveAny": { - "rows": 1928, - "digest": "240aada76b79de1ca67ecbe455621378" - }, - "approveUpdateAndSend": { - "rows": 3102, - "digest": "77efc708b9517c16722bad9cca54eb9c" - }, - "transferToAddress": { - "rows": 1044, - "digest": "212879ca2441ccc20f5e58940833cf35" - }, - "transferToUpdate": { - "rows": 2326, - "digest": "a7241cbc2946a3c468e600003a5d9a16" - }, "getBalance": { "rows": 686, "digest": "44a90b65d1d7ee553811759b115d12cc" } }, "verificationKey": { - "data": "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", - "hash": "20215142001741862869723990740096021895498505655157925381074115836190155964997" + "data": "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", + "hash": "27384842021225823013412718366201408258687963922479378403873398307032584328437" } }, "Dex": { - "digest": "14f902411526156cdf7de9a822a3f6467f7608a135504038993cbc8efeaf720a", + "digest": "6c2cdcc82896f8438ccae4490b4c2b47cc3ba430a9cdf53018eead6ba782950", "methods": { "supplyLiquidityBase": { - "rows": 3749, - "digest": "08830f49d9e8a4bf683db63c1c19bd28" + "rows": 2877, + "digest": "f7d4b4e34113b33d8543a74dfc3110dc" }, "swapX": { - "rows": 1986, - "digest": "e1c79fee9c8f94815daa5d6fee7c5181" + "rows": 1561, + "digest": "563d256fe4739dda2a992f8984fddd06" }, "swapY": { - "rows": 1986, - "digest": "4cf07c1491e7fc167edcf3a26d636f3d" + "rows": 1561, + "digest": "e2b1374e5ab54c3de6c5f31b9e4cd9b9" }, "burnLiquidity": { "rows": 718, @@ -132,8 +116,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "6361961148584909856756402479432671413765163664396823312454174383287651676472" + "data": "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", + "hash": "23362525313143196140641665177215350862002766202717183338959891568504853279430" } }, "Group Primitive": { @@ -236,29 +220,29 @@ } }, "ecdsa-only": { - "digest": "2a2beadf929015559514abb88dd77694dbe62a6a5904e5a9d05ab21a3f293c0c", + "digest": "f4439d84374753ce55c597e7d9593bc50aa45b942c95068ee9e4c96fef2a293", "methods": { "verifySignedHash": { - "rows": 28186, - "digest": "dd43cd30a8277b5af02fd85a4762a488" + "rows": 28220, + "digest": "5e793e1d827ea3900ab558c586c8b4cf" } }, "verificationKey": { - "data": "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", - "hash": "16626558875595050675741741208497473516532634598762284173771479275503819571624" + "data": "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", + "hash": "8521505138081104393457014021668593383409974534445035107673472947642693857881" } }, "ecdsa": { - "digest": "2f78a9c0307e6f9c6872499c3ca7c412f5771a7ac7e7e88c351ed1b62d6ac408", + "digest": "143d65f22de8f3368259408ea25ce6dd91a5355df5dd87898779db0cb08c1c79", "methods": { "verifyEcdsa": { - "rows": 42684, - "digest": "2bb042b0fa7bbbb32f7e77d392f43d2c" + "rows": 42718, + "digest": "f26be91413483ab6f928f052542d1752" } }, "verificationKey": { - "data": "AACzYt9qtBkn6y40KDH0lkzRSMBh3W41urWE6j0PSP2KB9GxsBAHAI3uzay1Vqyc+LMXeANSzNcoYSYnZts9Pk0nFNjCZ84EnGkie609NhFB8tU9k5Vkoqw3jihdsoJEUy6GK0H30dl/7H1rGxsx6Ec05aaFhiPw6t0jLxF1kj4uIeipqOScf8snKuzywk02FqvRxSHlk9pkEsUOvpNIwywxzhvHjWgXEQzROQF8v6q5R/1aJk3swpM1iRct9URLIjdin4GWyDB9279EZ6D6avFW2l7WuMJG++xBqGsNKZUgNM4WkUGNfCd+m42hJgt46eOy89db672su0n24IZG9tAsgQl8vPsVKfsTvTWlMj6/jISm7Dcctr1rZpSb8hRPsQstlfqMw3q6qijtTkFiMsdGRwJ6LNukSFUxOarhVsfREQngJufm4IxFpJJMR5F1DFSDPiOPuylEqXzke+j078Y4vr+QRo07YRlsoEv4a6ChcxMd3uu5Oami+D747/YVaS8kLd/3bO+WFpubID5fv4F7/JO4Fy/O7n1waPpNnzi/PZRlHVlwzNVAs09OmTmgzNM4/jAJBO9lRgCFA1SW0BADAEpKzWIdD67NW6DITBu81HFi90ZKqH/tTyg8BRgemaMcXAVfF3/CHvPHdZUINwyjjUZgwtny5dXgmckibPYQMC/PFNzYqZw3swyXzQ3nvZqWU2ARuzo1BgMrvnDgW1H+AMbKbNGU7IYXIYaLfTR9S7qrUbHESHac4wo9J9HmRiU1/IQdyr5LldYkzYtZOrjM4SzBkYYVtpSH7Sopij/TTy0U9CXNle7iCnZQS/72C8kwyJ+BGqpULLkSWhHoj+U9GSW9UgDHZ62jRTzvuZz5QaX/hYOmpNChNMFS1zoDYVE7ZIzVQKX03IDkzHAVJCXggwhQO3NK6OGhlP7A/heM6zgiR3/LlOa8uW4fcow50XC3280SDziK0Uczab3zlYXPPH6KqGPJfnftgwuvcHsRgddOWDVfEH3Q9mAj0y1R1Fopt1If5n2cJqYKMwqIuVNVDgSsRQxaMD38oJyY5QtXHR96Wbu2UpN9wcXXdEgD1Bs6BpvysAXbC1jpPlI97VMyMw2qDXacvJQHRIiBHfPZ3G52Z2lTf6OGg/elBurqGhA2wdDAQrBIWJwiTClONbV+8yR/4Md7aPi44E4XICLpHhE5hzko7ePy9cwh3oXy3btBt0urRwrl4d/jhHvoYt1eE2inNWEOYdlkXFUDlDErwOpFVsyQon0G25zNLAcVaZgdJLWueU1y3G0XkfHRqMZ8eV1iNAegPCCNRCvJ6SVsSwcQ67s45a8VqFxSSW0F65bDCI6Ue3Hwpb1RFKbfSIJbPyUrVSq5K99wUJ01O93Kn8LQlrAbjHWo5Za+tW0a/+Qlbr5E2eSEge+ldnbMbA9rcJwZf4bT457dBXMdlD7mECIDZtD8M/KLeyzMEinDzPfqnwZjU2ifxs6gaJPXOQAWPzbCm/z2vGlRbXDGZF6yTbLTdjzviuPhVtb7bzsZW2AYC+TlZqb4qm9MAVsH5rX3OZmvvmw5oRKeSj+FFD7uSRwfutDGC99i93uptU8syL/8Tr8xU3atxITlSqHqG+rVGWdLO9i3iq38zXgXbvZacrc3CMF5QBIM8yZXNslXH5k39D5SqubSHBWTqAJ1I0heOjaIHQGLROBYLn178tckBxfKQ2UpyfkvMw1Waw+fp5f64Ce+5bmYyZr6Dhmw/xcoAihjUsEqoecrLuGPp6qI4hQt9qOnVrAxHzwwtJGxcqoiCbe1mgz0fxMCt/i0z3ygdqAn20DKPHuBdqgVUFwx2T7Ac9fUCf3RHMq34onrr2nLHc038GYedmlFjoUZStujGwA8tSwLWyuWZTDVV+ZaW92qkhmrACog6NwhR6SEjQgsMRCVBQZzYirZxyulYmcNWH6BUmnLLFsn3GbS40xUr70gujEPnjZUK/ExGRfUPOfrYYb8mAciE9nP8OeK/UI+zjJy6Qp8mMroFw7gVHCfDtKTeQFt4JV3zubGsD7jypquHKCqPewhgn9tZ1UIsKIQB7+hBwDHzhlOZ2FfR4eLwQkO8sz275tpjHDAqX/TBWWRVg/yBDii0CWN4bP8UuX36jZKZboJUxIkM1xThiGZM2/oMbe5cZyjgrBR3P21wiDHAAlsHkaMfJgkVLqvZOw8hflKRIMa2dEYo5voD6aV30sATHQLoV0o+MlV3WA38RA+23Jqt1g+UZ7ReAuDP88jXhqWFcIvWHrJG0oy+rpAPQU/38vhIxbl//lirsirdVK2LrU47CC1f9/pRi07vTnvAm+n02dhwriqpwOmI2o2OU4mO0q96pCueKjAttkXgz+NSIJzcwprvNyE9UtKWswmIQg=", - "hash": "13907522711254991952405567976395529829664716172124500348498751038796408381729" + "data": "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", + "hash": "25193230245026500505730278477940803504648445281519558230631756595987510650479" } }, "sha256": { From 58e7128cd1dff1244b2d64556c371bae1cd5e627 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 08:46:47 +0100 Subject: [PATCH 253/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index a884dc593d..a9354195b4 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a884dc593dbab69e55ab9602b998ec12dfc3a288 +Subproject commit a9354195b4bb369cb9da827ccdfe81628385c1b6 From 51c55587907167dbcc6fa798e68ae3d45590fe69 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 09:23:36 +0100 Subject: [PATCH 254/524] fix changelog --- CHANGELOG.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 229892c5e7..e76325b791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/be748e42e...HEAD) +### Breaking changes + +- Reduce number of constraints of ECDSA verification by 5%, which breaks deployed contracts using ECDSA https://github.com/o1-labs/o1js/pull/1376 + ### Changed - Improve performance of Wasm Poseidon hashing by a factor of 13x https://github.com/o1-labs/o1js/pull/1378 @@ -26,27 +30,24 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -- Target `network ID` configuration. https://github.com/o1-labs/o1js/pull/1387 - - Defaults to the `testnet` (as it was before). +- Configurable `networkId` when declaring a Mina instance. https://github.com/o1-labs/o1js/pull/1387 + - Defaults to `"testnet"`, the other option is `"mainnet"` + - The `networkId` parameter influences the algorithm used for signatures, and ensures that testnet transactions can't be replayed on mainnet +- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 ## [0.15.3](https://github.com/o1-labs/o1js/compare/1ad7333e9e...be748e42e) -### Breaking changes +### Added + +- **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 + +### Changed - `Mina.accountCreationFee()` is deprecated in favor of `Mina.getNetworkConstants().accountCreationFee`. https://github.com/o1-labs/o1js/pull/1367 - `Mina.getNetworkConstants()` returns: - [default](https://github.com/o1-labs/o1js/pull/1367/files#diff-ef2c3547d64a8eaa8253cd82b3623288f3271e14f1dc893a0a3ddc1ff4b9688fR7) network constants if used outside of the transaction scope. - [actual](https://github.com/o1-labs/o1js/pull/1367/files#diff-437f2c15df7c90ad8154c5de1677ec0838d51859bcc0a0cefd8a0424b5736f31R1051) network constants if used within the transaction scope. -### Breaking changes - -- Reduce number of constraints of ECDSA verification by 5%, which breaks deployed contracts using ECDSA https://github.com/o1-labs/o1js/pull/1376 - -### Added - -- **SHA256 hash function** exposed via `Hash.SHA2_256` or `Gadgets.SHA256`. https://github.com/o1-labs/o1js/pull/1285 -- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 - ### Fixed - Fix approving of complex account update layouts https://github.com/o1-labs/o1js/pull/1364 From 59e531e8310e2bed799ed95c274611b90d7b19fe Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 09:30:39 +0100 Subject: [PATCH 255/524] fix changelog --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71e7e94c86..caa07e8004 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,12 +15,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm _Security_ in case of vulnerabilities. --> -## [Unreleased](https://github.com/o1-labs/o1js/compare/be748e42e...HEAD) +## [Unreleased](https://github.com/o1-labs/o1js/compare/e5d1e0f...HEAD) ### Breaking changes - Reduce number of constraints of ECDSA verification by 5%, which breaks deployed contracts using ECDSA https://github.com/o1-labs/o1js/pull/1376 +### Added + +- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 +- Provable type `Hashed` to represent provable types by their hash https://github.com/o1-labs/o1js/pull/1377 + +## [0.15.4](https://github.com/o1-labs/o1js/compare/be748e42e...e5d1e0f) + ### Changed - Improve performance of Wasm Poseidon hashing by a factor of 13x https://github.com/o1-labs/o1js/pull/1378 @@ -33,8 +40,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Configurable `networkId` when declaring a Mina instance. https://github.com/o1-labs/o1js/pull/1387 - Defaults to `"testnet"`, the other option is `"mainnet"` - The `networkId` parameter influences the algorithm used for signatures, and ensures that testnet transactions can't be replayed on mainnet -- Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 -- Provable type `Hashed` to represent provable types by their hash https://github.com/o1-labs/o1js/pull/1377 ## [0.15.3](https://github.com/o1-labs/o1js/compare/1ad7333e9e...be748e42e) From 58eb90ed375f5ea4e1fde1e2a376892d4fe3ab0e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 10:09:00 +0100 Subject: [PATCH 256/524] allow overriding the hash function, expose `hashPacked` --- src/lib/hash.ts | 22 +++++++++++++++ src/lib/provable-types/packed.ts | 47 +++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 1a55da5181..6f4dbc9347 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -14,6 +14,7 @@ export { Poseidon, TokenSymbol }; // internal API export { + ProvableHashable, HashInput, HashHelpers, emptyHashWithPrefix, @@ -24,6 +25,9 @@ export { hashConstant, }; +type Hashable = { toInput: (x: T) => HashInput; empty: () => T }; +type ProvableHashable = Provable & Hashable; + class Sponge { #sponge: unknown; @@ -96,6 +100,24 @@ const Poseidon = { return { x, y: { x0, x1 } }; }, + /** + * Hashes a provable type efficiently. + * + * ```ts + * let skHash = Poseidon.hashPacked(PrivateKey, secretKey); + * ``` + * + * Note: Instead of just doing `Poseidon.hash(value.toFields())`, this + * uses the `toInput()` method on the provable type to pack the input into as few + * field elements as possible. This saves constraints because packing has a much + * lower per-field element cost than hashing. + */ + hashPacked(type: Hashable, value: T) { + let input = type.toInput(value); + let packed = packToFields(input); + return Poseidon.hash(packed); + }, + initialState(): [Field, Field, Field] { return [Field(0), Field(0), Field(0)]; }, diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index d3ff8bbd31..a12b9cbb54 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -6,9 +6,9 @@ import { } from '../circuit_value.js'; import { Field } from '../field.js'; import { assert } from '../gadgets/common.js'; -import { Poseidon, packToFields } from '../hash.js'; +import { Poseidon, ProvableHashable, packToFields } from '../hash.js'; import { Provable } from '../provable.js'; -import { fields } from './fields.js'; +import { fields, modifiedField } from './fields.js'; export { Packed, Hashed }; @@ -63,6 +63,10 @@ class Packed { packed: fields(packedSize), value: Unconstrained.provable, }) as ProvableHashable>; + + static empty(): Packed { + return Packed_.pack(type.empty()); + } }; } @@ -120,8 +124,6 @@ class Packed { } } -type ProvableHashable = Provable & { toInput: (x: T) => HashInput }; - function countFields(input: HashInput) { let n = input.fields?.length ?? 0; let pendingBits = 0; @@ -172,13 +174,26 @@ class Hashed { /** * Create a hashed representation of `type`. You can then use `HashedType.hash(x)` to wrap a value in a `Hashed`. */ - static create(type: ProvableExtended): typeof Hashed { + static create( + type: ProvableHashable, + hash?: (t: T) => Field + ): typeof Hashed { + let _hash = hash ?? ((t: T) => Poseidon.hashPacked(type, t)); + + let dummyHash = _hash(type.empty()); + return class Hashed_ extends Hashed { static _innerProvable = type; static _provable = provableFromClass(Hashed_, { - hash: Field, + hash: modifiedField({ empty: () => dummyHash }), value: Unconstrained.provable, }) as ProvableHashable>; + + static _hash = _hash satisfies (t: T) => Field; + + static empty(): Hashed { + return new this(dummyHash, Unconstrained.from(type.empty())); + } }; } @@ -187,14 +202,16 @@ class Hashed { this.value = value; } + static _hash(_: any): Field { + assert(false, 'Hashed not initialized'); + } + /** * Wrap a value, and represent it by its hash in provable code. */ - static hash(x: T): Hashed { - let input = this.innerProvable.toInput(x); - let packed = packToFields(input); - let hash = Poseidon.hash(packed); - return new this(hash, Unconstrained.from(x)); + static hash(value: T): Hashed { + let hash = this._hash(value); + return new this(hash, Unconstrained.from(value)); } /** @@ -206,9 +223,7 @@ class Hashed { ); // prove that the value hashes to the hash - let input = this.Constructor.innerProvable.toInput(value); - let packed = packToFields(input); - let hash = Poseidon.hash(packed); + let hash = this.Constructor._hash(value); this.hash.assertEquals(hash); return value; @@ -220,7 +235,7 @@ class Hashed { // dynamic subclassing infra static _provable: ProvableHashable> | undefined; - static _innerProvable: ProvableExtended | undefined; + static _innerProvable: ProvableHashable | undefined; get Constructor(): typeof Hashed { return this.constructor as typeof Hashed; @@ -230,7 +245,7 @@ class Hashed { assert(this._provable !== undefined, 'Hashed not initialized'); return this._provable; } - static get innerProvable(): ProvableExtended { + static get innerProvable(): ProvableHashable { assert(this._innerProvable !== undefined, 'Hashed not initialized'); return this._innerProvable; } From 40344b6a0438b7d7e8f91ecb5d0225c2af844767 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 10:09:04 +0100 Subject: [PATCH 257/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index a9354195b4..c7d681e395 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a9354195b4bb369cb9da827ccdfe81628385c1b6 +Subproject commit c7d681e395d7758c0a5d339ad934c002e2c1e8de From 2cd85061c78f7ac1b520454f3227c5bdff62adc6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 10:10:04 +0100 Subject: [PATCH 258/524] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index caa07e8004..433daf8f5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 - Provable type `Hashed` to represent provable types by their hash https://github.com/o1-labs/o1js/pull/1377 + - This also exposes `Poseidon.hashPacked()` to efficiently hash an arbitrary type ## [0.15.4](https://github.com/o1-labs/o1js/compare/be748e42e...e5d1e0f) From 6f61b567a0974848e4982b413f865d07a87df8ae Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 10:21:18 +0100 Subject: [PATCH 259/524] bindings fixup --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index c7d681e395..772ce4ba92 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit c7d681e395d7758c0a5d339ad934c002e2c1e8de +Subproject commit 772ce4ba92e63453253250bb706339016a8d1e8c From a811a87b55f09b6a9f9ee1df02d817622701f505 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 10:32:23 +0100 Subject: [PATCH 260/524] submodules --- src/bindings | 2 +- src/mina | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index 339a03c984..772ce4ba92 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 339a03c984140c10dd8c4e1f3b26438d38550c6a +Subproject commit 772ce4ba92e63453253250bb706339016a8d1e8c diff --git a/src/mina b/src/mina index 2a968c8347..f4e67fe467 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 2a968c83477ed9f9e3b30a02cc357e541b76dcac +Subproject commit f4e67fe4672762b327026614882a2e8847287688 From c40bfa3d22e2f03c8a97cde7c4761b435fe118cd Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 10:46:06 +0100 Subject: [PATCH 261/524] merge fixups --- src/lib/account_update.ts | 4 ++-- src/lib/hash.ts | 4 ---- src/lib/mina.ts | 2 ++ src/lib/mina/mina-instance.ts | 36 +++++++++++++++++++++++++---------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 17d39df40c..bb15d8450f 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1942,7 +1942,7 @@ function addMissingSignatures( let signature = signFieldElement( fullCommitment, privateKey.toBigInt(), - Mina.getNetworkId() + activeInstance.getNetworkId() ); return { body, authorization: Signature.toBase58(signature) }; } @@ -1975,7 +1975,7 @@ function addMissingSignatures( let signature = signFieldElement( transactionCommitment, privateKey.toBigInt(), - Mina.getNetworkId() + activeInstance.getNetworkId() ); Authorization.setSignature(accountUpdate, Signature.toBase58(signature)); return accountUpdate as AccountUpdate & { lazyAuthorization: undefined }; diff --git a/src/lib/hash.ts b/src/lib/hash.ts index a66ce1b946..7f766c9412 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -129,10 +129,6 @@ const Poseidon = { return Poseidon.hash(packed); }, - initialState(): [Field, Field, Field] { - return [Field(0), Field(0), Field(0)]; - }, - Sponge, }; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index d01884f4e2..e98bf61a83 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -41,6 +41,8 @@ import { FeePayerSpec, DeprecatedFeePayerSpec, ActionStates, + defaultNetworkConstants, + NetworkConstants, } from './mina/mina-instance.js'; export { diff --git a/src/lib/mina/mina-instance.ts b/src/lib/mina/mina-instance.ts index a64f82ec01..dbaba81b33 100644 --- a/src/lib/mina/mina-instance.ts +++ b/src/lib/mina/mina-instance.ts @@ -8,18 +8,27 @@ import type { Transaction, TransactionId } from '../mina.js'; import type { Account } from './account.js'; import type { NetworkValue } from '../precondition.js'; import type * as Fetch from '../fetch.js'; +import type { NetworkId } from '../../mina-signer/src/TSTypes.js'; export { Mina, FeePayerSpec, DeprecatedFeePayerSpec, ActionStates, + NetworkConstants, + defaultNetworkConstants, activeInstance, setActiveInstance, ZkappStateLength, }; const defaultAccountCreationFee = 1_000_000_000; +const defaultNetworkConstants: NetworkConstants = { + genesisTimestamp: UInt64.from(0), + slotTime: UInt64.from(3 * 60 * 1000), + accountCreationFee: UInt64.from(defaultAccountCreationFee), +}; + const ZkappStateLength = 8; /** @@ -59,6 +68,15 @@ type ActionStates = { endActionState?: Field; }; +type NetworkConstants = { + genesisTimestamp: UInt64; + /** + * Duration of 1 slot in millisecondw + */ + slotTime: UInt64; + accountCreationFee: UInt64; +}; + interface Mina { transaction( sender: DeprecatedFeePayerSpec, @@ -68,14 +86,10 @@ interface Mina { hasAccount(publicKey: PublicKey, tokenId?: Field): boolean; getAccount(publicKey: PublicKey, tokenId?: Field): Account; getNetworkState(): NetworkValue; - getNetworkConstants(): { - genesisTimestamp: UInt64; - /** - * Duration of 1 slot in millisecondw - */ - slotTime: UInt64; - accountCreationFee: UInt64; - }; + getNetworkConstants(): NetworkConstants; + /** + * @deprecated use {@link getNetworkConstants} + */ accountCreationFee(): UInt64; sendTransaction(transaction: Transaction): Promise; fetchEvents: ( @@ -94,11 +108,12 @@ interface Mina { tokenId?: Field ) => { hash: string; actions: string[][] }[]; proofsEnabled: boolean; + getNetworkId(): NetworkId; } let activeInstance: Mina = { - accountCreationFee: () => UInt64.from(defaultAccountCreationFee), - getNetworkConstants: noActiveInstance, + accountCreationFee: () => defaultNetworkConstants.accountCreationFee, + getNetworkConstants: () => defaultNetworkConstants, currentSlot: noActiveInstance, hasAccount: noActiveInstance, getAccount: noActiveInstance, @@ -109,6 +124,7 @@ let activeInstance: Mina = { fetchActions: noActiveInstance, getActions: noActiveInstance, proofsEnabled: true, + getNetworkId: () => 'testnet', }; /** From a6c3ebff0b0a32f46e033449b60602249badd464 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 19:00:47 +0100 Subject: [PATCH 262/524] merkle list --- src/examples/zkapps/token/merkle-list.ts | 68 ++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/examples/zkapps/token/merkle-list.ts diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts new file mode 100644 index 0000000000..7a1e696884 --- /dev/null +++ b/src/examples/zkapps/token/merkle-list.ts @@ -0,0 +1,68 @@ +import { Field, Poseidon, Provable, Struct, Unconstrained } from 'o1js'; + +export { MerkleList }; + +class Element extends Struct({ previousHash: Field, element: Field }) {} + +const emptyHash = Field(0); +const dummyElement = Field(0); + +class MerkleList { + hash: Field; + value: Unconstrained; + + private constructor(hash: Field, value: Element[]) { + this.hash = hash; + this.value = Unconstrained.from(value); + } + + isEmpty() { + return this.hash.equals(emptyHash); + } + + static create(): MerkleList { + return new MerkleList(emptyHash, []); + } + + push(element: Field) { + let previousHash = this.hash; + this.hash = Poseidon.hash([previousHash, element]); + Provable.asProver(() => { + this.value.set([...this.value.get(), { previousHash, element }]); + }); + } + + private popWitness() { + return Provable.witness(Element, () => { + let value = this.value.get(); + let head = value.at(-1) ?? { + previousHash: emptyHash, + element: dummyElement, + }; + this.value.set(value.slice(0, -1)); + return head; + }); + } + + pop(): Field { + let { previousHash, element } = this.popWitness(); + + let requiredHash = Poseidon.hash([previousHash, element]); + this.hash.assertEquals(requiredHash); + + this.hash = previousHash; + return element; + } + + popOrDummy(): Field { + let { previousHash, element } = this.popWitness(); + + let isEmpty = this.isEmpty(); + let correctHash = Poseidon.hash([previousHash, element]); + let requiredHash = Provable.if(isEmpty, emptyHash, correctHash); + this.hash.assertEquals(requiredHash); + + this.hash = Provable.if(isEmpty, emptyHash, previousHash); + return Provable.if(isEmpty, dummyElement, element); + } +} From f89c5a71280e32ee0a9459f020e1cb9d1e512e40 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:48:50 +0100 Subject: [PATCH 263/524] export unconstrained properly --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 8fb4e0c9ef..8b8aa0672e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,6 @@ export type { FlexibleProvable, FlexibleProvablePure, InferProvable, - Unconstrained, } from './lib/circuit_value.js'; export { CircuitValue, @@ -31,6 +30,7 @@ export { provable, provablePure, Struct, + Unconstrained, } from './lib/circuit_value.js'; export { Provable } from './lib/provable.js'; export { Circuit, Keypair, public_, circuitMain } from './lib/circuit.js'; From dc58706d5a8584ea5dd6364e483d7b518d3d971a Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:49:04 +0100 Subject: [PATCH 264/524] export hash with prefix --- src/lib/hash.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 6f4dbc9347..7f766c9412 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -66,6 +66,17 @@ const Poseidon = { return MlFieldArray.from(newState) as [Field, Field, Field]; }, + hashWithPrefix(prefix: string, input: Field[]) { + let init = Poseidon.update(Poseidon.initialState(), [ + prefixToField(prefix), + ]); + return Poseidon.update(init, input)[0]; + }, + + initialState(): [Field, Field, Field] { + return [Field(0), Field(0), Field(0)]; + }, + hashToGroup(input: Field[]) { if (isConstant(input)) { let result = PoseidonBigint.hashToGroup(toBigints(input)); @@ -118,10 +129,6 @@ const Poseidon = { return Poseidon.hash(packed); }, - initialState(): [Field, Field, Field] { - return [Field(0), Field(0), Field(0)]; - }, - Sponge, }; From 9719eb76357ce7c200e888d5d632302380198d14 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:49:19 +0100 Subject: [PATCH 265/524] generic merkle tree --- src/examples/zkapps/token/merkle-list.ts | 52 +++++++++++++++++------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 7a1e696884..3cefc1e941 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -4,65 +4,87 @@ export { MerkleList }; class Element extends Struct({ previousHash: Field, element: Field }) {} +type WithHash = { previousHash: Field; element: T }; +function WithHash(type: ProvableHashable): Provable> { + return Struct({ previousHash: Field, element: type }); +} + const emptyHash = Field(0); -const dummyElement = Field(0); -class MerkleList { +class MerkleList { hash: Field; - value: Unconstrained; + value: Unconstrained[]>; + provable: ProvableHashable; + nextHash: (hash: Field, t: T) => Field; - private constructor(hash: Field, value: Element[]) { + private constructor( + hash: Field, + value: WithHash[], + provable: ProvableHashable, + nextHash: (hash: Field, t: T) => Field + ) { this.hash = hash; this.value = Unconstrained.from(value); + this.provable = provable; + this.nextHash = nextHash; } isEmpty() { return this.hash.equals(emptyHash); } - static create(): MerkleList { - return new MerkleList(emptyHash, []); + static create( + provable: ProvableHashable, + nextHash: (hash: Field, t: T) => Field + ): MerkleList { + return new MerkleList(emptyHash, [], provable, nextHash); } - push(element: Field) { + push(element: T) { let previousHash = this.hash; - this.hash = Poseidon.hash([previousHash, element]); + this.hash = this.nextHash(previousHash, element); Provable.asProver(() => { this.value.set([...this.value.get(), { previousHash, element }]); }); } private popWitness() { - return Provable.witness(Element, () => { + return Provable.witness(WithHash(this.provable), () => { let value = this.value.get(); let head = value.at(-1) ?? { previousHash: emptyHash, - element: dummyElement, + element: this.provable.empty(), }; this.value.set(value.slice(0, -1)); return head; }); } - pop(): Field { + pop(): T { let { previousHash, element } = this.popWitness(); - let requiredHash = Poseidon.hash([previousHash, element]); + let requiredHash = this.nextHash(previousHash, element); this.hash.assertEquals(requiredHash); this.hash = previousHash; return element; } - popOrDummy(): Field { + popOrDummy(): T { let { previousHash, element } = this.popWitness(); let isEmpty = this.isEmpty(); - let correctHash = Poseidon.hash([previousHash, element]); + let correctHash = this.nextHash(previousHash, element); let requiredHash = Provable.if(isEmpty, emptyHash, correctHash); this.hash.assertEquals(requiredHash); this.hash = Provable.if(isEmpty, emptyHash, previousHash); - return Provable.if(isEmpty, dummyElement, element); + return Provable.if(isEmpty, this.provable, this.provable.empty(), element); } } + +type HashInput = { fields?: Field[]; packed?: [Field, number][] }; +type ProvableHashable = Provable & { + toInput: (x: T) => HashInput; + empty: () => T; +}; From 6540c420728c6866266df30a458b09eeef963517 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 22 Jan 2024 21:49:30 +0100 Subject: [PATCH 266/524] start call forest --- src/examples/zkapps/token/call-forest.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/examples/zkapps/token/call-forest.ts diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts new file mode 100644 index 0000000000..4384f51c54 --- /dev/null +++ b/src/examples/zkapps/token/call-forest.ts @@ -0,0 +1,17 @@ +import { AccountUpdate, Field, Hashed } from 'o1js'; +import { MerkleList } from './merkle-list.js'; + +export { CallForest }; + +class CallTree { + accountUpdate: Hashed; + calls: CallTree[]; +} + +// basically a MerkleList if MerkleList were generic +type CallForest = MerkleList[]; + +class PartialCallForest { + forest: Hashed; + pendingForests: MerkleList; +} From 8219ef0aa63367d2df4519852f93226b2a39d82e Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 00:55:24 +0100 Subject: [PATCH 267/524] call forest logic --- src/examples/zkapps/token/call-forest.ts | 69 +++++++++++++++++++++--- src/examples/zkapps/token/merkle-list.ts | 24 +++++---- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 4384f51c54..0c431bb991 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -1,17 +1,72 @@ -import { AccountUpdate, Field, Hashed } from 'o1js'; -import { MerkleList } from './merkle-list.js'; +import { AccountUpdate, Field, Hashed, Poseidon, Unconstrained } from 'o1js'; +import { MerkleList, WithStackHash, emptyHash } from './merkle-list.js'; export { CallForest }; -class CallTree { +class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => + a.hash() +) {} + +type CallTree = { accountUpdate: Hashed; - calls: CallTree[]; -} + calls: CallForest; +}; + +type CallForest = WithStackHash; + +const CallForest = { + fromAccountUpdates(updates: AccountUpdate[]): CallForest { + let forest = CallForest.empty(); + + for (let update of [...updates].reverse()) { + let accountUpdate = HashedAccountUpdate.hash(update); + let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); + CallForest.cons(forest, { accountUpdate, calls }); + } + return forest; + }, + + empty(): CallForest { + return { hash: emptyHash, stack: Unconstrained.from([]) }; + }, -// basically a MerkleList if MerkleList were generic -type CallForest = MerkleList[]; + cons(forest: CallForest, tree: CallTree) { + let node = { previousHash: forest.hash, element: tree }; + let nodeHash = CallForest.hashNode(tree); + + forest.stack.set([...forest.stack.get(), node]); + forest.hash = CallForest.hashCons(forest, nodeHash); + }, + + hashNode(tree: CallTree) { + return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ + tree.accountUpdate.hash, + tree.calls.hash, + ]); + }, + hashCons(forest: CallForest, nodeHash: Field) { + return Poseidon.hashWithPrefix('MinaAcctUpdateCons**', [ + forest.hash, + nodeHash, + ]); + }, + + provable: WithStackHash(), +}; + +const CallForestHashed = Hashed.create( + CallForest.provable, + (forest) => forest.hash +); class PartialCallForest { forest: Hashed; pendingForests: MerkleList; + + constructor(forest: CallForest) { + this.forest = CallForestHashed.hash(forest); + this.pendingForests = MerkleList.create(CallForest.provable, (hash, t) => + Poseidon.hash([hash, t.hash]) + ); + } } diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 3cefc1e941..1fd6b09c2c 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -1,19 +1,25 @@ -import { Field, Poseidon, Provable, Struct, Unconstrained } from 'o1js'; +import { Field, Provable, ProvableExtended, Struct, Unconstrained } from 'o1js'; -export { MerkleList }; - -class Element extends Struct({ previousHash: Field, element: Field }) {} +export { MerkleList, WithHash, WithStackHash, emptyHash }; type WithHash = { previousHash: Field; element: T }; function WithHash(type: ProvableHashable): Provable> { return Struct({ previousHash: Field, element: type }); } +type WithStackHash = { + hash: Field; + stack: Unconstrained[]>; +}; +function WithStackHash(): ProvableExtended> { + return Struct({ hash: Field, stack: Unconstrained.provable }); +} + const emptyHash = Field(0); class MerkleList { hash: Field; - value: Unconstrained[]>; + stack: Unconstrained[]>; provable: ProvableHashable; nextHash: (hash: Field, t: T) => Field; @@ -24,7 +30,7 @@ class MerkleList { nextHash: (hash: Field, t: T) => Field ) { this.hash = hash; - this.value = Unconstrained.from(value); + this.stack = Unconstrained.from(value); this.provable = provable; this.nextHash = nextHash; } @@ -44,18 +50,18 @@ class MerkleList { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); Provable.asProver(() => { - this.value.set([...this.value.get(), { previousHash, element }]); + this.stack.set([...this.stack.get(), { previousHash, element }]); }); } private popWitness() { return Provable.witness(WithHash(this.provable), () => { - let value = this.value.get(); + let value = this.stack.get(); let head = value.at(-1) ?? { previousHash: emptyHash, element: this.provable.empty(), }; - this.value.set(value.slice(0, -1)); + this.stack.set(value.slice(0, -1)); return head; }); } From c65c92c506375c13d065fbc7f40fd1ab31018f41 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 01:37:19 +0100 Subject: [PATCH 268/524] properly generic merkle list --- src/examples/zkapps/token/merkle-list.ts | 88 ++++++++++++++++++------ 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 1fd6b09c2c..b58599fb15 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -1,6 +1,14 @@ -import { Field, Provable, ProvableExtended, Struct, Unconstrained } from 'o1js'; - -export { MerkleList, WithHash, WithStackHash, emptyHash }; +import { + Field, + Provable, + ProvableExtended, + Struct, + Unconstrained, + assert, +} from 'o1js'; +import { provableFromClass } from 'src/bindings/lib/provable-snarky.js'; + +export { MerkleList, WithHash, WithStackHash, emptyHash, ProvableHashable }; type WithHash = { previousHash: Field; element: T }; function WithHash(type: ProvableHashable): Provable> { @@ -20,30 +28,18 @@ const emptyHash = Field(0); class MerkleList { hash: Field; stack: Unconstrained[]>; - provable: ProvableHashable; - nextHash: (hash: Field, t: T) => Field; - private constructor( - hash: Field, - value: WithHash[], - provable: ProvableHashable, - nextHash: (hash: Field, t: T) => Field - ) { + constructor(hash: Field, value: WithHash[]) { this.hash = hash; this.stack = Unconstrained.from(value); - this.provable = provable; - this.nextHash = nextHash; } isEmpty() { return this.hash.equals(emptyHash); } - static create( - provable: ProvableHashable, - nextHash: (hash: Field, t: T) => Field - ): MerkleList { - return new MerkleList(emptyHash, [], provable, nextHash); + static empty(): MerkleList { + return new this(emptyHash, []); } push(element: T) { @@ -55,11 +51,11 @@ class MerkleList { } private popWitness() { - return Provable.witness(WithHash(this.provable), () => { + return Provable.witness(WithHash(this.innerProvable), () => { let value = this.stack.get(); let head = value.at(-1) ?? { previousHash: emptyHash, - element: this.provable.empty(), + element: this.innerProvable.empty(), }; this.stack.set(value.slice(0, -1)); return head; @@ -85,7 +81,57 @@ class MerkleList { this.hash.assertEquals(requiredHash); this.hash = Provable.if(isEmpty, emptyHash, previousHash); - return Provable.if(isEmpty, this.provable, this.provable.empty(), element); + let provable = this.innerProvable; + return Provable.if(isEmpty, provable, provable.empty(), element); + } + + /** + * Create a Merkle list type + */ + static create( + type: ProvableHashable, + nextHash: (hash: Field, t: T) => Field + ): typeof MerkleList { + return class MerkleList_ extends MerkleList { + static _innerProvable = type; + + static _provable = provableFromClass(MerkleList_, { + hash: Field, + stack: Unconstrained.provable, + }) as ProvableHashable>; + + static _nextHash = nextHash; + }; + } + + // dynamic subclassing infra + static _nextHash: ((hash: Field, t: any) => Field) | undefined; + + static _provable: ProvableHashable> | undefined; + static _innerProvable: ProvableHashable | undefined; + + get Constructor() { + return this.constructor as typeof MerkleList; + } + + nextHash(hash: Field, t: T): Field { + assert( + this.Constructor._nextHash !== undefined, + 'MerkleList not initialized' + ); + return this.Constructor._nextHash(hash, t); + } + + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'MerkleList not initialized'); + return this._provable; + } + get innerProvable(): ProvableHashable { + assert( + this.Constructor._innerProvable !== undefined, + 'MerkleList not initialized' + ); + return this.Constructor._innerProvable; } } From 85f917412e294fe174e19493e7832b6fd51e943f Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 01:37:58 +0100 Subject: [PATCH 269/524] make callforest a merkle list --- src/examples/zkapps/token/call-forest.ts | 82 +++++++++++------------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 0c431bb991..1646d3e476 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -1,5 +1,5 @@ -import { AccountUpdate, Field, Hashed, Poseidon, Unconstrained } from 'o1js'; -import { MerkleList, WithStackHash, emptyHash } from './merkle-list.js'; +import { AccountUpdate, Field, Hashed, Poseidon, Struct } from 'o1js'; +import { MerkleList, ProvableHashable, WithStackHash } from './merkle-list.js'; export { CallForest }; @@ -9,64 +9,58 @@ class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => type CallTree = { accountUpdate: Hashed; - calls: CallForest; + calls: WithStackHash; }; +const CallTree: ProvableHashable = Struct({ + accountUpdate: HashedAccountUpdate.provable, + calls: WithStackHash(), +}); -type CallForest = WithStackHash; +class CallForest extends MerkleList.create(CallTree, function (hash, tree) { + return hashCons(hash, hashNode(tree)); +}) { + static empty(): CallForest { + return super.empty(); + } -const CallForest = { - fromAccountUpdates(updates: AccountUpdate[]): CallForest { + static fromAccountUpdates(updates: AccountUpdate[]): CallForest { let forest = CallForest.empty(); for (let update of [...updates].reverse()) { let accountUpdate = HashedAccountUpdate.hash(update); let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); - CallForest.cons(forest, { accountUpdate, calls }); + forest.push({ accountUpdate, calls }); } - return forest; - }, - - empty(): CallForest { - return { hash: emptyHash, stack: Unconstrained.from([]) }; - }, - - cons(forest: CallForest, tree: CallTree) { - let node = { previousHash: forest.hash, element: tree }; - let nodeHash = CallForest.hashNode(tree); - forest.stack.set([...forest.stack.get(), node]); - forest.hash = CallForest.hashCons(forest, nodeHash); - }, - - hashNode(tree: CallTree) { - return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ - tree.accountUpdate.hash, - tree.calls.hash, - ]); - }, - hashCons(forest: CallForest, nodeHash: Field) { - return Poseidon.hashWithPrefix('MinaAcctUpdateCons**', [ - forest.hash, - nodeHash, - ]); - }, - - provable: WithStackHash(), -}; + return forest; + } +} -const CallForestHashed = Hashed.create( - CallForest.provable, - (forest) => forest.hash +const PendingForests = MerkleList.create(CallForest.provable, (hash, t) => + Poseidon.hash([hash, t.hash]) ); class PartialCallForest { - forest: Hashed; + forest: CallForest; pendingForests: MerkleList; constructor(forest: CallForest) { - this.forest = CallForestHashed.hash(forest); - this.pendingForests = MerkleList.create(CallForest.provable, (hash, t) => - Poseidon.hash([hash, t.hash]) - ); + this.forest = forest; + this.pendingForests = PendingForests.empty(); } } + +// how to hash a forest + +function hashNode(tree: CallTree) { + return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ + tree.accountUpdate.hash, + tree.calls.hash, + ]); +} +function hashCons(forestHash: Field, nodeHash: Field) { + return Poseidon.hashWithPrefix('MinaAcctUpdateCons**', [ + forestHash, + nodeHash, + ]); +} From c4dc1f47f0d793dc8c20d928e53072c99bd24eed Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 02:33:46 +0100 Subject: [PATCH 270/524] start writing pop account update --- src/examples/zkapps/token/call-forest.ts | 29 +++++++++++++++++++--- src/examples/zkapps/token/merkle-list.ts | 31 +++++++++++++++++++----- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 1646d3e476..fa476bf727 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -16,12 +16,13 @@ const CallTree: ProvableHashable = Struct({ calls: WithStackHash(), }); -class CallForest extends MerkleList.create(CallTree, function (hash, tree) { - return hashCons(hash, hashNode(tree)); -}) { +class CallForest extends MerkleList.create(CallTree, nextHash) { static empty(): CallForest { return super.empty(); } + static from(value: WithStackHash): CallForest { + return new this(value.hash, value.stack); + } static fromAccountUpdates(updates: AccountUpdate[]): CallForest { let forest = CallForest.empty(); @@ -34,6 +35,11 @@ class CallForest extends MerkleList.create(CallTree, function (hash, tree) { return forest; } + + pop() { + let { accountUpdate, calls } = super.pop(); + return { accountUpdate, calls: CallForest.from(calls) }; + } } const PendingForests = MerkleList.create(CallForest.provable, (hash, t) => @@ -48,10 +54,27 @@ class PartialCallForest { this.forest = forest; this.pendingForests = PendingForests.empty(); } + + popAccountUpdate() { + let { accountUpdate, calls: forest } = this.forest.pop(); + let restOfForest = this.forest; + + this.pendingForests.pushIf(restOfForest.isEmpty().not(), restOfForest); + this.forest = forest; + + // TODO add a notion of 'current token' to partial call forest, + // or as input to this method + // TODO replace forest with empty forest if account update can't access current token + let update = accountUpdate.unhash(); + } } // how to hash a forest +function nextHash(forestHash: Field, tree: CallTree) { + return hashCons(forestHash, hashNode(tree)); +} + function hashNode(tree: CallTree) { return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ tree.accountUpdate.hash, diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index b58599fb15..16e5f0390f 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -1,4 +1,5 @@ import { + Bool, Field, Provable, ProvableExtended, @@ -15,23 +16,27 @@ function WithHash(type: ProvableHashable): Provable> { return Struct({ previousHash: Field, element: type }); } +const emptyHash = Field(0); + type WithStackHash = { hash: Field; stack: Unconstrained[]>; }; function WithStackHash(): ProvableExtended> { - return Struct({ hash: Field, stack: Unconstrained.provable }); + return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { + static empty(): WithStackHash { + return { hash: emptyHash, stack: Unconstrained.from([]) }; + } + }; } -const emptyHash = Field(0); - class MerkleList { hash: Field; stack: Unconstrained[]>; - constructor(hash: Field, value: WithHash[]) { + constructor(hash: Field, value: Unconstrained[]>) { this.hash = hash; - this.stack = Unconstrained.from(value); + this.stack = value; } isEmpty() { @@ -39,7 +44,7 @@ class MerkleList { } static empty(): MerkleList { - return new this(emptyHash, []); + return new this(emptyHash, Unconstrained.from([])); } push(element: T) { @@ -50,6 +55,20 @@ class MerkleList { }); } + pushIf(condition: Bool, element: T) { + let previousHash = this.hash; + this.hash = Provable.if( + condition, + this.nextHash(previousHash, element), + previousHash + ); + Provable.asProver(() => { + if (condition.toBoolean()) { + this.stack.set([...this.stack.get(), { previousHash, element }]); + } + }); + } + private popWitness() { return Provable.witness(WithHash(this.innerProvable), () => { let value = this.stack.get(); From c21ca68a72bd0154a321febc4f8fbbff46a6e09c Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 23 Jan 2024 02:51:01 +0100 Subject: [PATCH 271/524] finish core pop account update logic --- src/examples/zkapps/token/call-forest.ts | 24 ++++++++++++++++++++++-- src/examples/zkapps/token/merkle-list.ts | 9 +++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index fa476bf727..274d13fe8a 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -1,4 +1,4 @@ -import { AccountUpdate, Field, Hashed, Poseidon, Struct } from 'o1js'; +import { AccountUpdate, Field, Hashed, Poseidon, Provable, Struct } from 'o1js'; import { MerkleList, ProvableHashable, WithStackHash } from './merkle-list.js'; export { CallForest }; @@ -60,12 +60,32 @@ class PartialCallForest { let restOfForest = this.forest; this.pendingForests.pushIf(restOfForest.isEmpty().not(), restOfForest); - this.forest = forest; // TODO add a notion of 'current token' to partial call forest, // or as input to this method // TODO replace forest with empty forest if account update can't access current token let update = accountUpdate.unhash(); + + let currentIsEmpty = forest.isEmpty(); + + let pendingForests = this.pendingForests.clone(); + let nextForest = this.pendingForests.pop(); + let newPendingForests = this.pendingForests; + + this.forest = Provable.if( + currentIsEmpty, + CallForest.provable, + nextForest, + forest + ); + this.pendingForests = Provable.if( + currentIsEmpty, + PendingForests.provable, + newPendingForests, + pendingForests + ); + + return update; } } diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 16e5f0390f..b79402ee9a 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -81,7 +81,7 @@ class MerkleList { }); } - pop(): T { + popExn(): T { let { previousHash, element } = this.popWitness(); let requiredHash = this.nextHash(previousHash, element); @@ -91,7 +91,7 @@ class MerkleList { return element; } - popOrDummy(): T { + pop(): T { let { previousHash, element } = this.popWitness(); let isEmpty = this.isEmpty(); @@ -104,6 +104,11 @@ class MerkleList { return Provable.if(isEmpty, provable, provable.empty(), element); } + clone(): MerkleList { + let stack = Unconstrained.witness(() => [...this.stack.get()]); + return new this.Constructor(this.hash, stack); + } + /** * Create a Merkle list type */ From ae3ac66907eb5c5eaf1805eb71f92b9039db0d10 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 12:11:17 +0100 Subject: [PATCH 272/524] finish pop account update & refactor partial call forest --- src/examples/zkapps/token/call-forest.ts | 112 ++++++++++++++--------- src/examples/zkapps/token/merkle-list.ts | 73 +++++++++------ 2 files changed, 115 insertions(+), 70 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 274d13fe8a..504618408b 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -1,7 +1,15 @@ -import { AccountUpdate, Field, Hashed, Poseidon, Provable, Struct } from 'o1js'; +import { + AccountUpdate, + Field, + Hashed, + Poseidon, + Provable, + Struct, + TokenId, +} from 'o1js'; import { MerkleList, ProvableHashable, WithStackHash } from './merkle-list.js'; -export { CallForest }; +export { CallForest, PartialCallForest }; class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => a.hash() @@ -16,15 +24,8 @@ const CallTree: ProvableHashable = Struct({ calls: WithStackHash(), }); -class CallForest extends MerkleList.create(CallTree, nextHash) { - static empty(): CallForest { - return super.empty(); - } - static from(value: WithStackHash): CallForest { - return new this(value.hash, value.stack); - } - - static fromAccountUpdates(updates: AccountUpdate[]): CallForest { +class CallForest extends MerkleList.create(CallTree, merkleListHash) { + static fromAccountUpdates(updates: AccountUpdate[]) { let forest = CallForest.empty(); for (let update of [...updates].reverse()) { @@ -35,63 +36,90 @@ class CallForest extends MerkleList.create(CallTree, nextHash) { return forest; } - - pop() { - let { accountUpdate, calls } = super.pop(); - return { accountUpdate, calls: CallForest.from(calls) }; - } } -const PendingForests = MerkleList.create(CallForest.provable, (hash, t) => - Poseidon.hash([hash, t.hash]) -); +class ForestMayUseToken extends Struct({ + forest: CallForest.provable, + mayUseToken: AccountUpdate.MayUseToken.type, +}) {} +const PendingForests = MerkleList.create(ForestMayUseToken); + +type MayUseToken = AccountUpdate['body']['mayUseToken']; +const MayUseToken = AccountUpdate.MayUseToken; class PartialCallForest { - forest: CallForest; - pendingForests: MerkleList; + current: ForestMayUseToken; + pending: MerkleList; - constructor(forest: CallForest) { - this.forest = forest; - this.pendingForests = PendingForests.empty(); + constructor(forest: CallForest, mayUseToken: MayUseToken) { + this.current = { forest, mayUseToken }; + this.pending = PendingForests.empty(); } - popAccountUpdate() { - let { accountUpdate, calls: forest } = this.forest.pop(); - let restOfForest = this.forest; + popAccountUpdate(selfToken: Field) { + // get next account update from the current forest (might be a dummy) + let { accountUpdate, calls } = this.current.forest.pop(); + let forest = new CallForest(calls); + let restOfForest = this.current.forest; - this.pendingForests.pushIf(restOfForest.isEmpty().not(), restOfForest); + this.pending.pushIf(restOfForest.notEmpty(), { + forest: restOfForest, + mayUseToken: this.current.mayUseToken, + }); - // TODO add a notion of 'current token' to partial call forest, - // or as input to this method - // TODO replace forest with empty forest if account update can't access current token + // check if this account update / it's children can use the token let update = accountUpdate.unhash(); + let canAccessThisToken = Provable.equal( + MayUseToken.type, + update.body.mayUseToken, + this.current.mayUseToken + ); + let isSelf = TokenId.derive(update.publicKey, update.tokenId).equals( + selfToken + ); + + let usesThisToken = update.tokenId + .equals(selfToken) + .and(canAccessThisToken); + + // if we don't have to check the children, replace forest with an empty one + let checkSubtree = canAccessThisToken.and(isSelf.not()); + forest = Provable.if( + checkSubtree, + CallForest.provable, + forest, + CallForest.empty() + ); + + // if the current forest is empty, switch to the next pending forest + let current = { forest, mayUseToken: MayUseToken.InheritFromParent }; let currentIsEmpty = forest.isEmpty(); - let pendingForests = this.pendingForests.clone(); - let nextForest = this.pendingForests.pop(); - let newPendingForests = this.pendingForests; + let pendingForests = this.pending.clone(); + let next = this.pending.pop(); + let nextPendingForests = this.pending; - this.forest = Provable.if( + this.current = Provable.if( currentIsEmpty, - CallForest.provable, - nextForest, - forest + ForestMayUseToken, + next, + current ); - this.pendingForests = Provable.if( + this.pending = Provable.if( currentIsEmpty, PendingForests.provable, - newPendingForests, + nextPendingForests, pendingForests ); - return update; + return { update, usesThisToken }; } } // how to hash a forest -function nextHash(forestHash: Field, tree: CallTree) { +function merkleListHash(forestHash: Field, tree: CallTree) { return hashCons(forestHash, hashNode(tree)); } diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index b79402ee9a..be02ccfa79 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -1,6 +1,7 @@ import { Bool, Field, + Poseidon, Provable, ProvableExtended, Struct, @@ -8,25 +9,15 @@ import { assert, } from 'o1js'; import { provableFromClass } from 'src/bindings/lib/provable-snarky.js'; +import { packToFields } from 'src/lib/hash.js'; export { MerkleList, WithHash, WithStackHash, emptyHash, ProvableHashable }; -type WithHash = { previousHash: Field; element: T }; -function WithHash(type: ProvableHashable): Provable> { - return Struct({ previousHash: Field, element: type }); -} - -const emptyHash = Field(0); - -type WithStackHash = { - hash: Field; - stack: Unconstrained[]>; -}; -function WithStackHash(): ProvableExtended> { - return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { - static empty(): WithStackHash { - return { hash: emptyHash, stack: Unconstrained.from([]) }; - } +function merkleListHash(provable: ProvableHashable, prefix = '') { + return function nextHash(hash: Field, value: T) { + let input = provable.toInput(value); + let packed = packToFields(input); + return Poseidon.hashWithPrefix(prefix, [hash, ...packed]); }; } @@ -34,17 +25,16 @@ class MerkleList { hash: Field; stack: Unconstrained[]>; - constructor(hash: Field, value: Unconstrained[]>) { + constructor({ hash, stack }: WithStackHash) { this.hash = hash; - this.stack = value; + this.stack = stack; } isEmpty() { return this.hash.equals(emptyHash); } - - static empty(): MerkleList { - return new this(emptyHash, Unconstrained.from([])); + notEmpty() { + return this.hash.equals(emptyHash).not(); } push(element: T) { @@ -106,7 +96,7 @@ class MerkleList { clone(): MerkleList { let stack = Unconstrained.witness(() => [...this.stack.get()]); - return new this.Constructor(this.hash, stack); + return new this.Constructor({ hash: this.hash, stack }); } /** @@ -114,8 +104,11 @@ class MerkleList { */ static create( type: ProvableHashable, - nextHash: (hash: Field, t: T) => Field - ): typeof MerkleList { + nextHash: (hash: Field, value: T) => Field = merkleListHash(type) + ): typeof MerkleList & { + empty: () => MerkleList; + provable: ProvableHashable>; + } { return class MerkleList_ extends MerkleList { static _innerProvable = type; @@ -125,6 +118,15 @@ class MerkleList { }) as ProvableHashable>; static _nextHash = nextHash; + + static empty(): MerkleList { + return new this({ hash: emptyHash, stack: Unconstrained.from([]) }); + } + + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'MerkleList not initialized'); + return this._provable; + } }; } @@ -146,10 +148,6 @@ class MerkleList { return this.Constructor._nextHash(hash, t); } - static get provable(): ProvableHashable> { - assert(this._provable !== undefined, 'MerkleList not initialized'); - return this._provable; - } get innerProvable(): ProvableHashable { assert( this.Constructor._innerProvable !== undefined, @@ -159,6 +157,25 @@ class MerkleList { } } +type WithHash = { previousHash: Field; element: T }; +function WithHash(type: ProvableHashable): Provable> { + return Struct({ previousHash: Field, element: type }); +} + +const emptyHash = Field(0); + +type WithStackHash = { + hash: Field; + stack: Unconstrained[]>; +}; +function WithStackHash(): ProvableExtended> { + return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { + static empty(): WithStackHash { + return { hash: emptyHash, stack: Unconstrained.from([]) }; + } + }; +} + type HashInput = { fields?: Field[]; packed?: [Field, number][] }; type ProvableHashable = Provable & { toInput: (x: T) => HashInput; From c6b7f6cf3ac40578a07d65426d55c64704da0d54 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 12:25:59 +0100 Subject: [PATCH 273/524] improve variable naming --- src/examples/zkapps/token/call-forest.ts | 50 ++++++++++++------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 504618408b..34a845391d 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -38,33 +38,33 @@ class CallForest extends MerkleList.create(CallTree, merkleListHash) { } } -class ForestMayUseToken extends Struct({ +class Layer extends Struct({ forest: CallForest.provable, mayUseToken: AccountUpdate.MayUseToken.type, }) {} -const PendingForests = MerkleList.create(ForestMayUseToken); +const ParentLayers = MerkleList.create(Layer); type MayUseToken = AccountUpdate['body']['mayUseToken']; const MayUseToken = AccountUpdate.MayUseToken; class PartialCallForest { - current: ForestMayUseToken; - pending: MerkleList; + currentLayer: Layer; + nonEmptyParentLayers: MerkleList; constructor(forest: CallForest, mayUseToken: MayUseToken) { - this.current = { forest, mayUseToken }; - this.pending = PendingForests.empty(); + this.currentLayer = { forest, mayUseToken }; + this.nonEmptyParentLayers = ParentLayers.empty(); } popAccountUpdate(selfToken: Field) { // get next account update from the current forest (might be a dummy) - let { accountUpdate, calls } = this.current.forest.pop(); + let { accountUpdate, calls } = this.currentLayer.forest.pop(); let forest = new CallForest(calls); - let restOfForest = this.current.forest; + let restOfForest = this.currentLayer.forest; - this.pending.pushIf(restOfForest.notEmpty(), { + this.nonEmptyParentLayers.pushIf(restOfForest.notEmpty(), { forest: restOfForest, - mayUseToken: this.current.mayUseToken, + mayUseToken: this.currentLayer.mayUseToken, }); // check if this account update / it's children can use the token @@ -73,7 +73,7 @@ class PartialCallForest { let canAccessThisToken = Provable.equal( MayUseToken.type, update.body.mayUseToken, - this.current.mayUseToken + this.currentLayer.mayUseToken ); let isSelf = TokenId.derive(update.publicKey, update.tokenId).equals( selfToken @@ -92,25 +92,27 @@ class PartialCallForest { CallForest.empty() ); - // if the current forest is empty, switch to the next pending forest - let current = { forest, mayUseToken: MayUseToken.InheritFromParent }; + // if the current forest is empty, step up to the next non-empty parent layer + // invariant: the current layer will _never_ be empty _except_ at the point where we stepped + // through the entire forest and there are no remaining parent layers + let currentLayer = { forest, mayUseToken: MayUseToken.InheritFromParent }; let currentIsEmpty = forest.isEmpty(); - let pendingForests = this.pending.clone(); - let next = this.pending.pop(); - let nextPendingForests = this.pending; + let parentLayers = this.nonEmptyParentLayers.clone(); + let nextParentLayer = this.nonEmptyParentLayers.pop(); + let parentLayersIfSteppingUp = this.nonEmptyParentLayers; - this.current = Provable.if( + this.currentLayer = Provable.if( currentIsEmpty, - ForestMayUseToken, - next, - current + Layer, + nextParentLayer, + currentLayer ); - this.pending = Provable.if( + this.nonEmptyParentLayers = Provable.if( currentIsEmpty, - PendingForests.provable, - nextPendingForests, - pendingForests + ParentLayers.provable, + parentLayersIfSteppingUp, + parentLayers ); return { update, usesThisToken }; From 6a76e43e25e992133f17d7206b9f107039746f05 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 13:49:04 +0100 Subject: [PATCH 274/524] merkle array type --- src/examples/zkapps/token/merkle-list.ts | 165 ++++++++++++++++++++++- src/lib/circuit_value.ts | 7 + src/lib/provable.ts | 2 - 3 files changed, 171 insertions(+), 3 deletions(-) diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index be02ccfa79..3e0ecf7122 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -11,7 +11,14 @@ import { import { provableFromClass } from 'src/bindings/lib/provable-snarky.js'; import { packToFields } from 'src/lib/hash.js'; -export { MerkleList, WithHash, WithStackHash, emptyHash, ProvableHashable }; +export { + MerkleArray, + MerkleList, + WithHash, + WithStackHash, + emptyHash, + ProvableHashable, +}; function merkleListHash(provable: ProvableHashable, prefix = '') { return function nextHash(hash: Field, value: T) { @@ -181,3 +188,159 @@ type ProvableHashable = Provable & { toInput: (x: T) => HashInput; empty: () => T; }; + +// merkle array + +type MerkleArrayBase = { + readonly array: Unconstrained[]>; + readonly fullHash: Field; + + currentHash: Field; + currentIndex: Unconstrained; +}; + +/** + * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, + * instead of needlessly mutating itself / throwing away context while stepping through it. + * + * We maintain two commitments, both of which are equivalent to a Merkle list hash starting _from the end_ of the array: + * - One to the entire array, to prove that we start iterating at the beginning. + * - One to the array from the current index until the end, to efficiently step forward. + */ +class MerkleArray implements MerkleArrayBase { + // fixed parts + readonly array: Unconstrained[]>; + readonly fullHash: Field; + + // mutable parts + currentHash: Field; + currentIndex: Unconstrained; + + constructor(value: MerkleArrayBase) { + Object.assign(this, value); + } + + isEmpty() { + return this.fullHash.equals(emptyHash); + } + isAtEnd() { + return this.currentHash.equals(emptyHash); + } + assertAtStart() { + return this.currentHash.assertEquals(this.fullHash); + } + + next() { + // next corresponds to `pop()` in MerkleList + // it returns a dummy element if we're at the end of the array + let index = Unconstrained.witness(() => this.currentIndex.get() + 1); + + let { previousHash, element } = Provable.witness( + WithHash(this.innerProvable), + () => + this.array.get()[index.get()] ?? { + previousHash: this.fullHash, + element: this.innerProvable.empty(), + } + ); + + let isDummy = this.isAtEnd(); + let correctHash = this.nextHash(previousHash, element); + let requiredHash = Provable.if(isDummy, emptyHash, correctHash); + this.currentHash.assertEquals(requiredHash); + + this.currentIndex.setTo(index); + this.currentHash = Provable.if(isDummy, emptyHash, previousHash); + + return Provable.if( + isDummy, + this.innerProvable, + this.innerProvable.empty(), + element + ); + } + + clone(): MerkleArray { + let array = Unconstrained.witness(() => [...this.array.get()]); + let currentIndex = Unconstrained.witness(() => this.currentIndex.get()); + return new this.Constructor({ + array, + fullHash: this.fullHash, + currentHash: this.currentHash, + currentIndex, + }); + } + + /** + * Create a Merkle list type + */ + static create( + type: ProvableHashable, + nextHash: (hash: Field, value: T) => Field = merkleListHash(type) + ): typeof MerkleArray & { + from: (array: T[]) => MerkleArray; + provable: ProvableHashable>; + } { + return class MerkleArray_ extends MerkleArray { + static _innerProvable = type; + + static _provable = provableFromClass(MerkleArray_, { + array: Unconstrained.provable, + fullHash: Field, + currentHash: Field, + currentIndex: Unconstrained.provable, + }) as ProvableHashable>; + + static _nextHash = nextHash; + + static from(array: T[]): MerkleArray { + let n = array.length; + let arrayWithHashes = Array>(n); + let currentHash = emptyHash; + + for (let i = n - 1; i >= 0; i--) { + arrayWithHashes[i] = { previousHash: currentHash, element: array[i] }; + currentHash = nextHash(currentHash, array[i]); + } + + return new this({ + array: Unconstrained.from(arrayWithHashes), + fullHash: currentHash, + currentHash: currentHash, + currentIndex: Unconstrained.from(0), + }); + } + + static get provable(): ProvableHashable> { + assert(this._provable !== undefined, 'MerkleArray not initialized'); + return this._provable; + } + }; + } + + // dynamic subclassing infra + static _nextHash: ((hash: Field, t: any) => Field) | undefined; + + static _provable: ProvableHashable> | undefined; + static _innerProvable: ProvableHashable | undefined; + + get Constructor() { + return this.constructor as typeof MerkleArray; + } + + nextHash(hash: Field, t: T): Field { + assert( + this.Constructor._nextHash !== undefined, + 'MerkleArray not initialized' + ); + return this.Constructor._nextHash(hash, t); + } + + get innerProvable(): ProvableHashable { + assert( + this.Constructor._innerProvable !== undefined, + 'MerkleArray not initialized' + ); + return this.Constructor._innerProvable; + } +} diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 6720c343cb..adccc9a249 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -538,6 +538,13 @@ and Provable.asProver() blocks, which execute outside the proof. this.option = { isSome: true, value }; } + /** + * Set the unconstrained value to the same as another `Unconstrained`. + */ + setTo(value: Unconstrained) { + this.option = value.option; + } + /** * Create an `Unconstrained` with the given `value`. */ diff --git a/src/lib/provable.ts b/src/lib/provable.ts index 8094bcf89e..961f723451 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -341,8 +341,6 @@ function ifImplicit(condition: Bool, x: T, y: T): T { `If x, y are Structs or other custom types, you can use the following:\n` + `Provable.if(bool, MyType, x, y)` ); - // TODO remove second condition once we have consolidated field class back into one - // if (type !== y.constructor) { if (type !== y.constructor) { throw Error( 'Provable.if: Mismatched argument types. Try using an explicit type argument:\n' + From e096d3c156418372ab587c14db44e2ae6adfefea Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 14:28:00 +0100 Subject: [PATCH 275/524] make call forest a merkle array, iteration --- src/examples/zkapps/token/call-forest.ts | 72 ++++++++++--------- src/examples/zkapps/token/merkle-list.ts | 89 ++++++++++++++++++++---- 2 files changed, 109 insertions(+), 52 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 34a845391d..938cb5e1b1 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -7,7 +7,12 @@ import { Struct, TokenId, } from 'o1js'; -import { MerkleList, ProvableHashable, WithStackHash } from './merkle-list.js'; +import { + MerkleArray, + MerkleArrayBase, + MerkleList, + ProvableHashable, +} from './merkle-list.js'; export { CallForest, PartialCallForest }; @@ -17,24 +22,22 @@ class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => type CallTree = { accountUpdate: Hashed; - calls: WithStackHash; + calls: MerkleArrayBase; }; const CallTree: ProvableHashable = Struct({ accountUpdate: HashedAccountUpdate.provable, - calls: WithStackHash(), + calls: MerkleArrayBase(), }); -class CallForest extends MerkleList.create(CallTree, merkleListHash) { - static fromAccountUpdates(updates: AccountUpdate[]) { - let forest = CallForest.empty(); - - for (let update of [...updates].reverse()) { +class CallForest extends MerkleArray.create(CallTree, merkleListHash) { + static fromAccountUpdates(updates: AccountUpdate[]): CallForest { + let nodes = updates.map((update) => { let accountUpdate = HashedAccountUpdate.hash(update); let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); - forest.push({ accountUpdate, calls }); - } + return { accountUpdate, calls }; + }); - return forest; + return CallForest.from(nodes); } } @@ -49,21 +52,21 @@ const MayUseToken = AccountUpdate.MayUseToken; class PartialCallForest { currentLayer: Layer; - nonEmptyParentLayers: MerkleList; + unfinishedParentLayers: MerkleList; constructor(forest: CallForest, mayUseToken: MayUseToken) { this.currentLayer = { forest, mayUseToken }; - this.nonEmptyParentLayers = ParentLayers.empty(); + this.unfinishedParentLayers = ParentLayers.empty(); } - popAccountUpdate(selfToken: Field) { + nextAccountUpdate(selfToken: Field) { // get next account update from the current forest (might be a dummy) - let { accountUpdate, calls } = this.currentLayer.forest.pop(); - let forest = new CallForest(calls); - let restOfForest = this.currentLayer.forest; + let { accountUpdate, calls } = this.currentLayer.forest.next(); + let forest = CallForest.startIterating(calls); + let parentLayer = this.currentLayer.forest; - this.nonEmptyParentLayers.pushIf(restOfForest.notEmpty(), { - forest: restOfForest, + this.unfinishedParentLayers.pushIf(parentLayer.isAtEnd(), { + forest: parentLayer, mayUseToken: this.currentLayer.mayUseToken, }); @@ -83,33 +86,28 @@ class PartialCallForest { .equals(selfToken) .and(canAccessThisToken); - // if we don't have to check the children, replace forest with an empty one - let checkSubtree = canAccessThisToken.and(isSelf.not()); - forest = Provable.if( - checkSubtree, - CallForest.provable, - forest, - CallForest.empty() - ); + // if we don't have to check the children, ignore the forest by jumping to its end + let skipSubtree = canAccessThisToken.not().or(isSelf); + forest.jumpToEndIf(skipSubtree); - // if the current forest is empty, step up to the next non-empty parent layer - // invariant: the current layer will _never_ be empty _except_ at the point where we stepped - // through the entire forest and there are no remaining parent layers + // if we're at the end of the current layer, step up to the next unfinished parent layer + // invariant: the new current layer will _never_ be finished _except_ at the point where we stepped + // through the entire forest and there are no remaining parent layers to finish let currentLayer = { forest, mayUseToken: MayUseToken.InheritFromParent }; - let currentIsEmpty = forest.isEmpty(); + let currentIsFinished = forest.isAtEnd(); - let parentLayers = this.nonEmptyParentLayers.clone(); - let nextParentLayer = this.nonEmptyParentLayers.pop(); - let parentLayersIfSteppingUp = this.nonEmptyParentLayers; + let parentLayers = this.unfinishedParentLayers.clone(); + let nextParentLayer = this.unfinishedParentLayers.pop(); + let parentLayersIfSteppingUp = this.unfinishedParentLayers; this.currentLayer = Provable.if( - currentIsEmpty, + currentIsFinished, Layer, nextParentLayer, currentLayer ); - this.nonEmptyParentLayers = Provable.if( - currentIsEmpty, + this.unfinishedParentLayers = Provable.if( + currentIsFinished, ParentLayers.provable, parentLayersIfSteppingUp, parentLayers diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/examples/zkapps/token/merkle-list.ts index 3e0ecf7122..a82c4f0e56 100644 --- a/src/examples/zkapps/token/merkle-list.ts +++ b/src/examples/zkapps/token/merkle-list.ts @@ -3,7 +3,6 @@ import { Field, Poseidon, Provable, - ProvableExtended, Struct, Unconstrained, assert, @@ -13,6 +12,8 @@ import { packToFields } from 'src/lib/hash.js'; export { MerkleArray, + MerkleArrayIterator, + MerkleArrayBase, MerkleList, WithHash, WithStackHash, @@ -175,7 +176,7 @@ type WithStackHash = { hash: Field; stack: Unconstrained[]>; }; -function WithStackHash(): ProvableExtended> { +function WithStackHash(): ProvableHashable> { return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { static empty(): WithStackHash { return { hash: emptyHash, stack: Unconstrained.from([]) }; @@ -193,12 +194,43 @@ type ProvableHashable = Provable & { type MerkleArrayBase = { readonly array: Unconstrained[]>; - readonly fullHash: Field; + readonly hash: Field; +}; + +function MerkleArrayBase(): ProvableHashable> { + return class extends Struct({ array: Unconstrained.provable, hash: Field }) { + static empty(): MerkleArrayBase { + return { array: Unconstrained.from([]), hash: emptyHash }; + } + }; +} + +type MerkleArrayIterator = { + readonly array: Unconstrained[]>; + readonly hash: Field; currentHash: Field; currentIndex: Unconstrained; }; +function MerkleArrayIterator(): ProvableHashable> { + return class extends Struct({ + array: Unconstrained.provable, + hash: Field, + currentHash: Field, + currentIndex: Unconstrained.provable, + }) { + static empty(): MerkleArrayIterator { + return { + array: Unconstrained.from([]), + hash: emptyHash, + currentHash: emptyHash, + currentIndex: Unconstrained.from(0), + }; + } + }; +} + /** * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, * instead of needlessly mutating itself / throwing away context while stepping through it. @@ -207,27 +239,47 @@ type MerkleArrayBase = { * - One to the entire array, to prove that we start iterating at the beginning. * - One to the array from the current index until the end, to efficiently step forward. */ -class MerkleArray implements MerkleArrayBase { +class MerkleArray implements MerkleArrayIterator { // fixed parts readonly array: Unconstrained[]>; - readonly fullHash: Field; + readonly hash: Field; // mutable parts currentHash: Field; currentIndex: Unconstrained; - constructor(value: MerkleArrayBase) { + constructor(value: MerkleArrayIterator) { Object.assign(this, value); } - isEmpty() { - return this.fullHash.equals(emptyHash); + static startIterating({ array, hash }: MerkleArrayBase) { + return new this({ + array, + hash, + currentHash: hash, + currentIndex: Unconstrained.from(0), + }); + } + assertAtStart() { + return this.currentHash.assertEquals(this.hash); } + isAtEnd() { return this.currentHash.equals(emptyHash); } - assertAtStart() { - return this.currentHash.assertEquals(this.fullHash); + jumpToEnd() { + this.currentIndex.setTo( + Unconstrained.witness(() => this.array.get().length) + ); + this.currentHash = emptyHash; + } + jumpToEndIf(condition: Bool) { + Provable.asProver(() => { + if (condition.toBoolean()) { + this.currentIndex.set(this.array.get().length); + } + }); + this.currentHash = Provable.if(condition, emptyHash, this.currentHash); } next() { @@ -239,7 +291,7 @@ class MerkleArray implements MerkleArrayBase { WithHash(this.innerProvable), () => this.array.get()[index.get()] ?? { - previousHash: this.fullHash, + previousHash: this.hash, element: this.innerProvable.empty(), } ); @@ -265,7 +317,7 @@ class MerkleArray implements MerkleArrayBase { let currentIndex = Unconstrained.witness(() => this.currentIndex.get()); return new this.Constructor({ array, - fullHash: this.fullHash, + hash: this.hash, currentHash: this.currentHash, currentIndex, }); @@ -279,6 +331,7 @@ class MerkleArray implements MerkleArrayBase { nextHash: (hash: Field, value: T) => Field = merkleListHash(type) ): typeof MerkleArray & { from: (array: T[]) => MerkleArray; + empty: () => MerkleArray; provable: ProvableHashable>; } { return class MerkleArray_ extends MerkleArray { @@ -286,10 +339,12 @@ class MerkleArray implements MerkleArrayBase { static _provable = provableFromClass(MerkleArray_, { array: Unconstrained.provable, - fullHash: Field, + hash: Field, currentHash: Field, currentIndex: Unconstrained.provable, - }) as ProvableHashable>; + }) satisfies ProvableHashable> as ProvableHashable< + MerkleArray + >; static _nextHash = nextHash; @@ -305,12 +360,16 @@ class MerkleArray implements MerkleArrayBase { return new this({ array: Unconstrained.from(arrayWithHashes), - fullHash: currentHash, + hash: currentHash, currentHash: currentHash, currentIndex: Unconstrained.from(0), }); } + static empty(): MerkleArray { + return this.from([]); + } + static get provable(): ProvableHashable> { assert(this._provable !== undefined, 'MerkleArray not initialized'); return this._provable; From fa64041ea49bd9ea9db930e933c9ac5bc891c214 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 16:11:06 +0100 Subject: [PATCH 276/524] tweaks, doccomments --- src/examples/zkapps/token/call-forest.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 938cb5e1b1..84e6320a22 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -59,14 +59,24 @@ class PartialCallForest { this.unfinishedParentLayers = ParentLayers.empty(); } + /** + * Make a single step through a tree of account updates. + * + * This function will visit each account update in the tree exactly once when called repeatedly, + * and the internal state of `PartialCallForest` represents the work still to be done. + * + * Makes a best effort to avoid visiting account updates that are not using the token and in particular, to avoid returning dummy updates + * -- but both can't be ruled out, so we're returning { update, usesThisToken } and let the caller handle the irrelevant case. + */ nextAccountUpdate(selfToken: Field) { // get next account update from the current forest (might be a dummy) + // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); let forest = CallForest.startIterating(calls); - let parentLayer = this.currentLayer.forest; + let parentForest = this.currentLayer.forest; - this.unfinishedParentLayers.pushIf(parentLayer.isAtEnd(), { - forest: parentLayer, + this.unfinishedParentLayers.pushIf(parentForest.isAtEnd().not(), { + forest: parentForest, mayUseToken: this.currentLayer.mayUseToken, }); From 7576435e2a1edbdf07642c0ad0dff2ad6e03d382 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 16:16:07 +0100 Subject: [PATCH 277/524] improve comment --- src/examples/zkapps/token/call-forest.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/examples/zkapps/token/call-forest.ts index 84e6320a22..090ed664dd 100644 --- a/src/examples/zkapps/token/call-forest.ts +++ b/src/examples/zkapps/token/call-forest.ts @@ -60,13 +60,18 @@ class PartialCallForest { } /** - * Make a single step through a tree of account updates. + * Make a single step along a tree of account updates. * - * This function will visit each account update in the tree exactly once when called repeatedly, - * and the internal state of `PartialCallForest` represents the work still to be done. + * This function is guaranteed to visit each account update in the tree that uses the token + * exactly once, when called repeatedly. * - * Makes a best effort to avoid visiting account updates that are not using the token and in particular, to avoid returning dummy updates - * -- but both can't be ruled out, so we're returning { update, usesThisToken } and let the caller handle the irrelevant case. + * The internal state of `PartialCallForest` represents the work still to be done, and + * can be passed from one proof to the next. + * + * The method makes a best effort to avoid visiting account updates that are not using the token, + * and in particular, to avoid returning dummy updates. + * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the + * caller handle the irrelevant case where `usesThisToken` is false. */ nextAccountUpdate(selfToken: Field) { // get next account update from the current forest (might be a dummy) From 482007d806c3842994e17fb2ade00cc0f3994299 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 16:31:09 +0100 Subject: [PATCH 278/524] move files for now --- src/{examples/zkapps => lib/mina}/token/call-forest.ts | 0 src/{examples/zkapps => lib/mina}/token/merkle-list.ts | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{examples/zkapps => lib/mina}/token/call-forest.ts (100%) rename src/{examples/zkapps => lib/mina}/token/merkle-list.ts (100%) diff --git a/src/examples/zkapps/token/call-forest.ts b/src/lib/mina/token/call-forest.ts similarity index 100% rename from src/examples/zkapps/token/call-forest.ts rename to src/lib/mina/token/call-forest.ts diff --git a/src/examples/zkapps/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts similarity index 100% rename from src/examples/zkapps/token/merkle-list.ts rename to src/lib/mina/token/merkle-list.ts From f679c156697a37c63e5493e090f567aaeefb43d9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 17:14:55 +0100 Subject: [PATCH 279/524] start creating test --- src/lib/mina/token/call-forest.ts | 17 +++++++-- src/lib/mina/token/call-forest.unit-test.ts | 40 +++++++++++++++++++++ src/lib/mina/token/merkle-list.ts | 15 ++++++-- src/lib/testing/random.ts | 3 +- 4 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 src/lib/mina/token/call-forest.unit-test.ts diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 090ed664dd..d5e00897ae 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,3 +1,4 @@ +import { prefixes } from '../../../provable/poseidon-bigint.js'; import { AccountUpdate, Field, @@ -6,18 +7,20 @@ import { Provable, Struct, TokenId, -} from 'o1js'; +} from '../../../index.js'; import { MerkleArray, MerkleArrayBase, MerkleList, ProvableHashable, + genericHash, } from './merkle-list.js'; export { CallForest, PartialCallForest }; -class HashedAccountUpdate extends Hashed.create(AccountUpdate, (a) => - a.hash() +class HashedAccountUpdate extends Hashed.create( + AccountUpdate, + hashAccountUpdate ) {} type CallTree = { @@ -59,6 +62,10 @@ class PartialCallForest { this.unfinishedParentLayers = ParentLayers.empty(); } + static create(forest: CallForest) { + return new PartialCallForest(forest, MayUseToken.ParentsOwnToken); + } + /** * Make a single step along a tree of account updates. * @@ -150,3 +157,7 @@ function hashCons(forestHash: Field, nodeHash: Field) { nodeHash, ]); } + +function hashAccountUpdate(update: AccountUpdate) { + return genericHash(AccountUpdate, prefixes.body, update); +} diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts new file mode 100644 index 0000000000..73521b60a9 --- /dev/null +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -0,0 +1,40 @@ +import { Random, test } from '../../testing/property.js'; +import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; +import { CallForest, PartialCallForest } from './call-forest.js'; +import { AccountUpdate, ZkappCommand } from '../../account_update.js'; +import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; +import { Pickles } from '../../../snarky.js'; +import { Field } from '../../../lib/core.js'; + +// rng for account updates + +let [, data, hashMl] = Pickles.dummyVerificationKey(); +let verificationKey = { data, hash: Field(hashMl) }; + +const accountUpdates = Random.map( + RandomTransaction.zkappCommand, + (txBigint) => { + // bigint to json, then to provable + let txJson = TypesBigint.ZkappCommand.toJSON(txBigint); + + let accountUpdates = txJson.accountUpdates.map((aJson) => { + let a = AccountUpdate.fromJSON(aJson); + + // fix verification key + if (a.body.update.verificationKey.isSome) { + a.body.update.verificationKey.value = verificationKey; + } + return a; + }); + + return accountUpdates; + } +); + +// tests begin here + +test.custom({ timeBudget: 10000 })(accountUpdates, (updates) => { + console.log({ length: updates.length }); + + CallForest.fromAccountUpdates(updates); +}); diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index a82c4f0e56..edd1577079 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -7,8 +7,8 @@ import { Unconstrained, assert, } from 'o1js'; -import { provableFromClass } from 'src/bindings/lib/provable-snarky.js'; -import { packToFields } from 'src/lib/hash.js'; +import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; +import { packToFields } from '../../hash.js'; export { MerkleArray, @@ -19,8 +19,19 @@ export { WithStackHash, emptyHash, ProvableHashable, + genericHash, }; +function genericHash( + provable: ProvableHashable, + prefix: string, + value: T +) { + let input = provable.toInput(value); + let packed = packToFields(input); + return Poseidon.hashWithPrefix(prefix, packed); +} + function merkleListHash(provable: ProvableHashable, prefix = '') { return function nextHash(hash: Field, value: T) { let input = provable.toInput(value); diff --git a/src/lib/testing/random.ts b/src/lib/testing/random.ts index 358b0ac206..12b82802ff 100644 --- a/src/lib/testing/random.ts +++ b/src/lib/testing/random.ts @@ -162,7 +162,8 @@ const accountUpdate = mapWithInvalid( a.body.authorizationKind.isProved = Bool(false); } if (!a.body.authorizationKind.isProved) { - a.body.authorizationKind.verificationKeyHash = Field(0); + a.body.authorizationKind.verificationKeyHash = + VerificationKeyHash.empty(); } // ensure mayUseToken is valid let { inheritFromParent, parentsOwnToken } = a.body.mayUseToken; From 342705f91f19667eebec631d3b306438ad9b70e7 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 17:39:58 +0100 Subject: [PATCH 280/524] compute stack hash that is equivalent between provable & mins-aigner, but not yet the new implementation --- src/lib/mina/token/call-forest.unit-test.ts | 73 +++++++++++++++++---- src/mina-signer/src/sign-zkapp-command.ts | 1 + 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 73521b60a9..9387ff8ba6 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,40 +1,85 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; import { CallForest, PartialCallForest } from './call-forest.js'; -import { AccountUpdate, ZkappCommand } from '../../account_update.js'; +import { + AccountUpdate, + CallForest as ProvableCallForest, +} from '../../account_update.js'; import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; import { Pickles } from '../../../snarky.js'; -import { Field } from '../../../lib/core.js'; +import { + accountUpdatesToCallForest, + callForestHash, + CallForest as SimpleCallForest, +} from '../../../mina-signer/src/sign-zkapp-command.js'; // rng for account updates let [, data, hashMl] = Pickles.dummyVerificationKey(); -let verificationKey = { data, hash: Field(hashMl) }; +let verificationKey = { data, hash: hashMl[1] }; -const accountUpdates = Random.map( +const callForest: Random = Random.map( RandomTransaction.zkappCommand, (txBigint) => { - // bigint to json, then to provable - let txJson = TypesBigint.ZkappCommand.toJSON(txBigint); - - let accountUpdates = txJson.accountUpdates.map((aJson) => { - let a = AccountUpdate.fromJSON(aJson); - + let flatUpdates = txBigint.accountUpdates.map((a) => { // fix verification key if (a.body.update.verificationKey.isSome) { a.body.update.verificationKey.value = verificationKey; } return a; }); - - return accountUpdates; + return accountUpdatesToCallForest(flatUpdates); } ); // tests begin here -test.custom({ timeBudget: 10000 })(accountUpdates, (updates) => { +test.custom({ timeBudget: 10000 })(callForest, (forestBigint) => { + // reference: bigint callforest hash from mina-signer + let stackHash = callForestHash(forestBigint); + + let updates = callForestToNestedArray( + mapCallForest(forestBigint, accountUpdateFromBigint) + ); + console.log({ length: updates.length }); - CallForest.fromAccountUpdates(updates); + let dummyParent = AccountUpdate.dummy(); + dummyParent.children.accountUpdates = updates; + + let hash = ProvableCallForest.hashChildren(dummyParent); + hash.assertEquals(stackHash); + + let forest = CallForest.fromAccountUpdates(updates); }); + +// call forest helpers + +type AbstractSimpleCallForest = { + accountUpdate: A; + children: AbstractSimpleCallForest; +}[]; + +function mapCallForest( + forest: AbstractSimpleCallForest, + mapOne: (a: A) => B +): AbstractSimpleCallForest { + return forest.map(({ accountUpdate, children }) => ({ + accountUpdate: mapOne(accountUpdate), + children: mapCallForest(children, mapOne), + })); +} + +function accountUpdateFromBigint(a: TypesBigint.AccountUpdate): AccountUpdate { + // bigint to json, then to provable + return AccountUpdate.fromJSON(TypesBigint.AccountUpdate.toJSON(a)); +} + +function callForestToNestedArray( + forest: AbstractSimpleCallForest +): AccountUpdate[] { + return forest.map(({ accountUpdate, children }) => { + accountUpdate.children.accountUpdates = callForestToNestedArray(children); + return accountUpdate; + }); +} diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 7644c2ee32..4daea29443 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -33,6 +33,7 @@ export { createFeePayer, accountUpdateFromFeePayer, isCallDepthValid, + CallForest, }; function signZkappCommand( From 895dd71cd9694d2a4e7a80201dae71caccc01716 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 17:48:00 +0100 Subject: [PATCH 281/524] test on wider/deeper trees --- src/lib/mina/token/call-forest.unit-test.ts | 13 ++++++++++--- src/mina-signer/src/random-transaction.ts | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 9387ff8ba6..2d06732feb 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -18,16 +18,23 @@ import { let [, data, hashMl] = Pickles.dummyVerificationKey(); let verificationKey = { data, hash: hashMl[1] }; +let accountUpdates = Random.array( + RandomTransaction.accountUpdateWithCallDepth, + Random.int(0, 50), + { reset: true } +); + const callForest: Random = Random.map( - RandomTransaction.zkappCommand, - (txBigint) => { - let flatUpdates = txBigint.accountUpdates.map((a) => { + accountUpdates, + (accountUpdates) => { + let flatUpdates = accountUpdates.map((a) => { // fix verification key if (a.body.update.verificationKey.isSome) { a.body.update.verificationKey.value = verificationKey; } return a; }); + console.log({ totalLength: flatUpdates.length }); return accountUpdatesToCallForest(flatUpdates); } ); diff --git a/src/mina-signer/src/random-transaction.ts b/src/mina-signer/src/random-transaction.ts index 8b58405ae3..f75c7ae93b 100644 --- a/src/mina-signer/src/random-transaction.ts +++ b/src/mina-signer/src/random-transaction.ts @@ -142,4 +142,5 @@ const RandomTransaction = { zkappCommandAndFeePayerKey, zkappCommandJson, networkId: Random.oneOf('testnet', 'mainnet'), + accountUpdateWithCallDepth: accountUpdate, }; From 7372293b81fc4638e5bbb0b4bdf6ee937908002a Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 23 Jan 2024 21:38:10 +0100 Subject: [PATCH 282/524] debugging --- src/lib/mina/token/call-forest.ts | 2 ++ src/lib/mina/token/call-forest.unit-test.ts | 40 +++++++++++++-------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index d5e00897ae..6354199c37 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -18,6 +18,8 @@ import { export { CallForest, PartialCallForest }; +export { HashedAccountUpdate }; + class HashedAccountUpdate extends Hashed.create( AccountUpdate, hashAccountUpdate diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 2d06732feb..538f101344 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,6 +1,6 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { CallForest, PartialCallForest } from './call-forest.js'; +import { CallForest, HashedAccountUpdate } from './call-forest.js'; import { AccountUpdate, CallForest as ProvableCallForest, @@ -41,24 +41,36 @@ const callForest: Random = Random.map( // tests begin here -test.custom({ timeBudget: 10000 })(callForest, (forestBigint) => { - // reference: bigint callforest hash from mina-signer - let stackHash = callForestHash(forestBigint); +test.custom({ timeBudget: 10000, logFailures: false })( + callForest, + (forestBigint) => { + // reference: bigint callforest hash from mina-signer + let stackHash = callForestHash(forestBigint); - let updates = callForestToNestedArray( - mapCallForest(forestBigint, accountUpdateFromBigint) - ); + let updates = callForestToNestedArray( + mapCallForest(forestBigint, accountUpdateFromBigint) + ); - console.log({ length: updates.length }); + console.log({ length: updates.length }); - let dummyParent = AccountUpdate.dummy(); - dummyParent.children.accountUpdates = updates; + let dummyParent = AccountUpdate.dummy(); + dummyParent.children.accountUpdates = updates; - let hash = ProvableCallForest.hashChildren(dummyParent); - hash.assertEquals(stackHash); + let hash = ProvableCallForest.hashChildren(dummyParent); + hash.assertEquals(stackHash); - let forest = CallForest.fromAccountUpdates(updates); -}); + let nodes = updates.map((update) => { + let accountUpdate = HashedAccountUpdate.hash(update); + let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); + return { accountUpdate, calls }; + }); + + console.log({ nodes: nodes.map((n) => n.calls.hash.toBigInt()) }); + + let forest = CallForest.fromAccountUpdates(updates); + forest.hash.assertEquals(stackHash); + } +); // call forest helpers From 12d7449e97eb5a1647835a09610c62e1c8d210a6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 10:12:13 +0100 Subject: [PATCH 283/524] fix test by fixing order in hashCons --- src/lib/mina/token/call-forest.ts | 6 ++--- src/lib/mina/token/call-forest.unit-test.ts | 27 ++++++--------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 6354199c37..9a4a540388 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -148,15 +148,15 @@ function merkleListHash(forestHash: Field, tree: CallTree) { } function hashNode(tree: CallTree) { - return Poseidon.hashWithPrefix('MinaAcctUpdateNode**', [ + return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ tree.accountUpdate.hash, tree.calls.hash, ]); } function hashCons(forestHash: Field, nodeHash: Field) { - return Poseidon.hashWithPrefix('MinaAcctUpdateCons**', [ - forestHash, + return Poseidon.hashWithPrefix(prefixes.accountUpdateCons, [ nodeHash, + forestHash, ]); } diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 538f101344..782a4e7d9e 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -39,40 +39,27 @@ const callForest: Random = Random.map( } ); -// tests begin here +// TESTS + +// correctly hashes a call forest test.custom({ timeBudget: 10000, logFailures: false })( callForest, (forestBigint) => { // reference: bigint callforest hash from mina-signer - let stackHash = callForestHash(forestBigint); + let expectedHash = callForestHash(forestBigint); + // convert to o1js-style list of nested `AccountUpdate`s let updates = callForestToNestedArray( mapCallForest(forestBigint, accountUpdateFromBigint) ); - console.log({ length: updates.length }); - - let dummyParent = AccountUpdate.dummy(); - dummyParent.children.accountUpdates = updates; - - let hash = ProvableCallForest.hashChildren(dummyParent); - hash.assertEquals(stackHash); - - let nodes = updates.map((update) => { - let accountUpdate = HashedAccountUpdate.hash(update); - let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); - return { accountUpdate, calls }; - }); - - console.log({ nodes: nodes.map((n) => n.calls.hash.toBigInt()) }); - let forest = CallForest.fromAccountUpdates(updates); - forest.hash.assertEquals(stackHash); + forest.hash.assertEquals(expectedHash); } ); -// call forest helpers +// HELPERS type AbstractSimpleCallForest = { accountUpdate: A; From 4c4ce8e3bc4fdcceb35a68553ffb7daf38d607c2 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 12:08:01 +0100 Subject: [PATCH 284/524] fix hash.empty() and merkleArray.next() --- src/lib/mina/token/merkle-list.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index edd1577079..b5cca46e5a 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -268,7 +268,7 @@ class MerkleArray implements MerkleArrayIterator { array, hash, currentHash: hash, - currentIndex: Unconstrained.from(0), + currentIndex: Unconstrained.from(-1), }); } assertAtStart() { @@ -302,7 +302,7 @@ class MerkleArray implements MerkleArrayIterator { WithHash(this.innerProvable), () => this.array.get()[index.get()] ?? { - previousHash: this.hash, + previousHash: emptyHash, element: this.innerProvable.empty(), } ); @@ -372,8 +372,8 @@ class MerkleArray implements MerkleArrayIterator { return new this({ array: Unconstrained.from(arrayWithHashes), hash: currentHash, - currentHash: currentHash, - currentIndex: Unconstrained.from(0), + currentHash, + currentIndex: Unconstrained.from(-1), }); } From 737fc24cdc29e29d86a16838962c05319c8a63f7 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 12:08:15 +0100 Subject: [PATCH 285/524] make some code generic for reuse --- src/mina-signer/src/sign-zkapp-command.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 4daea29443..004a3f0940 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -123,16 +123,22 @@ function transactionCommitments(zkappCommand: ZkappCommand) { return { commitment, fullCommitment }; } -type CallTree = { accountUpdate: AccountUpdate; children: CallForest }; -type CallForest = CallTree[]; +type CallTree = { + accountUpdate: AccountUpdate; + children: CallForest; +}; +type CallForest = CallTree[]; /** * Turn flat list into a hierarchical structure (forest) by letting the callDepth * determine parent-child relationships */ -function accountUpdatesToCallForest(updates: AccountUpdate[], callDepth = 0) { +function accountUpdatesToCallForest( + updates: A[], + callDepth = 0 +) { let remainingUpdates = callDepth > 0 ? updates : [...updates]; - let forest: CallForest = []; + let forest: CallForest = []; while (remainingUpdates.length > 0) { let accountUpdate = remainingUpdates[0]; if (accountUpdate.body.callDepth < callDepth) return forest; @@ -150,7 +156,7 @@ function accountUpdateHash(update: AccountUpdate) { return hashWithPrefix(prefixes.body, fields); } -function callForestHash(forest: CallForest): Field { +function callForestHash(forest: CallForest): Field { let stackHash = 0n; for (let callTree of [...forest].reverse()) { let calls = callForestHash(callTree.children); From fb8b3fa1e0a18c3840028555f0a0edbb816a77bc Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 12:42:32 +0100 Subject: [PATCH 286/524] refactor, start test that traverses the tree --- src/lib/mina/token/call-forest.ts | 22 ++-- src/lib/mina/token/call-forest.unit-test.ts | 111 +++++++++++++------- 2 files changed, 85 insertions(+), 48 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 9a4a540388..a60a93ea1b 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -16,7 +16,7 @@ import { genericHash, } from './merkle-list.js'; -export { CallForest, PartialCallForest }; +export { CallForest, PartialCallForest, hashAccountUpdate }; export { HashedAccountUpdate }; @@ -58,14 +58,20 @@ const MayUseToken = AccountUpdate.MayUseToken; class PartialCallForest { currentLayer: Layer; unfinishedParentLayers: MerkleList; + selfToken: Field; - constructor(forest: CallForest, mayUseToken: MayUseToken) { + constructor(forest: CallForest, mayUseToken: MayUseToken, selfToken: Field) { this.currentLayer = { forest, mayUseToken }; this.unfinishedParentLayers = ParentLayers.empty(); + this.selfToken = selfToken; } - static create(forest: CallForest) { - return new PartialCallForest(forest, MayUseToken.ParentsOwnToken); + static create(forest: CallForest, selfToken: Field) { + return new PartialCallForest( + forest, + MayUseToken.ParentsOwnToken, + selfToken + ); } /** @@ -82,7 +88,7 @@ class PartialCallForest { * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the * caller handle the irrelevant case where `usesThisToken` is false. */ - nextAccountUpdate(selfToken: Field) { + next() { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); @@ -103,11 +109,11 @@ class PartialCallForest { this.currentLayer.mayUseToken ); let isSelf = TokenId.derive(update.publicKey, update.tokenId).equals( - selfToken + this.selfToken ); let usesThisToken = update.tokenId - .equals(selfToken) + .equals(this.selfToken) .and(canAccessThisToken); // if we don't have to check the children, ignore the forest by jumping to its end @@ -137,7 +143,7 @@ class PartialCallForest { parentLayers ); - return { update, usesThisToken }; + return { accountUpdate: update, usesThisToken }; } } diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 782a4e7d9e..0d3d792bf9 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,9 +1,15 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { CallForest, HashedAccountUpdate } from './call-forest.js'; +import { + CallForest, + HashedAccountUpdate, + PartialCallForest, + hashAccountUpdate, +} from './call-forest.js'; import { AccountUpdate, CallForest as ProvableCallForest, + TokenId, } from '../../account_update.js'; import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; import { Pickles } from '../../../snarky.js'; @@ -12,46 +18,47 @@ import { callForestHash, CallForest as SimpleCallForest, } from '../../../mina-signer/src/sign-zkapp-command.js'; +import assert from 'assert'; +import { Field } from '../../field.js'; -// rng for account updates +// RANDOM NUMBER GENERATORS for account updates let [, data, hashMl] = Pickles.dummyVerificationKey(); -let verificationKey = { data, hash: hashMl[1] }; +let dummyVerificationKey = { data, hash: hashMl[1] }; -let accountUpdates = Random.array( +const accountUpdateBigint = Random.map( RandomTransaction.accountUpdateWithCallDepth, - Random.int(0, 50), - { reset: true } -); - -const callForest: Random = Random.map( - accountUpdates, - (accountUpdates) => { - let flatUpdates = accountUpdates.map((a) => { - // fix verification key - if (a.body.update.verificationKey.isSome) { - a.body.update.verificationKey.value = verificationKey; - } - return a; - }); - console.log({ totalLength: flatUpdates.length }); - return accountUpdatesToCallForest(flatUpdates); + (a) => { + // fix verification key + if (a.body.update.verificationKey.isSome) + a.body.update.verificationKey.value = dummyVerificationKey; + return a; } ); +const accountUpdate = Random.map(accountUpdateBigint, accountUpdateFromBigint); + +const arrayOf = (x: Random) => + // `reset: true` to start callDepth at 0 for each array + Random.array(x, Random.int(20, 50), { reset: true }); + +const flatAccountUpdatesBigint = arrayOf(accountUpdateBigint); +const flatAccountUpdates = arrayOf(accountUpdate); // TESTS // correctly hashes a call forest -test.custom({ timeBudget: 10000, logFailures: false })( - callForest, - (forestBigint) => { +test.custom({ timeBudget: 1000, logFailures: false })( + flatAccountUpdatesBigint, + (flatUpdatesBigint) => { // reference: bigint callforest hash from mina-signer + let forestBigint = accountUpdatesToCallForest(flatUpdatesBigint); let expectedHash = callForestHash(forestBigint); // convert to o1js-style list of nested `AccountUpdate`s + let flatUpdates = flatUpdatesBigint.map(accountUpdateFromBigint); let updates = callForestToNestedArray( - mapCallForest(forestBigint, accountUpdateFromBigint) + accountUpdatesToCallForest(flatUpdates) ); let forest = CallForest.fromAccountUpdates(updates); @@ -59,22 +66,46 @@ test.custom({ timeBudget: 10000, logFailures: false })( } ); -// HELPERS +// traverses a call forest in correct depth-first order -type AbstractSimpleCallForest = { - accountUpdate: A; - children: AbstractSimpleCallForest; -}[]; - -function mapCallForest( - forest: AbstractSimpleCallForest, - mapOne: (a: A) => B -): AbstractSimpleCallForest { - return forest.map(({ accountUpdate, children }) => ({ - accountUpdate: mapOne(accountUpdate), - children: mapCallForest(children, mapOne), - })); -} +test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( + flatAccountUpdates, + (flatUpdates) => { + // convert to o1js-style list of nested `AccountUpdate`s + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + + let forest = CallForest.fromAccountUpdates(updates); + let tokenId = Field.random(); + let partialForest = PartialCallForest.create(forest, tokenId); + + let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + + let n = flatUpdates.length; + for (let i = 0; i < n; i++) { + assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); + + let expected = flatUpdates[i]; + let actual = partialForest.next().accountUpdate; + + console.log( + 'expected: ', + expected.body.callDepth, + expected.body.publicKey.toBase58(), + hashAccountUpdate(expected).toBigInt() + ); + console.log( + 'actual: ', + actual.body.callDepth, + actual.body.publicKey.toBase58(), + hashAccountUpdate(actual).toBigInt() + ); + } + } +); + +// HELPERS function accountUpdateFromBigint(a: TypesBigint.AccountUpdate): AccountUpdate { // bigint to json, then to provable @@ -82,7 +113,7 @@ function accountUpdateFromBigint(a: TypesBigint.AccountUpdate): AccountUpdate { } function callForestToNestedArray( - forest: AbstractSimpleCallForest + forest: SimpleCallForest ): AccountUpdate[] { return forest.map(({ accountUpdate, children }) => { accountUpdate.children.accountUpdates = callForestToNestedArray(children); From e1adaaf223ff0c2b6b6b78a63470a84cf0d09abe Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 13:07:08 +0100 Subject: [PATCH 287/524] confirm that callforest.next() works --- src/lib/mina/token/call-forest.unit-test.ts | 63 ++++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 0d3d792bf9..291d84660d 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -39,7 +39,7 @@ const accountUpdate = Random.map(accountUpdateBigint, accountUpdateFromBigint); const arrayOf = (x: Random) => // `reset: true` to start callDepth at 0 for each array - Random.array(x, Random.int(20, 50), { reset: true }); + Random.array(x, 10, { reset: true }); const flatAccountUpdatesBigint = arrayOf(accountUpdateBigint); const flatAccountUpdates = arrayOf(accountUpdate); @@ -66,6 +66,31 @@ test.custom({ timeBudget: 1000, logFailures: false })( } ); +// traverses the top level of a call forest in correct order +// i.e., CallForest.next() works + +test.custom({ timeBudget: 10000, logFailures: false })( + flatAccountUpdates, + (flatUpdates) => { + // convert to o1js-style list of nested `AccountUpdate`s + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + let forest = CallForest.fromAccountUpdates(updates); + + let n = updates.length; + for (let i = 0; i < n; i++) { + let expected = updates[i]; + let actual = forest.next().accountUpdate.value.get(); + assertEqual(actual, expected); + } + + // doing next again should return a dummy + let actual = forest.next().accountUpdate.value.get(); + assertEqual(actual, AccountUpdate.dummy()); + } +); + // traverses a call forest in correct depth-first order test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( @@ -76,6 +101,10 @@ test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( accountUpdatesToCallForest(flatUpdates) ); + let dummyParent = AccountUpdate.dummy(); + dummyParent.children.accountUpdates = updates; + console.log(dummyParent.toPrettyLayout()); + let forest = CallForest.fromAccountUpdates(updates); let tokenId = Field.random(); let partialForest = PartialCallForest.create(forest, tokenId); @@ -87,20 +116,31 @@ test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); let expected = flatUpdates[i]; + let expectedHash = hashAccountUpdate(expected).toBigInt(); let actual = partialForest.next().accountUpdate; + let actualHash = hashAccountUpdate(actual).toBigInt(); + + let isCorrect = String(expectedHash === actualHash).padStart(5, ' '); + console.log( + 'actual: ', + actual.body.callDepth, + isCorrect, + actual.body.publicKey.toBase58(), + actualHash + ); + } + console.log(); + + for (let i = 0; i < n; i++) { + let expected = flatUpdates[i]; console.log( 'expected: ', expected.body.callDepth, + ' true', expected.body.publicKey.toBase58(), hashAccountUpdate(expected).toBigInt() ); - console.log( - 'actual: ', - actual.body.callDepth, - actual.body.publicKey.toBase58(), - hashAccountUpdate(actual).toBigInt() - ); } } ); @@ -120,3 +160,12 @@ function callForestToNestedArray( return accountUpdate; }); } + +function assertEqual(actual: AccountUpdate, expected: AccountUpdate) { + let actualHash = hashAccountUpdate(actual).toBigInt(); + let expectedHash = hashAccountUpdate(expected).toBigInt(); + + assert.deepStrictEqual(actual.body, expected.body); + assert.deepStrictEqual(actual.authorization, expected.authorization); + assert.deepStrictEqual(actualHash, expectedHash); +} From 32e606b8d3aed14a24c21d444af504e26545dc13 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 14:04:46 +0100 Subject: [PATCH 288/524] it actually works when not skipping subtrees --- src/lib/mina/token/call-forest.ts | 8 ++-- src/lib/mina/token/call-forest.unit-test.ts | 52 ++++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index a60a93ea1b..607a1c365a 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,7 +1,6 @@ import { prefixes } from '../../../provable/poseidon-bigint.js'; import { AccountUpdate, - Field, Hashed, Poseidon, Provable, @@ -15,6 +14,7 @@ import { ProvableHashable, genericHash, } from './merkle-list.js'; +import { Field, Bool } from '../../core.js'; export { CallForest, PartialCallForest, hashAccountUpdate }; @@ -88,7 +88,7 @@ class PartialCallForest { * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the * caller handle the irrelevant case where `usesThisToken` is false. */ - next() { + next({ skipSubtrees = true } = {}) { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); @@ -117,7 +117,9 @@ class PartialCallForest { .and(canAccessThisToken); // if we don't have to check the children, ignore the forest by jumping to its end - let skipSubtree = canAccessThisToken.not().or(isSelf); + let skipSubtree = skipSubtrees + ? canAccessThisToken.not().or(isSelf) + : new Bool(false); forest.jumpToEndIf(skipSubtree); // if we're at the end of the current layer, step up to the next unfinished parent layer diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 291d84660d..ae5f79abb6 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -39,7 +39,7 @@ const accountUpdate = Random.map(accountUpdateBigint, accountUpdateFromBigint); const arrayOf = (x: Random) => // `reset: true` to start callDepth at 0 for each array - Random.array(x, 10, { reset: true }); + Random.array(x, Random.int(10, 40), { reset: true }); const flatAccountUpdatesBigint = arrayOf(accountUpdateBigint); const flatAccountUpdates = arrayOf(accountUpdate); @@ -69,7 +69,7 @@ test.custom({ timeBudget: 1000, logFailures: false })( // traverses the top level of a call forest in correct order // i.e., CallForest.next() works -test.custom({ timeBudget: 10000, logFailures: false })( +test.custom({ timeBudget: 1000, logFailures: false })( flatAccountUpdates, (flatUpdates) => { // convert to o1js-style list of nested `AccountUpdate`s @@ -85,13 +85,46 @@ test.custom({ timeBudget: 10000, logFailures: false })( assertEqual(actual, expected); } - // doing next again should return a dummy + // doing next() again should return a dummy let actual = forest.next().accountUpdate.value.get(); assertEqual(actual, AccountUpdate.dummy()); } ); -// traverses a call forest in correct depth-first order +// traverses a call forest in correct depth-first order, +// assuming we don't skip any subtrees + +test.custom({ timeBudget: 10000, logFailures: false })( + flatAccountUpdates, + (flatUpdates) => { + // convert to o1js-style list of nested `AccountUpdate`s + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + + let forest = CallForest.fromAccountUpdates(updates); + let tokenId = Field.random(); + let partialForest = PartialCallForest.create(forest, tokenId); + + let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + + let n = flatUpdates.length; + for (let i = 0; i < n; i++) { + assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); + + let expected = flatUpdates[i]; + let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; + assertEqual(actual, expected); + } + + // doing next() again should return a dummy + let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; + assertEqual(actual, AccountUpdate.dummy()); + } +); + +// TODO +// traverses a call forest in correct depth-first order, when skipping subtrees test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( flatAccountUpdates, @@ -117,7 +150,7 @@ test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( let expected = flatUpdates[i]; let expectedHash = hashAccountUpdate(expected).toBigInt(); - let actual = partialForest.next().accountUpdate; + let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; let actualHash = hashAccountUpdate(actual).toBigInt(); let isCorrect = String(expectedHash === actualHash).padStart(5, ' '); @@ -166,6 +199,13 @@ function assertEqual(actual: AccountUpdate, expected: AccountUpdate) { let expectedHash = hashAccountUpdate(expected).toBigInt(); assert.deepStrictEqual(actual.body, expected.body); - assert.deepStrictEqual(actual.authorization, expected.authorization); + assert.deepStrictEqual( + actual.authorization.proof, + expected.authorization.proof + ); + assert.deepStrictEqual( + actual.authorization.signature, + expected.authorization.signature + ); assert.deepStrictEqual(actualHash, expectedHash); } From ad069e5585648dc7820c3fa4a9f9f83d09d20afd Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 16:35:01 +0100 Subject: [PATCH 289/524] finish unit tests for call forest iteration --- src/lib/mina/token/call-forest.ts | 6 +- src/lib/mina/token/call-forest.unit-test.ts | 214 ++++++++++++-------- 2 files changed, 137 insertions(+), 83 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 607a1c365a..146f97d1ff 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -88,7 +88,7 @@ class PartialCallForest { * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the * caller handle the irrelevant case where `usesThisToken` is false. */ - next({ skipSubtrees = true } = {}) { + next() { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); @@ -117,9 +117,7 @@ class PartialCallForest { .and(canAccessThisToken); // if we don't have to check the children, ignore the forest by jumping to its end - let skipSubtree = skipSubtrees - ? canAccessThisToken.not().or(isSelf) - : new Bool(false); + let skipSubtree = canAccessThisToken.not().or(isSelf); forest.jumpToEndIf(skipSubtree); // if we're at the end of the current layer, step up to the next unfinished parent layer diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index ae5f79abb6..de62ceb354 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -2,8 +2,7 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; import { CallForest, - HashedAccountUpdate, - PartialCallForest, + PartialCallForest as CallForestIterator, hashAccountUpdate, } from './call-forest.js'; import { @@ -19,7 +18,9 @@ import { CallForest as SimpleCallForest, } from '../../../mina-signer/src/sign-zkapp-command.js'; import assert from 'assert'; -import { Field } from '../../field.js'; +import { Field, Bool } from '../../core.js'; +import { Bool as BoolB } from '../../../provable/field-bigint.js'; +import { PublicKey } from '../../signature.js'; // RANDOM NUMBER GENERATORS for account updates @@ -32,6 +33,12 @@ const accountUpdateBigint = Random.map( // fix verification key if (a.body.update.verificationKey.isSome) a.body.update.verificationKey.value = dummyVerificationKey; + + // ensure that, by default, all account updates are token-accessible + a.body.mayUseToken = + a.body.callDepth === 0 + ? { parentsOwnToken: BoolB(true), inheritFromParent: BoolB(false) } + : { parentsOwnToken: BoolB(false), inheritFromParent: BoolB(true) }; return a; } ); @@ -48,7 +55,7 @@ const flatAccountUpdates = arrayOf(accountUpdate); // correctly hashes a call forest -test.custom({ timeBudget: 1000, logFailures: false })( +test.custom({ timeBudget: 1000 })( flatAccountUpdatesBigint, (flatUpdatesBigint) => { // reference: bigint callforest hash from mina-signer @@ -66,114 +73,163 @@ test.custom({ timeBudget: 1000, logFailures: false })( } ); +// can recover flat account updates from nested updates +// this is here to assert that we compute `updates` correctly in the other tests + +test(flatAccountUpdates, (flatUpdates) => { + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + let n = flatUpdates.length; + for (let i = 0; i < n; i++) { + assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); + } +}); + // traverses the top level of a call forest in correct order // i.e., CallForest.next() works -test.custom({ timeBudget: 1000, logFailures: false })( - flatAccountUpdates, - (flatUpdates) => { - // convert to o1js-style list of nested `AccountUpdate`s - let updates = callForestToNestedArray( - accountUpdatesToCallForest(flatUpdates) - ); - let forest = CallForest.fromAccountUpdates(updates); - - let n = updates.length; - for (let i = 0; i < n; i++) { - let expected = updates[i]; - let actual = forest.next().accountUpdate.value.get(); - assertEqual(actual, expected); - } +test.custom({ timeBudget: 1000 })(flatAccountUpdates, (flatUpdates) => { + // prepare call forest from flat account updates + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + let forest = CallForest.fromAccountUpdates(updates); - // doing next() again should return a dummy + // step through top-level by calling forest.next() repeatedly + let n = updates.length; + for (let i = 0; i < n; i++) { + let expected = updates[i]; let actual = forest.next().accountUpdate.value.get(); - assertEqual(actual, AccountUpdate.dummy()); + assertEqual(actual, expected); } -); + + // doing next() again should return a dummy + let actual = forest.next().accountUpdate.value.get(); + assertEqual(actual, AccountUpdate.dummy()); +}); // traverses a call forest in correct depth-first order, -// assuming we don't skip any subtrees +// when no subtrees are skipped + +test.custom({ timeBudget: 5000 })(flatAccountUpdates, (flatUpdates) => { + // with default token id, no subtrees will be skipped + let tokenId = TokenId.default; -test.custom({ timeBudget: 10000, logFailures: false })( + // prepare forest iterator from flat account updates + let updates = callForestToNestedArray( + accountUpdatesToCallForest(flatUpdates) + ); + let forest = CallForest.fromAccountUpdates(updates); + let forestIterator = CallForestIterator.create(forest, tokenId); + + // step through forest iterator and compare against expected updates + let expectedUpdates = flatUpdates; + + let n = flatUpdates.length; + for (let i = 0; i < n; i++) { + let expected = expectedUpdates[i]; + let actual = forestIterator.next().accountUpdate; + assertEqual(actual, expected); + } + + // doing next() again should return a dummy + let actual = forestIterator.next().accountUpdate; + assertEqual(actual, AccountUpdate.dummy()); +}); + +// correctly skips subtrees for various reasons + +// in this test, we make all updates inaccessible by setting the top level to `no` or `inherit`, or to the token owner +// this means we wouldn't need to traverse any update in the whole tree +// but we only notice inaccessibility when we have already traversed the inaccessible update +// so, the result should be that we traverse the top level and nothing else +test.custom({ timeBudget: 5000 })( flatAccountUpdates, - (flatUpdates) => { - // convert to o1js-style list of nested `AccountUpdate`s + Random.publicKey, + (flatUpdates, publicKey) => { + // create token owner and derive token id + let tokenOwner = PublicKey.fromObject({ + x: Field.from(publicKey.x), + isOdd: Bool(!!publicKey.isOdd), + }); + let tokenId = TokenId.derive(tokenOwner); + + // prepare forest iterator from flat account updates let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); + // make all top-level updates inaccessible + updates.forEach((u, i) => { + if (i % 3 === 0) { + u.body.mayUseToken = AccountUpdate.MayUseToken.No; + } else if (i % 3 === 1) { + u.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; + } else { + u.body.publicKey = tokenOwner; + u.body.tokenId = TokenId.default; + } + }); + let forest = CallForest.fromAccountUpdates(updates); - let tokenId = Field.random(); - let partialForest = PartialCallForest.create(forest, tokenId); + let forestIterator = CallForestIterator.create(forest, tokenId); - let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + // step through forest iterator and compare against expected updates + let expectedUpdates = updates; let n = flatUpdates.length; for (let i = 0; i < n; i++) { - assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); - - let expected = flatUpdates[i]; - let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; + let expected = expectedUpdates[i] ?? AccountUpdate.dummy(); + let actual = forestIterator.next().accountUpdate; assertEqual(actual, expected); } - - // doing next() again should return a dummy - let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; - assertEqual(actual, AccountUpdate.dummy()); } ); -// TODO -// traverses a call forest in correct depth-first order, when skipping subtrees - -test.custom({ timeBudget: 10000, logFailures: false, minRuns: 1, maxRuns: 1 })( +// similar to the test before, but now we make all updates in the second layer inaccessible +// so the iteration should walk through the first and second layer +test.custom({ timeBudget: 5000 })( flatAccountUpdates, - (flatUpdates) => { - // convert to o1js-style list of nested `AccountUpdate`s + Random.publicKey, + (flatUpdates, publicKey) => { + // create token owner and derive token id + let tokenOwner = PublicKey.fromObject({ + x: Field.from(publicKey.x), + isOdd: Bool(!!publicKey.isOdd), + }); + let tokenId = TokenId.derive(tokenOwner); + + // make all second-level updates inaccessible + flatUpdates + .filter((u) => u.body.callDepth === 1) + .forEach((u, i) => { + if (i % 3 === 0) { + u.body.mayUseToken = AccountUpdate.MayUseToken.No; + } else if (i % 3 === 1) { + u.body.mayUseToken = AccountUpdate.MayUseToken.ParentsOwnToken; + } else { + u.body.publicKey = tokenOwner; + u.body.tokenId = TokenId.default; + } + }); + + // prepare forest iterator from flat account updates let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - - let dummyParent = AccountUpdate.dummy(); - dummyParent.children.accountUpdates = updates; - console.log(dummyParent.toPrettyLayout()); - let forest = CallForest.fromAccountUpdates(updates); - let tokenId = Field.random(); - let partialForest = PartialCallForest.create(forest, tokenId); + let forestIterator = CallForestIterator.create(forest, tokenId); - let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + // step through forest iterator and compare against expected updates + let expectedUpdates = flatUpdates.filter((u) => u.body.callDepth <= 1); let n = flatUpdates.length; for (let i = 0; i < n; i++) { - assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); - - let expected = flatUpdates[i]; - let expectedHash = hashAccountUpdate(expected).toBigInt(); - let actual = partialForest.next({ skipSubtrees: false }).accountUpdate; - let actualHash = hashAccountUpdate(actual).toBigInt(); - - let isCorrect = String(expectedHash === actualHash).padStart(5, ' '); - - console.log( - 'actual: ', - actual.body.callDepth, - isCorrect, - actual.body.publicKey.toBase58(), - actualHash - ); - } - console.log(); - - for (let i = 0; i < n; i++) { - let expected = flatUpdates[i]; - console.log( - 'expected: ', - expected.body.callDepth, - ' true', - expected.body.publicKey.toBase58(), - hashAccountUpdate(expected).toBigInt() - ); + let expected = expectedUpdates[i] ?? AccountUpdate.dummy(); + let actual = forestIterator.next().accountUpdate; + assertEqual(actual, expected); } } ); From 2803ed904f9b4694b5984b943a25bbf34dd38e32 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 24 Jan 2024 18:16:02 +0100 Subject: [PATCH 290/524] rename, doccomments --- src/lib/mina/token/call-forest.ts | 37 +++++++++++++++------ src/lib/mina/token/call-forest.unit-test.ts | 2 +- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 146f97d1ff..8b2d953d65 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -14,9 +14,9 @@ import { ProvableHashable, genericHash, } from './merkle-list.js'; -import { Field, Bool } from '../../core.js'; +import { Field } from '../../core.js'; -export { CallForest, PartialCallForest, hashAccountUpdate }; +export { CallForest, CallForestIterator, hashAccountUpdate }; export { HashedAccountUpdate }; @@ -55,7 +55,13 @@ const ParentLayers = MerkleList.create(Layer); type MayUseToken = AccountUpdate['body']['mayUseToken']; const MayUseToken = AccountUpdate.MayUseToken; -class PartialCallForest { +/** + * Data structure to represent a forest tree of account updates that is being iterated over. + * + * Important: Since this is to be used for token manager contracts to process it's entire subtree + * of account updates, the iterator skips subtrees that don't inherit token permissions. + */ +class CallForestIterator { currentLayer: Layer; unfinishedParentLayers: MerkleList; selfToken: Field; @@ -67,7 +73,7 @@ class PartialCallForest { } static create(forest: CallForest, selfToken: Field) { - return new PartialCallForest( + return new CallForestIterator( forest, MayUseToken.ParentsOwnToken, selfToken @@ -80,9 +86,6 @@ class PartialCallForest { * This function is guaranteed to visit each account update in the tree that uses the token * exactly once, when called repeatedly. * - * The internal state of `PartialCallForest` represents the work still to be done, and - * can be passed from one proof to the next. - * * The method makes a best effort to avoid visiting account updates that are not using the token, * and in particular, to avoid returning dummy updates. * However, neither can be ruled out. We're returning { update, usesThisToken: Bool } and let the @@ -112,9 +115,7 @@ class PartialCallForest { this.selfToken ); - let usesThisToken = update.tokenId - .equals(this.selfToken) - .and(canAccessThisToken); + let usesThisToken = update.tokenId.equals(this.selfToken); // if we don't have to check the children, ignore the forest by jumping to its end let skipSubtree = canAccessThisToken.not().or(isSelf); @@ -147,6 +148,22 @@ class PartialCallForest { } } +// helper class to represent the position in a tree = the last visited node + +// every entry in the array is a layer +// so if there are two entries, we last visited a node in the second layer +// this index is the index of the node in that layer +type TreePosition = { index: number; isDone: boolean }[]; +// const TreePosition = { +// stepDown(position: TreePosition, numberOfChildren: number) { +// position.push({ index: 0, isDone: false }); +// }, +// stepUp(position: TreePosition) { +// position.pop(); +// position[position.length - 1].index++; +// }, +// }; + // how to hash a forest function merkleListHash(forestHash: Field, tree: CallTree) { diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index de62ceb354..11b1ea3592 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -2,7 +2,7 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; import { CallForest, - PartialCallForest as CallForestIterator, + CallForestIterator, hashAccountUpdate, } from './call-forest.js'; import { From 18fb5699a04788ab00689fb39bf3ecb342e978b3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 11:09:41 +0100 Subject: [PATCH 291/524] unify base types of merklelist/array --- src/lib/mina/token/call-forest.ts | 6 +- src/lib/mina/token/merkle-list.ts | 164 +++++++++++++++--------------- 2 files changed, 83 insertions(+), 87 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 8b2d953d65..36d0840578 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -9,7 +9,7 @@ import { } from '../../../index.js'; import { MerkleArray, - MerkleArrayBase, + MerkleListBase, MerkleList, ProvableHashable, genericHash, @@ -27,11 +27,11 @@ class HashedAccountUpdate extends Hashed.create( type CallTree = { accountUpdate: Hashed; - calls: MerkleArrayBase; + calls: MerkleListBase; }; const CallTree: ProvableHashable = Struct({ accountUpdate: HashedAccountUpdate.provable, - calls: MerkleArrayBase(), + calls: MerkleListBase(), }); class CallForest extends MerkleArray.create(CallTree, merkleListHash) { diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index b5cca46e5a..ae0164f5c3 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -11,42 +11,49 @@ import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; import { packToFields } from '../../hash.js'; export { - MerkleArray, - MerkleArrayIterator, - MerkleArrayBase, + MerkleListBase, MerkleList, + MerkleListIterator, + MerkleArray, WithHash, - WithStackHash, emptyHash, ProvableHashable, genericHash, + merkleListHash, }; -function genericHash( - provable: ProvableHashable, - prefix: string, - value: T -) { - let input = provable.toInput(value); - let packed = packToFields(input); - return Poseidon.hashWithPrefix(prefix, packed); +// common base types for both MerkleList and MerkleArray + +const emptyHash = Field(0); + +type WithHash = { previousHash: Field; element: T }; + +function WithHash(type: ProvableHashable): ProvableHashable> { + return Struct({ previousHash: Field, element: type }); } -function merkleListHash(provable: ProvableHashable, prefix = '') { - return function nextHash(hash: Field, value: T) { - let input = provable.toInput(value); - let packed = packToFields(input); - return Poseidon.hashWithPrefix(prefix, [hash, ...packed]); +type MerkleListBase = { + hash: Field; + data: Unconstrained[]>; +}; + +function MerkleListBase(): ProvableHashable> { + return class extends Struct({ hash: Field, data: Unconstrained.provable }) { + static empty(): MerkleListBase { + return { hash: emptyHash, data: Unconstrained.from([]) }; + } }; } -class MerkleList { +// merkle list + +class MerkleList implements MerkleListBase { hash: Field; - stack: Unconstrained[]>; + data: Unconstrained[]>; - constructor({ hash, stack }: WithStackHash) { + constructor({ hash, data }: MerkleListBase) { this.hash = hash; - this.stack = stack; + this.data = data; } isEmpty() { @@ -60,7 +67,7 @@ class MerkleList { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); Provable.asProver(() => { - this.stack.set([...this.stack.get(), { previousHash, element }]); + this.data.set([...this.data.get(), { previousHash, element }]); }); } @@ -73,19 +80,19 @@ class MerkleList { ); Provable.asProver(() => { if (condition.toBoolean()) { - this.stack.set([...this.stack.get(), { previousHash, element }]); + this.data.set([...this.data.get(), { previousHash, element }]); } }); } private popWitness() { return Provable.witness(WithHash(this.innerProvable), () => { - let value = this.stack.get(); + let value = this.data.get(); let head = value.at(-1) ?? { previousHash: emptyHash, element: this.innerProvable.empty(), }; - this.stack.set(value.slice(0, -1)); + this.data.set(value.slice(0, -1)); return head; }); } @@ -114,8 +121,8 @@ class MerkleList { } clone(): MerkleList { - let stack = Unconstrained.witness(() => [...this.stack.get()]); - return new this.Constructor({ hash: this.hash, stack }); + let data = Unconstrained.witness(() => [...this.data.get()]); + return new this.Constructor({ hash: this.hash, data }); } /** @@ -125,6 +132,7 @@ class MerkleList { type: ProvableHashable, nextHash: (hash: Field, value: T) => Field = merkleListHash(type) ): typeof MerkleList & { + // override static methods with strict types empty: () => MerkleList; provable: ProvableHashable>; } { @@ -133,13 +141,13 @@ class MerkleList { static _provable = provableFromClass(MerkleList_, { hash: Field, - stack: Unconstrained.provable, + data: Unconstrained.provable, }) as ProvableHashable>; static _nextHash = nextHash; static empty(): MerkleList { - return new this({ hash: emptyHash, stack: Unconstrained.from([]) }); + return new this({ hash: emptyHash, data: Unconstrained.from([]) }); } static get provable(): ProvableHashable> { @@ -176,25 +184,6 @@ class MerkleList { } } -type WithHash = { previousHash: Field; element: T }; -function WithHash(type: ProvableHashable): Provable> { - return Struct({ previousHash: Field, element: type }); -} - -const emptyHash = Field(0); - -type WithStackHash = { - hash: Field; - stack: Unconstrained[]>; -}; -function WithStackHash(): ProvableHashable> { - return class extends Struct({ hash: Field, stack: Unconstrained.provable }) { - static empty(): WithStackHash { - return { hash: emptyHash, stack: Unconstrained.from([]) }; - } - }; -} - type HashInput = { fields?: Field[]; packed?: [Field, number][] }; type ProvableHashable = Provable & { toInput: (x: T) => HashInput; @@ -203,38 +192,25 @@ type ProvableHashable = Provable & { // merkle array -type MerkleArrayBase = { - readonly array: Unconstrained[]>; - readonly hash: Field; -}; - -function MerkleArrayBase(): ProvableHashable> { - return class extends Struct({ array: Unconstrained.provable, hash: Field }) { - static empty(): MerkleArrayBase { - return { array: Unconstrained.from([]), hash: emptyHash }; - } - }; -} - -type MerkleArrayIterator = { - readonly array: Unconstrained[]>; +type MerkleListIterator = { readonly hash: Field; + readonly data: Unconstrained[]>; currentHash: Field; currentIndex: Unconstrained; }; -function MerkleArrayIterator(): ProvableHashable> { +function MerkleListIterator(): ProvableHashable> { return class extends Struct({ - array: Unconstrained.provable, hash: Field, + data: Unconstrained.provable, currentHash: Field, currentIndex: Unconstrained.provable, }) { - static empty(): MerkleArrayIterator { + static empty(): MerkleListIterator { return { - array: Unconstrained.from([]), hash: emptyHash, + data: Unconstrained.from([]), currentHash: emptyHash, currentIndex: Unconstrained.from(0), }; @@ -244,28 +220,28 @@ function MerkleArrayIterator(): ProvableHashable> { /** * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, - * instead of needlessly mutating itself / throwing away context while stepping through it. + * instead of mutating itself / throwing away context while stepping through it. * * We maintain two commitments, both of which are equivalent to a Merkle list hash starting _from the end_ of the array: * - One to the entire array, to prove that we start iterating at the beginning. * - One to the array from the current index until the end, to efficiently step forward. */ -class MerkleArray implements MerkleArrayIterator { +class MerkleArray implements MerkleListIterator { // fixed parts - readonly array: Unconstrained[]>; + readonly data: Unconstrained[]>; readonly hash: Field; // mutable parts currentHash: Field; currentIndex: Unconstrained; - constructor(value: MerkleArrayIterator) { + constructor(value: MerkleListIterator) { Object.assign(this, value); } - static startIterating({ array, hash }: MerkleArrayBase) { + static startIterating({ data, hash }: MerkleListBase) { return new this({ - array, + data, hash, currentHash: hash, currentIndex: Unconstrained.from(-1), @@ -280,14 +256,14 @@ class MerkleArray implements MerkleArrayIterator { } jumpToEnd() { this.currentIndex.setTo( - Unconstrained.witness(() => this.array.get().length) + Unconstrained.witness(() => this.data.get().length) ); this.currentHash = emptyHash; } jumpToEndIf(condition: Bool) { Provable.asProver(() => { if (condition.toBoolean()) { - this.currentIndex.set(this.array.get().length); + this.currentIndex.set(this.data.get().length); } }); this.currentHash = Provable.if(condition, emptyHash, this.currentHash); @@ -301,7 +277,7 @@ class MerkleArray implements MerkleArrayIterator { let { previousHash, element } = Provable.witness( WithHash(this.innerProvable), () => - this.array.get()[index.get()] ?? { + this.data.get()[index.get()] ?? { previousHash: emptyHash, element: this.innerProvable.empty(), } @@ -324,10 +300,10 @@ class MerkleArray implements MerkleArrayIterator { } clone(): MerkleArray { - let array = Unconstrained.witness(() => [...this.array.get()]); + let data = Unconstrained.witness(() => [...this.data.get()]); let currentIndex = Unconstrained.witness(() => this.currentIndex.get()); return new this.Constructor({ - array, + data, hash: this.hash, currentHash: this.currentHash, currentIndex, @@ -335,7 +311,7 @@ class MerkleArray implements MerkleArrayIterator { } /** - * Create a Merkle list type + * Create a Merkle array type */ static create( type: ProvableHashable, @@ -349,8 +325,8 @@ class MerkleArray implements MerkleArrayIterator { static _innerProvable = type; static _provable = provableFromClass(MerkleArray_, { - array: Unconstrained.provable, hash: Field, + data: Unconstrained.provable, currentHash: Field, currentIndex: Unconstrained.provable, }) satisfies ProvableHashable> as ProvableHashable< @@ -370,7 +346,7 @@ class MerkleArray implements MerkleArrayIterator { } return new this({ - array: Unconstrained.from(arrayWithHashes), + data: Unconstrained.from(arrayWithHashes), hash: currentHash, currentHash, currentIndex: Unconstrained.from(-1), @@ -389,7 +365,7 @@ class MerkleArray implements MerkleArrayIterator { } // dynamic subclassing infra - static _nextHash: ((hash: Field, t: any) => Field) | undefined; + static _nextHash: ((hash: Field, value: any) => Field) | undefined; static _provable: ProvableHashable> | undefined; static _innerProvable: ProvableHashable | undefined; @@ -398,12 +374,12 @@ class MerkleArray implements MerkleArrayIterator { return this.constructor as typeof MerkleArray; } - nextHash(hash: Field, t: T): Field { + nextHash(hash: Field, value: T): Field { assert( this.Constructor._nextHash !== undefined, 'MerkleArray not initialized' ); - return this.Constructor._nextHash(hash, t); + return this.Constructor._nextHash(hash, value); } get innerProvable(): ProvableHashable { @@ -414,3 +390,23 @@ class MerkleArray implements MerkleArrayIterator { return this.Constructor._innerProvable; } } + +// hash helpers + +function genericHash( + provable: ProvableHashable, + prefix: string, + value: T +) { + let input = provable.toInput(value); + let packed = packToFields(input); + return Poseidon.hashWithPrefix(prefix, packed); +} + +function merkleListHash(provable: ProvableHashable, prefix = '') { + return function nextHash(hash: Field, value: T) { + let input = provable.toInput(value); + let packed = packToFields(input); + return Poseidon.hashWithPrefix(prefix, [hash, ...packed]); + }; +} From d3d777bcb391dca29342b30c3e090aec09e42f46 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 11:35:41 +0100 Subject: [PATCH 292/524] simple way to update unconstrained --- src/lib/circuit_value.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index adccc9a249..67bc6515af 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -562,6 +562,16 @@ and Provable.asProver() blocks, which execute outside the proof. ); } + /** + * Update an `Unconstrained` by a witness computation. + */ + updateAsProver(compute: (value: T) => T) { + return Provable.asProver(() => { + let value = this.get(); + this.set(compute(value)); + }); + } + static provable: Provable> & { toInput: (x: Unconstrained) => { fields?: Field[]; From a970de2391d94e79e00eb889736098868d698c40 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 11:38:21 +0100 Subject: [PATCH 293/524] change merkle array start index from -1 to 0 --- src/lib/mina/token/merkle-list.ts | 52 ++++++++++++------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index ae0164f5c3..0e621f7279 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -66,9 +66,7 @@ class MerkleList implements MerkleListBase { push(element: T) { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); - Provable.asProver(() => { - this.data.set([...this.data.get(), { previousHash, element }]); - }); + this.data.updateAsProver((data) => [...data, { previousHash, element }]); } pushIf(condition: Bool, element: T) { @@ -78,11 +76,9 @@ class MerkleList implements MerkleListBase { this.nextHash(previousHash, element), previousHash ); - Provable.asProver(() => { - if (condition.toBoolean()) { - this.data.set([...this.data.get(), { previousHash, element }]); - } - }); + this.data.updateAsProver((data) => + condition.toBoolean() ? [...data, { previousHash, element }] : data + ); } private popWitness() { @@ -196,28 +192,20 @@ type MerkleListIterator = { readonly hash: Field; readonly data: Unconstrained[]>; + /** + * The merkle list hash of `[data[currentIndex], ..., data[length-1]]` (when hashing from right to left). + * + * For example: + * - If `currentIndex === 0`, then `currentHash === this.hash` is the hash of the entire array. + * - If `currentIndex === length`, then `currentHash === emptyHash` is the hash of an empty array. + */ currentHash: Field; + /** + * The index of the element that will be returned by the next call to `next()`. + */ currentIndex: Unconstrained; }; -function MerkleListIterator(): ProvableHashable> { - return class extends Struct({ - hash: Field, - data: Unconstrained.provable, - currentHash: Field, - currentIndex: Unconstrained.provable, - }) { - static empty(): MerkleListIterator { - return { - hash: emptyHash, - data: Unconstrained.from([]), - currentHash: emptyHash, - currentIndex: Unconstrained.from(0), - }; - } - }; -} - /** * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, * instead of mutating itself / throwing away context while stepping through it. @@ -244,7 +232,7 @@ class MerkleArray implements MerkleListIterator { data, hash, currentHash: hash, - currentIndex: Unconstrained.from(-1), + currentIndex: Unconstrained.from(0), }); } assertAtStart() { @@ -272,12 +260,10 @@ class MerkleArray implements MerkleListIterator { next() { // next corresponds to `pop()` in MerkleList // it returns a dummy element if we're at the end of the array - let index = Unconstrained.witness(() => this.currentIndex.get() + 1); - let { previousHash, element } = Provable.witness( WithHash(this.innerProvable), () => - this.data.get()[index.get()] ?? { + this.data.get()[this.currentIndex.get()] ?? { previousHash: emptyHash, element: this.innerProvable.empty(), } @@ -288,7 +274,9 @@ class MerkleArray implements MerkleListIterator { let requiredHash = Provable.if(isDummy, emptyHash, correctHash); this.currentHash.assertEquals(requiredHash); - this.currentIndex.setTo(index); + this.currentIndex.updateAsProver((i) => + Math.min(i + 1, this.data.get().length) + ); this.currentHash = Provable.if(isDummy, emptyHash, previousHash); return Provable.if( @@ -349,7 +337,7 @@ class MerkleArray implements MerkleListIterator { data: Unconstrained.from(arrayWithHashes), hash: currentHash, currentHash, - currentIndex: Unconstrained.from(-1), + currentIndex: Unconstrained.from(0), }); } From 42fcb39830975ac94631b0f21c573c34c0cb7c5d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 12:37:26 +0100 Subject: [PATCH 294/524] invert internal order in merkle list --- src/lib/mina/token/merkle-list.ts | 70 ++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index 0e621f7279..a3ded82ab3 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -6,9 +6,9 @@ import { Struct, Unconstrained, assert, -} from 'o1js'; +} from '../../../index.js'; import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; -import { packToFields } from '../../hash.js'; +import { packToFields, ProvableHashable } from '../../hash.js'; export { MerkleListBase, @@ -32,6 +32,9 @@ function WithHash(type: ProvableHashable): ProvableHashable> { return Struct({ previousHash: Field, element: type }); } +/** + * Common base type for {@link MerkleList} and {@link MerkleArray} + */ type MerkleListBase = { hash: Field; data: Unconstrained[]>; @@ -47,6 +50,25 @@ function MerkleListBase(): ProvableHashable> { // merkle list +/** + * Dynamic-length list which is represented as a single hash + * + * Supported operations are {@link push()} and {@link pop()} and some variants thereof. + * + * **Important:** `push()` adds elements to the _start_ of the internal array and `pop()` removes them from the start. + * This is so that the hash which represents the list is consistent with {@link MerkleArray}, + * and so a `MerkleList` can be used as input to `MerkleArray.startIterating(list)` (which will then iterate starting from the last pushed element). + * + * A Merkle list is generic over its element types, so before using it you must create a subclass for your element type: + * + * ```ts + * class MyList extends MerkleList.create(MyType) {} + * + * // now use it + * let list = MyList.empty(); + * list.push(new MyType(...)); + * ``` + */ class MerkleList implements MerkleListBase { hash: Field; data: Unconstrained[]>; @@ -59,14 +81,11 @@ class MerkleList implements MerkleListBase { isEmpty() { return this.hash.equals(emptyHash); } - notEmpty() { - return this.hash.equals(emptyHash).not(); - } push(element: T) { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); - this.data.updateAsProver((data) => [...data, { previousHash, element }]); + this.data.updateAsProver((data) => [{ previousHash, element }, ...data]); } pushIf(condition: Bool, element: T) { @@ -77,18 +96,18 @@ class MerkleList implements MerkleListBase { previousHash ); this.data.updateAsProver((data) => - condition.toBoolean() ? [...data, { previousHash, element }] : data + condition.toBoolean() ? [{ previousHash, element }, ...data] : data ); } private popWitness() { return Provable.witness(WithHash(this.innerProvable), () => { - let value = this.data.get(); - let head = value.at(-1) ?? { + let [value, ...data] = this.data.get(); + let head = value ?? { previousHash: emptyHash, element: this.innerProvable.empty(), }; - this.data.set(value.slice(0, -1)); + this.data.set(data); return head; }); } @@ -96,8 +115,8 @@ class MerkleList implements MerkleListBase { popExn(): T { let { previousHash, element } = this.popWitness(); - let requiredHash = this.nextHash(previousHash, element); - this.hash.assertEquals(requiredHash); + let currentHash = this.nextHash(previousHash, element); + this.hash.assertEquals(currentHash); this.hash = previousHash; return element; @@ -105,11 +124,11 @@ class MerkleList implements MerkleListBase { pop(): T { let { previousHash, element } = this.popWitness(); - let isEmpty = this.isEmpty(); - let correctHash = this.nextHash(previousHash, element); - let requiredHash = Provable.if(isEmpty, emptyHash, correctHash); - this.hash.assertEquals(requiredHash); + + let currentHash = this.nextHash(previousHash, element); + currentHash = Provable.if(isEmpty, emptyHash, currentHash); + this.hash.assertEquals(currentHash); this.hash = Provable.if(isEmpty, emptyHash, previousHash); let provable = this.innerProvable; @@ -123,6 +142,15 @@ class MerkleList implements MerkleListBase { /** * Create a Merkle list type + * + * Optionally, you can tell `create()` how to do the hash that pushed a new list element, by passing a `nextHash` function. + * + * @example + * ```ts + * class MyList extends MerkleList.create(Field, (hash, x) => + * Poseidon.hashWithPrefix('custom', [hash, x]) + * ) {} + * ``` */ static create( type: ProvableHashable, @@ -163,12 +191,12 @@ class MerkleList implements MerkleListBase { return this.constructor as typeof MerkleList; } - nextHash(hash: Field, t: T): Field { + nextHash(hash: Field, value: T): Field { assert( this.Constructor._nextHash !== undefined, 'MerkleList not initialized' ); - return this.Constructor._nextHash(hash, t); + return this.Constructor._nextHash(hash, value); } get innerProvable(): ProvableHashable { @@ -180,12 +208,6 @@ class MerkleList implements MerkleListBase { } } -type HashInput = { fields?: Field[]; packed?: [Field, number][] }; -type ProvableHashable = Provable & { - toInput: (x: T) => HashInput; - empty: () => T; -}; - // merkle array type MerkleListIterator = { From 28e906bb84493fba58d31be90bd6ae464fc77795 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 13:51:04 +0100 Subject: [PATCH 295/524] make merkle list the main callforest intf --- src/lib/mina/token/call-forest.ts | 18 ++++-- src/lib/mina/token/call-forest.unit-test.ts | 8 +-- src/lib/mina/token/merkle-list.ts | 61 ++++++++++++++------- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 36d0840578..a4b070481d 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -16,7 +16,7 @@ import { } from './merkle-list.js'; import { Field } from '../../core.js'; -export { CallForest, CallForestIterator, hashAccountUpdate }; +export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; export { HashedAccountUpdate }; @@ -34,7 +34,7 @@ const CallTree: ProvableHashable = Struct({ calls: MerkleListBase(), }); -class CallForest extends MerkleArray.create(CallTree, merkleListHash) { +class CallForest extends MerkleList.create(CallTree, merkleListHash) { static fromAccountUpdates(updates: AccountUpdate[]): CallForest { let nodes = updates.map((update) => { let accountUpdate = HashedAccountUpdate.hash(update); @@ -46,8 +46,10 @@ class CallForest extends MerkleArray.create(CallTree, merkleListHash) { } } +class CallForestArray extends MerkleArray.createFromList(CallForest) {} + class Layer extends Struct({ - forest: CallForest.provable, + forest: CallForestArray.provable, mayUseToken: AccountUpdate.MayUseToken.type, }) {} const ParentLayers = MerkleList.create(Layer); @@ -66,7 +68,11 @@ class CallForestIterator { unfinishedParentLayers: MerkleList; selfToken: Field; - constructor(forest: CallForest, mayUseToken: MayUseToken, selfToken: Field) { + constructor( + forest: CallForestArray, + mayUseToken: MayUseToken, + selfToken: Field + ) { this.currentLayer = { forest, mayUseToken }; this.unfinishedParentLayers = ParentLayers.empty(); this.selfToken = selfToken; @@ -74,7 +80,7 @@ class CallForestIterator { static create(forest: CallForest, selfToken: Field) { return new CallForestIterator( - forest, + CallForestArray.startIterating(forest), MayUseToken.ParentsOwnToken, selfToken ); @@ -95,7 +101,7 @@ class CallForestIterator { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); - let forest = CallForest.startIterating(calls); + let forest = CallForestArray.startIterating(calls); let parentForest = this.currentLayer.forest; this.unfinishedParentLayers.pushIf(parentForest.isAtEnd().not(), { diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index 11b1ea3592..a9ca56bc56 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -88,25 +88,25 @@ test(flatAccountUpdates, (flatUpdates) => { }); // traverses the top level of a call forest in correct order -// i.e., CallForest.next() works +// i.e., CallForestArray works test.custom({ timeBudget: 1000 })(flatAccountUpdates, (flatUpdates) => { // prepare call forest from flat account updates let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - let forest = CallForest.fromAccountUpdates(updates); + let forest = CallForest.fromAccountUpdates(updates).startIterating(); // step through top-level by calling forest.next() repeatedly let n = updates.length; for (let i = 0; i < n; i++) { let expected = updates[i]; - let actual = forest.next().accountUpdate.value.get(); + let actual = forest.next().accountUpdate.unhash(); assertEqual(actual, expected); } // doing next() again should return a dummy - let actual = forest.next().accountUpdate.value.get(); + let actual = forest.next().accountUpdate.unhash(); assertEqual(actual, AccountUpdate.dummy()); }); diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index a3ded82ab3..60139dca77 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -140,6 +140,11 @@ class MerkleList implements MerkleListBase { return new this.Constructor({ hash: this.hash, data }); } + startIterating(): MerkleArray { + let merkleArray = MerkleArray.createFromList(this.Constructor); + return merkleArray.startIterating(this); + } + /** * Create a Merkle list type * @@ -158,6 +163,7 @@ class MerkleList implements MerkleListBase { ): typeof MerkleList & { // override static methods with strict types empty: () => MerkleList; + from: (array: T[]) => MerkleList; provable: ProvableHashable>; } { return class MerkleList_ extends MerkleList { @@ -174,6 +180,11 @@ class MerkleList implements MerkleListBase { return new this({ hash: emptyHash, data: Unconstrained.from([]) }); } + static from(array: T[]): MerkleList { + let { hash, data } = withHashes(array, nextHash); + return new this({ data: Unconstrained.from(data), hash }); + } + static get provable(): ProvableHashable> { assert(this._provable !== undefined, 'MerkleList not initialized'); return this._provable; @@ -249,14 +260,6 @@ class MerkleArray implements MerkleListIterator { Object.assign(this, value); } - static startIterating({ data, hash }: MerkleListBase) { - return new this({ - data, - hash, - currentHash: hash, - currentIndex: Unconstrained.from(0), - }); - } assertAtStart() { return this.currentHash.assertEquals(this.hash); } @@ -328,6 +331,7 @@ class MerkleArray implements MerkleListIterator { nextHash: (hash: Field, value: T) => Field = merkleListHash(type) ): typeof MerkleArray & { from: (array: T[]) => MerkleArray; + startIterating: (list: MerkleListBase) => MerkleArray; empty: () => MerkleArray; provable: ProvableHashable>; } { @@ -346,19 +350,15 @@ class MerkleArray implements MerkleListIterator { static _nextHash = nextHash; static from(array: T[]): MerkleArray { - let n = array.length; - let arrayWithHashes = Array>(n); - let currentHash = emptyHash; - - for (let i = n - 1; i >= 0; i--) { - arrayWithHashes[i] = { previousHash: currentHash, element: array[i] }; - currentHash = nextHash(currentHash, array[i]); - } + let { hash, data } = withHashes(array, nextHash); + return this.startIterating({ data: Unconstrained.from(data), hash }); + } + static startIterating({ data, hash }: MerkleListBase): MerkleArray { return new this({ - data: Unconstrained.from(arrayWithHashes), - hash: currentHash, - currentHash, + data, + hash, + currentHash: hash, currentIndex: Unconstrained.from(0), }); } @@ -374,6 +374,13 @@ class MerkleArray implements MerkleListIterator { }; } + static createFromList(merkleList: typeof MerkleList) { + return this.create( + merkleList.prototype.innerProvable, + merkleList._nextHash + ); + } + // dynamic subclassing infra static _nextHash: ((hash: Field, value: any) => Field) | undefined; @@ -420,3 +427,19 @@ function merkleListHash(provable: ProvableHashable, prefix = '') { return Poseidon.hashWithPrefix(prefix, [hash, ...packed]); }; } + +function withHashes( + data: T[], + nextHash: (hash: Field, value: T) => Field +): { data: WithHash[]; hash: Field } { + let n = data.length; + let arrayWithHashes = Array>(n); + let currentHash = emptyHash; + + for (let i = n - 1; i >= 0; i--) { + arrayWithHashes[i] = { previousHash: currentHash, element: data[i] }; + currentHash = nextHash(currentHash, data[i]); + } + + return { data: arrayWithHashes, hash: currentHash }; +} From e0c44fd0961a245d4359120a4002290550a6609c Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:05:25 +0100 Subject: [PATCH 296/524] lower level deps for merkle list --- src/lib/mina/token/merkle-list.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/mina/token/merkle-list.ts index 60139dca77..7fc72b1592 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/mina/token/merkle-list.ts @@ -1,14 +1,9 @@ -import { - Bool, - Field, - Poseidon, - Provable, - Struct, - Unconstrained, - assert, -} from '../../../index.js'; +import { Bool, Field } from '../../core.js'; +import { Provable } from '../../provable.js'; +import { Struct, Unconstrained } from '../../circuit_value.js'; +import { assert } from '../../gadgets/common.js'; import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; -import { packToFields, ProvableHashable } from '../../hash.js'; +import { Poseidon, packToFields, ProvableHashable } from '../../hash.js'; export { MerkleListBase, From cbde4ff6e70d56e563890c251397d8b34b9456d0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:06:32 +0100 Subject: [PATCH 297/524] move merkle list --- src/lib/mina/token/call-forest.ts | 2 +- .../{mina/token => provable-types}/merkle-list.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) rename src/lib/{mina/token => provable-types}/merkle-list.ts (97%) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index a4b070481d..a589546838 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -13,7 +13,7 @@ import { MerkleList, ProvableHashable, genericHash, -} from './merkle-list.js'; +} from '../../provable-types/merkle-list.js'; import { Field } from '../../core.js'; export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; diff --git a/src/lib/mina/token/merkle-list.ts b/src/lib/provable-types/merkle-list.ts similarity index 97% rename from src/lib/mina/token/merkle-list.ts rename to src/lib/provable-types/merkle-list.ts index 7fc72b1592..3e299f8c0c 100644 --- a/src/lib/mina/token/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -1,9 +1,9 @@ -import { Bool, Field } from '../../core.js'; -import { Provable } from '../../provable.js'; -import { Struct, Unconstrained } from '../../circuit_value.js'; -import { assert } from '../../gadgets/common.js'; -import { provableFromClass } from '../../../bindings/lib/provable-snarky.js'; -import { Poseidon, packToFields, ProvableHashable } from '../../hash.js'; +import { Bool, Field } from '../core.js'; +import { Provable } from '../provable.js'; +import { Struct, Unconstrained } from '../circuit_value.js'; +import { assert } from '../gadgets/common.js'; +import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; +import { Poseidon, packToFields, ProvableHashable } from '../hash.js'; export { MerkleListBase, From c6813a4e7f37c2d4bba212fb5c00ec1eaf72eee2 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:06:40 +0100 Subject: [PATCH 298/524] expose merkle list/array --- src/index.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/index.ts b/src/index.ts index 8b8aa0672e..81f3bb4193 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,6 +40,12 @@ export { Packed, Hashed } from './lib/provable-types/packed.js'; export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; +export { + MerkleList, + MerkleArray, + ProvableHashable, +} from './lib/provable-types/merkle-list.js'; + export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; export { From de2f3ca50b81a8fa2f8bfe503c2c68f30da62b4e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:17:24 +0100 Subject: [PATCH 299/524] remove unnecessary code --- src/lib/mina/token/call-forest.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index a589546838..84fa0f1a41 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -154,22 +154,6 @@ class CallForestIterator { } } -// helper class to represent the position in a tree = the last visited node - -// every entry in the array is a layer -// so if there are two entries, we last visited a node in the second layer -// this index is the index of the node in that layer -type TreePosition = { index: number; isDone: boolean }[]; -// const TreePosition = { -// stepDown(position: TreePosition, numberOfChildren: number) { -// position.push({ index: 0, isDone: false }); -// }, -// stepUp(position: TreePosition) { -// position.pop(); -// position[position.length - 1].index++; -// }, -// }; - // how to hash a forest function merkleListHash(forestHash: Field, tree: CallTree) { From dc09c20ff2784355734bf73b4918fa18f6e5f408 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:41:20 +0100 Subject: [PATCH 300/524] fix dependencies --- src/lib/mina/token/call-forest.ts | 17 +++++++---------- src/lib/provable-types/merkle-list.ts | 1 - 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 84fa0f1a41..2083842f5a 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,20 +1,17 @@ import { prefixes } from '../../../provable/poseidon-bigint.js'; -import { - AccountUpdate, - Hashed, - Poseidon, - Provable, - Struct, - TokenId, -} from '../../../index.js'; +import { AccountUpdate, TokenId } from '../../account_update.js'; +import { Field } from '../../core.js'; +import { Provable } from '../../provable.js'; +import { Struct } from '../../circuit_value.js'; +import { assert } from '../../gadgets/common.js'; +import { Poseidon, ProvableHashable } from '../../hash.js'; +import { Hashed } from '../../provable-types/packed.js'; import { MerkleArray, MerkleListBase, MerkleList, - ProvableHashable, genericHash, } from '../../provable-types/merkle-list.js'; -import { Field } from '../../core.js'; export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index 3e299f8c0c..72dee6d025 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -12,7 +12,6 @@ export { MerkleArray, WithHash, emptyHash, - ProvableHashable, genericHash, merkleListHash, }; From bd8f5359e5d5cbb91ea130483aea201d2f73e01b Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 25 Jan 2024 21:42:11 +0100 Subject: [PATCH 301/524] fix build --- src/index.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index 81f3bb4193..d18500bfb7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,7 @@ export { } from './lib/foreign-field.js'; export { createForeignCurve, ForeignCurve } from './lib/foreign-curve.js'; export { createEcdsa, EcdsaSignature } from './lib/foreign-ecdsa.js'; -export { Poseidon, TokenSymbol } from './lib/hash.js'; +export { Poseidon, TokenSymbol, ProvableHashable } from './lib/hash.js'; export { Keccak } from './lib/keccak.js'; export { Hash } from './lib/hashes-combined.js'; @@ -40,11 +40,7 @@ export { Packed, Hashed } from './lib/provable-types/packed.js'; export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; -export { - MerkleList, - MerkleArray, - ProvableHashable, -} from './lib/provable-types/merkle-list.js'; +export { MerkleList, MerkleArray } from './lib/provable-types/merkle-list.js'; export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; From 985f5708b8c4a7441fc15d81858ecd1fe321c9f4 Mon Sep 17 00:00:00 2001 From: Gregor Date: Sat, 27 Jan 2024 18:03:02 +0100 Subject: [PATCH 302/524] add edge case to investigate --- src/lib/gadgets/ecdsa.unit-test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index 250b088709..f21adb722f 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -43,6 +43,17 @@ for (let Curve of curves) { msg: scalar, publicKey: record({ x: field, y: field }), }); + badSignature.rng = Random.withHardCoded(badSignature.rng, { + signature: { + r: 3243632040670678816425112099743675011873398345579979202080647260629177216981n, + s: 0n, + }, + msg: 0n, + publicKey: { + x: 28948022309329048855892746252171976963363056481941560715954676764349967630336n, + y: 2n, + }, + }); let signatureInputs = record({ privateKey, msg: scalar }); From 09f9fb9b401a4a36f360a4b26427321be5614788 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 29 Jan 2024 12:13:41 +0100 Subject: [PATCH 303/524] export token contract --- src/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.ts b/src/index.ts index d18500bfb7..c2eac0ee4a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,6 +41,10 @@ export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; export { MerkleList, MerkleArray } from './lib/provable-types/merkle-list.js'; +export { + CallForest, + CallForestIterator, +} from './lib/mina/token/call-forest.js'; export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; From 05b300b8cf0f9c736a9fe701efe9a4499b224fc7 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 31 Jan 2024 12:05:05 +0100 Subject: [PATCH 304/524] move call forest code next to account update --- src/index.ts | 7 +- src/lib/account_update.ts | 201 ++++++++++++++++++-- src/lib/mina.ts | 9 +- src/lib/mina/token/call-forest.ts | 65 +------ src/lib/mina/token/call-forest.unit-test.ts | 12 +- src/lib/provable-types/merkle-list.ts | 1 + src/lib/zkapp.ts | 4 + 7 files changed, 204 insertions(+), 95 deletions(-) diff --git a/src/index.ts b/src/index.ts index c2eac0ee4a..dc7607afa7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -41,10 +41,6 @@ export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; export { MerkleList, MerkleArray } from './lib/provable-types/merkle-list.js'; -export { - CallForest, - CallForestIterator, -} from './lib/mina/token/call-forest.js'; export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; @@ -75,8 +71,11 @@ export { AccountUpdate, Permissions, ZkappPublicInput, + CallForest, } from './lib/account_update.js'; +export { CallForestIterator } from './lib/mina/token/call-forest.js'; + export type { TransactionStatus } from './lib/fetch.js'; export { fetchAccount, diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index f585fed1cd..22e3d6ea9f 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -3,6 +3,8 @@ import { FlexibleProvable, provable, provablePure, + Struct, + Unconstrained, } from './circuit_value.js'; import { memoizationContext, memoizeWitness, Provable } from './provable.js'; import { Field, Bool } from './core.js'; @@ -25,7 +27,12 @@ import { Actions, } from '../bindings/mina-transaction/transaction-leaves.js'; import { TokenId as Base58TokenId } from './base58-encodings.js'; -import { hashWithPrefix, packToFields } from './hash.js'; +import { + hashWithPrefix, + packToFields, + Poseidon, + ProvableHashable, +} from './hash.js'; import { mocks, prefixes } from '../bindings/crypto/constants.js'; import { Context } from './global-context.js'; import { assert } from './errors.js'; @@ -33,9 +40,16 @@ import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; +import { + genericHash, + MerkleList, + MerkleListBase, + withHashes, +} from './provable-types/merkle-list.js'; +import { Hashed } from './provable-types/packed.js'; // external API -export { AccountUpdate, Permissions, ZkappPublicInput }; +export { AccountUpdate, Permissions, ZkappPublicInput, CallForest }; // internal API export { smartContractContext, @@ -53,12 +67,16 @@ export { Actions, TokenId, Token, - CallForest, + CallForestHelpers, createChildAccountUpdate, AccountUpdatesLayout, zkAppProver, SmartContractContext, dummySignature, + LazyProof, + CallTree, + CallForestUnderConstruction, + hashAccountUpdate, }; const ZkappStateLength = 8; @@ -1045,7 +1063,7 @@ class AccountUpdate implements Types.AccountUpdate { if (isSameAsFeePayer) nonce++; // now, we check how often this account update already updated its nonce in // this tx, and increase nonce from `getAccount` by that amount - CallForest.forEachPredecessor( + CallForestHelpers.forEachPredecessor( Mina.currentTransaction.get().accountUpdates, update as AccountUpdate, (otherUpdate) => { @@ -1092,7 +1110,7 @@ class AccountUpdate implements Types.AccountUpdate { toPublicInput(): ZkappPublicInput { let accountUpdate = this.hash(); - let calls = CallForest.hashChildren(this); + let calls = CallForestHelpers.hashChildren(this); return { accountUpdate, calls }; } @@ -1368,7 +1386,7 @@ class AccountUpdate implements Types.AccountUpdate { if (n === 0) { accountUpdate.children.callsType = { type: 'Equals', - value: CallForest.emptyHash(), + value: CallForestHelpers.emptyHash(), }; } } @@ -1573,7 +1591,148 @@ type WithCallers = { children: WithCallers[]; }; -const CallForest = { +// call forest stuff + +function hashAccountUpdate(update: AccountUpdate) { + return genericHash(AccountUpdate, prefixes.body, update); +} + +class HashedAccountUpdate extends Hashed.create( + AccountUpdate, + hashAccountUpdate +) {} + +type CallTree = { + accountUpdate: Hashed; + calls: MerkleListBase; +}; +const CallTree: ProvableHashable = Struct({ + accountUpdate: HashedAccountUpdate.provable, + calls: MerkleListBase(), +}); + +class CallForest extends MerkleList.create(CallTree, merkleListHash) { + static fromAccountUpdates(updates: AccountUpdate[]): CallForest { + let nodes = updates.map((update) => { + let accountUpdate = HashedAccountUpdate.hash(update); + let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); + return { accountUpdate, calls }; + }); + + return CallForest.from(nodes); + } +} + +// how to hash a forest + +function merkleListHash(forestHash: Field, tree: CallTree) { + return hashCons(forestHash, hashNode(tree)); +} +function hashNode(tree: CallTree) { + return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ + tree.accountUpdate.hash, + tree.calls.hash, + ]); +} +function hashCons(forestHash: Field, nodeHash: Field) { + return Poseidon.hashWithPrefix(prefixes.accountUpdateCons, [ + nodeHash, + forestHash, + ]); +} + +/** + * Structure for constructing a call forest from a circuit. + * + * The circuit can mutate account updates and change their array of children, so here we can't hash + * everything immediately. Instead, we maintain a structure consisting of either hashes or full account + * updates that can be hashed into a final call forest at the end. + */ +type CallForestUnderConstruction = HashOrValue< + { + accountUpdate: HashOrValue; + calls: CallForestUnderConstruction; + }[] +>; + +type HashOrValue = + | { useHash: true; hash: Field; value: T } + | { useHash: false; value: T }; + +const CallForestUnderConstruction = { + empty(): CallForestUnderConstruction { + return { useHash: false, value: [] }; + }, + + setHash(forest: CallForestUnderConstruction, hash: Field) { + Object.assign(forest, { useHash: true, hash }); + }, + + witnessHash(forest: CallForestUnderConstruction) { + let hash = Provable.witness(Field, () => { + let nodes = forest.value.map(toCallTree); + return withHashes(nodes, merkleListHash).hash; + }); + CallForestUnderConstruction.setHash(forest, hash); + }, + + push( + forest: CallForestUnderConstruction, + accountUpdate: HashOrValue, + calls?: CallForestUnderConstruction + ) { + forest.value.push({ + accountUpdate, + calls: calls ?? CallForestUnderConstruction.empty(), + }); + }, + + remove(forest: CallForestUnderConstruction, accountUpdate: AccountUpdate) { + // find account update by .id + let index = forest.value.findIndex( + (node) => node.accountUpdate.value.id === accountUpdate.id + ); + + // nothing to do if it's not there + if (index === -1) return; + + // remove it + forest.value.splice(index, 1); + }, + + finalize(forest: CallForestUnderConstruction): CallForest { + if (forest.useHash) { + let data = Unconstrained.witness(() => { + let nodes = forest.value.map(toCallTree); + return withHashes(nodes, merkleListHash).data; + }); + return new CallForest({ hash: forest.hash, data }); + } + + // not using the hash means we calculate it in-circuit + let nodes = forest.value.map(toCallTree); + return CallForest.from(nodes); + }, +}; + +function toCallTree(node: { + accountUpdate: HashOrValue; + calls: CallForestUnderConstruction; +}): CallTree { + let accountUpdate = node.accountUpdate.useHash + ? new HashedAccountUpdate( + node.accountUpdate.hash, + Unconstrained.from(node.accountUpdate.value) + ) + : HashedAccountUpdate.hash(node.accountUpdate.value); + + return { + accountUpdate, + calls: CallForestUnderConstruction.finalize(node.calls), + }; +} + +const CallForestHelpers = { // similar to Mina_base.ZkappCommand.Call_forest.to_account_updates_list // takes a list of accountUpdates, which each can have children, so they form a "forest" (list of trees) // returns a flattened list, with `accountUpdate.body.callDepth` specifying positions in the forest @@ -1590,7 +1749,7 @@ const CallForest = { let children = accountUpdate.children.accountUpdates; accountUpdates.push( accountUpdate, - ...CallForest.toFlatList(children, mutate, depth + 1) + ...CallForestHelpers.toFlatList(children, mutate, depth + 1) ); } return accountUpdates; @@ -1605,23 +1764,29 @@ const CallForest = { // hashes a accountUpdate's children (and their children, and ...) to compute // the `calls` field of ZkappPublicInput hashChildren(update: AccountUpdate): Field { + if (!Provable.inCheckedComputation()) { + return CallForestHelpers.hashChildrenBase(update); + } + let { callsType } = update.children; // compute hash outside the circuit if callsType is "Witness" // i.e., allowing accountUpdates with arbitrary children if (callsType.type === 'Witness') { - return Provable.witness(Field, () => CallForest.hashChildrenBase(update)); + return Provable.witness(Field, () => + CallForestHelpers.hashChildrenBase(update) + ); } - let calls = CallForest.hashChildrenBase(update); - if (callsType.type === 'Equals' && Provable.inCheckedComputation()) { + let calls = CallForestHelpers.hashChildrenBase(update); + if (callsType.type === 'Equals') { calls.assertEquals(callsType.value); } return calls; }, hashChildrenBase({ children }: AccountUpdate) { - let stackHash = CallForest.emptyHash(); + let stackHash = CallForestHelpers.emptyHash(); for (let accountUpdate of [...children.accountUpdates].reverse()) { - let calls = CallForest.hashChildren(accountUpdate); + let calls = CallForestHelpers.hashChildren(accountUpdate); let nodeHash = hashWithPrefix(prefixes.accountUpdateNode, [ accountUpdate.hash(), calls, @@ -1663,7 +1828,7 @@ const CallForest = { withCallers.push({ accountUpdate: update, caller, - children: CallForest.addCallers( + children: CallForestHelpers.addCallers( update.children.accountUpdates, childContext ), @@ -1711,7 +1876,7 @@ const CallForest = { let newUpdates: AccountUpdate[] = []; for (let update of updates) { let newUpdate = map(update); - newUpdate.children.accountUpdates = CallForest.map( + newUpdate.children.accountUpdates = CallForestHelpers.map( update.children.accountUpdates, map ); @@ -1723,7 +1888,7 @@ const CallForest = { forEach(updates: AccountUpdate[], callback: (update: AccountUpdate) => void) { for (let update of updates) { callback(update); - CallForest.forEach(update.children.accountUpdates, callback); + CallForestHelpers.forEach(update.children.accountUpdates, callback); } }, @@ -1733,7 +1898,7 @@ const CallForest = { callback: (update: AccountUpdate) => void ) { let isPredecessor = true; - CallForest.forEach(updates, (otherUpdate) => { + CallForestHelpers.forEach(updates, (otherUpdate) => { if (otherUpdate.id === update.id) isPredecessor = false; if (isPredecessor) callback(otherUpdate); }); @@ -1860,7 +2025,7 @@ const Authorization = { priorAccountUpdates = priorAccountUpdates.filter( (a) => a.id !== myAccountUpdateId ); - let priorAccountUpdatesFlat = CallForest.toFlatList( + let priorAccountUpdatesFlat = CallForestHelpers.toFlatList( priorAccountUpdates, false ); diff --git a/src/lib/mina.ts b/src/lib/mina.ts index fbac14657b..78ad16ecd4 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -10,7 +10,7 @@ import { AccountUpdate, ZkappPublicInput, TokenId, - CallForest, + CallForestHelpers, Authorization, Actions, Events, @@ -242,8 +242,9 @@ function createTransaction( f(); Provable.asProver(() => { let tx = currentTransaction.get(); - tx.accountUpdates = CallForest.map(tx.accountUpdates, (a) => - toConstant(AccountUpdate, a) + tx.accountUpdates = CallForestHelpers.map( + tx.accountUpdates, + (a) => toConstant(AccountUpdate, a) ); }); }); @@ -263,7 +264,7 @@ function createTransaction( let accountUpdates = currentTransaction.get().accountUpdates; // TODO: I'll be back // CallForest.addCallers(accountUpdates); - accountUpdates = CallForest.toFlatList(accountUpdates); + accountUpdates = CallForestHelpers.toFlatList(accountUpdates); try { // check that on-chain values weren't used without setting a precondition diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 2083842f5a..624a865938 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,47 +1,11 @@ -import { prefixes } from '../../../provable/poseidon-bigint.js'; -import { AccountUpdate, TokenId } from '../../account_update.js'; +import { AccountUpdate, CallForest, TokenId } from '../../account_update.js'; import { Field } from '../../core.js'; import { Provable } from '../../provable.js'; import { Struct } from '../../circuit_value.js'; import { assert } from '../../gadgets/common.js'; -import { Poseidon, ProvableHashable } from '../../hash.js'; -import { Hashed } from '../../provable-types/packed.js'; -import { - MerkleArray, - MerkleListBase, - MerkleList, - genericHash, -} from '../../provable-types/merkle-list.js'; - -export { CallForest, CallForestArray, CallForestIterator, hashAccountUpdate }; - -export { HashedAccountUpdate }; - -class HashedAccountUpdate extends Hashed.create( - AccountUpdate, - hashAccountUpdate -) {} - -type CallTree = { - accountUpdate: Hashed; - calls: MerkleListBase; -}; -const CallTree: ProvableHashable = Struct({ - accountUpdate: HashedAccountUpdate.provable, - calls: MerkleListBase(), -}); - -class CallForest extends MerkleList.create(CallTree, merkleListHash) { - static fromAccountUpdates(updates: AccountUpdate[]): CallForest { - let nodes = updates.map((update) => { - let accountUpdate = HashedAccountUpdate.hash(update); - let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); - return { accountUpdate, calls }; - }); +import { MerkleArray, MerkleList } from '../../provable-types/merkle-list.js'; - return CallForest.from(nodes); - } -} +export { CallForestArray, CallForestIterator }; class CallForestArray extends MerkleArray.createFromList(CallForest) {} @@ -150,26 +114,3 @@ class CallForestIterator { return { accountUpdate: update, usesThisToken }; } } - -// how to hash a forest - -function merkleListHash(forestHash: Field, tree: CallTree) { - return hashCons(forestHash, hashNode(tree)); -} - -function hashNode(tree: CallTree) { - return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ - tree.accountUpdate.hash, - tree.calls.hash, - ]); -} -function hashCons(forestHash: Field, nodeHash: Field) { - return Poseidon.hashWithPrefix(prefixes.accountUpdateCons, [ - nodeHash, - forestHash, - ]); -} - -function hashAccountUpdate(update: AccountUpdate) { - return genericHash(AccountUpdate, prefixes.body, update); -} diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index a9ca56bc56..fa5d46b508 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,14 +1,12 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { - CallForest, - CallForestIterator, - hashAccountUpdate, -} from './call-forest.js'; +import { CallForestIterator } from './call-forest.js'; import { AccountUpdate, - CallForest as ProvableCallForest, + CallForest, + CallForestHelpers, TokenId, + hashAccountUpdate, } from '../../account_update.js'; import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; import { Pickles } from '../../../snarky.js'; @@ -80,7 +78,7 @@ test(flatAccountUpdates, (flatUpdates) => { let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - let flatUpdates2 = ProvableCallForest.toFlatList(updates, false); + let flatUpdates2 = CallForestHelpers.toFlatList(updates, false); let n = flatUpdates.length; for (let i = 0; i < n; i++) { assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index 72dee6d025..0ee9c1500d 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -14,6 +14,7 @@ export { emptyHash, genericHash, merkleListHash, + withHashes, }; // common base types for both MerkleList and MerkleArray diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 9346973f72..cd4d662cc2 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -16,6 +16,9 @@ import { ZkappPublicInput, ZkappStateLength, SmartContractContext, + LazyProof, + CallForestHelpers, + CallForestUnderConstruction, } from './account_update.js'; import { cloneCircuitValue, @@ -56,6 +59,7 @@ import { snarkContext, } from './provable-context.js'; import { Cache } from './proof-system/cache.js'; +import { assert } from './gadgets/common.js'; // external API export { From 7cf2a85768dd38b7c5200dd697d6ce9dfb97014d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 11:38:19 +0100 Subject: [PATCH 305/524] remove accidental/premature changes --- src/lib/account_update.ts | 92 ------------------------------ src/lib/gadgets/ecdsa.unit-test.ts | 11 ---- src/lib/zkapp.ts | 4 -- 3 files changed, 107 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 22e3d6ea9f..8ee3739b4c 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -75,7 +75,6 @@ export { dummySignature, LazyProof, CallTree, - CallForestUnderConstruction, hashAccountUpdate, }; @@ -1641,97 +1640,6 @@ function hashCons(forestHash: Field, nodeHash: Field) { ]); } -/** - * Structure for constructing a call forest from a circuit. - * - * The circuit can mutate account updates and change their array of children, so here we can't hash - * everything immediately. Instead, we maintain a structure consisting of either hashes or full account - * updates that can be hashed into a final call forest at the end. - */ -type CallForestUnderConstruction = HashOrValue< - { - accountUpdate: HashOrValue; - calls: CallForestUnderConstruction; - }[] ->; - -type HashOrValue = - | { useHash: true; hash: Field; value: T } - | { useHash: false; value: T }; - -const CallForestUnderConstruction = { - empty(): CallForestUnderConstruction { - return { useHash: false, value: [] }; - }, - - setHash(forest: CallForestUnderConstruction, hash: Field) { - Object.assign(forest, { useHash: true, hash }); - }, - - witnessHash(forest: CallForestUnderConstruction) { - let hash = Provable.witness(Field, () => { - let nodes = forest.value.map(toCallTree); - return withHashes(nodes, merkleListHash).hash; - }); - CallForestUnderConstruction.setHash(forest, hash); - }, - - push( - forest: CallForestUnderConstruction, - accountUpdate: HashOrValue, - calls?: CallForestUnderConstruction - ) { - forest.value.push({ - accountUpdate, - calls: calls ?? CallForestUnderConstruction.empty(), - }); - }, - - remove(forest: CallForestUnderConstruction, accountUpdate: AccountUpdate) { - // find account update by .id - let index = forest.value.findIndex( - (node) => node.accountUpdate.value.id === accountUpdate.id - ); - - // nothing to do if it's not there - if (index === -1) return; - - // remove it - forest.value.splice(index, 1); - }, - - finalize(forest: CallForestUnderConstruction): CallForest { - if (forest.useHash) { - let data = Unconstrained.witness(() => { - let nodes = forest.value.map(toCallTree); - return withHashes(nodes, merkleListHash).data; - }); - return new CallForest({ hash: forest.hash, data }); - } - - // not using the hash means we calculate it in-circuit - let nodes = forest.value.map(toCallTree); - return CallForest.from(nodes); - }, -}; - -function toCallTree(node: { - accountUpdate: HashOrValue; - calls: CallForestUnderConstruction; -}): CallTree { - let accountUpdate = node.accountUpdate.useHash - ? new HashedAccountUpdate( - node.accountUpdate.hash, - Unconstrained.from(node.accountUpdate.value) - ) - : HashedAccountUpdate.hash(node.accountUpdate.value); - - return { - accountUpdate, - calls: CallForestUnderConstruction.finalize(node.calls), - }; -} - const CallForestHelpers = { // similar to Mina_base.ZkappCommand.Call_forest.to_account_updates_list // takes a list of accountUpdates, which each can have children, so they form a "forest" (list of trees) diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index f21adb722f..250b088709 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -43,17 +43,6 @@ for (let Curve of curves) { msg: scalar, publicKey: record({ x: field, y: field }), }); - badSignature.rng = Random.withHardCoded(badSignature.rng, { - signature: { - r: 3243632040670678816425112099743675011873398345579979202080647260629177216981n, - s: 0n, - }, - msg: 0n, - publicKey: { - x: 28948022309329048855892746252171976963363056481941560715954676764349967630336n, - y: 2n, - }, - }); let signatureInputs = record({ privateKey, msg: scalar }); diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index cd4d662cc2..9346973f72 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -16,9 +16,6 @@ import { ZkappPublicInput, ZkappStateLength, SmartContractContext, - LazyProof, - CallForestHelpers, - CallForestUnderConstruction, } from './account_update.js'; import { cloneCircuitValue, @@ -59,7 +56,6 @@ import { snarkContext, } from './provable-context.js'; import { Cache } from './proof-system/cache.js'; -import { assert } from './gadgets/common.js'; // external API export { From 4632f93f578fa6f12bd87011cac9301d6315adb2 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 11:40:51 +0100 Subject: [PATCH 306/524] prune import not needed yet --- src/lib/account_update.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 8ee3739b4c..4ab04e792b 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -4,7 +4,6 @@ import { provable, provablePure, Struct, - Unconstrained, } from './circuit_value.js'; import { memoizationContext, memoizeWitness, Provable } from './provable.js'; import { Field, Bool } from './core.js'; @@ -44,7 +43,6 @@ import { genericHash, MerkleList, MerkleListBase, - withHashes, } from './provable-types/merkle-list.js'; import { Hashed } from './provable-types/packed.js'; From b8b5cf6c2fc95ba7f5eee1481f8c2e55ed49097c Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 12:18:58 +0100 Subject: [PATCH 307/524] renames and changelog --- CHANGELOG.md | 3 + src/index.ts | 9 ++- src/lib/account_update.ts | 22 ++++-- src/lib/mina/token/call-forest.ts | 56 ++++++++++----- src/lib/mina/token/call-forest.unit-test.ts | 20 +++--- src/lib/provable-types/merkle-list.ts | 75 ++++++++++++--------- 6 files changed, 122 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 433daf8f5e..1f9f78f9a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,9 +23,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added +- `MerkleList` to enable provable operations on a dynamically-sized list https://github.com/o1-labs/o1js/pull/1398 + - including `MerkleListIterator` to iterate over a merkle list - Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 - Provable type `Hashed` to represent provable types by their hash https://github.com/o1-labs/o1js/pull/1377 - This also exposes `Poseidon.hashPacked()` to efficiently hash an arbitrary type +- `TokenAccountUpdateIterator`, a primitive for token contracts to iterate over all token account updates in a transaction. https://github.com/o1-labs/o1js/pull/1398 ## [0.15.4](https://github.com/o1-labs/o1js/compare/be748e42e...e5d1e0f) diff --git a/src/index.ts b/src/index.ts index dc7607afa7..405edf638c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,7 +40,10 @@ export { Packed, Hashed } from './lib/provable-types/packed.js'; export { Gadgets } from './lib/gadgets/gadgets.js'; export { Types } from './bindings/mina-transaction/types.js'; -export { MerkleList, MerkleArray } from './lib/provable-types/merkle-list.js'; +export { + MerkleList, + MerkleListIterator, +} from './lib/provable-types/merkle-list.js'; export * as Mina from './lib/mina.js'; export type { DeployArgs } from './lib/zkapp.js'; @@ -71,10 +74,10 @@ export { AccountUpdate, Permissions, ZkappPublicInput, - CallForest, + AccountUpdateForest, } from './lib/account_update.js'; -export { CallForestIterator } from './lib/mina/token/call-forest.js'; +export { TokenAccountUpdateIterator } from './lib/mina/token/call-forest.js'; export type { TransactionStatus } from './lib/fetch.js'; export { diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 4ab04e792b..5e3332f7af 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -47,7 +47,7 @@ import { import { Hashed } from './provable-types/packed.js'; // external API -export { AccountUpdate, Permissions, ZkappPublicInput, CallForest }; +export { AccountUpdate, Permissions, ZkappPublicInput, AccountUpdateForest }; // internal API export { smartContractContext, @@ -1608,15 +1608,27 @@ const CallTree: ProvableHashable = Struct({ calls: MerkleListBase(), }); -class CallForest extends MerkleList.create(CallTree, merkleListHash) { - static fromAccountUpdates(updates: AccountUpdate[]): CallForest { +/** + * Class which represents a forest (list of trees) of account updates, + * in a compressed way which allows iterating and selectively witnessing the account updates. + * + * The (recursive) type signature is: + * ``` + * type AccountUpdateForest = MerkleList<{ + * accountUpdate: Hashed; + * calls: AccountUpdateForest; + * }>; + * ``` + */ +class AccountUpdateForest extends MerkleList.create(CallTree, merkleListHash) { + static fromArray(updates: AccountUpdate[]): AccountUpdateForest { let nodes = updates.map((update) => { let accountUpdate = HashedAccountUpdate.hash(update); - let calls = CallForest.fromAccountUpdates(update.children.accountUpdates); + let calls = AccountUpdateForest.fromArray(update.children.accountUpdates); return { accountUpdate, calls }; }); - return CallForest.from(nodes); + return AccountUpdateForest.from(nodes); } } diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 624a865938..c5878991db 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -1,16 +1,24 @@ -import { AccountUpdate, CallForest, TokenId } from '../../account_update.js'; +import { + AccountUpdate, + AccountUpdateForest, + TokenId, +} from '../../account_update.js'; import { Field } from '../../core.js'; import { Provable } from '../../provable.js'; import { Struct } from '../../circuit_value.js'; -import { assert } from '../../gadgets/common.js'; -import { MerkleArray, MerkleList } from '../../provable-types/merkle-list.js'; +import { + MerkleListIterator, + MerkleList, +} from '../../provable-types/merkle-list.js'; -export { CallForestArray, CallForestIterator }; +export { AccountUpdateIterator, TokenAccountUpdateIterator }; -class CallForestArray extends MerkleArray.createFromList(CallForest) {} +class AccountUpdateIterator extends MerkleListIterator.createFromList( + AccountUpdateForest +) {} class Layer extends Struct({ - forest: CallForestArray.provable, + forest: AccountUpdateIterator.provable, mayUseToken: AccountUpdate.MayUseToken.type, }) {} const ParentLayers = MerkleList.create(Layer); @@ -19,18 +27,36 @@ type MayUseToken = AccountUpdate['body']['mayUseToken']; const MayUseToken = AccountUpdate.MayUseToken; /** - * Data structure to represent a forest tree of account updates that is being iterated over. + * Data structure to represent a forest of account updates that is being iterated over, + * in the context of a token manager contract. * - * Important: Since this is to be used for token manager contracts to process it's entire subtree - * of account updates, the iterator skips subtrees that don't inherit token permissions. + * The iteration is done in a depth-first manner. + * + * ```ts + * let forest: AccountUpdateForest = ...; + * let tokenIterator = TokenAccountUpdateIterator.create(forest, tokenId); + * + * // process the first 5 account updates in the tree + * for (let i = 0; i < 5; i++) { + * let { accountUpdate, usesThisToken } = tokenIterator.next(); + * // ... do something with the account update ... + * } + * ``` + * + * **Important**: Since this is specifically used by token manager contracts to process their entire subtree + * of account updates, the iterator skips subtrees that don't inherit token permissions and can therefore definitely not use the token. + * + * So, the assumption is that the consumer of this iterator is only interested in account updates that use the token. + * We still can't avoid processing some account updates that don't use the token, therefore the iterator returns a boolean + * `usesThisToken` alongside each account update. */ -class CallForestIterator { +class TokenAccountUpdateIterator { currentLayer: Layer; unfinishedParentLayers: MerkleList; selfToken: Field; constructor( - forest: CallForestArray, + forest: AccountUpdateIterator, mayUseToken: MayUseToken, selfToken: Field ) { @@ -39,9 +65,9 @@ class CallForestIterator { this.selfToken = selfToken; } - static create(forest: CallForest, selfToken: Field) { - return new CallForestIterator( - CallForestArray.startIterating(forest), + static create(forest: AccountUpdateForest, selfToken: Field) { + return new TokenAccountUpdateIterator( + AccountUpdateIterator.startIterating(forest), MayUseToken.ParentsOwnToken, selfToken ); @@ -62,7 +88,7 @@ class CallForestIterator { // get next account update from the current forest (might be a dummy) // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); - let forest = CallForestArray.startIterating(calls); + let forest = AccountUpdateIterator.startIterating(calls); let parentForest = this.currentLayer.forest; this.unfinishedParentLayers.pushIf(parentForest.isAtEnd().not(), { diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/call-forest.unit-test.ts index fa5d46b508..7b093f829e 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/call-forest.unit-test.ts @@ -1,9 +1,9 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { CallForestIterator } from './call-forest.js'; +import { TokenAccountUpdateIterator } from './call-forest.js'; import { AccountUpdate, - CallForest, + AccountUpdateForest, CallForestHelpers, TokenId, hashAccountUpdate, @@ -66,7 +66,7 @@ test.custom({ timeBudget: 1000 })( accountUpdatesToCallForest(flatUpdates) ); - let forest = CallForest.fromAccountUpdates(updates); + let forest = AccountUpdateForest.fromArray(updates); forest.hash.assertEquals(expectedHash); } ); @@ -93,7 +93,7 @@ test.custom({ timeBudget: 1000 })(flatAccountUpdates, (flatUpdates) => { let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - let forest = CallForest.fromAccountUpdates(updates).startIterating(); + let forest = AccountUpdateForest.fromArray(updates).startIterating(); // step through top-level by calling forest.next() repeatedly let n = updates.length; @@ -119,8 +119,8 @@ test.custom({ timeBudget: 5000 })(flatAccountUpdates, (flatUpdates) => { let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - let forest = CallForest.fromAccountUpdates(updates); - let forestIterator = CallForestIterator.create(forest, tokenId); + let forest = AccountUpdateForest.fromArray(updates); + let forestIterator = TokenAccountUpdateIterator.create(forest, tokenId); // step through forest iterator and compare against expected updates let expectedUpdates = flatUpdates; @@ -171,8 +171,8 @@ test.custom({ timeBudget: 5000 })( } }); - let forest = CallForest.fromAccountUpdates(updates); - let forestIterator = CallForestIterator.create(forest, tokenId); + let forest = AccountUpdateForest.fromArray(updates); + let forestIterator = TokenAccountUpdateIterator.create(forest, tokenId); // step through forest iterator and compare against expected updates let expectedUpdates = updates; @@ -217,8 +217,8 @@ test.custom({ timeBudget: 5000 })( let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - let forest = CallForest.fromAccountUpdates(updates); - let forestIterator = CallForestIterator.create(forest, tokenId); + let forest = AccountUpdateForest.fromArray(updates); + let forestIterator = TokenAccountUpdateIterator.create(forest, tokenId); // step through forest iterator and compare against expected updates let expectedUpdates = flatUpdates.filter((u) => u.body.callDepth <= 1); diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index 0ee9c1500d..c74f734cf3 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -8,8 +8,8 @@ import { Poseidon, packToFields, ProvableHashable } from '../hash.js'; export { MerkleListBase, MerkleList, + MerkleListIteratorBase, MerkleListIterator, - MerkleArray, WithHash, emptyHash, genericHash, @@ -17,7 +17,7 @@ export { withHashes, }; -// common base types for both MerkleList and MerkleArray +// common base types for both MerkleList and MerkleListIterator const emptyHash = Field(0); @@ -28,7 +28,7 @@ function WithHash(type: ProvableHashable): ProvableHashable> { } /** - * Common base type for {@link MerkleList} and {@link MerkleArray} + * Common base type for {@link MerkleList} and {@link MerkleListIterator} */ type MerkleListBase = { hash: Field; @@ -51,8 +51,9 @@ function MerkleListBase(): ProvableHashable> { * Supported operations are {@link push()} and {@link pop()} and some variants thereof. * * **Important:** `push()` adds elements to the _start_ of the internal array and `pop()` removes them from the start. - * This is so that the hash which represents the list is consistent with {@link MerkleArray}, - * and so a `MerkleList` can be used as input to `MerkleArray.startIterating(list)` (which will then iterate starting from the last pushed element). + * This is so that the hash which represents the list is consistent with {@link MerkleListIterator}, + * and so a `MerkleList` can be used as input to `MerkleListIterator.startIterating(list)` + * (which will then iterate starting from the last pushed element). * * A Merkle list is generic over its element types, so before using it you must create a subclass for your element type: * @@ -135,8 +136,8 @@ class MerkleList implements MerkleListBase { return new this.Constructor({ hash: this.hash, data }); } - startIterating(): MerkleArray { - let merkleArray = MerkleArray.createFromList(this.Constructor); + startIterating(): MerkleListIterator { + let merkleArray = MerkleListIterator.createFromList(this.Constructor); return merkleArray.startIterating(this); } @@ -214,9 +215,9 @@ class MerkleList implements MerkleListBase { } } -// merkle array +// merkle list iterator -type MerkleListIterator = { +type MerkleListIteratorBase = { readonly hash: Field; readonly data: Unconstrained[]>; @@ -235,14 +236,22 @@ type MerkleListIterator = { }; /** - * MerkleArray is similar to a MerkleList, but it maintains the entire array througout a computation, + * MerkleListIterator is similar to a MerkleList, but it maintains the entire array througout a computation, * instead of mutating itself / throwing away context while stepping through it. * + * The core method that support iteration is {@link next()}. + * + * ```ts + * let iterator = MerkleListIterator.startIterating(list); + * + * let firstElement = iterator.next(); + * ``` + * * We maintain two commitments, both of which are equivalent to a Merkle list hash starting _from the end_ of the array: * - One to the entire array, to prove that we start iterating at the beginning. * - One to the array from the current index until the end, to efficiently step forward. */ -class MerkleArray implements MerkleListIterator { +class MerkleListIterator implements MerkleListIteratorBase { // fixed parts readonly data: Unconstrained[]>; readonly hash: Field; @@ -251,7 +260,7 @@ class MerkleArray implements MerkleListIterator { currentHash: Field; currentIndex: Unconstrained; - constructor(value: MerkleListIterator) { + constructor(value: MerkleListIteratorBase) { Object.assign(this, value); } @@ -307,7 +316,7 @@ class MerkleArray implements MerkleListIterator { ); } - clone(): MerkleArray { + clone(): MerkleListIterator { let data = Unconstrained.witness(() => [...this.data.get()]); let currentIndex = Unconstrained.witness(() => this.currentIndex.get()); return new this.Constructor({ @@ -324,13 +333,13 @@ class MerkleArray implements MerkleListIterator { static create( type: ProvableHashable, nextHash: (hash: Field, value: T) => Field = merkleListHash(type) - ): typeof MerkleArray & { - from: (array: T[]) => MerkleArray; - startIterating: (list: MerkleListBase) => MerkleArray; - empty: () => MerkleArray; - provable: ProvableHashable>; + ): typeof MerkleListIterator & { + from: (array: T[]) => MerkleListIterator; + startIterating: (list: MerkleListBase) => MerkleListIterator; + empty: () => MerkleListIterator; + provable: ProvableHashable>; } { - return class MerkleArray_ extends MerkleArray { + return class MerkleArray_ extends MerkleListIterator { static _innerProvable = type; static _provable = provableFromClass(MerkleArray_, { @@ -338,18 +347,21 @@ class MerkleArray implements MerkleListIterator { data: Unconstrained.provable, currentHash: Field, currentIndex: Unconstrained.provable, - }) satisfies ProvableHashable> as ProvableHashable< - MerkleArray + }) satisfies ProvableHashable> as ProvableHashable< + MerkleListIterator >; static _nextHash = nextHash; - static from(array: T[]): MerkleArray { + static from(array: T[]): MerkleListIterator { let { hash, data } = withHashes(array, nextHash); return this.startIterating({ data: Unconstrained.from(data), hash }); } - static startIterating({ data, hash }: MerkleListBase): MerkleArray { + static startIterating({ + data, + hash, + }: MerkleListBase): MerkleListIterator { return new this({ data, hash, @@ -358,12 +370,15 @@ class MerkleArray implements MerkleListIterator { }); } - static empty(): MerkleArray { + static empty(): MerkleListIterator { return this.from([]); } - static get provable(): ProvableHashable> { - assert(this._provable !== undefined, 'MerkleArray not initialized'); + static get provable(): ProvableHashable> { + assert( + this._provable !== undefined, + 'MerkleListIterator not initialized' + ); return this._provable; } }; @@ -379,17 +394,17 @@ class MerkleArray implements MerkleListIterator { // dynamic subclassing infra static _nextHash: ((hash: Field, value: any) => Field) | undefined; - static _provable: ProvableHashable> | undefined; + static _provable: ProvableHashable> | undefined; static _innerProvable: ProvableHashable | undefined; get Constructor() { - return this.constructor as typeof MerkleArray; + return this.constructor as typeof MerkleListIterator; } nextHash(hash: Field, value: T): Field { assert( this.Constructor._nextHash !== undefined, - 'MerkleArray not initialized' + 'MerkleListIterator not initialized' ); return this.Constructor._nextHash(hash, value); } @@ -397,7 +412,7 @@ class MerkleArray implements MerkleListIterator { get innerProvable(): ProvableHashable { assert( this.Constructor._innerProvable !== undefined, - 'MerkleArray not initialized' + 'MerkleListIterator not initialized' ); return this.Constructor._innerProvable; } From 6792c903999e3feb64cefe67818b9b8ea3ed52b6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 12:34:26 +0100 Subject: [PATCH 308/524] fixup --- src/lib/account_update.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 3e6c820a04..05190faf67 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1547,6 +1547,18 @@ const CallTree: ProvableHashable = Struct({ calls: MerkleListBase(), }); +/** + * Class which represents a forest (list of trees) of account updates, + * in a compressed way which allows iterating and selectively witnessing the account updates. + * + * The (recursive) type signature is: + * ``` + * type AccountUpdateForest = MerkleList<{ + * accountUpdate: Hashed; + * calls: AccountUpdateForest; + * }>; + * ``` + */ class AccountUpdateForest extends MerkleList.create(CallTree, merkleListHash) { static fromArray(updates: AccountUpdate[]): AccountUpdateForest { let nodes = updates.map((update) => { From 0162679d79dfdf5ffd552f6ca0cc528b6751f9a8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 12:36:06 +0100 Subject: [PATCH 309/524] fixup --- src/lib/mina/token/call-forest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/call-forest.ts index 1b7f51ddea..1eaebf69d5 100644 --- a/src/lib/mina/token/call-forest.ts +++ b/src/lib/mina/token/call-forest.ts @@ -144,7 +144,7 @@ class TokenAccountUpdateIterator { assertFinished(message?: string) { assert( this.currentLayer.forest.isAtEnd(), - message ?? 'CallForest not finished' + message ?? 'TokenAccountUpdateIterator not finished' ); } } From b8ac126b64ac10c3e51de9bb182ce1a066da63ef Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 12:40:48 +0100 Subject: [PATCH 310/524] better file name --- src/index.ts | 2 +- src/lib/mina/token/{call-forest.ts => forest-iterator.ts} | 0 .../{call-forest.unit-test.ts => forest-iterator.unit-test.ts} | 2 +- src/lib/mina/token/token-contract.ts | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename src/lib/mina/token/{call-forest.ts => forest-iterator.ts} (100%) rename src/lib/mina/token/{call-forest.unit-test.ts => forest-iterator.unit-test.ts} (99%) diff --git a/src/index.ts b/src/index.ts index f00480667a..3078d416e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -77,7 +77,7 @@ export { AccountUpdateForest, } from './lib/account_update.js'; -export { TokenAccountUpdateIterator } from './lib/mina/token/call-forest.js'; +export { TokenAccountUpdateIterator } from './lib/mina/token/forest-iterator.js'; export { TokenContract } from './lib/mina/token/token-contract.js'; export type { TransactionStatus } from './lib/fetch.js'; diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/forest-iterator.ts similarity index 100% rename from src/lib/mina/token/call-forest.ts rename to src/lib/mina/token/forest-iterator.ts diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/forest-iterator.unit-test.ts similarity index 99% rename from src/lib/mina/token/call-forest.unit-test.ts rename to src/lib/mina/token/forest-iterator.unit-test.ts index 7b093f829e..d6a18affde 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/forest-iterator.unit-test.ts @@ -1,6 +1,6 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { TokenAccountUpdateIterator } from './call-forest.js'; +import { TokenAccountUpdateIterator } from './forest-iterator.js'; import { AccountUpdate, AccountUpdateForest, diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index b936b5a1d3..456c84c495 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -12,7 +12,7 @@ import { smartContractContext, } from '../../account_update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; -import { TokenAccountUpdateIterator } from './call-forest.js'; +import { TokenAccountUpdateIterator } from './forest-iterator.js'; export { TokenContract }; From 12060ae92f766139e6b1b537d1559c00e3b5c876 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 12:40:48 +0100 Subject: [PATCH 311/524] better file name --- src/index.ts | 2 +- src/lib/mina/token/{call-forest.ts => forest-iterator.ts} | 0 .../{call-forest.unit-test.ts => forest-iterator.unit-test.ts} | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/lib/mina/token/{call-forest.ts => forest-iterator.ts} (100%) rename src/lib/mina/token/{call-forest.unit-test.ts => forest-iterator.unit-test.ts} (99%) diff --git a/src/index.ts b/src/index.ts index 405edf638c..91ddfa7a29 100644 --- a/src/index.ts +++ b/src/index.ts @@ -77,7 +77,7 @@ export { AccountUpdateForest, } from './lib/account_update.js'; -export { TokenAccountUpdateIterator } from './lib/mina/token/call-forest.js'; +export { TokenAccountUpdateIterator } from './lib/mina/token/forest-iterator.js'; export type { TransactionStatus } from './lib/fetch.js'; export { diff --git a/src/lib/mina/token/call-forest.ts b/src/lib/mina/token/forest-iterator.ts similarity index 100% rename from src/lib/mina/token/call-forest.ts rename to src/lib/mina/token/forest-iterator.ts diff --git a/src/lib/mina/token/call-forest.unit-test.ts b/src/lib/mina/token/forest-iterator.unit-test.ts similarity index 99% rename from src/lib/mina/token/call-forest.unit-test.ts rename to src/lib/mina/token/forest-iterator.unit-test.ts index 7b093f829e..d6a18affde 100644 --- a/src/lib/mina/token/call-forest.unit-test.ts +++ b/src/lib/mina/token/forest-iterator.unit-test.ts @@ -1,6 +1,6 @@ import { Random, test } from '../../testing/property.js'; import { RandomTransaction } from '../../../mina-signer/src/random-transaction.js'; -import { TokenAccountUpdateIterator } from './call-forest.js'; +import { TokenAccountUpdateIterator } from './forest-iterator.js'; import { AccountUpdate, AccountUpdateForest, From ef5cc97b1ee12a55b674c2c3e8d1d6bcc65f7e15 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 12:46:22 +0100 Subject: [PATCH 312/524] update comment --- src/lib/gadgets/elliptic-curve.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts index 9dbce8e544..87876e788c 100644 --- a/src/lib/gadgets/elliptic-curve.ts +++ b/src/lib/gadgets/elliptic-curve.ts @@ -414,8 +414,8 @@ function multiScalarMul( sliceField3(s, { maxBits, chunkSize: windowSizes[i] }) ); - // pack points to make array access more efficient - // a Point is 6 x 88-bit field elements, which are packed into 3 field elements + // hash points to make array access more efficient + // a Point is 6 field elements, the hash is just 1 field element const HashedPoint = Hashed.create(Point.provable); let hashedTables = tables.map((table) => From 687891afba3ea9e4ef11aff5486d19076ba2e16b Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 12:51:30 +0100 Subject: [PATCH 313/524] revert rename which became unnecessary --- src/lib/account_update.ts | 34 +++++++++---------- src/lib/mina.ts | 20 +++++------ .../mina/token/forest-iterator.unit-test.ts | 4 +-- 3 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 5e3332f7af..af5df67640 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -65,7 +65,7 @@ export { Actions, TokenId, Token, - CallForestHelpers, + CallForest, createChildAccountUpdate, AccountUpdatesLayout, zkAppProver, @@ -1060,7 +1060,7 @@ class AccountUpdate implements Types.AccountUpdate { if (isSameAsFeePayer) nonce++; // now, we check how often this account update already updated its nonce in // this tx, and increase nonce from `getAccount` by that amount - CallForestHelpers.forEachPredecessor( + CallForest.forEachPredecessor( Mina.currentTransaction.get().accountUpdates, update as AccountUpdate, (otherUpdate) => { @@ -1107,7 +1107,7 @@ class AccountUpdate implements Types.AccountUpdate { toPublicInput(): ZkappPublicInput { let accountUpdate = this.hash(); - let calls = CallForestHelpers.hashChildren(this); + let calls = CallForest.hashChildren(this); return { accountUpdate, calls }; } @@ -1383,7 +1383,7 @@ class AccountUpdate implements Types.AccountUpdate { if (n === 0) { accountUpdate.children.callsType = { type: 'Equals', - value: CallForestHelpers.emptyHash(), + value: CallForest.emptyHash(), }; } } @@ -1650,7 +1650,7 @@ function hashCons(forestHash: Field, nodeHash: Field) { ]); } -const CallForestHelpers = { +const CallForest = { // similar to Mina_base.ZkappCommand.Call_forest.to_account_updates_list // takes a list of accountUpdates, which each can have children, so they form a "forest" (list of trees) // returns a flattened list, with `accountUpdate.body.callDepth` specifying positions in the forest @@ -1667,7 +1667,7 @@ const CallForestHelpers = { let children = accountUpdate.children.accountUpdates; accountUpdates.push( accountUpdate, - ...CallForestHelpers.toFlatList(children, mutate, depth + 1) + ...CallForest.toFlatList(children, mutate, depth + 1) ); } return accountUpdates; @@ -1683,18 +1683,16 @@ const CallForestHelpers = { // the `calls` field of ZkappPublicInput hashChildren(update: AccountUpdate): Field { if (!Provable.inCheckedComputation()) { - return CallForestHelpers.hashChildrenBase(update); + return CallForest.hashChildrenBase(update); } let { callsType } = update.children; // compute hash outside the circuit if callsType is "Witness" // i.e., allowing accountUpdates with arbitrary children if (callsType.type === 'Witness') { - return Provable.witness(Field, () => - CallForestHelpers.hashChildrenBase(update) - ); + return Provable.witness(Field, () => CallForest.hashChildrenBase(update)); } - let calls = CallForestHelpers.hashChildrenBase(update); + let calls = CallForest.hashChildrenBase(update); if (callsType.type === 'Equals') { calls.assertEquals(callsType.value); } @@ -1702,9 +1700,9 @@ const CallForestHelpers = { }, hashChildrenBase({ children }: AccountUpdate) { - let stackHash = CallForestHelpers.emptyHash(); + let stackHash = CallForest.emptyHash(); for (let accountUpdate of [...children.accountUpdates].reverse()) { - let calls = CallForestHelpers.hashChildren(accountUpdate); + let calls = CallForest.hashChildren(accountUpdate); let nodeHash = hashWithPrefix(prefixes.accountUpdateNode, [ accountUpdate.hash(), calls, @@ -1746,7 +1744,7 @@ const CallForestHelpers = { withCallers.push({ accountUpdate: update, caller, - children: CallForestHelpers.addCallers( + children: CallForest.addCallers( update.children.accountUpdates, childContext ), @@ -1794,7 +1792,7 @@ const CallForestHelpers = { let newUpdates: AccountUpdate[] = []; for (let update of updates) { let newUpdate = map(update); - newUpdate.children.accountUpdates = CallForestHelpers.map( + newUpdate.children.accountUpdates = CallForest.map( update.children.accountUpdates, map ); @@ -1806,7 +1804,7 @@ const CallForestHelpers = { forEach(updates: AccountUpdate[], callback: (update: AccountUpdate) => void) { for (let update of updates) { callback(update); - CallForestHelpers.forEach(update.children.accountUpdates, callback); + CallForest.forEach(update.children.accountUpdates, callback); } }, @@ -1816,7 +1814,7 @@ const CallForestHelpers = { callback: (update: AccountUpdate) => void ) { let isPredecessor = true; - CallForestHelpers.forEach(updates, (otherUpdate) => { + CallForest.forEach(updates, (otherUpdate) => { if (otherUpdate.id === update.id) isPredecessor = false; if (isPredecessor) callback(otherUpdate); }); @@ -1943,7 +1941,7 @@ const Authorization = { priorAccountUpdates = priorAccountUpdates.filter( (a) => a.id !== myAccountUpdateId ); - let priorAccountUpdatesFlat = CallForestHelpers.toFlatList( + let priorAccountUpdatesFlat = CallForest.toFlatList( priorAccountUpdates, false ); diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 78ad16ecd4..7961cc3943 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -10,7 +10,7 @@ import { AccountUpdate, ZkappPublicInput, TokenId, - CallForestHelpers, + CallForest, Authorization, Actions, Events, @@ -242,9 +242,8 @@ function createTransaction( f(); Provable.asProver(() => { let tx = currentTransaction.get(); - tx.accountUpdates = CallForestHelpers.map( - tx.accountUpdates, - (a) => toConstant(AccountUpdate, a) + tx.accountUpdates = CallForest.map(tx.accountUpdates, (a) => + toConstant(AccountUpdate, a) ); }); }); @@ -264,7 +263,7 @@ function createTransaction( let accountUpdates = currentTransaction.get().accountUpdates; // TODO: I'll be back // CallForest.addCallers(accountUpdates); - accountUpdates = CallForestHelpers.toFlatList(accountUpdates); + accountUpdates = CallForest.toFlatList(accountUpdates); try { // check that on-chain values weren't used without setting a precondition @@ -430,8 +429,8 @@ function LocalBlockchain({ getNetworkId: () => minaNetworkId, proofsEnabled, /** - * @deprecated use {@link Mina.getNetworkConstants} - */ + * @deprecated use {@link Mina.getNetworkConstants} + */ accountCreationFee: () => defaultNetworkConstants.accountCreationFee, getNetworkConstants() { return { @@ -519,7 +518,8 @@ function LocalBlockchain({ // TODO: label updates, and try to give precise explanations about what went wrong let errors = JSON.parse(err.message); err.message = invalidTransactionError(txn.transaction, errors, { - accountCreationFee: defaultNetworkConstants.accountCreationFee.toString(), + accountCreationFee: + defaultNetworkConstants.accountCreationFee.toString(), }); } finally { throw err; @@ -765,8 +765,8 @@ function Network( return { getNetworkId: () => minaNetworkId, /** - * @deprecated use {@link Mina.getNetworkConstants} - */ + * @deprecated use {@link Mina.getNetworkConstants} + */ accountCreationFee: () => defaultNetworkConstants.accountCreationFee, getNetworkConstants() { if (currentTransaction()?.fetchMode === 'test') { diff --git a/src/lib/mina/token/forest-iterator.unit-test.ts b/src/lib/mina/token/forest-iterator.unit-test.ts index d6a18affde..06c9981847 100644 --- a/src/lib/mina/token/forest-iterator.unit-test.ts +++ b/src/lib/mina/token/forest-iterator.unit-test.ts @@ -4,7 +4,7 @@ import { TokenAccountUpdateIterator } from './forest-iterator.js'; import { AccountUpdate, AccountUpdateForest, - CallForestHelpers, + CallForest, TokenId, hashAccountUpdate, } from '../../account_update.js'; @@ -78,7 +78,7 @@ test(flatAccountUpdates, (flatUpdates) => { let updates = callForestToNestedArray( accountUpdatesToCallForest(flatUpdates) ); - let flatUpdates2 = CallForestHelpers.toFlatList(updates, false); + let flatUpdates2 = CallForest.toFlatList(updates, false); let n = flatUpdates.length; for (let i = 0; i < n; i++) { assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); From 1a4bd8a6fc5616f637fd3fab21dd3aca9dbfe692 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 12:51:33 +0100 Subject: [PATCH 314/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 772ce4ba92..55e313bfad 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 772ce4ba92e63453253250bb706339016a8d1e8c +Subproject commit 55e313bfadc0659fb0b2e528dc43e82836ef7e7a From cfc8172f04c5249d8c62b7fd5899a8e3ecf53c18 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 13:01:48 +0100 Subject: [PATCH 315/524] more renaming --- src/lib/account_update.ts | 24 ++++++++++++++---------- src/lib/mina/token/forest-iterator.ts | 10 +++++----- src/lib/provable-types/merkle-list.ts | 4 ++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index af5df67640..e3caa5ca58 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -72,7 +72,7 @@ export { SmartContractContext, dummySignature, LazyProof, - CallTree, + AccountUpdateTree, hashAccountUpdate, }; @@ -1599,13 +1599,13 @@ class HashedAccountUpdate extends Hashed.create( hashAccountUpdate ) {} -type CallTree = { +type AccountUpdateTree = { accountUpdate: Hashed; - calls: MerkleListBase; + calls: MerkleListBase; }; -const CallTree: ProvableHashable = Struct({ +const AccountUpdateTree: ProvableHashable = Struct({ accountUpdate: HashedAccountUpdate.provable, - calls: MerkleListBase(), + calls: MerkleListBase(), }); /** @@ -1614,13 +1614,17 @@ const CallTree: ProvableHashable = Struct({ * * The (recursive) type signature is: * ``` - * type AccountUpdateForest = MerkleList<{ + * type AccountUpdateForest = MerkleList; + * type AccountUpdateTree = { * accountUpdate: Hashed; * calls: AccountUpdateForest; - * }>; + * }; * ``` */ -class AccountUpdateForest extends MerkleList.create(CallTree, merkleListHash) { +class AccountUpdateForest extends MerkleList.create( + AccountUpdateTree, + merkleListHash +) { static fromArray(updates: AccountUpdate[]): AccountUpdateForest { let nodes = updates.map((update) => { let accountUpdate = HashedAccountUpdate.hash(update); @@ -1634,10 +1638,10 @@ class AccountUpdateForest extends MerkleList.create(CallTree, merkleListHash) { // how to hash a forest -function merkleListHash(forestHash: Field, tree: CallTree) { +function merkleListHash(forestHash: Field, tree: AccountUpdateTree) { return hashCons(forestHash, hashNode(tree)); } -function hashNode(tree: CallTree) { +function hashNode(tree: AccountUpdateTree) { return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ tree.accountUpdate.hash, tree.calls.hash, diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index c5878991db..5d83b9e077 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -1,6 +1,7 @@ import { AccountUpdate, AccountUpdateForest, + AccountUpdateTree, TokenId, } from '../../account_update.js'; import { Field } from '../../core.js'; @@ -11,11 +12,10 @@ import { MerkleList, } from '../../provable-types/merkle-list.js'; -export { AccountUpdateIterator, TokenAccountUpdateIterator }; +export { TokenAccountUpdateIterator }; -class AccountUpdateIterator extends MerkleListIterator.createFromList( - AccountUpdateForest -) {} +const AccountUpdateIterator = + MerkleListIterator.createFromList(AccountUpdateForest); class Layer extends Struct({ forest: AccountUpdateIterator.provable, @@ -56,7 +56,7 @@ class TokenAccountUpdateIterator { selfToken: Field; constructor( - forest: AccountUpdateIterator, + forest: MerkleListIterator, mayUseToken: MayUseToken, selfToken: Field ) { diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index c74f734cf3..3ad345f57c 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -339,10 +339,10 @@ class MerkleListIterator implements MerkleListIteratorBase { empty: () => MerkleListIterator; provable: ProvableHashable>; } { - return class MerkleArray_ extends MerkleListIterator { + return class Iterator extends MerkleListIterator { static _innerProvable = type; - static _provable = provableFromClass(MerkleArray_, { + static _provable = provableFromClass(Iterator, { hash: Field, data: Unconstrained.provable, currentHash: Field, From d1f87be3edccff7d5fdfaf44d73f159bf5c3904f Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 15:21:32 +0100 Subject: [PATCH 316/524] add popIf and doccomments --- src/lib/provable-types/merkle-list.ts | 56 +++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index 3ad345f57c..a06e19c563 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -50,10 +50,6 @@ function MerkleListBase(): ProvableHashable> { * * Supported operations are {@link push()} and {@link pop()} and some variants thereof. * - * **Important:** `push()` adds elements to the _start_ of the internal array and `pop()` removes them from the start. - * This is so that the hash which represents the list is consistent with {@link MerkleListIterator}, - * and so a `MerkleList` can be used as input to `MerkleListIterator.startIterating(list)` - * (which will then iterate starting from the last pushed element). * * A Merkle list is generic over its element types, so before using it you must create a subclass for your element type: * @@ -62,8 +58,16 @@ function MerkleListBase(): ProvableHashable> { * * // now use it * let list = MyList.empty(); + * * list.push(new MyType(...)); + * + * let element = list.pop(); * ``` + * + * Internal detail: `push()` adds elements to the _start_ of the internal array and `pop()` removes them from the start. + * This is so that the hash which represents the list is consistent with {@link MerkleListIterator}, + * and so a `MerkleList` can be used as input to `MerkleListIterator.startIterating(list)` + * (which will then iterate starting from the last pushed element). */ class MerkleList implements MerkleListBase { hash: Field; @@ -78,12 +82,18 @@ class MerkleList implements MerkleListBase { return this.hash.equals(emptyHash); } + /** + * Push a new element to the list. + */ push(element: T) { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); this.data.updateAsProver((data) => [{ previousHash, element }, ...data]); } + /** + * Push a new element to the list, if the `condition` is true. + */ pushIf(condition: Bool, element: T) { let previousHash = this.hash; this.hash = Provable.if( @@ -108,6 +118,11 @@ class MerkleList implements MerkleListBase { }); } + /** + * Remove the last element from the list and return it. + * + * This proves that the list is non-empty, and fails otherwise. + */ popExn(): T { let { previousHash, element } = this.popWitness(); @@ -118,6 +133,11 @@ class MerkleList implements MerkleListBase { return element; } + /** + * Remove the last element from the list and return it. + * + * If the list is empty, returns a dummy element. + */ pop(): T { let { previousHash, element } = this.popWitness(); let isEmpty = this.isEmpty(); @@ -131,6 +151,26 @@ class MerkleList implements MerkleListBase { return Provable.if(isEmpty, provable, provable.empty(), element); } + /** + * Return the last element, but only remove it if `condition` is true. + * + * If the list is empty, returns a dummy element. + */ + popIf(condition: Bool) { + let originalHash = this.hash; + let element = this.pop(); + + // if the condition is false, we restore the original state + this.data.updateAsProver((data) => + condition.toBoolean() + ? data + : [{ previousHash: this.hash, element }, ...data] + ); + this.hash = Provable.if(condition, this.hash, originalHash); + + return element; + } + clone(): MerkleList { let data = Unconstrained.witness(() => [...this.data.get()]); return new this.Constructor({ hash: this.hash, data }); @@ -144,7 +184,7 @@ class MerkleList implements MerkleListBase { /** * Create a Merkle list type * - * Optionally, you can tell `create()` how to do the hash that pushed a new list element, by passing a `nextHash` function. + * Optionally, you can tell `create()` how to do the hash that pushes a new list element, by passing a `nextHash` function. * * @example * ```ts @@ -236,10 +276,10 @@ type MerkleListIteratorBase = { }; /** - * MerkleListIterator is similar to a MerkleList, but it maintains the entire array througout a computation, - * instead of mutating itself / throwing away context while stepping through it. + * MerkleListIterator helps iterating through a Merkle list. + * This works similar to calling `list.pop()` repreatedly, but maintaining the entire list instead of removing elements. * - * The core method that support iteration is {@link next()}. + * The core method that supports iteration is {@link next()}. * * ```ts * let iterator = MerkleListIterator.startIterating(list); From c8d6bfcfa1300a20e96ace38c8638489d0516603 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 15:22:11 +0100 Subject: [PATCH 317/524] improve clarity of forest iteration logic --- src/lib/mina/token/forest-iterator.ts | 57 ++++++++++++--------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index 5d83b9e077..90ee4f411a 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -86,19 +86,17 @@ class TokenAccountUpdateIterator { */ next() { // get next account update from the current forest (might be a dummy) - // and step down into the layer of its children let { accountUpdate, calls } = this.currentLayer.forest.next(); - let forest = AccountUpdateIterator.startIterating(calls); - let parentForest = this.currentLayer.forest; + let childForest = AccountUpdateIterator.startIterating(calls); + let childLayer = { + forest: childForest, + mayUseToken: MayUseToken.InheritFromParent, + }; - this.unfinishedParentLayers.pushIf(parentForest.isAtEnd().not(), { - forest: parentForest, - mayUseToken: this.currentLayer.mayUseToken, - }); - - // check if this account update / it's children can use the token let update = accountUpdate.unhash(); + let usesThisToken = update.tokenId.equals(this.selfToken); + // check if this account update / it's children can use the token let canAccessThisToken = Provable.equal( MayUseToken.type, update.body.mayUseToken, @@ -108,33 +106,30 @@ class TokenAccountUpdateIterator { this.selfToken ); - let usesThisToken = update.tokenId.equals(this.selfToken); - // if we don't have to check the children, ignore the forest by jumping to its end let skipSubtree = canAccessThisToken.not().or(isSelf); - forest.jumpToEndIf(skipSubtree); - - // if we're at the end of the current layer, step up to the next unfinished parent layer - // invariant: the new current layer will _never_ be finished _except_ at the point where we stepped - // through the entire forest and there are no remaining parent layers to finish - let currentLayer = { forest, mayUseToken: MayUseToken.InheritFromParent }; - let currentIsFinished = forest.isAtEnd(); - - let parentLayers = this.unfinishedParentLayers.clone(); - let nextParentLayer = this.unfinishedParentLayers.pop(); - let parentLayersIfSteppingUp = this.unfinishedParentLayers; + childForest.jumpToEndIf(skipSubtree); + + // there are three cases for how to proceed: + // 1. if we have to process children, we step down and add the current layer to the stack of unfinished parent layers + // 2. we don't have to process children, but we're not finished with the current layer yet, so we stay in the current layer + // 3. both of the above are false, so we step up to the next unfinished parent layer + let currentForest = this.currentLayer.forest; + let currentLayerFinished = currentForest.isAtEnd(); + let childLayerFinished = childForest.isAtEnd(); + + this.unfinishedParentLayers.pushIf( + currentLayerFinished.not(), + this.currentLayer + ); + let currentOrParentLayer = + this.unfinishedParentLayers.popIf(childLayerFinished); this.currentLayer = Provable.if( - currentIsFinished, + childLayerFinished, Layer, - nextParentLayer, - currentLayer - ); - this.unfinishedParentLayers = Provable.if( - currentIsFinished, - ParentLayers.provable, - parentLayersIfSteppingUp, - parentLayers + currentOrParentLayer, + childLayer ); return { accountUpdate: update, usesThisToken }; From 085f7aba479ec6ddb54740c81e111a06d9692b84 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 15:29:12 +0100 Subject: [PATCH 318/524] update to new names, dump new vks --- src/examples/zkapps/dex/dex.ts | 4 ++-- src/lib/mina/token/token-contract.ts | 4 ++-- src/lib/zkapp.ts | 4 ++-- tests/vk-regression/vk-regression.json | 28 +++++++++++++------------- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 0981ca909d..39dca7c33c 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -17,7 +17,7 @@ import { method, state, TokenContract as BaseTokenContract, - CallForest, + AccountUpdateForest, } from 'o1js'; export { TokenContract, addresses, createDex, keys, randomAccounts, tokenIds }; @@ -404,7 +404,7 @@ class TokenContract extends BaseTokenContract { } @method - approveBase(forest: CallForest) { + approveBase(forest: AccountUpdateForest) { this.checkZeroBalanceChange(forest); } diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 456c84c495..344963e565 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -6,7 +6,7 @@ import { AccountUpdate, AccountUpdateForest, CallForestUnderConstruction, - CallTree, + AccountUpdateTree, HashedAccountUpdate, Permissions, smartContractContext, @@ -148,7 +148,7 @@ function finalizeAccountUpdates(updates: AccountUpdate[]): AccountUpdateForest { return AccountUpdateForest.from(trees); } -function finalizeAccountUpdate(update: AccountUpdate): CallTree { +function finalizeAccountUpdate(update: AccountUpdate): AccountUpdateTree { let calls: AccountUpdateForest; let insideContract = smartContractContext.get(); diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 267c625211..0134b8bfa8 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -17,7 +17,7 @@ import { ZkappStateLength, SmartContractContext, LazyProof, - CallForestHelpers, + CallForest, CallForestUnderConstruction, } from './account_update.js'; import { @@ -1536,7 +1536,7 @@ const ProofAuthorization = { priorAccountUpdates = priorAccountUpdates.filter( (a) => a.id !== myAccountUpdateId ); - let priorAccountUpdatesFlat = CallForestHelpers.toFlatList( + let priorAccountUpdatesFlat = CallForest.toFlatList( priorAccountUpdates, false ); diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index a22d774c87..2c2d6511b7 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -63,7 +63,7 @@ } }, "TokenContract": { - "digest": "20b64a7b0520ae119f8dc91a8ef10bb91b3f30ab8d6ade52b5b628c429b43d0b", + "digest": "3617eb15679a710b2321c511f9870464f846de5e93c31c0e6d40a93d1d3b15e1", "methods": { "init": { "rows": 655, @@ -75,7 +75,7 @@ }, "approveBase": { "rows": 13194, - "digest": "46100d5f715c68fc81f93c70e32c21f2" + "digest": "8f8ad42a6586936a28b570877403960c" }, "deployZkapp": { "rows": 702, @@ -87,8 +87,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "27384842021225823013412718366201408258687963922479378403873398307032584328437" + "data": "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", + "hash": "27127639205719537939166450295035225998965373687685248985354531474736258095446" } }, "Dex": { @@ -220,29 +220,29 @@ } }, "ecdsa-only": { - "digest": "f4439d84374753ce55c597e7d9593bc50aa45b942c95068ee9e4c96fef2a293", + "digest": "2a2beadf929015559514abb88dd77694dbe62a6a5904e5a9d05ab21a3f293c0c", "methods": { "verifySignedHash": { - "rows": 28220, - "digest": "5e793e1d827ea3900ab558c586c8b4cf" + "rows": 28186, + "digest": "dd43cd30a8277b5af02fd85a4762a488" } }, "verificationKey": { - "data": "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", - "hash": "8521505138081104393457014021668593383409974534445035107673472947642693857881" + "data": "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", + "hash": "16626558875595050675741741208497473516532634598762284173771479275503819571624" } }, "ecdsa": { - "digest": "143d65f22de8f3368259408ea25ce6dd91a5355df5dd87898779db0cb08c1c79", + "digest": "2f78a9c0307e6f9c6872499c3ca7c412f5771a7ac7e7e88c351ed1b62d6ac408", "methods": { "verifyEcdsa": { - "rows": 42718, - "digest": "f26be91413483ab6f928f052542d1752" + "rows": 42684, + "digest": "2bb042b0fa7bbbb32f7e77d392f43d2c" } }, "verificationKey": { - "data": "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", - "hash": "25193230245026500505730278477940803504648445281519558230631756595987510650479" + "data": "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", + "hash": "13907522711254991952405567976395529829664716172124500348498751038796408381729" } }, "sha256": { From 0f383542185594800fd0b01030234bb304dc5add Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 15:44:42 +0100 Subject: [PATCH 319/524] normalize comments --- src/lib/mina/token/forest-iterator.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index 90ee4f411a..549a18d68c 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -112,8 +112,9 @@ class TokenAccountUpdateIterator { // there are three cases for how to proceed: // 1. if we have to process children, we step down and add the current layer to the stack of unfinished parent layers - // 2. we don't have to process children, but we're not finished with the current layer yet, so we stay in the current layer - // 3. both of the above are false, so we step up to the next unfinished parent layer + // 2. if we don't have to process children, but are not finished with the current layer, we stay in the current layer + // (below, this is the case where the current layer is first pushed to and then popped from the stack of unfinished parent layers) + // 3. if both of the above are false, we step up to the next unfinished parent layer let currentForest = this.currentLayer.forest; let currentLayerFinished = currentForest.isAtEnd(); let childLayerFinished = childForest.isAtEnd(); From 1435bb5b18a5200f41ef4a3c9802f9b0bf983fd0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 15:59:07 +0100 Subject: [PATCH 320/524] remove unused code --- src/examples/zkapps/dex/dex.ts | 45 ---------------------------------- 1 file changed, 45 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 39dca7c33c..4193903e73 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -418,51 +418,6 @@ class TokenContract extends BaseTokenContract { zkapp.requireSignature(); } - // @method _approveUpdate(zkappUpdate: AccountUpdate) { - // this.approve(zkappUpdate); - // let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); - // balanceChange.assertEquals(Int64.from(0)); - // } - - // // FIXME: remove this - // @method _approveAny(zkappUpdate: AccountUpdate) { - // this.approve(zkappUpdate, AccountUpdate.Layout.AnyChildren); - // } - - // // let a zkapp send tokens to someone, provided the token supply stays constant - // @method _approveUpdateAndSend( - // zkappUpdate: AccountUpdate, - // to: PublicKey, - // amount: UInt64 - // ) { - // // approve a layout of two grandchildren, both of which can't inherit the token permission - // let { StaticChildren, AnyChildren } = AccountUpdate.Layout; - // this.approve(zkappUpdate, StaticChildren(AnyChildren, AnyChildren)); - // zkappUpdate.body.mayUseToken.parentsOwnToken.assertTrue(); - // let [grandchild1, grandchild2] = zkappUpdate.children.accountUpdates; - // grandchild1.body.mayUseToken.inheritFromParent.assertFalse(); - // grandchild2.body.mayUseToken.inheritFromParent.assertFalse(); - - // // see if balance change cancels the amount sent - // let balanceChange = Int64.fromObject(zkappUpdate.body.balanceChange); - // balanceChange.assertEquals(Int64.from(amount).neg()); - // // add same amount of tokens to the receiving address - // this.token.mint({ address: to, amount }); - // } - - // _transfer(from: PublicKey, to: PublicKey | AccountUpdate, amount: UInt64) { - // if (to instanceof PublicKey) - // return this._transferToAddress(from, to, amount); - // if (to instanceof AccountUpdate) - // return this._transferToUpdate(from, to, amount); - // } - // @method _transferToAddress(from: PublicKey, to: PublicKey, value: UInt64) { - // this.token.send({ from, to, amount: value }); - // } - // @method _transferToUpdate(from: PublicKey, to: AccountUpdate, value: UInt64) { - // this.token.send({ from, to, amount: value }); - // } - @method getBalance(publicKey: PublicKey): UInt64 { let accountUpdate = AccountUpdate.create(publicKey, this.token.id); let balance = accountUpdate.account.balance.get(); From 943d871ed77d2af57eb6a08d4d9aee2edf0b24cf Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 16:15:32 +0100 Subject: [PATCH 321/524] remove deployZkapp --- src/examples/zkapps/dex/dex.ts | 10 ---------- src/examples/zkapps/dex/upgradability.ts | 22 ++++++++++++++++++---- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 4193903e73..6f0920d7bd 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -408,16 +408,6 @@ class TokenContract extends BaseTokenContract { this.checkZeroBalanceChange(forest); } - // this is a very standardized deploy method. instead, we could also take the account update from a callback - // => need callbacks for signatures - @method deployZkapp(address: PublicKey, verificationKey: VerificationKey) { - let tokenId = this.token.id; - let zkapp = AccountUpdate.create(address, tokenId); - zkapp.account.permissions.set(Permissions.default()); - zkapp.account.verificationKey.set(verificationKey); - zkapp.requireSignature(); - } - @method getBalance(publicKey: PublicKey): UInt64 { let accountUpdate = AccountUpdate.create(publicKey, this.token.id); let balance = accountUpdate.account.balance.get(); diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index bf77c6c758..375e661dba 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -255,6 +255,8 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { let tokenX = new TokenContract(addresses.tokenX); let tokenY = new TokenContract(addresses.tokenY); let dex = new Dex(addresses.dex); + let dexTokenHolderX = new DexTokenHolder(addresses.dex, tokenIds.X); + let dexTokenHolderY = new DexTokenHolder(addresses.dex, tokenIds.Y); console.log('deploy & init token contracts...'); tx = await Mina.transaction(feePayerAddress, () => { @@ -300,8 +302,10 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { // pay fees for creating 3 dex accounts AccountUpdate.fundNewAccount(feePayerAddress, 3); dex.deploy(); - tokenX.deployZkapp(addresses.dex, DexTokenHolder._verificationKey!); - tokenY.deployZkapp(addresses.dex, DexTokenHolder._verificationKey!); + dexTokenHolderX.deploy(); + tokenX.approveAccountUpdate(dexTokenHolderX.self); + dexTokenHolderY.deploy(); + tokenY.approveAccountUpdate(dexTokenHolderY.self); }); await tx.prove(); tx.sign([feePayerKey, keys.dex]); @@ -345,11 +349,21 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { console.log('compiling modified Dex contract...'); await ModifiedDex.compile(); let modifiedDex = new ModifiedDex(addresses.dex); + let modifiedDexTokenHolderX = new ModifiedDexTokenHolder( + addresses.dex, + tokenIds.X + ); + let modifiedDexTokenHolderY = new ModifiedDexTokenHolder( + addresses.dex, + tokenIds.Y + ); tx = await Mina.transaction(feePayerAddress, () => { modifiedDex.deploy(); - tokenX.deployZkapp(addresses.dex, ModifiedDexTokenHolder._verificationKey!); - tokenY.deployZkapp(addresses.dex, ModifiedDexTokenHolder._verificationKey!); + modifiedDexTokenHolderX.deploy(); + tokenX.approveAccountUpdate(modifiedDexTokenHolderX.self); + modifiedDexTokenHolderY.deploy(); + tokenY.approveAccountUpdate(modifiedDexTokenHolderY.self); }); await tx.prove(); tx.sign([feePayerKey, keys.dex]); From 25718e060ea1de77801137399f2a193aa820ba24 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 16:38:26 +0100 Subject: [PATCH 322/524] revisit erc20 example and tweak base token contract to be more similar to erc20 --- src/examples/zkapps/dex/dex-with-actions.ts | 10 +- src/examples/zkapps/dex/dex.ts | 23 ++- src/examples/zkapps/dex/erc20.ts | 132 +++++------------- .../zkapps/dex/happy-path-with-actions.ts | 4 +- .../zkapps/dex/happy-path-with-proofs.ts | 4 +- src/examples/zkapps/dex/run.ts | 14 +- src/examples/zkapps/dex/run_live.ts | 4 +- src/examples/zkapps/dex/upgradability.ts | 12 +- src/lib/mina/token/token-contract.ts | 9 +- 9 files changed, 82 insertions(+), 130 deletions(-) diff --git a/src/examples/zkapps/dex/dex-with-actions.ts b/src/examples/zkapps/dex/dex-with-actions.ts index 0a93ce572a..ae2b257638 100644 --- a/src/examples/zkapps/dex/dex-with-actions.ts +++ b/src/examples/zkapps/dex/dex-with-actions.ts @@ -101,8 +101,8 @@ class Dex extends SmartContract { let isDyCorrect = dy.equals(dx.mul(y).div(xSafe)); isDyCorrect.or(isXZero).assertTrue(); - tokenX.transfer(user, dexX, dx); - tokenY.transfer(user, dexY, dy); + tokenX.transfer(dexX, dx); + tokenY.transfer(dexY, dy); // calculate liquidity token output simply as dl = dx + dx // => maintains ratio x/l, y/l @@ -188,7 +188,7 @@ class Dex extends SmartContract { let tokenY = new TokenContract(this.tokenY); let dexY = new DexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.transfer(dexY.self, this.sender, dy); + tokenY.transferFrom(dexY.self, this.sender, dy); return dy; } @@ -206,7 +206,7 @@ class Dex extends SmartContract { let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dx = dexX.swap(this.sender, dy, this.tokenY); - tokenX.transfer(dexX.self, this.sender, dx); + tokenX.transferFrom(dexX.self, this.sender, dx); return dx; } @@ -299,7 +299,7 @@ class DexTokenHolder extends SmartContract { let y = this.account.balance.getAndRequireEquals(); // send x from user to us (i.e., to the same address as this but with the other token) - tokenX.transfer(user, dexX, dx); + tokenX.transferFrom(user, dexX, dx); // compute and send dy let dy = y.mul(dx).div(x.add(dx)); diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 6f0920d7bd..b2d3b5edf0 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -3,7 +3,6 @@ import { AccountUpdate, Bool, Mina, - Permissions, PrivateKey, Provable, PublicKey, @@ -13,7 +12,6 @@ import { TokenId, UInt32, UInt64, - VerificationKey, method, state, TokenContract as BaseTokenContract, @@ -50,7 +48,6 @@ function createDex({ * instead, the input X and Y amounts determine the initial ratio. */ @method supplyLiquidityBase(dx: UInt64, dy: UInt64): UInt64 { - let user = this.sender; let tokenX = new TokenContract(this.tokenX); let tokenY = new TokenContract(this.tokenY); @@ -67,13 +64,13 @@ function createDex({ let isDyCorrect = dy.equals(dx.mul(dexYBalance).div(xSafe)); isDyCorrect.or(isXZero).assertTrue(); - tokenX.transfer(user, dexXUpdate, dx); - tokenY.transfer(user, dexYUpdate, dy); + tokenX.transfer(dexXUpdate, dx); + tokenY.transfer(dexYUpdate, dy); // calculate liquidity token output simply as dl = dx + dy // => maintains ratio x/l, y/l let dl = dy.add(dx); - let userUpdate = this.token.mint({ address: user, amount: dl }); + let userUpdate = this.token.mint({ address: this.sender, amount: dl }); if (lockedLiquiditySlots !== undefined) { /** * exercise the "timing" (vesting) feature to lock the received liquidity tokens. @@ -141,7 +138,7 @@ function createDex({ let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dxdy = dexX.redeemLiquidity(this.sender, dl, this.tokenY); let dx = dxdy[0]; - tokenX.transfer(dexX.self, this.sender, dx); + tokenX.transferFrom(dexX.self, this.sender, dx); return dxdy; } @@ -156,7 +153,7 @@ function createDex({ let tokenY = new TokenContract(this.tokenY); let dexY = new DexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.transfer(dexY.self, this.sender, dy); + tokenY.transferFrom(dexY.self, this.sender, dy); return dy; } @@ -171,7 +168,7 @@ function createDex({ let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dx = dexX.swap(this.sender, dy, this.tokenY); - tokenX.transfer(dexX.self, this.sender, dx); + tokenX.transferFrom(dexX.self, this.sender, dx); return dx; } @@ -205,7 +202,7 @@ function createDex({ let tokenY = new TokenContract(this.tokenY); let dexY = new ModifiedDexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.transfer(dexY.self, this.sender, dy); + tokenY.transferFrom(dexY.self, this.sender, dy); return dy; } } @@ -247,7 +244,7 @@ function createDex({ let result = dexY.redeemLiquidityPartial(user, dl); let l = result[0]; let dy = result[1]; - tokenY.transfer(dexY.self, user, dy); + tokenY.transferFrom(dexY.self, user, dy); // in return for dl, we give back dx, the X token part let x = this.account.balance.get(); @@ -273,7 +270,7 @@ function createDex({ let y = this.account.balance.get(); this.account.balance.requireEquals(y); // send x from user to us (i.e., to the same address as this but with the other token) - tokenX.transfer(user, this.address, dx); + tokenX.transferFrom(user, this.address, dx); // compute and send dy let dy = y.mul(dx).div(x.add(dx)); // just subtract dy balance and let adding balance be handled one level higher @@ -296,7 +293,7 @@ function createDex({ let x = tokenX.getBalance(this.address); let y = this.account.balance.get(); this.account.balance.requireEquals(y); - tokenX.transfer(user, this.address, dx); + tokenX.transferFrom(user, this.address, dx); // this formula has been changed - we just give the user an additional 15 token let dy = y.mul(dx).div(x.add(dx)).add(15); diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index 9648640f8a..52010806b2 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -8,19 +8,24 @@ import { method, AccountUpdate, PublicKey, - SmartContract, UInt64, - Account, - Experimental, Permissions, Mina, - Int64, - VerificationKey, + TokenContract, + AccountUpdateForest, } from 'o1js'; /** - * ERC-20 token standard. + * ERC-20-like token standard. * https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ + * + * Differences to ERC-20: + * - No approvals / allowance, because zkApps don't need them and they are a security footgun. + * - `transferFrom()`, `transfer()` and `balanceOf()` can also take an account update as an argument. + * This form might be needed for zkApp token accounts, where the account update has to come from a method + * (in order to get proof authorization), and can't be created by the token contract itself. + * - `transferFrom()` and `transfer()` don't return a boolean, because in the zkApp protocol, + * a transaction succeeds or fails in its entirety, and there is no need to handle partial failures. */ type Erc20 = { // pure view functions which don't need @method @@ -28,13 +33,15 @@ type Erc20 = { symbol?: () => CircuitString; decimals?: () => Field; // TODO: should be UInt8 which doesn't exist yet totalSupply(): UInt64; - balanceOf(owner: PublicKey): UInt64; - allowance(owner: PublicKey, spender: PublicKey): UInt64; + balanceOf(owner: PublicKey | AccountUpdate): UInt64; // mutations which need @method - transfer(to: PublicKey, value: UInt64): Bool; // emits "Transfer" event - transferFrom(from: PublicKey, to: PublicKey, value: UInt64): Bool; // emits "Transfer" event - approveSpend(spender: PublicKey, value: UInt64): Bool; // emits "Approve" event + transfer(to: PublicKey | AccountUpdate, value: UInt64): void; // emits "Transfer" event + transferFrom( + from: PublicKey | AccountUpdate, + to: PublicKey | AccountUpdate, + value: UInt64 + ): void; // emits "Transfer" event // events events: { @@ -43,11 +50,6 @@ type Erc20 = { to: PublicKey; value: UInt64; }>; - Approval: ProvablePure<{ - owner: PublicKey; - spender: PublicKey; - value: UInt64; - }>; }; }; @@ -61,18 +63,15 @@ type Erc20 = { * Functionality: * Just enough to be swapped by the DEX contract, and be secure */ -class TrivialCoin extends SmartContract implements Erc20 { +class TrivialCoin extends TokenContract implements Erc20 { // constant supply SUPPLY = UInt64.from(10n ** 18n); deploy(args: DeployArgs) { super.deploy(args); - this.account.tokenSymbol.set('SOM'); - this.account.permissions.set({ - ...Permissions.default(), - setPermissions: Permissions.proof(), - }); + this.account.tokenSymbol.set('TRIV'); } + @method init() { super.init(); @@ -101,10 +100,10 @@ class TrivialCoin extends SmartContract implements Erc20 { // ERC20 API name(): CircuitString { - return CircuitString.fromString('SomeCoin'); + return CircuitString.fromString('TrivialCoin'); } symbol(): CircuitString { - return CircuitString.fromString('SOM'); + return CircuitString.fromString('TRIV'); } decimals(): Field { return Field(9); @@ -112,32 +111,12 @@ class TrivialCoin extends SmartContract implements Erc20 { totalSupply(): UInt64 { return this.SUPPLY; } - balanceOf(owner: PublicKey): UInt64 { - let account = Account(owner, this.token.id); - let balance = account.balance.get(); - account.balance.requireEquals(balance); - return balance; - } - allowance(owner: PublicKey, spender: PublicKey): UInt64 { - // TODO: implement allowances - return UInt64.zero; - } - - @method transfer(to: PublicKey, value: UInt64): Bool { - this.token.send({ from: this.sender, to, amount: value }); - this.emitEvent('Transfer', { from: this.sender, to, value }); - // we don't have to check the balance of the sender -- this is done by the zkApp protocol - return Bool(true); - } - @method transferFrom(from: PublicKey, to: PublicKey, value: UInt64): Bool { - this.token.send({ from, to, amount: value }); - this.emitEvent('Transfer', { from, to, value }); - // we don't have to check the balance of the sender -- this is done by the zkApp protocol - return Bool(true); - } - @method approveSpend(spender: PublicKey, value: UInt64): Bool { - // TODO: implement allowances - return Bool(false); + balanceOf(owner: PublicKey | AccountUpdate): UInt64 { + let update = + owner instanceof PublicKey + ? AccountUpdate.create(owner, this.token.id) + : owner; + return update.account.balance.getAndRequireEquals(); } events = { @@ -146,58 +125,11 @@ class TrivialCoin extends SmartContract implements Erc20 { to: PublicKey, value: UInt64, }), - Approval: provablePure({ - owner: PublicKey, - spender: PublicKey, - value: UInt64, - }), }; - // additional API needed for zkApp token accounts - - @method transferFromZkapp( - from: PublicKey, - to: PublicKey, - value: UInt64, - approve: Experimental.Callback - ): Bool { - // TODO: need to be able to witness a certain layout of account updates, in this case - // tokenContract --> sender --> receiver - let fromUpdate = this.approve(approve, AccountUpdate.Layout.NoChildren); - - let negativeAmount = Int64.fromObject(fromUpdate.body.balanceChange); - negativeAmount.assertEquals(Int64.from(value).neg()); - let tokenId = this.token.id; - fromUpdate.body.tokenId.assertEquals(tokenId); - fromUpdate.body.publicKey.assertEquals(from); - - let toUpdate = AccountUpdate.create(to, tokenId); - toUpdate.balance.addInPlace(value); - this.emitEvent('Transfer', { from, to, value }); - return Bool(true); - } - - // this is a very standardized deploy method. instead, we could also take the account update from a callback - @method deployZkapp( - zkappAddress: PublicKey, - verificationKey: VerificationKey - ) { - let tokenId = this.token.id; - let zkapp = Experimental.createChildAccountUpdate( - this.self, - zkappAddress, - tokenId - ); - zkapp.account.permissions.set(Permissions.default()); - zkapp.account.verificationKey.set(verificationKey); - zkapp.requireSignature(); - } + // implement Approvable API - // for letting a zkapp do whatever it wants, as long as no tokens are transferred - // TODO: atm, we have to restrict the zkapp to have no children - // -> need to be able to witness a general layout of account updates - @method approveZkapp(callback: Experimental.Callback) { - let zkappUpdate = this.approve(callback, AccountUpdate.Layout.NoChildren); - Int64.fromObject(zkappUpdate.body.balanceChange).assertEquals(UInt64.zero); + @method approveBase(forest: AccountUpdateForest) { + this.checkZeroBalanceChange(forest); } } diff --git a/src/examples/zkapps/dex/happy-path-with-actions.ts b/src/examples/zkapps/dex/happy-path-with-actions.ts index 5aaebb49a3..55c020a969 100644 --- a/src/examples/zkapps/dex/happy-path-with-actions.ts +++ b/src/examples/zkapps/dex/happy-path-with-actions.ts @@ -80,8 +80,8 @@ tx = await Mina.transaction(feePayerAddress, () => { // pay fees for creating 3 user accounts let feePayer = AccountUpdate.fundNewAccount(feePayerAddress, 3); feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees - tokenX.transfer(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); - tokenY.transfer(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); + tokenX.transferFrom(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); + tokenY.transferFrom(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); }); await tx.prove(); await tx.sign([feePayerKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index e007e6b000..32898bd3ba 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -84,8 +84,8 @@ tx = await Mina.transaction(feePayerAddress, () => { // pay fees for creating 3 user accounts let feePayer = AccountUpdate.fundNewAccount(feePayerAddress, 3); feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees - tokenX.transfer(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); - tokenY.transfer(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); + tokenX.transferFrom(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); + tokenY.transferFrom(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); }); await tx.prove(); await tx.sign([feePayerKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index 32576068ad..d37525d17f 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -119,8 +119,16 @@ async function main({ withVesting }: { withVesting: boolean }) { feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees feePayer.send({ to: addresses.user2, amount: 20e9 }); // transfer to fee payer so they can provide initial liquidity - tokenX.transfer(addresses.tokenX, feePayerAddress, UInt64.from(10_000)); - tokenY.transfer(addresses.tokenY, feePayerAddress, UInt64.from(10_000)); + tokenX.transferFrom( + addresses.tokenX, + feePayerAddress, + UInt64.from(10_000) + ); + tokenY.transferFrom( + addresses.tokenY, + feePayerAddress, + UInt64.from(10_000) + ); // mint tokens to the user (this is additional to the tokens minted at the beginning, so we can overflow the balance tokenX.init2(); tokenY.init2(); @@ -270,7 +278,7 @@ async function main({ withVesting }: { withVesting: boolean }) { console.log('prepare supplying overflowing liquidity'); tx = await Mina.transaction(feePayerAddress, () => { AccountUpdate.fundNewAccount(feePayerAddress); - tokenY.transfer( + tokenY.transferFrom( addresses.tokenY, addresses.tokenX, UInt64.MAXINT().sub(200_000) diff --git a/src/examples/zkapps/dex/run_live.ts b/src/examples/zkapps/dex/run_live.ts index 97632223ef..2df769e7e6 100644 --- a/src/examples/zkapps/dex/run_live.ts +++ b/src/examples/zkapps/dex/run_live.ts @@ -125,8 +125,8 @@ if (successfulTransactions <= 2) { // pay fees for creating 3 user accounts let feePayer = AccountUpdate.fundNewAccount(sender, 3); feePayer.send({ to: addresses.user, amount: 8e9 }); // give users MINA to pay fees - tokenX.transfer(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); - tokenY.transfer(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); + tokenX.transferFrom(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); + tokenY.transferFrom(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); }); await tx.prove(); pendingTx = await tx.sign([senderKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 375e661dba..94f55b91cc 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -325,8 +325,16 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees feePayer.send({ to: addresses.user2, amount: 20e9 }); // transfer to fee payer so they can provide initial liquidity - tokenX.transfer(addresses.tokenX, feePayerAddress, UInt64.from(10_000)); - tokenY.transfer(addresses.tokenY, feePayerAddress, UInt64.from(10_000)); + tokenX.transferFrom( + addresses.tokenX, + feePayerAddress, + UInt64.from(10_000) + ); + tokenY.transferFrom( + addresses.tokenY, + feePayerAddress, + UInt64.from(10_000) + ); // mint tokens to the user (this is additional to the tokens minted at the beginning, so we can overflow the balance tokenX.init2(); tokenY.init2(); diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 344963e565..d103fa946c 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -118,7 +118,7 @@ abstract class TokenContract extends SmartContract { /** * Transfer `amount` of tokens from `from` to `to`. */ - transfer( + transferFrom( from: PublicKey | AccountUpdate, to: PublicKey | AccountUpdate, amount: UInt64 @@ -141,6 +141,13 @@ abstract class TokenContract extends SmartContract { let forest = finalizeAccountUpdates([from, to]); this.approveBase(forest); } + + /** + * Transfer `amount` of tokens from this.sender to `to` + */ + transfer(to: PublicKey | AccountUpdate, amount: UInt64) { + this.transferFrom(this.sender, to, amount); + } } function finalizeAccountUpdates(updates: AccountUpdate[]): AccountUpdateForest { From c23b64a5629f9acc6bf44c1499d8124825ed94e3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 16:43:04 +0100 Subject: [PATCH 323/524] revert renaming towards erc20 --- src/examples/zkapps/dex/dex-with-actions.ts | 10 ++++----- src/examples/zkapps/dex/dex.ts | 21 ++++++++++--------- src/examples/zkapps/dex/erc20.ts | 13 ++++++------ .../zkapps/dex/happy-path-with-actions.ts | 4 ++-- .../zkapps/dex/happy-path-with-proofs.ts | 4 ++-- src/examples/zkapps/dex/run.ts | 14 +++---------- src/examples/zkapps/dex/run_live.ts | 4 ++-- src/examples/zkapps/dex/upgradability.ts | 12 ++--------- src/lib/mina/token/token-contract.ts | 9 +------- 9 files changed, 35 insertions(+), 56 deletions(-) diff --git a/src/examples/zkapps/dex/dex-with-actions.ts b/src/examples/zkapps/dex/dex-with-actions.ts index ae2b257638..0a93ce572a 100644 --- a/src/examples/zkapps/dex/dex-with-actions.ts +++ b/src/examples/zkapps/dex/dex-with-actions.ts @@ -101,8 +101,8 @@ class Dex extends SmartContract { let isDyCorrect = dy.equals(dx.mul(y).div(xSafe)); isDyCorrect.or(isXZero).assertTrue(); - tokenX.transfer(dexX, dx); - tokenY.transfer(dexY, dy); + tokenX.transfer(user, dexX, dx); + tokenY.transfer(user, dexY, dy); // calculate liquidity token output simply as dl = dx + dx // => maintains ratio x/l, y/l @@ -188,7 +188,7 @@ class Dex extends SmartContract { let tokenY = new TokenContract(this.tokenY); let dexY = new DexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.transferFrom(dexY.self, this.sender, dy); + tokenY.transfer(dexY.self, this.sender, dy); return dy; } @@ -206,7 +206,7 @@ class Dex extends SmartContract { let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dx = dexX.swap(this.sender, dy, this.tokenY); - tokenX.transferFrom(dexX.self, this.sender, dx); + tokenX.transfer(dexX.self, this.sender, dx); return dx; } @@ -299,7 +299,7 @@ class DexTokenHolder extends SmartContract { let y = this.account.balance.getAndRequireEquals(); // send x from user to us (i.e., to the same address as this but with the other token) - tokenX.transferFrom(user, dexX, dx); + tokenX.transfer(user, dexX, dx); // compute and send dy let dy = y.mul(dx).div(x.add(dx)); diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index b2d3b5edf0..89c4182af3 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -48,6 +48,7 @@ function createDex({ * instead, the input X and Y amounts determine the initial ratio. */ @method supplyLiquidityBase(dx: UInt64, dy: UInt64): UInt64 { + let user = this.sender; let tokenX = new TokenContract(this.tokenX); let tokenY = new TokenContract(this.tokenY); @@ -64,13 +65,13 @@ function createDex({ let isDyCorrect = dy.equals(dx.mul(dexYBalance).div(xSafe)); isDyCorrect.or(isXZero).assertTrue(); - tokenX.transfer(dexXUpdate, dx); - tokenY.transfer(dexYUpdate, dy); + tokenX.transfer(user, dexXUpdate, dx); + tokenY.transfer(user, dexYUpdate, dy); // calculate liquidity token output simply as dl = dx + dy // => maintains ratio x/l, y/l let dl = dy.add(dx); - let userUpdate = this.token.mint({ address: this.sender, amount: dl }); + let userUpdate = this.token.mint({ address: user, amount: dl }); if (lockedLiquiditySlots !== undefined) { /** * exercise the "timing" (vesting) feature to lock the received liquidity tokens. @@ -138,7 +139,7 @@ function createDex({ let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dxdy = dexX.redeemLiquidity(this.sender, dl, this.tokenY); let dx = dxdy[0]; - tokenX.transferFrom(dexX.self, this.sender, dx); + tokenX.transfer(dexX.self, this.sender, dx); return dxdy; } @@ -153,7 +154,7 @@ function createDex({ let tokenY = new TokenContract(this.tokenY); let dexY = new DexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.transferFrom(dexY.self, this.sender, dy); + tokenY.transfer(dexY.self, this.sender, dy); return dy; } @@ -168,7 +169,7 @@ function createDex({ let tokenX = new TokenContract(this.tokenX); let dexX = new DexTokenHolder(this.address, tokenX.token.id); let dx = dexX.swap(this.sender, dy, this.tokenY); - tokenX.transferFrom(dexX.self, this.sender, dx); + tokenX.transfer(dexX.self, this.sender, dx); return dx; } @@ -202,7 +203,7 @@ function createDex({ let tokenY = new TokenContract(this.tokenY); let dexY = new ModifiedDexTokenHolder(this.address, tokenY.token.id); let dy = dexY.swap(this.sender, dx, this.tokenX); - tokenY.transferFrom(dexY.self, this.sender, dy); + tokenY.transfer(dexY.self, this.sender, dy); return dy; } } @@ -244,7 +245,7 @@ function createDex({ let result = dexY.redeemLiquidityPartial(user, dl); let l = result[0]; let dy = result[1]; - tokenY.transferFrom(dexY.self, user, dy); + tokenY.transfer(dexY.self, user, dy); // in return for dl, we give back dx, the X token part let x = this.account.balance.get(); @@ -270,7 +271,7 @@ function createDex({ let y = this.account.balance.get(); this.account.balance.requireEquals(y); // send x from user to us (i.e., to the same address as this but with the other token) - tokenX.transferFrom(user, this.address, dx); + tokenX.transfer(user, this.address, dx); // compute and send dy let dy = y.mul(dx).div(x.add(dx)); // just subtract dy balance and let adding balance be handled one level higher @@ -293,7 +294,7 @@ function createDex({ let x = tokenX.getBalance(this.address); let y = this.account.balance.get(); this.account.balance.requireEquals(y); - tokenX.transferFrom(user, this.address, dx); + tokenX.transfer(user, this.address, dx); // this formula has been changed - we just give the user an additional 15 token let dy = y.mul(dx).div(x.add(dx)).add(15); diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index 52010806b2..c492036964 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -21,13 +21,15 @@ import { * * Differences to ERC-20: * - No approvals / allowance, because zkApps don't need them and they are a security footgun. - * - `transferFrom()`, `transfer()` and `balanceOf()` can also take an account update as an argument. + * - `transfer()` and `transfer()` are collapsed into a single `transfer()` method which takes + * both the sender and the receiver as arguments. + * - `transfer()` and `balanceOf()` can also take an account update as an argument. * This form might be needed for zkApp token accounts, where the account update has to come from a method * (in order to get proof authorization), and can't be created by the token contract itself. - * - `transferFrom()` and `transfer()` don't return a boolean, because in the zkApp protocol, + * - `transfer()` doesn't return a boolean, because in the zkApp protocol, * a transaction succeeds or fails in its entirety, and there is no need to handle partial failures. */ -type Erc20 = { +type Erc20Like = { // pure view functions which don't need @method name?: () => CircuitString; symbol?: () => CircuitString; @@ -36,8 +38,7 @@ type Erc20 = { balanceOf(owner: PublicKey | AccountUpdate): UInt64; // mutations which need @method - transfer(to: PublicKey | AccountUpdate, value: UInt64): void; // emits "Transfer" event - transferFrom( + transfer( from: PublicKey | AccountUpdate, to: PublicKey | AccountUpdate, value: UInt64 @@ -63,7 +64,7 @@ type Erc20 = { * Functionality: * Just enough to be swapped by the DEX contract, and be secure */ -class TrivialCoin extends TokenContract implements Erc20 { +class TrivialCoin extends TokenContract implements Erc20Like { // constant supply SUPPLY = UInt64.from(10n ** 18n); diff --git a/src/examples/zkapps/dex/happy-path-with-actions.ts b/src/examples/zkapps/dex/happy-path-with-actions.ts index 55c020a969..5aaebb49a3 100644 --- a/src/examples/zkapps/dex/happy-path-with-actions.ts +++ b/src/examples/zkapps/dex/happy-path-with-actions.ts @@ -80,8 +80,8 @@ tx = await Mina.transaction(feePayerAddress, () => { // pay fees for creating 3 user accounts let feePayer = AccountUpdate.fundNewAccount(feePayerAddress, 3); feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees - tokenX.transferFrom(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); - tokenY.transferFrom(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); + tokenX.transfer(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); + tokenY.transfer(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); }); await tx.prove(); await tx.sign([feePayerKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index 32898bd3ba..e007e6b000 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -84,8 +84,8 @@ tx = await Mina.transaction(feePayerAddress, () => { // pay fees for creating 3 user accounts let feePayer = AccountUpdate.fundNewAccount(feePayerAddress, 3); feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees - tokenX.transferFrom(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); - tokenY.transferFrom(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); + tokenX.transfer(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); + tokenY.transfer(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); }); await tx.prove(); await tx.sign([feePayerKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index d37525d17f..32576068ad 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -119,16 +119,8 @@ async function main({ withVesting }: { withVesting: boolean }) { feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees feePayer.send({ to: addresses.user2, amount: 20e9 }); // transfer to fee payer so they can provide initial liquidity - tokenX.transferFrom( - addresses.tokenX, - feePayerAddress, - UInt64.from(10_000) - ); - tokenY.transferFrom( - addresses.tokenY, - feePayerAddress, - UInt64.from(10_000) - ); + tokenX.transfer(addresses.tokenX, feePayerAddress, UInt64.from(10_000)); + tokenY.transfer(addresses.tokenY, feePayerAddress, UInt64.from(10_000)); // mint tokens to the user (this is additional to the tokens minted at the beginning, so we can overflow the balance tokenX.init2(); tokenY.init2(); @@ -278,7 +270,7 @@ async function main({ withVesting }: { withVesting: boolean }) { console.log('prepare supplying overflowing liquidity'); tx = await Mina.transaction(feePayerAddress, () => { AccountUpdate.fundNewAccount(feePayerAddress); - tokenY.transferFrom( + tokenY.transfer( addresses.tokenY, addresses.tokenX, UInt64.MAXINT().sub(200_000) diff --git a/src/examples/zkapps/dex/run_live.ts b/src/examples/zkapps/dex/run_live.ts index 2df769e7e6..97632223ef 100644 --- a/src/examples/zkapps/dex/run_live.ts +++ b/src/examples/zkapps/dex/run_live.ts @@ -125,8 +125,8 @@ if (successfulTransactions <= 2) { // pay fees for creating 3 user accounts let feePayer = AccountUpdate.fundNewAccount(sender, 3); feePayer.send({ to: addresses.user, amount: 8e9 }); // give users MINA to pay fees - tokenX.transferFrom(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); - tokenY.transferFrom(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); + tokenX.transfer(addresses.tokenX, addresses.user, UInt64.from(USER_DX)); + tokenY.transfer(addresses.tokenY, addresses.user, UInt64.from(USER_DX)); }); await tx.prove(); pendingTx = await tx.sign([senderKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 94f55b91cc..375e661dba 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -325,16 +325,8 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { feePayer.send({ to: addresses.user, amount: 20e9 }); // give users MINA to pay fees feePayer.send({ to: addresses.user2, amount: 20e9 }); // transfer to fee payer so they can provide initial liquidity - tokenX.transferFrom( - addresses.tokenX, - feePayerAddress, - UInt64.from(10_000) - ); - tokenY.transferFrom( - addresses.tokenY, - feePayerAddress, - UInt64.from(10_000) - ); + tokenX.transfer(addresses.tokenX, feePayerAddress, UInt64.from(10_000)); + tokenY.transfer(addresses.tokenY, feePayerAddress, UInt64.from(10_000)); // mint tokens to the user (this is additional to the tokens minted at the beginning, so we can overflow the balance tokenX.init2(); tokenY.init2(); diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index d103fa946c..344963e565 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -118,7 +118,7 @@ abstract class TokenContract extends SmartContract { /** * Transfer `amount` of tokens from `from` to `to`. */ - transferFrom( + transfer( from: PublicKey | AccountUpdate, to: PublicKey | AccountUpdate, amount: UInt64 @@ -141,13 +141,6 @@ abstract class TokenContract extends SmartContract { let forest = finalizeAccountUpdates([from, to]); this.approveBase(forest); } - - /** - * Transfer `amount` of tokens from this.sender to `to` - */ - transfer(to: PublicKey | AccountUpdate, amount: UInt64) { - this.transferFrom(this.sender, to, amount); - } } function finalizeAccountUpdates(updates: AccountUpdate[]): AccountUpdateForest { From 09960d128846337f41219bd3f663145b1e38604c Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 16:48:03 +0100 Subject: [PATCH 324/524] minor --- src/examples/zkapps/dex/erc20.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index c492036964..98c2438db9 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -2,7 +2,6 @@ import { ProvablePure, Bool, CircuitString, - provablePure, DeployArgs, Field, method, @@ -13,6 +12,7 @@ import { Mina, TokenContract, AccountUpdateForest, + Struct, } from 'o1js'; /** @@ -121,13 +121,11 @@ class TrivialCoin extends TokenContract implements Erc20Like { } events = { - Transfer: provablePure({ - from: PublicKey, - to: PublicKey, - value: UInt64, - }), + Transfer: Struct({ from: PublicKey, to: PublicKey, value: UInt64 }), }; + // TODO: doesn't emit a Transfer event yet (need to make transfer() a separate method from approveBase) + // implement Approvable API @method approveBase(forest: AccountUpdateForest) { From 1f463709c67a5bb890cc75584b01857cc88b8f2d Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 16:55:24 +0100 Subject: [PATCH 325/524] remove debug stuff --- src/examples/zkapps/dex/happy-path-with-proofs.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index e007e6b000..33a3c03363 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -43,8 +43,6 @@ let dex = new Dex(addresses.dex); let dexTokenHolderX = new DexTokenHolder(addresses.dex, tokenIds.X); let dexTokenHolderY = new DexTokenHolder(addresses.dex, tokenIds.Y); -Local.setProofsEnabled(false); - tic('deploy & init token contracts'); tx = await Mina.transaction(feePayerAddress, () => { const accountFee = Mina.getNetworkConstants().accountCreationFee; @@ -106,8 +104,6 @@ console.log('account updates length', tx.transaction.accountUpdates.length); [oldBalances, balances] = [balances, getTokenBalances()]; expect(balances.user.X).toEqual(0n); -Local.setProofsEnabled(true); - tic('redeem liquidity'); let USER_DL = 100n; tx = await Mina.transaction(addresses.user, () => { From 08f99b527052d453937d7b7201184a8df242b548 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 16:55:49 +0100 Subject: [PATCH 326/524] remove debug stuff --- src/examples/zkapps/dex/happy-path-with-proofs.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index 33a3c03363..6f44280549 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -110,9 +110,6 @@ tx = await Mina.transaction(addresses.user, () => { dex.redeemLiquidity(UInt64.from(USER_DL)); }); -console.log(tx.transaction.accountUpdates[0].toPrettyLayout()); -console.log(tx.toPretty()); - await tx.prove(); await tx.sign([keys.user]).send(); toc(); From c171442aaeda65c6459671600728ede5243e0529 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 17:15:12 +0100 Subject: [PATCH 327/524] minor stuff --- src/examples/zkapps/dex/dex.ts | 23 ++++++++--------------- src/examples/zkapps/dex/erc20.ts | 24 ++++++++++++------------ src/examples/zkapps/dex/run.ts | 3 --- src/lib/mina/token/token-contract.ts | 4 ++-- src/lib/zkapp.ts | 2 +- tests/vk-regression/vk-regression.json | 14 +++----------- 6 files changed, 26 insertions(+), 44 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 89c4182af3..5799e6f80a 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -267,11 +267,11 @@ function createDex({ let dx = otherTokenAmount; let tokenX = new TokenContract(otherTokenAddress); // get balances - let x = tokenX.getBalance(this.address); - let y = this.account.balance.get(); - this.account.balance.requireEquals(y); + let dexX = AccountUpdate.create(this.address, tokenX.token.id); + let x = dexX.account.balance.getAndRequireEquals(); + let y = this.account.balance.getAndRequireEquals(); // send x from user to us (i.e., to the same address as this but with the other token) - tokenX.transfer(user, this.address, dx); + tokenX.transfer(user, dexX, dx); // compute and send dy let dy = y.mul(dx).div(x.add(dx)); // just subtract dy balance and let adding balance be handled one level higher @@ -291,10 +291,12 @@ function createDex({ ): UInt64 { let dx = otherTokenAmount; let tokenX = new TokenContract(otherTokenAddress); - let x = tokenX.getBalance(this.address); + // get balances + let dexX = AccountUpdate.create(this.address, tokenX.token.id); + let x = dexX.account.balance.getAndRequireEquals(); let y = this.account.balance.get(); this.account.balance.requireEquals(y); - tokenX.transfer(user, this.address, dx); + tokenX.transfer(user, dexX, dx); // this formula has been changed - we just give the user an additional 15 token let dy = y.mul(dx).div(x.add(dx)).add(15); @@ -405,15 +407,6 @@ class TokenContract extends BaseTokenContract { approveBase(forest: AccountUpdateForest) { this.checkZeroBalanceChange(forest); } - - @method getBalance(publicKey: PublicKey): UInt64 { - let accountUpdate = AccountUpdate.create(publicKey, this.token.id); - let balance = accountUpdate.account.balance.get(); - accountUpdate.account.balance.requireEquals( - accountUpdate.account.balance.get() - ); - return balance; - } } const savedKeys = [ diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index 98c2438db9..58d5f73c48 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -71,6 +71,14 @@ class TrivialCoin extends TokenContract implements Erc20Like { deploy(args: DeployArgs) { super.deploy(args); this.account.tokenSymbol.set('TRIV'); + + // make account non-upgradable forever + this.account.permissions.set({ + ...Permissions.default(), + setVerificationKey: Permissions.impossible(), + setPermissions: Permissions.impossible(), + access: Permissions.proofOrSignature(), + }); } @method init() { @@ -78,25 +86,16 @@ class TrivialCoin extends TokenContract implements Erc20Like { // mint the entire supply to the token account with the same address as this contract let address = this.self.body.publicKey; - let receiver = this.token.mint({ - address, - amount: this.SUPPLY, - }); + let receiver = this.token.mint({ address, amount: this.SUPPLY }); + // assert that the receiving account is new, so this can be only done once receiver.account.isNew.requireEquals(Bool(true)); + // pay fees for opened account this.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); // since this is the only method of this zkApp that resets the entire state, provedState: true implies // that this function was run. Since it can be run only once, this implies it was run exactly once - - // make account non-upgradable forever - this.account.permissions.set({ - ...Permissions.default(), - setVerificationKey: Permissions.impossible(), - setPermissions: Permissions.impossible(), - access: Permissions.proofOrSignature(), - }); } // ERC20 API @@ -117,6 +116,7 @@ class TrivialCoin extends TokenContract implements Erc20Like { owner instanceof PublicKey ? AccountUpdate.create(owner, this.token.id) : owner; + this.approveAccountUpdate(update); return update.account.balance.getAndRequireEquals(); } diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index 32576068ad..ca831647e3 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -367,9 +367,6 @@ async function main({ withVesting }: { withVesting: boolean }) { await tx.prove(); tx.sign([keys.user]); - console.log(tx.transaction.accountUpdates[0].toPrettyLayout()); - console.log(tx.toPretty()); - await tx.send(); [oldBalances, balances] = [balances, getTokenBalances()]; console.log('DEX liquidity (X, Y):', balances.dex.X, balances.dex.Y); diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 344963e565..7f9350a4f0 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -22,7 +22,7 @@ const MAX_ACCOUNT_UPDATES = 20; /** * Base token contract which - * - implements the `Approvable` API with some easy bit left to be defined by subclasses + * - implements the `Approvable` API, with the `approveBase()` method left to be defined by subclasses * - implements the `Transferable` API as a wrapper around the `Approvable` API */ abstract class TokenContract extends SmartContract { @@ -82,7 +82,7 @@ abstract class TokenContract extends SmartContract { /** * Use `forEachUpdate()` to prove that the total balance change of child account updates is zero. * - * This is provided out of the box as it is both a good example, and probably the most common implementation, of `approveUpdates()`. + * This is provided out of the box as it is both a good example, and probably the most common implementation, of `approveBase()`. */ checkZeroBalanceChange(updates: AccountUpdateForest) { let totalBalanceChange = Int64.zero; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 0134b8bfa8..821913ffca 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -756,7 +756,7 @@ super.init(); */ init() { // let accountUpdate = this.newSelf(); // this would emulate the behaviour of init() being a @method - this.account.provedState.assertEquals(Bool(false)); + this.account.provedState.requireEquals(Bool(false)); let accountUpdate = this.self; for (let i = 0; i < ZkappStateLength; i++) { AccountUpdate.setValue(accountUpdate.body.update.appState[i], Field(0)); diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 2c2d6511b7..d2c7e2d099 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -63,7 +63,7 @@ } }, "TokenContract": { - "digest": "3617eb15679a710b2321c511f9870464f846de5e93c31c0e6d40a93d1d3b15e1", + "digest": "19aa0f5b442d011f2bbdd68ac189bf91cd017803f0f91a5ab6a61d50e3136c2", "methods": { "init": { "rows": 655, @@ -76,19 +76,11 @@ "approveBase": { "rows": 13194, "digest": "8f8ad42a6586936a28b570877403960c" - }, - "deployZkapp": { - "rows": 702, - "digest": "e5ac2667a79f44f1e7a65b12d8ac006c" - }, - "getBalance": { - "rows": 686, - "digest": "44a90b65d1d7ee553811759b115d12cc" } }, "verificationKey": { - "data": "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", - "hash": "27127639205719537939166450295035225998965373687685248985354531474736258095446" + "data": "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", + "hash": "10629507172660088077994796699854359657108723627411391158715881834104929001219" } }, "Dex": { From 97cdacfae56147df1c90599dd8ec94f51ba12570 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 14:18:11 +0100 Subject: [PATCH 328/524] fix edge case in ecdsa unit test --- src/lib/gadgets/ecdsa.unit-test.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index 250b088709..a04ccc932c 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -59,14 +59,23 @@ for (let Curve of curves) { let noGlv = fromRandom(Random.map(Random.fraction(), (f) => f < 0.3)); // provable method we want to test - const verify = (s: Second, noGlv: boolean) => { + const verify = (sig: Second, noGlv: boolean) => { // invalid public key can lead to either a failing constraint, or verify() returning false - EllipticCurve.assertOnCurve(s.publicKey, Curve); + EllipticCurve.assertOnCurve(sig.publicKey, Curve); + + // additional checks which are inconsistent between constant and variable verification + let { s, r } = sig.signature; + if (Field3.isConstant(s)) { + assert(Field3.toBigint(s) !== 0n, 'invalid signature (s=0)'); + } + if (Field3.isConstant(r)) { + assert(Field3.toBigint(r) !== 0n, 'invalid signature (r=0)'); + } let hasGlv = Curve.hasEndomorphism; if (noGlv) Curve.hasEndomorphism = false; // hack to force non-GLV version try { - return Ecdsa.verify(Curve, s.signature, s.msg, s.publicKey); + return Ecdsa.verify(Curve, sig.signature, sig.msg, sig.publicKey); } finally { Curve.hasEndomorphism = hasGlv; } From c5c4c35656ef8e13bc62b7a1ac2e4d7e37d1662c Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 15:49:06 +0100 Subject: [PATCH 329/524] restructure code to fix import cycles in account_update.ts --- src/lib/account_update.ts | 209 ++++------------------------ src/lib/mina.ts | 203 ++------------------------- src/lib/mina/mina-instance.ts | 123 ++++++++++++++++ src/lib/mina/smart-contract-base.ts | 13 ++ src/lib/mina/transaction-context.ts | 16 +++ src/lib/precondition.ts | 173 ++++++++++++++++++++--- src/lib/zkapp.ts | 64 ++++++++- 7 files changed, 412 insertions(+), 389 deletions(-) create mode 100644 src/lib/mina/mina-instance.ts create mode 100644 src/lib/mina/smart-contract-base.ts create mode 100644 src/lib/mina/transaction-context.ts diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index f585fed1cd..397db60f73 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -15,9 +15,17 @@ import { } from '../bindings/mina-transaction/types.js'; import { PrivateKey, PublicKey } from './signature.js'; import { UInt64, UInt32, Int64, Sign } from './int.js'; -import * as Mina from './mina.js'; -import { SmartContract } from './zkapp.js'; -import * as Precondition from './precondition.js'; +import type { SmartContract } from './zkapp.js'; +import { + Preconditions, + Account, + Network, + CurrentSlot, + preconditions, + OrIgnore, + ClosedInterval, + getAccountPreconditions, +} from './precondition.js'; import { dummyBase64Proof, Empty, Proof, Prover } from './proof_system.js'; import { Memo } from '../mina-signer/src/memo.js'; import { @@ -28,11 +36,13 @@ import { TokenId as Base58TokenId } from './base58-encodings.js'; import { hashWithPrefix, packToFields } from './hash.js'; import { mocks, prefixes } from '../bindings/crypto/constants.js'; import { Context } from './global-context.js'; -import { assert } from './errors.js'; import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; +import { currentTransaction } from './mina/transaction-context.js'; +import { isSmartContract } from './mina/smart-contract-base.js'; +import { activeInstance } from './mina/mina-instance.js'; // external API export { AccountUpdate, Permissions, ZkappPublicInput }; @@ -59,6 +69,7 @@ export { zkAppProver, SmartContractContext, dummySignature, + LazyProof, }; const ZkappStateLength = 8; @@ -86,11 +97,6 @@ type Update = AccountUpdateBody['update']; type MayUseToken = AccountUpdateBody['mayUseToken']; -/** - * Preconditions for the network and accounts - */ -type Preconditions = AccountUpdateBody['preconditions']; - /** * Either set a value or keep it the same. */ @@ -483,107 +489,6 @@ type FeePayerUnsigned = FeePayer & { lazyAuthorization?: LazySignature | undefined; }; -/** - * Either check a value or ignore it. - * - * Used within [[ AccountPredicate ]]s and [[ ProtocolStatePredicate ]]s. - */ -type OrIgnore = { isSome: Bool; value: T }; - -/** - * An interval representing all the values between `lower` and `upper` inclusive - * of both the `lower` and `upper` values. - * - * @typeParam A something with an ordering where one can quantify a lower and - * upper bound. - */ -type ClosedInterval = { lower: T; upper: T }; - -type NetworkPrecondition = Preconditions['network']; -let NetworkPrecondition = { - ignoreAll(): NetworkPrecondition { - let stakingEpochData = { - ledger: { hash: ignore(Field(0)), totalCurrency: ignore(uint64()) }, - seed: ignore(Field(0)), - startCheckpoint: ignore(Field(0)), - lockCheckpoint: ignore(Field(0)), - epochLength: ignore(uint32()), - }; - let nextEpochData = cloneCircuitValue(stakingEpochData); - return { - snarkedLedgerHash: ignore(Field(0)), - blockchainLength: ignore(uint32()), - minWindowDensity: ignore(uint32()), - totalCurrency: ignore(uint64()), - globalSlotSinceGenesis: ignore(uint32()), - stakingEpochData, - nextEpochData, - }; - }, -}; - -/** - * Ignores a `dummy` - * - * @param dummy The value to ignore - * @returns Always an ignored value regardless of the input. - */ -function ignore(dummy: T): OrIgnore { - return { isSome: Bool(false), value: dummy }; -} - -/** - * Ranges between all uint32 values - */ -const uint32 = () => ({ lower: UInt32.from(0), upper: UInt32.MAXINT() }); - -/** - * Ranges between all uint64 values - */ -const uint64 = () => ({ lower: UInt64.from(0), upper: UInt64.MAXINT() }); - -type AccountPrecondition = Preconditions['account']; -const AccountPrecondition = { - ignoreAll(): AccountPrecondition { - let appState: Array> = []; - for (let i = 0; i < ZkappStateLength; ++i) { - appState.push(ignore(Field(0))); - } - return { - balance: ignore(uint64()), - nonce: ignore(uint32()), - receiptChainHash: ignore(Field(0)), - delegate: ignore(PublicKey.empty()), - state: appState, - actionState: ignore(Actions.emptyActionState()), - provedState: ignore(Bool(false)), - isNew: ignore(Bool(false)), - }; - }, - nonce(nonce: UInt32): AccountPrecondition { - let p = AccountPrecondition.ignoreAll(); - AccountUpdate.assertEquals(p.nonce, nonce); - return p; - }, -}; - -type GlobalSlotPrecondition = Preconditions['validWhile']; -const GlobalSlotPrecondition = { - ignoreAll(): GlobalSlotPrecondition { - return ignore(uint32()); - }, -}; - -const Preconditions = { - ignoreAll(): Preconditions { - return { - account: AccountPrecondition.ignoreAll(), - network: NetworkPrecondition.ignoreAll(), - validWhile: GlobalSlotPrecondition.ignoreAll(), - }; - }, -}; - type Control = Types.AccountUpdate['authorization']; type LazyNone = { kind: 'lazy-none'; @@ -664,9 +569,9 @@ class AccountUpdate implements Types.AccountUpdate { authorization: Control; lazyAuthorization: LazySignature | LazyProof | LazyNone | undefined = undefined; - account: Precondition.Account; - network: Precondition.Network; - currentSlot: Precondition.CurrentSlot; + account: Account; + network: Network; + currentSlot: CurrentSlot; children: { callsType: | { type: 'None' } @@ -688,10 +593,7 @@ class AccountUpdate implements Types.AccountUpdate { this.id = Math.random(); this.body = body; this.authorization = authorization; - let { account, network, currentSlot } = Precondition.preconditions( - this, - isSelf - ); + let { account, network, currentSlot } = preconditions(this, isSelf); this.account = account; this.network = network; this.currentSlot = currentSlot; @@ -730,7 +632,7 @@ class AccountUpdate implements Types.AccountUpdate { accountLike: PublicKey | AccountUpdate | SmartContract, label: string ) { - if (accountLike instanceof SmartContract) { + if (isSmartContract(accountLike)) { accountLike = accountLike.self; } if (accountLike instanceof AccountUpdate) { @@ -842,7 +744,7 @@ class AccountUpdate implements Types.AccountUpdate { if (to instanceof AccountUpdate) { receiver = to; receiver.body.tokenId.assertEquals(this.body.tokenId); - } else if (to instanceof SmartContract) { + } else if (isSmartContract(to)) { receiver = to.self; receiver.body.tokenId.assertEquals(this.body.tokenId); } else { @@ -1032,13 +934,11 @@ class AccountUpdate implements Types.AccountUpdate { let publicKey = update.body.publicKey; let tokenId = update instanceof AccountUpdate ? update.body.tokenId : TokenId.default; - let nonce = Number( - Precondition.getAccountPreconditions(update.body).nonce.toString() - ); + let nonce = Number(getAccountPreconditions(update.body).nonce.toString()); // if the fee payer is the same account update as this one, we have to start // the nonce predicate at one higher, bc the fee payer already increases its // nonce - let isFeePayer = Mina.currentTransaction()?.sender?.equals(publicKey); + let isFeePayer = currentTransaction()?.sender?.equals(publicKey); let isSameAsFeePayer = !!isFeePayer ?.and(tokenId.equals(TokenId.default)) .toBoolean(); @@ -1046,7 +946,7 @@ class AccountUpdate implements Types.AccountUpdate { // now, we check how often this account update already updated its nonce in // this tx, and increase nonce from `getAccount` by that amount CallForest.forEachPredecessor( - Mina.currentTransaction.get().accountUpdates, + currentTransaction.get().accountUpdates, update as AccountUpdate, (otherUpdate) => { let shouldIncreaseNonce = otherUpdate.publicKey @@ -1163,7 +1063,7 @@ class AccountUpdate implements Types.AccountUpdate { self.label || 'Unlabeled' } > AccountUpdate.create()`; } else { - Mina.currentTransaction()?.accountUpdates.push(accountUpdate); + currentTransaction()?.accountUpdates.push(accountUpdate); accountUpdate.label = `Mina.transaction > AccountUpdate.create()`; } return accountUpdate; @@ -1182,8 +1082,8 @@ class AccountUpdate implements Types.AccountUpdate { if (selfUpdate === accountUpdate) return; insideContract.this.self.approve(accountUpdate); } else { - if (!Mina.currentTransaction.has()) return; - let updates = Mina.currentTransaction.get().accountUpdates; + if (!currentTransaction.has()) return; + let updates = currentTransaction.get().accountUpdates; if (!updates.find((update) => update.id === accountUpdate.id)) { updates.push(accountUpdate); } @@ -1195,7 +1095,7 @@ class AccountUpdate implements Types.AccountUpdate { static unlink(accountUpdate: AccountUpdate) { let siblings = accountUpdate.parent?.children.accountUpdates ?? - Mina.currentTransaction()?.accountUpdates; + currentTransaction()?.accountUpdates; if (siblings === undefined) return; let i = siblings?.findIndex((update) => update.id === accountUpdate.id); if (i !== undefined && i !== -1) { @@ -1273,7 +1173,7 @@ class AccountUpdate implements Types.AccountUpdate { ) { let accountUpdate = AccountUpdate.createSigned(feePayer as PrivateKey); accountUpdate.label = 'AccountUpdate.fundNewAccount()'; - let fee = Mina.getNetworkConstants().accountCreationFee; + let fee = activeInstance.getNetworkConstants().accountCreationFee; numberOfAccounts ??= 1; if (typeof numberOfAccounts === 'number') fee = fee.mul(numberOfAccounts); else fee = fee.add(UInt64.from(numberOfAccounts.initialBalance ?? 0)); @@ -1842,57 +1742,6 @@ const Authorization = { accountUpdate.authorization = {}; accountUpdate.lazyAuthorization = { ...signature, kind: 'lazy-signature' }; }, - setProofAuthorizationKind( - { body, id }: AccountUpdate, - priorAccountUpdates?: AccountUpdate[] - ) { - body.authorizationKind.isSigned = Bool(false); - body.authorizationKind.isProved = Bool(true); - let hash = Provable.witness(Field, () => { - let proverData = zkAppProver.getData(); - let isProver = proverData !== undefined; - assert( - isProver || priorAccountUpdates !== undefined, - 'Called `setProofAuthorizationKind()` outside the prover without passing in `priorAccountUpdates`.' - ); - let myAccountUpdateId = isProver ? proverData.accountUpdate.id : id; - priorAccountUpdates ??= proverData.transaction.accountUpdates; - priorAccountUpdates = priorAccountUpdates.filter( - (a) => a.id !== myAccountUpdateId - ); - let priorAccountUpdatesFlat = CallForest.toFlatList( - priorAccountUpdates, - false - ); - let accountUpdate = [...priorAccountUpdatesFlat] - .reverse() - .find((body_) => - body_.update.verificationKey.isSome - .and(body_.tokenId.equals(body.tokenId)) - .and(body_.publicKey.equals(body.publicKey)) - .toBoolean() - ); - if (accountUpdate !== undefined) { - return accountUpdate.body.update.verificationKey.value.hash; - } - try { - let account = Mina.getAccount(body.publicKey, body.tokenId); - return account.zkapp?.verificationKey?.hash ?? Field(0); - } catch { - return Field(0); - } - }); - body.authorizationKind.verificationKeyHash = hash; - }, - setLazyProof( - accountUpdate: AccountUpdate, - proof: Omit, - priorAccountUpdates: AccountUpdate[] - ) { - Authorization.setProofAuthorizationKind(accountUpdate, priorAccountUpdates); - accountUpdate.authorization = {}; - accountUpdate.lazyAuthorization = { ...proof, kind: 'lazy-proof' }; - }, setLazyNone(accountUpdate: AccountUpdate) { accountUpdate.body.authorizationKind.isSigned = Bool(false); accountUpdate.body.authorizationKind.isProved = Bool(false); diff --git a/src/lib/mina.ts b/src/lib/mina.ts index fbac14657b..155dda1204 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -20,7 +20,6 @@ import * as Fetch from './fetch.js'; import { assertPreconditionInvariants, NetworkValue } from './precondition.js'; import { cloneCircuitValue, toConstant } from './circuit_value.js'; import { Empty, Proof, verify } from './proof_system.js'; -import { Context } from './global-context.js'; import { SmartContract } from './zkapp.js'; import { invalidTransactionError } from './mina/errors.js'; import { Types, TypesBigint } from '../bindings/mina-transaction/types.js'; @@ -34,6 +33,15 @@ import { verifyAccountUpdateSignature, } from '../mina-signer/src/sign-zkapp-command.js'; import { NetworkId } from '../mina-signer/src/TSTypes.js'; +import { FetchMode, currentTransaction } from './mina/transaction-context.js'; +import { + activeInstance, + setActiveInstance, + Mina, + FeePayerSpec, + DeprecatedFeePayerSpec, + ActionStates, +} from './mina/mina-instance.js'; export { createTransaction, @@ -41,7 +49,6 @@ export { Network, LocalBlockchain, currentTransaction, - CurrentTransaction, Transaction, TransactionId, activeInstance, @@ -120,63 +127,6 @@ const Transaction = { }, }; -type FetchMode = 'fetch' | 'cached' | 'test'; -type CurrentTransaction = { - sender?: PublicKey; - accountUpdates: AccountUpdate[]; - fetchMode: FetchMode; - isFinalRunOutsideCircuit: boolean; - numberOfRuns: 0 | 1 | undefined; -}; - -let currentTransaction = Context.create(); - -/** - * Allows you to specify information about the fee payer account and the transaction. - */ -type FeePayerSpec = - | PublicKey - | { - sender: PublicKey; - fee?: number | string | UInt64; - memo?: string; - nonce?: number; - } - | undefined; - -type DeprecatedFeePayerSpec = - | PublicKey - | PrivateKey - | (( - | { - feePayerKey: PrivateKey; - sender?: PublicKey; - } - | { - feePayerKey?: PrivateKey; - sender: PublicKey; - } - ) & { - fee?: number | string | UInt64; - memo?: string; - nonce?: number; - }) - | undefined; - -type ActionStates = { - fromActionState?: Field; - endActionState?: Field; -}; - -type NetworkConstants = { - genesisTimestamp: UInt64; - /** - * Duration of 1 slot in milliseconds - */ - slotTime: UInt64; - accountCreationFee: UInt64; -}; - function reportGetAccountError(publicKey: string, tokenId: string) { if (tokenId === TokenId.toBase58(TokenId.default)) { return `getAccount: Could not find account for public key ${publicKey}`; @@ -345,40 +295,6 @@ function newTransaction(transaction: ZkappCommand, proofsEnabled?: boolean) { return self; } -interface Mina { - transaction( - sender: DeprecatedFeePayerSpec, - f: () => void - ): Promise; - currentSlot(): UInt32; - hasAccount(publicKey: PublicKey, tokenId?: Field): boolean; - getAccount(publicKey: PublicKey, tokenId?: Field): Account; - getNetworkState(): NetworkValue; - getNetworkConstants(): NetworkConstants; - /** - * @deprecated use {@link getNetworkConstants} - */ - accountCreationFee(): UInt64; - sendTransaction(transaction: Transaction): Promise; - fetchEvents: ( - publicKey: PublicKey, - tokenId?: Field, - filterOptions?: Fetch.EventActionFilterOptions - ) => ReturnType; - fetchActions: ( - publicKey: PublicKey, - actionStates?: ActionStates, - tokenId?: Field - ) => ReturnType; - getActions: ( - publicKey: PublicKey, - actionStates?: ActionStates, - tokenId?: Field - ) => { hash: string; actions: string[][] }[]; - proofsEnabled: boolean; - getNetworkId(): NetworkId; -} - const defaultAccountCreationFee = 1_000_000_000; const defaultNetworkConstants: NetworkConstants = { genesisTimestamp: UInt64.from(0), @@ -429,8 +345,8 @@ function LocalBlockchain({ getNetworkId: () => minaNetworkId, proofsEnabled, /** - * @deprecated use {@link Mina.getNetworkConstants} - */ + * @deprecated use {@link Mina.getNetworkConstants} + */ accountCreationFee: () => defaultNetworkConstants.accountCreationFee, getNetworkConstants() { return { @@ -518,7 +434,8 @@ function LocalBlockchain({ // TODO: label updates, and try to give precise explanations about what went wrong let errors = JSON.parse(err.message); err.message = invalidTransactionError(txn.transaction, errors, { - accountCreationFee: defaultNetworkConstants.accountCreationFee.toString(), + accountCreationFee: + defaultNetworkConstants.accountCreationFee.toString(), }); } finally { throw err; @@ -764,8 +681,8 @@ function Network( return { getNetworkId: () => minaNetworkId, /** - * @deprecated use {@link Mina.getNetworkConstants} - */ + * @deprecated use {@link Mina.getNetworkConstants} + */ accountCreationFee: () => defaultNetworkConstants.accountCreationFee, getNetworkConstants() { if (currentTransaction()?.fetchMode === 'test') { @@ -1026,96 +943,6 @@ function BerkeleyQANet(graphqlEndpoint: string) { return Network(graphqlEndpoint); } -let activeInstance: Mina = { - getNetworkId: () => 'testnet', - /** - * @deprecated use {@link Mina.getNetworkConstants} - */ - accountCreationFee: () => defaultNetworkConstants.accountCreationFee, - getNetworkConstants() { - return defaultNetworkConstants; - }, - currentSlot: () => { - throw new Error('must call Mina.setActiveInstance first'); - }, - hasAccount(publicKey: PublicKey, tokenId: Field = TokenId.default) { - if ( - !currentTransaction.has() || - currentTransaction.get().fetchMode === 'cached' - ) { - return !!Fetch.getCachedAccount( - publicKey, - tokenId, - Fetch.networkConfig.minaEndpoint - ); - } - return false; - }, - getAccount(publicKey: PublicKey, tokenId: Field = TokenId.default) { - if (currentTransaction()?.fetchMode === 'test') { - Fetch.markAccountToBeFetched( - publicKey, - tokenId, - Fetch.networkConfig.minaEndpoint - ); - return dummyAccount(publicKey); - } - if ( - !currentTransaction.has() || - currentTransaction.get().fetchMode === 'cached' - ) { - let account = Fetch.getCachedAccount( - publicKey, - tokenId, - Fetch.networkConfig.minaEndpoint - ); - if (account === undefined) - throw Error( - `${reportGetAccountError( - publicKey.toBase58(), - TokenId.toBase58(tokenId) - )}\n\nEither call Mina.setActiveInstance first or explicitly add the account with addCachedAccount` - ); - return account; - } - throw new Error('must call Mina.setActiveInstance first'); - }, - getNetworkState() { - throw new Error('must call Mina.setActiveInstance first'); - }, - sendTransaction() { - throw new Error('must call Mina.setActiveInstance first'); - }, - async transaction(sender: DeprecatedFeePayerSpec, f: () => void) { - return createTransaction(sender, f, 0); - }, - fetchEvents(_publicKey: PublicKey, _tokenId: Field = TokenId.default) { - throw Error('must call Mina.setActiveInstance first'); - }, - fetchActions( - _publicKey: PublicKey, - _actionStates?: ActionStates, - _tokenId: Field = TokenId.default - ) { - throw Error('must call Mina.setActiveInstance first'); - }, - getActions( - _publicKey: PublicKey, - _actionStates?: ActionStates, - _tokenId: Field = TokenId.default - ) { - throw Error('must call Mina.setActiveInstance first'); - }, - proofsEnabled: true, -}; - -/** - * Set the currently used Mina instance. - */ -function setActiveInstance(m: Mina) { - activeInstance = m; -} - /** * Construct a smart contract transaction. Within the callback passed to this function, * you can call into the methods of smart contracts. diff --git a/src/lib/mina/mina-instance.ts b/src/lib/mina/mina-instance.ts new file mode 100644 index 0000000000..a64f82ec01 --- /dev/null +++ b/src/lib/mina/mina-instance.ts @@ -0,0 +1,123 @@ +/** + * This module holds the global Mina instance and its interface. + */ +import type { Field } from '../field.js'; +import { UInt64, UInt32 } from '../int.js'; +import type { PublicKey, PrivateKey } from '../signature.js'; +import type { Transaction, TransactionId } from '../mina.js'; +import type { Account } from './account.js'; +import type { NetworkValue } from '../precondition.js'; +import type * as Fetch from '../fetch.js'; + +export { + Mina, + FeePayerSpec, + DeprecatedFeePayerSpec, + ActionStates, + activeInstance, + setActiveInstance, + ZkappStateLength, +}; + +const defaultAccountCreationFee = 1_000_000_000; +const ZkappStateLength = 8; + +/** + * Allows you to specify information about the fee payer account and the transaction. + */ +type FeePayerSpec = + | PublicKey + | { + sender: PublicKey; + fee?: number | string | UInt64; + memo?: string; + nonce?: number; + } + | undefined; + +type DeprecatedFeePayerSpec = + | PublicKey + | PrivateKey + | (( + | { + feePayerKey: PrivateKey; + sender?: PublicKey; + } + | { + feePayerKey?: PrivateKey; + sender: PublicKey; + } + ) & { + fee?: number | string | UInt64; + memo?: string; + nonce?: number; + }) + | undefined; + +type ActionStates = { + fromActionState?: Field; + endActionState?: Field; +}; + +interface Mina { + transaction( + sender: DeprecatedFeePayerSpec, + f: () => void + ): Promise; + currentSlot(): UInt32; + hasAccount(publicKey: PublicKey, tokenId?: Field): boolean; + getAccount(publicKey: PublicKey, tokenId?: Field): Account; + getNetworkState(): NetworkValue; + getNetworkConstants(): { + genesisTimestamp: UInt64; + /** + * Duration of 1 slot in millisecondw + */ + slotTime: UInt64; + accountCreationFee: UInt64; + }; + accountCreationFee(): UInt64; + sendTransaction(transaction: Transaction): Promise; + fetchEvents: ( + publicKey: PublicKey, + tokenId?: Field, + filterOptions?: Fetch.EventActionFilterOptions + ) => ReturnType; + fetchActions: ( + publicKey: PublicKey, + actionStates?: ActionStates, + tokenId?: Field + ) => ReturnType; + getActions: ( + publicKey: PublicKey, + actionStates?: ActionStates, + tokenId?: Field + ) => { hash: string; actions: string[][] }[]; + proofsEnabled: boolean; +} + +let activeInstance: Mina = { + accountCreationFee: () => UInt64.from(defaultAccountCreationFee), + getNetworkConstants: noActiveInstance, + currentSlot: noActiveInstance, + hasAccount: noActiveInstance, + getAccount: noActiveInstance, + getNetworkState: noActiveInstance, + sendTransaction: noActiveInstance, + transaction: noActiveInstance, + fetchEvents: noActiveInstance, + fetchActions: noActiveInstance, + getActions: noActiveInstance, + proofsEnabled: true, +}; + +/** + * Set the currently used Mina instance. + */ +function setActiveInstance(m: Mina) { + activeInstance = m; +} + +function noActiveInstance(): never { + throw Error('Must call Mina.setActiveInstance first'); +} diff --git a/src/lib/mina/smart-contract-base.ts b/src/lib/mina/smart-contract-base.ts new file mode 100644 index 0000000000..37d54379e0 --- /dev/null +++ b/src/lib/mina/smart-contract-base.ts @@ -0,0 +1,13 @@ +/** + * This file exists to avoid an import cycle between code that just needs access + * to a smart contract base class, and the code that implements `SmartContract`. + */ +import type { SmartContract } from '../zkapp.js'; + +export { isSmartContract, SmartContractBase }; + +class SmartContractBase {} + +function isSmartContract(object: unknown): object is SmartContract { + return object instanceof SmartContractBase; +} diff --git a/src/lib/mina/transaction-context.ts b/src/lib/mina/transaction-context.ts new file mode 100644 index 0000000000..a3bb040b1c --- /dev/null +++ b/src/lib/mina/transaction-context.ts @@ -0,0 +1,16 @@ +import type { AccountUpdate } from '../account_update.js'; +import type { PublicKey } from '../signature.js'; +import { Context } from '../global-context.js'; + +export { currentTransaction, CurrentTransaction, FetchMode }; + +type FetchMode = 'fetch' | 'cached' | 'test'; +type CurrentTransaction = { + sender?: PublicKey; + accountUpdates: AccountUpdate[]; + fetchMode: FetchMode; + isFinalRunOutsideCircuit: boolean; + numberOfRuns: 0 | 1 | undefined; +}; + +let currentTransaction = Context.create(); diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index ac5fd8f8aa..eba86036bf 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -1,14 +1,19 @@ import { Bool, Field } from './core.js'; -import { circuitValueEquals } from './circuit_value.js'; +import { circuitValueEquals, cloneCircuitValue } from './circuit_value.js'; import { Provable } from './provable.js'; -import * as Mina from './mina.js'; -import { Actions, AccountUpdate, Preconditions } from './account_update.js'; +import { activeInstance as Mina } from './mina/mina-instance.js'; +import type { AccountUpdate } from './account_update.js'; import { Int64, UInt32, UInt64 } from './int.js'; import { Layout } from '../bindings/mina-transaction/gen/transaction.js'; import { jsLayout } from '../bindings/mina-transaction/gen/js-layout.js'; import { emptyReceiptChainHash, TokenSymbol } from './hash.js'; import { PublicKey } from './signature.js'; -import { ZkappUri } from '../bindings/mina-transaction/transaction-leaves.js'; +import { + Actions, + ZkappUri, +} from '../bindings/mina-transaction/transaction-leaves.js'; +import type { Types } from '../bindings/mina-transaction/types.js'; +import { ZkappStateLength } from './mina/mina-instance.js'; export { preconditions, @@ -20,6 +25,145 @@ export { AccountValue, NetworkValue, getAccountPreconditions, + Preconditions, + OrIgnore, + ClosedInterval, +}; + +type AccountUpdateBody = Types.AccountUpdate['body']; + +/** + * Preconditions for the network and accounts + */ +type Preconditions = AccountUpdateBody['preconditions']; + +/** + * Either check a value or ignore it. + * + * Used within [[ AccountPredicate ]]s and [[ ProtocolStatePredicate ]]s. + */ +type OrIgnore = { isSome: Bool; value: T }; + +/** + * An interval representing all the values between `lower` and `upper` inclusive + * of both the `lower` and `upper` values. + * + * @typeParam A something with an ordering where one can quantify a lower and + * upper bound. + */ +type ClosedInterval = { lower: T; upper: T }; + +type NetworkPrecondition = Preconditions['network']; +let NetworkPrecondition = { + ignoreAll(): NetworkPrecondition { + let stakingEpochData = { + ledger: { hash: ignore(Field(0)), totalCurrency: ignore(uint64()) }, + seed: ignore(Field(0)), + startCheckpoint: ignore(Field(0)), + lockCheckpoint: ignore(Field(0)), + epochLength: ignore(uint32()), + }; + let nextEpochData = cloneCircuitValue(stakingEpochData); + return { + snarkedLedgerHash: ignore(Field(0)), + blockchainLength: ignore(uint32()), + minWindowDensity: ignore(uint32()), + totalCurrency: ignore(uint64()), + globalSlotSinceGenesis: ignore(uint32()), + stakingEpochData, + nextEpochData, + }; + }, +}; + +/** + * Ignores a `dummy` + * + * @param dummy The value to ignore + * @returns Always an ignored value regardless of the input. + */ +function ignore(dummy: T): OrIgnore { + return { isSome: Bool(false), value: dummy }; +} + +/** + * Ranges between all uint32 values + */ +const uint32 = () => ({ lower: UInt32.from(0), upper: UInt32.MAXINT() }); + +/** + * Ranges between all uint64 values + */ +const uint64 = () => ({ lower: UInt64.from(0), upper: UInt64.MAXINT() }); + +/** + * Fix a property to a certain value. + * + * @param property The property to constrain + * @param value The value it is fixed to + * + * Example: To fix the account nonce of a SmartContract to 0, you can use + * + * ```ts + * \@method onlyRunsWhenNonceIsZero() { + * AccountUpdate.assertEquals(this.self.body.preconditions.account.nonce, UInt32.zero); + * // ... + * } + * ``` + */ +function assertEquals( + property: OrIgnore | T>, + value: T +) { + property.isSome = Bool(true); + if ('lower' in property.value && 'upper' in property.value) { + property.value.lower = value; + property.value.upper = value; + } else { + property.value = value; + } +} + +type AccountPrecondition = Preconditions['account']; +const AccountPrecondition = { + ignoreAll(): AccountPrecondition { + let appState: Array> = []; + for (let i = 0; i < ZkappStateLength; ++i) { + appState.push(ignore(Field(0))); + } + return { + balance: ignore(uint64()), + nonce: ignore(uint32()), + receiptChainHash: ignore(Field(0)), + delegate: ignore(PublicKey.empty()), + state: appState, + actionState: ignore(Actions.emptyActionState()), + provedState: ignore(Bool(false)), + isNew: ignore(Bool(false)), + }; + }, + nonce(nonce: UInt32): AccountPrecondition { + let p = AccountPrecondition.ignoreAll(); + assertEquals(p.nonce, nonce); + return p; + }, +}; + +type GlobalSlotPrecondition = Preconditions['validWhile']; +const GlobalSlotPrecondition = { + ignoreAll(): GlobalSlotPrecondition { + return ignore(uint32()); + }, +}; + +const Preconditions = { + ignoreAll(): Preconditions { + return { + account: AccountPrecondition.ignoreAll(), + network: NetworkPrecondition.ignoreAll(), + validWhile: GlobalSlotPrecondition.ignoreAll(), + }; + }, }; function preconditions(accountUpdate: AccountUpdate, isSelf: boolean) { @@ -57,8 +201,7 @@ function Network(accountUpdate: AccountUpdate): Network { return this.getAndRequireEquals(); }, requireEquals(value: UInt64) { - let { genesisTimestamp, slotTime } = - Mina.getNetworkConstants(); + let { genesisTimestamp, slotTime } = Mina.getNetworkConstants(); let slot = timestampToGlobalSlot( value, `Timestamp precondition unsatisfied: the timestamp can only equal numbers of the form ${genesisTimestamp} + k*${slotTime},\n` + @@ -318,13 +461,11 @@ function getVariable( } function globalSlotToTimestamp(slot: UInt32) { - let { genesisTimestamp, slotTime } = - Mina.getNetworkConstants(); + let { genesisTimestamp, slotTime } = Mina.getNetworkConstants(); return UInt64.from(slot).mul(slotTime).add(genesisTimestamp); } function timestampToGlobalSlot(timestamp: UInt64, message: string) { - let { genesisTimestamp, slotTime } = - Mina.getNetworkConstants(); + let { genesisTimestamp, slotTime } = Mina.getNetworkConstants(); let { quotient: slot, rest } = timestamp .sub(genesisTimestamp) .divMod(slotTime); @@ -339,8 +480,7 @@ function timestampToGlobalSlotRange( // we need `slotLower <= current slot <= slotUpper` to imply `tsLower <= current timestamp <= tsUpper` // so we have to make the range smaller -- round up `tsLower` and round down `tsUpper` // also, we should clamp to the UInt32 max range [0, 2**32-1] - let { genesisTimestamp, slotTime } = - Mina.getNetworkConstants(); + let { genesisTimestamp, slotTime } = Mina.getNetworkConstants(); let tsLowerInt = Int64.from(tsLower) .sub(genesisTimestamp) .add(slotTime) @@ -452,7 +592,6 @@ const preconditionContexts = new WeakMap(); // exported types -type NetworkPrecondition = Preconditions['network']; type NetworkValue = PreconditionBaseTypes; type RawNetwork = PreconditionClassType; type Network = RawNetwork & { @@ -461,9 +600,9 @@ type Network = RawNetwork & { // TODO: should we add account.state? // then can just use circuitArray(Field, 8) as the type -type AccountPrecondition = Omit; -type AccountValue = PreconditionBaseTypes; -type Account = PreconditionClassType & Update; +type AccountPreconditionNoState = Omit; +type AccountValue = PreconditionBaseTypes; +type Account = PreconditionClassType & Update; type CurrentSlotPrecondition = Preconditions['validWhile']; type CurrentSlot = { @@ -552,7 +691,7 @@ type PreconditionFlatEntry = T extends RangeCondition type FlatPreconditionValue = { [S in PreconditionFlatEntry as `network.${S[0]}`]: S[2]; } & { - [S in PreconditionFlatEntry as `account.${S[0]}`]: S[2]; + [S in PreconditionFlatEntry as `account.${S[0]}`]: S[2]; } & { validWhile: PreconditionFlatEntry[2] }; type LongKey = keyof FlatPreconditionValue; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 9346973f72..85f172b3d6 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -16,6 +16,8 @@ import { ZkappPublicInput, ZkappStateLength, SmartContractContext, + LazyProof, + CallForest, } from './account_update.js'; import { cloneCircuitValue, @@ -56,6 +58,8 @@ import { snarkContext, } from './provable-context.js'; import { Cache } from './proof-system/cache.js'; +import { SmartContractBase } from './mina/smart-contract-base.js'; +import { assert } from './gadgets/common.js'; // external API export { @@ -209,7 +213,7 @@ function wrapMethod( blindingValue ); accountUpdate.body.callData = Poseidon.hash(callDataFields); - Authorization.setProofAuthorizationKind(accountUpdate); + ProofAuthorization.setKind(accountUpdate); // TODO: currently commented out, but could come back in some form when we add caller to the public input // // compute `caller` field from `isDelegateCall` and a context determined by the transaction @@ -331,7 +335,7 @@ function wrapMethod( accountUpdate.body.callData = Poseidon.hash(callDataFields); if (!Authorization.hasAny(accountUpdate)) { - Authorization.setLazyProof( + ProofAuthorization.setLazyProof( accountUpdate, { methodName: methodIntf.methodName, @@ -425,7 +429,7 @@ function wrapMethod( accountUpdate.body.callData = hashConstant(callDataFields); if (!Authorization.hasAny(accountUpdate)) { - Authorization.setLazyProof( + ProofAuthorization.setLazyProof( accountUpdate, { methodName: methodIntf.methodName, @@ -597,7 +601,7 @@ class Callback extends GenericArgument { * ``` * */ -class SmartContract { +class SmartContract extends SmartContractBase { address: PublicKey; tokenId: Field; @@ -635,6 +639,7 @@ class SmartContract { } constructor(address: PublicKey, tokenId?: Field) { + super(); this.address = address; this.tokenId = tokenId ?? TokenId.default; Object.defineProperty(this, 'reducer', { @@ -1558,6 +1563,57 @@ const Reducer: (< { get: Actions.emptyActionState } ) as any; +const ProofAuthorization = { + setKind({ body, id }: AccountUpdate, priorAccountUpdates?: AccountUpdate[]) { + body.authorizationKind.isSigned = Bool(false); + body.authorizationKind.isProved = Bool(true); + let hash = Provable.witness(Field, () => { + let proverData = zkAppProver.getData(); + let isProver = proverData !== undefined; + assert( + isProver || priorAccountUpdates !== undefined, + 'Called `setProofAuthorizationKind()` outside the prover without passing in `priorAccountUpdates`.' + ); + let myAccountUpdateId = isProver ? proverData.accountUpdate.id : id; + priorAccountUpdates ??= proverData.transaction.accountUpdates; + priorAccountUpdates = priorAccountUpdates.filter( + (a) => a.id !== myAccountUpdateId + ); + let priorAccountUpdatesFlat = CallForest.toFlatList( + priorAccountUpdates, + false + ); + let accountUpdate = [...priorAccountUpdatesFlat] + .reverse() + .find((body_) => + body_.update.verificationKey.isSome + .and(body_.tokenId.equals(body.tokenId)) + .and(body_.publicKey.equals(body.publicKey)) + .toBoolean() + ); + if (accountUpdate !== undefined) { + return accountUpdate.body.update.verificationKey.value.hash; + } + try { + let account = Mina.getAccount(body.publicKey, body.tokenId); + return account.zkapp?.verificationKey?.hash ?? Field(0); + } catch { + return Field(0); + } + }); + body.authorizationKind.verificationKeyHash = hash; + }, + setLazyProof( + accountUpdate: AccountUpdate, + proof: Omit, + priorAccountUpdates: AccountUpdate[] + ) { + this.setKind(accountUpdate, priorAccountUpdates); + accountUpdate.authorization = {}; + accountUpdate.lazyAuthorization = { ...proof, kind: 'lazy-proof' }; + }, +}; + /** * this is useful to debug a very common error: when the consistency check between * -) the account update that went into the public input, and From 9c74795394e01bfb38197b2347c10cb3210cfcc5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 10:46:06 +0100 Subject: [PATCH 330/524] merge fixups --- src/lib/account_update.ts | 4 ++-- src/lib/hash.ts | 4 ---- src/lib/mina.ts | 2 ++ src/lib/mina/mina-instance.ts | 36 +++++++++++++++++++++++++---------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 397db60f73..58e6d89dfe 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1783,7 +1783,7 @@ function addMissingSignatures( let signature = signFieldElement( fullCommitment, privateKey.toBigInt(), - Mina.getNetworkId() + activeInstance.getNetworkId() ); return { body, authorization: Signature.toBase58(signature) }; } @@ -1816,7 +1816,7 @@ function addMissingSignatures( let signature = signFieldElement( transactionCommitment, privateKey.toBigInt(), - Mina.getNetworkId() + activeInstance.getNetworkId() ); Authorization.setSignature(accountUpdate, Signature.toBase58(signature)); return accountUpdate as AccountUpdate & { lazyAuthorization: undefined }; diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 6f4dbc9347..d205b53f0f 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -118,10 +118,6 @@ const Poseidon = { return Poseidon.hash(packed); }, - initialState(): [Field, Field, Field] { - return [Field(0), Field(0), Field(0)]; - }, - Sponge, }; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 155dda1204..069a4d82ae 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -41,6 +41,8 @@ import { FeePayerSpec, DeprecatedFeePayerSpec, ActionStates, + defaultNetworkConstants, + NetworkConstants, } from './mina/mina-instance.js'; export { diff --git a/src/lib/mina/mina-instance.ts b/src/lib/mina/mina-instance.ts index a64f82ec01..dbaba81b33 100644 --- a/src/lib/mina/mina-instance.ts +++ b/src/lib/mina/mina-instance.ts @@ -8,18 +8,27 @@ import type { Transaction, TransactionId } from '../mina.js'; import type { Account } from './account.js'; import type { NetworkValue } from '../precondition.js'; import type * as Fetch from '../fetch.js'; +import type { NetworkId } from '../../mina-signer/src/TSTypes.js'; export { Mina, FeePayerSpec, DeprecatedFeePayerSpec, ActionStates, + NetworkConstants, + defaultNetworkConstants, activeInstance, setActiveInstance, ZkappStateLength, }; const defaultAccountCreationFee = 1_000_000_000; +const defaultNetworkConstants: NetworkConstants = { + genesisTimestamp: UInt64.from(0), + slotTime: UInt64.from(3 * 60 * 1000), + accountCreationFee: UInt64.from(defaultAccountCreationFee), +}; + const ZkappStateLength = 8; /** @@ -59,6 +68,15 @@ type ActionStates = { endActionState?: Field; }; +type NetworkConstants = { + genesisTimestamp: UInt64; + /** + * Duration of 1 slot in millisecondw + */ + slotTime: UInt64; + accountCreationFee: UInt64; +}; + interface Mina { transaction( sender: DeprecatedFeePayerSpec, @@ -68,14 +86,10 @@ interface Mina { hasAccount(publicKey: PublicKey, tokenId?: Field): boolean; getAccount(publicKey: PublicKey, tokenId?: Field): Account; getNetworkState(): NetworkValue; - getNetworkConstants(): { - genesisTimestamp: UInt64; - /** - * Duration of 1 slot in millisecondw - */ - slotTime: UInt64; - accountCreationFee: UInt64; - }; + getNetworkConstants(): NetworkConstants; + /** + * @deprecated use {@link getNetworkConstants} + */ accountCreationFee(): UInt64; sendTransaction(transaction: Transaction): Promise; fetchEvents: ( @@ -94,11 +108,12 @@ interface Mina { tokenId?: Field ) => { hash: string; actions: string[][] }[]; proofsEnabled: boolean; + getNetworkId(): NetworkId; } let activeInstance: Mina = { - accountCreationFee: () => UInt64.from(defaultAccountCreationFee), - getNetworkConstants: noActiveInstance, + accountCreationFee: () => defaultNetworkConstants.accountCreationFee, + getNetworkConstants: () => defaultNetworkConstants, currentSlot: noActiveInstance, hasAccount: noActiveInstance, getAccount: noActiveInstance, @@ -109,6 +124,7 @@ let activeInstance: Mina = { fetchActions: noActiveInstance, getActions: noActiveInstance, proofsEnabled: true, + getNetworkId: () => 'testnet', }; /** From b7c9fd96e6a3d4d50ce9826320933413edd5738e Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 17:39:02 +0100 Subject: [PATCH 331/524] build fixup --- src/lib/hash.ts | 4 ++++ src/lib/mina.ts | 7 ------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/lib/hash.ts b/src/lib/hash.ts index d205b53f0f..6f4dbc9347 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -118,6 +118,10 @@ const Poseidon = { return Poseidon.hash(packed); }, + initialState(): [Field, Field, Field] { + return [Field(0), Field(0), Field(0)]; + }, + Sponge, }; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 069a4d82ae..d49d1affe2 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -297,13 +297,6 @@ function newTransaction(transaction: ZkappCommand, proofsEnabled?: boolean) { return self; } -const defaultAccountCreationFee = 1_000_000_000; -const defaultNetworkConstants: NetworkConstants = { - genesisTimestamp: UInt64.from(0), - slotTime: UInt64.from(3 * 60 * 1000), - accountCreationFee: UInt64.from(defaultAccountCreationFee), -}; - /** * A mock Mina blockchain running locally and useful for testing. */ From 83e0b96ef7dfa74989ab458fa77149ae5b9a71f7 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 30 Jan 2024 16:04:02 +0100 Subject: [PATCH 332/524] fixup --- src/lib/mina.ts | 8 ++++++++ src/lib/zkapp.ts | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index d49d1affe2..95bf7835e4 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -79,6 +79,14 @@ export { type NetworkConstants, }; +// patch active instance so that we can still create basic transactions without giving Mina network details +setActiveInstance({ + ...activeInstance, + async transaction(sender: DeprecatedFeePayerSpec, f: () => void) { + return createTransaction(sender, f, 0); + }, +}); + interface TransactionId { isSuccess: boolean; wait(options?: { maxAttempts?: number; interval?: number }): Promise; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 85f172b3d6..2676b604d9 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -58,8 +58,8 @@ import { snarkContext, } from './provable-context.js'; import { Cache } from './proof-system/cache.js'; -import { SmartContractBase } from './mina/smart-contract-base.js'; import { assert } from './gadgets/common.js'; +import { SmartContractBase } from './mina/smart-contract-base.js'; // external API export { From b7188f4e5fe5a5871dea17bcbc22645be4cda8ba Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 1 Feb 2024 18:01:24 +0100 Subject: [PATCH 333/524] fix import cycle mina.ts <-> zkapp.ts --- src/lib/mina.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 95bf7835e4..2f847cea13 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -19,8 +19,7 @@ import { import * as Fetch from './fetch.js'; import { assertPreconditionInvariants, NetworkValue } from './precondition.js'; import { cloneCircuitValue, toConstant } from './circuit_value.js'; -import { Empty, Proof, verify } from './proof_system.js'; -import { SmartContract } from './zkapp.js'; +import { Empty, JsonProof, Proof, verify } from './proof_system.js'; import { invalidTransactionError } from './mina/errors.js'; import { Types, TypesBigint } from '../bindings/mina-transaction/types.js'; import { Account } from './mina/account.js'; @@ -38,11 +37,11 @@ import { activeInstance, setActiveInstance, Mina, - FeePayerSpec, - DeprecatedFeePayerSpec, - ActionStates, defaultNetworkConstants, - NetworkConstants, + type FeePayerSpec, + type DeprecatedFeePayerSpec, + type ActionStates, + type NetworkConstants, } from './mina/mina-instance.js'; export { @@ -1233,15 +1232,15 @@ async function verifyAccountUpdate( let publicInput = accountUpdate.toPublicInput(); let publicInputFields = ZkappPublicInput.toFields(publicInput); - const proof = SmartContract.Proof().fromJSON({ + let proof: JsonProof = { maxProofsVerified: 2, proof: accountUpdate.authorization.proof!, publicInput: publicInputFields.map((f) => f.toString()), publicOutput: [], - }); + }; let verificationKey = account.zkapp?.verificationKey?.data!; - isValidProof = await verify(proof.toJSON(), verificationKey); + isValidProof = await verify(proof, verificationKey); if (!isValidProof) { throw Error( `Invalid proof for account update\n${JSON.stringify(update)}` From bc6870099acd66ac82716e041feb5c8de4a2ddb1 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 08:23:53 +0100 Subject: [PATCH 334/524] normalize comment --- src/lib/account_update.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 810bdb21dd..3a7e7f343a 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -2008,17 +2008,16 @@ function dummySignature() { /** * The public input for zkApps consists of certain hashes of the proving - AccountUpdate (and its child accountUpdates) which is constructed during method - execution. - - For SmartContract proving, a method is run twice: First outside the proof, to - obtain the public input, and once in the prover, which takes the public input - as input. The current transaction is hashed again inside the prover, which - asserts that the result equals the input public input, as part of the snark - circuit. The block producer will also hash the transaction they receive and - pass it as a public input to the verifier. Thus, the transaction is fully - constrained by the proof - the proof couldn't be used to attest to a different - transaction. + * account update (and its child updates) which is constructed during method execution. + * + * For SmartContract proving, a method is run twice: First outside the proof, to + * obtain the public input, and once in the prover, which takes the public input + * as input. The current transaction is hashed again inside the prover, which + * asserts that the result equals the input public input, as part of the snark + * circuit. The block producer will also hash the transaction they receive and + * pass it as a public input to the verifier. Thus, the transaction is fully + * constrained by the proof - the proof couldn't be used to attest to a different + * transaction. */ type ZkappPublicInput = { accountUpdate: Field; From 97709a62cd9cfcc7641014192589376cf1ed89d5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 15:33:38 +0100 Subject: [PATCH 335/524] use erc20 example for dex with actions --- src/examples/zkapps/dex/dex-with-actions.ts | 3 ++- src/examples/zkapps/dex/erc20.ts | 8 ++++++-- src/examples/zkapps/dex/happy-path-with-actions.ts | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/examples/zkapps/dex/dex-with-actions.ts b/src/examples/zkapps/dex/dex-with-actions.ts index 0a93ce572a..add16ae08b 100644 --- a/src/examples/zkapps/dex/dex-with-actions.ts +++ b/src/examples/zkapps/dex/dex-with-actions.ts @@ -22,7 +22,8 @@ import { state, } from 'o1js'; -import { TokenContract, randomAccounts } from './dex.js'; +import { randomAccounts } from './dex.js'; +import { TrivialCoin as TokenContract } from './erc20.js'; export { Dex, DexTokenHolder, addresses, getTokenBalances, keys, tokenIds }; diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index 58d5f73c48..be7dd0d149 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -15,6 +15,8 @@ import { Struct, } from 'o1js'; +export { Erc20Like, TrivialCoin }; + /** * ERC-20-like token standard. * https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ @@ -68,7 +70,7 @@ class TrivialCoin extends TokenContract implements Erc20Like { // constant supply SUPPLY = UInt64.from(10n ** 18n); - deploy(args: DeployArgs) { + deploy(args?: DeployArgs) { super.deploy(args); this.account.tokenSymbol.set('TRIV'); @@ -124,7 +126,9 @@ class TrivialCoin extends TokenContract implements Erc20Like { Transfer: Struct({ from: PublicKey, to: PublicKey, value: UInt64 }), }; - // TODO: doesn't emit a Transfer event yet (need to make transfer() a separate method from approveBase) + // TODO: doesn't emit a Transfer event yet + // need to make transfer() a separate method from approveBase, which does the same as + // `transfer()` on the base contract, but also emits the event // implement Approvable API diff --git a/src/examples/zkapps/dex/happy-path-with-actions.ts b/src/examples/zkapps/dex/happy-path-with-actions.ts index 5aaebb49a3..47ca1078bf 100644 --- a/src/examples/zkapps/dex/happy-path-with-actions.ts +++ b/src/examples/zkapps/dex/happy-path-with-actions.ts @@ -9,7 +9,7 @@ import { keys, tokenIds, } from './dex-with-actions.js'; -import { TokenContract } from './dex.js'; +import { TrivialCoin as TokenContract } from './erc20.js'; let proofsEnabled = true; tic('Happy path with actions'); From b59392747d71c4e7d5f7acd514c473f4b99090bc Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 15:34:13 +0100 Subject: [PATCH 336/524] allow skipping dummies --- src/lib/account_update.ts | 46 ++++++++++++++++++++-------- src/lib/mina/token/token-contract.ts | 4 ++- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 810bdb21dd..b3ff390ebf 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1564,15 +1564,35 @@ class AccountUpdateForest extends MerkleList.create( AccountUpdateTree, merkleListHash ) { - static fromArray(updates: AccountUpdate[]): AccountUpdateForest { + static fromArray( + updates: AccountUpdate[], + { skipDummies = false } = {} + ): AccountUpdateForest { + if (skipDummies) return AccountUpdateForest.fromArraySkipDummies(updates); + let nodes = updates.map((update) => { let accountUpdate = HashedAccountUpdate.hash(update); let calls = AccountUpdateForest.fromArray(update.children.accountUpdates); return { accountUpdate, calls }; }); - return AccountUpdateForest.from(nodes); } + + private static fromArraySkipDummies( + updates: AccountUpdate[] + ): AccountUpdateForest { + let forest = AccountUpdateForest.empty(); + + for (let update of [...updates].reverse()) { + let accountUpdate = HashedAccountUpdate.hash(update); + let calls = AccountUpdateForest.fromArraySkipDummies( + update.children.accountUpdates + ); + forest.pushIf(update.isDummy().not(), { accountUpdate, calls }); + } + + return forest; + } } // how to hash a forest @@ -1580,6 +1600,7 @@ class AccountUpdateForest extends MerkleList.create( function merkleListHash(forestHash: Field, tree: AccountUpdateTree) { return hashCons(forestHash, hashNode(tree)); } + function hashNode(tree: AccountUpdateTree) { return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ tree.accountUpdate.hash, @@ -2008,17 +2029,16 @@ function dummySignature() { /** * The public input for zkApps consists of certain hashes of the proving - AccountUpdate (and its child accountUpdates) which is constructed during method - execution. - - For SmartContract proving, a method is run twice: First outside the proof, to - obtain the public input, and once in the prover, which takes the public input - as input. The current transaction is hashed again inside the prover, which - asserts that the result equals the input public input, as part of the snark - circuit. The block producer will also hash the transaction they receive and - pass it as a public input to the verifier. Thus, the transaction is fully - constrained by the proof - the proof couldn't be used to attest to a different - transaction. + * account update (and its child updates) which is constructed during method execution. + * + * For SmartContract proving, a method is run twice: First outside the proof, to + * obtain the public input, and once in the prover, which takes the public input + * as input. The current transaction is hashed again inside the prover, which + * asserts that the result equals the input public input, as part of the snark + * circuit. The block producer will also hash the transaction they receive and + * pass it as a public input to the verifier. Thus, the transaction is fully + * constrained by the proof - the proof couldn't be used to attest to a different + * transaction. */ type ZkappPublicInput = { accountUpdate: Field; diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 7f9350a4f0..8896008b66 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -160,7 +160,9 @@ function finalizeAccountUpdate(update: AccountUpdate): AccountUpdateTree { calls = CallForestUnderConstruction.finalize(node.calls); } } - calls ??= AccountUpdateForest.fromArray(update.children.accountUpdates); + calls ??= AccountUpdateForest.fromArray(update.children.accountUpdates, { + skipDummies: true, + }); AccountUpdate.unlink(update); return { accountUpdate: HashedAccountUpdate.hash(update), calls }; From 4a9c05c15561a36394171fd2b0dc529eea15d489 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 16:28:45 +0100 Subject: [PATCH 337/524] support skipping dummies --- src/lib/account_update.ts | 85 ++++++++++++++++++++++----------------- src/lib/zkapp.ts | 2 +- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index b3ff390ebf..9eac649019 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -798,10 +798,7 @@ class AccountUpdate implements Types.AccountUpdate { // TODO: this is not as general as approve suggests let insideContract = smartContractContext.get(); if (insideContract && insideContract.selfUpdate.id === this.id) { - CallForestUnderConstruction.push(insideContract.selfCalls, { - useHash: false, - value: childUpdate, - }); + CallForestUnderConstruction.push(insideContract.selfCalls, childUpdate); } } @@ -814,10 +811,7 @@ class AccountUpdate implements Types.AccountUpdate { // TODO: this is not as general as adopt suggests let insideContract = smartContractContext.get(); if (insideContract && insideContract.selfUpdate.id === this.id) { - CallForestUnderConstruction.push(insideContract.selfCalls, { - useHash: false, - value: childUpdate, - }); + CallForestUnderConstruction.push(insideContract.selfCalls, childUpdate); } } @@ -1621,12 +1615,13 @@ function hashCons(forestHash: Field, nodeHash: Field) { * everything immediately. Instead, we maintain a structure consisting of either hashes or full account * updates that can be hashed into a final call forest at the end. */ -type CallForestUnderConstruction = HashOrValue< - { - accountUpdate: HashOrValue; - calls: CallForestUnderConstruction; - }[] ->; +type CallForestUnderConstruction = HashOrValue; + +type AccountUpdateNode = { + accountUpdate: HashOrValue; + isDummy: Bool; + calls: CallForestUnderConstruction; +}; type HashOrValue = | { useHash: true; hash: Field; value: T } @@ -1643,7 +1638,7 @@ const CallForestUnderConstruction = { witnessHash(forest: CallForestUnderConstruction) { let hash = Provable.witness(Field, () => { - let nodes = forest.value.map(toCallTree); + let nodes = forest.value.map(toTree); return withHashes(nodes, merkleListHash).hash; }); CallForestUnderConstruction.setHash(forest, hash); @@ -1653,28 +1648,35 @@ const CallForestUnderConstruction = { updates: AccountUpdate[], useHash = false ): CallForestUnderConstruction { - let nodes = updates.map((update) => { - let accountUpdate: HashOrValue = { - useHash: false, - value: update, + if (useHash) { + let forest = CallForestUnderConstruction.empty(); + Provable.asProver(() => { + forest = CallForestUnderConstruction.fromAccountUpdates(updates); + }); + CallForestUnderConstruction.witnessHash(forest); + return forest; + } + + let nodes = updates.map((update): AccountUpdateNode => { + return { + accountUpdate: { useHash: false, value: update }, + isDummy: update.isDummy(), + calls: CallForestUnderConstruction.fromAccountUpdates( + update.children.accountUpdates + ), }; - let calls = CallForestUnderConstruction.fromAccountUpdates( - update.children.accountUpdates - ); - return { accountUpdate, calls }; }); - let forest: CallForestUnderConstruction = { useHash: false, value: nodes }; - if (useHash) CallForestUnderConstruction.witnessHash(forest); - return forest; + return { useHash: false, value: nodes }; }, push( forest: CallForestUnderConstruction, - accountUpdate: HashOrValue, + accountUpdate: AccountUpdate, calls?: CallForestUnderConstruction ) { forest.value.push({ - accountUpdate, + accountUpdate: { useHash: false, value: accountUpdate }, + isDummy: accountUpdate.isDummy(), calls: calls ?? CallForestUnderConstruction.empty(), }); }, @@ -1694,23 +1696,29 @@ const CallForestUnderConstruction = { finalize(forest: CallForestUnderConstruction): AccountUpdateForest { if (forest.useHash) { - let data = Unconstrained.witness(() => { - let nodes = forest.value.map(toCallTree); - return withHashes(nodes, merkleListHash).data; - }); + let data = Unconstrained.witness(() => + CallForestUnderConstruction.finalize({ + ...forest, + useHash: false, + }).data.get() + ); return new AccountUpdateForest({ hash: forest.hash, data }); } // not using the hash means we calculate it in-circuit - let nodes = forest.value.map(toCallTree); - return AccountUpdateForest.from(nodes); + let nodes = forest.value.map(toTree); + let finalForest = AccountUpdateForest.empty(); + + for (let { isDummy, ...tree } of [...nodes].reverse()) { + finalForest.pushIf(isDummy.not(), tree); + } + return finalForest; }, }; -function toCallTree(node: { - accountUpdate: HashOrValue; - calls: CallForestUnderConstruction; -}): AccountUpdateTree { +function toTree( + node: AccountUpdateNode +): AccountUpdateTree & { isDummy: Bool } { let accountUpdate = node.accountUpdate.useHash ? new HashedAccountUpdate( node.accountUpdate.hash, @@ -1720,6 +1728,7 @@ function toCallTree(node: { return { accountUpdate, + isDummy: node.isDummy, calls: CallForestUnderConstruction.finalize(node.calls), }; } diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 821913ffca..d5e993e32e 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -417,7 +417,7 @@ function wrapMethod( parentAccountUpdate.children.accountUpdates.push(accountUpdate); CallForestUnderConstruction.push( insideContract.selfCalls, - { useHash: false, value: accountUpdate }, + accountUpdate, CallForestUnderConstruction.fromAccountUpdates( accountUpdate.children.accountUpdates, true From e6b2a4a1e9db6d9086f7c51dddcd9957c38bfe63 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 16:45:58 +0100 Subject: [PATCH 338/524] improve name and clean up code --- src/lib/account_update.ts | 82 ++++++++++------------------ src/lib/mina/token/token-contract.ts | 4 +- src/lib/zkapp.ts | 13 ++--- 3 files changed, 36 insertions(+), 63 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 9eac649019..19b8d5e4da 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -85,7 +85,7 @@ export { dummySignature, LazyProof, AccountUpdateTree, - CallForestUnderConstruction, + UnfinishedForest, hashAccountUpdate, HashedAccountUpdate, }; @@ -96,7 +96,7 @@ type SmartContractContext = { this: SmartContract; methodCallDepth: number; selfUpdate: AccountUpdate; - selfCalls: CallForestUnderConstruction; + selfCalls: UnfinishedForest; }; let smartContractContext = Context.create({ default: null, @@ -798,7 +798,7 @@ class AccountUpdate implements Types.AccountUpdate { // TODO: this is not as general as approve suggests let insideContract = smartContractContext.get(); if (insideContract && insideContract.selfUpdate.id === this.id) { - CallForestUnderConstruction.push(insideContract.selfCalls, childUpdate); + UnfinishedForest.push(insideContract.selfCalls, childUpdate); } } @@ -811,7 +811,7 @@ class AccountUpdate implements Types.AccountUpdate { // TODO: this is not as general as adopt suggests let insideContract = smartContractContext.get(); if (insideContract && insideContract.selfUpdate.id === this.id) { - CallForestUnderConstruction.push(insideContract.selfCalls, childUpdate); + UnfinishedForest.push(insideContract.selfCalls, childUpdate); } } @@ -1142,10 +1142,7 @@ class AccountUpdate implements Types.AccountUpdate { // TODO duplicate logic let insideContract = smartContractContext.get(); if (insideContract) { - CallForestUnderConstruction.remove( - insideContract.selfCalls, - accountUpdate - ); + UnfinishedForest.remove(insideContract.selfCalls, accountUpdate); } let siblings = accountUpdate.parent?.children.accountUpdates ?? @@ -1609,79 +1606,66 @@ function hashCons(forestHash: Field, nodeHash: Field) { } /** - * Structure for constructing a call forest from a circuit. + * Structure for constructing the forest of child account updates, from a circuit. * * The circuit can mutate account updates and change their array of children, so here we can't hash * everything immediately. Instead, we maintain a structure consisting of either hashes or full account * updates that can be hashed into a final call forest at the end. */ -type CallForestUnderConstruction = HashOrValue; +type UnfinishedForest = HashOrValue; -type AccountUpdateNode = { +type UnfinishedTree = { accountUpdate: HashOrValue; isDummy: Bool; - calls: CallForestUnderConstruction; + calls: UnfinishedForest; }; type HashOrValue = | { useHash: true; hash: Field; value: T } | { useHash: false; value: T }; -const CallForestUnderConstruction = { - empty(): CallForestUnderConstruction { +const UnfinishedForest = { + empty(): UnfinishedForest { return { useHash: false, value: [] }; }, - setHash(forest: CallForestUnderConstruction, hash: Field) { - Object.assign(forest, { useHash: true, hash }); - }, - - witnessHash(forest: CallForestUnderConstruction) { + witnessHash(forest: UnfinishedForest) { let hash = Provable.witness(Field, () => { - let nodes = forest.value.map(toTree); - return withHashes(nodes, merkleListHash).hash; + return UnfinishedForest.finalize(forest).hash; }); - CallForestUnderConstruction.setHash(forest, hash); + return { useHash: true, hash, value: forest.value }; }, - fromAccountUpdates( - updates: AccountUpdate[], - useHash = false - ): CallForestUnderConstruction { + fromArray(updates: AccountUpdate[], useHash = false): UnfinishedForest { if (useHash) { - let forest = CallForestUnderConstruction.empty(); - Provable.asProver(() => { - forest = CallForestUnderConstruction.fromAccountUpdates(updates); - }); - CallForestUnderConstruction.witnessHash(forest); - return forest; + let forest = UnfinishedForest.empty(); + Provable.asProver(() => (forest = UnfinishedForest.fromArray(updates))); + return UnfinishedForest.witnessHash(forest); } - let nodes = updates.map((update): AccountUpdateNode => { + let nodes = updates.map((update): UnfinishedTree => { return { accountUpdate: { useHash: false, value: update }, isDummy: update.isDummy(), - calls: CallForestUnderConstruction.fromAccountUpdates( - update.children.accountUpdates - ), + calls: UnfinishedForest.fromArray(update.children.accountUpdates), }; }); return { useHash: false, value: nodes }; }, push( - forest: CallForestUnderConstruction, + forest: UnfinishedForest, accountUpdate: AccountUpdate, - calls?: CallForestUnderConstruction + calls?: UnfinishedForest ) { forest.value.push({ accountUpdate: { useHash: false, value: accountUpdate }, isDummy: accountUpdate.isDummy(), - calls: calls ?? CallForestUnderConstruction.empty(), + calls: calls ?? UnfinishedForest.empty(), }); }, - remove(forest: CallForestUnderConstruction, accountUpdate: AccountUpdate) { + remove(forest: UnfinishedForest, accountUpdate: AccountUpdate) { // find account update by .id let index = forest.value.findIndex( (node) => node.accountUpdate.value.id === accountUpdate.id @@ -1694,13 +1678,10 @@ const CallForestUnderConstruction = { forest.value.splice(index, 1); }, - finalize(forest: CallForestUnderConstruction): AccountUpdateForest { + finalize(forest: UnfinishedForest): AccountUpdateForest { if (forest.useHash) { let data = Unconstrained.witness(() => - CallForestUnderConstruction.finalize({ - ...forest, - useHash: false, - }).data.get() + UnfinishedForest.finalize({ ...forest, useHash: false }).data.get() ); return new AccountUpdateForest({ hash: forest.hash, data }); } @@ -1716,9 +1697,7 @@ const CallForestUnderConstruction = { }, }; -function toTree( - node: AccountUpdateNode -): AccountUpdateTree & { isDummy: Bool } { +function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { let accountUpdate = node.accountUpdate.useHash ? new HashedAccountUpdate( node.accountUpdate.hash, @@ -1726,11 +1705,8 @@ function toTree( ) : HashedAccountUpdate.hash(node.accountUpdate.value); - return { - accountUpdate, - isDummy: node.isDummy, - calls: CallForestUnderConstruction.finalize(node.calls), - }; + let calls = UnfinishedForest.finalize(node.calls); + return { accountUpdate, isDummy: node.isDummy, calls }; } const CallForest = { diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 8896008b66..765fd184f9 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -5,7 +5,7 @@ import { PublicKey } from '../../signature.js'; import { AccountUpdate, AccountUpdateForest, - CallForestUnderConstruction, + UnfinishedForest, AccountUpdateTree, HashedAccountUpdate, Permissions, @@ -157,7 +157,7 @@ function finalizeAccountUpdate(update: AccountUpdate): AccountUpdateTree { (c) => c.accountUpdate.value.id === update.id ); if (node !== undefined) { - calls = CallForestUnderConstruction.finalize(node.calls); + calls = UnfinishedForest.finalize(node.calls); } } calls ??= AccountUpdateForest.fromArray(update.children.accountUpdates, { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index d5e993e32e..466076c7a0 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -18,7 +18,7 @@ import { SmartContractContext, LazyProof, CallForest, - CallForestUnderConstruction, + UnfinishedForest, } from './account_update.js'; import { cloneCircuitValue, @@ -169,7 +169,7 @@ function wrapMethod( this: this, methodCallDepth: 0, selfUpdate: selfAccountUpdate(this, methodName), - selfCalls: CallForestUnderConstruction.empty(), + selfCalls: UnfinishedForest.empty(), }; let id = smartContractContext.enter(context); try { @@ -313,7 +313,7 @@ function wrapMethod( this: this, methodCallDepth: methodCallDepth + 1, selfUpdate: selfAccountUpdate(this, methodName), - selfCalls: CallForestUnderConstruction.empty(), + selfCalls: UnfinishedForest.empty(), }; let id = smartContractContext.enter(innerContext); try { @@ -415,13 +415,10 @@ function wrapMethod( // nothing is asserted about them -- it's the callee's task to check their children accountUpdate.children.callsType = { type: 'Witness' }; parentAccountUpdate.children.accountUpdates.push(accountUpdate); - CallForestUnderConstruction.push( + UnfinishedForest.push( insideContract.selfCalls, accountUpdate, - CallForestUnderConstruction.fromAccountUpdates( - accountUpdate.children.accountUpdates, - true - ) + UnfinishedForest.fromArray(accountUpdate.children.accountUpdates, true) ); // assert that we really called the right zkapp From b582e70d45a02a710a1cf9e1a94577f5cb4aaf12 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 16:52:59 +0100 Subject: [PATCH 339/524] minor --- src/lib/account_update.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 19b8d5e4da..e6b800fcdf 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1621,8 +1621,8 @@ type UnfinishedTree = { }; type HashOrValue = - | { useHash: true; hash: Field; value: T } - | { useHash: false; value: T }; + | { readonly useHash: true; hash: Field; readonly value: T } + | { readonly useHash: false; value: T }; const UnfinishedForest = { empty(): UnfinishedForest { From 36dc9a3d14edb1d8f8479d2531e529e318d7ac0e Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 16:55:26 +0100 Subject: [PATCH 340/524] dump vks --- tests/vk-regression/vk-regression.json | 50 +++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index d2c7e2d099..f0459aa452 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -1,22 +1,22 @@ { "Voting_": { - "digest": "22daa074b7870490b90b23421015e6d08e682f4ed5e622a9db2504dee5cd5671", + "digest": "5796cb30bf420c4052ee0ca04d6a7eb554ac0806183ee97c92c51bc1cc2976e", "methods": { "voterRegistration": { - "rows": 1258, - "digest": "ae840e7007243291e18a1bd5216a49ed" + "rows": 1261, + "digest": "8b6b8083bb59f6dd4746b5b3da3e10e3" }, "candidateRegistration": { - "rows": 1258, - "digest": "e4f3b101d10ead3ce9cb2ccb37f6bc11" + "rows": 1261, + "digest": "73947a300094dcebedff84c86887a06d" }, "approveRegistrations": { - "rows": 1146, - "digest": "fe826d28b553da5307c15d28d0fd632d" + "rows": 1149, + "digest": "8fa35ef942084cc163bbd27bdaccb468" }, "vote": { - "rows": 1672, - "digest": "cfbc84a918636da1ca6afc95d02e014e" + "rows": 1675, + "digest": "9c607a19228a5913cd32278394d65f10" }, "countVotes": { "rows": 5796, @@ -24,16 +24,16 @@ } }, "verificationKey": { - "data": "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", - "hash": "1875936070919967236715828287620658152293796457000976946864180886256059059334" + "data": "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", + "hash": "18930773283376165145392075130641581795923440685773146685646791501279243480469" } }, "Membership_": { - "digest": "255745fb9365ff4f970b96ed630c01c9c8f63e21744f83fbe833396731d096e2", + "digest": "262bb745a13f7fac8f6ad1dd8ff13ae5789e0c26b63f4b41a5dbc43c6ac9df85", "methods": { "addEntry": { - "rows": 1353, - "digest": "fa32e32384aef8ce6a7d019142149d95" + "rows": 1355, + "digest": "9bf99a1ea1fa39ed9590c0b0a67bfeb7" }, "isMember": { "rows": 469, @@ -45,8 +45,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "16610506589527352533348678289715227768202510979537802187565243095524972136674" + "data": "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", + "hash": "16174092625812619544041752898938261603039773654468059859161573381120826639333" } }, "HelloWorld": { @@ -84,19 +84,19 @@ } }, "Dex": { - "digest": "6c2cdcc82896f8438ccae4490b4c2b47cc3ba430a9cdf53018eead6ba782950", + "digest": "2289275173dc37af7005d29de2d1bc87ef6633a38aab74653528d62a3ea2c53b", "methods": { "supplyLiquidityBase": { - "rows": 2877, - "digest": "f7d4b4e34113b33d8543a74dfc3110dc" + "rows": 2883, + "digest": "a5811e58fa24f5ebde41076fc94849b7" }, "swapX": { - "rows": 1561, - "digest": "563d256fe4739dda2a992f8984fddd06" + "rows": 1564, + "digest": "f6b047df744d8dcf297bd7a4fb45c812" }, "swapY": { - "rows": 1561, - "digest": "e2b1374e5ab54c3de6c5f31b9e4cd9b9" + "rows": 1564, + "digest": "3082a30634a7a8da1603a7994b0cd4af" }, "burnLiquidity": { "rows": 718, @@ -108,8 +108,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "23362525313143196140641665177215350862002766202717183338959891568504853279430" + "data": "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", + "hash": "10792977795272926498471289159457543677416779853262843765284042897388782617920" } }, "Group Primitive": { From 2c52e8bef9c3d7726b90c44f5c667e0752d0e299 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 17:11:25 +0100 Subject: [PATCH 341/524] switch over how we hash calls in the circuit --- src/lib/zkapp.ts | 16 +++--- tests/vk-regression/vk-regression.json | 72 +++++++++++++------------- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 466076c7a0..f91731ce53 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -19,6 +19,7 @@ import { LazyProof, CallForest, UnfinishedForest, + AccountUpdateForest, } from './account_update.js'; import { cloneCircuitValue, @@ -194,11 +195,11 @@ function wrapMethod( let blindingValue = Provable.witness(Field, getBlindingValue); // it's also good if we prove that we use the same blinding value across the method // that's why we pass the variable (not the constant) into a new context - let context = memoizationContext() ?? { + let memoCtx = memoizationContext() ?? { memoized: [], currentIndex: 0, }; - let id = memoizationContext.enter({ ...context, blindingValue }); + let id = memoizationContext.enter({ ...memoCtx, blindingValue }); let result: unknown; try { let clonedArgs = actualArgs.map(cloneCircuitValue); @@ -218,7 +219,8 @@ function wrapMethod( ProofAuthorization.setKind(accountUpdate); debugPublicInput(accountUpdate); - checkPublicInput(publicInput, accountUpdate); + let calls = UnfinishedForest.finalize(context.selfCalls); + checkPublicInput(publicInput, accountUpdate, calls); // check the self accountUpdate right after calling the method // TODO: this needs to be done in a unified way for all account updates that are created @@ -448,11 +450,11 @@ function wrapMethod( function checkPublicInput( { accountUpdate, calls }: ZkappPublicInput, - self: AccountUpdate + self: AccountUpdate, + selfCalls: AccountUpdateForest ) { - let otherInput = self.toPublicInput(); - accountUpdate.assertEquals(otherInput.accountUpdate); - calls.assertEquals(otherInput.calls); + accountUpdate.assertEquals(self.hash()); + calls.assertEquals(selfCalls.hash); } /** diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index f0459aa452..01cde4af40 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -1,22 +1,22 @@ { "Voting_": { - "digest": "5796cb30bf420c4052ee0ca04d6a7eb554ac0806183ee97c92c51bc1cc2976e", + "digest": "385ea3e72a679aad6a48b0734008d80da05f27a17854b9088edc8de420ad907d", "methods": { "voterRegistration": { - "rows": 1261, - "digest": "8b6b8083bb59f6dd4746b5b3da3e10e3" + "rows": 1259, + "digest": "a59bad21fc55ce7d8d099d24cb91d1e6" }, "candidateRegistration": { - "rows": 1261, - "digest": "73947a300094dcebedff84c86887a06d" + "rows": 1259, + "digest": "ee209f54f9f7996bae2bf5c0fd4bf9da" }, "approveRegistrations": { - "rows": 1149, - "digest": "8fa35ef942084cc163bbd27bdaccb468" + "rows": 1147, + "digest": "748673762a27be8b58ef15084bd49beb" }, "vote": { - "rows": 1675, - "digest": "9c607a19228a5913cd32278394d65f10" + "rows": 1672, + "digest": "0862eef1a5edb8ade4d1d5dab1f76abc" }, "countVotes": { "rows": 5796, @@ -24,16 +24,16 @@ } }, "verificationKey": { - "data": "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", - "hash": "18930773283376165145392075130641581795923440685773146685646791501279243480469" + "data": "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", + "hash": "10242427736291135326630618424974868967569697518211103043324256389485118856981" } }, "Membership_": { - "digest": "262bb745a13f7fac8f6ad1dd8ff13ae5789e0c26b63f4b41a5dbc43c6ac9df85", + "digest": "3b4598fa0324c27a3535ee0f187419dcd43cde203ac1053df4bcc5a108fd0c52", "methods": { "addEntry": { - "rows": 1355, - "digest": "9bf99a1ea1fa39ed9590c0b0a67bfeb7" + "rows": 1353, + "digest": "657f6ba31355b4d6daa80b84e86a5341" }, "isMember": { "rows": 469, @@ -45,8 +45,8 @@ } }, "verificationKey": { - "data": "AACwuS3vTWCwpRIX/QlJQqJcmPO9nPm4+sCfcrqiY1NUMiV9k6Pc8kFkMsbGLst78T8uAnYwc1Ql49kq0I2GizwshS9xkBcfxRTAAMBHXhf8KDkK39AalVocKIrfWMV0MSShinj0bCxPCc10K0cya4Voy8fud4+hktDOuwjaAstpEJSbKRHMIki77xHmJWlFUYdkgPg30MU4Ta3ev/h+mcMWmofyhLSQqUbaV6hM95n3Y0Wcn2LRNxJP8TRwHndIcylleqPsGMh3P+A+N9c32N4kl29nreMJJdcUrCXK90GLPAFOB9mHIjKk9+9o3eZc3cGQ+jppXoN3zwO91DeT/GYvXqCZTAudLxIwuJU11UBThG5CKKABa9ulQ1bYGXj9Eydy0vPxfojDeFrnKMi9GKSjiSMzmOLbIw7Dt+g9ggjsHM5rPrT7dY1VV4ZT9shjlcX3029xnk3Bjz4Q9PiK+A8o6f7L6aVB07I+QY2iDtwSQWuXYPohrk85I1UbPfY+giWqFXBtHaN45PMWCyBx0TKaozETCmv0kA5KGTzesYQCECPQ8F2DM+oXz8xly+z9/Ypt/Zx9NvF7wute/1s6Q/QuAHHgQqvSF2AEzSEy6kDop6fnFtVTxzp0MgW0M9X0uVcRTRJTkcVZSz1JzihGEjzkEZnZW6tVr6CEkmzXh/t3DSq2vXswFt90jphf6jgLtFJULrvKVg+YCMNM/04QLTGcMmjjzv4LciQ6IVXth7zhVKxfL1/2peC0r/ZrP8k+Ox4LEBXWMCQE5kfK476bQgrLeKJfQ45PZfgB688DGwaYAxWbcxBV822/aAsA55ijFY1Xf7S+DiytY4a/u0bellKMDUQqTOq9VwmbDv868zXscUwKpNVR3wy2En/q9M/HJJc4BZyuuQvlQSR59m0gL4hKHf5Dci/YVvM6ACHmg+5SxCr1pUNKbyy2lsIa5Ma40ZmsTpT4/lQczmGENQSQXA9bFibT0Q+Vj885p9heLOCCXyAujC4DhAdYmT1MQ7v4IxcktsWwr3mRVBRM4iPa87OEKZOxq0wWPrGcTnmqV/ihFAcp38VS2KUNwsiWjprCq1MFDCf1dT4c1U6/mdLP6AI/AN9kYvvR877q6oMqJpDqVYREnzfYQrrfTeHLoHt8YFYuKuDpScyj0wx13bSNn9KyRYZfWPYXhdOsDeUzZ0bPMQj6l24QrqQAp0ebGEbpXqv21bhlr6dYBsculE2VU9SuGJ2g6yuuKf4+lfJ2V5TkIxFvlgw5cxTXNQ010JYug38++ZDV+MibXPzg+cODE5wfZ3jon5wVNkAiG642DzXzNj67x80zBWLdt3UKnFZs9dpa1fYpTjlJg8T+dnJJiKf2IvmvF8xyi1HAwAFyhDL2dn/w/pDE2Kl9QdpZpQYDEBQgCCkegsZszQ+2mjxU9pLXzz5GSoqz8jABW5Qo3abBAhvYKKaAs6NoRgeAD6SadFDbQmXaftE+Y1MVOtjnaZDUBdwahWiJMlkfZpxW1aubEc/GSX8WzCZ8h9HeakcRc7kcN0CR8kmfER3eiZ2JMbt5cQl/afNjwGGAmeXzTaR34AgFjiw/RlZJkhYm9jyf18M8yP94QGBMxd6Y6wrNvOmJHzEnp8aitJsDlZklm8LKbjumlSbLcbBokpIDhFBBKfwP2qsQX7eHLCZ/3mztoFKoIiYXgrHWG8m2SzIJ/ljn6Rg7AxIsPjzZyEw1eXAOC7A1FCT/757ygMsnk+rLlpDTBYLmhJtQdt61MQFDi5BuCmQ/PY9C/74/k4APl5htiNcCZty/1JElFwjuCQFjvAiMPUMyqp7/ALFapsTZqhSs1g6jd8uhuJoTNEqLDvKUUbs0kMvGy8BOG0YXNxmNccabGwBzxmijv6LF/Xinecl4aD8FCh6opY98TJnOHd3XSYL1DbLqmmc6CXEM+g5iDGnXr/CkI2Jy37OkF8X03jz4AH0Yj0+J63yH4IS+PrNpKZEXKh7PvXNaLGGKsFcKEi63/xKPKH0G4RzvFKbkp+IWqtIYjMiwIJMwzmfS1NLLXqqpFiD364eFcXINR2rrDKcoTUp1JkVZVfXfKwaRUPWSGFYIYMtwPh2w8ZfubAmXZFpyzstORhFyg9rtVAAy0lcDhQwWVlhFFkR2qbdoy0EFLBrfKqUIkd1N6vDQQYL1RGaTAv/ybregrJsFo+VP3ZatlR6LnKYWp1m7vPkGm3I6Pus/mvp1k10QGk8nhFuR31DjsG3lzZ4gXSs1oSv0qbxD2S6g5+Y6cPbITEGX3uQjsunXnQ9PHd22Mk+fqbDakTiCJh6aFqqPNShiAXkGSuC1oXJHX3zqnbn75dWO0UVhBNAbjYkSnQeyka1wnZb12sR+PlRMvWQVcd93t5L/FiE0ORo=", - "hash": "16174092625812619544041752898938261603039773654468059859161573381120826639333" + "data": "AACwuS3vTWCwpRIX/QlJQqJcmPO9nPm4+sCfcrqiY1NUMiV9k6Pc8kFkMsbGLst78T8uAnYwc1Ql49kq0I2GizwshS9xkBcfxRTAAMBHXhf8KDkK39AalVocKIrfWMV0MSShinj0bCxPCc10K0cya4Voy8fud4+hktDOuwjaAstpEJSbKRHMIki77xHmJWlFUYdkgPg30MU4Ta3ev/h+mcMWmofyhLSQqUbaV6hM95n3Y0Wcn2LRNxJP8TRwHndIcylleqPsGMh3P+A+N9c32N4kl29nreMJJdcUrCXK90GLPAFOB9mHIjKk9+9o3eZc3cGQ+jppXoN3zwO91DeT/GYvXqCZTAudLxIwuJU11UBThG5CKKABa9ulQ1bYGXj9Eydy0vPxfojDeFrnKMi9GKSjiSMzmOLbIw7Dt+g9ggjsHM5rPrT7dY1VV4ZT9shjlcX3029xnk3Bjz4Q9PiK+A8o6f7L6aVB07I+QY2iDtwSQWuXYPohrk85I1UbPfY+giWqFXBtHaN45PMWCyBx0TKaozETCmv0kA5KGTzesYQCECPQ8F2DM+oXz8xly+z9/Ypt/Zx9NvF7wute/1s6Q/QuAHHgQqvSF2AEzSEy6kDop6fnFtVTxzp0MgW0M9X0uVcRTRJTkcVZSz1JzihGEjzkEZnZW6tVr6CEkmzXh/t3DSq2vXswFt90jphf6jgLtFJULrvKVg+YCMNM/04QLTGcMmjjzv4LciQ6IVXth7zhVKxfL1/2peC0r/ZrP8k+Ox4LEBXWMCQE5kfK476bQgrLeKJfQ45PZfgB688DGwaYAxWbcxBV822/aAsA55ijFY1Xf7S+DiytY4a/u0bellKMDUQqTOq9VwmbDv868zXscUwKpNVR3wy2En/q9M/HJJc4BZyuuQvlQSR59m0gL4hKHf5Dci/YVvM6ACHmg+5SxCr1pUNKbyy2lsIa5Ma40ZmsTpT4/lQczmGENQSQXA9bFibT0Q+Vj885p9heLOCCXyAujC4DhAdYmT1MQ7v4IxcktsWwr3mRVBRM4iPa87OEKZOxq0wWPrGcTnmqV/ihFAcp38VS2KUNwsiWjprCq1MFDCf1dT4c1U6/mdLP6AI/AOnOLSedUi3koHnIpmgavJ2yK8bDbXiztr9N8OkeuOQhdo/j83qKYtClSs06ygjSvnlaa4mJuIpiKGYNjYx/KRP6l24QrqQAp0ebGEbpXqv21bhlr6dYBsculE2VU9SuGJ2g6yuuKf4+lfJ2V5TkIxFvlgw5cxTXNQ010JYug38++ZDV+MibXPzg+cODE5wfZ3jon5wVNkAiG642DzXzNj67x80zBWLdt3UKnFZs9dpa1fYpTjlJg8T+dnJJiKf2IvmvF8xyi1HAwAFyhDL2dn/w/pDE2Kl9QdpZpQYDEBQgCCkegsZszQ+2mjxU9pLXzz5GSoqz8jABW5Qo3abBAhvYKKaAs6NoRgeAD6SadFDbQmXaftE+Y1MVOtjnaZDUBdwahWiJMlkfZpxW1aubEc/GSX8WzCZ8h9HeakcRc7kcN0CR8kmfER3eiZ2JMbt5cQl/afNjwGGAmeXzTaR34AgFjiw/RlZJkhYm9jyf18M8yP94QGBMxd6Y6wrNvOmJHzEnp8aitJsDlZklm8LKbjumlSbLcbBokpIDhFBBKfwP2qsQX7eHLCZ/3mztoFKoIiYXgrHWG8m2SzIJ/ljn6Rg7AxIsPjzZyEw1eXAOC7A1FCT/757ygMsnk+rLlpDTBYLmhJtQdt61MQFDi5BuCmQ/PY9C/74/k4APl5htiNcCZty/1JElFwjuCQFjvAiMPUMyqp7/ALFapsTZqhSs1g6jd8uhuJoTNEqLDvKUUbs0kMvGy8BOG0YXNxmNccabGwBzxmijv6LF/Xinecl4aD8FCh6opY98TJnOHd3XSYL1DbLqmmc6CXEM+g5iDGnXr/CkI2Jy37OkF8X03jz4AH0Yj0+J63yH4IS+PrNpKZEXKh7PvXNaLGGKsFcKEi63/xKPKH0G4RzvFKbkp+IWqtIYjMiwIJMwzmfS1NLLXqqpFiD364eFcXINR2rrDKcoTUp1JkVZVfXfKwaRUPWSGFYIYMtwPh2w8ZfubAmXZFpyzstORhFyg9rtVAAy0lcDhQwWVlhFFkR2qbdoy0EFLBrfKqUIkd1N6vDQQYL1RGaTAv/ybregrJsFo+VP3ZatlR6LnKYWp1m7vPkGm3I6Pus/mvp1k10QGk8nhFuR31DjsG3lzZ4gXSs1oSv0qbxD2S6g5+Y6cPbITEGX3uQjsunXnQ9PHd22Mk+fqbDakTiCJh6aFqqPNShiAXkGSuC1oXJHX3zqnbn75dWO0UVhBNAbjYkSnQeyka1wnZb12sR+PlRMvWQVcd93t5L/FiE0ORo=", + "hash": "27077061932938200348860230517448044253628907171665029926735544979854107063304" } }, "HelloWorld": { @@ -63,15 +63,15 @@ } }, "TokenContract": { - "digest": "19aa0f5b442d011f2bbdd68ac189bf91cd017803f0f91a5ab6a61d50e3136c2", + "digest": "1b4cc9f8af1e49b7f727bde338ff93b3aa72d1be175669d51dc7991296ce7dbd", "methods": { "init": { - "rows": 655, - "digest": "3941ac88f0b92eec098dfcf46faa4e60" + "rows": 342, + "digest": "f9f73a136c5642d519ec0f73e033e6f3" }, "init2": { - "rows": 652, - "digest": "1ebb84a10bafd30accfd3e8046d2e20d" + "rows": 342, + "digest": "c4303618beb22bb040bffacff35286cb" }, "approveBase": { "rows": 13194, @@ -79,37 +79,37 @@ } }, "verificationKey": { - "data": "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", - "hash": "10629507172660088077994796699854359657108723627411391158715881834104929001219" + "data": "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", + "hash": "20122457815983542378345864836658988429829729096873540353338563912426952908135" } }, "Dex": { - "digest": "2289275173dc37af7005d29de2d1bc87ef6633a38aab74653528d62a3ea2c53b", + "digest": "272c50dc8fd51817806dc4e5e0c17e4315d5bad077b23f6b44dac5b999ceb9a2", "methods": { "supplyLiquidityBase": { - "rows": 2883, - "digest": "a5811e58fa24f5ebde41076fc94849b7" + "rows": 2566, + "digest": "13f3375abe98f94b605c5b079315f066" }, "swapX": { - "rows": 1564, - "digest": "f6b047df744d8dcf297bd7a4fb45c812" + "rows": 1562, + "digest": "c29354e0f9d6a787a9330730ba19e689" }, "swapY": { - "rows": 1564, - "digest": "3082a30634a7a8da1603a7994b0cd4af" + "rows": 1562, + "digest": "9d82e9d413a342d45179001358495e89" }, "burnLiquidity": { - "rows": 718, - "digest": "99fb50066d2aa2f3f7afe801009cb503" + "rows": 403, + "digest": "7e7c3df3049d99703023a39ab1bfe8e4" }, "transfer": { - "rows": 1044, - "digest": "7c188ff9cf8e7db108e2d24f8e097622" + "rows": 414, + "digest": "2a4ce4f790fedf38514d331fa0d57eb0" } }, "verificationKey": { - "data": "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", - "hash": "10792977795272926498471289159457543677416779853262843765284042897388782617920" + "data": "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", + "hash": "18952871976004328548139095054727555812687816649131669760777722323942382863169" } }, "Group Primitive": { From 40ed27a9c0a2508a0ae8ee5895c549dcadda241f Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 17:57:08 +0100 Subject: [PATCH 342/524] add hint to problem --- src/lib/account_update.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index e6b800fcdf..c3fc5a819c 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -662,6 +662,7 @@ class AccountUpdate implements Types.AccountUpdate { if (accountLike instanceof PublicKey) { accountLike = AccountUpdate.defaultAccountUpdate(accountLike, id); makeChildAccountUpdate(thisAccountUpdate, accountLike); + // TODO existing API not affecting `UnfinishedForest` } if (!accountLike.label) accountLike.label = `${ From 0e8aed6d63cf2ca77f79601ca225fe565ee2b9e5 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Fri, 2 Feb 2024 09:54:00 -0800 Subject: [PATCH 343/524] fix(docs): Add backtick annotations to generic type comments --- src/lib/gadgets/foreign-field.ts | 2 +- src/lib/provable-types/packed.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/gadgets/foreign-field.ts b/src/lib/gadgets/foreign-field.ts index ce90b7947d..30c3725b80 100644 --- a/src/lib/gadgets/foreign-field.ts +++ b/src/lib/gadgets/foreign-field.ts @@ -464,7 +464,7 @@ const Field3 = { }, /** - * Provable interface for `Field3 = [Field, Field, Field]`. + * `Provable` interface for `Field3 = [Field, Field, Field]`. * * Note: Witnessing this creates a plain tuple of field elements without any implicit * range checks. diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index a12b9cbb54..eb0a04184e 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -13,7 +13,7 @@ import { fields, modifiedField } from './fields.js'; export { Packed, Hashed }; /** - * Packed is a "packed" representation of any type T. + * `Packed` is a "packed" representation of any type `T`. * * "Packed" means that field elements which take up fewer than 254 bits are packed together into * as few field elements as possible. @@ -141,7 +141,7 @@ function countFields(input: HashInput) { } /** - * Hashed represents a type T by its hash. + * `Hashed` represents a type `T` by its hash. * * Since a hash is only a single field element, this can be more efficient in provable code * where the number of constraints depends on the number of field elements per value. From 6f0107271073bd3dd24180ca00bb821710053927 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 21:09:44 +0100 Subject: [PATCH 344/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 89a967ee68..83f8520624 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 89a967ee68a628479647249c967b6884ec344a79 +Subproject commit 83f85206241c2fabd2be360acc5347bc104da452 From 69cb4aa9006e5865254b7de41c0f68d09d0cb614 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 21:16:54 +0100 Subject: [PATCH 345/524] fix examples build --- src/examples/zkapps/dex/erc20.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index 9648640f8a..d627533a6e 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -16,6 +16,7 @@ import { Mina, Int64, VerificationKey, + TransactionVersion, } from 'o1js'; /** @@ -93,7 +94,10 @@ class TrivialCoin extends SmartContract implements Erc20 { // make account non-upgradable forever this.account.permissions.set({ ...Permissions.default(), - setVerificationKey: Permissions.impossible(), + setVerificationKey: { + auth: Permissions.impossible(), + txnVersion: TransactionVersion.current(), + }, setPermissions: Permissions.impossible(), access: Permissions.proofOrSignature(), }); From 07fcbb0620ebc28a5068f9f8b49dac9eab1c9d6b Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 21:33:17 +0100 Subject: [PATCH 346/524] fix account query --- src/lib/mina/account.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/mina/account.ts b/src/lib/mina/account.ts index d4db268aed..1e66331cbc 100644 --- a/src/lib/mina/account.ts +++ b/src/lib/mina/account.ts @@ -86,7 +86,10 @@ const accountQuery = (publicKey: string, tokenId: string) => `{ receive setDelegate setPermissions - setVerificationKey + setVerificationKey { + auth + txnVersion + } setZkappUri editActionState setTokenSymbol From 993b1da5f4f7d7ff275cac55a9e0c5466d108a39 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 21:33:24 +0100 Subject: [PATCH 347/524] submodule --- src/mina | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina b/src/mina index 9600d09d30..b1b443ffdc 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit 9600d09d309db95ee7440f55705e0318632f535c +Subproject commit b1b443ffdc15ffd8569f2c244ecdeb5029c35097 From d2d2848fd6637c705bd18d1691e61edb20cf1dcd Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 21:54:50 +0100 Subject: [PATCH 348/524] dump vks --- tests/vk-regression/vk-regression.json | 98 +++++++++++++------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 2697fed274..d56f05e9e1 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -1,139 +1,139 @@ { "Voting_": { - "digest": "3f56ff09ceba13daf64b20cd48419395a04aa0007cac20e6e9c5f9106f251c3a", + "digest": "322daa2a9c3592f1329e878a1a026e0de84c687fca7ac2738d38cd61623fa48b", "methods": { "voterRegistration": { - "rows": 1258, - "digest": "5572b0d59feea6b199f3f45af7498d92" + "rows": 1259, + "digest": "d9565b2cf8dd64a390d91a2ac3a3687a" }, "candidateRegistration": { - "rows": 1258, - "digest": "07c8451f1c1ea4e9653548d411d5728c" + "rows": 1259, + "digest": "3e38523bcd4cc5ff0b68bb7cc9c42199" }, "approveRegistrations": { - "rows": 1146, - "digest": "ec68c1d8ab22e779ccbd2659dd6b46cd" + "rows": 1147, + "digest": "e95e1e0c7b28986f41a41a7dff987326" }, "vote": { - "rows": 1672, - "digest": "fa5671190ca2cc46084cae922a62288e" + "rows": 1673, + "digest": "d9873064350c897a2cbd1627650c91a4" }, "countVotes": { "rows": 5796, - "digest": "775f327a408b3f3d7bae4e3ff18aeb54" + "digest": "49bedc90e4f9004081a7cf4ec1890691" } }, "verificationKey": { - "data": "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", - "hash": "1740450553572902301764143810281331039416167348454304895395553400061364101079" + "data": "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", + "hash": "3069311164607256989232206084261152196484697330820251610414600118708759574210" } }, "Membership_": { - "digest": "255745fb9365ff4f970b96ed630c01c9c8f63e21744f83fbe833396731d096e2", + "digest": "612f6028fdec9ef6a676e4bb85d5ce7585ae1a6958656c99dbc81b0cecae243", "methods": { "addEntry": { "rows": 1353, - "digest": "fa32e32384aef8ce6a7d019142149d95" + "digest": "c2718951228ac1d35ec7ffcc8239a8db" }, "isMember": { "rows": 469, - "digest": "16dae12385ed7e5aca9161030a426335" + "digest": "8a434b9f7ed15f870c663eb7c7bde640" }, "publish": { "rows": 694, - "digest": "c7f77b05b8ec477338849af8dcb34a11" + "digest": "5c39ad497d024b86cf7b76b50cf14d6e" } }, "verificationKey": { - "data": "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", - "hash": "16610506589527352533348678289715227768202510979537802187565243095524972136674" + "data": "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", + "hash": "19732858713116457648848864472124826646289486820424277163143514654198128704704" } }, "HelloWorld": { - "digest": "20cadc6f44ecd546700e9fac15aa2740d4357d46ee03c2627c36be49b02e8227", + "digest": "1a84219f9b337333c108c12995dad5c952626f05f53f1fda6e1268bf4633a028", "methods": { "update": { "rows": 2351, - "digest": "f5b77fd12fee155fd3a40946dd453962" + "digest": "4b2d9758d8d9916c406ab0c9e7e2c458" } }, "verificationKey": { - "data": "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", - "hash": "28560680247074990771744165492810964987846406526367865642032954725768850073454" + "data": "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", + "hash": "19706647995548987613961520635625100103221832114196294239672830342799845503757" } }, "TokenContract": { - "digest": "f97dbbb67a72f01d956539b9f510633c0f88057f5dfbad8493e8d08dc4ca049", + "digest": "63fd66e39fcd25ad7dcc5b66090bbd6e18a1d6bfb0b72aa382a47bc6166ef8a", "methods": { "init": { "rows": 655, - "digest": "3941ac88f0b92eec098dfcf46faa4e60" + "digest": "5ab91ce22c35bb2dba025b823eb7aa4e" }, "init2": { "rows": 652, - "digest": "1ebb84a10bafd30accfd3e8046d2e20d" + "digest": "7abe7b8feb9e908aa0a128c57fef92ab" }, "deployZkapp": { "rows": 702, - "digest": "e5ac2667a79f44f1e7a65b12d8ac006c" + "digest": "98a146a469532f2ca77a9a34e11dcc09" }, "approveUpdate": { - "rows": 1928, - "digest": "f8bd1807567dc405f841283227dfb158" + "rows": 1943, + "digest": "8b900e97f701c15b5f3bf2fbc930581e" }, "approveAny": { - "rows": 1928, - "digest": "240aada76b79de1ca67ecbe455621378" + "rows": 1943, + "digest": "13bde12e4698c25d2160ba9a4e43622a" }, "approveUpdateAndSend": { - "rows": 3102, - "digest": "77efc708b9517c16722bad9cca54eb9c" + "rows": 3106, + "digest": "ccc638988d8d6af7dfa242b58fecd969" }, "transferToAddress": { "rows": 1044, - "digest": "212879ca2441ccc20f5e58940833cf35" + "digest": "f068c92fef5498c55d37fa3dc6c0ac48" }, "transferToUpdate": { - "rows": 2326, - "digest": "a7241cbc2946a3c468e600003a5d9a16" + "rows": 2329, + "digest": "51cac083ac0796235616e1f75da0f565" }, "getBalance": { "rows": 686, - "digest": "44a90b65d1d7ee553811759b115d12cc" + "digest": "759379647a60fc7454f7bdc46dc1eecf" } }, "verificationKey": { - "data": "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", - "hash": "20215142001741862869723990740096021895498505655157925381074115836190155964997" + "data": "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", + "hash": "14210531269021730583064312514751400108785387937383252076469247608838854744752" } }, "Dex": { - "digest": "14f902411526156cdf7de9a822a3f6467f7608a135504038993cbc8efeaf720a", + "digest": "24e6d334e791e937ae7fb3a82f282f2e8af3e6e786546356590211ef1bdb5a80", "methods": { "supplyLiquidityBase": { - "rows": 3749, - "digest": "08830f49d9e8a4bf683db63c1c19bd28" + "rows": 3751, + "digest": "9237dffd61ed31ba0a8c72fe50f0c4d7" }, "swapX": { - "rows": 1986, - "digest": "e1c79fee9c8f94815daa5d6fee7c5181" + "rows": 1987, + "digest": "62ad64b6e96aaa83724a16bae03972ce" }, "swapY": { - "rows": 1986, - "digest": "4cf07c1491e7fc167edcf3a26d636f3d" + "rows": 1987, + "digest": "bb7155ae8ea8315d20374058a762862e" }, "burnLiquidity": { "rows": 718, - "digest": "99fb50066d2aa2f3f7afe801009cb503" + "digest": "d205382392651e933604c5d0e5ddeceb" }, "transfer": { "rows": 1044, - "digest": "7c188ff9cf8e7db108e2d24f8e097622" + "digest": "837b98775353a242e8f57bcbaddf11d8" } }, "verificationKey": { - "data": "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", - "hash": "6361961148584909856756402479432671413765163664396823312454174383287651676472" + "data": "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", + "hash": "591942528341816705764438604356575660858495553881116404294999229299577002001" } }, "Group Primitive": { From 834a440024f608020293d71d4ee844af98aee162 Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 21:57:07 +0100 Subject: [PATCH 349/524] 0.16.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f6abce7a96..7596decd36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "o1js", - "version": "0.15.4", + "version": "0.16.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "o1js", - "version": "0.15.4", + "version": "0.16.0", "license": "Apache-2.0", "dependencies": { "blakejs": "1.2.1", diff --git a/package.json b/package.json index 59b5e2c705..80f4b4340f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "o1js", "description": "TypeScript framework for zk-SNARKs and zkApps", - "version": "0.15.4", + "version": "0.16.0", "license": "Apache-2.0", "homepage": "https://github.com/o1-labs/o1js/", "keywords": [ From 8caeb56df628825d56178a3e25fbb0e287dcd18e Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 21:57:44 +0100 Subject: [PATCH 350/524] changelog --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 433daf8f5e..00613385e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm _Security_ in case of vulnerabilities. --> -## [Unreleased](https://github.com/o1-labs/o1js/compare/e5d1e0f...HEAD) +## [Unreleased](https://github.com/o1-labs/o1js/compare/834a44002...HEAD) + +## [0.16.0](https://github.com/o1-labs/o1js/compare/e5d1e0f...834a44002) ### Breaking changes From 6dea1fa88813eef469ac5669032d8498b8dfb28a Mon Sep 17 00:00:00 2001 From: Gregor Date: Fri, 2 Feb 2024 22:02:57 +0100 Subject: [PATCH 351/524] changelog --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00613385e4..5d284ef883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Breaking changes -- Reduce number of constraints of ECDSA verification by 5%, which breaks deployed contracts using ECDSA https://github.com/o1-labs/o1js/pull/1376 +- Protocol change that adds a "transaction version" to the permission to set verification keys https://github.com/MinaProtocol/mina/pull/14407 + - See [the relevant RFC](https://github.com/MinaProtocol/mina/blob/9577ad689a8e4d4f97e1d0fc3d26e20219f4abd1/rfcs/0051-verification-key-permissions.md) for the motivation behind this change + - Breaks all deployed contracts, as it changes the account update layout ### Added @@ -29,6 +31,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Provable type `Hashed` to represent provable types by their hash https://github.com/o1-labs/o1js/pull/1377 - This also exposes `Poseidon.hashPacked()` to efficiently hash an arbitrary type +### Changed + +- Reduce number of constraints of ECDSA verification by 5% https://github.com/o1-labs/o1js/pull/1376 + ## [0.15.4](https://github.com/o1-labs/o1js/compare/be748e42e...e5d1e0f) ### Changed From fd45ad7cc96c1ee33c5bbbce2306a6415b38392f Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 15:27:01 +0100 Subject: [PATCH 352/524] simple ledger to record permissions changes throughout tx validation --- src/lib/mina.ts | 27 ++++++++--- src/lib/mina/account.ts | 13 ++++- src/lib/mina/transaction-logic/apply.ts | 30 ++++++++++++ src/lib/mina/transaction-logic/ledger.ts | 60 ++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 src/lib/mina/transaction-logic/apply.ts create mode 100644 src/lib/mina/transaction-logic/ledger.ts diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 95bf7835e4..728ca2df5f 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -44,6 +44,7 @@ import { defaultNetworkConstants, NetworkConstants, } from './mina/mina-instance.js'; +import { SimpleLedger } from './mina/transaction-logic/ledger.js'; export { createTransaction, @@ -396,12 +397,10 @@ function LocalBlockchain({ if (enforceTransactionLimits) verifyTransactionLimits(txn.transaction); - for (const update of txn.transaction.accountUpdates) { - let accountJson = ledger.getAccount( - Ml.fromPublicKey(update.body.publicKey), - Ml.constFromField(update.body.tokenId) - ); + // create an ad-hoc ledger to record changes to accounts within the transaction + let simpleLedger = SimpleLedger.create(); + for (const update of txn.transaction.accountUpdates) { let authIsProof = !!update.authorization.proof; let kindIsProof = update.body.authorizationKind.isProved.toBoolean(); // checks and edge case where a proof is expected, but the developer forgot to invoke await tx.prove() @@ -412,9 +411,22 @@ function LocalBlockchain({ ); } - if (accountJson) { - let account = Account.fromJSON(accountJson); + let account = simpleLedger.load(update.body); + + // the first time we encounter an account, use it from the persistent ledger + if (account === undefined) { + let accountJson = ledger.getAccount( + Ml.fromPublicKey(update.body.publicKey), + Ml.constFromField(update.body.tokenId) + ); + if (accountJson !== undefined) { + let storedAccount = Account.fromJSON(accountJson); + simpleLedger.store(storedAccount); + account = storedAccount; + } + } + if (account !== undefined) { await verifyAccountUpdate( account, update, @@ -423,6 +435,7 @@ function LocalBlockchain({ this.getNetworkId() ); } + simpleLedger.apply(update); } try { diff --git a/src/lib/mina/account.ts b/src/lib/mina/account.ts index dc31d0d864..ac4e77098e 100644 --- a/src/lib/mina/account.ts +++ b/src/lib/mina/account.ts @@ -13,12 +13,23 @@ import { jsLayout } from '../../bindings/mina-transaction/gen/js-layout.js'; import { ProvableExtended } from '../circuit_value.js'; export { FetchedAccount, Account, PartialAccount }; -export { accountQuery, parseFetchedAccount, fillPartialAccount }; +export { newAccount, accountQuery, parseFetchedAccount, fillPartialAccount }; type AuthRequired = Types.Json.AuthRequired; type Account = Types.Account; const Account = Types.Account; +function newAccount(accountId: { + publicKey: PublicKey; + tokenId?: Field; +}): Account { + let account = Account.empty(); + account.publicKey = accountId.publicKey; + account.tokenId = accountId.tokenId ?? Types.TokenId.empty(); + account.permissions = Permissions.initial(); + return account; +} + type PartialAccount = Omit, 'zkapp'> & { zkapp?: Partial; }; diff --git a/src/lib/mina/transaction-logic/apply.ts b/src/lib/mina/transaction-logic/apply.ts new file mode 100644 index 0000000000..52e38f2065 --- /dev/null +++ b/src/lib/mina/transaction-logic/apply.ts @@ -0,0 +1,30 @@ +/** + * Apply transactions to a ledger of accounts. + */ +import { type AccountUpdate } from '../../account_update.js'; +import { Account } from '../account.js'; + +export { applyAccountUpdate }; + +/** + * Apply a single account update to update an account. + * + * TODO: + * - This must receive and return some context global to the transaction, to check validity + * - Should operate on the value / bigint type, not the provable type + */ +function applyAccountUpdate(account: Account, update: AccountUpdate): Account { + account.publicKey.assertEquals(update.publicKey); + account.tokenId.assertEquals(update.tokenId, 'token id mismatch'); + + // clone account (TODO: do this efficiently) + let json = Account.toJSON(account); + account = Account.fromJSON(json); + + // update permissions + if (update.update.permissions.isSome.toBoolean()) { + account.permissions = update.update.permissions.value; + } + + return account; +} diff --git a/src/lib/mina/transaction-logic/ledger.ts b/src/lib/mina/transaction-logic/ledger.ts new file mode 100644 index 0000000000..ecf292c8fd --- /dev/null +++ b/src/lib/mina/transaction-logic/ledger.ts @@ -0,0 +1,60 @@ +/** + * A ledger of accounts - simple model of a local blockchain. + */ +import { PublicKey } from '../../signature.js'; +import { type AccountUpdate, TokenId } from '../../account_update.js'; +import { Account, newAccount } from '../account.js'; +import { Field } from '../../field.js'; +import { applyAccountUpdate } from './apply.js'; + +export { SimpleLedger }; + +class SimpleLedger { + accounts: Map; + + constructor() { + this.accounts = new Map(); + } + + static create(): SimpleLedger { + return new SimpleLedger(); + } + + exists({ publicKey, tokenId = TokenId.default }: InputAccountId): boolean { + return this.accounts.has(accountId({ publicKey, tokenId })); + } + + store(account: Account): void { + this.accounts.set(accountId(account), account); + } + + load({ + publicKey, + tokenId = TokenId.default, + }: InputAccountId): Account | undefined { + let id = accountId({ publicKey, tokenId }); + let account = this.accounts.get(id); + return account; + } + + apply(update: AccountUpdate): void { + let id = accountId(update); + let account = this.accounts.get(id); + account ??= newAccount(update); + + let updated = applyAccountUpdate(account, update); + this.accounts.set(id, updated); + } +} + +type AccountId = { publicKey: PublicKey; tokenId: Field }; +type InputAccountId = { publicKey: PublicKey; tokenId?: Field }; + +function accountId(account: AccountId): bigint { + let id = account.publicKey.x.toBigInt(); + id <<= 1n; + id |= BigInt(account.publicKey.isOdd.toBoolean()); + id <<= BigInt(Field.sizeInBits); + id |= account.tokenId.toBigInt(); + return id; +} From dec12867627b68969e5e9bc7b9aa6c3ff0d3e008 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 15:27:18 +0100 Subject: [PATCH 353/524] token contract unit test --- .../mina/token/token-contract.unit-test.ts | 82 +++++++++++++------ 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts index e0a990f8ef..6bac61c881 100644 --- a/src/lib/mina/token/token-contract.unit-test.ts +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -1,12 +1,12 @@ +import assert from 'node:assert'; import { - Bool, - Int64, method, Mina, - Provable, UInt64, + AccountUpdate, AccountUpdateForest, TokenContract, + Int64, } from '../../../index.js'; class ExampleTokenContract extends TokenContract { @@ -14,20 +14,9 @@ class ExampleTokenContract extends TokenContract { @method approveBase(updates: AccountUpdateForest) { - let totalBalanceChange = Int64.zero; - - this.forEachUpdate(updates, (accountUpdate, usesToken) => { - totalBalanceChange = totalBalanceChange.add( - Provable.if(usesToken, accountUpdate.balanceChange, Int64.zero) - ); - }); - - // prove that the total balance change is zero - totalBalanceChange.assertEquals(0); + this.checkZeroBalanceChange(updates); } - // BELOW: example implementation specific to this token - // constant supply SUPPLY = UInt64.from(10n ** 18n); @@ -36,23 +25,62 @@ class ExampleTokenContract extends TokenContract { super.init(); // mint the entire supply to the token account with the same address as this contract - let receiver = this.token.mint({ - address: this.address, - amount: this.SUPPLY, - }); - - // assert that the receiving account is new, so this can be only done once - receiver.account.isNew.requireEquals(Bool(true)); + this.token.mint({ address: this.address, amount: this.SUPPLY }); // pay fees for opened account - this.balance.subInPlace(Mina.accountCreationFee()); + this.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); } } // TESTS -ExampleTokenContract.analyzeMethods({ printSummary: true }); +let Local = Mina.LocalBlockchain({ proofsEnabled: false }); +Mina.setActiveInstance(Local); + +let { publicKey: sender, privateKey: senderKey } = Local.testAccounts[0]; +let { publicKey: tokenAddress, privateKey: tokenKey } = Local.testAccounts[1]; +let { publicKey: otherAddress } = Local.testAccounts[2]; + +let token = new ExampleTokenContract(tokenAddress); +let tokenId = token.token.id; + +// deploy token contract +let deployTx = await Mina.transaction(sender, () => token.deploy()); +await deployTx.prove(); +await deployTx.sign([tokenKey, senderKey]).send(); + +assert( + Mina.getAccount(tokenAddress).zkapp?.verificationKey !== undefined, + 'token contract deployed' +); + +// can transfer tokens between two accounts +let transferTx = await Mina.transaction(sender, () => { + AccountUpdate.fundNewAccount(sender); + token.transfer(tokenAddress, otherAddress, UInt64.one); +}); +await transferTx.prove(); +await transferTx.sign([tokenKey, senderKey]).send(); + +Mina.getBalance(otherAddress, tokenId).assertEquals(UInt64.one); + +// fails to approve a deep account update tree with correct token permissions, but a non-zero balance sum +let update1 = AccountUpdate.create(otherAddress); +update1.body.mayUseToken = AccountUpdate.MayUseToken.ParentsOwnToken; + +let update2 = AccountUpdate.create(otherAddress); +update2.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; + +let update3 = AccountUpdate.create(otherAddress, tokenId); +update3.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; +update3.balanceChange = Int64.one; + +update1.adopt(update2); +update2.adopt(update3); + +let forest = AccountUpdateForest.fromArray([update1]); -console.time('compile'); -await ExampleTokenContract.compile(); -console.timeEnd('compile'); +await assert.rejects( + () => Mina.transaction(sender, () => token.approveBase(forest)), + /Field\.assertEquals\(\): 1 != 0/ +); From 1a672a5838fa94f8ce32db430673a0822875a216 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 17:48:59 +0100 Subject: [PATCH 354/524] only use internal apply on existing accounts for now --- src/lib/mina.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 728ca2df5f..5cfd83168c 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -45,6 +45,7 @@ import { NetworkConstants, } from './mina/mina-instance.js'; import { SimpleLedger } from './mina/transaction-logic/ledger.js'; +import { assert } from './gadgets/common.js'; export { createTransaction, @@ -426,6 +427,7 @@ function LocalBlockchain({ } } + // TODO: verify account update even if the account doesn't exist yet, using a default initial account if (account !== undefined) { await verifyAccountUpdate( account, @@ -434,8 +436,8 @@ function LocalBlockchain({ this.proofsEnabled, this.getNetworkId() ); + simpleLedger.apply(update); } - simpleLedger.apply(update); } try { @@ -1253,7 +1255,11 @@ async function verifyAccountUpdate( publicOutput: [], }); - let verificationKey = account.zkapp?.verificationKey?.data!; + let verificationKey = account.zkapp?.verificationKey?.data; + assert( + verificationKey !== undefined, + 'Account does not have a verification key' + ); isValidProof = await verify(proof.toJSON(), verificationKey); if (!isValidProof) { throw Error( @@ -1261,7 +1267,7 @@ async function verifyAccountUpdate( ); } } catch (error) { - errorTrace += '\n\n' + (error as Error).message; + errorTrace += '\n\n' + (error as Error).stack; isValidProof = false; } } @@ -1275,7 +1281,7 @@ async function verifyAccountUpdate( networkId ); } catch (error) { - errorTrace += '\n\n' + (error as Error).message; + errorTrace += '\n\n' + (error as Error).stack; isValidSignature = false; } } @@ -1303,7 +1309,7 @@ async function verifyAccountUpdate( if (!verified) { throw Error( `Transaction verification failed: Cannot update field '${field}' because permission for this field is '${p}', but the required authorization was not provided or is invalid. - ${errorTrace !== '' ? 'Error trace: ' + errorTrace : ''}` + ${errorTrace !== '' ? 'Error trace: ' + errorTrace : ''}\n\n` ); } } From d26c792756aec5820732af66dbf65bf620f1642d Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 21:12:14 +0100 Subject: [PATCH 355/524] adapt upgradability test --- src/examples/zkapps/dex/upgradability.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 375e661dba..aebac741c6 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -178,7 +178,7 @@ async function atomicActionsTest({ withVesting }: { withVesting: boolean }) { }); await tx.prove(); await expect(tx.sign([feePayerKey, keys.dex]).send()).rejects.toThrow( - /Update_not_permitted_delegate/ + /Cannot update field 'delegate'/ ); /** From 6212e814853834a7b5298c896cb60d26be4a1af1 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 15:27:01 +0100 Subject: [PATCH 356/524] simple ledger to record permissions changes throughout tx validation --- src/lib/mina.ts | 27 ++++++++--- src/lib/mina/account.ts | 13 ++++- src/lib/mina/transaction-logic/apply.ts | 30 ++++++++++++ src/lib/mina/transaction-logic/ledger.ts | 60 ++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 src/lib/mina/transaction-logic/apply.ts create mode 100644 src/lib/mina/transaction-logic/ledger.ts diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 46cacdc568..51520763c7 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -43,6 +43,7 @@ import { type ActionStates, type NetworkConstants, } from './mina/mina-instance.js'; +import { SimpleLedger } from './mina/transaction-logic/ledger.js'; export { createTransaction, @@ -395,12 +396,10 @@ function LocalBlockchain({ if (enforceTransactionLimits) verifyTransactionLimits(txn.transaction); - for (const update of txn.transaction.accountUpdates) { - let accountJson = ledger.getAccount( - Ml.fromPublicKey(update.body.publicKey), - Ml.constFromField(update.body.tokenId) - ); + // create an ad-hoc ledger to record changes to accounts within the transaction + let simpleLedger = SimpleLedger.create(); + for (const update of txn.transaction.accountUpdates) { let authIsProof = !!update.authorization.proof; let kindIsProof = update.body.authorizationKind.isProved.toBoolean(); // checks and edge case where a proof is expected, but the developer forgot to invoke await tx.prove() @@ -411,9 +410,22 @@ function LocalBlockchain({ ); } - if (accountJson) { - let account = Account.fromJSON(accountJson); + let account = simpleLedger.load(update.body); + + // the first time we encounter an account, use it from the persistent ledger + if (account === undefined) { + let accountJson = ledger.getAccount( + Ml.fromPublicKey(update.body.publicKey), + Ml.constFromField(update.body.tokenId) + ); + if (accountJson !== undefined) { + let storedAccount = Account.fromJSON(accountJson); + simpleLedger.store(storedAccount); + account = storedAccount; + } + } + if (account !== undefined) { await verifyAccountUpdate( account, update, @@ -422,6 +434,7 @@ function LocalBlockchain({ this.getNetworkId() ); } + simpleLedger.apply(update); } try { diff --git a/src/lib/mina/account.ts b/src/lib/mina/account.ts index 1e66331cbc..1eb38eb214 100644 --- a/src/lib/mina/account.ts +++ b/src/lib/mina/account.ts @@ -13,12 +13,23 @@ import { jsLayout } from '../../bindings/mina-transaction/gen/js-layout.js'; import { ProvableExtended } from '../circuit_value.js'; export { FetchedAccount, Account, PartialAccount }; -export { accountQuery, parseFetchedAccount, fillPartialAccount }; +export { newAccount, accountQuery, parseFetchedAccount, fillPartialAccount }; type AuthRequired = Types.Json.AuthRequired; type Account = Types.Account; const Account = Types.Account; +function newAccount(accountId: { + publicKey: PublicKey; + tokenId?: Field; +}): Account { + let account = Account.empty(); + account.publicKey = accountId.publicKey; + account.tokenId = accountId.tokenId ?? Types.TokenId.empty(); + account.permissions = Permissions.initial(); + return account; +} + type PartialAccount = Omit, 'zkapp'> & { zkapp?: Partial; }; diff --git a/src/lib/mina/transaction-logic/apply.ts b/src/lib/mina/transaction-logic/apply.ts new file mode 100644 index 0000000000..52e38f2065 --- /dev/null +++ b/src/lib/mina/transaction-logic/apply.ts @@ -0,0 +1,30 @@ +/** + * Apply transactions to a ledger of accounts. + */ +import { type AccountUpdate } from '../../account_update.js'; +import { Account } from '../account.js'; + +export { applyAccountUpdate }; + +/** + * Apply a single account update to update an account. + * + * TODO: + * - This must receive and return some context global to the transaction, to check validity + * - Should operate on the value / bigint type, not the provable type + */ +function applyAccountUpdate(account: Account, update: AccountUpdate): Account { + account.publicKey.assertEquals(update.publicKey); + account.tokenId.assertEquals(update.tokenId, 'token id mismatch'); + + // clone account (TODO: do this efficiently) + let json = Account.toJSON(account); + account = Account.fromJSON(json); + + // update permissions + if (update.update.permissions.isSome.toBoolean()) { + account.permissions = update.update.permissions.value; + } + + return account; +} diff --git a/src/lib/mina/transaction-logic/ledger.ts b/src/lib/mina/transaction-logic/ledger.ts new file mode 100644 index 0000000000..ecf292c8fd --- /dev/null +++ b/src/lib/mina/transaction-logic/ledger.ts @@ -0,0 +1,60 @@ +/** + * A ledger of accounts - simple model of a local blockchain. + */ +import { PublicKey } from '../../signature.js'; +import { type AccountUpdate, TokenId } from '../../account_update.js'; +import { Account, newAccount } from '../account.js'; +import { Field } from '../../field.js'; +import { applyAccountUpdate } from './apply.js'; + +export { SimpleLedger }; + +class SimpleLedger { + accounts: Map; + + constructor() { + this.accounts = new Map(); + } + + static create(): SimpleLedger { + return new SimpleLedger(); + } + + exists({ publicKey, tokenId = TokenId.default }: InputAccountId): boolean { + return this.accounts.has(accountId({ publicKey, tokenId })); + } + + store(account: Account): void { + this.accounts.set(accountId(account), account); + } + + load({ + publicKey, + tokenId = TokenId.default, + }: InputAccountId): Account | undefined { + let id = accountId({ publicKey, tokenId }); + let account = this.accounts.get(id); + return account; + } + + apply(update: AccountUpdate): void { + let id = accountId(update); + let account = this.accounts.get(id); + account ??= newAccount(update); + + let updated = applyAccountUpdate(account, update); + this.accounts.set(id, updated); + } +} + +type AccountId = { publicKey: PublicKey; tokenId: Field }; +type InputAccountId = { publicKey: PublicKey; tokenId?: Field }; + +function accountId(account: AccountId): bigint { + let id = account.publicKey.x.toBigInt(); + id <<= 1n; + id |= BigInt(account.publicKey.isOdd.toBoolean()); + id <<= BigInt(Field.sizeInBits); + id |= account.tokenId.toBigInt(); + return id; +} From c16d6052ab7a6894d07d6007b98ad5121bdd38e3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 17:48:59 +0100 Subject: [PATCH 357/524] only use internal apply on existing accounts for now --- src/lib/mina.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 51520763c7..22c83fe9bb 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -44,6 +44,7 @@ import { type NetworkConstants, } from './mina/mina-instance.js'; import { SimpleLedger } from './mina/transaction-logic/ledger.js'; +import { assert } from './gadgets/common.js'; export { createTransaction, @@ -425,6 +426,7 @@ function LocalBlockchain({ } } + // TODO: verify account update even if the account doesn't exist yet, using a default initial account if (account !== undefined) { await verifyAccountUpdate( account, @@ -433,8 +435,8 @@ function LocalBlockchain({ this.proofsEnabled, this.getNetworkId() ); + simpleLedger.apply(update); } - simpleLedger.apply(update); } try { @@ -1252,7 +1254,12 @@ async function verifyAccountUpdate( publicOutput: [], }; - let verificationKey = account.zkapp?.verificationKey?.data!; + let verificationKey = account.zkapp?.verificationKey?.data; + assert( + verificationKey !== undefined, + 'Account does not have a verification key' + ); + isValidProof = await verify(proof, verificationKey); if (!isValidProof) { throw Error( @@ -1260,7 +1267,7 @@ async function verifyAccountUpdate( ); } } catch (error) { - errorTrace += '\n\n' + (error as Error).message; + errorTrace += '\n\n' + (error as Error).stack; isValidProof = false; } } @@ -1274,7 +1281,7 @@ async function verifyAccountUpdate( networkId ); } catch (error) { - errorTrace += '\n\n' + (error as Error).message; + errorTrace += '\n\n' + (error as Error).stack; isValidSignature = false; } } @@ -1302,7 +1309,7 @@ async function verifyAccountUpdate( if (!verified) { throw Error( `Transaction verification failed: Cannot update field '${field}' because permission for this field is '${p}', but the required authorization was not provided or is invalid. - ${errorTrace !== '' ? 'Error trace: ' + errorTrace : ''}` + ${errorTrace !== '' ? 'Error trace: ' + errorTrace : ''}\n\n` ); } } From 191f605e5a31c88004aa4cf7a8f935af434f0edb Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 21:12:14 +0100 Subject: [PATCH 358/524] adapt upgradability test --- src/examples/zkapps/dex/upgradability.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 78c5c68379..d6cf13d6ad 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -185,7 +185,7 @@ async function atomicActionsTest({ withVesting }: { withVesting: boolean }) { }); await tx.prove(); await expect(tx.sign([feePayerKey, keys.dex]).send()).rejects.toThrow( - /Update_not_permitted_delegate/ + /Cannot update field 'delegate'/ ); /** From ca46087c0db6101ab938b7627d3203be6ca2466e Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 21:16:40 +0100 Subject: [PATCH 359/524] submodules --- src/bindings | 2 +- src/mina | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index 83f8520624..3503101051 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 83f85206241c2fabd2be360acc5347bc104da452 +Subproject commit 35031010512993426bf226dbd6f1048fa1da09cc diff --git a/src/mina b/src/mina index b1b443ffdc..a5c7f667a5 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit b1b443ffdc15ffd8569f2c244ecdeb5029c35097 +Subproject commit a5c7f667a5008c15243f28921505c3930a4fdf35 From 6c9606dd0e10891c7fc9637cfccf9ee8623268d6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 21:25:34 +0100 Subject: [PATCH 360/524] don't rely on getters --- src/lib/mina/transaction-logic/ledger.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/mina/transaction-logic/ledger.ts b/src/lib/mina/transaction-logic/ledger.ts index ecf292c8fd..266054ca50 100644 --- a/src/lib/mina/transaction-logic/ledger.ts +++ b/src/lib/mina/transaction-logic/ledger.ts @@ -38,9 +38,9 @@ class SimpleLedger { } apply(update: AccountUpdate): void { - let id = accountId(update); + let id = accountId(update.body); let account = this.accounts.get(id); - account ??= newAccount(update); + account ??= newAccount(update.body); let updated = applyAccountUpdate(account, update); this.accounts.set(id, updated); From fbeb4ff62661928a7974412a101f45d393f1a827 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 21:35:44 +0100 Subject: [PATCH 361/524] submodules --- src/bindings | 2 +- src/mina | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings b/src/bindings index 83f8520624..3503101051 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 83f85206241c2fabd2be360acc5347bc104da452 +Subproject commit 35031010512993426bf226dbd6f1048fa1da09cc diff --git a/src/mina b/src/mina index b1b443ffdc..a5c7f667a5 160000 --- a/src/mina +++ b/src/mina @@ -1 +1 @@ -Subproject commit b1b443ffdc15ffd8569f2c244ecdeb5029c35097 +Subproject commit a5c7f667a5008c15243f28921505c3930a4fdf35 From 4703db02e09b14de3275bc6b0ab3e1277b02c6a9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 5 Feb 2024 21:37:21 +0100 Subject: [PATCH 362/524] fix changelog --- CHANGELOG.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efcb589c57..16f8222a28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/834a44002...HEAD) +### Added + +- `MerkleList` to enable provable operations on a dynamically-sized list https://github.com/o1-labs/o1js/pull/1398 + - including `MerkleListIterator` to iterate over a merkle list +- `TokenAccountUpdateIterator`, a primitive for token contracts to iterate over all token account updates in a transaction. https://github.com/o1-labs/o1js/pull/1398 + ## [0.16.0](https://github.com/o1-labs/o1js/compare/e5d1e0f...834a44002) ### Breaking changes @@ -27,12 +33,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -- `MerkleList` to enable provable operations on a dynamically-sized list https://github.com/o1-labs/o1js/pull/1398 - - including `MerkleListIterator` to iterate over a merkle list - Provable type `Packed` to pack small field elements into fewer field elements https://github.com/o1-labs/o1js/pull/1376 - Provable type `Hashed` to represent provable types by their hash https://github.com/o1-labs/o1js/pull/1377 - This also exposes `Poseidon.hashPacked()` to efficiently hash an arbitrary type -- `TokenAccountUpdateIterator`, a primitive for token contracts to iterate over all token account updates in a transaction. https://github.com/o1-labs/o1js/pull/1398 ### Changed From 516d6ce17132d7be76409ef9040bee9c104b53de Mon Sep 17 00:00:00 2001 From: robrobbins Date: Tue, 6 Feb 2024 01:29:38 +0000 Subject: [PATCH 363/524] adjust dist path in mina-signer tests. fixes for fieldToHex --- src/mina-signer/src/rosetta.ts | 7 ++++--- src/mina-signer/tests/client.test.ts | 2 +- src/mina-signer/tests/keypair.test.ts | 2 +- src/mina-signer/tests/message.test.ts | 4 ++-- src/mina-signer/tests/payment.test.ts | 4 ++-- src/mina-signer/tests/rosetta.test.ts | 2 +- src/mina-signer/tests/stake-delegation.test.ts | 4 ++-- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/mina-signer/src/rosetta.ts b/src/mina-signer/src/rosetta.ts index 395a078ce1..835bec83a5 100644 --- a/src/mina-signer/src/rosetta.ts +++ b/src/mina-signer/src/rosetta.ts @@ -27,12 +27,13 @@ function fieldToHex( ) { let bytes = binable.toBytes(x); // set highest bit (which is empty) - bytes[bytes.length - 1] &= Number(paddingBit) << 7; - // map each byte to a hex string of length 2 + bytes[bytes.length - 1] |= Number(paddingBit) << 7; + // map each byte to a 0-padded hex string of length 2 return bytes - .map((byte) => byte.toString(16).split('').reverse().join('')) + .map((byte) => byte.toString(16).padStart(2, '0').split('').reverse().join('')) .join(''); } + function fieldFromHex( binable: Binable, hex: string diff --git a/src/mina-signer/tests/client.test.ts b/src/mina-signer/tests/client.test.ts index ca0cce3934..c55eaa0012 100644 --- a/src/mina-signer/tests/client.test.ts +++ b/src/mina-signer/tests/client.test.ts @@ -1,4 +1,4 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; +import Client from '../../../dist/node/mina-signer/MinaSigner.js'; describe('Client Class Initialization', () => { let client; diff --git a/src/mina-signer/tests/keypair.test.ts b/src/mina-signer/tests/keypair.test.ts index 5e3e48dd59..30fa986b88 100644 --- a/src/mina-signer/tests/keypair.test.ts +++ b/src/mina-signer/tests/keypair.test.ts @@ -1,4 +1,4 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; +import Client from '../../../dist/node/mina-signer/MinaSigner.js'; describe('Keypair', () => { let client: Client; diff --git a/src/mina-signer/tests/message.test.ts b/src/mina-signer/tests/message.test.ts index 26011eabff..00c09b662a 100644 --- a/src/mina-signer/tests/message.test.ts +++ b/src/mina-signer/tests/message.test.ts @@ -1,5 +1,5 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; -import type { PrivateKey } from '../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../../../dist/node/mina-signer/MinaSigner.js'; +import type { PrivateKey } from '../../../dist/node/mina-signer/src/TSTypes.js'; describe('Message', () => { describe('Mainnet network', () => { diff --git a/src/mina-signer/tests/payment.test.ts b/src/mina-signer/tests/payment.test.ts index 9a7d52804d..a5a34878ce 100644 --- a/src/mina-signer/tests/payment.test.ts +++ b/src/mina-signer/tests/payment.test.ts @@ -1,5 +1,5 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; -import type { Keypair } from '../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../../../dist/node/mina-signer/MinaSigner.js'; +import type { Keypair } from '../../../dist/node/mina-signer/src/TSTypes.js'; describe('Payment', () => { describe('Mainnet network', () => { diff --git a/src/mina-signer/tests/rosetta.test.ts b/src/mina-signer/tests/rosetta.test.ts index 5db94f21e1..1410ddeab8 100644 --- a/src/mina-signer/tests/rosetta.test.ts +++ b/src/mina-signer/tests/rosetta.test.ts @@ -1,4 +1,4 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; +import Client from '../../../dist/node/mina-signer/MinaSigner.js'; describe('Rosetta', () => { let client: Client; diff --git a/src/mina-signer/tests/stake-delegation.test.ts b/src/mina-signer/tests/stake-delegation.test.ts index ea59458e1f..d7ac8f9231 100644 --- a/src/mina-signer/tests/stake-delegation.test.ts +++ b/src/mina-signer/tests/stake-delegation.test.ts @@ -1,5 +1,5 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; -import type { Keypair } from '../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../../../dist/node/mina-signer/MinaSigner.js'; +import type { Keypair } from '../../../dist/node/mina-signer/src/TSTypes.js'; describe('Stake Delegation', () => { describe('Mainnet network', () => { From 12774c5252392dffeb65f8948076f1d9983a36e3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 09:36:34 +0100 Subject: [PATCH 364/524] changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16f8222a28..5ca42cd21a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `MerkleList` to enable provable operations on a dynamically-sized list https://github.com/o1-labs/o1js/pull/1398 - including `MerkleListIterator` to iterate over a merkle list -- `TokenAccountUpdateIterator`, a primitive for token contracts to iterate over all token account updates in a transaction. https://github.com/o1-labs/o1js/pull/1398 +- `TokenContract`, a new base smart contract class for token contracts https://github.com/o1-labs/o1js/pull/1384 + - Usage example: `https://github.com/o1-labs/o1js/blob/main/src/lib/mina/token/token-contract.unit-test.ts` +- `TokenAccountUpdateIterator`, a primitive to iterate over all token account updates in a transaction https://github.com/o1-labs/o1js/pull/1398 + - this is used to implement `TokenContract` under the hood ## [0.16.0](https://github.com/o1-labs/o1js/compare/e5d1e0f...834a44002) From 15b1b023a3c2cfdbadcbbb11c9ac97b52e4a7342 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 10:02:40 +0100 Subject: [PATCH 365/524] dump vks --- tests/vk-regression/vk-regression.json | 60 +++++++++++++------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 40cd2c9678..201702e8d8 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -1,22 +1,22 @@ { "Voting_": { - "digest": "5796cb30bf420c4052ee0ca04d6a7eb554ac0806183ee97c92c51bc1cc2976e", + "digest": "27e70f037e04461a113b08cf9633feee59f84edeccb059022ced1a98ded95064", "methods": { "voterRegistration": { - "rows": 1261, - "digest": "8b6b8083bb59f6dd4746b5b3da3e10e3" + "rows": 1262, + "digest": "c826896d08315d78c96d09a638934600" }, "candidateRegistration": { - "rows": 1261, - "digest": "73947a300094dcebedff84c86887a06d" + "rows": 1262, + "digest": "c4e91618c623fbf3fcc8e489a50a7d46" }, "approveRegistrations": { - "rows": 1149, - "digest": "8fa35ef942084cc163bbd27bdaccb468" + "rows": 1150, + "digest": "d8fab66c623d2361b20328162c4119b1" }, "vote": { - "rows": 1675, - "digest": "9c607a19228a5913cd32278394d65f10" + "rows": 1676, + "digest": "31ca7fde9282e5de63e94112ac57cf51" }, "countVotes": { "rows": 5796, @@ -24,16 +24,16 @@ } }, "verificationKey": { - "data": "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", - "hash": "18930773283376165145392075130641581795923440685773146685646791501279243480469" + "data": "AACd9tWcrEA7+0z2zM4uOSwj5GdeIBIROoVsS/yRuSRjKmnpZwY33yiryBLa9HQWpeZDSJI5y91gKJ9g5atltQApAhMdOuU5+NrHN3RCJtswX+WPvwaHJnihtSy2FcJPyghvBVTi2i7dtWIPQLVDIzC5ARu8f8H9JWjzjVVYE/rQLruuq2qUsCrqdVsdRaw+6OjIFeAXS6mzvrVv5iYGslg5CV5mgLBg3xC408jZJ0pe8ua2mcIEDMGEdSR/+VuhPQaqxZTJPBVhazVc1P9gRyS26SdOohL85UmEc4duqlJOOlXOFuwOT6dvoiUcdQtzuPp1pzA/LHueqm9yQG9mlT0Df8uY/A+rwM4l/ypTP/o0+5GCM9jJf9bl/z0DpGWheCJY+LZbIGeBUOpg0Gx1+KZsD9ivWJ0vxNz8zKcAS1i3FgntjqyfY+62jfTR8PW1Y4wdaFan6jSxaaH6WYnvccAo2QHxEAFL91CfnZB1pwF8NAT395N/rXr5XhMHFPoCkSHd2+5u+b62pkvFqqZZ9r24SMQOe9Bl2ZfMew2DyFLMPzwTowHw8onMEXcVKabFs9zQVp66AMf/wlipirNztdguAOTukl79VY+q9+Oqsqr22wIGLmdRnUUuM/hgijiWngMxIppSxLh2uZfhYkaE0cyLDQDqt3EJrUeCjWqrqa+dMRIc4d5ts+btlepIrTet7yJK5rlsFQfJGzaeTz9BN+g+C2ZK8B+2a2Qrz386FvB+elJAkJ2/Agn35oBHB2HobDkF6sRfrXOdH5l+QV7vR2v385RKRtfnmcJeUQcpq5/JTgVwagDJ/FarTN5jFsrBBRTeW3yZ5/CfVNA7NNWxoKhjBaHVIhn/fLT5sFLYzYdCx/uTsusyZmE2d6iqnLS+j1IXNJX/zR0ZD3aGuoUc4MaFZQnN5om4dfpbloe4Roob3BuDhBHTKoYC+nVsyEvDRyiYLEOjJ45/bSwTCfwngYKtNmo3sVTvQ9mqBf0cLdBCn8skp3S/gz324TFm8iJ+t8EWfmglbzNqGxtEtCPZ4le8g/DwaNuMMq0QGEOae2JzdyCNIxfKjlmct0j2bI5cVPbQp3+N8O8HIpOmIdpmyni7Gxdhpw3Kg7+mzqJXSR5Y0pGSHC/5UQbXxUNYj0phx2QwQP05s2POtbwkYF5FtuKl3ZyiZSgG+X1qIfkjQriJ9B8MqH5O4Df/c6DNekL1d6QYnjO0/3LMvY/f/y1+b7nPHI8+1Wqp5jZH8UsuN63SSMdfBEe6x46AG/R+YS/wH78GKekabWu9QQnUJdjXyXiqF4qRebvfcmpQz91anvVz3ggBqCv4sYqCIvP0ysDtMdi36zFErV+8SdUu+NsPDGvdPSCGdLuC25izxb21up2HORmlM5R7yuIW3rCiq8DeLD0OHjqOBZ+IEv9zEkb5fHTJvxoxnZlArtZSBpD6iIDPVDymuK+BsOggZav3K+TytjeD2Gcld5NfyRISFWUIMkZNFQRL8AQpET6RJnG1HSW0CaRfNeomtjCBWIr85wFCrp06j/D1J8B3EyhloZLJJ6ywxt41smXVugxA8LRTO+6lVBOBF14jHQCCUl6u7uiWCe1z4/bC5wQXPwWSljp8NVU8Erp1U9ModNK7W63Pkh0efvgSD5d0nLzbfa0jTdxZ1JkfKsnvYk43Ed+vmXooHZhUeZAIX8ZCizhb1Gfvm02JFwxYXmiYAOp5wkGzweU2I5zo8r5yZFI1r4XibNQs7eAfKGRv3gh8/EuLkX/bdettgPvNsI8ndpQ3kL/V8W2PQN4/hjC9AKCYBeXQG42bRncYZdLe++R2KA1ZdPDxQPF3sxUIKhzmRWqbozrtv310Maorwv6eZJjldlCJwICR9QgcDwDuNj+UFJnX3RWsdIWsUbI1T4wO0sE2sBiMX/OqmiGJEAnBegioistlFyfRvm54h+duNOl/ol1Fva7NoXvsL/wThAWUly7bnc7/Al2bBQlUrmEX46UnKXzYntkZDee7Lx1u1BBkJAj/5BH1YZOPmMCh498rBUiHmc+4uQqebqNSHdOSgC39ESss4u7GNhWj3fi9XXta6UT9wapEMGq0WTg2Kry6xNP2YZ5X8eaapRQc/KzYgz9XjQL6TKpqNuGEbRlmfYvIuoFbnOkZI7RYoGp3YheMs1pQErwOxLzZa9W3Okwx16TSDwPLR0xMdAyogMrOdKN4JSMyNnmOaoVf6PkN+K9fz7RuHtvgjKpuz4vsK5Z2wRneqPrnfu6PkgHcRQrd0SxqCbN23Z/yp8qOcN6XU49iCNEBjztT00tolQ9hCPMSE/eTZ+ioez7m3pJFVks3T5Rk/e+6MeowJWIOv20x6CPS9mhpr1JPwdNFrWdgs19VsobntCpF/rWxksdrYyk=", + "hash": "18877603486899771012708643218593061070120605011629548276683277334885785617595" } }, "Membership_": { - "digest": "262bb745a13f7fac8f6ad1dd8ff13ae5789e0c26b63f4b41a5dbc43c6ac9df85", + "digest": "ceb4d74a167be69adf0ef8ee382ab94b1f719baef157b436124b5899fe2ccb1", "methods": { "addEntry": { "rows": 1355, - "digest": "9bf99a1ea1fa39ed9590c0b0a67bfeb7" + "digest": "112d1f76c4f742d7442c9c2bfa1276ee" }, "isMember": { "rows": 469, @@ -45,8 +45,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "16174092625812619544041752898938261603039773654468059859161573381120826639333" + "data": "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", + "hash": "5535930703920638657421952138351450446443212191906585261070337225336596376335" } }, "HelloWorld": { @@ -63,7 +63,7 @@ } }, "TokenContract": { - "digest": "19aa0f5b442d011f2bbdd68ac189bf91cd017803f0f91a5ab6a61d50e3136c2", + "digest": "1355cefd6d9405bd490d1cdc5138cd1f4c0a874d6c08ccff7577bf1db33ac2f3", "methods": { "init": { "rows": 655, @@ -74,29 +74,29 @@ "digest": "7abe7b8feb9e908aa0a128c57fef92ab" }, "approveBase": { - "rows": 13194, - "digest": "8f8ad42a6586936a28b570877403960c" + "rows": 13244, + "digest": "4dd81f1cc1a06b617677e08883e50fbd" } }, "verificationKey": { - "data": "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", - "hash": "10629507172660088077994796699854359657108723627411391158715881834104929001219" + "data": "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", + "hash": "28008044568252732309294943203765202925095616783374137364924282422136908799323" } }, "Dex": { - "digest": "2289275173dc37af7005d29de2d1bc87ef6633a38aab74653528d62a3ea2c53b", + "digest": "f4d4a511ccaa3e2f4d793387ac561fbe109e700f60dd2ac0c269be720ba0e61", "methods": { "supplyLiquidityBase": { - "rows": 2883, - "digest": "a5811e58fa24f5ebde41076fc94849b7" + "rows": 2884, + "digest": "c39cfd5031cb9e938f9748f4c3504b25" }, "swapX": { - "rows": 1564, - "digest": "f6b047df744d8dcf297bd7a4fb45c812" + "rows": 1565, + "digest": "0d084e4bebf31426806d8d616879afa8" }, "swapY": { - "rows": 1564, - "digest": "3082a30634a7a8da1603a7994b0cd4af" + "rows": 1565, + "digest": "20e2671dea607a4f0d1508faa4483dbe" }, "burnLiquidity": { "rows": 718, @@ -108,8 +108,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "10792977795272926498471289159457543677416779853262843765284042897388782617920" + "data": "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", + "hash": "6889182109222827677998879306887098403215976687219856364080055862647496337052" } }, "Group Primitive": { @@ -250,4 +250,4 @@ "hash": "22296391645667701199385692837408020819294441951376164803693884547686842878882" } } -} +} \ No newline at end of file From 1c2648cf47e1eaf47a7f14ea6d2f4a920139a744 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 10:02:56 +0100 Subject: [PATCH 366/524] minor --- src/lib/account_update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index f701e11656..8595b762a6 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1658,7 +1658,7 @@ const UnfinishedForest = { return { useHash: false, value: [] }; }, - witnessHash(forest: UnfinishedForest) { + witnessHash(forest: UnfinishedForest): UnfinishedForest { let hash = Provable.witness(Field, () => { return UnfinishedForest.finalize(forest).hash; }); From 9a032fc3ed29d5c58eebd3170b03573f3b775af6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 10:26:06 +0100 Subject: [PATCH 367/524] fixup not updating unfinished forest --- src/lib/account_update.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 28cecc9df5..6c807e7c6e 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -690,8 +690,7 @@ class AccountUpdate implements Types.AccountUpdate { } if (accountLike instanceof PublicKey) { accountLike = AccountUpdate.defaultAccountUpdate(accountLike, id); - makeChildAccountUpdate(thisAccountUpdate, accountLike); - // TODO existing API not affecting `UnfinishedForest` + thisAccountUpdate.adopt(accountLike); } if (!accountLike.label) accountLike.label = `${ @@ -1659,7 +1658,11 @@ const UnfinishedForest = { return { useHash: false, value: [] }; }, - witnessHash(forest: UnfinishedForest): UnfinishedForest { + witnessHash(forest: UnfinishedForest): { + readonly useHash: true; + hash: Field; + readonly value: UnfinishedTree[]; + } { let hash = Provable.witness(Field, () => { return UnfinishedForest.finalize(forest).hash; }); From af6956e6070b046423c7427e17728511dcb1623a Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 10:26:22 +0100 Subject: [PATCH 368/524] adapt token contract to new hashing --- src/lib/mina/token/token-contract.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 765fd184f9..64d3e21a2b 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -64,19 +64,22 @@ abstract class TokenContract extends SmartContract { `the supported limit of ${MAX_ACCOUNT_UPDATES}.\n` ); - // skip hashing our child account updates in the method wrapper - // since we just did that in the loop above - this.self.children.callsType = { - type: 'WitnessEquals', - value: updates.hash, - }; - // make top-level updates our children Provable.asProver(() => { updates.data.get().forEach((update) => { this.self.adopt(update.element.accountUpdate.value.get()); }); }); + + // skip hashing our child account updates in the method wrapper + // since we just did that in the loop above + let insideContract = smartContractContext.get(); + if (insideContract) { + insideContract.selfCalls = UnfinishedForest.witnessHash( + insideContract.selfCalls + ); + insideContract.selfCalls.hash.assertEquals(updates.hash); + } } /** From 755e7499eeb293202715b4ef930b3e0d1531bbc6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 10:57:50 +0100 Subject: [PATCH 369/524] add more unfinished forest helpers --- src/lib/account_update.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 6c807e7c6e..2803d7fedc 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1698,6 +1698,18 @@ const UnfinishedForest = { }); }, + pushTree(forest: UnfinishedForest, tree: AccountUpdateTree) { + let value = AccountUpdate.dummy(); + Provable.asProver(() => { + value = tree.accountUpdate.value.get(); + }); + forest.value.push({ + accountUpdate: { useHash: true, hash: tree.accountUpdate.hash, value }, + isDummy: Bool(false), + calls: UnfinishedForest.fromForest(tree.calls), + }); + }, + remove(forest: UnfinishedForest, accountUpdate: AccountUpdate) { // find account update by .id let index = forest.value.findIndex( @@ -1711,6 +1723,22 @@ const UnfinishedForest = { forest.value.splice(index, 1); }, + fromForest(forest: MerkleListBase): UnfinishedForest { + let value: UnfinishedTree[] = []; + Provable.asProver(() => { + value = forest.data.get().map(({ element: tree }) => ({ + accountUpdate: { + useHash: true, + hash: tree.accountUpdate.hash, + value: tree.accountUpdate.value.get(), + }, + isDummy: Bool(false), + calls: UnfinishedForest.fromForest(tree.calls), + })); + }); + return { useHash: true, hash: forest.hash, value }; + }, + finalize(forest: UnfinishedForest): AccountUpdateForest { if (forest.useHash) { let data = Unconstrained.witness(() => From ba293791f88bae887078c2bb14a96818a97fba63 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 10:58:34 +0100 Subject: [PATCH 370/524] fix adaptation of token contract --- src/lib/mina/token/token-contract.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 64d3e21a2b..53e71acc6e 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -65,9 +65,11 @@ abstract class TokenContract extends SmartContract { ); // make top-level updates our children + // TODO: this must not be necessary once we move everything to `selfCalls` Provable.asProver(() => { updates.data.get().forEach((update) => { - this.self.adopt(update.element.accountUpdate.value.get()); + let accountUpdate = update.element.accountUpdate.value.get(); + this.self.adopt(accountUpdate); }); }); @@ -75,10 +77,7 @@ abstract class TokenContract extends SmartContract { // since we just did that in the loop above let insideContract = smartContractContext.get(); if (insideContract) { - insideContract.selfCalls = UnfinishedForest.witnessHash( - insideContract.selfCalls - ); - insideContract.selfCalls.hash.assertEquals(updates.hash); + insideContract.selfCalls = UnfinishedForest.fromForest(updates); } } From 3e533a77af4ccd7da6fc640a44e6330ca5e6cc08 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 15:33:34 +0100 Subject: [PATCH 371/524] minor --- src/lib/mina/token/token-contract.unit-test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts index 6bac61c881..b49d6b8190 100644 --- a/src/lib/mina/token/token-contract.unit-test.ts +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -37,9 +37,11 @@ class ExampleTokenContract extends TokenContract { let Local = Mina.LocalBlockchain({ proofsEnabled: false }); Mina.setActiveInstance(Local); -let { publicKey: sender, privateKey: senderKey } = Local.testAccounts[0]; -let { publicKey: tokenAddress, privateKey: tokenKey } = Local.testAccounts[1]; -let { publicKey: otherAddress } = Local.testAccounts[2]; +let [ + { publicKey: sender, privateKey: senderKey }, + { publicKey: tokenAddress, privateKey: tokenKey }, + { publicKey: otherAddress }, +] = Local.testAccounts; let token = new ExampleTokenContract(tokenAddress); let tokenId = token.token.id; From d69c73a3fe0752ef43816d84a7c9cd67abee347c Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 15:33:42 +0100 Subject: [PATCH 372/524] add failing unit test --- .../mina/account-update-layout.unit-test.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/lib/mina/account-update-layout.unit-test.ts diff --git a/src/lib/mina/account-update-layout.unit-test.ts b/src/lib/mina/account-update-layout.unit-test.ts new file mode 100644 index 0000000000..91fbfcab67 --- /dev/null +++ b/src/lib/mina/account-update-layout.unit-test.ts @@ -0,0 +1,42 @@ +import { Mina } from '../../index.js'; +import { AccountUpdate } from '../account_update.js'; +import { UInt64 } from '../int.js'; +import { SmartContract, method } from '../zkapp.js'; + +// smart contract which creates an account update that has a child of its own + +class NestedCall extends SmartContract { + @method deposit() { + const payerUpdate = AccountUpdate.createSigned(this.sender); + payerUpdate.send({ to: this.address, amount: UInt64.one }); + } +} + +// setup + +let Local = Mina.LocalBlockchain({ proofsEnabled: true }); +Mina.setActiveInstance(Local); + +let [ + { publicKey: sender, privateKey: senderKey }, + { publicKey: zkappAddress, privateKey: zkappKey }, +] = Local.testAccounts; + +await NestedCall.compile(); +let zkapp = new NestedCall(zkappAddress); + +// deploy zkapp + +await (await Mina.transaction(sender, () => zkapp.deploy())) + .sign([zkappKey, senderKey]) + .send(); + +// deposit call +let balanceBefore = Mina.getBalance(zkappAddress); + +let depositTx = await Mina.transaction(sender, () => zkapp.deposit()); +console.log(depositTx.toPretty()); +await depositTx.prove(); +await depositTx.sign([senderKey]).send(); + +Mina.getBalance(zkappAddress).assertEquals(balanceBefore.add(1)); From 765b0bda42c9da3f62a187b4462e298608b4acd9 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 6 Feb 2024 08:13:51 -0800 Subject: [PATCH 373/524] chore(mina-signer/package.json): bump version from 2.1.2 to 3.0.0 for the new major release --- src/mina-signer/package-lock.json | 4 ++-- src/mina-signer/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mina-signer/package-lock.json b/src/mina-signer/package-lock.json index 325fd4e3e0..55178b872b 100644 --- a/src/mina-signer/package-lock.json +++ b/src/mina-signer/package-lock.json @@ -1,12 +1,12 @@ { "name": "mina-signer", - "version": "2.1.2", + "version": "3.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mina-signer", - "version": "2.1.2", + "version": "3.0.0", "license": "Apache-2.0", "dependencies": { "blakejs": "^1.2.1", diff --git a/src/mina-signer/package.json b/src/mina-signer/package.json index 1eda8112fd..b6d951a18c 100644 --- a/src/mina-signer/package.json +++ b/src/mina-signer/package.json @@ -1,7 +1,7 @@ { "name": "mina-signer", "description": "Node API for signing transactions on various networks for Mina Protocol", - "version": "2.1.2", + "version": "3.0.0", "type": "module", "scripts": { "build": "tsc -p ../../tsconfig.mina-signer.json", From 118cc06cf85b0267cc3ea3503dbe34c4c3d606ac Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 17:49:17 +0100 Subject: [PATCH 374/524] move smart contract context and start account update layout class --- src/lib/account_update.ts | 26 ++-------- .../mina/account-update-layout.unit-test.ts | 1 + src/lib/mina/smart-contract-context.ts | 49 +++++++++++++++++++ src/lib/mina/token/token-contract.ts | 2 +- src/lib/zkapp.ts | 45 +++++++---------- 5 files changed, 75 insertions(+), 48 deletions(-) create mode 100644 src/lib/mina/smart-contract-context.ts diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 2803d7fedc..271637705a 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -46,7 +46,6 @@ import { prefixes, protocolVersions, } from '../bindings/crypto/constants.js'; -import { Context } from './global-context.js'; import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; @@ -58,9 +57,9 @@ import { genericHash, MerkleList, MerkleListBase, - withHashes, } from './provable-types/merkle-list.js'; import { Hashed } from './provable-types/packed.js'; +import { smartContractContext } from './mina/smart-contract-context.js'; // external API export { @@ -72,7 +71,6 @@ export { }; // internal API export { - smartContractContext, SetOrKeep, Permission, Preconditions, @@ -82,7 +80,6 @@ export { ZkappCommand, addMissingSignatures, addMissingProofs, - ZkappStateLength, Events, Actions, TokenId, @@ -91,31 +88,19 @@ export { createChildAccountUpdate, AccountUpdatesLayout, zkAppProver, - SmartContractContext, dummySignature, LazyProof, AccountUpdateTree, UnfinishedForest, + UnfinishedTree, hashAccountUpdate, HashedAccountUpdate, }; -const ZkappStateLength = 8; - const TransactionVersion = { current: () => UInt32.from(protocolVersions.txnVersion), }; -type SmartContractContext = { - this: SmartContract; - methodCallDepth: number; - selfUpdate: AccountUpdate; - selfCalls: UnfinishedForest; -}; -let smartContractContext = Context.create({ - default: null, -}); - type ZkappProverData = { transaction: ZkappCommand; accountUpdate: AccountUpdate; @@ -799,7 +784,7 @@ class AccountUpdate implements Types.AccountUpdate { } else { receiver = AccountUpdate.defaultAccountUpdate(to, this.body.tokenId); receiver.label = `${this.label ?? 'Unlabeled'}.send()`; - this.approve(receiver); + this.adopt(receiver); } // Sub the amount from the sender's account @@ -814,8 +799,7 @@ class AccountUpdate implements Types.AccountUpdate { } /** - * Makes an {@link AccountUpdate} a child-{@link AccountUpdate} of this and - * approves it. + * Makes an {@link AccountUpdate} a child of this and approves it. */ approve( childUpdate: AccountUpdate, @@ -832,7 +816,7 @@ class AccountUpdate implements Types.AccountUpdate { } /** - * Makes an {@link AccountUpdate} a child-{@link AccountUpdate} of this. + * Makes an {@link AccountUpdate} a child of this. */ adopt(childUpdate: AccountUpdate) { makeChildAccountUpdate(this, childUpdate); diff --git a/src/lib/mina/account-update-layout.unit-test.ts b/src/lib/mina/account-update-layout.unit-test.ts index 91fbfcab67..f75f503194 100644 --- a/src/lib/mina/account-update-layout.unit-test.ts +++ b/src/lib/mina/account-update-layout.unit-test.ts @@ -32,6 +32,7 @@ await (await Mina.transaction(sender, () => zkapp.deploy())) .send(); // deposit call + let balanceBefore = Mina.getBalance(zkappAddress); let depositTx = await Mina.transaction(sender, () => zkapp.deposit()); diff --git a/src/lib/mina/smart-contract-context.ts b/src/lib/mina/smart-contract-context.ts new file mode 100644 index 0000000000..a11b87141c --- /dev/null +++ b/src/lib/mina/smart-contract-context.ts @@ -0,0 +1,49 @@ +import type { SmartContract } from '../zkapp.js'; +import type { + AccountUpdate, + UnfinishedForest, + UnfinishedTree, +} from '../account_update.js'; +import { Context } from '../global-context.js'; + +export { smartContractContext, SmartContractContext }; + +type SmartContractContext = { + this: SmartContract; + selfUpdate: AccountUpdate; + selfLayout: AccountUpdateLayout; + selfCalls: UnfinishedForest; +}; +let smartContractContext = Context.create({ + default: null, +}); + +const SmartContractContext = { + enter(self: SmartContract, selfUpdate: AccountUpdate) { + let context: SmartContractContext = { + this: self, + selfUpdate, + selfLayout: new AccountUpdateLayout(), + selfCalls: { useHash: false, value: [] }, + }; + let id = smartContractContext.enter(context); + return { id, context }; + }, + leave(id: number) { + smartContractContext.leave(id); + }, + stepOutside() { + return smartContractContext.enter(null); + }, + get() { + return smartContractContext.get(); + }, +}; + +class AccountUpdateLayout { + map: Map; + + constructor() { + this.map = new Map(); + } +} diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 53e71acc6e..df391553c5 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -9,10 +9,10 @@ import { AccountUpdateTree, HashedAccountUpdate, Permissions, - smartContractContext, } from '../../account_update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; import { TokenAccountUpdateIterator } from './forest-iterator.js'; +import { smartContractContext } from '../smart-contract-context.js'; export { TokenContract }; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index f91731ce53..d1cbc72ba1 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -9,13 +9,10 @@ import { Permissions, Actions, SetOrKeep, - smartContractContext, TokenId, ZkappCommand, zkAppProver, ZkappPublicInput, - ZkappStateLength, - SmartContractContext, LazyProof, CallForest, UnfinishedForest, @@ -62,6 +59,8 @@ import { import { Cache } from './proof-system/cache.js'; import { assert } from './gadgets/common.js'; import { SmartContractBase } from './mina/smart-contract-base.js'; +import { SmartContractContext } from './mina/smart-contract-context.js'; +import { ZkappStateLength } from './mina/mina-instance.js'; // external API export { @@ -164,15 +163,12 @@ function wrapMethod( } }); - let insideContract = smartContractContext.get(); + let insideContract = SmartContractContext.get(); if (!insideContract) { - const context: SmartContractContext = { - this: this, - methodCallDepth: 0, - selfUpdate: selfAccountUpdate(this, methodName), - selfCalls: UnfinishedForest.empty(), - }; - let id = smartContractContext.enter(context); + const { id, context } = SmartContractContext.enter( + this, + selfAccountUpdate(this, methodName) + ); try { if (inCompile() || inProver() || inAnalyze()) { // important to run this with a fresh accountUpdate everytime, otherwise compile messes up our circuits @@ -304,20 +300,17 @@ function wrapMethod( return result; } } finally { - smartContractContext.leave(id); + SmartContractContext.leave(id); } } // if we're here, this method was called inside _another_ smart contract method let parentAccountUpdate = insideContract.this.self; - let methodCallDepth = insideContract.methodCallDepth; - let innerContext: SmartContractContext = { - this: this, - methodCallDepth: methodCallDepth + 1, - selfUpdate: selfAccountUpdate(this, methodName), - selfCalls: UnfinishedForest.empty(), - }; - let id = smartContractContext.enter(innerContext); + + let { id, context: innerContext } = SmartContractContext.enter( + this, + selfAccountUpdate(this, methodName) + ); try { // if the call result is not undefined but there's no known returnType, the returnType was probably not annotated properly, // so we have to explain to the user how to do that @@ -443,7 +436,7 @@ function wrapMethod( accountUpdate.body.callData.assertEquals(callData); return result; } finally { - smartContractContext.leave(id); + SmartContractContext.leave(id); } }; } @@ -800,7 +793,7 @@ super.init(); */ get self(): AccountUpdate { let inTransaction = Mina.currentTransaction.has(); - let inSmartContract = smartContractContext.get(); + let inSmartContract = SmartContractContext.get(); if (!inTransaction && !inSmartContract) { // TODO: it's inefficient to return a fresh account update everytime, would be better to return a constant "non-writable" account update, // or even expose the .get() methods independently of any account update (they don't need one) @@ -1141,8 +1134,8 @@ super.init(); throw err; } let id: number; - let insideSmartContract = !!smartContractContext.get(); - if (insideSmartContract) id = smartContractContext.enter(null); + let insideSmartContract = !!SmartContractContext.get(); + if (insideSmartContract) id = SmartContractContext.stepOutside(); try { for (let methodIntf of methodIntfs) { let accountUpdate: AccountUpdate; @@ -1169,7 +1162,7 @@ super.init(); if (printSummary) console.log(methodIntf.methodName, summary()); } } finally { - if (insideSmartContract) smartContractContext.leave(id!); + if (insideSmartContract) SmartContractContext.leave(id!); } } return methodMetadata; @@ -1463,7 +1456,7 @@ type DeployArgs = | undefined; function Account(address: PublicKey, tokenId?: Field) { - if (smartContractContext.get()) { + if (SmartContractContext.get()) { return AccountUpdate.create(address, tokenId).account; } else { return AccountUpdate.defaultAccountUpdate(address, tokenId).account; From 2bb10ac30995f118a9e7c3c8691b39c41ed9dc32 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 18:00:27 +0100 Subject: [PATCH 375/524] move some of it back --- src/lib/account_update.ts | 44 +++++++++++++++++++++++++- src/lib/mina/smart-contract-context.ts | 32 +------------------ src/lib/zkapp.ts | 2 +- 3 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 271637705a..257c5fdfc9 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -59,7 +59,10 @@ import { MerkleListBase, } from './provable-types/merkle-list.js'; import { Hashed } from './provable-types/packed.js'; -import { smartContractContext } from './mina/smart-contract-context.js'; +import { + SmartContractContext, + smartContractContext, +} from './mina/smart-contract-context.js'; // external API export { @@ -91,10 +94,12 @@ export { dummySignature, LazyProof, AccountUpdateTree, + AccountUpdateLayout, UnfinishedForest, UnfinishedTree, hashAccountUpdate, HashedAccountUpdate, + SmartContractContext, }; const TransactionVersion = { @@ -1754,6 +1759,43 @@ function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { return { accountUpdate, isDummy: node.isDummy, calls }; } +const SmartContractContext = { + enter(self: SmartContract, selfUpdate: AccountUpdate) { + let context: SmartContractContext = { + this: self, + selfUpdate, + selfLayout: new AccountUpdateLayout(), + selfCalls: { useHash: false, value: [] }, + }; + let id = smartContractContext.enter(context); + return { id, context }; + }, + leave(id: number) { + smartContractContext.leave(id); + }, + stepOutside() { + return smartContractContext.enter(null); + }, + get() { + return smartContractContext.get(); + }, +}; + +class AccountUpdateLayout { + map: Map; + + constructor() { + this.map = new Map(); + } + + pushChild(parent: AccountUpdate, child: AccountUpdate) { + // let insideContract = smartContractContext.get(); + // if (insideContract && insideContract.selfUpdate.id === this.id) { + // UnfinishedForest.push(insideContract.selfCalls, childUpdate); + // } + } +} + const CallForest = { // similar to Mina_base.ZkappCommand.Call_forest.to_account_updates_list // takes a list of accountUpdates, which each can have children, so they form a "forest" (list of trees) diff --git a/src/lib/mina/smart-contract-context.ts b/src/lib/mina/smart-contract-context.ts index a11b87141c..eae9e58b13 100644 --- a/src/lib/mina/smart-contract-context.ts +++ b/src/lib/mina/smart-contract-context.ts @@ -2,7 +2,7 @@ import type { SmartContract } from '../zkapp.js'; import type { AccountUpdate, UnfinishedForest, - UnfinishedTree, + AccountUpdateLayout, } from '../account_update.js'; import { Context } from '../global-context.js'; @@ -17,33 +17,3 @@ type SmartContractContext = { let smartContractContext = Context.create({ default: null, }); - -const SmartContractContext = { - enter(self: SmartContract, selfUpdate: AccountUpdate) { - let context: SmartContractContext = { - this: self, - selfUpdate, - selfLayout: new AccountUpdateLayout(), - selfCalls: { useHash: false, value: [] }, - }; - let id = smartContractContext.enter(context); - return { id, context }; - }, - leave(id: number) { - smartContractContext.leave(id); - }, - stepOutside() { - return smartContractContext.enter(null); - }, - get() { - return smartContractContext.get(); - }, -}; - -class AccountUpdateLayout { - map: Map; - - constructor() { - this.map = new Map(); - } -} diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index d1cbc72ba1..2f7b1f9b87 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -17,6 +17,7 @@ import { CallForest, UnfinishedForest, AccountUpdateForest, + SmartContractContext, } from './account_update.js'; import { cloneCircuitValue, @@ -59,7 +60,6 @@ import { import { Cache } from './proof-system/cache.js'; import { assert } from './gadgets/common.js'; import { SmartContractBase } from './mina/smart-contract-base.js'; -import { SmartContractContext } from './mina/smart-contract-context.js'; import { ZkappStateLength } from './mina/mina-instance.js'; // external API From 521e2a572bd0684ee83bfd76838d7fe4419efc54 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 6 Feb 2024 08:57:41 -0800 Subject: [PATCH 376/524] feat(release.yml): add steps to delete existing release tag and branch This change allows for the re-creation of a release if the previous attempt failed or was incorrect. It ensures that the release process can be re-run without manual intervention. --- .github/workflows/release.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7901f04858..3597ffda93 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: - name: Bump patch version run: | - git fetch --prune --unshallow + git fetch --prune --unshallow --tags --force NEW_VERSION=$(npm version patch) echo "New version: $NEW_VERSION" echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_ENV @@ -57,6 +57,19 @@ jobs: git add CHANGELOG.md git commit -m "Update CHANGELOG for new version $NEW_VERSION" + - name: Delete existing release tag + run: | + if git rev-parse $NEW_VERSION >/dev/null 2>&1; then + git tag -d $NEW_VERSION + git push origin :refs/tags/$NEW_VERSION + fi + + - name: Delete existing release branch + run: | + if git ls-remote --heads origin release/${NEW_VERSION} | grep release/${NEW_VERSION}; then + git push origin --delete release/${NEW_VERSION} + fi + - name: Create new release branch run: | NEW_BRANCH="release/${NEW_VERSION}" From db253350997287e548090312b504293dad11408e Mon Sep 17 00:00:00 2001 From: robrobbins Date: Tue, 6 Feb 2024 18:01:19 +0000 Subject: [PATCH 377/524] revert mina-signer test paths to dist. add dev readme section for building mina-signer and running single tests. fixup fixup --- README-dev.md | 15 +++++++++++++++ src/mina-signer/tests/client.test.ts | 2 +- src/mina-signer/tests/keypair.test.ts | 2 +- src/mina-signer/tests/message.test.ts | 4 ++-- src/mina-signer/tests/payment.test.ts | 4 ++-- src/mina-signer/tests/rosetta.test.ts | 2 +- src/mina-signer/tests/stake-delegation.test.ts | 4 ++-- 7 files changed, 24 insertions(+), 9 deletions(-) diff --git a/README-dev.md b/README-dev.md index 327717330d..6c1850ca9d 100644 --- a/README-dev.md +++ b/README-dev.md @@ -141,8 +141,23 @@ npm run test npm run test:unit ``` +#### Running Mina-Signer Tests +`npm run build` is not recursive, thus in order for the Mina Signer tests to run you must execute the following from the root directory: + +```sh +cd src/mina-signer +npm run build +cd ../.. +``` + This runs all the unit tests and provides you with a summary of the test results. +Note that you can run individual jest tests via the command: + +```sh +NODE_OPTIONS=--experimental-vm-modules npx jest +``` + You can also run integration tests by running: ```sh diff --git a/src/mina-signer/tests/client.test.ts b/src/mina-signer/tests/client.test.ts index c55eaa0012..ca0cce3934 100644 --- a/src/mina-signer/tests/client.test.ts +++ b/src/mina-signer/tests/client.test.ts @@ -1,4 +1,4 @@ -import Client from '../../../dist/node/mina-signer/MinaSigner.js'; +import Client from '../dist/node/mina-signer/MinaSigner.js'; describe('Client Class Initialization', () => { let client; diff --git a/src/mina-signer/tests/keypair.test.ts b/src/mina-signer/tests/keypair.test.ts index 30fa986b88..5e3e48dd59 100644 --- a/src/mina-signer/tests/keypair.test.ts +++ b/src/mina-signer/tests/keypair.test.ts @@ -1,4 +1,4 @@ -import Client from '../../../dist/node/mina-signer/MinaSigner.js'; +import Client from '../dist/node/mina-signer/MinaSigner.js'; describe('Keypair', () => { let client: Client; diff --git a/src/mina-signer/tests/message.test.ts b/src/mina-signer/tests/message.test.ts index 00c09b662a..26011eabff 100644 --- a/src/mina-signer/tests/message.test.ts +++ b/src/mina-signer/tests/message.test.ts @@ -1,5 +1,5 @@ -import Client from '../../../dist/node/mina-signer/MinaSigner.js'; -import type { PrivateKey } from '../../../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../dist/node/mina-signer/MinaSigner.js'; +import type { PrivateKey } from '../dist/node/mina-signer/src/TSTypes.js'; describe('Message', () => { describe('Mainnet network', () => { diff --git a/src/mina-signer/tests/payment.test.ts b/src/mina-signer/tests/payment.test.ts index a5a34878ce..9a7d52804d 100644 --- a/src/mina-signer/tests/payment.test.ts +++ b/src/mina-signer/tests/payment.test.ts @@ -1,5 +1,5 @@ -import Client from '../../../dist/node/mina-signer/MinaSigner.js'; -import type { Keypair } from '../../../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../dist/node/mina-signer/MinaSigner.js'; +import type { Keypair } from '../dist/node/mina-signer/src/TSTypes.js'; describe('Payment', () => { describe('Mainnet network', () => { diff --git a/src/mina-signer/tests/rosetta.test.ts b/src/mina-signer/tests/rosetta.test.ts index 1410ddeab8..5db94f21e1 100644 --- a/src/mina-signer/tests/rosetta.test.ts +++ b/src/mina-signer/tests/rosetta.test.ts @@ -1,4 +1,4 @@ -import Client from '../../../dist/node/mina-signer/MinaSigner.js'; +import Client from '../dist/node/mina-signer/MinaSigner.js'; describe('Rosetta', () => { let client: Client; diff --git a/src/mina-signer/tests/stake-delegation.test.ts b/src/mina-signer/tests/stake-delegation.test.ts index d7ac8f9231..ea59458e1f 100644 --- a/src/mina-signer/tests/stake-delegation.test.ts +++ b/src/mina-signer/tests/stake-delegation.test.ts @@ -1,5 +1,5 @@ -import Client from '../../../dist/node/mina-signer/MinaSigner.js'; -import type { Keypair } from '../../../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../dist/node/mina-signer/MinaSigner.js'; +import type { Keypair } from '../dist/node/mina-signer/src/TSTypes.js'; describe('Stake Delegation', () => { describe('Mainnet network', () => { From b67432f8f6a3eb66a8ad528e011c8be4215b984c Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Tue, 6 Feb 2024 21:24:27 +0100 Subject: [PATCH 378/524] Apply suggestions from code review --- README-dev.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README-dev.md b/README-dev.md index 6c1850ca9d..389be17d9d 100644 --- a/README-dev.md +++ b/README-dev.md @@ -141,8 +141,7 @@ npm run test npm run test:unit ``` -#### Running Mina-Signer Tests -`npm run build` is not recursive, thus in order for the Mina Signer tests to run you must execute the following from the root directory: +In order for the mina-signer tests to run you must also build from inside its subdirectory: ```sh cd src/mina-signer @@ -155,7 +154,7 @@ This runs all the unit tests and provides you with a summary of the test results Note that you can run individual jest tests via the command: ```sh -NODE_OPTIONS=--experimental-vm-modules npx jest +./jest ``` You can also run integration tests by running: From 996be22626a0afb857ab236554f11b46e3240141 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 22:10:42 +0100 Subject: [PATCH 379/524] minor --- src/lib/mina/transaction-logic/ledger.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/mina/transaction-logic/ledger.ts b/src/lib/mina/transaction-logic/ledger.ts index 266054ca50..daf7d3cb0d 100644 --- a/src/lib/mina/transaction-logic/ledger.ts +++ b/src/lib/mina/transaction-logic/ledger.ts @@ -2,10 +2,11 @@ * A ledger of accounts - simple model of a local blockchain. */ import { PublicKey } from '../../signature.js'; -import { type AccountUpdate, TokenId } from '../../account_update.js'; +import type { AccountUpdate } from '../../account_update.js'; import { Account, newAccount } from '../account.js'; import { Field } from '../../field.js'; import { applyAccountUpdate } from './apply.js'; +import { Types } from '../../../bindings/mina-transaction/types.js'; export { SimpleLedger }; @@ -20,7 +21,10 @@ class SimpleLedger { return new SimpleLedger(); } - exists({ publicKey, tokenId = TokenId.default }: InputAccountId): boolean { + exists({ + publicKey, + tokenId = Types.TokenId.empty(), + }: InputAccountId): boolean { return this.accounts.has(accountId({ publicKey, tokenId })); } @@ -30,7 +34,7 @@ class SimpleLedger { load({ publicKey, - tokenId = TokenId.default, + tokenId = Types.TokenId.empty(), }: InputAccountId): Account | undefined { let id = accountId({ publicKey, tokenId }); let account = this.accounts.get(id); From d2dd5fe834214a6acd3592621c62db867200f92f Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 22:13:13 +0100 Subject: [PATCH 380/524] develop account update layout enough to fix unit test --- src/lib/account_update.ts | 68 ++++++++++++++++++-------- src/lib/mina/smart-contract-context.ts | 6 ++- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 257c5fdfc9..58dc0cf35a 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -60,6 +60,7 @@ import { } from './provable-types/merkle-list.js'; import { Hashed } from './provable-types/packed.js'; import { + accountUpdates, SmartContractContext, smartContractContext, } from './mina/smart-contract-context.js'; @@ -812,12 +813,7 @@ class AccountUpdate implements Types.AccountUpdate { ) { makeChildAccountUpdate(this, childUpdate); AccountUpdate.witnessChildren(childUpdate, layout, { skipCheck: true }); - - // TODO: this is not as general as approve suggests - let insideContract = smartContractContext.get(); - if (insideContract && insideContract.selfUpdate.id === this.id) { - UnfinishedForest.push(insideContract.selfCalls, childUpdate); - } + accountUpdates()?.pushChild(this, childUpdate); } /** @@ -825,12 +821,7 @@ class AccountUpdate implements Types.AccountUpdate { */ adopt(childUpdate: AccountUpdate) { makeChildAccountUpdate(this, childUpdate); - - // TODO: this is not as general as adopt suggests - let insideContract = smartContractContext.get(); - if (insideContract && insideContract.selfUpdate.id === this.id) { - UnfinishedForest.push(insideContract.selfCalls, childUpdate); - } + accountUpdates()?.pushChild(this, childUpdate); } get balance() { @@ -1745,6 +1736,26 @@ const UnfinishedForest = { } return finalForest; }, + + print(forest: UnfinishedForest) { + let indent = 0; + let layout = ''; + + let toPretty = (a: UnfinishedForest) => { + indent += 2; + for (let tree of a.value) { + layout += + ' '.repeat(indent) + + `( ${tree.accountUpdate.value.label || ''} )` + + '\n'; + toPretty(tree.calls); + } + indent -= 2; + }; + + toPretty(forest); + console.log(layout); + }, }; function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { @@ -1761,11 +1772,16 @@ function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { const SmartContractContext = { enter(self: SmartContract, selfUpdate: AccountUpdate) { + let selfCalls = UnfinishedForest.empty(); let context: SmartContractContext = { this: self, selfUpdate, - selfLayout: new AccountUpdateLayout(), - selfCalls: { useHash: false, value: [] }, + selfLayout: new AccountUpdateLayout({ + accountUpdate: { useHash: false, value: selfUpdate }, + isDummy: Bool(false), + calls: selfCalls, + }), + selfCalls, }; let id = smartContractContext.enter(context); return { id, context }; @@ -1782,17 +1798,29 @@ const SmartContractContext = { }; class AccountUpdateLayout { - map: Map; + map: Map; - constructor() { + constructor(root: UnfinishedTree) { this.map = new Map(); + this.map.set(root.accountUpdate.value.id, root); + } + + getNode(update: AccountUpdate) { + let node = this.map.get(update.id); + if (node !== undefined) return node; + node = { + accountUpdate: { useHash: false, value: update }, + isDummy: update.isDummy(), + calls: UnfinishedForest.empty(), + }; + this.map.set(update.id, node); + return node; } pushChild(parent: AccountUpdate, child: AccountUpdate) { - // let insideContract = smartContractContext.get(); - // if (insideContract && insideContract.selfUpdate.id === this.id) { - // UnfinishedForest.push(insideContract.selfCalls, childUpdate); - // } + let parentNode = this.getNode(parent); + let childNode = this.getNode(child); + parentNode.calls.value.push(childNode); } } diff --git a/src/lib/mina/smart-contract-context.ts b/src/lib/mina/smart-contract-context.ts index eae9e58b13..de8b383657 100644 --- a/src/lib/mina/smart-contract-context.ts +++ b/src/lib/mina/smart-contract-context.ts @@ -6,7 +6,7 @@ import type { } from '../account_update.js'; import { Context } from '../global-context.js'; -export { smartContractContext, SmartContractContext }; +export { smartContractContext, SmartContractContext, accountUpdates }; type SmartContractContext = { this: SmartContract; @@ -17,3 +17,7 @@ type SmartContractContext = { let smartContractContext = Context.create({ default: null, }); + +function accountUpdates() { + return smartContractContext.get()?.selfLayout; +} From c8d439ad0fbce363854af7ddea58bd7ea01a7dff Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 6 Feb 2024 22:26:45 +0100 Subject: [PATCH 381/524] continue expanding account update layout --- src/lib/account_update.ts | 23 +++++++++++++++++++++-- src/lib/mina/token/token-contract.ts | 10 +++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 58dc0cf35a..4d6ae0b9b9 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1799,13 +1799,21 @@ const SmartContractContext = { class AccountUpdateLayout { map: Map; + root: UnfinishedTree; constructor(root: UnfinishedTree) { this.map = new Map(); this.map.set(root.accountUpdate.value.id, root); + this.root = root; } - getNode(update: AccountUpdate) { + getNode(update: AccountUpdate | UnfinishedTree): UnfinishedTree { + if (!(update instanceof AccountUpdate)) { + if (!this.map.has(update.accountUpdate.value.id)) { + this.map.set(update.accountUpdate.value.id, update); + } + return update; + } let node = this.map.get(update.id); if (node !== undefined) return node; node = { @@ -1817,11 +1825,22 @@ class AccountUpdateLayout { return node; } - pushChild(parent: AccountUpdate, child: AccountUpdate) { + pushChild(parent: AccountUpdate | UnfinishedTree, child: AccountUpdate) { let parentNode = this.getNode(parent); let childNode = this.getNode(child); parentNode.calls.value.push(childNode); } + + setChildren( + parent: AccountUpdate | UnfinishedTree, + children: AccountUpdateForest + ) { + let parentNode = this.getNode(parent); + parentNode.calls = UnfinishedForest.fromForest(children); + } + setTopLevel(children: AccountUpdateForest) { + this.root.calls = UnfinishedForest.fromForest(children); + } } const CallForest = { diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index df391553c5..e2e58bde0e 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -12,7 +12,10 @@ import { } from '../../account_update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; import { TokenAccountUpdateIterator } from './forest-iterator.js'; -import { smartContractContext } from '../smart-contract-context.js'; +import { + accountUpdates, + smartContractContext, +} from '../smart-contract-context.js'; export { TokenContract }; @@ -75,10 +78,7 @@ abstract class TokenContract extends SmartContract { // skip hashing our child account updates in the method wrapper // since we just did that in the loop above - let insideContract = smartContractContext.get(); - if (insideContract) { - insideContract.selfCalls = UnfinishedForest.fromForest(updates); - } + accountUpdates()?.setTopLevel(updates); } /** From 5e2b75645abe8273058df9237a8b1cacad5b4560 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 09:27:38 +0100 Subject: [PATCH 382/524] fixup set children --- src/lib/account_update.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 4d6ae0b9b9..d91d372257 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1626,7 +1626,8 @@ type UnfinishedForest = HashOrValue; type UnfinishedTree = { accountUpdate: HashOrValue; isDummy: Bool; - calls: UnfinishedForest; + // `children` must be readonly since it's referenced in each child's siblings + readonly calls: UnfinishedForest; }; type HashOrValue = @@ -1798,8 +1799,8 @@ const SmartContractContext = { }; class AccountUpdateLayout { - map: Map; - root: UnfinishedTree; + readonly map: Map; + readonly root: UnfinishedTree; constructor(root: UnfinishedTree) { this.map = new Map(); @@ -1836,10 +1837,12 @@ class AccountUpdateLayout { children: AccountUpdateForest ) { let parentNode = this.getNode(parent); - parentNode.calls = UnfinishedForest.fromForest(children); + // we're not allowed to switch parentNode.calls, it must stay the same reference + // so we mutate it in place + Object.assign(parentNode.calls, UnfinishedForest.fromForest(children)); } setTopLevel(children: AccountUpdateForest) { - this.root.calls = UnfinishedForest.fromForest(children); + this.setChildren(this.root, children); } } From ef4b154a5cf4470efa88b4122297be52d20d6d60 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 10:28:52 +0100 Subject: [PATCH 383/524] change name to avoid clash with ts keywored --- src/lib/testing/constraint-system.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/lib/testing/constraint-system.ts b/src/lib/testing/constraint-system.ts index 1f8ad9ba52..064b49d863 100644 --- a/src/lib/testing/constraint-system.ts +++ b/src/lib/testing/constraint-system.ts @@ -19,7 +19,7 @@ export { not, and, or, - satisfies, + fulfills, equals, contains, allConstant, @@ -186,7 +186,7 @@ function or(...tests: ConstraintSystemTest[]): ConstraintSystemTest { /** * General test */ -function satisfies( +function fulfills( label: string, run: (cs: Gate[], inputs: TypeAndValue[]) => boolean ): ConstraintSystemTest { @@ -276,10 +276,7 @@ function ifNotAllConstant(test: ConstraintSystemTest): ConstraintSystemTest { /** * Test whether constraint system is empty. */ -const isEmpty = satisfies( - 'constraint system is empty', - (cs) => cs.length === 0 -); +const isEmpty = fulfills('constraint system is empty', (cs) => cs.length === 0); /** * Modifies a test so that it runs on the constraint system with generic gates filtered out. From 599caca29978abf9ab7089fa58c17950968b26b9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 10:35:32 +0100 Subject: [PATCH 384/524] introduce siblings to be able to disattach --- src/lib/account_update.ts | 74 ++++++++++++++++++++++++++------------- src/lib/zkapp.ts | 7 +++- 2 files changed, 56 insertions(+), 25 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index d91d372257..1b6cdf8892 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1628,54 +1628,57 @@ type UnfinishedTree = { isDummy: Bool; // `children` must be readonly since it's referenced in each child's siblings readonly calls: UnfinishedForest; + siblings?: UnfinishedForest; }; - -type HashOrValue = - | { readonly useHash: true; hash: Field; readonly value: T } - | { readonly useHash: false; value: T }; +type UseHash = { readonly useHash: true; hash: Field; readonly value: T }; +type PlainValue = { readonly useHash: false; value: T }; +type HashOrValue = UseHash | PlainValue; const UnfinishedForest = { - empty(): UnfinishedForest { + empty(): PlainValue { return { useHash: false, value: [] }; }, - witnessHash(forest: UnfinishedForest): { - readonly useHash: true; - hash: Field; - readonly value: UnfinishedTree[]; - } { + setHash(forest: UnfinishedForest, hash: Field): UseHash { + return Object.assign(forest, { useHash: true as const, hash }); + }, + + witnessHash(forest: UnfinishedForest): UseHash { let hash = Provable.witness(Field, () => { return UnfinishedForest.finalize(forest).hash; }); - return { useHash: true, hash, value: forest.value }; + return UnfinishedForest.setHash(forest, hash); }, fromArray(updates: AccountUpdate[], useHash = false): UnfinishedForest { if (useHash) { - let forest = UnfinishedForest.empty(); + let forest: UnfinishedForest = UnfinishedForest.empty(); Provable.asProver(() => (forest = UnfinishedForest.fromArray(updates))); return UnfinishedForest.witnessHash(forest); } - let nodes = updates.map((update): UnfinishedTree => { + let forest = UnfinishedForest.empty(); + forest.value = updates.map((update): UnfinishedTree => { return { accountUpdate: { useHash: false, value: update }, isDummy: update.isDummy(), calls: UnfinishedForest.fromArray(update.children.accountUpdates), + siblings: forest, }; }); - return { useHash: false, value: nodes }; + return forest; }, push( forest: UnfinishedForest, accountUpdate: AccountUpdate, - calls?: UnfinishedForest + children?: UnfinishedForest ) { forest.value.push({ accountUpdate: { useHash: false, value: accountUpdate }, isDummy: accountUpdate.isDummy(), - calls: calls ?? UnfinishedForest.empty(), + calls: children ?? UnfinishedForest.empty(), + siblings: forest, }); }, @@ -1688,6 +1691,7 @@ const UnfinishedForest = { accountUpdate: { useHash: true, hash: tree.accountUpdate.hash, value }, isDummy: Bool(false), calls: UnfinishedForest.fromForest(tree.calls), + siblings: forest, }); }, @@ -1705,9 +1709,9 @@ const UnfinishedForest = { }, fromForest(forest: MerkleListBase): UnfinishedForest { - let value: UnfinishedTree[] = []; + let unfinished = UnfinishedForest.empty(); Provable.asProver(() => { - value = forest.data.get().map(({ element: tree }) => ({ + unfinished.value = forest.data.get().map(({ element: tree }) => ({ accountUpdate: { useHash: true, hash: tree.accountUpdate.hash, @@ -1715,9 +1719,11 @@ const UnfinishedForest = { }, isDummy: Bool(false), calls: UnfinishedForest.fromForest(tree.calls), + siblings: unfinished, })); }); - return { useHash: true, hash: forest.hash, value }; + Object.assign(unfinished, { useHash: true, hash: forest.hash }); + return unfinished; }, finalize(forest: UnfinishedForest): AccountUpdateForest { @@ -1808,7 +1814,14 @@ class AccountUpdateLayout { this.root = root; } - getNode(update: AccountUpdate | UnfinishedTree): UnfinishedTree { + get(update: AccountUpdate) { + return this.map.get(update.id); + } + + getOrCreate( + update: AccountUpdate | UnfinishedTree, + siblings?: UnfinishedForest + ): UnfinishedTree { if (!(update instanceof AccountUpdate)) { if (!this.map.has(update.accountUpdate.value.id)) { this.map.set(update.accountUpdate.value.id, update); @@ -1821,29 +1834,42 @@ class AccountUpdateLayout { accountUpdate: { useHash: false, value: update }, isDummy: update.isDummy(), calls: UnfinishedForest.empty(), + siblings, }; this.map.set(update.id, node); return node; } pushChild(parent: AccountUpdate | UnfinishedTree, child: AccountUpdate) { - let parentNode = this.getNode(parent); - let childNode = this.getNode(child); + let parentNode = this.getOrCreate(parent); + let childNode = this.getOrCreate(child, parentNode.calls); parentNode.calls.value.push(childNode); } + pushTopLevel(child: AccountUpdate) { + this.pushChild(this.root, child); + } + setChildren( parent: AccountUpdate | UnfinishedTree, children: AccountUpdateForest ) { - let parentNode = this.getNode(parent); - // we're not allowed to switch parentNode.calls, it must stay the same reference + let parentNode = this.getOrCreate(parent); + // we're not allowed to switch parentNode.children, it must stay the same reference // so we mutate it in place Object.assign(parentNode.calls, UnfinishedForest.fromForest(children)); } + setTopLevel(children: AccountUpdateForest) { this.setChildren(this.root, children); } + + disattach(update: AccountUpdate) { + let node = this.get(update); + if (node?.siblings === undefined) return; + UnfinishedForest.remove(node.siblings, update); + node.siblings = undefined; + } } const CallForest = { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 2f7b1f9b87..385c089d21 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -410,10 +410,15 @@ function wrapMethod( // nothing is asserted about them -- it's the callee's task to check their children accountUpdate.children.callsType = { type: 'Witness' }; parentAccountUpdate.children.accountUpdates.push(accountUpdate); + + let grandchildren = UnfinishedForest.fromArray( + accountUpdate.children.accountUpdates, + true + ); UnfinishedForest.push( insideContract.selfCalls, accountUpdate, - UnfinishedForest.fromArray(accountUpdate.children.accountUpdates, true) + grandchildren ); // assert that we really called the right zkapp From 43a34d50dda29abebc1c411deb59765f38b7f826 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 11:03:37 +0100 Subject: [PATCH 385/524] override `instanceof`? seriously? have I become a hard-core OO programmer now who will bend over backwards just to use OO idioms? --- src/lib/account_update.ts | 7 +++++-- src/lib/provable-types/merkle-list.ts | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 1b6cdf8892..adb089d9df 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1852,12 +1852,15 @@ class AccountUpdateLayout { setChildren( parent: AccountUpdate | UnfinishedTree, - children: AccountUpdateForest + children: AccountUpdateForest | UnfinishedForest ) { let parentNode = this.getOrCreate(parent); // we're not allowed to switch parentNode.children, it must stay the same reference // so we mutate it in place - Object.assign(parentNode.calls, UnfinishedForest.fromForest(children)); + if (children instanceof AccountUpdateForest) { + children = UnfinishedForest.fromForest(children); + } + Object.assign(parentNode.calls, children); } setTopLevel(children: AccountUpdateForest) { diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index a06e19c563..701dc314ae 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -202,10 +202,10 @@ class MerkleList implements MerkleListBase { from: (array: T[]) => MerkleList; provable: ProvableHashable>; } { - return class MerkleList_ extends MerkleList { + class MerkleListTBase extends MerkleList { static _innerProvable = type; - static _provable = provableFromClass(MerkleList_, { + static _provable = provableFromClass(MerkleListTBase, { hash: Field, data: Unconstrained.provable, }) as ProvableHashable>; @@ -225,6 +225,12 @@ class MerkleList implements MerkleListBase { assert(this._provable !== undefined, 'MerkleList not initialized'); return this._provable; } + } + // override `instanceof` for subclasses + return class MerkleListT extends MerkleListTBase { + static [Symbol.hasInstance](x: any): boolean { + return x instanceof MerkleListTBase; + } }; } From 54f596ffd6ed76e9b0dc2cf36c49fd2d4b409712 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 12:04:51 +0100 Subject: [PATCH 386/524] write to au layout in zkapp calls --- src/lib/zkapp.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 385c089d21..f115c7e468 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -411,14 +411,10 @@ function wrapMethod( accountUpdate.children.callsType = { type: 'Witness' }; parentAccountUpdate.children.accountUpdates.push(accountUpdate); - let grandchildren = UnfinishedForest.fromArray( - accountUpdate.children.accountUpdates, - true - ); - UnfinishedForest.push( - insideContract.selfCalls, + insideContract.selfLayout.pushTopLevel(accountUpdate); + insideContract.selfLayout.setChildren( accountUpdate, - grandchildren + UnfinishedForest.fromArray(accountUpdate.children.accountUpdates, true) ); // assert that we really called the right zkapp From 71a420050d631f31015785299510844c61b08df3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 12:12:29 +0100 Subject: [PATCH 387/524] get from layout in token contract --- src/lib/mina/token/token-contract.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index e2e58bde0e..d5f17d8a47 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -12,10 +12,7 @@ import { } from '../../account_update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; import { TokenAccountUpdateIterator } from './forest-iterator.js'; -import { - accountUpdates, - smartContractContext, -} from '../smart-contract-context.js'; +import { accountUpdates } from '../smart-contract-context.js'; export { TokenContract }; @@ -153,15 +150,11 @@ function finalizeAccountUpdates(updates: AccountUpdate[]): AccountUpdateForest { function finalizeAccountUpdate(update: AccountUpdate): AccountUpdateTree { let calls: AccountUpdateForest; - let insideContract = smartContractContext.get(); - if (insideContract) { - let node = insideContract.selfCalls.value.find( - (c) => c.accountUpdate.value.id === update.id - ); - if (node !== undefined) { - calls = UnfinishedForest.finalize(node.calls); - } + let node = accountUpdates()?.get(update); + if (node !== undefined) { + calls = UnfinishedForest.finalize(node.calls); } + calls ??= AccountUpdateForest.fromArray(update.children.accountUpdates, { skipDummies: true, }); From 4c71d09009151422fa8c1a6a231035754c2ee57b Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 12:12:52 +0100 Subject: [PATCH 388/524] disattach from layout in unlink --- src/lib/account_update.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index adb089d9df..67d9049489 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1149,10 +1149,7 @@ class AccountUpdate implements Types.AccountUpdate { */ static unlink(accountUpdate: AccountUpdate) { // TODO duplicate logic - let insideContract = smartContractContext.get(); - if (insideContract) { - UnfinishedForest.remove(insideContract.selfCalls, accountUpdate); - } + accountUpdates()?.disattach(accountUpdate); let siblings = accountUpdate.parent?.children.accountUpdates ?? currentTransaction()?.accountUpdates; From 6b6b64361b9a6f2f991ce05fe2af8235658535e3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 12:20:45 +0100 Subject: [PATCH 389/524] remove selfCalls --- src/lib/account_update.ts | 5 ++--- src/lib/mina/smart-contract-context.ts | 7 +------ src/lib/mina/token/token-contract.ts | 2 +- src/lib/zkapp.ts | 4 +++- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 67d9049489..9ce41f88c4 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1776,16 +1776,15 @@ function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { const SmartContractContext = { enter(self: SmartContract, selfUpdate: AccountUpdate) { - let selfCalls = UnfinishedForest.empty(); + let calls = UnfinishedForest.empty(); let context: SmartContractContext = { this: self, selfUpdate, selfLayout: new AccountUpdateLayout({ accountUpdate: { useHash: false, value: selfUpdate }, isDummy: Bool(false), - calls: selfCalls, + calls, }), - selfCalls, }; let id = smartContractContext.enter(context); return { id, context }; diff --git a/src/lib/mina/smart-contract-context.ts b/src/lib/mina/smart-contract-context.ts index de8b383657..603bab43c4 100644 --- a/src/lib/mina/smart-contract-context.ts +++ b/src/lib/mina/smart-contract-context.ts @@ -1,9 +1,5 @@ import type { SmartContract } from '../zkapp.js'; -import type { - AccountUpdate, - UnfinishedForest, - AccountUpdateLayout, -} from '../account_update.js'; +import type { AccountUpdate, AccountUpdateLayout } from '../account_update.js'; import { Context } from '../global-context.js'; export { smartContractContext, SmartContractContext, accountUpdates }; @@ -12,7 +8,6 @@ type SmartContractContext = { this: SmartContract; selfUpdate: AccountUpdate; selfLayout: AccountUpdateLayout; - selfCalls: UnfinishedForest; }; let smartContractContext = Context.create({ default: null, diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index d5f17d8a47..3eaa92a209 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -65,7 +65,7 @@ abstract class TokenContract extends SmartContract { ); // make top-level updates our children - // TODO: this must not be necessary once we move everything to `selfCalls` + // TODO: this must not be necessary once we move everything to `selfLayout` Provable.asProver(() => { updates.data.get().forEach((update) => { let accountUpdate = update.element.accountUpdate.value.get(); diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index f115c7e468..453335867e 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -215,7 +215,9 @@ function wrapMethod( ProofAuthorization.setKind(accountUpdate); debugPublicInput(accountUpdate); - let calls = UnfinishedForest.finalize(context.selfCalls); + let calls = UnfinishedForest.finalize( + context.selfLayout.root.calls + ); checkPublicInput(publicInput, accountUpdate, calls); // check the self accountUpdate right after calling the method From 3884e6e2b0bfa7e74af927520425cfec9bdf4ccf Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 12:40:53 +0100 Subject: [PATCH 390/524] finalize layout --- src/lib/account_update.ts | 11 +++++++++++ src/lib/mina/token/token-contract.ts | 9 +++------ src/lib/zkapp.ts | 4 +--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 9ce41f88c4..8dcab458ab 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1869,6 +1869,17 @@ class AccountUpdateLayout { UnfinishedForest.remove(node.siblings, update); node.siblings = undefined; } + + finalizeAndRemove(update: AccountUpdate) { + let node = this.get(update); + if (node === undefined) return; + this.disattach(update); + return UnfinishedForest.finalize(node.calls); + } + + finalize() { + return UnfinishedForest.finalize(this.root.calls); + } } const CallForest = { diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 3eaa92a209..afcbeeb6b2 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -148,17 +148,14 @@ function finalizeAccountUpdates(updates: AccountUpdate[]): AccountUpdateForest { } function finalizeAccountUpdate(update: AccountUpdate): AccountUpdateTree { - let calls: AccountUpdateForest; + let calls = accountUpdates()?.finalizeAndRemove(update); - let node = accountUpdates()?.get(update); - if (node !== undefined) { - calls = UnfinishedForest.finalize(node.calls); - } + // TODO remove once everything lives in `selfLayout` + AccountUpdate.unlink(update); calls ??= AccountUpdateForest.fromArray(update.children.accountUpdates, { skipDummies: true, }); - AccountUpdate.unlink(update); return { accountUpdate: HashedAccountUpdate.hash(update), calls }; } diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 453335867e..3266ac7026 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -215,9 +215,7 @@ function wrapMethod( ProofAuthorization.setKind(accountUpdate); debugPublicInput(accountUpdate); - let calls = UnfinishedForest.finalize( - context.selfLayout.root.calls - ); + let calls = context.selfLayout.finalize(); checkPublicInput(publicInput, accountUpdate, calls); // check the self accountUpdate right after calling the method From 1ae3d640f5489714ef28d214087d50b193116452 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 13:39:33 +0100 Subject: [PATCH 391/524] don't store variables in aux data --- src/lib/account_update.ts | 4 +++- src/lib/circuit_value.ts | 8 +++++++ src/lib/provable-types/merkle-list.ts | 34 ++++++++++++++++++++------- src/lib/provable-types/packed.ts | 13 +++++++--- src/lib/provable.ts | 10 ++++++++ 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 8dcab458ab..5da8ddbc08 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1766,7 +1766,9 @@ function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { let accountUpdate = node.accountUpdate.useHash ? new HashedAccountUpdate( node.accountUpdate.hash, - Unconstrained.from(node.accountUpdate.value) + Unconstrained.witness(() => + Provable.toConstant(AccountUpdate, node.accountUpdate.value) + ) ) : HashedAccountUpdate.hash(node.accountUpdate.value); diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index a24e58e83a..ab6f802684 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -547,6 +547,14 @@ and Provable.asProver() blocks, which execute outside the proof. /** * Create an `Unconstrained` with the given `value`. + * + * Note: If `T` contains provable types, `Unconstrained.from` is an anti-pattern, + * because it stores witnesses in a space that's intended to be used outside the proof. + * Something like the following should be used instead: + * + * ```ts + * let xWrapped = Unconstrained.witness(() => Provable.toConstant(type, x)); + * ``` */ static from(value: T) { return new Unconstrained(true, value); diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index 701dc314ae..dd62f3a375 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -26,6 +26,12 @@ type WithHash = { previousHash: Field; element: T }; function WithHash(type: ProvableHashable): ProvableHashable> { return Struct({ previousHash: Field, element: type }); } +function toConstant(type: Provable, node: WithHash): WithHash { + return { + previousHash: node.previousHash.toConstant(), + element: Provable.toConstant(type, node.element), + }; +} /** * Common base type for {@link MerkleList} and {@link MerkleListIterator} @@ -88,7 +94,10 @@ class MerkleList implements MerkleListBase { push(element: T) { let previousHash = this.hash; this.hash = this.nextHash(previousHash, element); - this.data.updateAsProver((data) => [{ previousHash, element }, ...data]); + this.data.updateAsProver((data) => [ + toConstant(this.innerProvable, { previousHash, element }), + ...data, + ]); } /** @@ -102,7 +111,9 @@ class MerkleList implements MerkleListBase { previousHash ); this.data.updateAsProver((data) => - condition.toBoolean() ? [{ previousHash, element }, ...data] : data + condition.toBoolean() + ? [toConstant(this.innerProvable, { previousHash, element }), ...data] + : data ); } @@ -161,11 +172,12 @@ class MerkleList implements MerkleListBase { let element = this.pop(); // if the condition is false, we restore the original state - this.data.updateAsProver((data) => - condition.toBoolean() + this.data.updateAsProver((data) => { + let node = { previousHash: this.hash, element }; + return condition.toBoolean() ? data - : [{ previousHash: this.hash, element }, ...data] - ); + : [toConstant(this.innerProvable, node), ...data]; + }); this.hash = Provable.if(condition, this.hash, originalHash); return element; @@ -218,7 +230,10 @@ class MerkleList implements MerkleListBase { static from(array: T[]): MerkleList { let { hash, data } = withHashes(array, nextHash); - return new this({ data: Unconstrained.from(data), hash }); + let unconstrained = Unconstrained.witness(() => + data.map((x) => toConstant(type, x)) + ); + return new this({ data: unconstrained, hash }); } static get provable(): ProvableHashable> { @@ -401,7 +416,10 @@ class MerkleListIterator implements MerkleListIteratorBase { static from(array: T[]): MerkleListIterator { let { hash, data } = withHashes(array, nextHash); - return this.startIterating({ data: Unconstrained.from(data), hash }); + let unconstrained = Unconstrained.witness(() => + data.map((x) => toConstant(type, x)) + ); + return this.startIterating({ data: unconstrained, hash }); } static startIterating({ diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index eb0a04184e..9389de406a 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -79,9 +79,13 @@ class Packed { * Pack a value. */ static pack(x: T): Packed { - let input = this.innerProvable.toInput(x); + let type = this.innerProvable; + let input = type.toInput(x); let packed = packToFields(input); - return new this(packed, Unconstrained.from(x)); + let unconstrained = Unconstrained.witness(() => + Provable.toConstant(type, x) + ); + return new this(packed, unconstrained); } /** @@ -211,7 +215,10 @@ class Hashed { */ static hash(value: T): Hashed { let hash = this._hash(value); - return new this(hash, Unconstrained.from(value)); + let unconstrained = Unconstrained.witness(() => + Provable.toConstant(this.innerProvable, value) + ); + return new this(hash, unconstrained); } /** diff --git a/src/lib/provable.ts b/src/lib/provable.ts index 961f723451..6d1eac028a 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -196,6 +196,16 @@ const Provable = { * ``` */ inCheckedComputation, + + /** + * Returns a constant version of a provable type. + */ + toConstant(type: Provable, value: T) { + return type.fromFields( + type.toFields(value).map((x) => x.toConstant()), + type.toAuxiliary(value) + ); + }, }; function witness = FlexibleProvable>( From f68dfcfb4f641e49b952c5a1ac816e4462064a0e Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 14:33:54 +0100 Subject: [PATCH 392/524] use layout to process zkapp calls --- src/lib/account_update.ts | 56 ++++++++++++++-------------- src/lib/mina/token/token-contract.ts | 1 - src/lib/zkapp.ts | 26 ++++++++----- 3 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 5da8ddbc08..20a6635ae8 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -64,6 +64,7 @@ import { SmartContractContext, smartContractContext, } from './mina/smart-contract-context.js'; +import { assert } from './util/assert.js'; // external API export { @@ -96,8 +97,6 @@ export { LazyProof, AccountUpdateTree, AccountUpdateLayout, - UnfinishedForest, - UnfinishedTree, hashAccountUpdate, HashedAccountUpdate, SmartContractContext, @@ -1590,6 +1589,19 @@ class AccountUpdateForest extends MerkleList.create( return forest; } + + // TODO this comes from paranoia and might be removed later + static assertConstant(forest: MerkleListBase) { + Provable.asProver(() => { + forest.data.get().forEach(({ element: tree }) => { + assert( + Provable.isConstant(AccountUpdate, tree.accountUpdate.value.get()), + 'account update not constant' + ); + AccountUpdateForest.assertConstant(tree.calls); + }); + }); + } } // how to hash a forest @@ -1647,25 +1659,6 @@ const UnfinishedForest = { return UnfinishedForest.setHash(forest, hash); }, - fromArray(updates: AccountUpdate[], useHash = false): UnfinishedForest { - if (useHash) { - let forest: UnfinishedForest = UnfinishedForest.empty(); - Provable.asProver(() => (forest = UnfinishedForest.fromArray(updates))); - return UnfinishedForest.witnessHash(forest); - } - - let forest = UnfinishedForest.empty(); - forest.value = updates.map((update): UnfinishedTree => { - return { - accountUpdate: { useHash: false, value: update }, - isDummy: update.isDummy(), - calls: UnfinishedForest.fromArray(update.children.accountUpdates), - siblings: forest, - }; - }); - return forest; - }, - push( forest: UnfinishedForest, accountUpdate: AccountUpdate, @@ -1705,21 +1698,26 @@ const UnfinishedForest = { forest.value.splice(index, 1); }, - fromForest(forest: MerkleListBase): UnfinishedForest { + fromForest( + forest: MerkleListBase, + recursiveCall = false + ): UnfinishedForest { let unfinished = UnfinishedForest.empty(); Provable.asProver(() => { unfinished.value = forest.data.get().map(({ element: tree }) => ({ accountUpdate: { useHash: true, - hash: tree.accountUpdate.hash, + hash: tree.accountUpdate.hash.toConstant(), value: tree.accountUpdate.value.get(), }, isDummy: Bool(false), - calls: UnfinishedForest.fromForest(tree.calls), + calls: UnfinishedForest.fromForest(tree.calls, true), siblings: unfinished, })); }); - Object.assign(unfinished, { useHash: true, hash: forest.hash }); + if (!recursiveCall) { + Object.assign(unfinished, { useHash: true, hash: forest.hash }); + } return unfinished; }, @@ -1805,6 +1803,7 @@ const SmartContractContext = { class AccountUpdateLayout { readonly map: Map; readonly root: UnfinishedTree; + final?: AccountUpdateForest; constructor(root: UnfinishedTree) { this.map = new Map(); @@ -1879,8 +1878,11 @@ class AccountUpdateLayout { return UnfinishedForest.finalize(node.calls); } - finalize() { - return UnfinishedForest.finalize(this.root.calls); + finalizeChildren() { + let final = UnfinishedForest.finalize(this.root.calls); + this.final = final; + AccountUpdateForest.assertConstant(final); + return final; } } diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index afcbeeb6b2..4617e011c5 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -5,7 +5,6 @@ import { PublicKey } from '../../signature.js'; import { AccountUpdate, AccountUpdateForest, - UnfinishedForest, AccountUpdateTree, HashedAccountUpdate, Permissions, diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 3266ac7026..e7469fce42 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -15,7 +15,6 @@ import { ZkappPublicInput, LazyProof, CallForest, - UnfinishedForest, AccountUpdateForest, SmartContractContext, } from './account_update.js'; @@ -215,7 +214,7 @@ function wrapMethod( ProofAuthorization.setKind(accountUpdate); debugPublicInput(accountUpdate); - let calls = context.selfLayout.finalize(); + let calls = context.selfLayout.finalizeChildren(); checkPublicInput(publicInput, accountUpdate, calls); // check the self accountUpdate right after calling the method @@ -388,12 +387,24 @@ function wrapMethod( Mina.currentTransaction()!.accountUpdates ); } - return { accountUpdate, result: result ?? null }; + // extract callee's account update layout + let children = innerContext.selfLayout.finalizeChildren(); + + return { + accountUpdate, + result: { result: result ?? null, children }, + }; }; // we have to run the called contract inside a witness block, to not affect the caller's circuit - let { accountUpdate, result } = AccountUpdate.witness( - returnType ?? provable(null), + let { + accountUpdate, + result: { result, children }, + } = AccountUpdate.witness<{ result: any; children: AccountUpdateForest }>( + provable({ + result: returnType ?? provable(null), + children: AccountUpdateForest.provable, + }), runCalledContract, { skipCheck: true } ); @@ -412,10 +423,7 @@ function wrapMethod( parentAccountUpdate.children.accountUpdates.push(accountUpdate); insideContract.selfLayout.pushTopLevel(accountUpdate); - insideContract.selfLayout.setChildren( - accountUpdate, - UnfinishedForest.fromArray(accountUpdate.children.accountUpdates, true) - ); + insideContract.selfLayout.setChildren(accountUpdate, children); // assert that we really called the right zkapp accountUpdate.body.publicKey.assertEquals(this.address); From 3778fa2dcc40db4912fffdf96b76d4671a029fbb Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 15:17:21 +0100 Subject: [PATCH 393/524] fix token test (don't use create child account update) --- src/lib/account_update.ts | 2 +- src/lib/token.test.ts | 43 +++++++++++++-------------------------- 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 20a6635ae8..c340b9cc13 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -676,7 +676,7 @@ class AccountUpdate implements Types.AccountUpdate { } if (accountLike instanceof AccountUpdate) { accountLike.tokenId.assertEquals(id); - thisAccountUpdate.approve(accountLike); + thisAccountUpdate.adopt(accountLike); } if (accountLike instanceof PublicKey) { accountLike = AccountUpdate.defaultAccountUpdate(accountLike, id); diff --git a/src/lib/token.test.ts b/src/lib/token.test.ts index 5f8502f902..961d20770e 100644 --- a/src/lib/token.test.ts +++ b/src/lib/token.test.ts @@ -12,7 +12,6 @@ import { Permissions, VerificationKey, Field, - Experimental, Int64, TokenId, } from 'o1js'; @@ -84,38 +83,31 @@ class TokenContract extends SmartContract { this.totalAmountInCirculation.set(newTotalAmountInCirculation); } - @method approveTransferCallback( + @method approveTransfer( senderAddress: PublicKey, receiverAddress: PublicKey, amount: UInt64, - callback: Experimental.Callback + senderAccountUpdate: AccountUpdate ) { - let layout = AccountUpdate.Layout.NoChildren; // Allow only 1 accountUpdate with no children - let senderAccountUpdate = this.approve(callback, layout); - let negativeAmount = Int64.fromObject( - senderAccountUpdate.body.balanceChange - ); + this.self.adopt(senderAccountUpdate); + let negativeAmount = senderAccountUpdate.balanceChange; negativeAmount.assertEquals(Int64.from(amount).neg()); let tokenId = this.token.id; senderAccountUpdate.body.tokenId.assertEquals(tokenId); senderAccountUpdate.body.publicKey.assertEquals(senderAddress); - let receiverAccountUpdate = Experimental.createChildAccountUpdate( - this.self, - receiverAddress, - tokenId - ); + let receiverAccountUpdate = AccountUpdate.create(receiverAddress, tokenId); receiverAccountUpdate.balance.addInPlace(amount); } } class ZkAppB extends SmartContract { - @method approveZkapp(amount: UInt64) { + @method approveSend(amount: UInt64) { this.balance.subInPlace(amount); } } class ZkAppC extends SmartContract { - @method approveZkapp(amount: UInt64) { + @method approveSend(amount: UInt64) { this.balance.subInPlace(amount); } @@ -535,16 +527,13 @@ describe('Token', () => { await tx.sign([feePayerKey, tokenZkappKey]).send(); tx = await Mina.transaction(feePayer, () => { - let approveSendingCallback = Experimental.Callback.create( - zkAppB, - 'approveZkapp', - [UInt64.from(10_000)] - ); - tokenZkapp.approveTransferCallback( + zkAppB.approveSend(UInt64.from(10_000)); + + tokenZkapp.approveTransfer( zkAppBAddress, zkAppCAddress, UInt64.from(10_000), - approveSendingCallback + zkAppB.self ); }); await tx.prove(); @@ -570,16 +559,12 @@ describe('Token', () => { await expect(() => Mina.transaction(feePayer, () => { - let approveSendingCallback = Experimental.Callback.create( - zkAppC, - 'approveIncorrectLayout', - [UInt64.from(10_000)] - ); - tokenZkapp.approveTransferCallback( + zkAppC.approveIncorrectLayout(UInt64.from(10_000)); + tokenZkapp.approveTransfer( zkAppBAddress, zkAppCAddress, UInt64.from(10_000), - approveSendingCallback + zkAppC.self ); }) ).rejects.toThrow(); From 756ade0ea74dcc4d70daa54388527465386a56ee Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 15:22:20 +0100 Subject: [PATCH 394/524] dump vks --- tests/vk-regression/vk-regression.json | 72 +++++++++++++------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 9eb6b8b65e..6e8a9a738d 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -1,22 +1,22 @@ { "Voting_": { - "digest": "385ea3e72a679aad6a48b0734008d80da05f27a17854b9088edc8de420ad907d", + "digest": "232c8378880bbb68117f00d34e442aa804f42255a6f02d343b738b8171d6ce65", "methods": { "voterRegistration": { "rows": 1259, - "digest": "a59bad21fc55ce7d8d099d24cb91d1e6" + "digest": "de76523858f6497e67b359668a14f51d" }, "candidateRegistration": { "rows": 1259, - "digest": "ee209f54f9f7996bae2bf5c0fd4bf9da" + "digest": "737730be005f7d071d339e036353ef7b" }, "approveRegistrations": { - "rows": 1147, - "digest": "748673762a27be8b58ef15084bd49beb" + "rows": 1148, + "digest": "08b40e8d2a9ea7369c371afd2e4e1f54" }, "vote": { - "rows": 1672, - "digest": "0862eef1a5edb8ade4d1d5dab1f76abc" + "rows": 1673, + "digest": "37fdfc514d42b21cbe6f0b914bd88efa" }, "countVotes": { "rows": 5796, @@ -24,16 +24,16 @@ } }, "verificationKey": { - "data": "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", - "hash": "10242427736291135326630618424974868967569697518211103043324256389485118856981" + "data": "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", + "hash": "15776151091008092402671254671490268418790844901048023495826534500844684898367" } }, "Membership_": { - "digest": "3b4598fa0324c27a3535ee0f187419dcd43cde203ac1053df4bcc5a108fd0c52", + "digest": "35aa3fb2f5114cb29b575c70c3cef15e6538c66074876997d0005cd51925cf9", "methods": { "addEntry": { "rows": 1353, - "digest": "657f6ba31355b4d6daa80b84e86a5341" + "digest": "9c4e2d1faf08ecf2ab6d46e29d3e7063" }, "isMember": { "rows": 469, @@ -45,8 +45,8 @@ } }, "verificationKey": { - "data": "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", - "hash": "27077061932938200348860230517448044253628907171665029926735544979854107063304" + "data": "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", + "hash": "26245886907208238580957987655381478268353243870285421747846360000722576368055" } }, "HelloWorld": { @@ -63,53 +63,53 @@ } }, "TokenContract": { - "digest": "1b4cc9f8af1e49b7f727bde338ff93b3aa72d1be175669d51dc7991296ce7dbd", + "digest": "13926c08b6e9f7e6dd936edc1d019db122882c357b6e5f3baf416e06530c608a", "methods": { "init": { - "rows": 342, - "digest": "f9f73a136c5642d519ec0f73e033e6f3" + "rows": 655, + "digest": "d535a589774b32cd36e2d6c3707afd30" }, "init2": { - "rows": 342, - "digest": "c4303618beb22bb040bffacff35286cb" + "rows": 652, + "digest": "4a781d299f945218e93f3d9235549373" }, "approveBase": { - "rows": 13194, - "digest": "8f8ad42a6586936a28b570877403960c" + "rows": 13244, + "digest": "4dd81f1cc1a06b617677e08883e50fbd" } }, "verificationKey": { - "data": "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", - "hash": "20122457815983542378345864836658988429829729096873540353338563912426952908135" + "data": "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", + "hash": "8425314086810278038786818130066444259495740778554070932241514249764148251692" } }, "Dex": { - "digest": "272c50dc8fd51817806dc4e5e0c17e4315d5bad077b23f6b44dac5b999ceb9a2", + "digest": "1adce234400fe0b0ea8b1e625256538660d9ed6e15767e2ac78bb7a59d83a3af", "methods": { "supplyLiquidityBase": { - "rows": 2566, - "digest": "13f3375abe98f94b605c5b079315f066" + "rows": 2882, + "digest": "9930f9a0b82eff6247cded6430c6356f" }, "swapX": { - "rows": 1562, - "digest": "c29354e0f9d6a787a9330730ba19e689" + "rows": 1563, + "digest": "e6c2a260178af42268a1020fc8e6113d" }, "swapY": { - "rows": 1562, - "digest": "9d82e9d413a342d45179001358495e89" + "rows": 1563, + "digest": "8a3959ec3394716f1979cf013a9a4ced" }, "burnLiquidity": { - "rows": 403, - "digest": "7e7c3df3049d99703023a39ab1bfe8e4" + "rows": 718, + "digest": "6c406099fe2d2493bd216f9bbe3ba934" }, "transfer": { - "rows": 414, - "digest": "2a4ce4f790fedf38514d331fa0d57eb0" + "rows": 1044, + "digest": "1df1d01485d388ee364156f940214d23" } }, "verificationKey": { - "data": "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", - "hash": "18952871976004328548139095054727555812687816649131669760777722323942382863169" + "data": "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", + "hash": "23594323045578615602880853374590447788338441806100547122393736875331781522763" } }, "Group Primitive": { @@ -250,4 +250,4 @@ "hash": "22296391645667701199385692837408020819294441951376164803693884547686842878882" } } -} +} \ No newline at end of file From 1928e17f1521ef8f75cefe0e94fa77ef420790e6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 16:07:24 +0100 Subject: [PATCH 395/524] [no children} fromFlatArray which doesn't rely on account update nesting --- src/lib/account_update.ts | 23 +++++- .../mina/token/forest-iterator.unit-test.ts | 80 +++++-------------- 2 files changed, 42 insertions(+), 61 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index c340b9cc13..ea6bbc3cd9 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -49,7 +49,11 @@ import { import { MlArray } from './ml/base.js'; import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; -import { transactionCommitments } from '../mina-signer/src/sign-zkapp-command.js'; +import { + accountUpdatesToCallForest, + CallForest, + transactionCommitments, +} from '../mina-signer/src/sign-zkapp-command.js'; import { currentTransaction } from './mina/transaction-context.js'; import { isSmartContract } from './mina/smart-contract-base.js'; import { activeInstance } from './mina/mina-instance.js'; @@ -1560,6 +1564,23 @@ class AccountUpdateForest extends MerkleList.create( AccountUpdateTree, merkleListHash ) { + static fromFlatArray(updates: AccountUpdate[]): AccountUpdateForest { + let simpleForest = accountUpdatesToCallForest(updates); + return this.fromSimpleForest(simpleForest); + } + + private static fromSimpleForest( + simpleForest: CallForest + ): AccountUpdateForest { + let nodes = simpleForest.map((node) => { + let accountUpdate = HashedAccountUpdate.hash(node.accountUpdate); + let calls = AccountUpdateForest.fromSimpleForest(node.children); + return { accountUpdate, calls }; + }); + return AccountUpdateForest.from(nodes); + } + + // TODO remove static fromArray( updates: AccountUpdate[], { skipDummies = false } = {} diff --git a/src/lib/mina/token/forest-iterator.unit-test.ts b/src/lib/mina/token/forest-iterator.unit-test.ts index 06c9981847..c9a92117a8 100644 --- a/src/lib/mina/token/forest-iterator.unit-test.ts +++ b/src/lib/mina/token/forest-iterator.unit-test.ts @@ -4,7 +4,6 @@ import { TokenAccountUpdateIterator } from './forest-iterator.js'; import { AccountUpdate, AccountUpdateForest, - CallForest, TokenId, hashAccountUpdate, } from '../../account_update.js'; @@ -13,7 +12,6 @@ import { Pickles } from '../../../snarky.js'; import { accountUpdatesToCallForest, callForestHash, - CallForest as SimpleCallForest, } from '../../../mina-signer/src/sign-zkapp-command.js'; import assert from 'assert'; import { Field, Bool } from '../../core.js'; @@ -60,40 +58,19 @@ test.custom({ timeBudget: 1000 })( let forestBigint = accountUpdatesToCallForest(flatUpdatesBigint); let expectedHash = callForestHash(forestBigint); - // convert to o1js-style list of nested `AccountUpdate`s let flatUpdates = flatUpdatesBigint.map(accountUpdateFromBigint); - let updates = callForestToNestedArray( - accountUpdatesToCallForest(flatUpdates) - ); - - let forest = AccountUpdateForest.fromArray(updates); + let forest = AccountUpdateForest.fromFlatArray(flatUpdates); forest.hash.assertEquals(expectedHash); } ); -// can recover flat account updates from nested updates -// this is here to assert that we compute `updates` correctly in the other tests - -test(flatAccountUpdates, (flatUpdates) => { - let updates = callForestToNestedArray( - accountUpdatesToCallForest(flatUpdates) - ); - let flatUpdates2 = CallForest.toFlatList(updates, false); - let n = flatUpdates.length; - for (let i = 0; i < n; i++) { - assert.deepStrictEqual(flatUpdates2[i], flatUpdates[i]); - } -}); - // traverses the top level of a call forest in correct order // i.e., CallForestArray works test.custom({ timeBudget: 1000 })(flatAccountUpdates, (flatUpdates) => { // prepare call forest from flat account updates - let updates = callForestToNestedArray( - accountUpdatesToCallForest(flatUpdates) - ); - let forest = AccountUpdateForest.fromArray(updates).startIterating(); + let forest = AccountUpdateForest.fromFlatArray(flatUpdates).startIterating(); + let updates = flatUpdates.filter((u) => u.body.callDepth === 0); // step through top-level by calling forest.next() repeatedly let n = updates.length; @@ -116,10 +93,7 @@ test.custom({ timeBudget: 5000 })(flatAccountUpdates, (flatUpdates) => { let tokenId = TokenId.default; // prepare forest iterator from flat account updates - let updates = callForestToNestedArray( - accountUpdatesToCallForest(flatUpdates) - ); - let forest = AccountUpdateForest.fromArray(updates); + let forest = AccountUpdateForest.fromFlatArray(flatUpdates); let forestIterator = TokenAccountUpdateIterator.create(forest, tokenId); // step through forest iterator and compare against expected updates @@ -154,28 +128,26 @@ test.custom({ timeBudget: 5000 })( }); let tokenId = TokenId.derive(tokenOwner); - // prepare forest iterator from flat account updates - let updates = callForestToNestedArray( - accountUpdatesToCallForest(flatUpdates) - ); - // make all top-level updates inaccessible - updates.forEach((u, i) => { - if (i % 3 === 0) { - u.body.mayUseToken = AccountUpdate.MayUseToken.No; - } else if (i % 3 === 1) { - u.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; - } else { - u.body.publicKey = tokenOwner; - u.body.tokenId = TokenId.default; - } - }); + flatUpdates + .filter((u) => u.body.callDepth === 0) + .forEach((u, i) => { + if (i % 3 === 0) { + u.body.mayUseToken = AccountUpdate.MayUseToken.No; + } else if (i % 3 === 1) { + u.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; + } else { + u.body.publicKey = tokenOwner; + u.body.tokenId = TokenId.default; + } + }); - let forest = AccountUpdateForest.fromArray(updates); + // prepare forest iterator from flat account updates + let forest = AccountUpdateForest.fromFlatArray(flatUpdates); let forestIterator = TokenAccountUpdateIterator.create(forest, tokenId); // step through forest iterator and compare against expected updates - let expectedUpdates = updates; + let expectedUpdates = flatUpdates.filter((u) => u.body.callDepth === 0); let n = flatUpdates.length; for (let i = 0; i < n; i++) { @@ -214,10 +186,7 @@ test.custom({ timeBudget: 5000 })( }); // prepare forest iterator from flat account updates - let updates = callForestToNestedArray( - accountUpdatesToCallForest(flatUpdates) - ); - let forest = AccountUpdateForest.fromArray(updates); + let forest = AccountUpdateForest.fromFlatArray(flatUpdates); let forestIterator = TokenAccountUpdateIterator.create(forest, tokenId); // step through forest iterator and compare against expected updates @@ -239,15 +208,6 @@ function accountUpdateFromBigint(a: TypesBigint.AccountUpdate): AccountUpdate { return AccountUpdate.fromJSON(TypesBigint.AccountUpdate.toJSON(a)); } -function callForestToNestedArray( - forest: SimpleCallForest -): AccountUpdate[] { - return forest.map(({ accountUpdate, children }) => { - accountUpdate.children.accountUpdates = callForestToNestedArray(children); - return accountUpdate; - }); -} - function assertEqual(actual: AccountUpdate, expected: AccountUpdate) { let actualHash = hashAccountUpdate(actual).toBigInt(); let expectedHash = hashAccountUpdate(expected).toBigInt(); From 0f917b1e77e69ebaf590709047f2079fe5775fa3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 17:56:37 +0100 Subject: [PATCH 396/524] minor fix --- src/lib/account_update.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index ea6bbc3cd9..a10c19bb02 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1797,14 +1797,13 @@ function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { const SmartContractContext = { enter(self: SmartContract, selfUpdate: AccountUpdate) { - let calls = UnfinishedForest.empty(); let context: SmartContractContext = { this: self, selfUpdate, selfLayout: new AccountUpdateLayout({ accountUpdate: { useHash: false, value: selfUpdate }, isDummy: Bool(false), - calls, + calls: UnfinishedForest.empty(), }), }; let id = smartContractContext.enter(context); @@ -1836,10 +1835,7 @@ class AccountUpdateLayout { return this.map.get(update.id); } - getOrCreate( - update: AccountUpdate | UnfinishedTree, - siblings?: UnfinishedForest - ): UnfinishedTree { + getOrCreate(update: AccountUpdate | UnfinishedTree): UnfinishedTree { if (!(update instanceof AccountUpdate)) { if (!this.map.has(update.accountUpdate.value.id)) { this.map.set(update.accountUpdate.value.id, update); @@ -1852,7 +1848,6 @@ class AccountUpdateLayout { accountUpdate: { useHash: false, value: update }, isDummy: update.isDummy(), calls: UnfinishedForest.empty(), - siblings, }; this.map.set(update.id, node); return node; @@ -1860,7 +1855,8 @@ class AccountUpdateLayout { pushChild(parent: AccountUpdate | UnfinishedTree, child: AccountUpdate) { let parentNode = this.getOrCreate(parent); - let childNode = this.getOrCreate(child, parentNode.calls); + let childNode = this.getOrCreate(child); + childNode.siblings = parentNode.calls; parentNode.calls.value.push(childNode); } From d5ec3fb1fc716446ad707ef62adf23e4a60da683 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 18:19:29 +0100 Subject: [PATCH 397/524] introduce (not yet used) layout for Mina.transaction --- src/lib/account_update.ts | 18 ++++++++++-------- src/lib/mina.ts | 2 ++ src/lib/mina/smart-contract-context.ts | 9 ++++++++- src/lib/mina/transaction-context.ts | 3 ++- src/lib/zkapp.ts | 4 ++++ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index a10c19bb02..3684d17695 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1800,11 +1800,7 @@ const SmartContractContext = { let context: SmartContractContext = { this: self, selfUpdate, - selfLayout: new AccountUpdateLayout({ - accountUpdate: { useHash: false, value: selfUpdate }, - isDummy: Bool(false), - calls: UnfinishedForest.empty(), - }), + selfLayout: new AccountUpdateLayout(selfUpdate), }; let id = smartContractContext.enter(context); return { id, context }; @@ -1825,10 +1821,16 @@ class AccountUpdateLayout { readonly root: UnfinishedTree; final?: AccountUpdateForest; - constructor(root: UnfinishedTree) { + constructor(root?: AccountUpdate) { this.map = new Map(); - this.map.set(root.accountUpdate.value.id, root); - this.root = root; + root ??= AccountUpdate.dummy(); + let rootTree: UnfinishedTree = { + accountUpdate: { useHash: false, value: root }, + isDummy: Bool(false), + calls: UnfinishedForest.empty(), + }; + this.map.set(root.id, rootTree); + this.root = rootTree; } get(update: AccountUpdate) { diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 22c83fe9bb..e39c2b0dec 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -15,6 +15,7 @@ import { Actions, Events, dummySignature, + AccountUpdateLayout, } from './account_update.js'; import * as Fetch from './fetch.js'; import { assertPreconditionInvariants, NetworkValue } from './precondition.js'; @@ -182,6 +183,7 @@ function createTransaction( let transactionId = currentTransaction.enter({ sender, accountUpdates: [], + layout: new AccountUpdateLayout(), fetchMode, isFinalRunOutsideCircuit, numberOfRuns, diff --git a/src/lib/mina/smart-contract-context.ts b/src/lib/mina/smart-contract-context.ts index 603bab43c4..a276a2ff1e 100644 --- a/src/lib/mina/smart-contract-context.ts +++ b/src/lib/mina/smart-contract-context.ts @@ -1,6 +1,7 @@ import type { SmartContract } from '../zkapp.js'; import type { AccountUpdate, AccountUpdateLayout } from '../account_update.js'; import { Context } from '../global-context.js'; +import { currentTransaction } from './transaction-context.js'; export { smartContractContext, SmartContractContext, accountUpdates }; @@ -14,5 +15,11 @@ let smartContractContext = Context.create({ }); function accountUpdates() { - return smartContractContext.get()?.selfLayout; + // in a smart contract, return the layout currently created in the contract call + let layout = smartContractContext.get()?.selfLayout; + + // if not in a smart contract but in a transaction, return the layout of the transaction + layout ??= currentTransaction()?.layout; + + return layout; } diff --git a/src/lib/mina/transaction-context.ts b/src/lib/mina/transaction-context.ts index a3bb040b1c..e007079522 100644 --- a/src/lib/mina/transaction-context.ts +++ b/src/lib/mina/transaction-context.ts @@ -1,4 +1,4 @@ -import type { AccountUpdate } from '../account_update.js'; +import type { AccountUpdate, AccountUpdateLayout } from '../account_update.js'; import type { PublicKey } from '../signature.js'; import { Context } from '../global-context.js'; @@ -8,6 +8,7 @@ type FetchMode = 'fetch' | 'cached' | 'test'; type CurrentTransaction = { sender?: PublicKey; accountUpdates: AccountUpdate[]; + layout: AccountUpdateLayout; fetchMode: FetchMode; isFinalRunOutsideCircuit: boolean; numberOfRuns: 0 | 1 | undefined; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index e7469fce42..1fbdae114c 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -17,6 +17,7 @@ import { CallForest, AccountUpdateForest, SmartContractContext, + AccountUpdateLayout, } from './account_update.js'; import { cloneCircuitValue, @@ -60,6 +61,7 @@ import { Cache } from './proof-system/cache.js'; import { assert } from './gadgets/common.js'; import { SmartContractBase } from './mina/smart-contract-base.js'; import { ZkappStateLength } from './mina/mina-instance.js'; +import { accountUpdates } from './mina/smart-contract-context.js'; // external API export { @@ -176,6 +178,8 @@ function wrapMethod( let txId = Mina.currentTransaction.enter({ sender: proverData?.transaction.feePayer.body.publicKey, accountUpdates: [], + // TODO could pass an update with the fee payer's content here? probably not bc it's not accessed + layout: new AccountUpdateLayout(), fetchMode: inProver() ? 'cached' : 'test', isFinalRunOutsideCircuit: false, numberOfRuns: undefined, From f3e9169b30ba5bf4f2299d3eba0bfd0a229b1066 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 19:47:21 +0100 Subject: [PATCH 398/524] match transaction.accountUpdates everywhere --- src/lib/account_update.ts | 61 ++++++++++++++++++++++++++++++++++++++- src/lib/mina.ts | 1 + src/lib/zkapp.ts | 27 +++++++++-------- 3 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 3684d17695..950817dac1 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1122,7 +1122,8 @@ class AccountUpdate implements Types.AccountUpdate { } > AccountUpdate.create()`; } else { currentTransaction()?.accountUpdates.push(accountUpdate); - accountUpdate.label = `Mina.transaction > AccountUpdate.create()`; + currentTransaction()?.layout.pushTopLevel(accountUpdate); + accountUpdate.label = `Mina.transaction() > AccountUpdate.create()`; } return accountUpdate; } @@ -1145,6 +1146,7 @@ class AccountUpdate implements Types.AccountUpdate { if (!updates.find((update) => update.id === accountUpdate.id)) { updates.push(accountUpdate); } + currentTransaction.get().layout.pushTopLevel(accountUpdate); } } /** @@ -1779,6 +1781,39 @@ const UnfinishedForest = { toPretty(forest); console.log(layout); }, + + toFlatList( + forest: UnfinishedForest, + mutate = true, + depth = 0 + ): AccountUpdate[] { + let flatUpdates: AccountUpdate[] = []; + for (let node of forest.value) { + if (node.isDummy.toBoolean()) continue; + let update = node.accountUpdate.value; + if (mutate) update.body.callDepth = depth; + let children = UnfinishedForest.toFlatList(node.calls, mutate, depth + 1); + flatUpdates.push(update, ...children); + } + return flatUpdates; + }, + + toConstantInPlace(forest: UnfinishedForest) { + for (let node of forest.value) { + // `as any` to override readonly - this method is explicit about its mutability + (node.accountUpdate as any).value = Provable.toConstant( + AccountUpdate, + node.accountUpdate.value + ); + if (node.accountUpdate.useHash) { + node.accountUpdate.hash = node.accountUpdate.hash.toConstant(); + } + UnfinishedForest.toConstantInPlace(node.calls); + } + if (forest.useHash) { + forest.hash = forest.hash.toConstant(); + } + }, }; function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { @@ -1858,6 +1893,11 @@ class AccountUpdateLayout { pushChild(parent: AccountUpdate | UnfinishedTree, child: AccountUpdate) { let parentNode = this.getOrCreate(parent); let childNode = this.getOrCreate(child); + if (childNode.siblings === parentNode.calls) return; + assert( + childNode.siblings === undefined, + 'AccountUpdateLayout.pushChild(): child already has another parent.' + ); childNode.siblings = parentNode.calls; parentNode.calls.value.push(childNode); } @@ -1903,6 +1943,25 @@ class AccountUpdateLayout { AccountUpdateForest.assertConstant(final); return final; } + + toFlatList({ mutate }: { mutate: boolean }) { + return UnfinishedForest.toFlatList(this.root.calls, mutate); + } + + forEachPredecessor( + update: AccountUpdate, + callback: (update: AccountUpdate) => void + ) { + let updates = this.toFlatList({ mutate: false }); + for (let otherUpdate of updates) { + if (otherUpdate.id === update.id) return; + callback(otherUpdate); + } + } + + toConstantInPlace() { + UnfinishedForest.toConstantInPlace(this.root.calls); + } } const CallForest = { diff --git a/src/lib/mina.ts b/src/lib/mina.ts index e39c2b0dec..376f5ba7bd 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -208,6 +208,7 @@ function createTransaction( tx.accountUpdates = CallForest.map(tx.accountUpdates, (a) => toConstant(AccountUpdate, a) ); + tx.layout.toConstantInPlace(); }); }); } else { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 1fbdae114c..666d11785c 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -243,7 +243,8 @@ function wrapMethod( // called smart contract at the top level, in a transaction! // => attach ours to the current list of account updates let accountUpdate = context.selfUpdate; - Mina.currentTransaction()?.accountUpdates.push(accountUpdate); + Mina.currentTransaction.get().accountUpdates.push(accountUpdate); + Mina.currentTransaction.get().layout.pushTopLevel(accountUpdate); // first, clone to protect against the method modifying arguments! // TODO: double-check that this works on all possible inputs, e.g. CircuitValue, o1js primitives @@ -297,7 +298,7 @@ function wrapMethod( memoized, blindingValue, }, - Mina.currentTransaction()!.accountUpdates + Mina.currentTransaction.get().layout ); } return result; @@ -388,7 +389,7 @@ function wrapMethod( memoized, blindingValue: constantBlindingValue, }, - Mina.currentTransaction()!.accountUpdates + Mina.currentTransaction()?.layout ?? new AccountUpdateLayout() ); } // extract callee's account update layout @@ -1526,7 +1527,10 @@ const Reducer: (< ) as any; const ProofAuthorization = { - setKind({ body, id }: AccountUpdate, priorAccountUpdates?: AccountUpdate[]) { + setKind( + { body, id }: AccountUpdate, + priorAccountUpdates?: AccountUpdateLayout + ) { body.authorizationKind.isSigned = Bool(false); body.authorizationKind.isProved = Bool(true); let hash = Provable.witness(Field, () => { @@ -1534,17 +1538,16 @@ const ProofAuthorization = { let isProver = proverData !== undefined; assert( isProver || priorAccountUpdates !== undefined, - 'Called `setProofAuthorizationKind()` outside the prover without passing in `priorAccountUpdates`.' + 'Called `setKind()` outside the prover without passing in `priorAccountUpdates`.' ); let myAccountUpdateId = isProver ? proverData.accountUpdate.id : id; - priorAccountUpdates ??= proverData.transaction.accountUpdates; - priorAccountUpdates = priorAccountUpdates.filter( + let priorAccountUpdatesFlat = priorAccountUpdates?.toFlatList({ + mutate: false, + }); + priorAccountUpdatesFlat ??= proverData.transaction.accountUpdates; + priorAccountUpdatesFlat = priorAccountUpdatesFlat.filter( (a) => a.id !== myAccountUpdateId ); - let priorAccountUpdatesFlat = CallForest.toFlatList( - priorAccountUpdates, - false - ); let accountUpdate = [...priorAccountUpdatesFlat] .reverse() .find((body_) => @@ -1568,7 +1571,7 @@ const ProofAuthorization = { setLazyProof( accountUpdate: AccountUpdate, proof: Omit, - priorAccountUpdates: AccountUpdate[] + priorAccountUpdates: AccountUpdateLayout ) { this.setKind(accountUpdate, priorAccountUpdates); accountUpdate.authorization = {}; From f43dc3d3f634a8ea53ab9d291d416ba48e1b89dd Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 20:20:35 +0100 Subject: [PATCH 399/524] throw error if layout doesn't match list length --- src/lib/account_update.ts | 1 + src/lib/mina.ts | 17 ++++++++++++++++- src/lib/zkapp.ts | 14 +++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 950817dac1..f663ebc1f9 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1805,6 +1805,7 @@ const UnfinishedForest = { AccountUpdate, node.accountUpdate.value ); + node.isDummy = Provable.toConstant(Bool, node.isDummy); if (node.accountUpdate.useHash) { node.accountUpdate.hash = node.accountUpdate.hash.toConstant(); } diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 376f5ba7bd..6d6c8c4d09 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -208,7 +208,6 @@ function createTransaction( tx.accountUpdates = CallForest.map(tx.accountUpdates, (a) => toConstant(AccountUpdate, a) ); - tx.layout.toConstantInPlace(); }); }); } else { @@ -229,6 +228,22 @@ function createTransaction( // CallForest.addCallers(accountUpdates); accountUpdates = CallForest.toFlatList(accountUpdates); + let otherAccountUpdates = currentTransaction + .get() + .layout.toFlatList({ mutate: true }); + + if (otherAccountUpdates.length !== accountUpdates.length) { + console.log( + 'expected', + accountUpdates.map((a) => a.toPretty()) + ); + console.log( + 'actual ', + otherAccountUpdates.map((a) => a.toPretty()) + ); + throw Error('mismatch'); + } + try { // check that on-chain values weren't used without setting a precondition for (let accountUpdate of accountUpdates) { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 666d11785c..b5d7f661d6 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -53,6 +53,7 @@ import { PrivateKey, PublicKey } from './signature.js'; import { assertStatePrecondition, cleanStatePrecondition } from './state.js'; import { inAnalyze, + inCheckedComputation, inCompile, inProver, snarkContext, @@ -244,7 +245,6 @@ function wrapMethod( // => attach ours to the current list of account updates let accountUpdate = context.selfUpdate; Mina.currentTransaction.get().accountUpdates.push(accountUpdate); - Mina.currentTransaction.get().layout.pushTopLevel(accountUpdate); // first, clone to protect against the method modifying arguments! // TODO: double-check that this works on all possible inputs, e.g. CircuitValue, o1js primitives @@ -301,6 +301,18 @@ function wrapMethod( Mina.currentTransaction.get().layout ); } + + // transfer layout from the smart contract context to the transaction + if (inCheckedComputation()) { + Provable.asProver(() => { + accountUpdate = Provable.toConstant(AccountUpdate, accountUpdate); + context.selfLayout.toConstantInPlace(); + }); + } + let txLayout = Mina.currentTransaction.get().layout; + txLayout.pushTopLevel(accountUpdate); + txLayout.setChildren(accountUpdate, context.selfLayout.root.calls); + return result; } } finally { From 6da02d72348d550dd79178a3ea157497e0cc4bd7 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 20:46:52 +0100 Subject: [PATCH 400/524] remove nested account updates from tx context --- src/lib/account_update.ts | 34 +++++++---------------------- src/lib/mina.ts | 23 ++----------------- src/lib/mina/transaction-context.ts | 1 - src/lib/zkapp.ts | 2 -- 4 files changed, 10 insertions(+), 50 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index f663ebc1f9..5ab281e77d 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1003,17 +1003,14 @@ class AccountUpdate implements Types.AccountUpdate { if (isSameAsFeePayer) nonce++; // now, we check how often this account update already updated its nonce in // this tx, and increase nonce from `getAccount` by that amount - CallForest.forEachPredecessor( - currentTransaction.get().accountUpdates, - update as AccountUpdate, - (otherUpdate) => { - let shouldIncreaseNonce = otherUpdate.publicKey - .equals(publicKey) - .and(otherUpdate.tokenId.equals(tokenId)) - .and(otherUpdate.body.incrementNonce); - if (shouldIncreaseNonce.toBoolean()) nonce++; - } - ); + let layout = currentTransaction.get().layout; + layout.forEachPredecessor(update as AccountUpdate, (otherUpdate) => { + let shouldIncreaseNonce = otherUpdate.publicKey + .equals(publicKey) + .and(otherUpdate.tokenId.equals(tokenId)) + .and(otherUpdate.body.incrementNonce); + if (shouldIncreaseNonce.toBoolean()) nonce++; + }); return { nonce: UInt32.from(nonce), isSameAsFeePayer: Bool(isSameAsFeePayer), @@ -1121,7 +1118,6 @@ class AccountUpdate implements Types.AccountUpdate { self.label || 'Unlabeled' } > AccountUpdate.create()`; } else { - currentTransaction()?.accountUpdates.push(accountUpdate); currentTransaction()?.layout.pushTopLevel(accountUpdate); accountUpdate.label = `Mina.transaction() > AccountUpdate.create()`; } @@ -1142,10 +1138,6 @@ class AccountUpdate implements Types.AccountUpdate { insideContract.this.self.approve(accountUpdate); } else { if (!currentTransaction.has()) return; - let updates = currentTransaction.get().accountUpdates; - if (!updates.find((update) => update.id === accountUpdate.id)) { - updates.push(accountUpdate); - } currentTransaction.get().layout.pushTopLevel(accountUpdate); } } @@ -1153,17 +1145,7 @@ class AccountUpdate implements Types.AccountUpdate { * Disattach an account update from where it's currently located in the transaction */ static unlink(accountUpdate: AccountUpdate) { - // TODO duplicate logic accountUpdates()?.disattach(accountUpdate); - let siblings = - accountUpdate.parent?.children.accountUpdates ?? - currentTransaction()?.accountUpdates; - if (siblings === undefined) return; - let i = siblings?.findIndex((update) => update.id === accountUpdate.id); - if (i !== undefined && i !== -1) { - siblings!.splice(i, 1); - } - accountUpdate.parent === undefined; } /** diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 6d6c8c4d09..9b1386e806 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -182,7 +182,6 @@ function createTransaction( let transactionId = currentTransaction.enter({ sender, - accountUpdates: [], layout: new AccountUpdateLayout(), fetchMode, isFinalRunOutsideCircuit, @@ -205,9 +204,7 @@ function createTransaction( f(); Provable.asProver(() => { let tx = currentTransaction.get(); - tx.accountUpdates = CallForest.map(tx.accountUpdates, (a) => - toConstant(AccountUpdate, a) - ); + tx.layout.toConstantInPlace(); }); }); } else { @@ -223,27 +220,11 @@ function createTransaction( currentTransaction.leave(transactionId); throw err; } - let accountUpdates = currentTransaction.get().accountUpdates; - // TODO: I'll be back - // CallForest.addCallers(accountUpdates); - accountUpdates = CallForest.toFlatList(accountUpdates); - let otherAccountUpdates = currentTransaction + let accountUpdates = currentTransaction .get() .layout.toFlatList({ mutate: true }); - if (otherAccountUpdates.length !== accountUpdates.length) { - console.log( - 'expected', - accountUpdates.map((a) => a.toPretty()) - ); - console.log( - 'actual ', - otherAccountUpdates.map((a) => a.toPretty()) - ); - throw Error('mismatch'); - } - try { // check that on-chain values weren't used without setting a precondition for (let accountUpdate of accountUpdates) { diff --git a/src/lib/mina/transaction-context.ts b/src/lib/mina/transaction-context.ts index e007079522..47c96a04fa 100644 --- a/src/lib/mina/transaction-context.ts +++ b/src/lib/mina/transaction-context.ts @@ -7,7 +7,6 @@ export { currentTransaction, CurrentTransaction, FetchMode }; type FetchMode = 'fetch' | 'cached' | 'test'; type CurrentTransaction = { sender?: PublicKey; - accountUpdates: AccountUpdate[]; layout: AccountUpdateLayout; fetchMode: FetchMode; isFinalRunOutsideCircuit: boolean; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index b5d7f661d6..d0f88d7875 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -178,7 +178,6 @@ function wrapMethod( let proverData = inProver() ? zkAppProver.getData() : undefined; let txId = Mina.currentTransaction.enter({ sender: proverData?.transaction.feePayer.body.publicKey, - accountUpdates: [], // TODO could pass an update with the fee payer's content here? probably not bc it's not accessed layout: new AccountUpdateLayout(), fetchMode: inProver() ? 'cached' : 'test', @@ -244,7 +243,6 @@ function wrapMethod( // called smart contract at the top level, in a transaction! // => attach ours to the current list of account updates let accountUpdate = context.selfUpdate; - Mina.currentTransaction.get().accountUpdates.push(accountUpdate); // first, clone to protect against the method modifying arguments! // TODO: double-check that this works on all possible inputs, e.g. CircuitValue, o1js primitives From 5c138eebc7d17e89776074794c6ede180aad4cc7 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 20:56:03 +0100 Subject: [PATCH 401/524] put back some code that was premature to remove --- src/lib/account_update.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 5ab281e77d..2c14519443 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1146,6 +1146,14 @@ class AccountUpdate implements Types.AccountUpdate { */ static unlink(accountUpdate: AccountUpdate) { accountUpdates()?.disattach(accountUpdate); + + let siblings = accountUpdate.parent?.children.accountUpdates; + if (siblings === undefined) return; + let i = siblings?.findIndex((update) => update.id === accountUpdate.id); + if (i !== undefined && i !== -1) { + siblings!.splice(i, 1); + } + accountUpdate.parent === undefined; } /** From f96e6a9be2b4d0e982b08efe1364c8ad8b726694 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 21:10:44 +0100 Subject: [PATCH 402/524] [no children] remove unnecessary logic in token contract --- src/lib/mina/token/token-contract.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 4617e011c5..de46d0c4bc 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -148,13 +148,7 @@ function finalizeAccountUpdates(updates: AccountUpdate[]): AccountUpdateForest { function finalizeAccountUpdate(update: AccountUpdate): AccountUpdateTree { let calls = accountUpdates()?.finalizeAndRemove(update); - - // TODO remove once everything lives in `selfLayout` - AccountUpdate.unlink(update); - - calls ??= AccountUpdateForest.fromArray(update.children.accountUpdates, { - skipDummies: true, - }); + calls ??= AccountUpdateForest.empty(); return { accountUpdate: HashedAccountUpdate.hash(update), calls }; } From 030646e4197894eab65462207486779c9d6ea5a9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 22:08:42 +0100 Subject: [PATCH 403/524] implement to public input w/o children --- src/lib/account_update.ts | 31 ++++++++++++++++++++--- src/lib/account_update.unit-test.ts | 7 +++-- src/lib/mina.ts | 4 ++- src/mina-signer/src/sign-zkapp-command.ts | 23 ++++++++++++++--- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 2c14519443..1a8061c140 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -52,12 +52,14 @@ import { MlFieldConstArray } from './ml/fields.js'; import { accountUpdatesToCallForest, CallForest, + callForestHashGeneric, transactionCommitments, } from '../mina-signer/src/sign-zkapp-command.js'; import { currentTransaction } from './mina/transaction-context.js'; import { isSmartContract } from './mina/smart-contract-base.js'; import { activeInstance } from './mina/mina-instance.js'; import { + emptyHash, genericHash, MerkleList, MerkleListBase, @@ -1045,9 +1047,32 @@ class AccountUpdate implements Types.AccountUpdate { } } - toPublicInput(): ZkappPublicInput { + toPublicInput({ + accountUpdates, + }: { + accountUpdates: AccountUpdate[]; + }): ZkappPublicInput { let accountUpdate = this.hash(); - let calls = CallForest.hashChildren(this); + + // collect this update's descendants + let descendants: AccountUpdate[] = []; + let callDepth = this.body.callDepth; + let i = accountUpdates.findIndex((a) => a.id === this.id); + assert(i !== -1, 'Account update not found in transaction'); + for (i++; i < accountUpdates.length; i++) { + let update = accountUpdates[i]; + if (update.body.callDepth <= callDepth) break; + descendants.push(update); + } + + // call forest hash + let forest = accountUpdatesToCallForest(descendants, callDepth + 1); + let calls = callForestHashGeneric( + forest, + (a) => a.hash(), + Poseidon.hashWithPrefix, + emptyHash + ); return { accountUpdate, calls }; } @@ -2350,7 +2375,7 @@ async function createZkappProof( }: LazyProof, { transaction, accountUpdate, index }: ZkappProverData ): Promise> { - let publicInput = accountUpdate.toPublicInput(); + let publicInput = accountUpdate.toPublicInput(transaction); let publicInputFields = MlFieldConstArray.to( ZkappPublicInput.toFields(publicInput) ); diff --git a/src/lib/account_update.unit-test.ts b/src/lib/account_update.unit-test.ts index a280b22712..845cd25a48 100644 --- a/src/lib/account_update.unit-test.ts +++ b/src/lib/account_update.unit-test.ts @@ -71,9 +71,12 @@ function createAccountUpdate() { let otherAddress = PrivateKey.random().toPublicKey(); let accountUpdate = AccountUpdate.create(address); - accountUpdate.approve(AccountUpdate.create(otherAddress)); + let otherUpdate = AccountUpdate.create(otherAddress); + accountUpdate.approve(otherUpdate); - let publicInput = accountUpdate.toPublicInput(); + let publicInput = accountUpdate.toPublicInput({ + accountUpdates: [accountUpdate, otherUpdate], + }); // create transaction JSON with the same accountUpdate structure, for ocaml version let tx = await Mina.transaction(() => { diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 9b1386e806..b00075b40f 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -427,9 +427,11 @@ function LocalBlockchain({ // TODO: verify account update even if the account doesn't exist yet, using a default initial account if (account !== undefined) { + let publicInput = update.toPublicInput(txn.transaction); await verifyAccountUpdate( account, update, + publicInput, commitments, this.proofsEnabled, this.getNetworkId() @@ -1162,6 +1164,7 @@ function defaultNetworkState(): NetworkValue { async function verifyAccountUpdate( account: Account, accountUpdate: AccountUpdate, + publicInput: ZkappPublicInput, transactionCommitments: { commitment: bigint; fullCommitment: bigint }, proofsEnabled: boolean, networkId: NetworkId @@ -1243,7 +1246,6 @@ async function verifyAccountUpdate( if (accountUpdate.authorization.proof && proofsEnabled) { try { - let publicInput = accountUpdate.toPublicInput(); let publicInputFields = ZkappPublicInput.toFields(publicInput); let proof: JsonProof = { diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 004a3f0940..0dc1f79a95 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -28,6 +28,7 @@ export { verifyAccountUpdateSignature, accountUpdatesToCallForest, callForestHash, + callForestHashGeneric, accountUpdateHash, feePayerHash, createFeePayer, @@ -156,11 +157,25 @@ function accountUpdateHash(update: AccountUpdate) { return hashWithPrefix(prefixes.body, fields); } -function callForestHash(forest: CallForest): Field { - let stackHash = 0n; +function callForestHash(forest: CallForest): bigint { + return callForestHashGeneric(forest, accountUpdateHash, hashWithPrefix, 0n); +} + +function callForestHashGeneric( + forest: CallForest, + hash: (a: A) => F, + hashWithPrefix: (prefix: string, input: F[]) => F, + emptyHash: F +): F { + let stackHash = emptyHash; for (let callTree of [...forest].reverse()) { - let calls = callForestHash(callTree.children); - let treeHash = accountUpdateHash(callTree.accountUpdate); + let calls = callForestHashGeneric( + callTree.children, + hash, + hashWithPrefix, + emptyHash + ); + let treeHash = hash(callTree.accountUpdate); let nodeHash = hashWithPrefix(prefixes.accountUpdateNode, [ treeHash, calls, From 09922c05b24463e8a51165d80fdedf21605e3da9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 22:13:57 +0100 Subject: [PATCH 404/524] [no children] refactor recursive diff --- src/lib/zkapp.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index d0f88d7875..5994ff93e1 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1620,10 +1620,12 @@ function diffRecursive( ) { let { transaction, index, accountUpdate: input } = inputData; diff(transaction, index, prover.toPretty(), input.toPretty()); - let nChildren = input.children.accountUpdates.length; + let inputChildren = accountUpdates()!.get(input)!.calls.value; + let proverChildren = accountUpdates()!.get(prover)!.calls.value; + let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { - let inputChild = input.children.accountUpdates[i]; - let child = prover.children.accountUpdates[i]; + let inputChild = inputChildren[i].accountUpdate.value; + let child = proverChildren[i].accountUpdate.value; if (!inputChild || !child) return; diffRecursive(child, { transaction, index, accountUpdate: inputChild }); } From 1cbaa487651e6a3cb3781e0597aede793b36efb3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 22:25:22 +0100 Subject: [PATCH 405/524] remove AccountUpdate.children --- src/examples/zkapps/token_with_proofs.ts | 6 +- src/lib/account_update.ts | 286 +----------------- .../mina/token/token-contract.unit-test.ts | 7 +- src/lib/zkapp.ts | 16 +- 4 files changed, 13 insertions(+), 302 deletions(-) diff --git a/src/examples/zkapps/token_with_proofs.ts b/src/examples/zkapps/token_with_proofs.ts index b685c8610c..17c3d1bee0 100644 --- a/src/examples/zkapps/token_with_proofs.ts +++ b/src/examples/zkapps/token_with_proofs.ts @@ -56,10 +56,8 @@ class TokenContract extends SmartContract { receiverAddress: PublicKey, callback: Experimental.Callback ) { - let senderAccountUpdate = this.approve( - callback, - AccountUpdate.Layout.AnyChildren - ); + // TODO use token contract methods for approve + let senderAccountUpdate = this.approve(callback); let amount = UInt64.from(1_000); let negativeAmount = Int64.fromObject( senderAccountUpdate.body.balanceChange diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 1a8061c140..4044bb0723 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -616,18 +616,6 @@ class AccountUpdate implements Types.AccountUpdate { account: Account; network: Network; currentSlot: CurrentSlot; - children: { - callsType: - | { type: 'None' } - | { type: 'Witness' } - | { type: 'Equals'; value: Field } - | { type: 'WitnessEquals'; value: Field }; - accountUpdates: AccountUpdate[]; - } = { - callsType: { type: 'None' }, - accountUpdates: [], - }; - parent: AccountUpdate | undefined = undefined; private isSelf: boolean; @@ -657,13 +645,8 @@ class AccountUpdate implements Types.AccountUpdate { accountUpdate.isSelf ); cloned.lazyAuthorization = accountUpdate.lazyAuthorization; - cloned.children.callsType = accountUpdate.children.callsType; - cloned.children.accountUpdates = accountUpdate.children.accountUpdates.map( - AccountUpdate.clone - ); cloned.id = accountUpdate.id; cloned.label = accountUpdate.label; - cloned.parent = accountUpdate.parent; return cloned; } @@ -812,12 +795,8 @@ class AccountUpdate implements Types.AccountUpdate { /** * Makes an {@link AccountUpdate} a child of this and approves it. */ - approve( - childUpdate: AccountUpdate, - layout: AccountUpdatesLayout = AccountUpdate.Layout.NoChildren - ) { + approve(childUpdate: AccountUpdate) { makeChildAccountUpdate(this, childUpdate); - AccountUpdate.witnessChildren(childUpdate, layout, { skipCheck: true }); accountUpdates()?.pushChild(this, childUpdate); } @@ -1077,27 +1056,9 @@ class AccountUpdate implements Types.AccountUpdate { } toPrettyLayout() { - let indent = 0; - let layout = ''; - let i = 0; - - let print = (a: AccountUpdate) => { - layout += - ' '.repeat(indent) + - `AccountUpdate(${i}, ${a.label || ''}, ${ - a.children.callsType.type - })` + - '\n'; - i++; - indent += 2; - for (let child of a.children.accountUpdates) { - print(child); - } - indent -= 2; - }; - - print(this); - return layout; + let node = accountUpdates()?.get(this); + assert(node !== undefined, 'AccountUpdate not found in layout'); + UnfinishedForest.print(node.calls); } static defaultAccountUpdate(address: PublicKey, tokenId?: Field) { @@ -1171,14 +1132,6 @@ class AccountUpdate implements Types.AccountUpdate { */ static unlink(accountUpdate: AccountUpdate) { accountUpdates()?.disattach(accountUpdate); - - let siblings = accountUpdate.parent?.children.accountUpdates; - if (siblings === undefined) return; - let i = siblings?.findIndex((update) => update.id === accountUpdate.id); - if (i !== undefined && i !== -1) { - siblings!.splice(i, 1); - } - accountUpdate.parent === undefined; } /** @@ -1263,21 +1216,10 @@ class AccountUpdate implements Types.AccountUpdate { static toFields = Types.AccountUpdate.toFields; static toAuxiliary(a?: AccountUpdate) { let aux = Types.AccountUpdate.toAuxiliary(a); - let children: AccountUpdate['children'] = { - callsType: { type: 'None' }, - accountUpdates: [], - }; let lazyAuthorization = a && a.lazyAuthorization; - if (a) { - children.callsType = a.children.callsType; - children.accountUpdates = a.children.accountUpdates.map( - AccountUpdate.clone - ); - } - let parent = a?.parent; let id = a?.id ?? Math.random(); let label = a?.label ?? ''; - return [{ lazyAuthorization, children, parent, id, label }, aux]; + return [{ lazyAuthorization, id, label }, aux]; } static toInput = Types.AccountUpdate.toInput; static empty() { @@ -1308,72 +1250,6 @@ class AccountUpdate implements Types.AccountUpdate { return Provable.witness(combinedType, compute); } - static witnessChildren( - accountUpdate: AccountUpdate, - childLayout: AccountUpdatesLayout, - options?: { skipCheck: boolean } - ) { - // just witness children's hash if childLayout === null - if (childLayout === AccountUpdate.Layout.AnyChildren) { - accountUpdate.children.callsType = { type: 'Witness' }; - return; - } - if (childLayout === AccountUpdate.Layout.NoDelegation) { - accountUpdate.children.callsType = { type: 'Witness' }; - accountUpdate.body.mayUseToken.parentsOwnToken.assertFalse(); - accountUpdate.body.mayUseToken.inheritFromParent.assertFalse(); - return; - } - accountUpdate.children.callsType = { type: 'None' }; - let childArray: AccountUpdatesLayout[] = - typeof childLayout === 'number' - ? Array(childLayout).fill(AccountUpdate.Layout.NoChildren) - : childLayout; - let n = childArray.length; - for (let i = 0; i < n; i++) { - accountUpdate.children.accountUpdates[i] = AccountUpdate.witnessTree( - provable(null), - childArray[i], - () => ({ - accountUpdate: - accountUpdate.children.accountUpdates[i] ?? AccountUpdate.dummy(), - result: null, - }), - options - ).accountUpdate; - } - if (n === 0) { - accountUpdate.children.callsType = { - type: 'Equals', - value: CallForest.emptyHash(), - }; - } - } - - /** - * Like AccountUpdate.witness, but lets you specify a layout for the - * accountUpdate's children, which also get witnessed - */ - static witnessTree( - resultType: FlexibleProvable, - childLayout: AccountUpdatesLayout, - compute: () => { - accountUpdate: AccountUpdate; - result: T; - }, - options?: { skipCheck: boolean } - ) { - // witness the root accountUpdate - let { accountUpdate, result } = AccountUpdate.witness( - resultType, - compute, - options - ); - // witness child account updates - AccountUpdate.witnessChildren(accountUpdate, childLayout, options); - return { accountUpdate, result }; - } - /** * Describes the children of an account update, which are laid out in a tree. * @@ -1597,37 +1473,6 @@ class AccountUpdateForest extends MerkleList.create( return AccountUpdateForest.from(nodes); } - // TODO remove - static fromArray( - updates: AccountUpdate[], - { skipDummies = false } = {} - ): AccountUpdateForest { - if (skipDummies) return AccountUpdateForest.fromArraySkipDummies(updates); - - let nodes = updates.map((update) => { - let accountUpdate = HashedAccountUpdate.hash(update); - let calls = AccountUpdateForest.fromArray(update.children.accountUpdates); - return { accountUpdate, calls }; - }); - return AccountUpdateForest.from(nodes); - } - - private static fromArraySkipDummies( - updates: AccountUpdate[] - ): AccountUpdateForest { - let forest = AccountUpdateForest.empty(); - - for (let update of [...updates].reverse()) { - let accountUpdate = HashedAccountUpdate.hash(update); - let calls = AccountUpdateForest.fromArraySkipDummies( - update.children.accountUpdates - ); - forest.pushIf(update.isDummy().not(), { accountUpdate, calls }); - } - - return forest; - } - // TODO this comes from paranoia and might be removed later static assertConstant(forest: MerkleListBase) { Provable.asProver(() => { @@ -1980,116 +1825,6 @@ class AccountUpdateLayout { } } -const CallForest = { - // similar to Mina_base.ZkappCommand.Call_forest.to_account_updates_list - // takes a list of accountUpdates, which each can have children, so they form a "forest" (list of trees) - // returns a flattened list, with `accountUpdate.body.callDepth` specifying positions in the forest - // also removes any "dummy" accountUpdates - toFlatList( - forest: AccountUpdate[], - mutate = true, - depth = 0 - ): AccountUpdate[] { - let accountUpdates = []; - for (let accountUpdate of forest) { - if (accountUpdate.isDummy().toBoolean()) continue; - if (mutate) accountUpdate.body.callDepth = depth; - let children = accountUpdate.children.accountUpdates; - accountUpdates.push( - accountUpdate, - ...CallForest.toFlatList(children, mutate, depth + 1) - ); - } - return accountUpdates; - }, - - // Mina_base.Zkapp_command.Digest.Forest.empty - emptyHash() { - return Field(0); - }, - - // similar to Mina_base.Zkapp_command.Call_forest.accumulate_hashes - // hashes a accountUpdate's children (and their children, and ...) to compute - // the `calls` field of ZkappPublicInput - hashChildren(update: AccountUpdate): Field { - if (!Provable.inCheckedComputation()) { - return CallForest.hashChildrenBase(update); - } - - let { callsType } = update.children; - // compute hash outside the circuit if callsType is "Witness" - // i.e., allowing accountUpdates with arbitrary children - if (callsType.type === 'Witness') { - return Provable.witness(Field, () => CallForest.hashChildrenBase(update)); - } - if (callsType.type === 'WitnessEquals') { - return callsType.value; - } - let calls = CallForest.hashChildrenBase(update); - if (callsType.type === 'Equals') { - calls.assertEquals(callsType.value); - } - return calls; - }, - - hashChildrenBase({ children }: AccountUpdate) { - let stackHash = CallForest.emptyHash(); - for (let accountUpdate of [...children.accountUpdates].reverse()) { - let calls = CallForest.hashChildren(accountUpdate); - let nodeHash = hashWithPrefix(prefixes.accountUpdateNode, [ - accountUpdate.hash(), - calls, - ]); - let newHash = hashWithPrefix(prefixes.accountUpdateCons, [ - nodeHash, - stackHash, - ]); - // skip accountUpdate if it's a dummy - stackHash = Provable.if(accountUpdate.isDummy(), stackHash, newHash); - } - return stackHash; - }, - - computeCallDepth(update: AccountUpdate) { - for (let callDepth = 0; ; callDepth++) { - if (update.parent === undefined) return callDepth; - update = update.parent; - } - }, - - map(updates: AccountUpdate[], map: (update: AccountUpdate) => AccountUpdate) { - let newUpdates: AccountUpdate[] = []; - for (let update of updates) { - let newUpdate = map(update); - newUpdate.children.accountUpdates = CallForest.map( - update.children.accountUpdates, - map - ); - newUpdates.push(newUpdate); - } - return newUpdates; - }, - - forEach(updates: AccountUpdate[], callback: (update: AccountUpdate) => void) { - for (let update of updates) { - callback(update); - CallForest.forEach(update.children.accountUpdates, callback); - } - }, - - forEachPredecessor( - updates: AccountUpdate[], - update: AccountUpdate, - callback: (update: AccountUpdate) => void - ) { - let isPredecessor = true; - CallForest.forEach(updates, (otherUpdate) => { - if (otherUpdate.id === update.id) isPredecessor = false; - if (isPredecessor) callback(otherUpdate); - }); - }, -}; - function createChildAccountUpdate( parent: AccountUpdate, childAddress: PublicKey, @@ -2101,16 +1836,7 @@ function createChildAccountUpdate( } function makeChildAccountUpdate(parent: AccountUpdate, child: AccountUpdate) { child.body.callDepth = parent.body.callDepth + 1; - let wasChildAlready = parent.children.accountUpdates.find( - (update) => update.id === child.id - ); - // add to our children if not already here - if (!wasChildAlready) { - parent.children.accountUpdates.push(child); - // remove the child from the top level list / its current parent - AccountUpdate.unlink(child); - } - child.parent = parent; + AccountUpdate.unlink(child); } // authorization diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts index b49d6b8190..2c9c059946 100644 --- a/src/lib/mina/token/token-contract.unit-test.ts +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -72,15 +72,14 @@ update1.body.mayUseToken = AccountUpdate.MayUseToken.ParentsOwnToken; let update2 = AccountUpdate.create(otherAddress); update2.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; +update2.body.callDepth = 1; let update3 = AccountUpdate.create(otherAddress, tokenId); update3.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; update3.balanceChange = Int64.one; +update3.body.callDepth = 2; -update1.adopt(update2); -update2.adopt(update3); - -let forest = AccountUpdateForest.fromArray([update1]); +let forest = AccountUpdateForest.fromFlatArray([update1, update2, update3]); await assert.rejects( () => Mina.transaction(sender, () => token.approveBase(forest)), diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 5994ff93e1..0826d944aa 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -351,7 +351,6 @@ function wrapMethod( let constantBlindingValue = blindingValue.toConstant(); let accountUpdate = this.self; accountUpdate.body.callDepth = parentAccountUpdate.body.callDepth + 1; - accountUpdate.parent = parentAccountUpdate; let memoContext = { memoized: [], @@ -431,11 +430,6 @@ function wrapMethod( // connect accountUpdate to our own. outside Provable.witness so compile knows the right structure when hashing children accountUpdate.body.callDepth = parentAccountUpdate.body.callDepth + 1; - accountUpdate.parent = parentAccountUpdate; - // beware: we don't include the callee's children in the caller circuit - // nothing is asserted about them -- it's the callee's task to check their children - accountUpdate.children.callsType = { type: 'Witness' }; - parentAccountUpdate.children.accountUpdates.push(accountUpdate); insideContract.selfLayout.pushTopLevel(accountUpdate); insideContract.selfLayout.setChildren(accountUpdate, children); @@ -928,22 +922,16 @@ super.init(); * * Under the hood, "approving" just means that the account update is made a child of the zkApp in the * tree of account updates that forms the transaction. - * The second parameter `layout` allows you to also make assertions about the approved update's _own_ children, - * by specifying a certain expected layout of children. See {@link AccountUpdate.Layout}. * * @param updateOrCallback - * @param layout * @returns The account update that was approved (needed when passing in a Callback) */ - approve( - updateOrCallback: AccountUpdate | Callback, - layout?: AccountUpdatesLayout - ) { + approve(updateOrCallback: AccountUpdate | Callback) { let accountUpdate = updateOrCallback instanceof AccountUpdate ? updateOrCallback : Provable.witness(AccountUpdate, () => updateOrCallback.accountUpdate); - this.self.approve(accountUpdate, layout); + this.self.approve(accountUpdate); return accountUpdate; } From e3a66d61b29eb64847e3caf0540df6ddcfbc707e Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 22:32:10 +0100 Subject: [PATCH 406/524] some cleanup --- src/lib/account_update.ts | 46 ----------------------------- src/lib/mina/transaction-context.ts | 2 +- src/lib/zkapp.ts | 2 -- 3 files changed, 1 insertion(+), 49 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 4044bb0723..9860a2619b 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -97,7 +97,6 @@ export { Token, CallForest, createChildAccountUpdate, - AccountUpdatesLayout, zkAppProver, dummySignature, LazyProof, @@ -1250,45 +1249,6 @@ class AccountUpdate implements Types.AccountUpdate { return Provable.witness(combinedType, compute); } - /** - * Describes the children of an account update, which are laid out in a tree. - * - * The tree layout is described recursively by using a combination of `AccountUpdate.Layout.NoChildren`, `AccountUpdate.Layout.StaticChildren(...)` and `AccountUpdate.Layout.AnyChildren`. - * - `NoChildren` means an account update that can't have children - * - `AnyChildren` means an account update can have an arbitrary amount of children, which means you can't access those children in your circuit (because the circuit is static). - * - `StaticChildren` means the account update must have a certain static amount of children and expects as arguments a description of each of those children. - * As a shortcut, you can also pass `StaticChildren` a number, which means it has that amount of children but no grandchildren. - * - * This is best understood by examples: - * - * ```ts - * let { NoChildren, AnyChildren, StaticChildren } = AccounUpdate.Layout; - * - * NoChildren // an account update with no children - * AnyChildren // an account update with arbitrary children - * StaticChildren(NoChildren) // an account update with 1 child, which doesn't have children itself - * StaticChildren(1) // shortcut for StaticChildren(NoChildren) - * StaticChildren(2) // shortcut for StaticChildren(NoChildren, NoChildren) - * StaticChildren(0) // equivalent to NoChildren - * - * // an update with 2 children, of which one has arbitrary children and the other has exactly 1 descendant - * StaticChildren(AnyChildren, StaticChildren(1)) - * ``` - */ - static Layout = { - StaticChildren: ((...args: any[]) => { - if (args.length === 1 && typeof args[0] === 'number') return args[0]; - if (args.length === 0) return 0; - return args; - }) as { - (n: number): AccountUpdatesLayout; - (...args: AccountUpdatesLayout[]): AccountUpdatesLayout; - }, - NoChildren: 0, - AnyChildren: 'AnyChildren' as const, - NoDelegation: 'NoDelegation' as const, - }; - static get MayUseToken() { return { type: provablePure({ parentsOwnToken: Bool, inheritFromParent: Bool }), @@ -1414,12 +1374,6 @@ class AccountUpdate implements Types.AccountUpdate { } } -type AccountUpdatesLayout = - | number - | 'AnyChildren' - | 'NoDelegation' - | AccountUpdatesLayout[]; - // call forest stuff function hashAccountUpdate(update: AccountUpdate) { diff --git a/src/lib/mina/transaction-context.ts b/src/lib/mina/transaction-context.ts index 47c96a04fa..d981384085 100644 --- a/src/lib/mina/transaction-context.ts +++ b/src/lib/mina/transaction-context.ts @@ -1,4 +1,4 @@ -import type { AccountUpdate, AccountUpdateLayout } from '../account_update.js'; +import type { AccountUpdateLayout } from '../account_update.js'; import type { PublicKey } from '../signature.js'; import { Context } from '../global-context.js'; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 0826d944aa..57d841bd96 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -2,7 +2,6 @@ import { Gate, Pickles, ProvablePure } from '../snarky.js'; import { Field, Bool } from './core.js'; import { AccountUpdate, - AccountUpdatesLayout, Authorization, Body, Events, @@ -14,7 +13,6 @@ import { zkAppProver, ZkappPublicInput, LazyProof, - CallForest, AccountUpdateForest, SmartContractContext, AccountUpdateLayout, From bfa8fc94fc1ddd771f01f856b4ef66c18ebfe9dd Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 7 Feb 2024 22:33:47 +0100 Subject: [PATCH 407/524] remove obsolete method --- src/lib/account_update.ts | 14 +++----------- src/lib/mina/token/token-contract.ts | 2 +- src/lib/token.test.ts | 2 +- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 9860a2619b..e2185e6166 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -664,11 +664,11 @@ class AccountUpdate implements Types.AccountUpdate { } if (accountLike instanceof AccountUpdate) { accountLike.tokenId.assertEquals(id); - thisAccountUpdate.adopt(accountLike); + thisAccountUpdate.approve(accountLike); } if (accountLike instanceof PublicKey) { accountLike = AccountUpdate.defaultAccountUpdate(accountLike, id); - thisAccountUpdate.adopt(accountLike); + thisAccountUpdate.approve(accountLike); } if (!accountLike.label) accountLike.label = `${ @@ -777,7 +777,7 @@ class AccountUpdate implements Types.AccountUpdate { } else { receiver = AccountUpdate.defaultAccountUpdate(to, this.body.tokenId); receiver.label = `${this.label ?? 'Unlabeled'}.send()`; - this.adopt(receiver); + this.approve(receiver); } // Sub the amount from the sender's account @@ -799,14 +799,6 @@ class AccountUpdate implements Types.AccountUpdate { accountUpdates()?.pushChild(this, childUpdate); } - /** - * Makes an {@link AccountUpdate} a child of this. - */ - adopt(childUpdate: AccountUpdate) { - makeChildAccountUpdate(this, childUpdate); - accountUpdates()?.pushChild(this, childUpdate); - } - get balance() { let accountUpdate = this; diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index de46d0c4bc..210b3d1a86 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -68,7 +68,7 @@ abstract class TokenContract extends SmartContract { Provable.asProver(() => { updates.data.get().forEach((update) => { let accountUpdate = update.element.accountUpdate.value.get(); - this.self.adopt(accountUpdate); + this.approve(accountUpdate); }); }); diff --git a/src/lib/token.test.ts b/src/lib/token.test.ts index 961d20770e..e6c2ee1f5b 100644 --- a/src/lib/token.test.ts +++ b/src/lib/token.test.ts @@ -89,7 +89,7 @@ class TokenContract extends SmartContract { amount: UInt64, senderAccountUpdate: AccountUpdate ) { - this.self.adopt(senderAccountUpdate); + this.approve(senderAccountUpdate); let negativeAmount = senderAccountUpdate.balanceChange; negativeAmount.assertEquals(Int64.from(amount).neg()); let tokenId = this.token.id; From 202116e1078a3d7a85657fbf8f689fd86ea85fff Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 10:55:59 +0100 Subject: [PATCH 408/524] make unfinished tree a class --- src/lib/account_update.ts | 236 +++++++++++++++++++++----------------- 1 file changed, 129 insertions(+), 107 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index e2185e6166..47456378fd 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1049,7 +1049,7 @@ class AccountUpdate implements Types.AccountUpdate { toPrettyLayout() { let node = accountUpdates()?.get(this); assert(node !== undefined, 'AccountUpdate not found in layout'); - UnfinishedForest.print(node.calls); + node.calls.print(); } static defaultAccountUpdate(address: PublicKey, tokenId?: Field) { @@ -1459,83 +1459,57 @@ function hashCons(forestHash: Field, nodeHash: Field) { * everything immediately. Instead, we maintain a structure consisting of either hashes or full account * updates that can be hashed into a final call forest at the end. */ -type UnfinishedForest = HashOrValue; +type UnfinishedForestBase = HashOrValue; +type UnfinishedForestPlain = UnfinishedForestCommon & + PlainValue; +type UnfinishedForestHashed = UnfinishedForestCommon & + UseHash; type UnfinishedTree = { accountUpdate: HashOrValue; isDummy: Bool; // `children` must be readonly since it's referenced in each child's siblings - readonly calls: UnfinishedForest; - siblings?: UnfinishedForest; + readonly calls: UnfinishedForestCommon; + siblings?: UnfinishedForestCommon; }; -type UseHash = { readonly useHash: true; hash: Field; readonly value: T }; -type PlainValue = { readonly useHash: false; value: T }; -type HashOrValue = UseHash | PlainValue; +type UseHash = { hash: Field; value: T }; +type PlainValue = { hash?: undefined; value: T }; +type HashOrValue = { hash?: Field; value: T }; -const UnfinishedForest = { - empty(): PlainValue { - return { useHash: false, value: [] }; - }, - - setHash(forest: UnfinishedForest, hash: Field): UseHash { - return Object.assign(forest, { useHash: true as const, hash }); - }, - - witnessHash(forest: UnfinishedForest): UseHash { - let hash = Provable.witness(Field, () => { - return UnfinishedForest.finalize(forest).hash; - }); - return UnfinishedForest.setHash(forest, hash); - }, +class UnfinishedForestCommon implements UnfinishedForestBase { + hash?: Field; + value: UnfinishedTree[]; // TODO: make private - push( - forest: UnfinishedForest, - accountUpdate: AccountUpdate, - children?: UnfinishedForest - ) { - forest.value.push({ - accountUpdate: { useHash: false, value: accountUpdate }, - isDummy: accountUpdate.isDummy(), - calls: children ?? UnfinishedForest.empty(), - siblings: forest, - }); - }, - - pushTree(forest: UnfinishedForest, tree: AccountUpdateTree) { - let value = AccountUpdate.dummy(); - Provable.asProver(() => { - value = tree.accountUpdate.value.get(); - }); - forest.value.push({ - accountUpdate: { useHash: true, hash: tree.accountUpdate.hash, value }, - isDummy: Bool(false), - calls: UnfinishedForest.fromForest(tree.calls), - siblings: forest, - }); - }, + usesHash(): this is UnfinishedForestHashed { + return this.hash !== undefined; + } + mutable(): this is UnfinishedForest { + return this instanceof UnfinishedForest && this.hash === undefined; + } - remove(forest: UnfinishedForest, accountUpdate: AccountUpdate) { - // find account update by .id - let index = forest.value.findIndex( - (node) => node.accountUpdate.value.id === accountUpdate.id - ); + constructor(value: UnfinishedTree[], hash?: Field) { + this.value = value; + this.hash = hash; + } - // nothing to do if it's not there - if (index === -1) return; + setHash(hash: Field): UnfinishedForestHashed { + this.hash = hash; + return this as any; // TODO? + } - // remove it - forest.value.splice(index, 1); - }, + witnessHash(): UnfinishedForestHashed { + let hash = Provable.witness(Field, () => this.finalize().hash); + return this.setHash(hash); + } - fromForest( + static fromForest( forest: MerkleListBase, recursiveCall = false - ): UnfinishedForest { + ): UnfinishedForestCommon { let unfinished = UnfinishedForest.empty(); Provable.asProver(() => { unfinished.value = forest.data.get().map(({ element: tree }) => ({ accountUpdate: { - useHash: true, hash: tree.accountUpdate.hash.toConstant(), value: tree.accountUpdate.value.get(), }, @@ -1545,34 +1519,34 @@ const UnfinishedForest = { })); }); if (!recursiveCall) { - Object.assign(unfinished, { useHash: true, hash: forest.hash }); + unfinished.setHash(forest.hash); } return unfinished; - }, + } - finalize(forest: UnfinishedForest): AccountUpdateForest { - if (forest.useHash) { + finalize(): AccountUpdateForest { + if (this.usesHash()) { let data = Unconstrained.witness(() => - UnfinishedForest.finalize({ ...forest, useHash: false }).data.get() + new UnfinishedForest(this.value).finalize().data.get() ); - return new AccountUpdateForest({ hash: forest.hash, data }); + return new AccountUpdateForest({ hash: this.hash, data }); } // not using the hash means we calculate it in-circuit - let nodes = forest.value.map(toTree); + let nodes = this.value.map(toTree); let finalForest = AccountUpdateForest.empty(); for (let { isDummy, ...tree } of [...nodes].reverse()) { finalForest.pushIf(isDummy.not(), tree); } return finalForest; - }, + } - print(forest: UnfinishedForest) { + print() { let indent = 0; let layout = ''; - let toPretty = (a: UnfinishedForest) => { + let toPretty = (a: UnfinishedForestBase) => { indent += 2; for (let tree of a.value) { layout += @@ -1584,56 +1558,99 @@ const UnfinishedForest = { indent -= 2; }; - toPretty(forest); + toPretty(this); console.log(layout); - }, + } - toFlatList( - forest: UnfinishedForest, - mutate = true, - depth = 0 - ): AccountUpdate[] { + toFlatList(mutate = true, depth = 0): AccountUpdate[] { let flatUpdates: AccountUpdate[] = []; - for (let node of forest.value) { + for (let node of this.value) { if (node.isDummy.toBoolean()) continue; let update = node.accountUpdate.value; if (mutate) update.body.callDepth = depth; - let children = UnfinishedForest.toFlatList(node.calls, mutate, depth + 1); + let children = node.calls.toFlatList(mutate, depth + 1); flatUpdates.push(update, ...children); } return flatUpdates; - }, + } - toConstantInPlace(forest: UnfinishedForest) { - for (let node of forest.value) { + toConstantInPlace() { + for (let node of this.value) { // `as any` to override readonly - this method is explicit about its mutability (node.accountUpdate as any).value = Provable.toConstant( AccountUpdate, node.accountUpdate.value ); node.isDummy = Provable.toConstant(Bool, node.isDummy); - if (node.accountUpdate.useHash) { + if (node.accountUpdate.hash !== undefined) { node.accountUpdate.hash = node.accountUpdate.hash.toConstant(); } - UnfinishedForest.toConstantInPlace(node.calls); + node.calls.toConstantInPlace(); } - if (forest.useHash) { - forest.hash = forest.hash.toConstant(); + if (this.usesHash()) { + this.hash = this.hash.toConstant(); } - }, -}; + } +} + +class UnfinishedForest + extends UnfinishedForestCommon + implements UnfinishedForestPlain +{ + hash?: undefined; + value: UnfinishedTree[]; + + static empty() { + return new UnfinishedForest([]); + } + + push(accountUpdate: AccountUpdate, children?: UnfinishedForest) { + this.value.push({ + accountUpdate: { value: accountUpdate }, + isDummy: accountUpdate.isDummy(), + calls: children ?? UnfinishedForest.empty(), + siblings: this, + }); + } + + pushTree(tree: AccountUpdateTree) { + let value = AccountUpdate.dummy(); + Provable.asProver(() => { + value = tree.accountUpdate.value.get(); + }); + this.value.push({ + accountUpdate: { hash: tree.accountUpdate.hash, value }, + isDummy: Bool(false), + calls: UnfinishedForest.fromForest(tree.calls), + siblings: this, + }); + } + + remove(accountUpdate: AccountUpdate) { + // find account update by .id + let index = this.value.findIndex( + (node) => node.accountUpdate.value.id === accountUpdate.id + ); + + // nothing to do if it's not there + if (index === -1) return; + + // remove it + this.value.splice(index, 1); + } +} function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { - let accountUpdate = node.accountUpdate.useHash - ? new HashedAccountUpdate( - node.accountUpdate.hash, - Unconstrained.witness(() => - Provable.toConstant(AccountUpdate, node.accountUpdate.value) + let accountUpdate = + node.accountUpdate.hash !== undefined + ? new HashedAccountUpdate( + node.accountUpdate.hash, + Unconstrained.witness(() => + Provable.toConstant(AccountUpdate, node.accountUpdate.value) + ) ) - ) - : HashedAccountUpdate.hash(node.accountUpdate.value); - - let calls = UnfinishedForest.finalize(node.calls); + : HashedAccountUpdate.hash(node.accountUpdate.value); + let calls = node.calls.finalize(); return { accountUpdate, isDummy: node.isDummy, calls }; } @@ -1667,7 +1684,7 @@ class AccountUpdateLayout { this.map = new Map(); root ??= AccountUpdate.dummy(); let rootTree: UnfinishedTree = { - accountUpdate: { useHash: false, value: root }, + accountUpdate: { value: root }, isDummy: Bool(false), calls: UnfinishedForest.empty(), }; @@ -1689,7 +1706,7 @@ class AccountUpdateLayout { let node = this.map.get(update.id); if (node !== undefined) return node; node = { - accountUpdate: { useHash: false, value: update }, + accountUpdate: { value: update }, isDummy: update.isDummy(), calls: UnfinishedForest.empty(), }; @@ -1715,14 +1732,15 @@ class AccountUpdateLayout { setChildren( parent: AccountUpdate | UnfinishedTree, - children: AccountUpdateForest | UnfinishedForest + children: AccountUpdateForest | UnfinishedForestCommon ) { let parentNode = this.getOrCreate(parent); - // we're not allowed to switch parentNode.children, it must stay the same reference - // so we mutate it in place + if (children instanceof AccountUpdateForest) { children = UnfinishedForest.fromForest(children); } + // we're not allowed to switch parentNode.children, it must stay the same reference + // so we mutate it in place Object.assign(parentNode.calls, children); } @@ -1733,7 +1751,11 @@ class AccountUpdateLayout { disattach(update: AccountUpdate) { let node = this.get(update); if (node?.siblings === undefined) return; - UnfinishedForest.remove(node.siblings, update); + assert( + node.siblings.mutable(), + 'Cannot disattach from an immutable layout' + ); + node.siblings.remove(update); node.siblings = undefined; } @@ -1741,18 +1763,18 @@ class AccountUpdateLayout { let node = this.get(update); if (node === undefined) return; this.disattach(update); - return UnfinishedForest.finalize(node.calls); + return node.calls.finalize(); } finalizeChildren() { - let final = UnfinishedForest.finalize(this.root.calls); + let final = this.root.calls.finalize(); this.final = final; AccountUpdateForest.assertConstant(final); return final; } toFlatList({ mutate }: { mutate: boolean }) { - return UnfinishedForest.toFlatList(this.root.calls, mutate); + return this.root.calls.toFlatList(mutate); } forEachPredecessor( @@ -1767,7 +1789,7 @@ class AccountUpdateLayout { } toConstantInPlace() { - UnfinishedForest.toConstantInPlace(this.root.calls); + this.root.calls.toConstantInPlace(); } } From a55136957c997e1fbc73cdcede9a33fc1011a977 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 10:58:44 +0100 Subject: [PATCH 409/524] move smart contract context --- src/lib/account_update.ts | 21 --------------------- src/lib/zkapp.ts | 26 +++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 47456378fd..b3ea598824 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1654,27 +1654,6 @@ function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { return { accountUpdate, isDummy: node.isDummy, calls }; } -const SmartContractContext = { - enter(self: SmartContract, selfUpdate: AccountUpdate) { - let context: SmartContractContext = { - this: self, - selfUpdate, - selfLayout: new AccountUpdateLayout(selfUpdate), - }; - let id = smartContractContext.enter(context); - return { id, context }; - }, - leave(id: number) { - smartContractContext.leave(id); - }, - stepOutside() { - return smartContractContext.enter(null); - }, - get() { - return smartContractContext.get(); - }, -}; - class AccountUpdateLayout { readonly map: Map; readonly root: UnfinishedTree; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 57d841bd96..cf1d9e8cde 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -60,7 +60,10 @@ import { Cache } from './proof-system/cache.js'; import { assert } from './gadgets/common.js'; import { SmartContractBase } from './mina/smart-contract-base.js'; import { ZkappStateLength } from './mina/mina-instance.js'; -import { accountUpdates } from './mina/smart-contract-context.js'; +import { + accountUpdates, + smartContractContext, +} from './mina/smart-contract-context.js'; // external API export { @@ -1458,6 +1461,27 @@ type ExecutionState = { accountUpdate: AccountUpdate; }; +const SmartContractContext = { + enter(self: SmartContract, selfUpdate: AccountUpdate) { + let context: SmartContractContext = { + this: self, + selfUpdate, + selfLayout: new AccountUpdateLayout(selfUpdate), + }; + let id = smartContractContext.enter(context); + return { id, context }; + }, + leave(id: number) { + smartContractContext.leave(id); + }, + stepOutside() { + return smartContractContext.enter(null); + }, + get() { + return smartContractContext.get(); + }, +}; + type DeployArgs = | { verificationKey?: { data: string; hash: string | Field }; From a4c22163780ec5f94f22745c5e7f2c1777a008b0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 10:59:50 +0100 Subject: [PATCH 410/524] finish moving smart contract context --- src/lib/account_update.ts | 2 -- src/lib/zkapp.ts | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index b3ea598824..827a64c3d4 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -67,7 +67,6 @@ import { import { Hashed } from './provable-types/packed.js'; import { accountUpdates, - SmartContractContext, smartContractContext, } from './mina/smart-contract-context.js'; import { assert } from './util/assert.js'; @@ -104,7 +103,6 @@ export { AccountUpdateLayout, hashAccountUpdate, HashedAccountUpdate, - SmartContractContext, }; const TransactionVersion = { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index cf1d9e8cde..aa32403f11 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -14,7 +14,6 @@ import { ZkappPublicInput, LazyProof, AccountUpdateForest, - SmartContractContext, AccountUpdateLayout, } from './account_update.js'; import { @@ -61,6 +60,7 @@ import { assert } from './gadgets/common.js'; import { SmartContractBase } from './mina/smart-contract-base.js'; import { ZkappStateLength } from './mina/mina-instance.js'; import { + SmartContractContext, accountUpdates, smartContractContext, } from './mina/smart-contract-context.js'; From 70302c8a5246c36d4ee94668b22f601b50854ca5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 11:21:40 +0100 Subject: [PATCH 411/524] play with ensuring invariants more strongly --- src/lib/account_update.ts | 50 +++++++++++++++++++-------------------- src/lib/zkapp.ts | 5 ++-- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 827a64c3d4..ea28897455 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1474,11 +1474,11 @@ type UseHash = { hash: Field; value: T }; type PlainValue = { hash?: undefined; value: T }; type HashOrValue = { hash?: Field; value: T }; -class UnfinishedForestCommon implements UnfinishedForestBase { +class UnfinishedForestCommon { hash?: Field; - value: UnfinishedTree[]; // TODO: make private + private value: UnfinishedTree[]; // TODO: make private - usesHash(): this is UnfinishedForestHashed { + usesHash(): this is { hash: Field } & UnfinishedForestCommon { return this.hash !== undefined; } mutable(): this is UnfinishedForest { @@ -1544,7 +1544,7 @@ class UnfinishedForestCommon implements UnfinishedForestBase { let indent = 0; let layout = ''; - let toPretty = (a: UnfinishedForestBase) => { + let toPretty = (a: UnfinishedForestCommon) => { indent += 2; for (let tree of a.value) { layout += @@ -1591,24 +1591,27 @@ class UnfinishedForestCommon implements UnfinishedForestBase { } } -class UnfinishedForest - extends UnfinishedForestCommon - implements UnfinishedForestPlain -{ +class UnfinishedForest extends UnfinishedForestCommon { hash?: undefined; - value: UnfinishedTree[]; static empty() { return new UnfinishedForest([]); } - push(accountUpdate: AccountUpdate, children?: UnfinishedForest) { - this.value.push({ - accountUpdate: { value: accountUpdate }, - isDummy: accountUpdate.isDummy(), - calls: children ?? UnfinishedForest.empty(), - siblings: this, - }); + private getValue(): UnfinishedTree[] { + // don't know how to do this differently, but this perfectly protects our invariant: + // only mutable forests can be modified + return (this as any).value; + } + + push(node: UnfinishedTree) { + if (node.siblings === this) return; + assert( + node.siblings === undefined, + 'Cannot push node that already has a parent.' + ); + node.siblings = this; + this.getValue().push(node); } pushTree(tree: AccountUpdateTree) { @@ -1616,7 +1619,7 @@ class UnfinishedForest Provable.asProver(() => { value = tree.accountUpdate.value.get(); }); - this.value.push({ + this.getValue().push({ accountUpdate: { hash: tree.accountUpdate.hash, value }, isDummy: Bool(false), calls: UnfinishedForest.fromForest(tree.calls), @@ -1626,7 +1629,7 @@ class UnfinishedForest remove(accountUpdate: AccountUpdate) { // find account update by .id - let index = this.value.findIndex( + let index = this.getValue().findIndex( (node) => node.accountUpdate.value.id === accountUpdate.id ); @@ -1634,7 +1637,7 @@ class UnfinishedForest if (index === -1) return; // remove it - this.value.splice(index, 1); + this.getValue().splice(index, 1); } } @@ -1694,13 +1697,8 @@ class AccountUpdateLayout { pushChild(parent: AccountUpdate | UnfinishedTree, child: AccountUpdate) { let parentNode = this.getOrCreate(parent); let childNode = this.getOrCreate(child); - if (childNode.siblings === parentNode.calls) return; - assert( - childNode.siblings === undefined, - 'AccountUpdateLayout.pushChild(): child already has another parent.' - ); - childNode.siblings = parentNode.calls; - parentNode.calls.value.push(childNode); + assert(parentNode.calls.mutable(), 'Cannot push to an immutable layout'); + parentNode.calls.push(childNode); } pushTopLevel(child: AccountUpdate) { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index aa32403f11..7aeb218a7b 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1630,8 +1630,9 @@ function diffRecursive( ) { let { transaction, index, accountUpdate: input } = inputData; diff(transaction, index, prover.toPretty(), input.toPretty()); - let inputChildren = accountUpdates()!.get(input)!.calls.value; - let proverChildren = accountUpdates()!.get(prover)!.calls.value; + // TODO + let inputChildren = (accountUpdates()!.get(input)!.calls as any).value; + let proverChildren = (accountUpdates()!.get(prover)!.calls as any).value; let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { let inputChild = inputChildren[i].accountUpdate.value; From ef9c18c932af39e53d80542fd745cf34a9c07003 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 11:31:13 +0100 Subject: [PATCH 412/524] rename calls to children --- src/lib/account_update.ts | 34 +++++++++++++++++----------------- src/lib/zkapp.ts | 6 +++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index ea28897455..74118046ce 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1047,7 +1047,7 @@ class AccountUpdate implements Types.AccountUpdate { toPrettyLayout() { let node = accountUpdates()?.get(this); assert(node !== undefined, 'AccountUpdate not found in layout'); - node.calls.print(); + node.children.print(); } static defaultAccountUpdate(address: PublicKey, tokenId?: Field) { @@ -1467,7 +1467,7 @@ type UnfinishedTree = { accountUpdate: HashOrValue; isDummy: Bool; // `children` must be readonly since it's referenced in each child's siblings - readonly calls: UnfinishedForestCommon; + readonly children: UnfinishedForestCommon; siblings?: UnfinishedForestCommon; }; type UseHash = { hash: Field; value: T }; @@ -1512,7 +1512,7 @@ class UnfinishedForestCommon { value: tree.accountUpdate.value.get(), }, isDummy: Bool(false), - calls: UnfinishedForest.fromForest(tree.calls, true), + children: UnfinishedForest.fromForest(tree.calls, true), siblings: unfinished, })); }); @@ -1551,7 +1551,7 @@ class UnfinishedForestCommon { ' '.repeat(indent) + `( ${tree.accountUpdate.value.label || ''} )` + '\n'; - toPretty(tree.calls); + toPretty(tree.children); } indent -= 2; }; @@ -1566,7 +1566,7 @@ class UnfinishedForestCommon { if (node.isDummy.toBoolean()) continue; let update = node.accountUpdate.value; if (mutate) update.body.callDepth = depth; - let children = node.calls.toFlatList(mutate, depth + 1); + let children = node.children.toFlatList(mutate, depth + 1); flatUpdates.push(update, ...children); } return flatUpdates; @@ -1583,7 +1583,7 @@ class UnfinishedForestCommon { if (node.accountUpdate.hash !== undefined) { node.accountUpdate.hash = node.accountUpdate.hash.toConstant(); } - node.calls.toConstantInPlace(); + node.children.toConstantInPlace(); } if (this.usesHash()) { this.hash = this.hash.toConstant(); @@ -1622,7 +1622,7 @@ class UnfinishedForest extends UnfinishedForestCommon { this.getValue().push({ accountUpdate: { hash: tree.accountUpdate.hash, value }, isDummy: Bool(false), - calls: UnfinishedForest.fromForest(tree.calls), + children: UnfinishedForest.fromForest(tree.calls), siblings: this, }); } @@ -1651,7 +1651,7 @@ function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { ) ) : HashedAccountUpdate.hash(node.accountUpdate.value); - let calls = node.calls.finalize(); + let calls = node.children.finalize(); return { accountUpdate, isDummy: node.isDummy, calls }; } @@ -1666,7 +1666,7 @@ class AccountUpdateLayout { let rootTree: UnfinishedTree = { accountUpdate: { value: root }, isDummy: Bool(false), - calls: UnfinishedForest.empty(), + children: UnfinishedForest.empty(), }; this.map.set(root.id, rootTree); this.root = rootTree; @@ -1688,7 +1688,7 @@ class AccountUpdateLayout { node = { accountUpdate: { value: update }, isDummy: update.isDummy(), - calls: UnfinishedForest.empty(), + children: UnfinishedForest.empty(), }; this.map.set(update.id, node); return node; @@ -1697,8 +1697,8 @@ class AccountUpdateLayout { pushChild(parent: AccountUpdate | UnfinishedTree, child: AccountUpdate) { let parentNode = this.getOrCreate(parent); let childNode = this.getOrCreate(child); - assert(parentNode.calls.mutable(), 'Cannot push to an immutable layout'); - parentNode.calls.push(childNode); + assert(parentNode.children.mutable(), 'Cannot push to an immutable layout'); + parentNode.children.push(childNode); } pushTopLevel(child: AccountUpdate) { @@ -1716,7 +1716,7 @@ class AccountUpdateLayout { } // we're not allowed to switch parentNode.children, it must stay the same reference // so we mutate it in place - Object.assign(parentNode.calls, children); + Object.assign(parentNode.children, children); } setTopLevel(children: AccountUpdateForest) { @@ -1738,18 +1738,18 @@ class AccountUpdateLayout { let node = this.get(update); if (node === undefined) return; this.disattach(update); - return node.calls.finalize(); + return node.children.finalize(); } finalizeChildren() { - let final = this.root.calls.finalize(); + let final = this.root.children.finalize(); this.final = final; AccountUpdateForest.assertConstant(final); return final; } toFlatList({ mutate }: { mutate: boolean }) { - return this.root.calls.toFlatList(mutate); + return this.root.children.toFlatList(mutate); } forEachPredecessor( @@ -1764,7 +1764,7 @@ class AccountUpdateLayout { } toConstantInPlace() { - this.root.calls.toConstantInPlace(); + this.root.children.toConstantInPlace(); } } diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 7aeb218a7b..5629f6ed45 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -310,7 +310,7 @@ function wrapMethod( } let txLayout = Mina.currentTransaction.get().layout; txLayout.pushTopLevel(accountUpdate); - txLayout.setChildren(accountUpdate, context.selfLayout.root.calls); + txLayout.setChildren(accountUpdate, context.selfLayout.root.children); return result; } @@ -1631,8 +1631,8 @@ function diffRecursive( let { transaction, index, accountUpdate: input } = inputData; diff(transaction, index, prover.toPretty(), input.toPretty()); // TODO - let inputChildren = (accountUpdates()!.get(input)!.calls as any).value; - let proverChildren = (accountUpdates()!.get(prover)!.calls as any).value; + let inputChildren = (accountUpdates()!.get(input)!.children as any).value; + let proverChildren = (accountUpdates()!.get(prover)!.children as any).value; let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { let inputChild = inputChildren[i].accountUpdate.value; From c03554bbed5e3f12fde4946ed1f5eb5d7e308794 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 12:03:44 +0100 Subject: [PATCH 413/524] start refactoring unfinished forest --- src/lib/account_update.ts | 101 ++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 58 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 74118046ce..300347363c 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1457,53 +1457,52 @@ function hashCons(forestHash: Field, nodeHash: Field) { * everything immediately. Instead, we maintain a structure consisting of either hashes or full account * updates that can be hashed into a final call forest at the end. */ -type UnfinishedForestBase = HashOrValue; -type UnfinishedForestPlain = UnfinishedForestCommon & - PlainValue; -type UnfinishedForestHashed = UnfinishedForestCommon & - UseHash; +type UnfinishedForestFinal = { final: AccountUpdateForest } & UnfinishedForest; +type UnfinishedForestMutable = { + final?: undefined; + value: UnfinishedTree[]; +} & UnfinishedForest; type UnfinishedTree = { accountUpdate: HashOrValue; isDummy: Bool; // `children` must be readonly since it's referenced in each child's siblings - readonly children: UnfinishedForestCommon; - siblings?: UnfinishedForestCommon; + readonly children: UnfinishedForest; + siblings?: UnfinishedForest; }; -type UseHash = { hash: Field; value: T }; -type PlainValue = { hash?: undefined; value: T }; type HashOrValue = { hash?: Field; value: T }; -class UnfinishedForestCommon { - hash?: Field; - private value: UnfinishedTree[]; // TODO: make private +class UnfinishedForest { + final?: AccountUpdateForest; + readonly value: readonly Readonly[]; // TODO: make private - usesHash(): this is { hash: Field } & UnfinishedForestCommon { - return this.hash !== undefined; + isFinal(): this is UnfinishedForestFinal { + return this.final !== undefined; } - mutable(): this is UnfinishedForest { - return this instanceof UnfinishedForest && this.hash === undefined; + isMutable(): this is UnfinishedForestMutable { + return this.final === undefined; } - constructor(value: UnfinishedTree[], hash?: Field) { + constructor(value: UnfinishedTree[], final?: AccountUpdateForest) { this.value = value; - this.hash = hash; + this.final = final; } - setHash(hash: Field): UnfinishedForestHashed { - this.hash = hash; - return this as any; // TODO? + setFinal(final: AccountUpdateForest): UnfinishedForestFinal { + return Object.assign(this, { final }); } - witnessHash(): UnfinishedForestHashed { - let hash = Provable.witness(Field, () => this.finalize().hash); - return this.setHash(hash); + witnessHash(): UnfinishedForestFinal { + let final = Provable.witness(AccountUpdateForest.provable, () => + this.finalize() + ); + return this.setFinal(final); } static fromForest( forest: MerkleListBase, recursiveCall = false - ): UnfinishedForestCommon { + ): UnfinishedForest { let unfinished = UnfinishedForest.empty(); Provable.asProver(() => { unfinished.value = forest.data.get().map(({ element: tree }) => ({ @@ -1517,17 +1516,14 @@ class UnfinishedForestCommon { })); }); if (!recursiveCall) { - unfinished.setHash(forest.hash); + unfinished.setFinal(new AccountUpdateForest(forest)); } return unfinished; } finalize(): AccountUpdateForest { - if (this.usesHash()) { - let data = Unconstrained.witness(() => - new UnfinishedForest(this.value).finalize().data.get() - ); - return new AccountUpdateForest({ hash: this.hash, data }); + if (this.isFinal()) { + return this.final; } // not using the hash means we calculate it in-circuit @@ -1537,6 +1533,7 @@ class UnfinishedForestCommon { for (let { isDummy, ...tree } of [...nodes].reverse()) { finalForest.pushIf(isDummy.not(), tree); } + this.setFinal(finalForest); return finalForest; } @@ -1544,7 +1541,7 @@ class UnfinishedForestCommon { let indent = 0; let layout = ''; - let toPretty = (a: UnfinishedForestCommon) => { + let toPretty = (a: UnfinishedForest) => { indent += 2; for (let tree of a.value) { layout += @@ -1575,33 +1572,23 @@ class UnfinishedForestCommon { toConstantInPlace() { for (let node of this.value) { // `as any` to override readonly - this method is explicit about its mutability - (node.accountUpdate as any).value = Provable.toConstant( + node.accountUpdate.value = Provable.toConstant( AccountUpdate, node.accountUpdate.value ); - node.isDummy = Provable.toConstant(Bool, node.isDummy); + (node as any).isDummy = Provable.toConstant(Bool, node.isDummy); if (node.accountUpdate.hash !== undefined) { node.accountUpdate.hash = node.accountUpdate.hash.toConstant(); } node.children.toConstantInPlace(); } - if (this.usesHash()) { - this.hash = this.hash.toConstant(); + if (this.isFinal()) { + this.final.hash = this.final.hash.toConstant(); } } -} - -class UnfinishedForest extends UnfinishedForestCommon { - hash?: undefined; - static empty() { - return new UnfinishedForest([]); - } - - private getValue(): UnfinishedTree[] { - // don't know how to do this differently, but this perfectly protects our invariant: - // only mutable forests can be modified - return (this as any).value; + static empty(): UnfinishedForestMutable { + return new UnfinishedForest([]) as any; } push(node: UnfinishedTree) { @@ -1611,7 +1598,8 @@ class UnfinishedForest extends UnfinishedForestCommon { 'Cannot push node that already has a parent.' ); node.siblings = this; - this.getValue().push(node); + assert(this.isMutable(), 'Cannot push to an immutable forest'); + this.value.push(node); } pushTree(tree: AccountUpdateTree) { @@ -1619,7 +1607,8 @@ class UnfinishedForest extends UnfinishedForestCommon { Provable.asProver(() => { value = tree.accountUpdate.value.get(); }); - this.getValue().push({ + assert(this.isMutable(), 'Cannot push to an immutable forest'); + this.value.push({ accountUpdate: { hash: tree.accountUpdate.hash, value }, isDummy: Bool(false), children: UnfinishedForest.fromForest(tree.calls), @@ -1629,7 +1618,7 @@ class UnfinishedForest extends UnfinishedForestCommon { remove(accountUpdate: AccountUpdate) { // find account update by .id - let index = this.getValue().findIndex( + let index = this.value.findIndex( (node) => node.accountUpdate.value.id === accountUpdate.id ); @@ -1637,7 +1626,8 @@ class UnfinishedForest extends UnfinishedForestCommon { if (index === -1) return; // remove it - this.getValue().splice(index, 1); + assert(this.isMutable(), 'Cannot remove from an immutable forest'); + this.value.splice(index, 1); } } @@ -1697,7 +1687,6 @@ class AccountUpdateLayout { pushChild(parent: AccountUpdate | UnfinishedTree, child: AccountUpdate) { let parentNode = this.getOrCreate(parent); let childNode = this.getOrCreate(child); - assert(parentNode.children.mutable(), 'Cannot push to an immutable layout'); parentNode.children.push(childNode); } @@ -1707,7 +1696,7 @@ class AccountUpdateLayout { setChildren( parent: AccountUpdate | UnfinishedTree, - children: AccountUpdateForest | UnfinishedForestCommon + children: AccountUpdateForest | UnfinishedForest ) { let parentNode = this.getOrCreate(parent); @@ -1726,10 +1715,6 @@ class AccountUpdateLayout { disattach(update: AccountUpdate) { let node = this.get(update); if (node?.siblings === undefined) return; - assert( - node.siblings.mutable(), - 'Cannot disattach from an immutable layout' - ); node.siblings.remove(update); node.siblings = undefined; } From f48955a24c682f8deaf0925f083314e80554814b Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Thu, 8 Feb 2024 12:30:24 +0100 Subject: [PATCH 414/524] reorder stuff --- src/lib/account_update.ts | 152 +++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 300347363c..0063e7395f 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1488,10 +1488,30 @@ class UnfinishedForest { this.final = final; } - setFinal(final: AccountUpdateForest): UnfinishedForestFinal { + static empty(): UnfinishedForestMutable { + return new UnfinishedForest([]) as any; + } + + private setFinal(final: AccountUpdateForest): UnfinishedForestFinal { return Object.assign(this, { final }); } + finalize(): AccountUpdateForest { + if (this.isFinal()) { + return this.final; + } + + // not using the hash means we calculate it in-circuit + let nodes = this.value.map(toTree); + let finalForest = AccountUpdateForest.empty(); + + for (let { isDummy, ...tree } of [...nodes].reverse()) { + finalForest.pushIf(isDummy.not(), tree); + } + this.setFinal(finalForest); + return finalForest; + } + witnessHash(): UnfinishedForestFinal { let final = Provable.witness(AccountUpdateForest.provable, () => this.finalize() @@ -1499,6 +1519,45 @@ class UnfinishedForest { return this.setFinal(final); } + push(node: UnfinishedTree) { + if (node.siblings === this) return; + assert( + node.siblings === undefined, + 'Cannot push node that already has a parent.' + ); + node.siblings = this; + assert(this.isMutable(), 'Cannot push to an immutable forest'); + this.value.push(node); + } + + pushTree(tree: AccountUpdateTree) { + assert(this.isMutable(), 'Cannot push to an immutable forest'); + let value = AccountUpdate.dummy(); + Provable.asProver(() => { + value = tree.accountUpdate.value.get(); + }); + this.value.push({ + accountUpdate: { hash: tree.accountUpdate.hash, value }, + isDummy: Bool(false), + children: UnfinishedForest.fromForest(tree.calls), + siblings: this, + }); + } + + remove(accountUpdate: AccountUpdate) { + // find account update by .id + let index = this.value.findIndex( + (node) => node.accountUpdate.value.id === accountUpdate.id + ); + + // nothing to do if it's not there + if (index === -1) return; + + // remove it + assert(this.isMutable(), 'Cannot remove from an immutable forest'); + this.value.splice(index, 1); + } + static fromForest( forest: MerkleListBase, recursiveCall = false @@ -1521,42 +1580,6 @@ class UnfinishedForest { return unfinished; } - finalize(): AccountUpdateForest { - if (this.isFinal()) { - return this.final; - } - - // not using the hash means we calculate it in-circuit - let nodes = this.value.map(toTree); - let finalForest = AccountUpdateForest.empty(); - - for (let { isDummy, ...tree } of [...nodes].reverse()) { - finalForest.pushIf(isDummy.not(), tree); - } - this.setFinal(finalForest); - return finalForest; - } - - print() { - let indent = 0; - let layout = ''; - - let toPretty = (a: UnfinishedForest) => { - indent += 2; - for (let tree of a.value) { - layout += - ' '.repeat(indent) + - `( ${tree.accountUpdate.value.label || ''} )` + - '\n'; - toPretty(tree.children); - } - indent -= 2; - }; - - toPretty(this); - console.log(layout); - } - toFlatList(mutate = true, depth = 0): AccountUpdate[] { let flatUpdates: AccountUpdate[] = []; for (let node of this.value) { @@ -1587,47 +1610,24 @@ class UnfinishedForest { } } - static empty(): UnfinishedForestMutable { - return new UnfinishedForest([]) as any; - } - - push(node: UnfinishedTree) { - if (node.siblings === this) return; - assert( - node.siblings === undefined, - 'Cannot push node that already has a parent.' - ); - node.siblings = this; - assert(this.isMutable(), 'Cannot push to an immutable forest'); - this.value.push(node); - } - - pushTree(tree: AccountUpdateTree) { - let value = AccountUpdate.dummy(); - Provable.asProver(() => { - value = tree.accountUpdate.value.get(); - }); - assert(this.isMutable(), 'Cannot push to an immutable forest'); - this.value.push({ - accountUpdate: { hash: tree.accountUpdate.hash, value }, - isDummy: Bool(false), - children: UnfinishedForest.fromForest(tree.calls), - siblings: this, - }); - } - - remove(accountUpdate: AccountUpdate) { - // find account update by .id - let index = this.value.findIndex( - (node) => node.accountUpdate.value.id === accountUpdate.id - ); + print() { + let indent = 0; + let layout = ''; - // nothing to do if it's not there - if (index === -1) return; + let toPretty = (a: UnfinishedForest) => { + indent += 2; + for (let tree of a.value) { + layout += + ' '.repeat(indent) + + `( ${tree.accountUpdate.value.label || ''} )` + + '\n'; + toPretty(tree.children); + } + indent -= 2; + }; - // remove it - assert(this.isMutable(), 'Cannot remove from an immutable forest'); - this.value.splice(index, 1); + toPretty(this); + console.log(layout); } } From b7a74c41509539dda22cd0ba0873857ad80ffdeb Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Thu, 8 Feb 2024 12:55:12 +0100 Subject: [PATCH 415/524] tweak remove --- src/lib/account_update.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 0063e7395f..f7571553e9 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1497,11 +1497,8 @@ class UnfinishedForest { } finalize(): AccountUpdateForest { - if (this.isFinal()) { - return this.final; - } + if (this.isFinal()) return this.final; - // not using the hash means we calculate it in-circuit let nodes = this.value.map(toTree); let finalForest = AccountUpdateForest.empty(); @@ -1530,6 +1527,7 @@ class UnfinishedForest { this.value.push(node); } + // TODO this isn't quite right pushTree(tree: AccountUpdateTree) { assert(this.isMutable(), 'Cannot push to an immutable forest'); let value = AccountUpdate.dummy(); @@ -1544,10 +1542,10 @@ class UnfinishedForest { }); } - remove(accountUpdate: AccountUpdate) { - // find account update by .id + remove(node: UnfinishedTree) { + // find by .id let index = this.value.findIndex( - (node) => node.accountUpdate.value.id === accountUpdate.id + (n) => n.accountUpdate.value.id === node.accountUpdate.value.id ); // nothing to do if it's not there @@ -1555,6 +1553,7 @@ class UnfinishedForest { // remove it assert(this.isMutable(), 'Cannot remove from an immutable forest'); + node.siblings = undefined; this.value.splice(index, 1); } @@ -1714,9 +1713,7 @@ class AccountUpdateLayout { disattach(update: AccountUpdate) { let node = this.get(update); - if (node?.siblings === undefined) return; - node.siblings.remove(update); - node.siblings = undefined; + node?.siblings?.remove(node); } finalizeAndRemove(update: AccountUpdate) { From cbc774c3ade14c60ac34dde4dc2bc24c80d0ef21 Mon Sep 17 00:00:00 2001 From: Gregor Mitscha-Baude Date: Thu, 8 Feb 2024 16:04:14 +0100 Subject: [PATCH 416/524] better set children logic --- src/lib/account_update.ts | 43 ++++++++++++++-------------- src/lib/mina/token/token-contract.ts | 9 ------ src/lib/zkapp.ts | 9 ++++-- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index f7571553e9..c9e1afc2ed 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1557,26 +1557,33 @@ class UnfinishedForest { this.value.splice(index, 1); } - static fromForest( - forest: MerkleListBase, - recursiveCall = false - ): UnfinishedForest { - let unfinished = UnfinishedForest.empty(); + setToForest(forest: MerkleListBase) { + if (this.isMutable()) { + assert( + this.value.length === 0, + 'Replacing a mutable forest that has existing children might be a mistake.' + ); + } + let value: UnfinishedTree[] = []; Provable.asProver(() => { - unfinished.value = forest.data.get().map(({ element: tree }) => ({ + value = forest.data.get().map(({ element: tree }) => ({ accountUpdate: { hash: tree.accountUpdate.hash.toConstant(), value: tree.accountUpdate.value.get(), }, isDummy: Bool(false), - children: UnfinishedForest.fromForest(tree.calls, true), - siblings: unfinished, + children: UnfinishedForest.fromForest(tree.calls), + siblings: this, })); }); - if (!recursiveCall) { - unfinished.setFinal(new AccountUpdateForest(forest)); - } - return unfinished; + Object.assign(this, { value }); + return this.setFinal(new AccountUpdateForest(forest)); + } + + static fromForest( + forest: MerkleListBase + ): UnfinishedForestFinal { + return UnfinishedForest.empty().setToForest(forest); } toFlatList(mutate = true, depth = 0): AccountUpdate[] { @@ -1665,7 +1672,7 @@ class AccountUpdateLayout { return this.map.get(update.id); } - getOrCreate(update: AccountUpdate | UnfinishedTree): UnfinishedTree { + private getOrCreate(update: AccountUpdate | UnfinishedTree): UnfinishedTree { if (!(update instanceof AccountUpdate)) { if (!this.map.has(update.accountUpdate.value.id)) { this.map.set(update.accountUpdate.value.id, update); @@ -1695,16 +1702,10 @@ class AccountUpdateLayout { setChildren( parent: AccountUpdate | UnfinishedTree, - children: AccountUpdateForest | UnfinishedForest + children: AccountUpdateForest ) { let parentNode = this.getOrCreate(parent); - - if (children instanceof AccountUpdateForest) { - children = UnfinishedForest.fromForest(children); - } - // we're not allowed to switch parentNode.children, it must stay the same reference - // so we mutate it in place - Object.assign(parentNode.children, children); + parentNode.children.setToForest(children); } setTopLevel(children: AccountUpdateForest) { diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 210b3d1a86..cb18a6c906 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -63,15 +63,6 @@ abstract class TokenContract extends SmartContract { `the supported limit of ${MAX_ACCOUNT_UPDATES}.\n` ); - // make top-level updates our children - // TODO: this must not be necessary once we move everything to `selfLayout` - Provable.asProver(() => { - updates.data.get().forEach((update) => { - let accountUpdate = update.element.accountUpdate.value.get(); - this.approve(accountUpdate); - }); - }); - // skip hashing our child account updates in the method wrapper // since we just did that in the loop above accountUpdates()?.setTopLevel(updates); diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 5629f6ed45..35d334d769 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -310,7 +310,10 @@ function wrapMethod( } let txLayout = Mina.currentTransaction.get().layout; txLayout.pushTopLevel(accountUpdate); - txLayout.setChildren(accountUpdate, context.selfLayout.root.children); + txLayout.setChildren( + accountUpdate, + context.selfLayout.finalizeChildren() + ); return result; } @@ -1631,8 +1634,8 @@ function diffRecursive( let { transaction, index, accountUpdate: input } = inputData; diff(transaction, index, prover.toPretty(), input.toPretty()); // TODO - let inputChildren = (accountUpdates()!.get(input)!.children as any).value; - let proverChildren = (accountUpdates()!.get(prover)!.children as any).value; + let inputChildren = accountUpdates()!.get(input)!.children.value; + let proverChildren = accountUpdates()!.get(prover)!.children.value; let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { let inputChild = inputChildren[i].accountUpdate.value; From c4b016a5f2974d8220877ec22af3417814f7cad6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 18:39:08 +0100 Subject: [PATCH 417/524] completely get rid of unconstrained array after finalizing --- src/lib/account_update.ts | 70 ++++++++++++++++++++++----------------- src/lib/zkapp.ts | 4 +-- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index c9e1afc2ed..b81f214e3e 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1405,6 +1405,20 @@ class AccountUpdateForest extends MerkleList.create( let simpleForest = accountUpdatesToCallForest(updates); return this.fromSimpleForest(simpleForest); } + static toFlatArray( + forest: MerkleListBase, + mutate = true, + depth = 0 + ) { + let flat: AccountUpdate[] = []; + for (let { element: tree } of forest.data.get()) { + let update = tree.accountUpdate.value.get(); + if (mutate) update.body.callDepth = depth; + flat.push(update); + flat.push(...this.toFlatArray(tree.calls, mutate, depth + 1)); + } + return flat; + } private static fromSimpleForest( simpleForest: CallForest @@ -1451,13 +1465,16 @@ function hashCons(forestHash: Field, nodeHash: Field) { } /** - * Structure for constructing the forest of child account updates, from a circuit. + * UnfinishedForest / UnfinishedTree are structures for constructing the forest of child account updates from a circuit. * * The circuit can mutate account updates and change their array of children, so here we can't hash * everything immediately. Instead, we maintain a structure consisting of either hashes or full account * updates that can be hashed into a final call forest at the end. */ -type UnfinishedForestFinal = { final: AccountUpdateForest } & UnfinishedForest; +type UnfinishedForestFinal = { + final: AccountUpdateForest; + value?: undefined; +} & UnfinishedForest; type UnfinishedForestMutable = { final?: undefined; value: UnfinishedTree[]; @@ -1474,13 +1491,13 @@ type HashOrValue = { hash?: Field; value: T }; class UnfinishedForest { final?: AccountUpdateForest; - readonly value: readonly Readonly[]; // TODO: make private + value?: UnfinishedTree[]; isFinal(): this is UnfinishedForestFinal { return this.final !== undefined; } isMutable(): this is UnfinishedForestMutable { - return this.final === undefined; + return this.value !== undefined; } constructor(value: UnfinishedTree[], final?: AccountUpdateForest) { @@ -1493,11 +1510,12 @@ class UnfinishedForest { } private setFinal(final: AccountUpdateForest): UnfinishedForestFinal { - return Object.assign(this, { final }); + return Object.assign(this, { final, value: undefined }); } finalize(): AccountUpdateForest { if (this.isFinal()) return this.final; + assert(this.isMutable(), 'final or mutable'); let nodes = this.value.map(toTree); let finalForest = AccountUpdateForest.empty(); @@ -1543,6 +1561,7 @@ class UnfinishedForest { } remove(node: UnfinishedTree) { + assert(this.isMutable(), 'Cannot remove from an immutable forest'); // find by .id let index = this.value.findIndex( (n) => n.accountUpdate.value.id === node.accountUpdate.value.id @@ -1552,7 +1571,6 @@ class UnfinishedForest { if (index === -1) return; // remove it - assert(this.isMutable(), 'Cannot remove from an immutable forest'); node.siblings = undefined; this.value.splice(index, 1); } @@ -1564,65 +1582,55 @@ class UnfinishedForest { 'Replacing a mutable forest that has existing children might be a mistake.' ); } - let value: UnfinishedTree[] = []; - Provable.asProver(() => { - value = forest.data.get().map(({ element: tree }) => ({ - accountUpdate: { - hash: tree.accountUpdate.hash.toConstant(), - value: tree.accountUpdate.value.get(), - }, - isDummy: Bool(false), - children: UnfinishedForest.fromForest(tree.calls), - siblings: this, - })); - }); - Object.assign(this, { value }); return this.setFinal(new AccountUpdateForest(forest)); } - static fromForest( - forest: MerkleListBase - ): UnfinishedForestFinal { + static fromForest(forest: MerkleListBase) { return UnfinishedForest.empty().setToForest(forest); } - toFlatList(mutate = true, depth = 0): AccountUpdate[] { + toFlatArray(mutate = true, depth = 0): AccountUpdate[] { + if (this.isFinal()) + return AccountUpdateForest.toFlatArray(this.final, mutate, depth); + assert(this.isMutable(), 'final or mutable'); let flatUpdates: AccountUpdate[] = []; for (let node of this.value) { if (node.isDummy.toBoolean()) continue; let update = node.accountUpdate.value; if (mutate) update.body.callDepth = depth; - let children = node.children.toFlatList(mutate, depth + 1); + let children = node.children.toFlatArray(mutate, depth + 1); flatUpdates.push(update, ...children); } return flatUpdates; } toConstantInPlace() { + if (this.isFinal()) { + this.final.hash = this.final.hash.toConstant(); + return; + } + assert(this.isMutable(), 'final or mutable'); for (let node of this.value) { - // `as any` to override readonly - this method is explicit about its mutability node.accountUpdate.value = Provable.toConstant( AccountUpdate, node.accountUpdate.value ); - (node as any).isDummy = Provable.toConstant(Bool, node.isDummy); + node.isDummy = Provable.toConstant(Bool, node.isDummy); if (node.accountUpdate.hash !== undefined) { node.accountUpdate.hash = node.accountUpdate.hash.toConstant(); } node.children.toConstantInPlace(); } - if (this.isFinal()) { - this.final.hash = this.final.hash.toConstant(); - } } print() { + assert(this.isMutable(), 'print(): unimplemented for final forests'); let indent = 0; let layout = ''; let toPretty = (a: UnfinishedForest) => { indent += 2; - for (let tree of a.value) { + for (let tree of a.value!) { layout += ' '.repeat(indent) + `( ${tree.accountUpdate.value.label || ''} )` + @@ -1732,7 +1740,7 @@ class AccountUpdateLayout { } toFlatList({ mutate }: { mutate: boolean }) { - return this.root.children.toFlatList(mutate); + return this.root.children.toFlatArray(mutate); } forEachPredecessor( diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 35d334d769..357e6b3849 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1634,8 +1634,8 @@ function diffRecursive( let { transaction, index, accountUpdate: input } = inputData; diff(transaction, index, prover.toPretty(), input.toPretty()); // TODO - let inputChildren = accountUpdates()!.get(input)!.children.value; - let proverChildren = accountUpdates()!.get(prover)!.children.value; + let inputChildren = accountUpdates()!.get(input)!.children.value!; + let proverChildren = accountUpdates()!.get(prover)!.children.value!; let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { let inputChild = inputChildren[i].accountUpdate.value; From 1e6278e9f31d84cc978723d8aa7d23d8409cc73f Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 18:56:35 +0100 Subject: [PATCH 418/524] cleanup, rename --- src/lib/account_update.ts | 69 +++++++++++++++++++++++---------------- src/lib/zkapp.ts | 4 +-- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index b81f214e3e..5f7019f457 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1465,44 +1465,56 @@ function hashCons(forestHash: Field, nodeHash: Field) { } /** - * UnfinishedForest / UnfinishedTree are structures for constructing the forest of child account updates from a circuit. + * `UnfinishedForest` / `UnfinishedTree` are structures for constructing the forest of child account updates from a circuit. * * The circuit can mutate account updates and change their array of children, so here we can't hash * everything immediately. Instead, we maintain a structure consisting of either hashes or full account * updates that can be hashed into a final call forest at the end. + * + * `UnfinishedForest` behaves like a tagged enum type: + * ``` + * type UnfinishedForest = + * | Mutable of UnfinishedTree[] + * | Final of AccountUpdateForest; + * ``` */ -type UnfinishedForestFinal = { - final: AccountUpdateForest; - value?: undefined; -} & UnfinishedForest; -type UnfinishedForestMutable = { - final?: undefined; - value: UnfinishedTree[]; -} & UnfinishedForest; - type UnfinishedTree = { - accountUpdate: HashOrValue; + // TODO it would be nice to make this closer to `Final of Hashed | Mutable of AccountUpdate` + accountUpdate: { hash?: Field; value: AccountUpdate }; isDummy: Bool; // `children` must be readonly since it's referenced in each child's siblings readonly children: UnfinishedForest; siblings?: UnfinishedForest; }; -type HashOrValue = { hash?: Field; value: T }; + +type UnfinishedForestFinal = UnfinishedForest & { + final: AccountUpdateForest; + mutable?: undefined; +}; + +type UnfinishedForestMutable = UnfinishedForest & { + final?: undefined; + mutable: UnfinishedTree[]; +}; class UnfinishedForest { final?: AccountUpdateForest; - value?: UnfinishedTree[]; + mutable?: UnfinishedTree[]; isFinal(): this is UnfinishedForestFinal { return this.final !== undefined; } isMutable(): this is UnfinishedForestMutable { - return this.value !== undefined; + return this.mutable !== undefined; } - constructor(value: UnfinishedTree[], final?: AccountUpdateForest) { - this.value = value; + constructor(mutable?: UnfinishedTree[], final?: AccountUpdateForest) { + assert( + (final === undefined) !== (mutable === undefined), + 'final or mutable' + ); this.final = final; + this.mutable = mutable; } static empty(): UnfinishedForestMutable { @@ -1510,14 +1522,14 @@ class UnfinishedForest { } private setFinal(final: AccountUpdateForest): UnfinishedForestFinal { - return Object.assign(this, { final, value: undefined }); + return Object.assign(this, { final, mutable: undefined }); } finalize(): AccountUpdateForest { if (this.isFinal()) return this.final; assert(this.isMutable(), 'final or mutable'); - let nodes = this.value.map(toTree); + let nodes = this.mutable.map(toTree); let finalForest = AccountUpdateForest.empty(); for (let { isDummy, ...tree } of [...nodes].reverse()) { @@ -1542,17 +1554,17 @@ class UnfinishedForest { ); node.siblings = this; assert(this.isMutable(), 'Cannot push to an immutable forest'); - this.value.push(node); + this.mutable.push(node); } - // TODO this isn't quite right + // TODO this isn't quite right - shouldn't have to save the value in a circuit-accessible way if it's hashed pushTree(tree: AccountUpdateTree) { assert(this.isMutable(), 'Cannot push to an immutable forest'); let value = AccountUpdate.dummy(); Provable.asProver(() => { value = tree.accountUpdate.value.get(); }); - this.value.push({ + this.mutable.push({ accountUpdate: { hash: tree.accountUpdate.hash, value }, isDummy: Bool(false), children: UnfinishedForest.fromForest(tree.calls), @@ -1563,7 +1575,7 @@ class UnfinishedForest { remove(node: UnfinishedTree) { assert(this.isMutable(), 'Cannot remove from an immutable forest'); // find by .id - let index = this.value.findIndex( + let index = this.mutable.findIndex( (n) => n.accountUpdate.value.id === node.accountUpdate.value.id ); @@ -1572,13 +1584,13 @@ class UnfinishedForest { // remove it node.siblings = undefined; - this.value.splice(index, 1); + this.mutable.splice(index, 1); } setToForest(forest: MerkleListBase) { if (this.isMutable()) { assert( - this.value.length === 0, + this.mutable.length === 0, 'Replacing a mutable forest that has existing children might be a mistake.' ); } @@ -1594,7 +1606,7 @@ class UnfinishedForest { return AccountUpdateForest.toFlatArray(this.final, mutate, depth); assert(this.isMutable(), 'final or mutable'); let flatUpdates: AccountUpdate[] = []; - for (let node of this.value) { + for (let node of this.mutable) { if (node.isDummy.toBoolean()) continue; let update = node.accountUpdate.value; if (mutate) update.body.callDepth = depth; @@ -1610,7 +1622,7 @@ class UnfinishedForest { return; } assert(this.isMutable(), 'final or mutable'); - for (let node of this.value) { + for (let node of this.mutable) { node.accountUpdate.value = Provable.toConstant( AccountUpdate, node.accountUpdate.value @@ -1624,13 +1636,14 @@ class UnfinishedForest { } print() { - assert(this.isMutable(), 'print(): unimplemented for final forests'); let indent = 0; let layout = ''; let toPretty = (a: UnfinishedForest) => { + if (a.isFinal()) layout += ' '.repeat(indent) + ' ( finalized forest )\n'; + assert(a.isMutable(), 'final or mutable'); indent += 2; - for (let tree of a.value!) { + for (let tree of a.mutable) { layout += ' '.repeat(indent) + `( ${tree.accountUpdate.value.label || ''} )` + diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 357e6b3849..7d2fbd7e23 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1634,8 +1634,8 @@ function diffRecursive( let { transaction, index, accountUpdate: input } = inputData; diff(transaction, index, prover.toPretty(), input.toPretty()); // TODO - let inputChildren = accountUpdates()!.get(input)!.children.value!; - let proverChildren = accountUpdates()!.get(prover)!.children.value!; + let inputChildren = accountUpdates()!.get(input)!.children.mutable!; + let proverChildren = accountUpdates()!.get(prover)!.children.mutable!; let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { let inputChild = inputChildren[i].accountUpdate.value; From ed0423b176bff28a3a999bad0976b9d2824ca828 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 21:13:47 +0100 Subject: [PATCH 419/524] extract tree --- src/lib/account_update.ts | 22 +++++++++++++--------- src/lib/mina/token/token-contract.ts | 25 +++++++++++-------------- src/lib/provable-types/packed.ts | 10 ++++++++-- src/lib/zkapp.ts | 2 +- 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 5f7019f457..adab08e9b0 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1050,6 +1050,14 @@ class AccountUpdate implements Types.AccountUpdate { node.children.print(); } + extractTree(): AccountUpdateTree { + let layout = accountUpdates(); + let hash = layout?.get(this)?.accountUpdate.hash; + let calls = layout?.finalizeAndRemove(this) ?? AccountUpdateForest.empty(); + let accountUpdate = HashedAccountUpdate.hash(this, hash); + return { accountUpdate, calls }; + } + static defaultAccountUpdate(address: PublicKey, tokenId?: Field) { return new AccountUpdate(Body.keepAll(address, tokenId)); } @@ -1659,15 +1667,10 @@ class UnfinishedForest { } function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { - let accountUpdate = - node.accountUpdate.hash !== undefined - ? new HashedAccountUpdate( - node.accountUpdate.hash, - Unconstrained.witness(() => - Provable.toConstant(AccountUpdate, node.accountUpdate.value) - ) - ) - : HashedAccountUpdate.hash(node.accountUpdate.value); + let accountUpdate = HashedAccountUpdate.hash( + node.accountUpdate.value, + node.accountUpdate.hash + ); let calls = node.children.finalize(); return { accountUpdate, isDummy: node.isDummy, calls }; } @@ -1736,6 +1739,7 @@ class AccountUpdateLayout { disattach(update: AccountUpdate) { let node = this.get(update); node?.siblings?.remove(node); + return node; } finalizeAndRemove(update: AccountUpdate) { diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index cb18a6c906..09950a2702 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -89,16 +89,16 @@ abstract class TokenContract extends SmartContract { /** * Approve a single account update (with arbitrarily many children). */ - approveAccountUpdate(accountUpdate: AccountUpdate) { - let forest = finalizeAccountUpdates([accountUpdate]); + approveAccountUpdate(accountUpdate: AccountUpdate | AccountUpdateTree) { + let forest = toForest([accountUpdate]); this.approveBase(forest); } /** * Approve a list of account updates (with arbitrarily many children). */ - approveAccountUpdates(accountUpdates: AccountUpdate[]) { - let forest = finalizeAccountUpdates(accountUpdates); + approveAccountUpdates(accountUpdates: (AccountUpdate | AccountUpdateTree)[]) { + let forest = toForest(accountUpdates); this.approveBase(forest); } @@ -127,19 +127,16 @@ abstract class TokenContract extends SmartContract { from.balanceChange = Int64.from(amount).neg(); to.balanceChange = Int64.from(amount); - let forest = finalizeAccountUpdates([from, to]); + let forest = toForest([from, to]); this.approveBase(forest); } } -function finalizeAccountUpdates(updates: AccountUpdate[]): AccountUpdateForest { - let trees = updates.map(finalizeAccountUpdate); +function toForest( + updates: (AccountUpdate | AccountUpdateTree)[] +): AccountUpdateForest { + let trees = updates.map((a) => + a instanceof AccountUpdate ? a.extractTree() : a + ); return AccountUpdateForest.from(trees); } - -function finalizeAccountUpdate(update: AccountUpdate): AccountUpdateTree { - let calls = accountUpdates()?.finalizeAndRemove(update); - calls ??= AccountUpdateForest.empty(); - - return { accountUpdate: HashedAccountUpdate.hash(update), calls }; -} diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index 9389de406a..d0b7ddef4f 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -212,9 +212,15 @@ class Hashed { /** * Wrap a value, and represent it by its hash in provable code. + * + * ```ts + * let hashed = HashedType.hash(value); + * ``` + * + * Optionally, if you already have the hash, you can pass it in and avoid recomputing it. */ - static hash(value: T): Hashed { - let hash = this._hash(value); + static hash(value: T, hash?: Field): Hashed { + hash ??= this._hash(value); let unconstrained = Unconstrained.witness(() => Provable.toConstant(this.innerProvable, value) ); diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 7d2fbd7e23..dcba0348b9 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -844,7 +844,7 @@ super.init(); this.#executionState = { transactionId, accountUpdate }; return accountUpdate; } - // same as this.self, but explicitly creates a _new_ account update + /** * Same as `SmartContract.self` but explicitly creates a new {@link AccountUpdate}. */ From acbb260b92be14a97aea6ec1cf1bca75879bb802 Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 22:27:19 +0100 Subject: [PATCH 420/524] minor --- src/lib/account_update.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index adab08e9b0..e25327ce57 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -790,11 +790,19 @@ class AccountUpdate implements Types.AccountUpdate { } /** - * Makes an {@link AccountUpdate} a child of this and approves it. + * Makes another {@link AccountUpdate} a child of this one. + * + * The parent-child relationship means that the child becomes part of the "statement" + * of the parent, and goes into the commitment that is authorized by either a signature + * or a proof. + * + * For a proof in particular, child account updates are contained in the public input + * of the proof that authorizes the parent account update. */ - approve(childUpdate: AccountUpdate) { - makeChildAccountUpdate(this, childUpdate); - accountUpdates()?.pushChild(this, childUpdate); + approve(child: AccountUpdate) { + child.body.callDepth = this.body.callDepth + 1; + accountUpdates()?.disattach(child); + accountUpdates()?.pushChild(this, child); } get balance() { @@ -1776,18 +1784,16 @@ class AccountUpdateLayout { } } +// TODO remove function createChildAccountUpdate( parent: AccountUpdate, childAddress: PublicKey, tokenId?: Field ) { let child = AccountUpdate.defaultAccountUpdate(childAddress, tokenId); - makeChildAccountUpdate(parent, child); - return child; -} -function makeChildAccountUpdate(parent: AccountUpdate, child: AccountUpdate) { child.body.callDepth = parent.body.callDepth + 1; AccountUpdate.unlink(child); + return child; } // authorization From d437c7e876eb3441b710e7a3a4128e2c7ff21b5b Mon Sep 17 00:00:00 2001 From: Gregor Date: Thu, 8 Feb 2024 22:44:35 +0100 Subject: [PATCH 421/524] not sure if this is a good idea yet --- src/lib/account_update.ts | 3 +++ src/lib/provable-types/auxiliary.ts | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 src/lib/provable-types/auxiliary.ts diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index e25327ce57..1b88d258d8 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -70,6 +70,7 @@ import { smartContractContext, } from './mina/smart-contract-context.js'; import { assert } from './util/assert.js'; +import { RandomId } from './provable-types/auxiliary.js'; // external API export { @@ -1392,10 +1393,12 @@ class HashedAccountUpdate extends Hashed.create( ) {} type AccountUpdateTree = { + // id: number; accountUpdate: Hashed; calls: MerkleListBase; }; const AccountUpdateTree: ProvableHashable = Struct({ + // id: RandomId, accountUpdate: HashedAccountUpdate.provable, calls: MerkleListBase(), }); diff --git a/src/lib/provable-types/auxiliary.ts b/src/lib/provable-types/auxiliary.ts new file mode 100644 index 0000000000..46ee535526 --- /dev/null +++ b/src/lib/provable-types/auxiliary.ts @@ -0,0 +1,13 @@ +import type { ProvableHashable } from '../hash.js'; + +export { RandomId }; + +const RandomId: ProvableHashable = { + sizeInFields: () => 0, + toFields: () => [], + toAuxiliary: (v = Math.random()) => [v], + fromFields: (_, [v]) => v, + check: () => {}, + toInput: () => ({}), + empty: () => Math.random(), +}; From fa935cc930f46f370e4222e05f44c2e2d4f18efd Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 09:33:18 +0100 Subject: [PATCH 422/524] refactor unfinished tree similarly --- src/lib/account_update.ts | 78 +++++++++++++++++++-------------------- src/lib/zkapp.ts | 4 +- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 1b88d258d8..bd1ec734b6 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1061,10 +1061,11 @@ class AccountUpdate implements Types.AccountUpdate { extractTree(): AccountUpdateTree { let layout = accountUpdates(); - let hash = layout?.get(this)?.accountUpdate.hash; + let hash = layout?.get(this)?.final?.hash; + let id = this.id; let calls = layout?.finalizeAndRemove(this) ?? AccountUpdateForest.empty(); let accountUpdate = HashedAccountUpdate.hash(this, hash); - return { accountUpdate, calls }; + return { accountUpdate, id, calls }; } static defaultAccountUpdate(address: PublicKey, tokenId?: Field) { @@ -1393,12 +1394,12 @@ class HashedAccountUpdate extends Hashed.create( ) {} type AccountUpdateTree = { - // id: number; + id: number; accountUpdate: Hashed; calls: MerkleListBase; }; const AccountUpdateTree: ProvableHashable = Struct({ - // id: RandomId, + id: RandomId, accountUpdate: HashedAccountUpdate.provable, calls: MerkleListBase(), }); @@ -1445,7 +1446,7 @@ class AccountUpdateForest extends MerkleList.create( let nodes = simpleForest.map((node) => { let accountUpdate = HashedAccountUpdate.hash(node.accountUpdate); let calls = AccountUpdateForest.fromSimpleForest(node.children); - return { accountUpdate, calls }; + return { accountUpdate, calls, id: node.accountUpdate.id }; }); return AccountUpdateForest.from(nodes); } @@ -1490,21 +1491,28 @@ function hashCons(forestHash: Field, nodeHash: Field) { * everything immediately. Instead, we maintain a structure consisting of either hashes or full account * updates that can be hashed into a final call forest at the end. * - * `UnfinishedForest` behaves like a tagged enum type: + * `UnfinishedForest` and `UnfinishedTree` behave like a tagged enum type: * ``` * type UnfinishedForest = * | Mutable of UnfinishedTree[] * | Final of AccountUpdateForest; + * + * type UnfinishedTree = ( + * | Mutable of AccountUpdate + * | Final of HashedAccountUpdate) + * ) & { children: UnfinishedForest, ... } * ``` */ type UnfinishedTree = { - // TODO it would be nice to make this closer to `Final of Hashed | Mutable of AccountUpdate` - accountUpdate: { hash?: Field; value: AccountUpdate }; + id: number; isDummy: Bool; // `children` must be readonly since it's referenced in each child's siblings readonly children: UnfinishedForest; siblings?: UnfinishedForest; -}; +} & ( + | { final: HashedAccountUpdate; mutable?: undefined } + | { final?: undefined; mutable: AccountUpdate } +); type UnfinishedForestFinal = UnfinishedForest & { final: AccountUpdateForest; @@ -1576,15 +1584,11 @@ class UnfinishedForest { this.mutable.push(node); } - // TODO this isn't quite right - shouldn't have to save the value in a circuit-accessible way if it's hashed pushTree(tree: AccountUpdateTree) { assert(this.isMutable(), 'Cannot push to an immutable forest'); - let value = AccountUpdate.dummy(); - Provable.asProver(() => { - value = tree.accountUpdate.value.get(); - }); this.mutable.push({ - accountUpdate: { hash: tree.accountUpdate.hash, value }, + final: tree.accountUpdate, + id: tree.id, isDummy: Bool(false), children: UnfinishedForest.fromForest(tree.calls), siblings: this, @@ -1594,9 +1598,7 @@ class UnfinishedForest { remove(node: UnfinishedTree) { assert(this.isMutable(), 'Cannot remove from an immutable forest'); // find by .id - let index = this.mutable.findIndex( - (n) => n.accountUpdate.value.id === node.accountUpdate.value.id - ); + let index = this.mutable.findIndex((n) => n.id === node.id); // nothing to do if it's not there if (index === -1) return; @@ -1627,7 +1629,7 @@ class UnfinishedForest { let flatUpdates: AccountUpdate[] = []; for (let node of this.mutable) { if (node.isDummy.toBoolean()) continue; - let update = node.accountUpdate.value; + let update = node.mutable ?? node.final.value.get(); if (mutate) update.body.callDepth = depth; let children = node.children.toFlatArray(mutate, depth + 1); flatUpdates.push(update, ...children); @@ -1642,14 +1644,12 @@ class UnfinishedForest { } assert(this.isMutable(), 'final or mutable'); for (let node of this.mutable) { - node.accountUpdate.value = Provable.toConstant( - AccountUpdate, - node.accountUpdate.value - ); - node.isDummy = Provable.toConstant(Bool, node.isDummy); - if (node.accountUpdate.hash !== undefined) { - node.accountUpdate.hash = node.accountUpdate.hash.toConstant(); + if (node.mutable !== undefined) { + node.mutable = Provable.toConstant(AccountUpdate, node.mutable); + } else { + node.final.hash = node.final.hash.toConstant(); } + node.isDummy = Provable.toConstant(Bool, node.isDummy); node.children.toConstantInPlace(); } } @@ -1663,10 +1663,11 @@ class UnfinishedForest { assert(a.isMutable(), 'final or mutable'); indent += 2; for (let tree of a.mutable) { - layout += - ' '.repeat(indent) + - `( ${tree.accountUpdate.value.label || ''} )` + - '\n'; + let label = tree.mutable?.label || ''; + if (tree.final !== undefined) { + Provable.asProver(() => (label = tree.final!.value.get().label)); + } + layout += ' '.repeat(indent) + `( ${label} )` + '\n'; toPretty(tree.children); } indent -= 2; @@ -1678,12 +1679,9 @@ class UnfinishedForest { } function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { - let accountUpdate = HashedAccountUpdate.hash( - node.accountUpdate.value, - node.accountUpdate.hash - ); + let accountUpdate = node.final ?? HashedAccountUpdate.hash(node.mutable); let calls = node.children.finalize(); - return { accountUpdate, isDummy: node.isDummy, calls }; + return { accountUpdate, id: node.id, isDummy: node.isDummy, calls }; } class AccountUpdateLayout { @@ -1695,7 +1693,8 @@ class AccountUpdateLayout { this.map = new Map(); root ??= AccountUpdate.dummy(); let rootTree: UnfinishedTree = { - accountUpdate: { value: root }, + mutable: root, + id: root.id, isDummy: Bool(false), children: UnfinishedForest.empty(), }; @@ -1709,15 +1708,16 @@ class AccountUpdateLayout { private getOrCreate(update: AccountUpdate | UnfinishedTree): UnfinishedTree { if (!(update instanceof AccountUpdate)) { - if (!this.map.has(update.accountUpdate.value.id)) { - this.map.set(update.accountUpdate.value.id, update); + if (!this.map.has(update.id)) { + this.map.set(update.id, update); } return update; } let node = this.map.get(update.id); if (node !== undefined) return node; node = { - accountUpdate: { value: update }, + mutable: update, + id: update.id, isDummy: update.isDummy(), children: UnfinishedForest.empty(), }; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index dcba0348b9..24cfbaf882 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1638,8 +1638,8 @@ function diffRecursive( let proverChildren = accountUpdates()!.get(prover)!.children.mutable!; let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { - let inputChild = inputChildren[i].accountUpdate.value; - let child = proverChildren[i].accountUpdate.value; + let inputChild = inputChildren[i].mutable; + let child = proverChildren[i].mutable; if (!inputChild || !child) return; diffRecursive(child, { transaction, index, accountUpdate: inputChild }); } From 44bf6c9835bf8623508c10b359dd759705a55a02 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 09:33:58 +0100 Subject: [PATCH 423/524] fix account update create signed outside tx --- src/lib/account_update.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index bd1ec734b6..c0eb98faf4 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -982,8 +982,8 @@ class AccountUpdate implements Types.AccountUpdate { if (isSameAsFeePayer) nonce++; // now, we check how often this account update already updated its nonce in // this tx, and increase nonce from `getAccount` by that amount - let layout = currentTransaction.get().layout; - layout.forEachPredecessor(update as AccountUpdate, (otherUpdate) => { + let layout = currentTransaction()?.layout; + layout?.forEachPredecessor(update as AccountUpdate, (otherUpdate) => { let shouldIncreaseNonce = otherUpdate.publicKey .equals(publicKey) .and(otherUpdate.tokenId.equals(tokenId)) From 0570bfb81bd5722bba6d8188535b2289de5cd667 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 09:34:10 +0100 Subject: [PATCH 424/524] another test case for token contract --- .../mina/token/token-contract.unit-test.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts index 2c9c059946..bcbe2bc882 100644 --- a/src/lib/mina/token/token-contract.unit-test.ts +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -40,7 +40,7 @@ Mina.setActiveInstance(Local); let [ { publicKey: sender, privateKey: senderKey }, { publicKey: tokenAddress, privateKey: tokenKey }, - { publicKey: otherAddress }, + { publicKey: otherAddress, privateKey: otherKey }, ] = Local.testAccounts; let token = new ExampleTokenContract(tokenAddress); @@ -85,3 +85,20 @@ await assert.rejects( () => Mina.transaction(sender, () => token.approveBase(forest)), /Field\.assertEquals\(\): 1 != 0/ ); + +// succeeds to approve deep account update tree with zero balance sum +let update4 = AccountUpdate.createSigned(otherAddress, tokenId); +update4.body.mayUseToken = AccountUpdate.MayUseToken.InheritFromParent; +update4.balanceChange = Int64.minusOne; +update4.body.callDepth = 2; + +forest = AccountUpdateForest.fromFlatArray([ + update1, + update2, + update3, + update4, +]); + +let approveTx = await Mina.transaction(sender, () => token.approveBase(forest)); +await approveTx.prove(); +await approveTx.sign([senderKey, otherKey]).send(); From e62be7e8284380f241cbf3712df1baf895ae68e8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 10:46:17 +0100 Subject: [PATCH 425/524] approve a whole tree --- src/lib/account_update.ts | 75 +++++++++++++++---- .../mina/account-update-layout.unit-test.ts | 41 +++++++++- src/lib/zkapp.ts | 9 ++- 3 files changed, 103 insertions(+), 22 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index c0eb98faf4..d1f41c7984 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -800,8 +800,10 @@ class AccountUpdate implements Types.AccountUpdate { * For a proof in particular, child account updates are contained in the public input * of the proof that authorizes the parent account update. */ - approve(child: AccountUpdate) { - child.body.callDepth = this.body.callDepth + 1; + approve(child: AccountUpdate | AccountUpdateTree) { + if (child instanceof AccountUpdate) { + child.body.callDepth = this.body.callDepth + 1; + } accountUpdates()?.disattach(child); accountUpdates()?.pushChild(this, child); } @@ -1659,7 +1661,10 @@ class UnfinishedForest { let layout = ''; let toPretty = (a: UnfinishedForest) => { - if (a.isFinal()) layout += ' '.repeat(indent) + ' ( finalized forest )\n'; + if (a.isFinal()) { + layout += ' '.repeat(indent) + ' ( finalized forest )\n'; + return; + } assert(a.isMutable(), 'final or mutable'); indent += 2; for (let tree of a.mutable) { @@ -1684,6 +1689,12 @@ function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { return { accountUpdate, id: node.id, isDummy: node.isDummy, calls }; } +function isUnfinished( + input: AccountUpdate | AccountUpdateTree | UnfinishedTree +): input is UnfinishedTree { + return 'final' in input || 'mutable' in input; +} + class AccountUpdateLayout { readonly map: Map; readonly root: UnfinishedTree; @@ -1702,30 +1713,62 @@ class AccountUpdateLayout { this.root = rootTree; } - get(update: AccountUpdate) { + get(update: AccountUpdate | AccountUpdateTree) { return this.map.get(update.id); } - private getOrCreate(update: AccountUpdate | UnfinishedTree): UnfinishedTree { - if (!(update instanceof AccountUpdate)) { + private getOrCreate( + update: AccountUpdate | AccountUpdateTree | UnfinishedTree + ): UnfinishedTree { + if (isUnfinished(update)) { if (!this.map.has(update.id)) { this.map.set(update.id, update); } return update; } let node = this.map.get(update.id); - if (node !== undefined) return node; - node = { - mutable: update, - id: update.id, - isDummy: update.isDummy(), - children: UnfinishedForest.empty(), - }; + if (node !== undefined) { + // might have to change node + if (update instanceof AccountUpdate) { + if (node.final !== undefined) { + Object.assign(node, { + mutable: update, + final: undefined, + children: UnfinishedForest.empty(), + }); + } + } else if (node.mutable !== undefined) { + Object.assign(node, { + mutable: undefined, + final: update.accountUpdate, + children: UnfinishedForest.fromForest(update.calls), + }); + } + return node; + } + if (update instanceof AccountUpdate) { + node = { + mutable: update, + id: update.id, + isDummy: update.isDummy(), + children: UnfinishedForest.empty(), + }; + } else { + node = { + final: update.accountUpdate, + id: update.id, + isDummy: Bool(false), + children: UnfinishedForest.fromForest(update.calls), + }; + } this.map.set(update.id, node); return node; } - pushChild(parent: AccountUpdate | UnfinishedTree, child: AccountUpdate) { + pushChild( + parent: AccountUpdate | UnfinishedTree, + child: AccountUpdate | AccountUpdateTree + ) { let parentNode = this.getOrCreate(parent); let childNode = this.getOrCreate(child); parentNode.children.push(childNode); @@ -1747,13 +1790,13 @@ class AccountUpdateLayout { this.setChildren(this.root, children); } - disattach(update: AccountUpdate) { + disattach(update: AccountUpdate | AccountUpdateTree) { let node = this.get(update); node?.siblings?.remove(node); return node; } - finalizeAndRemove(update: AccountUpdate) { + finalizeAndRemove(update: AccountUpdate | AccountUpdateTree) { let node = this.get(update); if (node === undefined) return; this.disattach(update); diff --git a/src/lib/mina/account-update-layout.unit-test.ts b/src/lib/mina/account-update-layout.unit-test.ts index f75f503194..71638976f2 100644 --- a/src/lib/mina/account-update-layout.unit-test.ts +++ b/src/lib/mina/account-update-layout.unit-test.ts @@ -1,5 +1,10 @@ import { Mina } from '../../index.js'; -import { AccountUpdate } from '../account_update.js'; +import { + AccountUpdate, + AccountUpdateForest, + AccountUpdateTree, + HashedAccountUpdate, +} from '../account_update.js'; import { UInt64 } from '../int.js'; import { SmartContract, method } from '../zkapp.js'; @@ -7,9 +12,30 @@ import { SmartContract, method } from '../zkapp.js'; class NestedCall extends SmartContract { @method deposit() { - const payerUpdate = AccountUpdate.createSigned(this.sender); + let payerUpdate = AccountUpdate.createSigned(this.sender); payerUpdate.send({ to: this.address, amount: UInt64.one }); } + + @method depositUsingTree() { + let payerUpdate = AccountUpdate.createSigned(this.sender); + let receiverUpdate = AccountUpdate.defaultAccountUpdate(this.address); + payerUpdate.send({ to: receiverUpdate, amount: UInt64.one }); + + // TODO make this super easy + let calls = AccountUpdateForest.empty(); + let tree: AccountUpdateTree = { + accountUpdate: HashedAccountUpdate.hash(payerUpdate), + id: payerUpdate.id, + calls, + }; + calls.push({ + accountUpdate: HashedAccountUpdate.hash(receiverUpdate), + id: receiverUpdate.id, + calls: AccountUpdateForest.empty(), + }); + + this.approve(tree); + } } // setup @@ -41,3 +67,14 @@ await depositTx.prove(); await depositTx.sign([senderKey]).send(); Mina.getBalance(zkappAddress).assertEquals(balanceBefore.add(1)); + +// deposit call using tree + +balanceBefore = balanceBefore.add(1); + +depositTx = await Mina.transaction(sender, () => zkapp.depositUsingTree()); +console.log(depositTx.toPretty()); +await depositTx.prove(); +await depositTx.sign([senderKey]).send(); + +Mina.getBalance(zkappAddress).assertEquals(balanceBefore.add(1)); diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 24cfbaf882..b604efabca 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -15,6 +15,7 @@ import { LazyProof, AccountUpdateForest, AccountUpdateLayout, + AccountUpdateTree, } from './account_update.js'; import { cloneCircuitValue, @@ -930,11 +931,11 @@ super.init(); * @param updateOrCallback * @returns The account update that was approved (needed when passing in a Callback) */ - approve(updateOrCallback: AccountUpdate | Callback) { + approve(updateOrCallback: AccountUpdate | AccountUpdateTree | Callback) { let accountUpdate = - updateOrCallback instanceof AccountUpdate - ? updateOrCallback - : Provable.witness(AccountUpdate, () => updateOrCallback.accountUpdate); + updateOrCallback instanceof Callback + ? Provable.witness(AccountUpdate, () => updateOrCallback.accountUpdate) + : updateOrCallback; this.self.approve(accountUpdate); return accountUpdate; } From ddde1de2de62a5029d7cfa1d644fc4da2dda1a18 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 11:04:20 +0100 Subject: [PATCH 426/524] proper type for packed/hashed.provable --- src/lib/provable-types/packed.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index d0b7ddef4f..c770b28145 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -52,7 +52,9 @@ class Packed { /** * Create a packed representation of `type`. You can then use `PackedType.pack(x)` to pack a value. */ - static create(type: ProvableExtended): typeof Packed { + static create(type: ProvableExtended): typeof Packed & { + provable: ProvableHashable>; + } { // compute size of packed representation let input = type.toInput(type.empty()); let packedSize = countFields(input); @@ -67,6 +69,11 @@ class Packed { static empty(): Packed { return Packed_.pack(type.empty()); } + + static get provable() { + assert(this._provable !== undefined, 'Packed not initialized'); + return this._provable; + } }; } @@ -118,10 +125,6 @@ class Packed { return this.constructor as typeof Packed; } - static get provable(): ProvableHashable> { - assert(this._provable !== undefined, 'Packed not initialized'); - return this._provable; - } static get innerProvable(): ProvableExtended { assert(this._innerProvable !== undefined, 'Packed not initialized'); return this._innerProvable; @@ -181,7 +184,9 @@ class Hashed { static create( type: ProvableHashable, hash?: (t: T) => Field - ): typeof Hashed { + ): typeof Hashed & { + provable: ProvableHashable>; + } { let _hash = hash ?? ((t: T) => Poseidon.hashPacked(type, t)); let dummyHash = _hash(type.empty()); @@ -198,6 +203,11 @@ class Hashed { static empty(): Hashed { return new this(dummyHash, Unconstrained.from(type.empty())); } + + static get provable() { + assert(this._provable !== undefined, 'Hashed not initialized'); + return this._provable; + } }; } @@ -254,10 +264,6 @@ class Hashed { return this.constructor as typeof Hashed; } - static get provable(): ProvableHashable> { - assert(this._provable !== undefined, 'Hashed not initialized'); - return this._provable; - } static get innerProvable(): ProvableHashable { assert(this._innerProvable !== undefined, 'Hashed not initialized'); return this._innerProvable; From fe85eb088c85a6dc6ed88e85185b42b596df5371 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 11:04:40 +0100 Subject: [PATCH 427/524] struct how it should be --- src/lib/circuit_value.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index ab6f802684..d1d24e9959 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -32,6 +32,7 @@ export { Struct, FlexibleProvable, FlexibleProvablePure, + Unconstrained, }; // internal API @@ -45,7 +46,7 @@ export { HashInput, InferJson, InferredProvable, - Unconstrained, + StructNoJson, }; type ProvableExtension = { @@ -477,6 +478,24 @@ function Struct< return Struct_ as any; } +function StructNoJson< + A, + T extends InferProvable = InferProvable, + Pure extends boolean = IsPure +>( + type: A +): (new (value: T) => T) & { _isStruct: true } & (Pure extends true + ? ProvablePure + : Provable) & { + toInput: (x: T) => { + fields?: Field[] | undefined; + packed?: [Field, number][] | undefined; + }; + empty: () => T; + } { + return Struct(type) satisfies Provable as any; +} + /** * Container which holds an unconstrained value. This can be used to pass values * between the out-of-circuit blocks in provable code. From e12edc73ef08c78aa03dd57f5248bd9c2b7e7acd Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 11:52:21 +0100 Subject: [PATCH 428/524] make account update tree a usable class --- src/index.ts | 1 + src/lib/account_update.ts | 98 ++++++++++++++----- .../mina/account-update-layout.unit-test.ts | 17 +--- src/lib/mina/token/forest-iterator.ts | 4 +- 4 files changed, 82 insertions(+), 38 deletions(-) diff --git a/src/index.ts b/src/index.ts index 3d3e7ca924..97240671ca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -76,6 +76,7 @@ export { ZkappPublicInput, TransactionVersion, AccountUpdateForest, + AccountUpdateTree, } from './lib/account_update.js'; export { TokenAccountUpdateIterator } from './lib/mina/token/forest-iterator.js'; diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index d1f41c7984..ed751763be 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -3,8 +3,7 @@ import { FlexibleProvable, provable, provablePure, - Struct, - Unconstrained, + StructNoJson, } from './circuit_value.js'; import { memoizationContext, memoizeWitness, Provable } from './provable.js'; import { Field, Bool } from './core.js'; @@ -35,12 +34,7 @@ import { Actions, } from '../bindings/mina-transaction/transaction-leaves.js'; import { TokenId as Base58TokenId } from './base58-encodings.js'; -import { - hashWithPrefix, - packToFields, - Poseidon, - ProvableHashable, -} from './hash.js'; +import { hashWithPrefix, packToFields, Poseidon } from './hash.js'; import { mocks, prefixes, @@ -79,6 +73,7 @@ export { ZkappPublicInput, TransactionVersion, AccountUpdateForest, + AccountUpdateTree, }; // internal API export { @@ -100,7 +95,7 @@ export { zkAppProver, dummySignature, LazyProof, - AccountUpdateTree, + AccountUpdateTreeBase, AccountUpdateLayout, hashAccountUpdate, HashedAccountUpdate, @@ -1067,7 +1062,7 @@ class AccountUpdate implements Types.AccountUpdate { let id = this.id; let calls = layout?.finalizeAndRemove(this) ?? AccountUpdateForest.empty(); let accountUpdate = HashedAccountUpdate.hash(this, hash); - return { accountUpdate, id, calls }; + return new AccountUpdateTree({ accountUpdate, id, calls }); } static defaultAccountUpdate(address: PublicKey, tokenId?: Field) { @@ -1395,15 +1390,17 @@ class HashedAccountUpdate extends Hashed.create( hashAccountUpdate ) {} -type AccountUpdateTree = { +type AccountUpdateTreeBase = { id: number; accountUpdate: Hashed; - calls: MerkleListBase; + calls: AccountUpdateForestBase; }; -const AccountUpdateTree: ProvableHashable = Struct({ +type AccountUpdateForestBase = MerkleListBase; + +const AccountUpdateTreeBase = StructNoJson({ id: RandomId, accountUpdate: HashedAccountUpdate.provable, - calls: MerkleListBase(), + calls: MerkleListBase(), }); /** @@ -1420,7 +1417,7 @@ const AccountUpdateTree: ProvableHashable = Struct({ * ``` */ class AccountUpdateForest extends MerkleList.create( - AccountUpdateTree, + AccountUpdateTreeBase, merkleListHash ) { static fromFlatArray(updates: AccountUpdate[]): AccountUpdateForest { @@ -1428,7 +1425,7 @@ class AccountUpdateForest extends MerkleList.create( return this.fromSimpleForest(simpleForest); } static toFlatArray( - forest: MerkleListBase, + forest: AccountUpdateForestBase, mutate = true, depth = 0 ) { @@ -1454,7 +1451,7 @@ class AccountUpdateForest extends MerkleList.create( } // TODO this comes from paranoia and might be removed later - static assertConstant(forest: MerkleListBase) { + static assertConstant(forest: AccountUpdateForestBase) { Provable.asProver(() => { forest.data.get().forEach(({ element: tree }) => { assert( @@ -1467,13 +1464,68 @@ class AccountUpdateForest extends MerkleList.create( } } +/** + * Class which represents a tree of account updates, + * in a compressed way which allows iterating and selectively witnessing the account updates. + * + * The (recursive) type signature is: + * ``` + * type AccountUpdateTree = { + * accountUpdate: Hashed; + * calls: AccountUpdateForest; + * }; + * type AccountUpdateForest = MerkleList; + * ``` + */ +class AccountUpdateTree extends StructNoJson({ + id: RandomId, + accountUpdate: HashedAccountUpdate.provable, + calls: AccountUpdateForest.provable, +}) { + /** + * Create a tree of account updates which only consists of a root. + */ + static from(update: AccountUpdate, hash?: Field) { + return new AccountUpdateTree({ + accountUpdate: HashedAccountUpdate.hash(update, hash), + id: update.id, + calls: AccountUpdateForest.empty(), + }); + } + + /** + * Add an {@link AccountUpdate} or {@link AccountUpdateTree} to the children of this tree's root. + * + * See {@link AccountUpdate.approve}. + */ + approve(update: AccountUpdate | AccountUpdateTree, hash?: Field) { + accountUpdates()?.disattach(update); + if (update instanceof AccountUpdate) { + this.calls.pushIf( + update.isDummy().not(), + AccountUpdateTree.from(update, hash) + ); + } else { + this.calls.push(update); + } + } + + // fix Struct type + static fromFields(fields: Field[], aux: any) { + return new AccountUpdateTree(super.fromFields(fields, aux)); + } + static empty() { + return new AccountUpdateTree(super.empty()); + } +} + // how to hash a forest -function merkleListHash(forestHash: Field, tree: AccountUpdateTree) { +function merkleListHash(forestHash: Field, tree: AccountUpdateTreeBase) { return hashCons(forestHash, hashNode(tree)); } -function hashNode(tree: AccountUpdateTree) { +function hashNode(tree: AccountUpdateTreeBase) { return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ tree.accountUpdate.hash, tree.calls.hash, @@ -1610,7 +1662,7 @@ class UnfinishedForest { this.mutable.splice(index, 1); } - setToForest(forest: MerkleListBase) { + setToForest(forest: AccountUpdateForestBase) { if (this.isMutable()) { assert( this.mutable.length === 0, @@ -1620,7 +1672,7 @@ class UnfinishedForest { return this.setFinal(new AccountUpdateForest(forest)); } - static fromForest(forest: MerkleListBase) { + static fromForest(forest: AccountUpdateForestBase) { return UnfinishedForest.empty().setToForest(forest); } @@ -1683,7 +1735,9 @@ class UnfinishedForest { } } -function toTree(node: UnfinishedTree): AccountUpdateTree & { isDummy: Bool } { +function toTree( + node: UnfinishedTree +): AccountUpdateTreeBase & { isDummy: Bool } { let accountUpdate = node.final ?? HashedAccountUpdate.hash(node.mutable); let calls = node.children.finalize(); return { accountUpdate, id: node.id, isDummy: node.isDummy, calls }; diff --git a/src/lib/mina/account-update-layout.unit-test.ts b/src/lib/mina/account-update-layout.unit-test.ts index 71638976f2..76964ae423 100644 --- a/src/lib/mina/account-update-layout.unit-test.ts +++ b/src/lib/mina/account-update-layout.unit-test.ts @@ -18,22 +18,11 @@ class NestedCall extends SmartContract { @method depositUsingTree() { let payerUpdate = AccountUpdate.createSigned(this.sender); - let receiverUpdate = AccountUpdate.defaultAccountUpdate(this.address); + let receiverUpdate = AccountUpdate.create(this.address); payerUpdate.send({ to: receiverUpdate, amount: UInt64.one }); - // TODO make this super easy - let calls = AccountUpdateForest.empty(); - let tree: AccountUpdateTree = { - accountUpdate: HashedAccountUpdate.hash(payerUpdate), - id: payerUpdate.id, - calls, - }; - calls.push({ - accountUpdate: HashedAccountUpdate.hash(receiverUpdate), - id: receiverUpdate.id, - calls: AccountUpdateForest.empty(), - }); - + let tree = AccountUpdateTree.from(payerUpdate); + tree.approve(receiverUpdate); this.approve(tree); } } diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index 441ae00951..8d0949e7a4 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -1,7 +1,7 @@ import { AccountUpdate, AccountUpdateForest, - AccountUpdateTree, + AccountUpdateTreeBase, TokenId, } from '../../account_update.js'; import { Field } from '../../core.js'; @@ -57,7 +57,7 @@ class TokenAccountUpdateIterator { selfToken: Field; constructor( - forest: MerkleListIterator, + forest: MerkleListIterator, mayUseToken: MayUseToken, selfToken: Field ) { From 24f9fb7315fa8da9789a3f83cdaa61d58cf701f6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 12:02:13 +0100 Subject: [PATCH 429/524] allow approving forests and some other polish --- src/lib/account_update.ts | 9 +++++++-- src/lib/mina/account-update-layout.unit-test.ts | 7 +------ src/lib/mina/token/token-contract.ts | 8 ++++---- src/lib/zkapp.ts | 8 +++++++- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index ed751763be..28e0d97701 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -795,7 +795,11 @@ class AccountUpdate implements Types.AccountUpdate { * For a proof in particular, child account updates are contained in the public input * of the proof that authorizes the parent account update. */ - approve(child: AccountUpdate | AccountUpdateTree) { + approve(child: AccountUpdate | AccountUpdateTree | AccountUpdateForest) { + if (child instanceof AccountUpdateForest) { + accountUpdates()?.setChildren(this, child); + return; + } if (child instanceof AccountUpdate) { child.body.callDepth = this.body.callDepth + 1; } @@ -1485,7 +1489,8 @@ class AccountUpdateTree extends StructNoJson({ /** * Create a tree of account updates which only consists of a root. */ - static from(update: AccountUpdate, hash?: Field) { + static from(update: AccountUpdate | AccountUpdateTree, hash?: Field) { + if (update instanceof AccountUpdateTree) return update; return new AccountUpdateTree({ accountUpdate: HashedAccountUpdate.hash(update, hash), id: update.id, diff --git a/src/lib/mina/account-update-layout.unit-test.ts b/src/lib/mina/account-update-layout.unit-test.ts index 76964ae423..7fd21e50b0 100644 --- a/src/lib/mina/account-update-layout.unit-test.ts +++ b/src/lib/mina/account-update-layout.unit-test.ts @@ -1,10 +1,5 @@ import { Mina } from '../../index.js'; -import { - AccountUpdate, - AccountUpdateForest, - AccountUpdateTree, - HashedAccountUpdate, -} from '../account_update.js'; +import { AccountUpdate, AccountUpdateTree } from '../account_update.js'; import { UInt64 } from '../int.js'; import { SmartContract, method } from '../zkapp.js'; diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 09950a2702..f1fe0a0fc1 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -49,23 +49,23 @@ abstract class TokenContract extends SmartContract { updates: AccountUpdateForest, callback: (update: AccountUpdate, usesToken: Bool) => void ) { - let forest = TokenAccountUpdateIterator.create(updates, this.token.id); + let iterator = TokenAccountUpdateIterator.create(updates, this.token.id); // iterate through the forest and apply user-defined logc for (let i = 0; i < MAX_ACCOUNT_UPDATES; i++) { - let { accountUpdate, usesThisToken } = forest.next(); + let { accountUpdate, usesThisToken } = iterator.next(); callback(accountUpdate, usesThisToken); } // prove that we checked all updates - forest.assertFinished( + iterator.assertFinished( `Number of account updates to approve exceed ` + `the supported limit of ${MAX_ACCOUNT_UPDATES}.\n` ); // skip hashing our child account updates in the method wrapper // since we just did that in the loop above - accountUpdates()?.setTopLevel(updates); + this.approve(updates); } /** diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index b604efabca..0c001e7c79 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -931,7 +931,13 @@ super.init(); * @param updateOrCallback * @returns The account update that was approved (needed when passing in a Callback) */ - approve(updateOrCallback: AccountUpdate | AccountUpdateTree | Callback) { + approve( + updateOrCallback: + | AccountUpdate + | AccountUpdateTree + | AccountUpdateForest + | Callback + ) { let accountUpdate = updateOrCallback instanceof Callback ? Provable.witness(AccountUpdate, () => updateOrCallback.accountUpdate) From adc23a2530dacf6058d2f3ad102b8e93c8b47559 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 12:13:02 +0100 Subject: [PATCH 430/524] calls -> children --- src/lib/account_update.ts | 41 ++++++++++++++------------- src/lib/mina/token/forest-iterator.ts | 4 +-- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 28e0d97701..aebba25314 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1064,9 +1064,10 @@ class AccountUpdate implements Types.AccountUpdate { let layout = accountUpdates(); let hash = layout?.get(this)?.final?.hash; let id = this.id; - let calls = layout?.finalizeAndRemove(this) ?? AccountUpdateForest.empty(); + let children = + layout?.finalizeAndRemove(this) ?? AccountUpdateForest.empty(); let accountUpdate = HashedAccountUpdate.hash(this, hash); - return new AccountUpdateTree({ accountUpdate, id, calls }); + return new AccountUpdateTree({ accountUpdate, id, children }); } static defaultAccountUpdate(address: PublicKey, tokenId?: Field) { @@ -1397,14 +1398,14 @@ class HashedAccountUpdate extends Hashed.create( type AccountUpdateTreeBase = { id: number; accountUpdate: Hashed; - calls: AccountUpdateForestBase; + children: AccountUpdateForestBase; }; type AccountUpdateForestBase = MerkleListBase; const AccountUpdateTreeBase = StructNoJson({ id: RandomId, accountUpdate: HashedAccountUpdate.provable, - calls: MerkleListBase(), + children: MerkleListBase(), }); /** @@ -1416,7 +1417,7 @@ const AccountUpdateTreeBase = StructNoJson({ * type AccountUpdateForest = MerkleList; * type AccountUpdateTree = { * accountUpdate: Hashed; - * calls: AccountUpdateForest; + * children: AccountUpdateForest; * }; * ``` */ @@ -1438,7 +1439,7 @@ class AccountUpdateForest extends MerkleList.create( let update = tree.accountUpdate.value.get(); if (mutate) update.body.callDepth = depth; flat.push(update); - flat.push(...this.toFlatArray(tree.calls, mutate, depth + 1)); + flat.push(...this.toFlatArray(tree.children, mutate, depth + 1)); } return flat; } @@ -1448,8 +1449,8 @@ class AccountUpdateForest extends MerkleList.create( ): AccountUpdateForest { let nodes = simpleForest.map((node) => { let accountUpdate = HashedAccountUpdate.hash(node.accountUpdate); - let calls = AccountUpdateForest.fromSimpleForest(node.children); - return { accountUpdate, calls, id: node.accountUpdate.id }; + let children = AccountUpdateForest.fromSimpleForest(node.children); + return { accountUpdate, children, id: node.accountUpdate.id }; }); return AccountUpdateForest.from(nodes); } @@ -1462,7 +1463,7 @@ class AccountUpdateForest extends MerkleList.create( Provable.isConstant(AccountUpdate, tree.accountUpdate.value.get()), 'account update not constant' ); - AccountUpdateForest.assertConstant(tree.calls); + AccountUpdateForest.assertConstant(tree.children); }); }); } @@ -1476,7 +1477,7 @@ class AccountUpdateForest extends MerkleList.create( * ``` * type AccountUpdateTree = { * accountUpdate: Hashed; - * calls: AccountUpdateForest; + * children: AccountUpdateForest; * }; * type AccountUpdateForest = MerkleList; * ``` @@ -1484,7 +1485,7 @@ class AccountUpdateForest extends MerkleList.create( class AccountUpdateTree extends StructNoJson({ id: RandomId, accountUpdate: HashedAccountUpdate.provable, - calls: AccountUpdateForest.provable, + children: AccountUpdateForest.provable, }) { /** * Create a tree of account updates which only consists of a root. @@ -1494,7 +1495,7 @@ class AccountUpdateTree extends StructNoJson({ return new AccountUpdateTree({ accountUpdate: HashedAccountUpdate.hash(update, hash), id: update.id, - calls: AccountUpdateForest.empty(), + children: AccountUpdateForest.empty(), }); } @@ -1506,12 +1507,12 @@ class AccountUpdateTree extends StructNoJson({ approve(update: AccountUpdate | AccountUpdateTree, hash?: Field) { accountUpdates()?.disattach(update); if (update instanceof AccountUpdate) { - this.calls.pushIf( + this.children.pushIf( update.isDummy().not(), AccountUpdateTree.from(update, hash) ); } else { - this.calls.push(update); + this.children.push(update); } } @@ -1533,7 +1534,7 @@ function merkleListHash(forestHash: Field, tree: AccountUpdateTreeBase) { function hashNode(tree: AccountUpdateTreeBase) { return Poseidon.hashWithPrefix(prefixes.accountUpdateNode, [ tree.accountUpdate.hash, - tree.calls.hash, + tree.children.hash, ]); } function hashCons(forestHash: Field, nodeHash: Field) { @@ -1649,7 +1650,7 @@ class UnfinishedForest { final: tree.accountUpdate, id: tree.id, isDummy: Bool(false), - children: UnfinishedForest.fromForest(tree.calls), + children: UnfinishedForest.fromForest(tree.children), siblings: this, }); } @@ -1744,8 +1745,8 @@ function toTree( node: UnfinishedTree ): AccountUpdateTreeBase & { isDummy: Bool } { let accountUpdate = node.final ?? HashedAccountUpdate.hash(node.mutable); - let calls = node.children.finalize(); - return { accountUpdate, id: node.id, isDummy: node.isDummy, calls }; + let children = node.children.finalize(); + return { accountUpdate, id: node.id, isDummy: node.isDummy, children }; } function isUnfinished( @@ -1800,7 +1801,7 @@ class AccountUpdateLayout { Object.assign(node, { mutable: undefined, final: update.accountUpdate, - children: UnfinishedForest.fromForest(update.calls), + children: UnfinishedForest.fromForest(update.children), }); } return node; @@ -1817,7 +1818,7 @@ class AccountUpdateLayout { final: update.accountUpdate, id: update.id, isDummy: Bool(false), - children: UnfinishedForest.fromForest(update.calls), + children: UnfinishedForest.fromForest(update.children), }; } this.map.set(update.id, node); diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index 8d0949e7a4..62b717e94a 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -87,8 +87,8 @@ class TokenAccountUpdateIterator { */ next() { // get next account update from the current forest (might be a dummy) - let { accountUpdate, calls } = this.currentLayer.forest.next(); - let childForest = AccountUpdateIterator.startIterating(calls); + let { accountUpdate, children } = this.currentLayer.forest.next(); + let childForest = AccountUpdateIterator.startIterating(children); let childLayer = { forest: childForest, mayUseToken: MayUseToken.InheritFromParent, From 314ab5fa0a731ae52d496b388b15cd4c1b08c964 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 12:14:46 +0100 Subject: [PATCH 431/524] more explicit name for globally accessible accout update layout --- src/lib/account_update.ts | 16 ++++++++-------- src/lib/mina/smart-contract-context.ts | 4 ++-- src/lib/mina/token/token-contract.ts | 2 -- src/lib/zkapp.ts | 6 +++--- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index aebba25314..2f6cb5925a 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -60,7 +60,7 @@ import { } from './provable-types/merkle-list.js'; import { Hashed } from './provable-types/packed.js'; import { - accountUpdates, + accountUpdateLayout, smartContractContext, } from './mina/smart-contract-context.js'; import { assert } from './util/assert.js'; @@ -797,14 +797,14 @@ class AccountUpdate implements Types.AccountUpdate { */ approve(child: AccountUpdate | AccountUpdateTree | AccountUpdateForest) { if (child instanceof AccountUpdateForest) { - accountUpdates()?.setChildren(this, child); + accountUpdateLayout()?.setChildren(this, child); return; } if (child instanceof AccountUpdate) { child.body.callDepth = this.body.callDepth + 1; } - accountUpdates()?.disattach(child); - accountUpdates()?.pushChild(this, child); + accountUpdateLayout()?.disattach(child); + accountUpdateLayout()?.pushChild(this, child); } get balance() { @@ -1055,13 +1055,13 @@ class AccountUpdate implements Types.AccountUpdate { } toPrettyLayout() { - let node = accountUpdates()?.get(this); + let node = accountUpdateLayout()?.get(this); assert(node !== undefined, 'AccountUpdate not found in layout'); node.children.print(); } extractTree(): AccountUpdateTree { - let layout = accountUpdates(); + let layout = accountUpdateLayout(); let hash = layout?.get(this)?.final?.hash; let id = this.id; let children = @@ -1140,7 +1140,7 @@ class AccountUpdate implements Types.AccountUpdate { * Disattach an account update from where it's currently located in the transaction */ static unlink(accountUpdate: AccountUpdate) { - accountUpdates()?.disattach(accountUpdate); + accountUpdateLayout()?.disattach(accountUpdate); } /** @@ -1505,7 +1505,7 @@ class AccountUpdateTree extends StructNoJson({ * See {@link AccountUpdate.approve}. */ approve(update: AccountUpdate | AccountUpdateTree, hash?: Field) { - accountUpdates()?.disattach(update); + accountUpdateLayout()?.disattach(update); if (update instanceof AccountUpdate) { this.children.pushIf( update.isDummy().not(), diff --git a/src/lib/mina/smart-contract-context.ts b/src/lib/mina/smart-contract-context.ts index a276a2ff1e..96f93c4e8c 100644 --- a/src/lib/mina/smart-contract-context.ts +++ b/src/lib/mina/smart-contract-context.ts @@ -3,7 +3,7 @@ import type { AccountUpdate, AccountUpdateLayout } from '../account_update.js'; import { Context } from '../global-context.js'; import { currentTransaction } from './transaction-context.js'; -export { smartContractContext, SmartContractContext, accountUpdates }; +export { smartContractContext, SmartContractContext, accountUpdateLayout }; type SmartContractContext = { this: SmartContract; @@ -14,7 +14,7 @@ let smartContractContext = Context.create({ default: null, }); -function accountUpdates() { +function accountUpdateLayout() { // in a smart contract, return the layout currently created in the contract call let layout = smartContractContext.get()?.selfLayout; diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index f1fe0a0fc1..38a2a64843 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -6,12 +6,10 @@ import { AccountUpdate, AccountUpdateForest, AccountUpdateTree, - HashedAccountUpdate, Permissions, } from '../../account_update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; import { TokenAccountUpdateIterator } from './forest-iterator.js'; -import { accountUpdates } from '../smart-contract-context.js'; export { TokenContract }; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 0c001e7c79..19c70e587c 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -62,7 +62,7 @@ import { SmartContractBase } from './mina/smart-contract-base.js'; import { ZkappStateLength } from './mina/mina-instance.js'; import { SmartContractContext, - accountUpdates, + accountUpdateLayout, smartContractContext, } from './mina/smart-contract-context.js'; @@ -1641,8 +1641,8 @@ function diffRecursive( let { transaction, index, accountUpdate: input } = inputData; diff(transaction, index, prover.toPretty(), input.toPretty()); // TODO - let inputChildren = accountUpdates()!.get(input)!.children.mutable!; - let proverChildren = accountUpdates()!.get(prover)!.children.mutable!; + let inputChildren = accountUpdateLayout()!.get(input)!.children.mutable!; + let proverChildren = accountUpdateLayout()!.get(prover)!.children.mutable!; let nChildren = inputChildren.length; for (let i = 0; i < nChildren; i++) { let inputChild = inputChildren[i].mutable; From e4a87290908a43140272dcf8281b579713ebe326 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 12:43:47 +0100 Subject: [PATCH 432/524] remove unnecessary changes --- src/lib/zkapp.ts | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 19c70e587c..3ac45d7307 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -167,7 +167,7 @@ function wrapMethod( } }); - let insideContract = SmartContractContext.get(); + let insideContract = smartContractContext.get(); if (!insideContract) { const { id, context } = SmartContractContext.enter( this, @@ -319,7 +319,7 @@ function wrapMethod( return result; } } finally { - SmartContractContext.leave(id); + smartContractContext.leave(id); } } @@ -459,7 +459,7 @@ function wrapMethod( accountUpdate.body.callData.assertEquals(callData); return result; } finally { - SmartContractContext.leave(id); + smartContractContext.leave(id); } }; } @@ -816,7 +816,7 @@ super.init(); */ get self(): AccountUpdate { let inTransaction = Mina.currentTransaction.has(); - let inSmartContract = SmartContractContext.get(); + let inSmartContract = smartContractContext.get(); if (!inTransaction && !inSmartContract) { // TODO: it's inefficient to return a fresh account update everytime, would be better to return a constant "non-writable" account update, // or even expose the .get() methods independently of any account update (they don't need one) @@ -1157,8 +1157,8 @@ super.init(); throw err; } let id: number; - let insideSmartContract = !!SmartContractContext.get(); - if (insideSmartContract) id = SmartContractContext.stepOutside(); + let insideSmartContract = !!smartContractContext.get(); + if (insideSmartContract) id = smartContractContext.enter(null); try { for (let methodIntf of methodIntfs) { let accountUpdate: AccountUpdate; @@ -1185,7 +1185,7 @@ super.init(); if (printSummary) console.log(methodIntf.methodName, summary()); } } finally { - if (insideSmartContract) SmartContractContext.leave(id!); + if (insideSmartContract) smartContractContext.leave(id!); } } return methodMetadata; @@ -1481,15 +1481,6 @@ const SmartContractContext = { let id = smartContractContext.enter(context); return { id, context }; }, - leave(id: number) { - smartContractContext.leave(id); - }, - stepOutside() { - return smartContractContext.enter(null); - }, - get() { - return smartContractContext.get(); - }, }; type DeployArgs = @@ -1500,7 +1491,7 @@ type DeployArgs = | undefined; function Account(address: PublicKey, tokenId?: Field) { - if (SmartContractContext.get()) { + if (smartContractContext.get()) { return AccountUpdate.create(address, tokenId).account; } else { return AccountUpdate.defaultAccountUpdate(address, tokenId).account; From aa37ee0902d51ea0883f36521dd190400477f5f0 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 12:44:05 +0100 Subject: [PATCH 433/524] some code organization --- src/lib/account_update.ts | 110 +++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 56 deletions(-) diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index 2f6cb5925a..bc5ec31f9c 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -1559,7 +1559,7 @@ function hashCons(forestHash: Field, nodeHash: Field) { * * type UnfinishedTree = ( * | Mutable of AccountUpdate - * | Final of HashedAccountUpdate) + * | Final of HashedAccountUpdate * ) & { children: UnfinishedForest, ... } * ``` */ @@ -1616,7 +1616,7 @@ class UnfinishedForest { if (this.isFinal()) return this.final; assert(this.isMutable(), 'final or mutable'); - let nodes = this.mutable.map(toTree); + let nodes = this.mutable.map(UnfinishedTree.finalize); let finalForest = AccountUpdateForest.empty(); for (let { isDummy, ...tree } of [...nodes].reverse()) { @@ -1644,17 +1644,6 @@ class UnfinishedForest { this.mutable.push(node); } - pushTree(tree: AccountUpdateTree) { - assert(this.isMutable(), 'Cannot push to an immutable forest'); - this.mutable.push({ - final: tree.accountUpdate, - id: tree.id, - isDummy: Bool(false), - children: UnfinishedForest.fromForest(tree.children), - siblings: this, - }); - } - remove(node: UnfinishedTree) { assert(this.isMutable(), 'Cannot remove from an immutable forest'); // find by .id @@ -1741,19 +1730,54 @@ class UnfinishedForest { } } -function toTree( - node: UnfinishedTree -): AccountUpdateTreeBase & { isDummy: Bool } { - let accountUpdate = node.final ?? HashedAccountUpdate.hash(node.mutable); - let children = node.children.finalize(); - return { accountUpdate, id: node.id, isDummy: node.isDummy, children }; -} +const UnfinishedTree = { + create(update: AccountUpdate | AccountUpdateTree): UnfinishedTree { + if (update instanceof AccountUpdate) { + return { + mutable: update, + id: update.id, + isDummy: update.isDummy(), + children: UnfinishedForest.empty(), + }; + } + return { + final: update.accountUpdate, + id: update.id, + isDummy: Bool(false), + children: UnfinishedForest.fromForest(update.children), + }; + }, -function isUnfinished( - input: AccountUpdate | AccountUpdateTree | UnfinishedTree -): input is UnfinishedTree { - return 'final' in input || 'mutable' in input; -} + setTo(node: UnfinishedTree, update: AccountUpdate | AccountUpdateTree) { + if (update instanceof AccountUpdate) { + if (node.final !== undefined) { + Object.assign(node, { + mutable: update, + final: undefined, + children: UnfinishedForest.empty(), + }); + } + } else if (node.mutable !== undefined) { + Object.assign(node, { + mutable: undefined, + final: update.accountUpdate, + children: UnfinishedForest.fromForest(update.children), + }); + } + }, + + finalize(node: UnfinishedTree): AccountUpdateTreeBase & { isDummy: Bool } { + let accountUpdate = node.final ?? HashedAccountUpdate.hash(node.mutable); + let children = node.children.finalize(); + return { accountUpdate, id: node.id, isDummy: node.isDummy, children }; + }, + + isUnfinished( + input: AccountUpdate | AccountUpdateTree | UnfinishedTree + ): input is UnfinishedTree { + return 'final' in input || 'mutable' in input; + }, +}; class AccountUpdateLayout { readonly map: Map; @@ -1780,47 +1804,21 @@ class AccountUpdateLayout { private getOrCreate( update: AccountUpdate | AccountUpdateTree | UnfinishedTree ): UnfinishedTree { - if (isUnfinished(update)) { + if (UnfinishedTree.isUnfinished(update)) { if (!this.map.has(update.id)) { this.map.set(update.id, update); } return update; } let node = this.map.get(update.id); + if (node !== undefined) { // might have to change node - if (update instanceof AccountUpdate) { - if (node.final !== undefined) { - Object.assign(node, { - mutable: update, - final: undefined, - children: UnfinishedForest.empty(), - }); - } - } else if (node.mutable !== undefined) { - Object.assign(node, { - mutable: undefined, - final: update.accountUpdate, - children: UnfinishedForest.fromForest(update.children), - }); - } + UnfinishedTree.setTo(node, update); return node; } - if (update instanceof AccountUpdate) { - node = { - mutable: update, - id: update.id, - isDummy: update.isDummy(), - children: UnfinishedForest.empty(), - }; - } else { - node = { - final: update.accountUpdate, - id: update.id, - isDummy: Bool(false), - children: UnfinishedForest.fromForest(update.children), - }; - } + + node = UnfinishedTree.create(update); this.map.set(update.id, node); return node; } From 694897dd06c3aad31e7c66ff30e4ac0144b75f2a Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 12:45:52 +0100 Subject: [PATCH 434/524] fixup examples build --- src/examples/zkapps/token_with_proofs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkapps/token_with_proofs.ts b/src/examples/zkapps/token_with_proofs.ts index 17c3d1bee0..556354db00 100644 --- a/src/examples/zkapps/token_with_proofs.ts +++ b/src/examples/zkapps/token_with_proofs.ts @@ -57,7 +57,7 @@ class TokenContract extends SmartContract { callback: Experimental.Callback ) { // TODO use token contract methods for approve - let senderAccountUpdate = this.approve(callback); + let senderAccountUpdate = this.approve(callback) as AccountUpdate; let amount = UInt64.from(1_000); let negativeAmount = Int64.fromObject( senderAccountUpdate.body.balanceChange From 1cb30fa5d4e0f004090b8894851ca50d036a54cc Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 12:50:54 +0100 Subject: [PATCH 435/524] changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ca42cd21a..d6b9b5cbf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/834a44002...HEAD) +### Breaking changes + +- Remove `AccountUpdate.children` and `AccountUpdate.parent` properties https://github.com/o1-labs/o1js/pull/1402 + - Also removes the optional `AccountUpdatesLayout` argument to `approve()` + - Adds `AccountUpdateTree` and `AccountUpdateForest`, new classes that represent a layout of account updates explicitly + - Both of the new types are now accepted as inputs to `approve()` + - `accountUpdate.extractTree()` to obtain the tree associated with an account update in the current transaction context. + ### Added - `MerkleList` to enable provable operations on a dynamically-sized list https://github.com/o1-labs/o1js/pull/1398 From a7b70aff3751efba74d536753bb51e1b0d225834 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 15:22:53 +0100 Subject: [PATCH 436/524] support custom empty hash in merkle list --- src/lib/provable-types/merkle-list.ts | 49 ++++++++++++++++++++------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index a06e19c563..6e949c050e 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -79,7 +79,7 @@ class MerkleList implements MerkleListBase { } isEmpty() { - return this.hash.equals(emptyHash); + return this.hash.equals(this.Constructor.emptyHash); } /** @@ -110,7 +110,7 @@ class MerkleList implements MerkleListBase { return Provable.witness(WithHash(this.innerProvable), () => { let [value, ...data] = this.data.get(); let head = value ?? { - previousHash: emptyHash, + previousHash: this.Constructor.emptyHash, element: this.innerProvable.empty(), }; this.data.set(data); @@ -141,6 +141,7 @@ class MerkleList implements MerkleListBase { pop(): T { let { previousHash, element } = this.popWitness(); let isEmpty = this.isEmpty(); + let emptyHash = this.Constructor.emptyHash; let currentHash = this.nextHash(previousHash, element); currentHash = Provable.if(isEmpty, emptyHash, currentHash); @@ -195,7 +196,8 @@ class MerkleList implements MerkleListBase { */ static create( type: ProvableHashable, - nextHash: (hash: Field, value: T) => Field = merkleListHash(type) + nextHash: (hash: Field, value: T) => Field = merkleListHash(type), + emptyHash_ = emptyHash ): typeof MerkleList & { // override static methods with strict types empty: () => MerkleList; @@ -211,13 +213,14 @@ class MerkleList implements MerkleListBase { }) as ProvableHashable>; static _nextHash = nextHash; + static _emptyHash = emptyHash_; static empty(): MerkleList { - return new this({ hash: emptyHash, data: Unconstrained.from([]) }); + return new this({ hash: emptyHash_, data: Unconstrained.from([]) }); } static from(array: T[]): MerkleList { - let { hash, data } = withHashes(array, nextHash); + let { hash, data } = withHashes(array, nextHash, emptyHash_); return new this({ data: Unconstrained.from(data), hash }); } @@ -230,6 +233,7 @@ class MerkleList implements MerkleListBase { // dynamic subclassing infra static _nextHash: ((hash: Field, t: any) => Field) | undefined; + static _emptyHash: Field | undefined; static _provable: ProvableHashable> | undefined; static _innerProvable: ProvableHashable | undefined; @@ -246,6 +250,11 @@ class MerkleList implements MerkleListBase { return this.Constructor._nextHash(hash, value); } + static get emptyHash() { + assert(this._emptyHash !== undefined, 'MerkleList not initialized'); + return this._emptyHash; + } + get innerProvable(): ProvableHashable { assert( this.Constructor._innerProvable !== undefined, @@ -277,7 +286,7 @@ type MerkleListIteratorBase = { /** * MerkleListIterator helps iterating through a Merkle list. - * This works similar to calling `list.pop()` repreatedly, but maintaining the entire list instead of removing elements. + * This works similar to calling `list.pop()` repeatedly, but maintaining the entire list instead of removing elements. * * The core method that supports iteration is {@link next()}. * @@ -309,13 +318,13 @@ class MerkleListIterator implements MerkleListIteratorBase { } isAtEnd() { - return this.currentHash.equals(emptyHash); + return this.currentHash.equals(this.Constructor.emptyHash); } jumpToEnd() { this.currentIndex.setTo( Unconstrained.witness(() => this.data.get().length) ); - this.currentHash = emptyHash; + this.currentHash = this.Constructor.emptyHash; } jumpToEndIf(condition: Bool) { Provable.asProver(() => { @@ -323,7 +332,11 @@ class MerkleListIterator implements MerkleListIteratorBase { this.currentIndex.set(this.data.get().length); } }); - this.currentHash = Provable.if(condition, emptyHash, this.currentHash); + this.currentHash = Provable.if( + condition, + this.Constructor.emptyHash, + this.currentHash + ); } next() { @@ -333,12 +346,13 @@ class MerkleListIterator implements MerkleListIteratorBase { WithHash(this.innerProvable), () => this.data.get()[this.currentIndex.get()] ?? { - previousHash: emptyHash, + previousHash: this.Constructor.emptyHash, element: this.innerProvable.empty(), } ); let isDummy = this.isAtEnd(); + let emptyHash = this.Constructor.emptyHash; let correctHash = this.nextHash(previousHash, element); let requiredHash = Provable.if(isDummy, emptyHash, correctHash); this.currentHash.assertEquals(requiredHash); @@ -372,7 +386,8 @@ class MerkleListIterator implements MerkleListIteratorBase { */ static create( type: ProvableHashable, - nextHash: (hash: Field, value: T) => Field = merkleListHash(type) + nextHash: (hash: Field, value: T) => Field = merkleListHash(type), + emptyHash_ = emptyHash ): typeof MerkleListIterator & { from: (array: T[]) => MerkleListIterator; startIterating: (list: MerkleListBase) => MerkleListIterator; @@ -392,9 +407,10 @@ class MerkleListIterator implements MerkleListIteratorBase { >; static _nextHash = nextHash; + static _emptyHash = emptyHash_; static from(array: T[]): MerkleListIterator { - let { hash, data } = withHashes(array, nextHash); + let { hash, data } = withHashes(array, nextHash, emptyHash_); return this.startIterating({ data: Unconstrained.from(data), hash }); } @@ -433,6 +449,7 @@ class MerkleListIterator implements MerkleListIteratorBase { // dynamic subclassing infra static _nextHash: ((hash: Field, value: any) => Field) | undefined; + static _emptyHash: Field | undefined; static _provable: ProvableHashable> | undefined; static _innerProvable: ProvableHashable | undefined; @@ -449,6 +466,11 @@ class MerkleListIterator implements MerkleListIteratorBase { return this.Constructor._nextHash(hash, value); } + static get emptyHash() { + assert(this._emptyHash !== undefined, 'MerkleList not initialized'); + return this._emptyHash; + } + get innerProvable(): ProvableHashable { assert( this.Constructor._innerProvable !== undefined, @@ -480,7 +502,8 @@ function merkleListHash(provable: ProvableHashable, prefix = '') { function withHashes( data: T[], - nextHash: (hash: Field, value: T) => Field + nextHash: (hash: Field, value: T) => Field, + emptyHash: Field ): { data: WithHash[]; hash: Field } { let n = data.length; let arrayWithHashes = Array>(n); From f3c1762d553ab031ba2382a227354ed22867a8fc Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 15:23:53 +0100 Subject: [PATCH 437/524] merkle list example that treats actions as a merkle list --- .../zkapps/reducer/actions-as-merkle-list.ts | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/examples/zkapps/reducer/actions-as-merkle-list.ts diff --git a/src/examples/zkapps/reducer/actions-as-merkle-list.ts b/src/examples/zkapps/reducer/actions-as-merkle-list.ts new file mode 100644 index 0000000000..86c38af844 --- /dev/null +++ b/src/examples/zkapps/reducer/actions-as-merkle-list.ts @@ -0,0 +1,148 @@ +/** + * This example shows how to iterate through incoming actions, not using `Reducer.reduce` but by + * treating the actions as a merkle list. + * + * This is mainly intended as an example for using `MerkleList`, but it might also be useful as + * a blueprint for processing actions in a custom and more explicit way. + */ +import { + AccountUpdate, + Bool, + Field, + MerkleList, + Mina, + Provable, + PublicKey, + Reducer, + SmartContract, + method, + assert, +} from 'o1js'; + +const { Actions } = AccountUpdate; + +// in this example, an action is just a public key +type Action = PublicKey; +const Action = PublicKey; + +// the actions within one account update are a Merkle list with a custom hash +const emptyHash = Actions.empty().hash; +const nextHash = (hash: Field, action: Action) => + Actions.pushEvent({ hash, data: [] }, action.toFields()).hash; + +class MerkleActions extends MerkleList.create(Action, nextHash, emptyHash) {} + +// the "action state" / actions from many account updates is a Merkle list +// of the above Merkle list, with another custom hash +let emptyActionsHash = Actions.emptyActionState(); +const nextActionsHash = (hash: Field, actions: MerkleActions) => + Actions.updateSequenceState(hash, actions.hash); + +class MerkleActionss extends MerkleList.create( + MerkleActions.provable, + nextActionsHash, + emptyActionsHash +) {} + +// constants for our static-sized provable code +const MAX_UPDATES_WITH_ACTIONS = 100; +const MAX_ACTIONS_PER_UPDATE = 2; + +/** + * This contract allows you to push either 1 or 2 public keys as actions, + * and has a reducer-like method which checks whether a given public key is contained in those actions. + */ +class ActionsContract extends SmartContract { + reducer = Reducer({ actionType: Action }); + + @method + postAddress(address: PublicKey) { + this.reducer.dispatch(address); + } + + // to exhibit the generality of reducer: can dispatch more than 1 action per account update + @method postTwoAddresses(a1: PublicKey, a2: PublicKey) { + this.reducer.dispatch(a1); + this.reducer.dispatch(a2); + } + + @method + assertContainsAddress(address: PublicKey) { + // get actions and, in a witness block, wrap them in a Merkle list of lists + + // note: need to reverse here because `getActions()` returns the last pushed action last, + // but MerkleList.from() wants it to be first to match the natural iteration order + let actionss = this.reducer.getActions().reverse(); + + let merkleActionss = Provable.witness(MerkleActionss.provable, () => + MerkleActionss.from(actionss.map((as) => MerkleActions.from(as))) + ); + + // prove that we know the correct action state + this.account.actionState.requireEquals(merkleActionss.hash); + + // now our provable code to process the actions is very straight-forward + // (note: if we're past the actual sizes, `.pop()` returns a dummy Action -- in this case, the "empty" public key which is not equal to any real address) + let hasAddress = Bool(false); + + for (let i = 0; i < MAX_UPDATES_WITH_ACTIONS; i++) { + let merkleActions = merkleActionss.pop(); + + for (let j = 0; j < MAX_ACTIONS_PER_UPDATE; j++) { + let action = merkleActions.pop(); + hasAddress = hasAddress.or(action.equals(address)); + } + } + + assert(hasAddress); + } +} + +// TESTS + +// set up a local blockchain + +let Local = Mina.LocalBlockchain({ proofsEnabled: false }); +Mina.setActiveInstance(Local); + +let [ + { publicKey: sender, privateKey: senderKey }, + { publicKey: zkappAddress, privateKey: zkappKey }, + { publicKey: otherAddress }, + { publicKey: anotherAddress }, +] = Local.testAccounts; + +let zkapp = new ActionsContract(zkappAddress); + +// deploy the contract + +await ActionsContract.compile(); +console.log( + `rows for ${MAX_UPDATES_WITH_ACTIONS} updates with actions`, + ActionsContract.analyzeMethods().assertContainsAddress.rows +); +let deployTx = await Mina.transaction(sender, () => zkapp.deploy()); +await deployTx.sign([senderKey, zkappKey]).send(); + +// push some actions + +let dispatchTx = await Mina.transaction(sender, () => { + zkapp.postAddress(otherAddress); + zkapp.postAddress(zkappAddress); + zkapp.postTwoAddresses(anotherAddress, sender); + zkapp.postAddress(anotherAddress); + zkapp.postTwoAddresses(zkappAddress, otherAddress); +}); +await dispatchTx.prove(); +await dispatchTx.sign([senderKey]).send(); + +assert(zkapp.reducer.getActions().length === 5); + +// check if the actions contain the `sender` address + +Local.setProofsEnabled(true); +let containsTx = await Mina.transaction(sender, () => + zkapp.assertContainsAddress(sender) +); +await containsTx.prove(); +await containsTx.sign([senderKey]).send(); From e60b6d77f3ff2a4ca5bb4b10dc2ec1d5dc727bde Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 17:34:12 +0100 Subject: [PATCH 438/524] address review --- src/examples/zkapps/dex/erc20.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index da30f7551e..a9cc97781b 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -23,11 +23,11 @@ export { Erc20Like, TrivialCoin }; * https://ethereum.org/en/developers/docs/standards/tokens/erc-20/ * * Differences to ERC-20: - * - No approvals / allowance, because zkApps don't need them and they are a security footgun. - * - `transfer()` and `transfer()` are collapsed into a single `transfer()` method which takes + * - No approvals / allowance, because zkApps don't need them and they are a security liability. + * - `transfer()` and `transferFrom()` are collapsed into a single `transfer()` method which takes * both the sender and the receiver as arguments. * - `transfer()` and `balanceOf()` can also take an account update as an argument. - * This form might be needed for zkApp token accounts, where the account update has to come from a method + * This form is needed for zkApp token accounts, where the account update has to come from a method * (in order to get proof authorization), and can't be created by the token contract itself. * - `transfer()` doesn't return a boolean, because in the zkApp protocol, * a transaction succeeds or fails in its entirety, and there is no need to handle partial failures. @@ -36,7 +36,7 @@ type Erc20Like = { // pure view functions which don't need @method name?: () => CircuitString; symbol?: () => CircuitString; - decimals?: () => Field; // TODO: should be UInt8 which doesn't exist yet + decimals?: () => Field; totalSupply(): UInt64; balanceOf(owner: PublicKey | AccountUpdate): UInt64; From b5ef43221392692036e91c864ea64719ff605860 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 12 Feb 2024 17:56:25 +0100 Subject: [PATCH 439/524] fixup dex live test --- src/examples/zkapps/dex/run_live.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/examples/zkapps/dex/run_live.ts b/src/examples/zkapps/dex/run_live.ts index 97632223ef..22c013966f 100644 --- a/src/examples/zkapps/dex/run_live.ts +++ b/src/examples/zkapps/dex/run_live.ts @@ -16,7 +16,7 @@ import { keys, tokenIds, } from './dex-with-actions.js'; -import { TokenContract } from './dex.js'; +import { TrivialCoin as TokenContract } from './erc20.js'; const useCustomLocalNetwork = process.env.USE_CUSTOM_LOCAL_NETWORK === 'true'; // setting this to a higher number allows you to skip a few transactions, to pick up after an error From 24475ddb6c6149639e53f22d1118b472e3e0e45d Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 10:33:28 +0100 Subject: [PATCH 440/524] bump live tests timeout --- .github/workflows/live-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/live-tests.yml b/.github/workflows/live-tests.yml index d6c3f72ff4..958c9bacfd 100644 --- a/.github/workflows/live-tests.yml +++ b/.github/workflows/live-tests.yml @@ -14,7 +14,7 @@ on: jobs: main-branch: - timeout-minutes: 25 + timeout-minutes: 45 runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.base_ref == 'main') services: @@ -40,7 +40,7 @@ jobs: mina-branch-name: o1js-main berkeley-branch: - timeout-minutes: 25 + timeout-minutes: 45 runs-on: ubuntu-latest if: github.ref == 'refs/heads/berkeley' || (github.event_name == 'pull_request' && github.base_ref == 'berkeley') services: @@ -66,7 +66,7 @@ jobs: mina-branch-name: berkeley develop-branch: - timeout-minutes: 25 + timeout-minutes: 45 runs-on: ubuntu-latest if: github.ref == 'refs/heads/develop' || (github.event_name == 'pull_request' && github.base_ref == 'develop') services: From 4b78640eac175943e26ee6d5e648404f7843664b Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 11:46:45 +0100 Subject: [PATCH 441/524] remove experimental callback and create-child-account-update --- src/index.ts | 9 ---- src/lib/account_update.ts | 13 ------ src/lib/circuit_value.ts | 5 +- src/lib/zkapp.ts | 98 ++++++--------------------------------- 4 files changed, 16 insertions(+), 109 deletions(-) diff --git a/src/index.ts b/src/index.ts index 97240671ca..c71bfafa93 100644 --- a/src/index.ts +++ b/src/index.ts @@ -110,19 +110,13 @@ export { ZkProgram }; export { Crypto } from './lib/crypto.js'; // experimental APIs -import { Callback } from './lib/zkapp.js'; -import { createChildAccountUpdate } from './lib/account_update.js'; import { memoizeWitness } from './lib/provable.js'; export { Experimental }; const Experimental_ = { - Callback, - createChildAccountUpdate, memoizeWitness, }; -type Callback_ = Callback; - /** * This module exposes APIs that are unstable, in the sense that the API surface is expected to change. * (Not unstable in the sense that they are less functional or tested than other parts.) @@ -132,10 +126,7 @@ namespace Experimental { * The old `Experimental.ZkProgram` API has been deprecated in favor of the new `ZkProgram` top-level import. */ export let ZkProgram = ExperimentalZkProgram; - export let createChildAccountUpdate = Experimental_.createChildAccountUpdate; export let memoizeWitness = Experimental_.memoizeWitness; - export let Callback = Experimental_.Callback; - export type Callback = Callback_; } Error.stackTraceLimit = 100000; diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index bc5ec31f9c..dace10bc9c 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -91,7 +91,6 @@ export { TokenId, Token, CallForest, - createChildAccountUpdate, zkAppProver, dummySignature, LazyProof, @@ -1888,18 +1887,6 @@ class AccountUpdateLayout { } } -// TODO remove -function createChildAccountUpdate( - parent: AccountUpdate, - childAddress: PublicKey, - tokenId?: Field -) { - let child = AccountUpdate.defaultAccountUpdate(childAddress, tokenId); - child.body.callDepth = parent.body.callDepth + 1; - AccountUpdate.unlink(child); - return child; -} - // authorization type ZkappCommand = { diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index d1d24e9959..9ac6daece5 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -627,10 +627,7 @@ function cloneCircuitValue(obj: T): T { if (typeof obj !== 'object' || obj === null) return obj; // HACK: callbacks - if ( - obj.constructor?.name.includes('GenericArgument') || - obj.constructor?.name.includes('Callback') - ) { + if (obj.constructor?.name.includes('GenericArgument')) { return obj; } // classes that define clone() are cloned using that method diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 3ac45d7307..9b4df0c32b 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -67,15 +67,7 @@ import { } from './mina/smart-contract-context.js'; // external API -export { - SmartContract, - method, - DeployArgs, - declareMethods, - Callback, - Account, - Reducer, -}; +export { SmartContract, method, DeployArgs, declareMethods, Account, Reducer }; const reservedPropNames = new Set(['_methods', '_']); @@ -511,57 +503,6 @@ function computeCallData( ]; } -class Callback extends GenericArgument { - instance: SmartContract; - methodIntf: MethodInterface & { returnType: Provable }; - args: any[]; - - result?: Result; - accountUpdate: AccountUpdate; - - static create( - instance: T, - methodName: K, - args: T[K] extends (...args: infer A) => any ? A : never - ) { - let ZkappClass = instance.constructor as typeof SmartContract; - let methodIntf_ = (ZkappClass._methods ?? []).find( - (i) => i.methodName === methodName - ); - if (methodIntf_ === undefined) - throw Error( - `Callback: could not find method ${ZkappClass.name}.${String( - methodName - )}` - ); - let methodIntf = { - ...methodIntf_, - returnType: methodIntf_.returnType ?? provable(null), - }; - - // call the callback, leveraging composability (if this is inside a smart contract method) - // to prove to the outer circuit that we called it - let result = (instance[methodName] as Function)(...args); - let accountUpdate = instance.self; - - let callback = new Callback({ - instance, - methodIntf, - args, - result, - accountUpdate, - isEmpty: false, - }); - - return callback; - } - - private constructor(self: Callback) { - super(); - Object.assign(this, self); - } -} - /** * The main zkapp class. To write a zkapp, extend this class as such: * @@ -913,37 +854,28 @@ super.init(); } /** - * Approve an account update or callback. This will include the account update in the zkApp's public input, - * which means it allows you to read and use its content in a proof, make assertions about it, and modify it. - * - * If this is called with a callback as the first parameter, it will first extract the account update produced by that callback. - * The extracted account update is returned. + * Approve an account update or tree / forest of updates. Doing this means you include the account update in the zkApp's public input, + * which allows you to read and use its content in a proof, make assertions about it, and modify it. * * ```ts - * \@method myApprovingMethod(callback: Callback) { - * let approvedUpdate = this.approve(callback); + * `@method` myApprovingMethod(update: AccountUpdate) { + * this.approve(update); + * + * // read balance on the account (for example) + * let balance = update.account.balance.getAndRequireEquals(); * } * ``` * * Under the hood, "approving" just means that the account update is made a child of the zkApp in the - * tree of account updates that forms the transaction. + * tree of account updates that forms the transaction. Similarly, if you pass in an {@link AccountUpdateTree}, + * the entire tree will become a subtree of the zkApp's account update. * - * @param updateOrCallback - * @returns The account update that was approved (needed when passing in a Callback) + * Passing in a forest is a bit different, because it means you set the entire children of the zkApp's account update + * at once. `approve()` will fail if the zkApp's account update already has children, to prevent you from accidentally + * excluding important information from the public input. */ - approve( - updateOrCallback: - | AccountUpdate - | AccountUpdateTree - | AccountUpdateForest - | Callback - ) { - let accountUpdate = - updateOrCallback instanceof Callback - ? Provable.witness(AccountUpdate, () => updateOrCallback.accountUpdate) - : updateOrCallback; - this.self.approve(accountUpdate); - return accountUpdate; + approve(update: AccountUpdate | AccountUpdateTree | AccountUpdateForest) { + this.self.approve(update); } send(args: { From 88e4ea6ec7f6184e5d15b714da45c7f779ae4093 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 11:52:16 +0100 Subject: [PATCH 442/524] remove "generic argument" which were only used for callbacks and could be implemented as an auxiliary provable nowadays --- src/lib/circuit_value.ts | 4 ---- src/lib/proof_system.ts | 44 ++++------------------------------------ src/lib/zkapp.ts | 1 - 3 files changed, 4 insertions(+), 45 deletions(-) diff --git a/src/lib/circuit_value.ts b/src/lib/circuit_value.ts index 9ac6daece5..d2e7465f70 100644 --- a/src/lib/circuit_value.ts +++ b/src/lib/circuit_value.ts @@ -626,10 +626,6 @@ function cloneCircuitValue(obj: T): T { // primitive JS types and functions aren't cloned if (typeof obj !== 'object' || obj === null) return obj; - // HACK: callbacks - if (obj.constructor?.name.includes('GenericArgument')) { - return obj; - } // classes that define clone() are cloned using that method if (obj.constructor !== undefined && 'clone' in obj.constructor) { return (obj as any).constructor.clone(obj); diff --git a/src/lib/proof_system.ts b/src/lib/proof_system.ts index 59ebf57ec4..bf0599c626 100644 --- a/src/lib/proof_system.ts +++ b/src/lib/proof_system.ts @@ -58,7 +58,6 @@ export { sortMethodArguments, getPreviousProofsForProver, MethodInterface, - GenericArgument, picklesRuleFromFunction, compileProgram, analyzeMethod, @@ -504,8 +503,7 @@ function sortMethodArguments( ): MethodInterface { let witnessArgs: Provable[] = []; let proofArgs: Subclass[] = []; - let allArgs: { type: 'proof' | 'witness' | 'generic'; index: number }[] = []; - let genericArgs: Subclass[] = []; + let allArgs: { type: 'proof' | 'witness'; index: number }[] = []; for (let i = 0; i < privateInputs.length; i++) { let privateInput = privateInputs[i]; if (isProof(privateInput)) { @@ -527,9 +525,6 @@ function sortMethodArguments( } else if (isAsFields((privateInput as any)?.provable)) { allArgs.push({ type: 'witness', index: witnessArgs.length }); witnessArgs.push((privateInput as any).provable); - } else if (isGeneric(privateInput)) { - allArgs.push({ type: 'generic', index: genericArgs.length }); - genericArgs.push(privateInput); } else { throw Error( `Argument ${ @@ -544,13 +539,7 @@ function sortMethodArguments( `Suggestion: You can merge more than two proofs by merging two at a time in a binary tree.` ); } - return { - methodName, - witnessArgs, - proofArgs, - allArgs, - genericArgs, - }; + return { methodName, witnessArgs, proofArgs, allArgs }; } function isAsFields( @@ -572,22 +561,6 @@ function isProof(type: unknown): type is typeof Proof { ); } -class GenericArgument { - isEmpty: boolean; - constructor(isEmpty = false) { - this.isEmpty = isEmpty; - } -} -let emptyGeneric = () => new GenericArgument(true); - -function isGeneric(type: unknown): type is typeof GenericArgument { - // the second case covers subclasses - return ( - type === GenericArgument || - (typeof type === 'function' && type.prototype instanceof GenericArgument) - ); -} - function getPreviousProofsForProver( methodArgs: any[], { allArgs }: MethodInterface @@ -605,11 +578,10 @@ function getPreviousProofsForProver( type MethodInterface = { methodName: string; // TODO: unify types of arguments - // "circuit types" should be flexible enough to encompass proofs and callback arguments + // proofs should just be `Provable` as well witnessArgs: Provable[]; proofArgs: Subclass[]; - genericArgs: Subclass[]; - allArgs: { type: 'witness' | 'proof' | 'generic'; index: number }[]; + allArgs: { type: 'witness' | 'proof'; index: number }[]; returnType?: Provable; }; @@ -778,8 +750,6 @@ function picklesRuleFromFunction( let input = toFieldVars(type.input, publicInput); let output = toFieldVars(type.output, publicOutput); previousStatements.push(MlPair(input, output)); - } else if (arg.type === 'generic') { - finalArgs[i] = argsWithoutPublicInput?.[i] ?? emptyGeneric(); } } let result: any; @@ -847,8 +817,6 @@ function synthesizeMethodArguments( let publicInput = empty(type.input); let publicOutput = empty(type.output); args.push(new Proof({ publicInput, publicOutput, proof: undefined })); - } else if (arg.type === 'generic') { - args.push(emptyGeneric()); } } return args; @@ -872,8 +840,6 @@ function methodArgumentsToConstant( constArgs.push( new Proof({ publicInput, publicOutput, proof: arg.proof }) ); - } else if (type === 'generic') { - constArgs.push(arg); } } return constArgs; @@ -901,8 +867,6 @@ function methodArgumentTypesAndValues( let type = provablePure({ input: types.input, output: types.output }); let value = { input: proof.publicInput, output: proof.publicOutput }; typesAndValues.push({ type, value }); - } else if (type === 'generic') { - typesAndValues.push({ type: Generic, value: arg }); } } return typesAndValues; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 9b4df0c32b..56be187d69 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -38,7 +38,6 @@ import { compileProgram, Empty, emptyValue, - GenericArgument, getPreviousProofsForProver, isAsFields, methodArgumentsToConstant, From ce1f2419c33ac758d84025a8f41e8590bebf1cbd Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 12:06:33 +0100 Subject: [PATCH 443/524] adapt token with proofs example --- src/examples/zkapps/token_with_proofs.ts | 54 +++++++++--------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/src/examples/zkapps/token_with_proofs.ts b/src/examples/zkapps/token_with_proofs.ts index 556354db00..7f23db5475 100644 --- a/src/examples/zkapps/token_with_proofs.ts +++ b/src/examples/zkapps/token_with_proofs.ts @@ -1,5 +1,4 @@ import { - isReady, method, Mina, AccountUpdate, @@ -7,21 +6,19 @@ import { SmartContract, PublicKey, UInt64, - shutdown, Int64, - Experimental, Permissions, DeployArgs, VerificationKey, TokenId, + AccountUpdateTree, + assert, } from 'o1js'; -await isReady; - class TokenContract extends SmartContract { deploy(args: DeployArgs) { super.deploy(args); - this.setPermissions({ + this.account.permissions.set({ ...Permissions.default(), access: Permissions.proofOrSignature(), }); @@ -30,12 +27,7 @@ class TokenContract extends SmartContract { @method tokenDeploy(deployer: PrivateKey, verificationKey: VerificationKey) { let address = deployer.toPublicKey(); - let tokenId = this.token.id; - let deployUpdate = Experimental.createChildAccountUpdate( - this.self, - address, - tokenId - ); + let deployUpdate = AccountUpdate.create(address, this.token.id); deployUpdate.account.permissions.set(Permissions.default()); deployUpdate.account.verificationKey.set(verificationKey); deployUpdate.sign(deployer); @@ -54,10 +46,12 @@ class TokenContract extends SmartContract { @method sendTokens( senderAddress: PublicKey, receiverAddress: PublicKey, - callback: Experimental.Callback + tree: AccountUpdateTree ) { // TODO use token contract methods for approve - let senderAccountUpdate = this.approve(callback) as AccountUpdate; + this.approve(tree); + assert(tree.children.isEmpty()); + let senderAccountUpdate = tree.accountUpdate.unhash(); let amount = UInt64.from(1_000); let negativeAmount = Int64.fromObject( senderAccountUpdate.body.balanceChange @@ -66,11 +60,7 @@ class TokenContract extends SmartContract { let tokenId = this.token.id; senderAccountUpdate.body.tokenId.assertEquals(tokenId); senderAccountUpdate.body.publicKey.assertEquals(senderAddress); - let receiverAccountUpdate = Experimental.createChildAccountUpdate( - this.self, - receiverAddress, - tokenId - ); + let receiverAccountUpdate = AccountUpdate.create(receiverAddress, tokenId); receiverAccountUpdate.balance.addInPlace(amount); } } @@ -162,13 +152,11 @@ await tx.send(); console.log('approve send from zkAppB'); tx = await Local.transaction(feePayer, () => { - let approveSendingCallback = Experimental.Callback.create( - zkAppB, - 'approveSend', - [] - ); - // we call the token contract with the callback - tokenZkApp.sendTokens(zkAppBAddress, zkAppCAddress, approveSendingCallback); + zkAppB.approveSend(); + let approveSendingTree = zkAppB.self.extractTree(); + + // we call the token contract with the tree + tokenZkApp.sendTokens(zkAppBAddress, zkAppCAddress, approveSendingTree); }); console.log('approve send (proof)'); await tx.prove(); @@ -183,13 +171,11 @@ console.log('approve send from zkAppC'); tx = await Local.transaction(feePayer, () => { // Pay for tokenAccount1's account creation AccountUpdate.fundNewAccount(feePayer); - let approveSendingCallback = Experimental.Callback.create( - zkAppC, - 'approveSend', - [] - ); - // we call the token contract with the callback - tokenZkApp.sendTokens(zkAppCAddress, tokenAccount1, approveSendingCallback); + zkAppC.approveSend(); + let approveSendingTree = zkAppC.self.extractTree(); + + // we call the token contract with the tree + tokenZkApp.sendTokens(zkAppCAddress, tokenAccount1, approveSendingTree); }); console.log('approve send (proof)'); await tx.prove(); @@ -199,5 +185,3 @@ console.log( `tokenAccount1's balance for tokenId: ${TokenId.toBase58(tokenId)}`, Mina.getBalance(tokenAccount1, tokenId).value.toBigInt() ); - -shutdown(); From 138aed5138d4ece5f3ed79146b4d1a620a845404 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 12:13:14 +0100 Subject: [PATCH 444/524] boy-scouting: token with proofs example --- src/examples/zkapps/token_with_proofs.ts | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/examples/zkapps/token_with_proofs.ts b/src/examples/zkapps/token_with_proofs.ts index 7f23db5475..eebd7344ad 100644 --- a/src/examples/zkapps/token_with_proofs.ts +++ b/src/examples/zkapps/token_with_proofs.ts @@ -82,7 +82,10 @@ class ZkAppC extends SmartContract { let Local = Mina.LocalBlockchain(); Mina.setActiveInstance(Local); -let feePayer = Local.testAccounts[0].privateKey; +let [ + { publicKey: sender, privateKey: senderKey }, + { publicKey: tokenAccount1 }, +] = Local.testAccounts; let initialBalance = 10_000_000; let tokenZkAppKey = PrivateKey.random(); @@ -94,9 +97,6 @@ let zkAppCAddress = zkAppCKey.toPublicKey(); let zkAppBKey = PrivateKey.random(); let zkAppBAddress = zkAppBKey.toPublicKey(); -let tokenAccount1Key = Local.testAccounts[1].privateKey; -let tokenAccount1 = tokenAccount1Key.toPublicKey(); - let tokenZkApp = new TokenContract(tokenZkAppAddress); let tokenId = tokenZkApp.token.id; @@ -108,7 +108,7 @@ console.log('tokenZkAppAddress', tokenZkAppAddress.toBase58()); console.log('zkAppB', zkAppBAddress.toBase58()); console.log('zkAppC', zkAppCAddress.toBase58()); console.log('receiverAddress', tokenAccount1.toBase58()); -console.log('feePayer', feePayer.toPublicKey().toBase58()); +console.log('feePayer', sender.toBase58()); console.log('-------------------------------------------'); console.log('compile (TokenContract)'); @@ -119,39 +119,39 @@ console.log('compile (ZkAppC)'); await ZkAppC.compile(); console.log('deploy tokenZkApp'); -tx = await Local.transaction(feePayer, () => { - AccountUpdate.fundNewAccount(feePayer, { initialBalance }); +tx = await Mina.transaction(sender, () => { + AccountUpdate.fundNewAccount(sender).balance.subInPlace(initialBalance); tokenZkApp.deploy({ zkappKey: tokenZkAppKey }); }); -await tx.send(); +await tx.sign([senderKey]).send(); console.log('deploy zkAppB'); -tx = await Local.transaction(feePayer, () => { - AccountUpdate.fundNewAccount(feePayer); +tx = await Mina.transaction(sender, () => { + AccountUpdate.fundNewAccount(sender); tokenZkApp.tokenDeploy(zkAppBKey, ZkAppB._verificationKey!); }); console.log('deploy zkAppB (proof)'); await tx.prove(); -await tx.send(); +await tx.sign([senderKey]).send(); console.log('deploy zkAppC'); -tx = await Local.transaction(feePayer, () => { - AccountUpdate.fundNewAccount(feePayer); +tx = await Mina.transaction(sender, () => { + AccountUpdate.fundNewAccount(sender); tokenZkApp.tokenDeploy(zkAppCKey, ZkAppC._verificationKey!); }); console.log('deploy zkAppC (proof)'); await tx.prove(); -await tx.send(); +await tx.sign([senderKey]).send(); console.log('mint token to zkAppB'); -tx = await Local.transaction(feePayer, () => { +tx = await Mina.transaction(sender, () => { tokenZkApp.mint(zkAppBAddress); }); await tx.prove(); -await tx.send(); +await tx.sign([senderKey]).send(); console.log('approve send from zkAppB'); -tx = await Local.transaction(feePayer, () => { +tx = await Mina.transaction(sender, () => { zkAppB.approveSend(); let approveSendingTree = zkAppB.self.extractTree(); @@ -160,7 +160,7 @@ tx = await Local.transaction(feePayer, () => { }); console.log('approve send (proof)'); await tx.prove(); -await tx.send(); +await tx.sign([senderKey]).send(); console.log( `zkAppC's balance for tokenId: ${TokenId.toBase58(tokenId)}`, @@ -168,9 +168,9 @@ console.log( ); console.log('approve send from zkAppC'); -tx = await Local.transaction(feePayer, () => { +tx = await Mina.transaction(sender, () => { // Pay for tokenAccount1's account creation - AccountUpdate.fundNewAccount(feePayer); + AccountUpdate.fundNewAccount(sender); zkAppC.approveSend(); let approveSendingTree = zkAppC.self.extractTree(); @@ -179,7 +179,7 @@ tx = await Local.transaction(feePayer, () => { }); console.log('approve send (proof)'); await tx.prove(); -await tx.send(); +await tx.sign([senderKey]).send(); console.log( `tokenAccount1's balance for tokenId: ${TokenId.toBase58(tokenId)}`, From 638ec7a55f8cf6a405346f532e9e8e448599d419 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 12:25:55 +0100 Subject: [PATCH 445/524] qol improvement for token transfer --- src/lib/mina/token/token-contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 38a2a64843..0979c372fb 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -108,7 +108,7 @@ abstract class TokenContract extends SmartContract { transfer( from: PublicKey | AccountUpdate, to: PublicKey | AccountUpdate, - amount: UInt64 + amount: UInt64 | number | bigint ) { // coerce the inputs to AccountUpdate and pass to `approveUpdates()` let tokenId = this.token.id; From ac501f5320b00c5479b3d99d78fcc04e121728ad Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 12:28:41 +0100 Subject: [PATCH 446/524] move example to token contract --- src/examples/zkapps/token_with_proofs.ts | 59 +++++++----------------- 1 file changed, 17 insertions(+), 42 deletions(-) diff --git a/src/examples/zkapps/token_with_proofs.ts b/src/examples/zkapps/token_with_proofs.ts index eebd7344ad..8d992ef476 100644 --- a/src/examples/zkapps/token_with_proofs.ts +++ b/src/examples/zkapps/token_with_proofs.ts @@ -6,25 +6,25 @@ import { SmartContract, PublicKey, UInt64, - Int64, Permissions, DeployArgs, VerificationKey, TokenId, - AccountUpdateTree, - assert, + TokenContract, + AccountUpdateForest, } from 'o1js'; -class TokenContract extends SmartContract { +class Token extends TokenContract { deploy(args: DeployArgs) { super.deploy(args); - this.account.permissions.set({ - ...Permissions.default(), - access: Permissions.proofOrSignature(), - }); this.balance.addInPlace(UInt64.from(initialBalance)); } + @method + approveBase(forest: AccountUpdateForest) { + this.checkZeroBalanceChange(forest); + } + @method tokenDeploy(deployer: PrivateKey, verificationKey: VerificationKey) { let address = deployer.toPublicKey(); let deployUpdate = AccountUpdate.create(address, this.token.id); @@ -34,48 +34,25 @@ class TokenContract extends SmartContract { } @method mint(receiverAddress: PublicKey) { - let amount = UInt64.from(1_000_000); + let amount = 1_000_000; this.token.mint({ address: receiverAddress, amount }); } @method burn(receiverAddress: PublicKey) { - let amount = UInt64.from(1_000); + let amount = 1_000; this.token.burn({ address: receiverAddress, amount }); } - - @method sendTokens( - senderAddress: PublicKey, - receiverAddress: PublicKey, - tree: AccountUpdateTree - ) { - // TODO use token contract methods for approve - this.approve(tree); - assert(tree.children.isEmpty()); - let senderAccountUpdate = tree.accountUpdate.unhash(); - let amount = UInt64.from(1_000); - let negativeAmount = Int64.fromObject( - senderAccountUpdate.body.balanceChange - ); - negativeAmount.assertEquals(Int64.from(amount).neg()); - let tokenId = this.token.id; - senderAccountUpdate.body.tokenId.assertEquals(tokenId); - senderAccountUpdate.body.publicKey.assertEquals(senderAddress); - let receiverAccountUpdate = AccountUpdate.create(receiverAddress, tokenId); - receiverAccountUpdate.balance.addInPlace(amount); - } } class ZkAppB extends SmartContract { @method approveSend() { - let amount = UInt64.from(1_000); - this.balance.subInPlace(amount); + this.balance.subInPlace(1_000); } } class ZkAppC extends SmartContract { @method approveSend() { - let amount = UInt64.from(1_000); - this.balance.subInPlace(amount); + this.balance.subInPlace(1_000); } } @@ -97,7 +74,7 @@ let zkAppCAddress = zkAppCKey.toPublicKey(); let zkAppBKey = PrivateKey.random(); let zkAppBAddress = zkAppBKey.toPublicKey(); -let tokenZkApp = new TokenContract(tokenZkAppAddress); +let tokenZkApp = new Token(tokenZkAppAddress); let tokenId = tokenZkApp.token.id; let zkAppB = new ZkAppB(zkAppBAddress, tokenId); @@ -112,7 +89,7 @@ console.log('feePayer', sender.toBase58()); console.log('-------------------------------------------'); console.log('compile (TokenContract)'); -await TokenContract.compile(); +await Token.compile(); console.log('compile (ZkAppB)'); await ZkAppB.compile(); console.log('compile (ZkAppC)'); @@ -153,10 +130,9 @@ await tx.sign([senderKey]).send(); console.log('approve send from zkAppB'); tx = await Mina.transaction(sender, () => { zkAppB.approveSend(); - let approveSendingTree = zkAppB.self.extractTree(); - // we call the token contract with the tree - tokenZkApp.sendTokens(zkAppBAddress, zkAppCAddress, approveSendingTree); + // we call the token contract with the self update + tokenZkApp.transfer(zkAppB.self, zkAppCAddress, 1_000); }); console.log('approve send (proof)'); await tx.prove(); @@ -172,10 +148,9 @@ tx = await Mina.transaction(sender, () => { // Pay for tokenAccount1's account creation AccountUpdate.fundNewAccount(sender); zkAppC.approveSend(); - let approveSendingTree = zkAppC.self.extractTree(); // we call the token contract with the tree - tokenZkApp.sendTokens(zkAppCAddress, tokenAccount1, approveSendingTree); + tokenZkApp.transfer(zkAppC.self, tokenAccount1, 1_000); }); console.log('approve send (proof)'); await tx.prove(); From 63b158c3ff88ddadc2d8121b375e1239c9c5e793 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 12:38:26 +0100 Subject: [PATCH 447/524] further cleanup token example --- src/examples/zkapps/token_with_proofs.ts | 47 +++++++----------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/src/examples/zkapps/token_with_proofs.ts b/src/examples/zkapps/token_with_proofs.ts index 8d992ef476..33e7b3fd96 100644 --- a/src/examples/zkapps/token_with_proofs.ts +++ b/src/examples/zkapps/token_with_proofs.ts @@ -5,34 +5,17 @@ import { PrivateKey, SmartContract, PublicKey, - UInt64, - Permissions, - DeployArgs, - VerificationKey, TokenId, TokenContract, AccountUpdateForest, } from 'o1js'; class Token extends TokenContract { - deploy(args: DeployArgs) { - super.deploy(args); - this.balance.addInPlace(UInt64.from(initialBalance)); - } - @method approveBase(forest: AccountUpdateForest) { this.checkZeroBalanceChange(forest); } - @method tokenDeploy(deployer: PrivateKey, verificationKey: VerificationKey) { - let address = deployer.toPublicKey(); - let deployUpdate = AccountUpdate.create(address, this.token.id); - deployUpdate.account.permissions.set(Permissions.default()); - deployUpdate.account.verificationKey.set(verificationKey); - deployUpdate.sign(deployer); - } - @method mint(receiverAddress: PublicKey) { let amount = 1_000_000; this.token.mint({ address: receiverAddress, amount }); @@ -97,28 +80,24 @@ await ZkAppC.compile(); console.log('deploy tokenZkApp'); tx = await Mina.transaction(sender, () => { - AccountUpdate.fundNewAccount(sender).balance.subInPlace(initialBalance); - tokenZkApp.deploy({ zkappKey: tokenZkAppKey }); + tokenZkApp.deploy(); + AccountUpdate.fundNewAccount(sender).send({ + to: tokenZkApp.self, + amount: initialBalance, + }); }); -await tx.sign([senderKey]).send(); +await tx.sign([senderKey, tokenZkAppKey]).send(); -console.log('deploy zkAppB'); +console.log('deploy zkAppB and zkAppC'); tx = await Mina.transaction(sender, () => { - AccountUpdate.fundNewAccount(sender); - tokenZkApp.tokenDeploy(zkAppBKey, ZkAppB._verificationKey!); + AccountUpdate.fundNewAccount(sender, 2); + zkAppC.deploy(); + zkAppB.deploy(); + tokenZkApp.approveAccountUpdates([zkAppC.self, zkAppB.self]); }); -console.log('deploy zkAppB (proof)'); +console.log('deploy zkAppB and zkAppC (proof)'); await tx.prove(); -await tx.sign([senderKey]).send(); - -console.log('deploy zkAppC'); -tx = await Mina.transaction(sender, () => { - AccountUpdate.fundNewAccount(sender); - tokenZkApp.tokenDeploy(zkAppCKey, ZkAppC._verificationKey!); -}); -console.log('deploy zkAppC (proof)'); -await tx.prove(); -await tx.sign([senderKey]).send(); +await tx.sign([senderKey, zkAppBKey, zkAppCKey]).send(); console.log('mint token to zkAppB'); tx = await Mina.transaction(sender, () => { From 40dc510ed039c14763554915d5300ff41183153b Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 12:41:49 +0100 Subject: [PATCH 448/524] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6b9b5cbf9..bde92e484f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Adds `AccountUpdateTree` and `AccountUpdateForest`, new classes that represent a layout of account updates explicitly - Both of the new types are now accepted as inputs to `approve()` - `accountUpdate.extractTree()` to obtain the tree associated with an account update in the current transaction context. +- Remove `Experimental.Callback` API https://github.com/o1-labs/o1js/pull/1430 ### Added From d7d9712f0bdb0c18ab959db71b187edcda558120 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 14:12:11 +0100 Subject: [PATCH 449/524] fix unit test --- src/lib/proof_system.unit-test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/proof_system.unit-test.ts b/src/lib/proof_system.unit-test.ts index 5da3390e71..d842cd2a1c 100644 --- a/src/lib/proof_system.unit-test.ts +++ b/src/lib/proof_system.unit-test.ts @@ -49,7 +49,6 @@ it('pickles rule creation', async () => { { type: 'proof', index: 0 }, { type: 'witness', index: 0 }, ], - genericArgs: [], }); // store compiled tag From 884a9d5f821e76d9c0376970aeb3f7481bc1f230 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 14:37:09 +0100 Subject: [PATCH 450/524] elliptic_curve --- src/bindings | 2 +- src/lib/crypto.ts | 2 +- src/lib/foreign-curve.ts | 2 +- src/lib/foreign-curve.unit-test.ts | 2 +- src/lib/foreign-ecdsa.ts | 2 +- src/lib/gadgets/ecdsa.unit-test.ts | 2 +- src/lib/gadgets/elliptic-curve.ts | 2 +- src/lib/gadgets/elliptic-curve.unit-test.ts | 2 +- src/lib/group.ts | 2 +- src/mina-signer/src/signature.ts | 2 +- src/provable/curve-bigint.ts | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/bindings b/src/bindings index 3503101051..44bbc3feb1 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 35031010512993426bf226dbd6f1048fa1da09cc +Subproject commit 44bbc3feb153b0206d372250b868871a145545c8 diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts index 3786284a9e..c58a220277 100644 --- a/src/lib/crypto.ts +++ b/src/lib/crypto.ts @@ -2,7 +2,7 @@ import { CurveParams as CurveParams_ } from '../bindings/crypto/elliptic-curve-e import { CurveAffine, createCurveAffine, -} from '../bindings/crypto/elliptic_curve.js'; +} from '../bindings/crypto/elliptic-curve.js'; // crypto namespace const Crypto = { diff --git a/src/lib/foreign-curve.ts b/src/lib/foreign-curve.ts index 08fb733bfd..56cbfe6ee1 100644 --- a/src/lib/foreign-curve.ts +++ b/src/lib/foreign-curve.ts @@ -2,7 +2,7 @@ import { CurveParams, CurveAffine, createCurveAffine, -} from '../bindings/crypto/elliptic_curve.js'; +} from '../bindings/crypto/elliptic-curve.js'; import type { Group } from './group.js'; import { ProvablePureExtended } from './circuit_value.js'; import { AlmostForeignField, createForeignField } from './foreign-field.js'; diff --git a/src/lib/foreign-curve.unit-test.ts b/src/lib/foreign-curve.unit-test.ts index 9cdac03730..b45c0ce930 100644 --- a/src/lib/foreign-curve.unit-test.ts +++ b/src/lib/foreign-curve.unit-test.ts @@ -1,6 +1,6 @@ import { createForeignCurve } from './foreign-curve.js'; import { Fq } from '../bindings/crypto/finite_field.js'; -import { Vesta as V } from '../bindings/crypto/elliptic_curve.js'; +import { Vesta as V } from '../bindings/crypto/elliptic-curve.js'; import { Provable } from './provable.js'; import { Field } from './field.js'; import { Crypto } from './crypto.js'; diff --git a/src/lib/foreign-ecdsa.ts b/src/lib/foreign-ecdsa.ts index bccbaa77ab..534850c134 100644 --- a/src/lib/foreign-ecdsa.ts +++ b/src/lib/foreign-ecdsa.ts @@ -1,5 +1,5 @@ import { provableFromClass } from '../bindings/lib/provable-snarky.js'; -import { CurveParams } from '../bindings/crypto/elliptic_curve.js'; +import { CurveParams } from '../bindings/crypto/elliptic-curve.js'; import { ProvablePureExtended } from './circuit_value.js'; import { FlexiblePoint, diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index a04ccc932c..b83f491745 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -1,4 +1,4 @@ -import { createCurveAffine } from '../../bindings/crypto/elliptic_curve.js'; +import { createCurveAffine } from '../../bindings/crypto/elliptic-curve.js'; import { Ecdsa, EllipticCurve, diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts index 87876e788c..c09487d44c 100644 --- a/src/lib/gadgets/elliptic-curve.ts +++ b/src/lib/gadgets/elliptic-curve.ts @@ -13,7 +13,7 @@ import { CurveAffine, affineAdd, affineDouble, -} from '../../bindings/crypto/elliptic_curve.js'; +} from '../../bindings/crypto/elliptic-curve.js'; import { Bool } from '../bool.js'; import { provable } from '../circuit_value.js'; import { assertPositiveInteger } from '../../bindings/crypto/non-negative.js'; diff --git a/src/lib/gadgets/elliptic-curve.unit-test.ts b/src/lib/gadgets/elliptic-curve.unit-test.ts index 39a6d41ce0..0119829060 100644 --- a/src/lib/gadgets/elliptic-curve.unit-test.ts +++ b/src/lib/gadgets/elliptic-curve.unit-test.ts @@ -1,5 +1,5 @@ import { CurveParams } from '../../bindings/crypto/elliptic-curve-examples.js'; -import { createCurveAffine } from '../../bindings/crypto/elliptic_curve.js'; +import { createCurveAffine } from '../../bindings/crypto/elliptic-curve.js'; import { array, equivalentProvable, diff --git a/src/lib/group.ts b/src/lib/group.ts index 67b021097e..c1367d2df3 100644 --- a/src/lib/group.ts +++ b/src/lib/group.ts @@ -2,7 +2,7 @@ import { Field, FieldVar } from './field.js'; import { Scalar } from './scalar.js'; import { Snarky } from '../snarky.js'; import { Field as Fp } from '../provable/field-bigint.js'; -import { GroupAffine, Pallas } from '../bindings/crypto/elliptic_curve.js'; +import { GroupAffine, Pallas } from '../bindings/crypto/elliptic-curve.js'; import { Provable } from './provable.js'; import { Bool } from './bool.js'; diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 9e719cebdd..fb29715f8c 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -26,7 +26,7 @@ import { } from '../../bindings/lib/binable.js'; import { base58 } from '../../lib/base58.js'; import { versionBytes } from '../../bindings/crypto/constants.js'; -import { Pallas } from '../../bindings/crypto/elliptic_curve.js'; +import { Pallas } from '../../bindings/crypto/elliptic-curve.js'; import { NetworkId } from './TSTypes.js'; export { diff --git a/src/provable/curve-bigint.ts b/src/provable/curve-bigint.ts index f4d18dc4a9..1e3adac16c 100644 --- a/src/provable/curve-bigint.ts +++ b/src/provable/curve-bigint.ts @@ -1,5 +1,5 @@ import { Fq, mod } from '../bindings/crypto/finite_field.js'; -import { GroupProjective, Pallas } from '../bindings/crypto/elliptic_curve.js'; +import { GroupProjective, Pallas } from '../bindings/crypto/elliptic-curve.js'; import { versionBytes } from '../bindings/crypto/constants.js'; import { record, From 412d3badabdbcee816e9f3f7ffc87cd3b085eb18 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 14:38:45 +0100 Subject: [PATCH 451/524] finite_field --- src/bindings | 2 +- src/lib/foreign-curve.unit-test.ts | 2 +- src/lib/foreign-field.ts | 2 +- src/lib/gadgets/basic.ts | 2 +- src/lib/gadgets/bitwise.unit-test.ts | 2 +- src/lib/gadgets/elliptic-curve.ts | 2 +- src/lib/gadgets/foreign-field.ts | 2 +- src/lib/gadgets/foreign-field.unit-test.ts | 2 +- src/lib/gadgets/range-check.ts | 2 +- src/lib/gadgets/range-check.unit-test.ts | 2 +- src/lib/gadgets/sha256.ts | 2 +- src/lib/gadgets/test-utils.ts | 2 +- src/lib/provable-context.ts | 2 +- src/lib/testing/random.ts | 2 +- src/mina-signer/src/nullifier.ts | 2 +- src/mina-signer/src/signature.unit-test.ts | 2 +- src/provable/curve-bigint.ts | 2 +- src/provable/field-bigint.ts | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/bindings b/src/bindings index 44bbc3feb1..9485b2c284 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 44bbc3feb153b0206d372250b868871a145545c8 +Subproject commit 9485b2c2849d767dd66a9e69763e6e167ff3c470 diff --git a/src/lib/foreign-curve.unit-test.ts b/src/lib/foreign-curve.unit-test.ts index b45c0ce930..8edb9d5875 100644 --- a/src/lib/foreign-curve.unit-test.ts +++ b/src/lib/foreign-curve.unit-test.ts @@ -1,5 +1,5 @@ import { createForeignCurve } from './foreign-curve.js'; -import { Fq } from '../bindings/crypto/finite_field.js'; +import { Fq } from '../bindings/crypto/finite-field.js'; import { Vesta as V } from '../bindings/crypto/elliptic-curve.js'; import { Provable } from './provable.js'; import { Field } from './field.js'; diff --git a/src/lib/foreign-field.ts b/src/lib/foreign-field.ts index 788b95129e..2047ab26db 100644 --- a/src/lib/foreign-field.ts +++ b/src/lib/foreign-field.ts @@ -3,7 +3,7 @@ import { Fp, FiniteField, createField, -} from '../bindings/crypto/finite_field.js'; +} from '../bindings/crypto/finite-field.js'; import { Field, FieldVar, checkBitLength, withMessage } from './field.js'; import { Provable } from './provable.js'; import { Bool } from './bool.js'; diff --git a/src/lib/gadgets/basic.ts b/src/lib/gadgets/basic.ts index 8f6f218935..4db98792bc 100644 --- a/src/lib/gadgets/basic.ts +++ b/src/lib/gadgets/basic.ts @@ -1,7 +1,7 @@ /** * Basic gadgets that only use generic gates */ -import { Fp } from '../../bindings/crypto/finite_field.js'; +import { Fp } from '../../bindings/crypto/finite-field.js'; import type { Field, VarField } from '../field.js'; import { existsOne, toVar } from './common.js'; import { Gates } from '../gates.js'; diff --git a/src/lib/gadgets/bitwise.unit-test.ts b/src/lib/gadgets/bitwise.unit-test.ts index f6f186e941..61cca631f1 100644 --- a/src/lib/gadgets/bitwise.unit-test.ts +++ b/src/lib/gadgets/bitwise.unit-test.ts @@ -5,7 +5,7 @@ import { field, fieldWithRng, } from '../testing/equivalent.js'; -import { Fp, mod } from '../../bindings/crypto/finite_field.js'; +import { Fp, mod } from '../../bindings/crypto/finite-field.js'; import { Field } from '../core.js'; import { Gadgets } from './gadgets.js'; import { Random } from '../testing/property.js'; diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts index c09487d44c..c9aed26c31 100644 --- a/src/lib/gadgets/elliptic-curve.ts +++ b/src/lib/gadgets/elliptic-curve.ts @@ -1,4 +1,4 @@ -import { inverse, mod } from '../../bindings/crypto/finite_field.js'; +import { inverse, mod } from '../../bindings/crypto/finite-field.js'; import { Field } from '../field.js'; import { Provable } from '../provable.js'; import { assert, exists } from './common.js'; diff --git a/src/lib/gadgets/foreign-field.ts b/src/lib/gadgets/foreign-field.ts index 30c3725b80..7476050856 100644 --- a/src/lib/gadgets/foreign-field.ts +++ b/src/lib/gadgets/foreign-field.ts @@ -4,7 +4,7 @@ import { inverse as modInverse, mod, -} from '../../bindings/crypto/finite_field.js'; +} from '../../bindings/crypto/finite-field.js'; import { provableTuple } from '../../bindings/lib/provable-snarky.js'; import { Bool } from '../bool.js'; import { Unconstrained } from '../circuit_value.js'; diff --git a/src/lib/gadgets/foreign-field.unit-test.ts b/src/lib/gadgets/foreign-field.unit-test.ts index 135fca61a1..61325ccc2a 100644 --- a/src/lib/gadgets/foreign-field.unit-test.ts +++ b/src/lib/gadgets/foreign-field.unit-test.ts @@ -1,4 +1,4 @@ -import type { FiniteField } from '../../bindings/crypto/finite_field.js'; +import type { FiniteField } from '../../bindings/crypto/finite-field.js'; import { exampleFields } from '../../bindings/crypto/finite-field-examples.js'; import { array, diff --git a/src/lib/gadgets/range-check.ts b/src/lib/gadgets/range-check.ts index 1d33dae0f8..a4b6161d92 100644 --- a/src/lib/gadgets/range-check.ts +++ b/src/lib/gadgets/range-check.ts @@ -1,5 +1,5 @@ import { Snarky } from '../../snarky.js'; -import { Fp } from '../../bindings/crypto/finite_field.js'; +import { Fp } from '../../bindings/crypto/finite-field.js'; import { Field as FieldProvable } from '../../provable/field-bigint.js'; import { Field } from '../field.js'; import { Gates } from '../gates.js'; diff --git a/src/lib/gadgets/range-check.unit-test.ts b/src/lib/gadgets/range-check.unit-test.ts index 1caea18f17..4396999ce4 100644 --- a/src/lib/gadgets/range-check.unit-test.ts +++ b/src/lib/gadgets/range-check.unit-test.ts @@ -1,4 +1,4 @@ -import { mod } from '../../bindings/crypto/finite_field.js'; +import { mod } from '../../bindings/crypto/finite-field.js'; import { Field } from '../../lib/core.js'; import { ZkProgram } from '../proof_system.js'; import { diff --git a/src/lib/gadgets/sha256.ts b/src/lib/gadgets/sha256.ts index ed69db8325..b1e764d3f6 100644 --- a/src/lib/gadgets/sha256.ts +++ b/src/lib/gadgets/sha256.ts @@ -1,5 +1,5 @@ // https://csrc.nist.gov/pubs/fips/180-4/upd1/final -import { mod } from '../../bindings/crypto/finite_field.js'; +import { mod } from '../../bindings/crypto/finite-field.js'; import { Field } from '../core.js'; import { UInt32, UInt8 } from '../int.js'; import { FlexibleBytes } from '../provable-types/bytes.js'; diff --git a/src/lib/gadgets/test-utils.ts b/src/lib/gadgets/test-utils.ts index 96c78866e3..6db27b513c 100644 --- a/src/lib/gadgets/test-utils.ts +++ b/src/lib/gadgets/test-utils.ts @@ -1,4 +1,4 @@ -import type { FiniteField } from '../../bindings/crypto/finite_field.js'; +import type { FiniteField } from '../../bindings/crypto/finite-field.js'; import { ProvableSpec, spec } from '../testing/equivalent.js'; import { Random } from '../testing/random.js'; import { Gadgets } from './gadgets.js'; diff --git a/src/lib/provable-context.ts b/src/lib/provable-context.ts index c285ee81f8..9779fa2ab1 100644 --- a/src/lib/provable-context.ts +++ b/src/lib/provable-context.ts @@ -2,7 +2,7 @@ import { Context } from './global-context.js'; import { Gate, GateType, JsonGate, Snarky } from '../snarky.js'; import { parseHexString32 } from '../bindings/crypto/bigint-helpers.js'; import { prettifyStacktrace } from './errors.js'; -import { Fp } from '../bindings/crypto/finite_field.js'; +import { Fp } from '../bindings/crypto/finite-field.js'; // internal API export { diff --git a/src/lib/testing/random.ts b/src/lib/testing/random.ts index e06a8cf2eb..20d509804d 100644 --- a/src/lib/testing/random.ts +++ b/src/lib/testing/random.ts @@ -39,7 +39,7 @@ import { Signable } from '../../bindings/lib/provable-bigint.js'; import { tokenSymbolLength } from '../../bindings/mina-transaction/derived-leaves.js'; import { stringLengthInBytes } from '../../bindings/lib/binable.js'; import { mocks } from '../../bindings/crypto/constants.js'; -import type { FiniteField } from '../../bindings/crypto/finite_field.js'; +import type { FiniteField } from '../../bindings/crypto/finite-field.js'; export { Random, sample, withHardCoded }; diff --git a/src/mina-signer/src/nullifier.ts b/src/mina-signer/src/nullifier.ts index 5d6f7c5c6d..1b3158dd18 100644 --- a/src/mina-signer/src/nullifier.ts +++ b/src/mina-signer/src/nullifier.ts @@ -1,4 +1,4 @@ -import { Fq } from '../../bindings/crypto/finite_field.js'; +import { Fq } from '../../bindings/crypto/finite-field.js'; import { Poseidon } from '../../bindings/crypto/poseidon.js'; import { Group, diff --git a/src/mina-signer/src/signature.unit-test.ts b/src/mina-signer/src/signature.unit-test.ts index 9743bda23b..c6ae459e04 100644 --- a/src/mina-signer/src/signature.unit-test.ts +++ b/src/mina-signer/src/signature.unit-test.ts @@ -12,7 +12,7 @@ import { Field as FieldSnarky } from '../../lib/core.js'; import { Field } from '../../provable/field-bigint.js'; import { PrivateKey, PublicKey } from '../../provable/curve-bigint.js'; import { PrivateKey as PrivateKeySnarky } from '../../lib/signature.js'; -import { p } from '../../bindings/crypto/finite_field.js'; +import { p } from '../../bindings/crypto/finite-field.js'; import { AccountUpdate } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; import { HashInput } from '../../bindings/lib/provable-bigint.js'; import { Ml } from '../../lib/ml/conversion.js'; diff --git a/src/provable/curve-bigint.ts b/src/provable/curve-bigint.ts index 1e3adac16c..5b1b0f6e36 100644 --- a/src/provable/curve-bigint.ts +++ b/src/provable/curve-bigint.ts @@ -1,4 +1,4 @@ -import { Fq, mod } from '../bindings/crypto/finite_field.js'; +import { Fq, mod } from '../bindings/crypto/finite-field.js'; import { GroupProjective, Pallas } from '../bindings/crypto/elliptic-curve.js'; import { versionBytes } from '../bindings/crypto/constants.js'; import { diff --git a/src/provable/field-bigint.ts b/src/provable/field-bigint.ts index c23e0e0bc4..99fcc95674 100644 --- a/src/provable/field-bigint.ts +++ b/src/provable/field-bigint.ts @@ -1,5 +1,5 @@ import { randomBytes } from '../bindings/crypto/random.js'; -import { Fp, mod } from '../bindings/crypto/finite_field.js'; +import { Fp, mod } from '../bindings/crypto/finite-field.js'; import { BinableBigint, HashInput, From 9883ee2a4e47550682ca3371505af347b1e10ba7 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 14:56:04 +0100 Subject: [PATCH 452/524] build scripts --- package.json | 10 +++++----- run-in-browser.js | 2 +- src/bindings | 2 +- src/build/{buildExample.js => build-example.js} | 0 src/build/{buildNode.js => build-node.js} | 0 src/build/{buildWeb.js => build-web.js} | 0 ...2eTestsBuildHelper.js => e2e-tests-build-helper.js} | 0 .../{jsLayoutToTypes.mjs => js-layout-to-types.mjs} | 2 +- src/build/run.js | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) rename src/build/{buildExample.js => build-example.js} (100%) rename src/build/{buildNode.js => build-node.js} (100%) rename src/build/{buildWeb.js => build-web.js} (100%) rename src/build/{e2eTestsBuildHelper.js => e2e-tests-build-helper.js} (100%) rename src/build/{jsLayoutToTypes.mjs => js-layout-to-types.mjs} (98%) diff --git a/package.json b/package.json index 80f4b4340f..b9dc29d6f0 100644 --- a/package.json +++ b/package.json @@ -43,15 +43,15 @@ }, "scripts": { "dev": "npx tsc -p tsconfig.test.json && node src/build/copy-to-dist.js", - "build": "node src/build/copy-artifacts.js && rimraf ./dist/node && npm run dev && node src/build/buildNode.js", + "build": "node src/build/copy-artifacts.js && rimraf ./dist/node && npm run dev && node src/build/build-node.js", "build:bindings": "./src/bindings/scripts/build-o1js-node.sh", "build:update-bindings": "./src/bindings/scripts/update-o1js-bindings.sh", "build:wasm": "./src/bindings/scripts/update-wasm-and-types.sh", - "build:web": "rimraf ./dist/web && node src/build/buildWeb.js", + "build:web": "rimraf ./dist/web && node src/build/build-web.js", "build:examples": "npm run build && rimraf ./dist/examples && npx tsc -p tsconfig.examples.json", "build:docs": "npx typedoc --tsconfig ./tsconfig.web.json", - "prepublish:web": "NODE_ENV=production node src/build/buildWeb.js", - "prepublish:node": "node src/build/copy-artifacts.js && rimraf ./dist/node && npx tsc -p tsconfig.node.json && node src/build/copy-to-dist.js && NODE_ENV=production node src/build/buildNode.js", + "prepublish:web": "NODE_ENV=production node src/build/build-web.js", + "prepublish:node": "node src/build/copy-artifacts.js && rimraf ./dist/node && npx tsc -p tsconfig.node.json && node src/build/copy-to-dist.js && NODE_ENV=production node src/build/build-node.js", "prepublishOnly": "npm run prepublish:web && npm run prepublish:node", "dump-vks": "./run tests/vk-regression/vk-regression.ts --bundle --dump", "format": "prettier --write --ignore-unknown **/*", @@ -61,7 +61,7 @@ "test:integration": "./run-integration-tests.sh", "test:unit": "./run-unit-tests.sh", "test:e2e": "rimraf ./tests/report && rimraf ./tests/test-artifacts && npx playwright test", - "e2e:prepare-server": "npm run build:examples && (cp -rf dist/examples dist/web || :) && node src/build/e2eTestsBuildHelper.js && cp -rf src/examples/plain-html/index.html src/examples/plain-html/server.js tests/artifacts/html/*.html tests/artifacts/javascript/*.js dist/web", + "e2e:prepare-server": "npm run build:examples && (cp -rf dist/examples dist/web || :) && node src/build/e2e-tests-build-helper.js && cp -rf src/examples/plain-html/index.html src/examples/plain-html/server.js tests/artifacts/html/*.html tests/artifacts/javascript/*.js dist/web", "e2e:run-server": "node dist/web/server.js", "e2e:install": "npx playwright install --with-deps", "e2e:show-report": "npx playwright show-report tests/report", diff --git a/run-in-browser.js b/run-in-browser.js index 98d416166a..70c0ca3534 100755 --- a/run-in-browser.js +++ b/run-in-browser.js @@ -3,7 +3,7 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import http from 'node:http'; import minimist from 'minimist'; -import { build } from './src/build/buildExample.js'; +import { build } from './src/build/build-example.js'; let { _: [filePath], diff --git a/src/bindings b/src/bindings index 9485b2c284..d5208ae948 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 9485b2c2849d767dd66a9e69763e6e167ff3c470 +Subproject commit d5208ae948bb2295caa8ab2a60fe1884a1ab763f diff --git a/src/build/buildExample.js b/src/build/build-example.js similarity index 100% rename from src/build/buildExample.js rename to src/build/build-example.js diff --git a/src/build/buildNode.js b/src/build/build-node.js similarity index 100% rename from src/build/buildNode.js rename to src/build/build-node.js diff --git a/src/build/buildWeb.js b/src/build/build-web.js similarity index 100% rename from src/build/buildWeb.js rename to src/build/build-web.js diff --git a/src/build/e2eTestsBuildHelper.js b/src/build/e2e-tests-build-helper.js similarity index 100% rename from src/build/e2eTestsBuildHelper.js rename to src/build/e2e-tests-build-helper.js diff --git a/src/build/jsLayoutToTypes.mjs b/src/build/js-layout-to-types.mjs similarity index 98% rename from src/build/jsLayoutToTypes.mjs rename to src/build/js-layout-to-types.mjs index d76907da9b..70c983d0f0 100644 --- a/src/build/jsLayoutToTypes.mjs +++ b/src/build/js-layout-to-types.mjs @@ -9,7 +9,7 @@ let selfPath = fileURLToPath(import.meta.url); let jsonPath = path.resolve(selfPath, '../../bindings/ocaml/jsLayout.json'); let jsLayout = JSON.parse(await fs.readFile(jsonPath, 'utf8')); -console.log(`jsLayoutToTypes.mjs: generating TS types from ${jsonPath}`); +console.log(`js-layout-to-types.mjs: generating TS types from ${jsonPath}`); let builtinLeafTypes = new Set([ 'number', diff --git a/src/build/run.js b/src/build/run.js index 51c89c8852..7902f1ccdf 100755 --- a/src/build/run.js +++ b/src/build/run.js @@ -1,6 +1,6 @@ #!/usr/bin/env node import minimist from 'minimist'; -import { buildAndImport, buildOne } from './buildExample.js'; +import { buildAndImport, buildOne } from './build-example.js'; let { _: [filePath], From 986796ba04dc416a154a41f918c05ffe6a71a8bd Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:00:56 +0100 Subject: [PATCH 453/524] mina-signer --- generate-keys.js | 2 +- src/lib/mina.ts | 2 +- src/lib/mina/mina-instance.ts | 2 +- src/lib/nullifier.ts | 2 +- src/mina-signer/build-web.sh | 2 +- src/mina-signer/index.cjs | 2 +- src/mina-signer/index.d.ts | 4 ++-- src/mina-signer/{MinaSigner.ts => mina-signer.ts} | 8 ++++---- src/mina-signer/package.json | 4 ++-- src/mina-signer/src/nullifier.ts | 2 +- src/mina-signer/src/random-transaction.ts | 2 +- src/mina-signer/src/sign-legacy.ts | 2 +- src/mina-signer/src/sign-legacy.unit-test.ts | 2 +- src/mina-signer/src/sign-zkapp-command.ts | 2 +- src/mina-signer/src/sign-zkapp-command.unit-test.ts | 2 +- src/mina-signer/src/signature.ts | 2 +- src/mina-signer/src/{TSTypes.ts => types.ts} | 0 src/mina-signer/src/{Utils.ts => utils.ts} | 2 +- src/mina-signer/tests/client.test.ts | 2 +- src/mina-signer/tests/keypair.test.ts | 2 +- src/mina-signer/tests/message.test.ts | 4 ++-- src/mina-signer/tests/payment.test.ts | 4 ++-- src/mina-signer/tests/rosetta.test.ts | 2 +- src/mina-signer/tests/stake-delegation.test.ts | 4 ++-- src/mina-signer/tests/verify-in-snark.unit-test.ts | 2 +- src/mina-signer/tests/zkapp.unit-test.ts | 2 +- tsconfig.mina-signer-web.json | 2 +- tsconfig.mina-signer.json | 2 +- tsconfig.node.json | 2 +- 29 files changed, 36 insertions(+), 36 deletions(-) rename src/mina-signer/{MinaSigner.ts => mina-signer.ts} (99%) rename src/mina-signer/src/{TSTypes.ts => types.ts} (100%) rename src/mina-signer/src/{Utils.ts => utils.ts} (98%) diff --git a/generate-keys.js b/generate-keys.js index ef1a4d7b9b..b702948882 100755 --- a/generate-keys.js +++ b/generate-keys.js @@ -1,5 +1,5 @@ #!/usr/bin/env node -import Client from './dist/node/mina-signer/MinaSigner.js'; +import Client from './dist/node/mina-signer/mina-signer.js'; let client = new Client({ network: 'testnet' }); diff --git a/src/lib/mina.ts b/src/lib/mina.ts index b00075b40f..8bb7fe728d 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -32,7 +32,7 @@ import { transactionCommitments, verifyAccountUpdateSignature, } from '../mina-signer/src/sign-zkapp-command.js'; -import { NetworkId } from '../mina-signer/src/TSTypes.js'; +import { NetworkId } from '../mina-signer/src/types.js'; import { FetchMode, currentTransaction } from './mina/transaction-context.js'; import { activeInstance, diff --git a/src/lib/mina/mina-instance.ts b/src/lib/mina/mina-instance.ts index dbaba81b33..e2a2252c77 100644 --- a/src/lib/mina/mina-instance.ts +++ b/src/lib/mina/mina-instance.ts @@ -8,7 +8,7 @@ import type { Transaction, TransactionId } from '../mina.js'; import type { Account } from './account.js'; import type { NetworkValue } from '../precondition.js'; import type * as Fetch from '../fetch.js'; -import type { NetworkId } from '../../mina-signer/src/TSTypes.js'; +import type { NetworkId } from '../../mina-signer/src/types.js'; export { Mina, diff --git a/src/lib/nullifier.ts b/src/lib/nullifier.ts index 2f68c234f9..cf3d09cb21 100644 --- a/src/lib/nullifier.ts +++ b/src/lib/nullifier.ts @@ -1,4 +1,4 @@ -import type { Nullifier as JsonNullifier } from '../mina-signer/src/TSTypes.js'; +import type { Nullifier as JsonNullifier } from '../mina-signer/src/types.js'; import { Struct } from './circuit_value.js'; import { Field, Group, Scalar } from './core.js'; import { Poseidon } from './hash.js'; diff --git a/src/mina-signer/build-web.sh b/src/mina-signer/build-web.sh index d3a0305851..b6aa9558e8 100755 --- a/src/mina-signer/build-web.sh +++ b/src/mina-signer/build-web.sh @@ -3,5 +3,5 @@ set -e tsc -p ../../tsconfig.mina-signer-web.json node moveWebFiles.js -npx esbuild --bundle --minify dist/tmp/mina-signer/MinaSigner.js --outfile=./dist/web/index.js --format=esm --target=es2021 +npx esbuild --bundle --minify dist/tmp/mina-signer/mina-signer.js --outfile=./dist/web/index.js --format=esm --target=es2021 npx rimraf dist/tmp diff --git a/src/mina-signer/index.cjs b/src/mina-signer/index.cjs index ac595416dd..600319e530 100644 --- a/src/mina-signer/index.cjs +++ b/src/mina-signer/index.cjs @@ -1,5 +1,5 @@ // this file is a wrapper for supporting commonjs imports -let Client = require('./MinaSigner.js'); +let Client = require('./mina-signer.js'); module.exports = Client.default; diff --git a/src/mina-signer/index.d.ts b/src/mina-signer/index.d.ts index 9c3a5698e8..96efc51dfd 100644 --- a/src/mina-signer/index.d.ts +++ b/src/mina-signer/index.d.ts @@ -1,6 +1,6 @@ // this file is a wrapper for supporting types in both commonjs and esm projects -import Client from './MinaSigner.js'; -export { type NetworkId } from './src/TSTypes.js'; +import Client from './mina-signer.ts'; +export { type NetworkId } from './src/types.ts'; export = Client; diff --git a/src/mina-signer/MinaSigner.ts b/src/mina-signer/mina-signer.ts similarity index 99% rename from src/mina-signer/MinaSigner.ts rename to src/mina-signer/mina-signer.ts index 559bcb27d3..fe3739404f 100644 --- a/src/mina-signer/MinaSigner.ts +++ b/src/mina-signer/mina-signer.ts @@ -1,6 +1,6 @@ import { PrivateKey, PublicKey } from '../provable/curve-bigint.js'; -import * as Json from './src/TSTypes.js'; -import type { SignedLegacy, Signed, NetworkId } from './src/TSTypes.js'; +import * as Json from './src/types.js'; +import type { SignedLegacy, Signed, NetworkId } from './src/types.js'; import { isPayment, @@ -10,7 +10,7 @@ import { isSignedZkappCommand, isStakeDelegation, isZkappCommand, -} from './src/Utils.js'; +} from './src/utils.js'; import * as TransactionJson from '../bindings/mina-transaction/gen/transaction-json.js'; import { ZkappCommand } from '../bindings/mina-transaction/gen/transaction-bigint.js'; import { @@ -496,7 +496,7 @@ class Client { /** * Returns the network ID. - * + * * @returns {NetworkId} The network ID. */ get networkId(): NetworkId { diff --git a/src/mina-signer/package.json b/src/mina-signer/package.json index b6d951a18c..8152789289 100644 --- a/src/mina-signer/package.json +++ b/src/mina-signer/package.json @@ -20,12 +20,12 @@ "homepage": "https://minaprotocol.com/", "repository": "https://github.com/o1-labs/o1js", "bugs": "https://github.com/o1-labs/o1js/issues", - "main": "dist/node/mina-signer/MinaSigner.js", + "main": "dist/node/mina-signer/mina-signer.js", "types": "dist/node/mina-signer/index.d.ts", "exports": { "web": "./dist/web/index.js", "require": "./dist/node/mina-signer/index.cjs", - "node": "./dist/node/mina-signer/MinaSigner.js", + "node": "./dist/node/mina-signer/mina-signer.js", "default": "./dist/web/index.js" }, "files": [ diff --git a/src/mina-signer/src/nullifier.ts b/src/mina-signer/src/nullifier.ts index 1b3158dd18..07b5edf7d2 100644 --- a/src/mina-signer/src/nullifier.ts +++ b/src/mina-signer/src/nullifier.ts @@ -7,7 +7,7 @@ import { PrivateKey, } from '../../provable/curve-bigint.js'; import { Field } from '../../provable/field-bigint.js'; -import { Nullifier } from './TSTypes.js'; +import { Nullifier } from './types.js'; export { createNullifier }; diff --git a/src/mina-signer/src/random-transaction.ts b/src/mina-signer/src/random-transaction.ts index f75c7ae93b..6ac2ab78ea 100644 --- a/src/mina-signer/src/random-transaction.ts +++ b/src/mina-signer/src/random-transaction.ts @@ -7,7 +7,7 @@ import { } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; import { PrivateKey } from '../../provable/curve-bigint.js'; import { Signature } from './signature.js'; -import { NetworkId } from './TSTypes.js'; +import { NetworkId } from './types.js'; export { RandomTransaction }; diff --git a/src/mina-signer/src/sign-legacy.ts b/src/mina-signer/src/sign-legacy.ts index 11c262c308..5d93cc0a75 100644 --- a/src/mina-signer/src/sign-legacy.ts +++ b/src/mina-signer/src/sign-legacy.ts @@ -10,7 +10,7 @@ import { } from './signature.js'; import { Json } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; import { bytesToBits, stringToBytes } from '../../bindings/lib/binable.js'; -import { NetworkId } from './TSTypes.js'; +import { NetworkId } from './types.js'; export { signPayment, diff --git a/src/mina-signer/src/sign-legacy.unit-test.ts b/src/mina-signer/src/sign-legacy.unit-test.ts index e5d6eb64ea..403c57287d 100644 --- a/src/mina-signer/src/sign-legacy.unit-test.ts +++ b/src/mina-signer/src/sign-legacy.unit-test.ts @@ -20,7 +20,7 @@ import { PublicKey, Scalar } from '../../provable/curve-bigint.js'; import { Field } from '../../provable/field-bigint.js'; import { Random, test } from '../../lib/testing/property.js'; import { RandomTransaction } from './random-transaction.js'; -import { NetworkId } from './TSTypes.js'; +import { NetworkId } from './types.js'; let { privateKey, publicKey } = keypair; let networks: NetworkId[] = ['testnet', 'mainnet']; diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 0dc1f79a95..40e983e4fa 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -17,7 +17,7 @@ import { verifyFieldElement, } from './signature.js'; import { mocks } from '../../bindings/crypto/constants.js'; -import { NetworkId } from './TSTypes.js'; +import { NetworkId } from './types.js'; // external API export { signZkappCommand, verifyZkappCommandSignature }; diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 49677e6ca9..5e60a87bb1 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -43,7 +43,7 @@ import { RandomTransaction } from './random-transaction.js'; import { Ml, MlHashInput } from '../../lib/ml/conversion.js'; import { FieldConst } from '../../lib/field.js'; import { mocks } from '../../bindings/crypto/constants.js'; -import { NetworkId } from './TSTypes.js'; +import { NetworkId } from './types.js'; // monkey-patch bigint to json (BigInt.prototype as any).toJSON = function () { diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index fb29715f8c..14a2096ec0 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -27,7 +27,7 @@ import { import { base58 } from '../../lib/base58.js'; import { versionBytes } from '../../bindings/crypto/constants.js'; import { Pallas } from '../../bindings/crypto/elliptic-curve.js'; -import { NetworkId } from './TSTypes.js'; +import { NetworkId } from './types.js'; export { sign, diff --git a/src/mina-signer/src/TSTypes.ts b/src/mina-signer/src/types.ts similarity index 100% rename from src/mina-signer/src/TSTypes.ts rename to src/mina-signer/src/types.ts diff --git a/src/mina-signer/src/Utils.ts b/src/mina-signer/src/utils.ts similarity index 98% rename from src/mina-signer/src/Utils.ts rename to src/mina-signer/src/utils.ts index 213246b81a..871c8c8081 100644 --- a/src/mina-signer/src/Utils.ts +++ b/src/mina-signer/src/utils.ts @@ -7,7 +7,7 @@ import type { SignedAny, SignedLegacy, SignableData, -} from './TSTypes.js'; +} from './types.js'; function hasCommonProperties(data: SignableData | ZkappCommand) { return ( diff --git a/src/mina-signer/tests/client.test.ts b/src/mina-signer/tests/client.test.ts index ca0cce3934..4ca384a2d2 100644 --- a/src/mina-signer/tests/client.test.ts +++ b/src/mina-signer/tests/client.test.ts @@ -1,4 +1,4 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; +import Client from '../dist/node/mina-signer/mina-signer.js'; describe('Client Class Initialization', () => { let client; diff --git a/src/mina-signer/tests/keypair.test.ts b/src/mina-signer/tests/keypair.test.ts index 5e3e48dd59..2c19c95caa 100644 --- a/src/mina-signer/tests/keypair.test.ts +++ b/src/mina-signer/tests/keypair.test.ts @@ -1,4 +1,4 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; +import Client from '../dist/node/mina-signer/mina-signer.js'; describe('Keypair', () => { let client: Client; diff --git a/src/mina-signer/tests/message.test.ts b/src/mina-signer/tests/message.test.ts index 26011eabff..bd9586fb19 100644 --- a/src/mina-signer/tests/message.test.ts +++ b/src/mina-signer/tests/message.test.ts @@ -1,5 +1,5 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; -import type { PrivateKey } from '../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../dist/node/mina-signer/mina-signer.js'; +import type { PrivateKey } from '../dist/node/mina-signer/src/types.js'; describe('Message', () => { describe('Mainnet network', () => { diff --git a/src/mina-signer/tests/payment.test.ts b/src/mina-signer/tests/payment.test.ts index 9a7d52804d..de84939d67 100644 --- a/src/mina-signer/tests/payment.test.ts +++ b/src/mina-signer/tests/payment.test.ts @@ -1,5 +1,5 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; -import type { Keypair } from '../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../dist/node/mina-signer/mina-signer.js'; +import type { Keypair } from '../dist/node/mina-signer/src/types.js'; describe('Payment', () => { describe('Mainnet network', () => { diff --git a/src/mina-signer/tests/rosetta.test.ts b/src/mina-signer/tests/rosetta.test.ts index 5db94f21e1..1981b135f3 100644 --- a/src/mina-signer/tests/rosetta.test.ts +++ b/src/mina-signer/tests/rosetta.test.ts @@ -1,4 +1,4 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; +import Client from '../dist/node/mina-signer/mina-signer.js'; describe('Rosetta', () => { let client: Client; diff --git a/src/mina-signer/tests/stake-delegation.test.ts b/src/mina-signer/tests/stake-delegation.test.ts index ea59458e1f..16cdf0d8f6 100644 --- a/src/mina-signer/tests/stake-delegation.test.ts +++ b/src/mina-signer/tests/stake-delegation.test.ts @@ -1,5 +1,5 @@ -import Client from '../dist/node/mina-signer/MinaSigner.js'; -import type { Keypair } from '../dist/node/mina-signer/src/TSTypes.js'; +import Client from '../dist/node/mina-signer/mina-signer.js'; +import type { Keypair } from '../dist/node/mina-signer/src/types.js'; describe('Stake Delegation', () => { describe('Mainnet network', () => { diff --git a/src/mina-signer/tests/verify-in-snark.unit-test.ts b/src/mina-signer/tests/verify-in-snark.unit-test.ts index c32180cff0..6ddcae8eb1 100644 --- a/src/mina-signer/tests/verify-in-snark.unit-test.ts +++ b/src/mina-signer/tests/verify-in-snark.unit-test.ts @@ -1,6 +1,6 @@ import { Field } from '../../lib/core.js'; import { ZkProgram } from '../../lib/proof_system.js'; -import Client from '../MinaSigner.js'; +import Client from '../mina-signer.js'; import { PrivateKey, Signature } from '../../lib/signature.js'; import { expect } from 'expect'; import { Provable } from '../../lib/provable.js'; diff --git a/src/mina-signer/tests/zkapp.unit-test.ts b/src/mina-signer/tests/zkapp.unit-test.ts index 5c2aaf6a3f..0cd8a636c3 100644 --- a/src/mina-signer/tests/zkapp.unit-test.ts +++ b/src/mina-signer/tests/zkapp.unit-test.ts @@ -1,6 +1,6 @@ import { ZkappCommand } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; import * as TransactionJson from '../../bindings/mina-transaction/gen/transaction-json.js'; -import Client from '../MinaSigner.js'; +import Client from '../mina-signer.js'; import { accountUpdateExample } from '../src/test-vectors/accountUpdate.js'; import { expect } from 'expect'; import { Transaction } from '../../lib/mina.js'; diff --git a/tsconfig.mina-signer-web.json b/tsconfig.mina-signer-web.json index be6f93c607..a5dc691296 100644 --- a/tsconfig.mina-signer-web.json +++ b/tsconfig.mina-signer-web.json @@ -1,6 +1,6 @@ { "extends": "./tsconfig.mina-signer.json", - "include": ["./src/mina-signer/MinaSigner.ts", "./src/**/*.web.ts"], + "include": ["./src/mina-signer/mina-signer.ts", "./src/**/*.web.ts"], "exclude": ["./src/examples"], "compilerOptions": { "outDir": "src/mina-signer/dist/tmp" diff --git a/tsconfig.mina-signer.json b/tsconfig.mina-signer.json index 9a6debaf3a..4c5c88a26c 100644 --- a/tsconfig.mina-signer.json +++ b/tsconfig.mina-signer.json @@ -1,5 +1,5 @@ { - "include": ["./src/mina-signer/MinaSigner.ts"], + "include": ["./src/mina-signer/mina-signer.ts"], "exclude": ["./src/**/*.unit-test.ts"], "compilerOptions": { "rootDir": "./src", diff --git a/tsconfig.node.json b/tsconfig.node.json index 7b5d2daf33..f98f4f1634 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -5,7 +5,7 @@ "./src/snarky.js", "./src/bindings/js/wrapper.js", "./src/mina-signer/src", - "./src/mina-signer/MinaSigner.ts", + "./src/mina-signer/mina-signer.ts", "./src/js_crypto", "./src/provable" ], From fb825c95667a52e1f3a5ea9d0b73460da11e33d8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:03:41 +0100 Subject: [PATCH 454/524] lib unit tests --- .../{account_update.unit-test.ts => account-update.unit-test.ts} | 0 src/lib/{circuit_value.test.ts => circuit-value.test.ts} | 0 .../{circuit_value.unit-test.ts => circuit-value.unit-test.ts} | 0 src/lib/{merkle_map.test.ts => merkle-map.test.ts} | 0 src/lib/{merkle_tree.test.ts => merkle-tree.test.ts} | 0 src/lib/{proof_system.unit-test.ts => proof-system.unit-test.ts} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename src/lib/{account_update.unit-test.ts => account-update.unit-test.ts} (100%) rename src/lib/{circuit_value.test.ts => circuit-value.test.ts} (100%) rename src/lib/{circuit_value.unit-test.ts => circuit-value.unit-test.ts} (100%) rename src/lib/{merkle_map.test.ts => merkle-map.test.ts} (100%) rename src/lib/{merkle_tree.test.ts => merkle-tree.test.ts} (100%) rename src/lib/{proof_system.unit-test.ts => proof-system.unit-test.ts} (100%) diff --git a/src/lib/account_update.unit-test.ts b/src/lib/account-update.unit-test.ts similarity index 100% rename from src/lib/account_update.unit-test.ts rename to src/lib/account-update.unit-test.ts diff --git a/src/lib/circuit_value.test.ts b/src/lib/circuit-value.test.ts similarity index 100% rename from src/lib/circuit_value.test.ts rename to src/lib/circuit-value.test.ts diff --git a/src/lib/circuit_value.unit-test.ts b/src/lib/circuit-value.unit-test.ts similarity index 100% rename from src/lib/circuit_value.unit-test.ts rename to src/lib/circuit-value.unit-test.ts diff --git a/src/lib/merkle_map.test.ts b/src/lib/merkle-map.test.ts similarity index 100% rename from src/lib/merkle_map.test.ts rename to src/lib/merkle-map.test.ts diff --git a/src/lib/merkle_tree.test.ts b/src/lib/merkle-tree.test.ts similarity index 100% rename from src/lib/merkle_tree.test.ts rename to src/lib/merkle-tree.test.ts diff --git a/src/lib/proof_system.unit-test.ts b/src/lib/proof-system.unit-test.ts similarity index 100% rename from src/lib/proof_system.unit-test.ts rename to src/lib/proof-system.unit-test.ts From e4c30caeae16681087487f79618098c5723073c1 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:04:39 +0100 Subject: [PATCH 455/524] circuit_value --- src/bindings | 2 +- src/index.ts | 4 ++-- src/lib/account_update.ts | 2 +- src/lib/{circuit_value.ts => circuit-value.ts} | 0 src/lib/circuit-value.unit-test.ts | 2 +- src/lib/field.unit-test.ts | 2 +- src/lib/foreign-curve.ts | 2 +- src/lib/foreign-ecdsa.ts | 2 +- src/lib/foreign-field.ts | 2 +- src/lib/gadgets/arithmetic.ts | 2 +- src/lib/gadgets/arithmetic.unit-test.ts | 2 +- src/lib/gadgets/elliptic-curve.ts | 2 +- src/lib/gadgets/foreign-field.ts | 2 +- src/lib/hash.ts | 2 +- src/lib/int.ts | 2 +- src/lib/merkle_map.ts | 2 +- src/lib/merkle_tree.ts | 2 +- src/lib/mina.ts | 2 +- src/lib/mina/account.ts | 2 +- src/lib/mina/token/forest-iterator.ts | 2 +- src/lib/ml/conversion.ts | 2 +- src/lib/nullifier.ts | 2 +- src/lib/precondition.ts | 2 +- src/lib/proof-system.unit-test.ts | 2 +- src/lib/proof_system.ts | 2 +- src/lib/provable-types/bytes.ts | 2 +- src/lib/provable-types/fields.ts | 2 +- src/lib/provable-types/merkle-list.ts | 2 +- src/lib/provable-types/packed.ts | 2 +- src/lib/provable.ts | 2 +- src/lib/signature.ts | 2 +- src/lib/state.ts | 2 +- src/lib/string.ts | 2 +- src/lib/testing/equivalent.ts | 2 +- src/lib/zkapp.ts | 2 +- 35 files changed, 35 insertions(+), 35 deletions(-) rename src/lib/{circuit_value.ts => circuit-value.ts} (100%) diff --git a/src/bindings b/src/bindings index d5208ae948..7c9feffb58 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit d5208ae948bb2295caa8ab2a60fe1884a1ab763f +Subproject commit 7c9feffb589deed29ce5606a135aeca5515c3a90 diff --git a/src/index.ts b/src/index.ts index c71bfafa93..a3acb82e36 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,7 @@ export type { FlexibleProvable, FlexibleProvablePure, InferProvable, -} from './lib/circuit_value.js'; +} from './lib/circuit-value.js'; export { CircuitValue, prop, @@ -31,7 +31,7 @@ export { provablePure, Struct, Unconstrained, -} from './lib/circuit_value.js'; +} from './lib/circuit-value.js'; export { Provable } from './lib/provable.js'; export { Circuit, Keypair, public_, circuitMain } from './lib/circuit.js'; export { UInt32, UInt64, Int64, Sign, UInt8 } from './lib/int.js'; diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index dace10bc9c..d7348cdcd5 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -4,7 +4,7 @@ import { provable, provablePure, StructNoJson, -} from './circuit_value.js'; +} from './circuit-value.js'; import { memoizationContext, memoizeWitness, Provable } from './provable.js'; import { Field, Bool } from './core.js'; import { Pickles, Test } from '../snarky.js'; diff --git a/src/lib/circuit_value.ts b/src/lib/circuit-value.ts similarity index 100% rename from src/lib/circuit_value.ts rename to src/lib/circuit-value.ts diff --git a/src/lib/circuit-value.unit-test.ts b/src/lib/circuit-value.unit-test.ts index 79fbbafa9d..731f0862d6 100644 --- a/src/lib/circuit-value.unit-test.ts +++ b/src/lib/circuit-value.unit-test.ts @@ -1,4 +1,4 @@ -import { provable, Struct, Unconstrained } from './circuit_value.js'; +import { provable, Struct, Unconstrained } from './circuit-value.js'; import { UInt32 } from './int.js'; import { PrivateKey, PublicKey } from './signature.js'; import { expect } from 'expect'; diff --git a/src/lib/field.unit-test.ts b/src/lib/field.unit-test.ts index 8f7d8843f5..2b955dca85 100644 --- a/src/lib/field.unit-test.ts +++ b/src/lib/field.unit-test.ts @@ -5,7 +5,7 @@ import { test, Random } from './testing/property.js'; import { deepEqual, throws } from 'node:assert/strict'; import { Provable } from './provable.js'; import { Binable } from '../bindings/lib/binable.js'; -import { ProvableExtended } from './circuit_value.js'; +import { ProvableExtended } from './circuit-value.js'; import { FieldType } from './field.js'; import { equivalentProvable as equivalent, diff --git a/src/lib/foreign-curve.ts b/src/lib/foreign-curve.ts index 56cbfe6ee1..8e02fb8dc7 100644 --- a/src/lib/foreign-curve.ts +++ b/src/lib/foreign-curve.ts @@ -4,7 +4,7 @@ import { createCurveAffine, } from '../bindings/crypto/elliptic-curve.js'; import type { Group } from './group.js'; -import { ProvablePureExtended } from './circuit_value.js'; +import { ProvablePureExtended } from './circuit-value.js'; import { AlmostForeignField, createForeignField } from './foreign-field.js'; import { EllipticCurve, Point } from './gadgets/elliptic-curve.js'; import { Field3 } from './gadgets/foreign-field.js'; diff --git a/src/lib/foreign-ecdsa.ts b/src/lib/foreign-ecdsa.ts index 534850c134..b29966fccc 100644 --- a/src/lib/foreign-ecdsa.ts +++ b/src/lib/foreign-ecdsa.ts @@ -1,6 +1,6 @@ import { provableFromClass } from '../bindings/lib/provable-snarky.js'; import { CurveParams } from '../bindings/crypto/elliptic-curve.js'; -import { ProvablePureExtended } from './circuit_value.js'; +import { ProvablePureExtended } from './circuit-value.js'; import { FlexiblePoint, ForeignCurve, diff --git a/src/lib/foreign-field.ts b/src/lib/foreign-field.ts index 2047ab26db..80db014c84 100644 --- a/src/lib/foreign-field.ts +++ b/src/lib/foreign-field.ts @@ -13,7 +13,7 @@ import { Gadgets } from './gadgets/gadgets.js'; import { ForeignField as FF } from './gadgets/foreign-field.js'; import { assert } from './gadgets/common.js'; import { l3, l } from './gadgets/range-check.js'; -import { ProvablePureExtended } from './circuit_value.js'; +import { ProvablePureExtended } from './circuit-value.js'; // external API export { createForeignField }; diff --git a/src/lib/gadgets/arithmetic.ts b/src/lib/gadgets/arithmetic.ts index 31350bfa6a..e5c07e6b76 100644 --- a/src/lib/gadgets/arithmetic.ts +++ b/src/lib/gadgets/arithmetic.ts @@ -1,5 +1,5 @@ import { Bool } from '../bool.js'; -import { provableTuple } from '../circuit_value.js'; +import { provableTuple } from '../circuit-value.js'; import { Field } from '../core.js'; import { assert } from '../errors.js'; import { Provable } from '../provable.js'; diff --git a/src/lib/gadgets/arithmetic.unit-test.ts b/src/lib/gadgets/arithmetic.unit-test.ts index 075a5d6e32..ea4083c293 100644 --- a/src/lib/gadgets/arithmetic.unit-test.ts +++ b/src/lib/gadgets/arithmetic.unit-test.ts @@ -7,7 +7,7 @@ import { } from '../testing/equivalent.js'; import { Field } from '../core.js'; import { Gadgets } from './gadgets.js'; -import { provable } from '../circuit_value.js'; +import { provable } from '../circuit-value.js'; import { assert } from './common.js'; let Arithmetic = ZkProgram({ diff --git a/src/lib/gadgets/elliptic-curve.ts b/src/lib/gadgets/elliptic-curve.ts index c9aed26c31..ab37aa5fcb 100644 --- a/src/lib/gadgets/elliptic-curve.ts +++ b/src/lib/gadgets/elliptic-curve.ts @@ -15,7 +15,7 @@ import { affineDouble, } from '../../bindings/crypto/elliptic-curve.js'; import { Bool } from '../bool.js'; -import { provable } from '../circuit_value.js'; +import { provable } from '../circuit-value.js'; import { assertPositiveInteger } from '../../bindings/crypto/non-negative.js'; import { arrayGet, assertBoolean } from './basic.js'; import { sliceField3 } from './bit-slices.js'; diff --git a/src/lib/gadgets/foreign-field.ts b/src/lib/gadgets/foreign-field.ts index 7476050856..c4fe120e6c 100644 --- a/src/lib/gadgets/foreign-field.ts +++ b/src/lib/gadgets/foreign-field.ts @@ -7,7 +7,7 @@ import { } from '../../bindings/crypto/finite-field.js'; import { provableTuple } from '../../bindings/lib/provable-snarky.js'; import { Bool } from '../bool.js'; -import { Unconstrained } from '../circuit_value.js'; +import { Unconstrained } from '../circuit-value.js'; import { Field } from '../field.js'; import { Gates, foreignFieldAdd } from '../gates.js'; import { modifiedField } from '../provable-types/fields.js'; diff --git a/src/lib/hash.ts b/src/lib/hash.ts index 7f766c9412..69cd297472 100644 --- a/src/lib/hash.ts +++ b/src/lib/hash.ts @@ -1,4 +1,4 @@ -import { HashInput, ProvableExtended, Struct } from './circuit_value.js'; +import { HashInput, ProvableExtended, Struct } from './circuit-value.js'; import { Snarky } from '../snarky.js'; import { Field } from './core.js'; import { createHashHelpers } from './hash-generic.js'; diff --git a/src/lib/int.ts b/src/lib/int.ts index de717abbe4..99c0d04e29 100644 --- a/src/lib/int.ts +++ b/src/lib/int.ts @@ -1,5 +1,5 @@ import { Field, Bool } from './core.js'; -import { AnyConstructor, CircuitValue, Struct, prop } from './circuit_value.js'; +import { AnyConstructor, CircuitValue, Struct, prop } from './circuit-value.js'; import { Types } from '../bindings/mina-transaction/types.js'; import { HashInput } from './hash.js'; import { Provable } from './provable.js'; diff --git a/src/lib/merkle_map.ts b/src/lib/merkle_map.ts index 468018b80e..b851398190 100644 --- a/src/lib/merkle_map.ts +++ b/src/lib/merkle_map.ts @@ -1,4 +1,4 @@ -import { arrayProp, CircuitValue } from './circuit_value.js'; +import { arrayProp, CircuitValue } from './circuit-value.js'; import { Field, Bool } from './core.js'; import { Poseidon } from './hash.js'; import { MerkleTree, MerkleWitness } from './merkle_tree.js'; diff --git a/src/lib/merkle_tree.ts b/src/lib/merkle_tree.ts index 5fcae6dbb9..29067d38bb 100644 --- a/src/lib/merkle_tree.ts +++ b/src/lib/merkle_tree.ts @@ -2,7 +2,7 @@ * This file contains all code related to the [Merkle Tree](https://en.wikipedia.org/wiki/Merkle_tree) implementation available in o1js. */ -import { CircuitValue, arrayProp } from './circuit_value.js'; +import { CircuitValue, arrayProp } from './circuit-value.js'; import { Circuit } from './circuit.js'; import { Poseidon } from './hash.js'; import { Bool, Field } from './core.js'; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 8bb7fe728d..072009e20d 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -19,7 +19,7 @@ import { } from './account_update.js'; import * as Fetch from './fetch.js'; import { assertPreconditionInvariants, NetworkValue } from './precondition.js'; -import { cloneCircuitValue, toConstant } from './circuit_value.js'; +import { cloneCircuitValue, toConstant } from './circuit-value.js'; import { Empty, JsonProof, Proof, verify } from './proof_system.js'; import { invalidTransactionError } from './mina/errors.js'; import { Types, TypesBigint } from '../bindings/mina-transaction/types.js'; diff --git a/src/lib/mina/account.ts b/src/lib/mina/account.ts index 1eb38eb214..a9c476168c 100644 --- a/src/lib/mina/account.ts +++ b/src/lib/mina/account.ts @@ -10,7 +10,7 @@ import { TypeMap, } from '../../bindings/mina-transaction/gen/transaction.js'; import { jsLayout } from '../../bindings/mina-transaction/gen/js-layout.js'; -import { ProvableExtended } from '../circuit_value.js'; +import { ProvableExtended } from '../circuit-value.js'; export { FetchedAccount, Account, PartialAccount }; export { newAccount, accountQuery, parseFetchedAccount, fillPartialAccount }; diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index 62b717e94a..88002b9f08 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -6,7 +6,7 @@ import { } from '../../account_update.js'; import { Field } from '../../core.js'; import { Provable } from '../../provable.js'; -import { Struct } from '../../circuit_value.js'; +import { Struct } from '../../circuit-value.js'; import { assert } from '../../gadgets/common.js'; import { MerkleListIterator, diff --git a/src/lib/ml/conversion.ts b/src/lib/ml/conversion.ts index de65656cce..60748b2118 100644 --- a/src/lib/ml/conversion.ts +++ b/src/lib/ml/conversion.ts @@ -3,7 +3,7 @@ */ import type { MlPublicKey, MlPublicKeyVar } from '../../snarky.js'; -import { HashInput } from '../circuit_value.js'; +import { HashInput } from '../circuit-value.js'; import { Bool, Field } from '../core.js'; import { FieldConst, FieldVar } from '../field.js'; import { Scalar, ScalarConst } from '../scalar.js'; diff --git a/src/lib/nullifier.ts b/src/lib/nullifier.ts index cf3d09cb21..a4a3f8c0b9 100644 --- a/src/lib/nullifier.ts +++ b/src/lib/nullifier.ts @@ -1,5 +1,5 @@ import type { Nullifier as JsonNullifier } from '../mina-signer/src/types.js'; -import { Struct } from './circuit_value.js'; +import { Struct } from './circuit-value.js'; import { Field, Group, Scalar } from './core.js'; import { Poseidon } from './hash.js'; import { MerkleMapWitness } from './merkle_map.js'; diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index 56582f947f..cdd7e29505 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -1,5 +1,5 @@ import { Bool, Field } from './core.js'; -import { circuitValueEquals, cloneCircuitValue } from './circuit_value.js'; +import { circuitValueEquals, cloneCircuitValue } from './circuit-value.js'; import { Provable } from './provable.js'; import { activeInstance as Mina } from './mina/mina-instance.js'; import type { AccountUpdate } from './account_update.js'; diff --git a/src/lib/proof-system.unit-test.ts b/src/lib/proof-system.unit-test.ts index d842cd2a1c..037a2f98ae 100644 --- a/src/lib/proof-system.unit-test.ts +++ b/src/lib/proof-system.unit-test.ts @@ -1,5 +1,5 @@ import { Field, Bool } from './core.js'; -import { Struct } from './circuit_value.js'; +import { Struct } from './circuit-value.js'; import { UInt64 } from './int.js'; import { CompiledTag, diff --git a/src/lib/proof_system.ts b/src/lib/proof_system.ts index bf0599c626..4682e42a97 100644 --- a/src/lib/proof_system.ts +++ b/src/lib/proof_system.ts @@ -22,7 +22,7 @@ import { provable, provablePure, toConstant, -} from './circuit_value.js'; +} from './circuit-value.js'; import { Provable } from './provable.js'; import { assert, prettifyStacktracePromise } from './errors.js'; import { snarkContext } from './provable-context.js'; diff --git a/src/lib/provable-types/bytes.ts b/src/lib/provable-types/bytes.ts index e6a9e1adb0..b8a443371e 100644 --- a/src/lib/provable-types/bytes.ts +++ b/src/lib/provable-types/bytes.ts @@ -1,5 +1,5 @@ import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; -import type { ProvablePureExtended } from '../circuit_value.js'; +import type { ProvablePureExtended } from '../circuit-value.js'; import { assert } from '../gadgets/common.js'; import { chunkString } from '../util/arrays.js'; import { Provable } from '../provable.js'; diff --git a/src/lib/provable-types/fields.ts b/src/lib/provable-types/fields.ts index 385c7d0cae..11dbe91061 100644 --- a/src/lib/provable-types/fields.ts +++ b/src/lib/provable-types/fields.ts @@ -1,4 +1,4 @@ -import { ProvablePureExtended } from '../circuit_value.js'; +import { ProvablePureExtended } from '../circuit-value.js'; import { Field } from '../field.js'; export { modifiedField, fields }; diff --git a/src/lib/provable-types/merkle-list.ts b/src/lib/provable-types/merkle-list.ts index 745b47ee9a..2cbcf5b33a 100644 --- a/src/lib/provable-types/merkle-list.ts +++ b/src/lib/provable-types/merkle-list.ts @@ -1,6 +1,6 @@ import { Bool, Field } from '../core.js'; import { Provable } from '../provable.js'; -import { Struct, Unconstrained } from '../circuit_value.js'; +import { Struct, Unconstrained } from '../circuit-value.js'; import { assert } from '../gadgets/common.js'; import { provableFromClass } from '../../bindings/lib/provable-snarky.js'; import { Poseidon, packToFields, ProvableHashable } from '../hash.js'; diff --git a/src/lib/provable-types/packed.ts b/src/lib/provable-types/packed.ts index c770b28145..edbb3565f9 100644 --- a/src/lib/provable-types/packed.ts +++ b/src/lib/provable-types/packed.ts @@ -3,7 +3,7 @@ import { HashInput, ProvableExtended, Unconstrained, -} from '../circuit_value.js'; +} from '../circuit-value.js'; import { Field } from '../field.js'; import { assert } from '../gadgets/common.js'; import { Poseidon, ProvableHashable, packToFields } from '../hash.js'; diff --git a/src/lib/provable.ts b/src/lib/provable.ts index 6d1eac028a..ea532b1c3a 100644 --- a/src/lib/provable.ts +++ b/src/lib/provable.ts @@ -5,7 +5,7 @@ */ import { Field, Bool } from './core.js'; import { Provable as Provable_, Snarky } from '../snarky.js'; -import type { FlexibleProvable, ProvableExtended } from './circuit_value.js'; +import type { FlexibleProvable, ProvableExtended } from './circuit-value.js'; import { Context } from './global-context.js'; import { HashInput, diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 58d68ccef0..261aab8cc2 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -1,5 +1,5 @@ import { Field, Bool, Group, Scalar } from './core.js'; -import { prop, CircuitValue, AnyConstructor } from './circuit_value.js'; +import { prop, CircuitValue, AnyConstructor } from './circuit-value.js'; import { hashWithPrefix } from './hash.js'; import { deriveNonce, diff --git a/src/lib/state.ts b/src/lib/state.ts index bf6a534ec6..22c713923f 100644 --- a/src/lib/state.ts +++ b/src/lib/state.ts @@ -1,5 +1,5 @@ import { ProvablePure } from '../snarky.js'; -import { FlexibleProvablePure } from './circuit_value.js'; +import { FlexibleProvablePure } from './circuit-value.js'; import { AccountUpdate, TokenId } from './account_update.js'; import { PublicKey } from './signature.js'; import * as Mina from './mina.js'; diff --git a/src/lib/string.ts b/src/lib/string.ts index 360f66465f..efdacfc70b 100644 --- a/src/lib/string.ts +++ b/src/lib/string.ts @@ -1,5 +1,5 @@ import { Bool, Field } from '../lib/core.js'; -import { arrayProp, CircuitValue, prop } from './circuit_value.js'; +import { arrayProp, CircuitValue, prop } from './circuit-value.js'; import { Provable } from './provable.js'; import { Poseidon } from './hash.js'; import { Gadgets } from './gadgets/gadgets.js'; diff --git a/src/lib/testing/equivalent.ts b/src/lib/testing/equivalent.ts index 7a0f20fd7d..d97b9fbae2 100644 --- a/src/lib/testing/equivalent.ts +++ b/src/lib/testing/equivalent.ts @@ -6,7 +6,7 @@ import { Provable } from '../provable.js'; import { deepEqual } from 'node:assert/strict'; import { Bool, Field } from '../core.js'; import { AnyFunction, Tuple } from '../util/types.js'; -import { provable } from '../circuit_value.js'; +import { provable } from '../circuit-value.js'; import { assert } from '../gadgets/common.js'; export { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 56be187d69..698b485b63 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -23,7 +23,7 @@ import { InferProvable, provable, toConstant, -} from './circuit_value.js'; +} from './circuit-value.js'; import { Provable, getBlindingValue, memoizationContext } from './provable.js'; import * as Encoding from '../bindings/lib/encoding.js'; import { Poseidon, hashConstant } from './hash.js'; From 3bdd184a4b954890f3c1511eeb8a92b67e8da167 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:05:36 +0100 Subject: [PATCH 456/524] merkle tree and map --- src/index.ts | 4 ++-- src/lib/{merkle_map.ts => merkle-map.ts} | 2 +- src/lib/{merkle_tree.ts => merkle-tree.ts} | 1 - src/lib/merkle-tree.unit-test.ts | 2 +- src/lib/nullifier.ts | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) rename src/lib/{merkle_map.ts => merkle-map.ts} (98%) rename src/lib/{merkle_tree.ts => merkle-tree.ts} (99%) diff --git a/src/index.ts b/src/index.ts index a3acb82e36..724225790b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -99,8 +99,8 @@ export { export * as Encryption from './lib/encryption.js'; export * as Encoding from './bindings/lib/encoding.js'; export { Character, CircuitString } from './lib/string.js'; -export { MerkleTree, MerkleWitness } from './lib/merkle_tree.js'; -export { MerkleMap, MerkleMapWitness } from './lib/merkle_map.js'; +export { MerkleTree, MerkleWitness } from './lib/merkle-tree.js'; +export { MerkleMap, MerkleMapWitness } from './lib/merkle-map.js'; export { Nullifier } from './lib/nullifier.js'; diff --git a/src/lib/merkle_map.ts b/src/lib/merkle-map.ts similarity index 98% rename from src/lib/merkle_map.ts rename to src/lib/merkle-map.ts index b851398190..d10eed78fe 100644 --- a/src/lib/merkle_map.ts +++ b/src/lib/merkle-map.ts @@ -1,7 +1,7 @@ import { arrayProp, CircuitValue } from './circuit-value.js'; import { Field, Bool } from './core.js'; import { Poseidon } from './hash.js'; -import { MerkleTree, MerkleWitness } from './merkle_tree.js'; +import { MerkleTree, MerkleWitness } from './merkle-tree.js'; import { Provable } from './provable.js'; const bits = 255; diff --git a/src/lib/merkle_tree.ts b/src/lib/merkle-tree.ts similarity index 99% rename from src/lib/merkle_tree.ts rename to src/lib/merkle-tree.ts index 29067d38bb..b4b24bd46a 100644 --- a/src/lib/merkle_tree.ts +++ b/src/lib/merkle-tree.ts @@ -3,7 +3,6 @@ */ import { CircuitValue, arrayProp } from './circuit-value.js'; -import { Circuit } from './circuit.js'; import { Poseidon } from './hash.js'; import { Bool, Field } from './core.js'; import { Provable } from './provable.js'; diff --git a/src/lib/merkle-tree.unit-test.ts b/src/lib/merkle-tree.unit-test.ts index fe81ef1756..8258f2d629 100644 --- a/src/lib/merkle-tree.unit-test.ts +++ b/src/lib/merkle-tree.unit-test.ts @@ -1,5 +1,5 @@ import { Bool, Field } from './core.js'; -import { maybeSwap, maybeSwapBad } from './merkle_tree.js'; +import { maybeSwap, maybeSwapBad } from './merkle-tree.js'; import { Random, test } from './testing/property.js'; import { expect } from 'expect'; diff --git a/src/lib/nullifier.ts b/src/lib/nullifier.ts index a4a3f8c0b9..c8e84984ef 100644 --- a/src/lib/nullifier.ts +++ b/src/lib/nullifier.ts @@ -2,7 +2,7 @@ import type { Nullifier as JsonNullifier } from '../mina-signer/src/types.js'; import { Struct } from './circuit-value.js'; import { Field, Group, Scalar } from './core.js'; import { Poseidon } from './hash.js'; -import { MerkleMapWitness } from './merkle_map.js'; +import { MerkleMapWitness } from './merkle-map.js'; import { PrivateKey, PublicKey, scaleShifted } from './signature.js'; import { Provable } from './provable.js'; From 2cdfedc792f1b16e54032c57dd1bb1697b5af02f Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:06:11 +0100 Subject: [PATCH 457/524] proof_system --- src/index.ts | 6 +++--- src/lib/account_update.ts | 2 +- src/lib/circuit-value.ts | 2 +- src/lib/gadgets/arithmetic.unit-test.ts | 2 +- src/lib/gadgets/bitwise.unit-test.ts | 2 +- src/lib/gadgets/ecdsa.unit-test.ts | 2 +- src/lib/gadgets/foreign-field.unit-test.ts | 2 +- src/lib/gadgets/range-check.unit-test.ts | 2 +- src/lib/gadgets/sha256.unit-test.ts | 2 +- src/lib/keccak.unit-test.ts | 2 +- src/lib/mina.ts | 2 +- src/lib/{proof_system.ts => proof-system.ts} | 0 src/lib/proof-system.unit-test.ts | 2 +- src/lib/proof-system/prover-keys.ts | 2 +- src/lib/testing/constraint-system.ts | 2 +- src/lib/zkapp.ts | 2 +- src/mina-signer/tests/verify-in-snark.unit-test.ts | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) rename src/lib/{proof_system.ts => proof-system.ts} (100%) diff --git a/src/index.ts b/src/index.ts index 724225790b..bec52ce535 100644 --- a/src/index.ts +++ b/src/index.ts @@ -56,7 +56,7 @@ export { } from './lib/zkapp.js'; export { state, State, declareState } from './lib/state.js'; -export type { JsonProof } from './lib/proof_system.js'; +export type { JsonProof } from './lib/proof-system.js'; export { Proof, SelfProof, @@ -65,7 +65,7 @@ export { Undefined, Void, VerificationKey, -} from './lib/proof_system.js'; +} from './lib/proof-system.js'; export { Cache, CacheHeader } from './lib/proof-system/cache.js'; export { @@ -104,7 +104,7 @@ export { MerkleMap, MerkleMapWitness } from './lib/merkle-map.js'; export { Nullifier } from './lib/nullifier.js'; -import { ExperimentalZkProgram, ZkProgram } from './lib/proof_system.js'; +import { ExperimentalZkProgram, ZkProgram } from './lib/proof-system.js'; export { ZkProgram }; export { Crypto } from './lib/crypto.js'; diff --git a/src/lib/account_update.ts b/src/lib/account_update.ts index d7348cdcd5..ff2e7c38d3 100644 --- a/src/lib/account_update.ts +++ b/src/lib/account_update.ts @@ -27,7 +27,7 @@ import { ClosedInterval, getAccountPreconditions, } from './precondition.js'; -import { dummyBase64Proof, Empty, Proof, Prover } from './proof_system.js'; +import { dummyBase64Proof, Empty, Proof, Prover } from './proof-system.js'; import { Memo } from '../mina-signer/src/memo.js'; import { Events, diff --git a/src/lib/circuit-value.ts b/src/lib/circuit-value.ts index d2e7465f70..3ade7bcb25 100644 --- a/src/lib/circuit-value.ts +++ b/src/lib/circuit-value.ts @@ -17,7 +17,7 @@ import type { import { Provable } from './provable.js'; import { assert } from './errors.js'; import { inCheckedComputation } from './provable-context.js'; -import { Proof } from './proof_system.js'; +import { Proof } from './proof-system.js'; // external API export { diff --git a/src/lib/gadgets/arithmetic.unit-test.ts b/src/lib/gadgets/arithmetic.unit-test.ts index ea4083c293..9030eec117 100644 --- a/src/lib/gadgets/arithmetic.unit-test.ts +++ b/src/lib/gadgets/arithmetic.unit-test.ts @@ -1,4 +1,4 @@ -import { ZkProgram } from '../proof_system.js'; +import { ZkProgram } from '../proof-system.js'; import { equivalentProvable as equivalent, equivalentAsync, diff --git a/src/lib/gadgets/bitwise.unit-test.ts b/src/lib/gadgets/bitwise.unit-test.ts index 61cca631f1..9e2f93f919 100644 --- a/src/lib/gadgets/bitwise.unit-test.ts +++ b/src/lib/gadgets/bitwise.unit-test.ts @@ -1,4 +1,4 @@ -import { ZkProgram } from '../proof_system.js'; +import { ZkProgram } from '../proof-system.js'; import { equivalentProvable as equivalent, equivalentAsync, diff --git a/src/lib/gadgets/ecdsa.unit-test.ts b/src/lib/gadgets/ecdsa.unit-test.ts index b83f491745..e008a011ef 100644 --- a/src/lib/gadgets/ecdsa.unit-test.ts +++ b/src/lib/gadgets/ecdsa.unit-test.ts @@ -9,7 +9,7 @@ import { import { Field3 } from './foreign-field.js'; import { CurveParams } from '../../bindings/crypto/elliptic-curve-examples.js'; import { Provable } from '../provable.js'; -import { ZkProgram } from '../proof_system.js'; +import { ZkProgram } from '../proof-system.js'; import { assert } from './common.js'; import { foreignField, uniformForeignField } from './test-utils.js'; import { diff --git a/src/lib/gadgets/foreign-field.unit-test.ts b/src/lib/gadgets/foreign-field.unit-test.ts index 61325ccc2a..e5af3e9a50 100644 --- a/src/lib/gadgets/foreign-field.unit-test.ts +++ b/src/lib/gadgets/foreign-field.unit-test.ts @@ -11,7 +11,7 @@ import { } from '../testing/equivalent.js'; import { Random } from '../testing/random.js'; import { Gadgets } from './gadgets.js'; -import { ZkProgram } from '../proof_system.js'; +import { ZkProgram } from '../proof-system.js'; import { Provable } from '../provable.js'; import { assert } from './common.js'; import { diff --git a/src/lib/gadgets/range-check.unit-test.ts b/src/lib/gadgets/range-check.unit-test.ts index 4396999ce4..584e52cb57 100644 --- a/src/lib/gadgets/range-check.unit-test.ts +++ b/src/lib/gadgets/range-check.unit-test.ts @@ -1,6 +1,6 @@ import { mod } from '../../bindings/crypto/finite-field.js'; import { Field } from '../../lib/core.js'; -import { ZkProgram } from '../proof_system.js'; +import { ZkProgram } from '../proof-system.js'; import { Spec, boolean, diff --git a/src/lib/gadgets/sha256.unit-test.ts b/src/lib/gadgets/sha256.unit-test.ts index f17c7ad51e..f6383a7409 100644 --- a/src/lib/gadgets/sha256.unit-test.ts +++ b/src/lib/gadgets/sha256.unit-test.ts @@ -1,4 +1,4 @@ -import { ZkProgram } from '../proof_system.js'; +import { ZkProgram } from '../proof-system.js'; import { Bytes } from '../provable-types/provable-types.js'; import { Gadgets } from './gadgets.js'; import { sha256 as nobleSha256 } from '@noble/hashes/sha256'; diff --git a/src/lib/keccak.unit-test.ts b/src/lib/keccak.unit-test.ts index aad02e2342..090a53e915 100644 --- a/src/lib/keccak.unit-test.ts +++ b/src/lib/keccak.unit-test.ts @@ -1,5 +1,5 @@ import { Keccak } from './keccak.js'; -import { ZkProgram } from './proof_system.js'; +import { ZkProgram } from './proof-system.js'; import { equivalentProvable, equivalent, diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 072009e20d..48965c5a32 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -20,7 +20,7 @@ import { import * as Fetch from './fetch.js'; import { assertPreconditionInvariants, NetworkValue } from './precondition.js'; import { cloneCircuitValue, toConstant } from './circuit-value.js'; -import { Empty, JsonProof, Proof, verify } from './proof_system.js'; +import { Empty, JsonProof, Proof, verify } from './proof-system.js'; import { invalidTransactionError } from './mina/errors.js'; import { Types, TypesBigint } from '../bindings/mina-transaction/types.js'; import { Account } from './mina/account.js'; diff --git a/src/lib/proof_system.ts b/src/lib/proof-system.ts similarity index 100% rename from src/lib/proof_system.ts rename to src/lib/proof-system.ts diff --git a/src/lib/proof-system.unit-test.ts b/src/lib/proof-system.unit-test.ts index 037a2f98ae..d9572e54d4 100644 --- a/src/lib/proof-system.unit-test.ts +++ b/src/lib/proof-system.unit-test.ts @@ -8,7 +8,7 @@ import { ZkProgram, picklesRuleFromFunction, sortMethodArguments, -} from './proof_system.js'; +} from './proof-system.js'; import { expect } from 'expect'; import { Pickles, ProvablePure, Snarky } from '../snarky.js'; import { AnyFunction } from './util/types.js'; diff --git a/src/lib/proof-system/prover-keys.ts b/src/lib/proof-system/prover-keys.ts index 18b68f27b9..c76db0ec71 100644 --- a/src/lib/proof-system/prover-keys.ts +++ b/src/lib/proof-system/prover-keys.ts @@ -14,7 +14,7 @@ import { VerifierIndex } from '../../bindings/crypto/bindings/kimchi-types.js'; import { getRustConversion } from '../../bindings/crypto/bindings.js'; import { MlString } from '../ml/base.js'; import { CacheHeader, cacheHeaderVersion } from './cache.js'; -import type { MethodInterface } from '../proof_system.js'; +import type { MethodInterface } from '../proof-system.js'; export { parseHeader, diff --git a/src/lib/testing/constraint-system.ts b/src/lib/testing/constraint-system.ts index 064b49d863..e01fd0627a 100644 --- a/src/lib/testing/constraint-system.ts +++ b/src/lib/testing/constraint-system.ts @@ -11,7 +11,7 @@ import { Provable } from '../provable.js'; import { Tuple } from '../util/types.js'; import { Random } from './random.js'; import { test } from './property.js'; -import { Undefined, ZkProgram } from '../proof_system.js'; +import { Undefined, ZkProgram } from '../proof-system.js'; import { printGates } from '../provable-context.js'; export { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 698b485b63..86f22dcc4d 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -45,7 +45,7 @@ import { MethodInterface, Proof, sortMethodArguments, -} from './proof_system.js'; +} from './proof-system.js'; import { PrivateKey, PublicKey } from './signature.js'; import { assertStatePrecondition, cleanStatePrecondition } from './state.js'; import { diff --git a/src/mina-signer/tests/verify-in-snark.unit-test.ts b/src/mina-signer/tests/verify-in-snark.unit-test.ts index 6ddcae8eb1..af9b2a3656 100644 --- a/src/mina-signer/tests/verify-in-snark.unit-test.ts +++ b/src/mina-signer/tests/verify-in-snark.unit-test.ts @@ -1,5 +1,5 @@ import { Field } from '../../lib/core.js'; -import { ZkProgram } from '../../lib/proof_system.js'; +import { ZkProgram } from '../../lib/proof-system.js'; import Client from '../mina-signer.js'; import { PrivateKey, Signature } from '../../lib/signature.js'; import { expect } from 'expect'; From 716afd685ae3627c3b8dde34bb3b364b74f24b44 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:07:21 +0100 Subject: [PATCH 458/524] account_update --- src/index.ts | 2 +- src/lib/{account_update.ts => account-update.ts} | 0 src/lib/caller.unit-test.ts | 2 +- src/lib/circuit-value.unit-test.ts | 2 +- src/lib/fetch.ts | 2 +- src/lib/mina.ts | 2 +- src/lib/mina/account-update-layout.unit-test.ts | 2 +- src/lib/mina/account.ts | 2 +- src/lib/mina/errors.ts | 2 +- src/lib/mina/smart-contract-context.ts | 2 +- src/lib/mina/token/forest-iterator.ts | 2 +- src/lib/mina/token/forest-iterator.unit-test.ts | 2 +- src/lib/mina/token/token-contract.ts | 2 +- src/lib/mina/transaction-context.ts | 2 +- src/lib/mina/transaction-logic/apply.ts | 2 +- src/lib/mina/transaction-logic/ledger.ts | 2 +- src/lib/ml/consistency.unit-test.ts | 2 +- src/lib/precondition.ts | 2 +- src/lib/state.ts | 2 +- src/lib/zkapp.ts | 2 +- src/mina-signer/src/sign-zkapp-command.unit-test.ts | 2 +- 21 files changed, 20 insertions(+), 20 deletions(-) rename src/lib/{account_update.ts => account-update.ts} (100%) diff --git a/src/index.ts b/src/index.ts index bec52ce535..6e6e1e6e51 100644 --- a/src/index.ts +++ b/src/index.ts @@ -77,7 +77,7 @@ export { TransactionVersion, AccountUpdateForest, AccountUpdateTree, -} from './lib/account_update.js'; +} from './lib/account-update.js'; export { TokenAccountUpdateIterator } from './lib/mina/token/forest-iterator.js'; export { TokenContract } from './lib/mina/token/token-contract.js'; diff --git a/src/lib/account_update.ts b/src/lib/account-update.ts similarity index 100% rename from src/lib/account_update.ts rename to src/lib/account-update.ts diff --git a/src/lib/caller.unit-test.ts b/src/lib/caller.unit-test.ts index 47e242de6f..2fed8f00d1 100644 --- a/src/lib/caller.unit-test.ts +++ b/src/lib/caller.unit-test.ts @@ -1,4 +1,4 @@ -import { AccountUpdate, TokenId } from './account_update.js'; +import { AccountUpdate, TokenId } from './account-update.js'; import * as Mina from './mina.js'; import { expect } from 'expect'; diff --git a/src/lib/circuit-value.unit-test.ts b/src/lib/circuit-value.unit-test.ts index 731f0862d6..4c7b194f6e 100644 --- a/src/lib/circuit-value.unit-test.ts +++ b/src/lib/circuit-value.unit-test.ts @@ -5,7 +5,7 @@ import { expect } from 'expect'; import { method, SmartContract } from './zkapp.js'; import { LocalBlockchain, setActiveInstance, transaction } from './mina.js'; import { State, state } from './state.js'; -import { AccountUpdate } from './account_update.js'; +import { AccountUpdate } from './account-update.js'; import { Provable } from './provable.js'; import { Field } from './core.js'; diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index f1d39ef8b8..02cf518f35 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -1,7 +1,7 @@ import 'isomorphic-fetch'; import { Field } from './core.js'; import { UInt32, UInt64 } from './int.js'; -import { Actions, TokenId } from './account_update.js'; +import { Actions, TokenId } from './account-update.js'; import { PublicKey, PrivateKey } from './signature.js'; import { NetworkValue } from './precondition.js'; import { Types } from '../bindings/mina-transaction/types.js'; diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 48965c5a32..a64163cb9b 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -16,7 +16,7 @@ import { Events, dummySignature, AccountUpdateLayout, -} from './account_update.js'; +} from './account-update.js'; import * as Fetch from './fetch.js'; import { assertPreconditionInvariants, NetworkValue } from './precondition.js'; import { cloneCircuitValue, toConstant } from './circuit-value.js'; diff --git a/src/lib/mina/account-update-layout.unit-test.ts b/src/lib/mina/account-update-layout.unit-test.ts index 7fd21e50b0..4adaeca841 100644 --- a/src/lib/mina/account-update-layout.unit-test.ts +++ b/src/lib/mina/account-update-layout.unit-test.ts @@ -1,5 +1,5 @@ import { Mina } from '../../index.js'; -import { AccountUpdate, AccountUpdateTree } from '../account_update.js'; +import { AccountUpdate, AccountUpdateTree } from '../account-update.js'; import { UInt64 } from '../int.js'; import { SmartContract, method } from '../zkapp.js'; diff --git a/src/lib/mina/account.ts b/src/lib/mina/account.ts index a9c476168c..af77941426 100644 --- a/src/lib/mina/account.ts +++ b/src/lib/mina/account.ts @@ -1,6 +1,6 @@ import { Types } from '../../bindings/mina-transaction/types.js'; import { Bool, Field } from '../core.js'; -import { Permissions } from '../account_update.js'; +import { Permissions } from '../account-update.js'; import { UInt32, UInt64 } from '../int.js'; import { PublicKey } from '../signature.js'; import { TokenId, ReceiptChainHash } from '../base58-encodings.js'; diff --git a/src/lib/mina/errors.ts b/src/lib/mina/errors.ts index d4ccd1ee4a..261005dfbc 100644 --- a/src/lib/mina/errors.ts +++ b/src/lib/mina/errors.ts @@ -1,5 +1,5 @@ import { Types } from '../../bindings/mina-transaction/types.js'; -import { TokenId } from '../account_update.js'; +import { TokenId } from '../account-update.js'; import { Int64 } from '../int.js'; export { invalidTransactionError }; diff --git a/src/lib/mina/smart-contract-context.ts b/src/lib/mina/smart-contract-context.ts index 96f93c4e8c..916908dd10 100644 --- a/src/lib/mina/smart-contract-context.ts +++ b/src/lib/mina/smart-contract-context.ts @@ -1,5 +1,5 @@ import type { SmartContract } from '../zkapp.js'; -import type { AccountUpdate, AccountUpdateLayout } from '../account_update.js'; +import type { AccountUpdate, AccountUpdateLayout } from '../account-update.js'; import { Context } from '../global-context.js'; import { currentTransaction } from './transaction-context.js'; diff --git a/src/lib/mina/token/forest-iterator.ts b/src/lib/mina/token/forest-iterator.ts index 88002b9f08..0bb673ff89 100644 --- a/src/lib/mina/token/forest-iterator.ts +++ b/src/lib/mina/token/forest-iterator.ts @@ -3,7 +3,7 @@ import { AccountUpdateForest, AccountUpdateTreeBase, TokenId, -} from '../../account_update.js'; +} from '../../account-update.js'; import { Field } from '../../core.js'; import { Provable } from '../../provable.js'; import { Struct } from '../../circuit-value.js'; diff --git a/src/lib/mina/token/forest-iterator.unit-test.ts b/src/lib/mina/token/forest-iterator.unit-test.ts index c9a92117a8..4a00bb57f7 100644 --- a/src/lib/mina/token/forest-iterator.unit-test.ts +++ b/src/lib/mina/token/forest-iterator.unit-test.ts @@ -6,7 +6,7 @@ import { AccountUpdateForest, TokenId, hashAccountUpdate, -} from '../../account_update.js'; +} from '../../account-update.js'; import { TypesBigint } from '../../../bindings/mina-transaction/types.js'; import { Pickles } from '../../../snarky.js'; import { diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 0979c372fb..58a0984a0e 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -7,7 +7,7 @@ import { AccountUpdateForest, AccountUpdateTree, Permissions, -} from '../../account_update.js'; +} from '../../account-update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; import { TokenAccountUpdateIterator } from './forest-iterator.js'; diff --git a/src/lib/mina/transaction-context.ts b/src/lib/mina/transaction-context.ts index d981384085..e152d4eca9 100644 --- a/src/lib/mina/transaction-context.ts +++ b/src/lib/mina/transaction-context.ts @@ -1,4 +1,4 @@ -import type { AccountUpdateLayout } from '../account_update.js'; +import type { AccountUpdateLayout } from '../account-update.js'; import type { PublicKey } from '../signature.js'; import { Context } from '../global-context.js'; diff --git a/src/lib/mina/transaction-logic/apply.ts b/src/lib/mina/transaction-logic/apply.ts index 52e38f2065..7a185b95bd 100644 --- a/src/lib/mina/transaction-logic/apply.ts +++ b/src/lib/mina/transaction-logic/apply.ts @@ -1,7 +1,7 @@ /** * Apply transactions to a ledger of accounts. */ -import { type AccountUpdate } from '../../account_update.js'; +import { type AccountUpdate } from '../../account-update.js'; import { Account } from '../account.js'; export { applyAccountUpdate }; diff --git a/src/lib/mina/transaction-logic/ledger.ts b/src/lib/mina/transaction-logic/ledger.ts index daf7d3cb0d..3951ff2f5f 100644 --- a/src/lib/mina/transaction-logic/ledger.ts +++ b/src/lib/mina/transaction-logic/ledger.ts @@ -2,7 +2,7 @@ * A ledger of accounts - simple model of a local blockchain. */ import { PublicKey } from '../../signature.js'; -import type { AccountUpdate } from '../../account_update.js'; +import type { AccountUpdate } from '../../account-update.js'; import { Account, newAccount } from '../account.js'; import { Field } from '../../field.js'; import { applyAccountUpdate } from './apply.js'; diff --git a/src/lib/ml/consistency.unit-test.ts b/src/lib/ml/consistency.unit-test.ts index d0b78d0a03..c0c6e8aee9 100644 --- a/src/lib/ml/consistency.unit-test.ts +++ b/src/lib/ml/consistency.unit-test.ts @@ -2,7 +2,7 @@ import { Ledger, Test } from '../../snarky.js'; import { Random, test } from '../testing/property.js'; import { Field, Bool } from '../core.js'; import { PrivateKey, PublicKey } from '../signature.js'; -import { TokenId, dummySignature } from '../account_update.js'; +import { TokenId, dummySignature } from '../account-update.js'; import { Ml } from './conversion.js'; import { expect } from 'expect'; import { FieldConst } from '../field.js'; diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index cdd7e29505..ff54c2c835 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -2,7 +2,7 @@ import { Bool, Field } from './core.js'; import { circuitValueEquals, cloneCircuitValue } from './circuit-value.js'; import { Provable } from './provable.js'; import { activeInstance as Mina } from './mina/mina-instance.js'; -import type { AccountUpdate } from './account_update.js'; +import type { AccountUpdate } from './account-update.js'; import { Int64, UInt32, UInt64 } from './int.js'; import { Layout } from '../bindings/mina-transaction/gen/transaction.js'; import { jsLayout } from '../bindings/mina-transaction/gen/js-layout.js'; diff --git a/src/lib/state.ts b/src/lib/state.ts index 22c713923f..78a97aa36b 100644 --- a/src/lib/state.ts +++ b/src/lib/state.ts @@ -1,6 +1,6 @@ import { ProvablePure } from '../snarky.js'; import { FlexibleProvablePure } from './circuit-value.js'; -import { AccountUpdate, TokenId } from './account_update.js'; +import { AccountUpdate, TokenId } from './account-update.js'; import { PublicKey } from './signature.js'; import * as Mina from './mina.js'; import { fetchAccount } from './fetch.js'; diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 86f22dcc4d..d927c490ae 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -16,7 +16,7 @@ import { AccountUpdateForest, AccountUpdateLayout, AccountUpdateTree, -} from './account_update.js'; +} from './account-update.js'; import { cloneCircuitValue, FlexibleProvablePure, diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 5e60a87bb1..f8a4b51fd1 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -7,7 +7,7 @@ import { import { AccountUpdate as AccountUpdateSnarky, ZkappCommand as ZkappCommandSnarky, -} from '../../lib/account_update.js'; +} from '../../lib/account-update.js'; import { PrivateKey, PublicKey } from '../../provable/curve-bigint.js'; import { AccountUpdate, From f4e6b2822cc329e82452b82b60aae82c5f86be97 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:13:05 +0100 Subject: [PATCH 459/524] most examples --- run-ci-live-tests.sh | 2 +- run-ci-tests.sh | 4 ++-- run-integration-tests.sh | 4 ++-- src/examples/{api_exploration.ts => api-exploration.ts} | 0 src/examples/{circuit_string.ts => circuit-string.ts} | 0 src/examples/{constraint_system.ts => constraint-system.ts} | 0 src/examples/{fetch_live.ts => fetch-live.ts} | 0 src/examples/{matrix_mul.ts => matrix-mul.ts} | 0 .../{simple_zkapp_berkeley.ts => simple-zkapp-berkeley.ts} | 0 src/examples/{simple_zkapp.js => simple-zkapp.js} | 0 src/examples/{simple_zkapp.ts => simple-zkapp.ts} | 0 src/examples/{simple_zkapp.web.ts => simple-zkapp.web.ts} | 0 .../zkapps/{local_events_zkapp.ts => local-events-zkapp.ts} | 0 .../merkle_zkapp.ts => merkle-tree/merkle-zkapp.ts} | 0 .../reducer/{reducer_composite.ts => reducer-composite.ts} | 0 ...reconditions_zkapp.ts => set-local-preconditions-zkapp.ts} | 0 .../{simple_zkapp_payment.ts => simple-zkapp-payment.ts} | 0 ...{simple_zkapp_with_proof.ts => simple-zkapp-with-proof.ts} | 0 .../zkapps/{token_with_proofs.ts => token-with-proofs.ts} | 0 19 files changed, 5 insertions(+), 5 deletions(-) rename src/examples/{api_exploration.ts => api-exploration.ts} (100%) rename src/examples/{circuit_string.ts => circuit-string.ts} (100%) rename src/examples/{constraint_system.ts => constraint-system.ts} (100%) rename src/examples/{fetch_live.ts => fetch-live.ts} (100%) rename src/examples/{matrix_mul.ts => matrix-mul.ts} (100%) rename src/examples/{simple_zkapp_berkeley.ts => simple-zkapp-berkeley.ts} (100%) rename src/examples/{simple_zkapp.js => simple-zkapp.js} (100%) rename src/examples/{simple_zkapp.ts => simple-zkapp.ts} (100%) rename src/examples/{simple_zkapp.web.ts => simple-zkapp.web.ts} (100%) rename src/examples/zkapps/{local_events_zkapp.ts => local-events-zkapp.ts} (100%) rename src/examples/zkapps/{merkle_tree/merkle_zkapp.ts => merkle-tree/merkle-zkapp.ts} (100%) rename src/examples/zkapps/reducer/{reducer_composite.ts => reducer-composite.ts} (100%) rename src/examples/zkapps/{set_local_preconditions_zkapp.ts => set-local-preconditions-zkapp.ts} (100%) rename src/examples/zkapps/{simple_zkapp_payment.ts => simple-zkapp-payment.ts} (100%) rename src/examples/zkapps/{simple_zkapp_with_proof.ts => simple-zkapp-with-proof.ts} (100%) rename src/examples/zkapps/{token_with_proofs.ts => token-with-proofs.ts} (100%) diff --git a/run-ci-live-tests.sh b/run-ci-live-tests.sh index 192041b540..e89b9ee4b9 100755 --- a/run-ci-live-tests.sh +++ b/run-ci-live-tests.sh @@ -17,7 +17,7 @@ echo "" HELLO_WORLD_PROC=$! ./run src/examples/zkapps/dex/run_live.ts --bundle | add_prefix "DEX" & DEX_PROC=$! -./run src/examples/fetch_live.ts --bundle | add_prefix "FETCH" & +./run src/examples/fetch-live.ts --bundle | add_prefix "FETCH" & FETCH_PROC=$! # Wait for each process and capture their exit statuses diff --git a/run-ci-tests.sh b/run-ci-tests.sh index 14f444a2a5..b8c0cbd9b2 100755 --- a/run-ci-tests.sh +++ b/run-ci-tests.sh @@ -5,8 +5,8 @@ case $TEST_TYPE in "Simple integration tests") echo "Running basic integration tests" ./run src/examples/zkapps/hello_world/run.ts --bundle - ./run src/examples/simple_zkapp.ts --bundle - ./run src/examples/zkapps/reducer/reducer_composite.ts --bundle + ./run src/examples/simple-zkapp.ts --bundle + ./run src/examples/zkapps/reducer/reducer-composite.ts --bundle ./run src/examples/zkapps/composability.ts --bundle ./run src/tests/fake-proof.ts ;; diff --git a/run-integration-tests.sh b/run-integration-tests.sh index 7c2241ed17..20fce0ebab 100755 --- a/run-integration-tests.sh +++ b/run-integration-tests.sh @@ -3,8 +3,8 @@ set -e ./run src/examples/zkapps/hello_world/run.ts --bundle ./run src/examples/zkapps/voting/run.ts --bundle -./run src/examples/simple_zkapp.ts --bundle -./run src/examples/zkapps/reducer/reducer_composite.ts --bundle +./run src/examples/simple-zkapp.ts --bundle +./run src/examples/zkapps/reducer/reducer-composite.ts --bundle ./run src/examples/zkapps/composability.ts --bundle ./run src/examples/zkapps/dex/run.ts --bundle ./run src/examples/zkapps/dex/happy-path-with-actions.ts --bundle diff --git a/src/examples/api_exploration.ts b/src/examples/api-exploration.ts similarity index 100% rename from src/examples/api_exploration.ts rename to src/examples/api-exploration.ts diff --git a/src/examples/circuit_string.ts b/src/examples/circuit-string.ts similarity index 100% rename from src/examples/circuit_string.ts rename to src/examples/circuit-string.ts diff --git a/src/examples/constraint_system.ts b/src/examples/constraint-system.ts similarity index 100% rename from src/examples/constraint_system.ts rename to src/examples/constraint-system.ts diff --git a/src/examples/fetch_live.ts b/src/examples/fetch-live.ts similarity index 100% rename from src/examples/fetch_live.ts rename to src/examples/fetch-live.ts diff --git a/src/examples/matrix_mul.ts b/src/examples/matrix-mul.ts similarity index 100% rename from src/examples/matrix_mul.ts rename to src/examples/matrix-mul.ts diff --git a/src/examples/simple_zkapp_berkeley.ts b/src/examples/simple-zkapp-berkeley.ts similarity index 100% rename from src/examples/simple_zkapp_berkeley.ts rename to src/examples/simple-zkapp-berkeley.ts diff --git a/src/examples/simple_zkapp.js b/src/examples/simple-zkapp.js similarity index 100% rename from src/examples/simple_zkapp.js rename to src/examples/simple-zkapp.js diff --git a/src/examples/simple_zkapp.ts b/src/examples/simple-zkapp.ts similarity index 100% rename from src/examples/simple_zkapp.ts rename to src/examples/simple-zkapp.ts diff --git a/src/examples/simple_zkapp.web.ts b/src/examples/simple-zkapp.web.ts similarity index 100% rename from src/examples/simple_zkapp.web.ts rename to src/examples/simple-zkapp.web.ts diff --git a/src/examples/zkapps/local_events_zkapp.ts b/src/examples/zkapps/local-events-zkapp.ts similarity index 100% rename from src/examples/zkapps/local_events_zkapp.ts rename to src/examples/zkapps/local-events-zkapp.ts diff --git a/src/examples/zkapps/merkle_tree/merkle_zkapp.ts b/src/examples/zkapps/merkle-tree/merkle-zkapp.ts similarity index 100% rename from src/examples/zkapps/merkle_tree/merkle_zkapp.ts rename to src/examples/zkapps/merkle-tree/merkle-zkapp.ts diff --git a/src/examples/zkapps/reducer/reducer_composite.ts b/src/examples/zkapps/reducer/reducer-composite.ts similarity index 100% rename from src/examples/zkapps/reducer/reducer_composite.ts rename to src/examples/zkapps/reducer/reducer-composite.ts diff --git a/src/examples/zkapps/set_local_preconditions_zkapp.ts b/src/examples/zkapps/set-local-preconditions-zkapp.ts similarity index 100% rename from src/examples/zkapps/set_local_preconditions_zkapp.ts rename to src/examples/zkapps/set-local-preconditions-zkapp.ts diff --git a/src/examples/zkapps/simple_zkapp_payment.ts b/src/examples/zkapps/simple-zkapp-payment.ts similarity index 100% rename from src/examples/zkapps/simple_zkapp_payment.ts rename to src/examples/zkapps/simple-zkapp-payment.ts diff --git a/src/examples/zkapps/simple_zkapp_with_proof.ts b/src/examples/zkapps/simple-zkapp-with-proof.ts similarity index 100% rename from src/examples/zkapps/simple_zkapp_with_proof.ts rename to src/examples/zkapps/simple-zkapp-with-proof.ts diff --git a/src/examples/zkapps/token_with_proofs.ts b/src/examples/zkapps/token-with-proofs.ts similarity index 100% rename from src/examples/zkapps/token_with_proofs.ts rename to src/examples/zkapps/token-with-proofs.ts From dbd7e2ac78ee4aa41818598d1958f7422bd09880 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:20:28 +0100 Subject: [PATCH 460/524] hello_world and dex --- README-dev.md | 2 +- run-ci-live-tests.sh | 4 ++-- run-ci-tests.sh | 2 +- run-integration-tests.sh | 2 +- src/examples/plain-html/index.html | 2 +- ...ry_token_interaction.ts => arbitrary-token-interaction.ts} | 0 src/examples/zkapps/dex/{run_live.ts => run-live.ts} | 0 .../hello_world.ts => hello-world/hello-world.ts} | 0 .../{hello_world/run_live.ts => hello-world/run-live.ts} | 2 +- src/examples/zkapps/{hello_world => hello-world}/run.ts | 2 +- .../javascript/{e2eTestsHelpers.js => e2e-tests-helpers.js} | 0 tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js | 4 ++-- tests/vk-regression/vk-regression.ts | 2 +- 13 files changed, 11 insertions(+), 11 deletions(-) rename src/examples/zkapps/dex/{arbitrary_token_interaction.ts => arbitrary-token-interaction.ts} (100%) rename src/examples/zkapps/dex/{run_live.ts => run-live.ts} (100%) rename src/examples/zkapps/{hello_world/hello_world.ts => hello-world/hello-world.ts} (100%) rename src/examples/zkapps/{hello_world/run_live.ts => hello-world/run-live.ts} (98%) rename src/examples/zkapps/{hello_world => hello-world}/run.ts (98%) rename tests/artifacts/javascript/{e2eTestsHelpers.js => e2e-tests-helpers.js} (100%) diff --git a/README-dev.md b/README-dev.md index 389be17d9d..1839d130e3 100644 --- a/README-dev.md +++ b/README-dev.md @@ -226,7 +226,7 @@ See the [Docker Hub repository](https://hub.docker.com/r/o1labs/mina-local-netwo Next up, get the Mina blockchain accounts information to be used in your zkApp. After the local network is up and running, you can use the [Lightnet](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/lib/fetch.ts#L1012) `o1js API namespace` to get the accounts information. -See the corresponding example in [src/examples/zkapps/hello_world/run_live.ts](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/examples/zkapps/hello_world/run_live.ts). +See the corresponding example in [src/examples/zkapps/hello-world/run-live.ts](https://github.com/o1-labs/o1js/blob/ec789794b2067addef6b6f9c9a91c6511e07e37c/src/examples/zkapps/hello-world/run-live.ts). ### Profiling o1js diff --git a/run-ci-live-tests.sh b/run-ci-live-tests.sh index e89b9ee4b9..ae0b85787d 100755 --- a/run-ci-live-tests.sh +++ b/run-ci-live-tests.sh @@ -13,9 +13,9 @@ echo "" echo "Running integration tests against the real Mina network." echo "" -./run src/examples/zkapps/hello_world/run_live.ts --bundle | add_prefix "HELLO_WORLD" & +./run src/examples/zkapps/hello-world/run-live.ts --bundle | add_prefix "HELLO_WORLD" & HELLO_WORLD_PROC=$! -./run src/examples/zkapps/dex/run_live.ts --bundle | add_prefix "DEX" & +./run src/examples/zkapps/dex/run-live.ts --bundle | add_prefix "DEX" & DEX_PROC=$! ./run src/examples/fetch-live.ts --bundle | add_prefix "FETCH" & FETCH_PROC=$! diff --git a/run-ci-tests.sh b/run-ci-tests.sh index b8c0cbd9b2..fb33b0bd85 100755 --- a/run-ci-tests.sh +++ b/run-ci-tests.sh @@ -4,7 +4,7 @@ set -e case $TEST_TYPE in "Simple integration tests") echo "Running basic integration tests" - ./run src/examples/zkapps/hello_world/run.ts --bundle + ./run src/examples/zkapps/hello-world/run.ts --bundle ./run src/examples/simple-zkapp.ts --bundle ./run src/examples/zkapps/reducer/reducer-composite.ts --bundle ./run src/examples/zkapps/composability.ts --bundle diff --git a/run-integration-tests.sh b/run-integration-tests.sh index 20fce0ebab..3ffbbe4cac 100755 --- a/run-integration-tests.sh +++ b/run-integration-tests.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -./run src/examples/zkapps/hello_world/run.ts --bundle +./run src/examples/zkapps/hello-world/run.ts --bundle ./run src/examples/zkapps/voting/run.ts --bundle ./run src/examples/simple-zkapp.ts --bundle ./run src/examples/zkapps/reducer/reducer-composite.ts --bundle diff --git a/src/examples/plain-html/index.html b/src/examples/plain-html/index.html index 656bbec220..248739ad8f 100644 --- a/src/examples/plain-html/index.html +++ b/src/examples/plain-html/index.html @@ -6,7 +6,7 @@ - diff --git a/src/examples/zkapps/dex/arbitrary_token_interaction.ts b/src/examples/zkapps/dex/arbitrary-token-interaction.ts similarity index 100% rename from src/examples/zkapps/dex/arbitrary_token_interaction.ts rename to src/examples/zkapps/dex/arbitrary-token-interaction.ts diff --git a/src/examples/zkapps/dex/run_live.ts b/src/examples/zkapps/dex/run-live.ts similarity index 100% rename from src/examples/zkapps/dex/run_live.ts rename to src/examples/zkapps/dex/run-live.ts diff --git a/src/examples/zkapps/hello_world/hello_world.ts b/src/examples/zkapps/hello-world/hello-world.ts similarity index 100% rename from src/examples/zkapps/hello_world/hello_world.ts rename to src/examples/zkapps/hello-world/hello-world.ts diff --git a/src/examples/zkapps/hello_world/run_live.ts b/src/examples/zkapps/hello-world/run-live.ts similarity index 98% rename from src/examples/zkapps/hello_world/run_live.ts rename to src/examples/zkapps/hello-world/run-live.ts index c54d323a8a..241b4d2b16 100644 --- a/src/examples/zkapps/hello_world/run_live.ts +++ b/src/examples/zkapps/hello-world/run-live.ts @@ -7,7 +7,7 @@ import { PrivateKey, fetchAccount, } from 'o1js'; -import { HelloWorld, adminPrivateKey } from './hello_world.js'; +import { HelloWorld, adminPrivateKey } from './hello-world.js'; const useCustomLocalNetwork = process.env.USE_CUSTOM_LOCAL_NETWORK === 'true'; const zkAppKey = PrivateKey.random(); diff --git a/src/examples/zkapps/hello_world/run.ts b/src/examples/zkapps/hello-world/run.ts similarity index 98% rename from src/examples/zkapps/hello_world/run.ts rename to src/examples/zkapps/hello-world/run.ts index 41a73dbb4d..272b4ea8f0 100644 --- a/src/examples/zkapps/hello_world/run.ts +++ b/src/examples/zkapps/hello-world/run.ts @@ -1,6 +1,6 @@ import { AccountUpdate, Field, Mina, PrivateKey } from 'o1js'; import { getProfiler } from '../../utils/profiler.js'; -import { HelloWorld, adminPrivateKey } from './hello_world.js'; +import { HelloWorld, adminPrivateKey } from './hello-world.js'; const HelloWorldProfier = getProfiler('Hello World'); HelloWorldProfier.start('Hello World test flow'); diff --git a/tests/artifacts/javascript/e2eTestsHelpers.js b/tests/artifacts/javascript/e2e-tests-helpers.js similarity index 100% rename from tests/artifacts/javascript/e2eTestsHelpers.js rename to tests/artifacts/javascript/e2e-tests-helpers.js diff --git a/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js b/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js index 568ca13ab2..22f7dc0f54 100644 --- a/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js +++ b/tests/artifacts/javascript/on-chain-state-mgmt-zkapp-ui.js @@ -1,8 +1,8 @@ -import { logEvents } from './e2eTestsHelpers.js'; +import { logEvents } from './e2e-tests-helpers.js'; import { adminPrivateKey, HelloWorld, -} from './examples/zkapps/hello_world/hello_world.js'; +} from './examples/zkapps/hello-world/hello-world.js'; import { AccountUpdate, Field, diff --git a/tests/vk-regression/vk-regression.ts b/tests/vk-regression/vk-regression.ts index cccf97e4bc..65360578b3 100644 --- a/tests/vk-regression/vk-regression.ts +++ b/tests/vk-regression/vk-regression.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import { Voting_ } from '../../src/examples/zkapps/voting/voting.js'; import { Membership_ } from '../../src/examples/zkapps/voting/membership.js'; -import { HelloWorld } from '../../src/examples/zkapps/hello_world/hello_world.js'; +import { HelloWorld } from '../../src/examples/zkapps/hello-world/hello-world.js'; import { TokenContract, createDex } from '../../src/examples/zkapps/dex/dex.js'; import { ecdsa, From a1dda36c9905561a620be9519a5447ccdd31c601 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 13 Feb 2024 15:22:28 +0100 Subject: [PATCH 461/524] voting --- src/examples/zkapps/voting/demo.ts | 2 +- .../voting/{deployContracts.ts => deploy-contracts.ts} | 0 .../zkapps/voting/{dummyContract.ts => dummy-contract.ts} | 0 ...lection_preconditions.ts => election-preconditions.ts} | 0 .../voting/{off_chain_storage.ts => off-chain-storage.ts} | 0 ...pant_preconditions.ts => participant-preconditions.ts} | 0 .../zkapps/voting/{run_berkeley.ts => run-berkeley.ts} | 4 ++-- src/examples/zkapps/voting/run.ts | 2 +- src/examples/zkapps/voting/test.ts | 8 ++++---- .../zkapps/voting/{voting_lib.ts => voting-lib.ts} | 2 +- 10 files changed, 9 insertions(+), 9 deletions(-) rename src/examples/zkapps/voting/{deployContracts.ts => deploy-contracts.ts} (100%) rename src/examples/zkapps/voting/{dummyContract.ts => dummy-contract.ts} (100%) rename src/examples/zkapps/voting/{election_preconditions.ts => election-preconditions.ts} (100%) rename src/examples/zkapps/voting/{off_chain_storage.ts => off-chain-storage.ts} (100%) rename src/examples/zkapps/voting/{participant_preconditions.ts => participant-preconditions.ts} (100%) rename src/examples/zkapps/voting/{run_berkeley.ts => run-berkeley.ts} (98%) rename src/examples/zkapps/voting/{voting_lib.ts => voting-lib.ts} (98%) diff --git a/src/examples/zkapps/voting/demo.ts b/src/examples/zkapps/voting/demo.ts index a8f7fac57e..bb2ca126a0 100644 --- a/src/examples/zkapps/voting/demo.ts +++ b/src/examples/zkapps/voting/demo.ts @@ -12,7 +12,7 @@ import { } from 'o1js'; import { VotingApp, VotingAppParams } from './factory.js'; import { Member, MyMerkleWitness } from './member.js'; -import { OffchainStorage } from './off_chain_storage.js'; +import { OffchainStorage } from './off-chain-storage.js'; import { ParticipantPreconditions, ElectionPreconditions, diff --git a/src/examples/zkapps/voting/deployContracts.ts b/src/examples/zkapps/voting/deploy-contracts.ts similarity index 100% rename from src/examples/zkapps/voting/deployContracts.ts rename to src/examples/zkapps/voting/deploy-contracts.ts diff --git a/src/examples/zkapps/voting/dummyContract.ts b/src/examples/zkapps/voting/dummy-contract.ts similarity index 100% rename from src/examples/zkapps/voting/dummyContract.ts rename to src/examples/zkapps/voting/dummy-contract.ts diff --git a/src/examples/zkapps/voting/election_preconditions.ts b/src/examples/zkapps/voting/election-preconditions.ts similarity index 100% rename from src/examples/zkapps/voting/election_preconditions.ts rename to src/examples/zkapps/voting/election-preconditions.ts diff --git a/src/examples/zkapps/voting/off_chain_storage.ts b/src/examples/zkapps/voting/off-chain-storage.ts similarity index 100% rename from src/examples/zkapps/voting/off_chain_storage.ts rename to src/examples/zkapps/voting/off-chain-storage.ts diff --git a/src/examples/zkapps/voting/participant_preconditions.ts b/src/examples/zkapps/voting/participant-preconditions.ts similarity index 100% rename from src/examples/zkapps/voting/participant_preconditions.ts rename to src/examples/zkapps/voting/participant-preconditions.ts diff --git a/src/examples/zkapps/voting/run_berkeley.ts b/src/examples/zkapps/voting/run-berkeley.ts similarity index 98% rename from src/examples/zkapps/voting/run_berkeley.ts rename to src/examples/zkapps/voting/run-berkeley.ts index 10e911f1e5..f8b7c1e426 100644 --- a/src/examples/zkapps/voting/run_berkeley.ts +++ b/src/examples/zkapps/voting/run-berkeley.ts @@ -14,12 +14,12 @@ import { } from 'o1js'; import { VotingApp, VotingAppParams } from './factory.js'; import { Member, MyMerkleWitness } from './member.js'; -import { OffchainStorage } from './off_chain_storage.js'; +import { OffchainStorage } from './off-chain-storage.js'; import { ParticipantPreconditions, ElectionPreconditions, } from './preconditions.js'; -import { getResults, vote } from './voting_lib.js'; +import { getResults, vote } from './voting-lib.js'; await isReady; const Berkeley = Mina.Network({ diff --git a/src/examples/zkapps/voting/run.ts b/src/examples/zkapps/voting/run.ts index b2a3a9ec65..b632ee2f69 100644 --- a/src/examples/zkapps/voting/run.ts +++ b/src/examples/zkapps/voting/run.ts @@ -5,7 +5,7 @@ import { ParticipantPreconditions, } from './preconditions.js'; -import { OffchainStorage } from './off_chain_storage.js'; +import { OffchainStorage } from './off-chain-storage.js'; import { Member } from './member.js'; import { testSet } from './test.js'; import { getProfiler } from '../../utils/profiler.js'; diff --git a/src/examples/zkapps/voting/test.ts b/src/examples/zkapps/voting/test.ts index 25c164607f..7253c1d715 100644 --- a/src/examples/zkapps/voting/test.ts +++ b/src/examples/zkapps/voting/test.ts @@ -7,19 +7,19 @@ import { UInt32, Permissions, } from 'o1js'; -import { deployContracts, deployInvalidContracts } from './deployContracts.js'; -import { DummyContract } from './dummyContract.js'; +import { deployContracts, deployInvalidContracts } from './deploy-contracts.js'; +import { DummyContract } from './dummy-contract.js'; import { VotingAppParams } from './factory.js'; import { Member, MyMerkleWitness } from './member.js'; import { Membership_ } from './membership.js'; -import { OffchainStorage } from './off_chain_storage.js'; +import { OffchainStorage } from './off-chain-storage.js'; import { Voting_ } from './voting.js'; import { assertValidTx, getResults, registerMember, vote, -} from './voting_lib.js'; +} from './voting-lib.js'; type Votes = OffchainStorage; type Candidates = OffchainStorage; diff --git a/src/examples/zkapps/voting/voting_lib.ts b/src/examples/zkapps/voting/voting-lib.ts similarity index 98% rename from src/examples/zkapps/voting/voting_lib.ts rename to src/examples/zkapps/voting/voting-lib.ts index 27b474f1c6..af6cb62e20 100644 --- a/src/examples/zkapps/voting/voting_lib.ts +++ b/src/examples/zkapps/voting/voting-lib.ts @@ -1,5 +1,5 @@ import { Member, MyMerkleWitness } from './member.js'; -import { OffchainStorage } from './off_chain_storage.js'; +import { OffchainStorage } from './off-chain-storage.js'; import { Voting_ } from './voting.js'; import { Mina, PrivateKey } from 'o1js'; import { Printer } from 'prettier'; From 3b5f7c7d05776fdedd52928c8a925c8a2180b20e Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 15 Feb 2024 18:38:50 +0200 Subject: [PATCH 462/524] Missed part of the Mainnet support. --- CHANGELOG.md | 6 ++ package-lock.json | 4 +- package.json | 2 +- src/bindings | 2 +- src/lib/account-update.ts | 21 +++++-- src/lib/mina.ts | 3 +- .../mina/token/forest-iterator.unit-test.ts | 2 +- src/mina-signer/package-lock.json | 4 +- src/mina-signer/package.json | 2 +- src/mina-signer/src/sign-zkapp-command.ts | 55 ++++++++++++++----- .../src/sign-zkapp-command.unit-test.ts | 8 +-- 11 files changed, 77 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bde92e484f..42e2e34910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/834a44002...HEAD) +## [0.16.1](https://github.com/o1-labs/o1js/compare/834a44002...) + ### Breaking changes - Remove `AccountUpdate.children` and `AccountUpdate.parent` properties https://github.com/o1-labs/o1js/pull/1402 @@ -35,6 +37,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `TokenAccountUpdateIterator`, a primitive to iterate over all token account updates in a transaction https://github.com/o1-labs/o1js/pull/1398 - this is used to implement `TokenContract` under the hood +### Fixed + +- Mainnet support. TBA + ## [0.16.0](https://github.com/o1-labs/o1js/compare/e5d1e0f...834a44002) ### Breaking changes diff --git a/package-lock.json b/package-lock.json index 7596decd36..5aab1ef0a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "o1js", - "version": "0.16.0", + "version": "0.16.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "o1js", - "version": "0.16.0", + "version": "0.16.1", "license": "Apache-2.0", "dependencies": { "blakejs": "1.2.1", diff --git a/package.json b/package.json index b9dc29d6f0..8d00104b7c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "o1js", "description": "TypeScript framework for zk-SNARKs and zkApps", - "version": "0.16.0", + "version": "0.16.1", "license": "Apache-2.0", "homepage": "https://github.com/o1-labs/o1js/", "keywords": [ diff --git a/src/bindings b/src/bindings index 7c9feffb58..1022de361e 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 7c9feffb589deed29ce5606a135aeca5515c3a90 +Subproject commit 1022de361ea86f19eaff1af151916eec9b397aff diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index ff2e7c38d3..d2f56128f8 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -1017,7 +1017,12 @@ class AccountUpdate implements Types.AccountUpdate { // implementations are equivalent, and catch regressions quickly if (Provable.inCheckedComputation()) { let input = Types.AccountUpdate.toInput(this); - return hashWithPrefix(prefixes.body, packToFields(input)); + return hashWithPrefix( + activeInstance.getNetworkId() === 'mainnet' + ? prefixes.zkappBodyMainnet + : prefixes.zkappBodyTestnet, + packToFields(input) + ); } else { let json = Types.AccountUpdate.toJSON(this); return Field(Test.hashFromJson.accountUpdate(JSON.stringify(json))); @@ -1048,7 +1053,8 @@ class AccountUpdate implements Types.AccountUpdate { forest, (a) => a.hash(), Poseidon.hashWithPrefix, - emptyHash + emptyHash, + activeInstance.getNetworkId() ); return { accountUpdate, calls }; } @@ -1386,7 +1392,13 @@ class AccountUpdate implements Types.AccountUpdate { // call forest stuff function hashAccountUpdate(update: AccountUpdate) { - return genericHash(AccountUpdate, prefixes.body, update); + return genericHash( + AccountUpdate, + activeInstance.getNetworkId() === 'mainnet' + ? prefixes.zkappBodyMainnet + : prefixes.zkappBodyTestnet, + update + ); } class HashedAccountUpdate extends Hashed.create( @@ -1983,7 +1995,8 @@ function addMissingSignatures( ): ZkappCommandSigned { let additionalPublicKeys = additionalKeys.map((sk) => sk.toPublicKey()); let { commitment, fullCommitment } = transactionCommitments( - TypesBigint.ZkappCommand.fromJSON(ZkappCommand.toJSON(zkappCommand)) + TypesBigint.ZkappCommand.fromJSON(ZkappCommand.toJSON(zkappCommand)), + activeInstance.getNetworkId() ); function addFeePayerSignature(accountUpdate: FeePayerUnsigned): FeePayer { diff --git a/src/lib/mina.ts b/src/lib/mina.ts index a64163cb9b..44c7830517 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -391,7 +391,8 @@ function LocalBlockchain({ let zkappCommandJson = ZkappCommand.toJSON(txn.transaction); let commitments = transactionCommitments( - TypesBigint.ZkappCommand.fromJSON(zkappCommandJson) + TypesBigint.ZkappCommand.fromJSON(zkappCommandJson), + minaNetworkId ); if (enforceTransactionLimits) verifyTransactionLimits(txn.transaction); diff --git a/src/lib/mina/token/forest-iterator.unit-test.ts b/src/lib/mina/token/forest-iterator.unit-test.ts index 4a00bb57f7..d93158b9ca 100644 --- a/src/lib/mina/token/forest-iterator.unit-test.ts +++ b/src/lib/mina/token/forest-iterator.unit-test.ts @@ -56,7 +56,7 @@ test.custom({ timeBudget: 1000 })( (flatUpdatesBigint) => { // reference: bigint callforest hash from mina-signer let forestBigint = accountUpdatesToCallForest(flatUpdatesBigint); - let expectedHash = callForestHash(forestBigint); + let expectedHash = callForestHash(forestBigint, 'testnet'); let flatUpdates = flatUpdatesBigint.map(accountUpdateFromBigint); let forest = AccountUpdateForest.fromFlatArray(flatUpdates); diff --git a/src/mina-signer/package-lock.json b/src/mina-signer/package-lock.json index 55178b872b..7b97ce6a84 100644 --- a/src/mina-signer/package-lock.json +++ b/src/mina-signer/package-lock.json @@ -1,12 +1,12 @@ { "name": "mina-signer", - "version": "3.0.0", + "version": "3.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mina-signer", - "version": "3.0.0", + "version": "3.0.1", "license": "Apache-2.0", "dependencies": { "blakejs": "^1.2.1", diff --git a/src/mina-signer/package.json b/src/mina-signer/package.json index 8152789289..f6efdbefab 100644 --- a/src/mina-signer/package.json +++ b/src/mina-signer/package.json @@ -1,7 +1,7 @@ { "name": "mina-signer", "description": "Node API for signing transactions on various networks for Mina Protocol", - "version": "3.0.0", + "version": "3.0.1", "type": "module", "scripts": { "build": "tsc -p ../../tsconfig.mina-signer.json", diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 40e983e4fa..a42ff3ec3b 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -44,7 +44,10 @@ function signZkappCommand( ): Json.ZkappCommand { let zkappCommand = ZkappCommand.fromJSON(zkappCommand_); - let { commitment, fullCommitment } = transactionCommitments(zkappCommand); + let { commitment, fullCommitment } = transactionCommitments( + zkappCommand, + networkId + ); let privateKey = PrivateKey.fromBase58(privateKeyBase58); let publicKey = zkappCommand.feePayer.body.publicKey; @@ -71,7 +74,10 @@ function verifyZkappCommandSignature( ) { let zkappCommand = ZkappCommand.fromJSON(zkappCommand_); - let { commitment, fullCommitment } = transactionCommitments(zkappCommand); + let { commitment, fullCommitment } = transactionCommitments( + zkappCommand, + networkId + ); let publicKey = PublicKey.fromBase58(publicKeyBase58); // verify fee payer signature @@ -108,14 +114,17 @@ function verifyAccountUpdateSignature( return verifyFieldElement(signature, usedCommitment, publicKey, networkId); } -function transactionCommitments(zkappCommand: ZkappCommand) { +function transactionCommitments( + zkappCommand: ZkappCommand, + networkId: NetworkId +) { if (!isCallDepthValid(zkappCommand)) { throw Error('zkapp command: invalid call depth'); } let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest); + let commitment = callForestHash(callForest, networkId); let memoHash = Memo.hash(Memo.fromBase58(zkappCommand.memo)); - let feePayerDigest = feePayerHash(zkappCommand.feePayer); + let feePayerDigest = feePayerHash(zkappCommand.feePayer, networkId); let fullCommitment = hashWithPrefix(prefixes.accountUpdateCons, [ memoHash, feePayerDigest, @@ -150,22 +159,37 @@ function accountUpdatesToCallForest( return forest; } -function accountUpdateHash(update: AccountUpdate) { +function accountUpdateHash(update: AccountUpdate, networkId: NetworkId) { assertAuthorizationKindValid(update); let input = AccountUpdate.toInput(update); let fields = packToFields(input); - return hashWithPrefix(prefixes.body, fields); + return hashWithPrefix( + networkId === 'mainnet' + ? prefixes.zkappBodyMainnet + : prefixes.zkappBodyTestnet, + fields + ); } -function callForestHash(forest: CallForest): bigint { - return callForestHashGeneric(forest, accountUpdateHash, hashWithPrefix, 0n); +function callForestHash( + forest: CallForest, + networkId: NetworkId +): bigint { + return callForestHashGeneric( + forest, + accountUpdateHash, + hashWithPrefix, + 0n, + networkId + ); } function callForestHashGeneric( forest: CallForest, - hash: (a: A) => F, + hash: (a: A, networkId: NetworkId) => F, hashWithPrefix: (prefix: string, input: F[]) => F, - emptyHash: F + emptyHash: F, + networkId: NetworkId ): F { let stackHash = emptyHash; for (let callTree of [...forest].reverse()) { @@ -173,9 +197,10 @@ function callForestHashGeneric( callTree.children, hash, hashWithPrefix, - emptyHash + emptyHash, + networkId ); - let treeHash = hash(callTree.accountUpdate); + let treeHash = hash(callTree.accountUpdate, networkId); let nodeHash = hashWithPrefix(prefixes.accountUpdateNode, [ treeHash, calls, @@ -193,9 +218,9 @@ type FeePayer = ZkappCommand['feePayer']; function createFeePayer(feePayer: FeePayer['body']): FeePayer { return { authorization: '', body: feePayer }; } -function feePayerHash(feePayer: FeePayer) { +function feePayerHash(feePayer: FeePayer, networkId: NetworkId) { let accountUpdate = accountUpdateFromFeePayer(feePayer); - return accountUpdateHash(accountUpdate); + return accountUpdateHash(accountUpdate, networkId); } function accountUpdateFromFeePayer({ diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index f8a4b51fd1..013beaa4f4 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -99,7 +99,7 @@ test(Random.accountUpdate, (accountUpdate) => { let packedSnarky = packToFieldsSnarky(inputSnarky); expect(toJSON(packed)).toEqual(toJSON(packedSnarky)); - let hash = accountUpdateHash(accountUpdate); + let hash = accountUpdateHash(accountUpdate, 'testnet'); let hashSnarky = accountUpdateSnarky.hash(); expect(hash).toEqual(hashSnarky.toBigInt()); }); @@ -134,7 +134,7 @@ test(RandomTransaction.zkappCommand, (zkappCommand, assert) => { JSON.stringify(zkappCommandJson) ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest); + let commitment = callForestHash(callForest, 'testnet'); expect(commitment).toEqual(FieldConst.toBigint(ocamlCommitments.commitment)); }); @@ -176,7 +176,7 @@ test( JSON.stringify(zkappCommandJson) ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest); + let commitment = callForestHash(callForest, 'testnet'); expect(commitment).toEqual( FieldConst.toBigint(ocamlCommitments.commitment) ); @@ -200,7 +200,7 @@ test( stringify(feePayerInput1.packed) ); - let feePayerDigest = feePayerHash(feePayer); + let feePayerDigest = feePayerHash(feePayer, 'testnet'); expect(feePayerDigest).toEqual( FieldConst.toBigint(ocamlCommitments.feePayerHash) ); From ffb50a23e944b237572470e92e63e833c7b7aa9d Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 15 Feb 2024 18:42:12 +0200 Subject: [PATCH 463/524] Changelog. --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e2e34910..b0cba5c1aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm _Security_ in case of vulnerabilities. --> -## [Unreleased](https://github.com/o1-labs/o1js/compare/834a44002...HEAD) +## [Unreleased](https://github.com/o1-labs/o1js/compare/3b5f7c7...HEAD) -## [0.16.1](https://github.com/o1-labs/o1js/compare/834a44002...) +## [0.16.1](https://github.com/o1-labs/o1js/compare/834a44002...3b5f7c7) ### Breaking changes @@ -39,7 +39,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed -- Mainnet support. TBA +- Mainnet support. https://github.com/o1-labs/o1js/pull/1437 ## [0.16.0](https://github.com/o1-labs/o1js/compare/e5d1e0f...834a44002) From 91db2ce85b043a48d8a1693a7b215492e6b947f3 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 15 Feb 2024 19:16:34 +0200 Subject: [PATCH 464/524] Updated bindings pin. --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 1022de361e..48a129938c 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 1022de361ea86f19eaff1af151916eec9b397aff +Subproject commit 48a129938c73295076deda6ee326277dac554796 From c9980974c93ed6f2f52dc077a745ab554e014e27 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 15 Feb 2024 19:26:29 +0200 Subject: [PATCH 465/524] Updated bindings. --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 48a129938c..575d972a54 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 48a129938c73295076deda6ee326277dac554796 +Subproject commit 575d972a5455088207c0c9f2f6a0ba0bea49f9ce From 57fbe4d3a7d5c6721e4a7ea77a63852f7454c1e2 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 15 Feb 2024 19:44:53 +0200 Subject: [PATCH 466/524] MIna-signer tests fix. --- .../src/sign-zkapp-command.unit-test.ts | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 013beaa4f4..2602f9c29c 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -125,18 +125,24 @@ test(memoGenerator, (memoString) => { }); // zkapp transaction - basic properties & commitment -test(RandomTransaction.zkappCommand, (zkappCommand, assert) => { - zkappCommand.accountUpdates.forEach(fixVerificationKey); +test( + RandomTransaction.zkappCommand, + RandomTransaction.networkId, + (zkappCommand, networkId, assert) => { + zkappCommand.accountUpdates.forEach(fixVerificationKey); - assert(isCallDepthValid(zkappCommand)); - let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); - let ocamlCommitments = Test.hashFromJson.transactionCommitments( - JSON.stringify(zkappCommandJson) - ); - let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest, 'testnet'); - expect(commitment).toEqual(FieldConst.toBigint(ocamlCommitments.commitment)); -}); + assert(isCallDepthValid(zkappCommand)); + let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); + let ocamlCommitments = Test.hashFromJson.transactionCommitments( + JSON.stringify(zkappCommandJson) + ); + let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); + let commitment = callForestHash(callForest, networkId); + expect(commitment).toEqual( + FieldConst.toBigint(ocamlCommitments.commitment) + ); + } +); // invalid zkapp transactions test.negative( @@ -151,7 +157,9 @@ test.negative( // zkapp transaction test( RandomTransaction.zkappCommandAndFeePayerKey, - ({ feePayerKey, zkappCommand }) => { + RandomTransaction.networkId, + (zkappCommandAndFeePayerKey, networkId) => { + const { feePayerKey, zkappCommand } = zkappCommandAndFeePayerKey; zkappCommand.accountUpdates.forEach(fixVerificationKey); let feePayerKeyBase58 = PrivateKey.toBase58(feePayerKey); @@ -176,7 +184,7 @@ test( JSON.stringify(zkappCommandJson) ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest, 'testnet'); + let commitment = callForestHash(callForest, networkId); expect(commitment).toEqual( FieldConst.toBigint(ocamlCommitments.commitment) ); From 2f74fdd817ec64ed51776f4a84b76e7f05cf2f02 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 15 Feb 2024 19:48:18 +0200 Subject: [PATCH 467/524] MIna-signer tests fix. --- .../src/sign-zkapp-command.unit-test.ts | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 2602f9c29c..8408a5f348 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -81,28 +81,32 @@ expect(stringify(dummyInput.packed)).toEqual( stringify(dummyInputSnarky.packed) ); -test(Random.accountUpdate, (accountUpdate) => { - fixVerificationKey(accountUpdate); +test( + Random.accountUpdate, + RandomTransaction.networkId, + (accountUpdate, networkId) => { + fixVerificationKey(accountUpdate); - // example account update - let accountUpdateJson: Json.AccountUpdate = - AccountUpdate.toJSON(accountUpdate); + // example account update + let accountUpdateJson: Json.AccountUpdate = + AccountUpdate.toJSON(accountUpdate); - // account update hash - let accountUpdateSnarky = AccountUpdateSnarky.fromJSON(accountUpdateJson); - let inputSnarky = TypesSnarky.AccountUpdate.toInput(accountUpdateSnarky); - let input = AccountUpdate.toInput(accountUpdate); - expect(toJSON(input.fields)).toEqual(toJSON(inputSnarky.fields)); - expect(toJSON(input.packed)).toEqual(toJSON(inputSnarky.packed)); + // account update hash + let accountUpdateSnarky = AccountUpdateSnarky.fromJSON(accountUpdateJson); + let inputSnarky = TypesSnarky.AccountUpdate.toInput(accountUpdateSnarky); + let input = AccountUpdate.toInput(accountUpdate); + expect(toJSON(input.fields)).toEqual(toJSON(inputSnarky.fields)); + expect(toJSON(input.packed)).toEqual(toJSON(inputSnarky.packed)); - let packed = packToFields(input); - let packedSnarky = packToFieldsSnarky(inputSnarky); - expect(toJSON(packed)).toEqual(toJSON(packedSnarky)); + let packed = packToFields(input); + let packedSnarky = packToFieldsSnarky(inputSnarky); + expect(toJSON(packed)).toEqual(toJSON(packedSnarky)); - let hash = accountUpdateHash(accountUpdate, 'testnet'); - let hashSnarky = accountUpdateSnarky.hash(); - expect(hash).toEqual(hashSnarky.toBigInt()); -}); + let hash = accountUpdateHash(accountUpdate, networkId); + let hashSnarky = accountUpdateSnarky.hash(); + expect(hash).toEqual(hashSnarky.toBigInt()); + } +); // private key to/from base58 test(Random.json.privateKey, (feePayerKeyBase58) => { @@ -208,7 +212,7 @@ test( stringify(feePayerInput1.packed) ); - let feePayerDigest = feePayerHash(feePayer, 'testnet'); + let feePayerDigest = feePayerHash(feePayer, networkId); expect(feePayerDigest).toEqual( FieldConst.toBigint(ocamlCommitments.feePayerHash) ); From 1984c25ca58075b6124dbe8c26e3bbe324001fbb Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Thu, 15 Feb 2024 21:24:26 +0200 Subject: [PATCH 468/524] Additional account update hash case. --- .../src/sign-zkapp-command.unit-test.ts | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 8408a5f348..f2b7fe9129 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -44,6 +44,7 @@ import { Ml, MlHashInput } from '../../lib/ml/conversion.js'; import { FieldConst } from '../../lib/field.js'; import { mocks } from '../../bindings/crypto/constants.js'; import { NetworkId } from './types.js'; +import { setActiveInstance, Network } from '../../lib/mina.js'; // monkey-patch bigint to json (BigInt.prototype as any).toJSON = function () { @@ -81,32 +82,42 @@ expect(stringify(dummyInput.packed)).toEqual( stringify(dummyInputSnarky.packed) ); -test( - Random.accountUpdate, - RandomTransaction.networkId, - (accountUpdate, networkId) => { - fixVerificationKey(accountUpdate); - - // example account update - let accountUpdateJson: Json.AccountUpdate = - AccountUpdate.toJSON(accountUpdate); - - // account update hash - let accountUpdateSnarky = AccountUpdateSnarky.fromJSON(accountUpdateJson); - let inputSnarky = TypesSnarky.AccountUpdate.toInput(accountUpdateSnarky); - let input = AccountUpdate.toInput(accountUpdate); - expect(toJSON(input.fields)).toEqual(toJSON(inputSnarky.fields)); - expect(toJSON(input.packed)).toEqual(toJSON(inputSnarky.packed)); - - let packed = packToFields(input); - let packedSnarky = packToFieldsSnarky(inputSnarky); - expect(toJSON(packed)).toEqual(toJSON(packedSnarky)); - - let hash = accountUpdateHash(accountUpdate, networkId); - let hashSnarky = accountUpdateSnarky.hash(); - expect(hash).toEqual(hashSnarky.toBigInt()); - } -); +test(Random.accountUpdate, (accountUpdate) => { + const testnetMinaInstance = Network({ + networkId: 'testnet', + mina: 'http://localhost:8080/graphql', + }); + const mainnetMinaInstance = Network({ + networkId: 'mainnet', + mina: 'http://localhost:8080/graphql', + }); + + fixVerificationKey(accountUpdate); + + // example account update + let accountUpdateJson: Json.AccountUpdate = + AccountUpdate.toJSON(accountUpdate); + + // account update hash + let accountUpdateSnarky = AccountUpdateSnarky.fromJSON(accountUpdateJson); + let inputSnarky = TypesSnarky.AccountUpdate.toInput(accountUpdateSnarky); + let input = AccountUpdate.toInput(accountUpdate); + expect(toJSON(input.fields)).toEqual(toJSON(inputSnarky.fields)); + expect(toJSON(input.packed)).toEqual(toJSON(inputSnarky.packed)); + + let packed = packToFields(input); + let packedSnarky = packToFieldsSnarky(inputSnarky); + expect(toJSON(packed)).toEqual(toJSON(packedSnarky)); + + let hashTestnet = accountUpdateHash(accountUpdate, 'testnet'); + let hashMainnet = accountUpdateHash(accountUpdate, 'mainnet'); + setActiveInstance(testnetMinaInstance); + let hashSnarkyTestnet = accountUpdateSnarky.hash(); + setActiveInstance(mainnetMinaInstance); + let hashSnarkyMainnet = accountUpdateSnarky.hash(); + expect(hashTestnet).toEqual(hashSnarkyTestnet.toBigInt()); + expect(hashMainnet).toEqual(hashSnarkyMainnet.toBigInt()); +}); // private key to/from base58 test(Random.json.privateKey, (feePayerKeyBase58) => { From 1eb25efe8d980ab8784e7ff2d66957f3eeebe9cc Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 16 Feb 2024 00:12:32 +0200 Subject: [PATCH 469/524] (WIP) Introduce the NetworkID parameter for some of the Test.hashFromJson methods. --- src/bindings | 2 +- src/lib/account-update.ts | 7 ++++++- src/snarky.d.ts | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bindings b/src/bindings index 575d972a54..363a00e72c 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 575d972a5455088207c0c9f2f6a0ba0bea49f9ce +Subproject commit 363a00e72c39b6e359538fd30211c487a531c6cd diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index d2f56128f8..f8673c7ad4 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -1025,7 +1025,12 @@ class AccountUpdate implements Types.AccountUpdate { ); } else { let json = Types.AccountUpdate.toJSON(this); - return Field(Test.hashFromJson.accountUpdate(JSON.stringify(json))); + return Field( + Test.hashFromJson.accountUpdate( + JSON.stringify(json), + activeInstance.getNetworkId() + ) + ); } } diff --git a/src/snarky.d.ts b/src/snarky.d.ts index 2c47b9dc63..79626cd3dc 100644 --- a/src/snarky.d.ts +++ b/src/snarky.d.ts @@ -659,7 +659,7 @@ declare const Test: { accountUpdate(json: string): MlArray; }; hashFromJson: { - accountUpdate(json: string): FieldConst; + accountUpdate(json: string, networkId: string): FieldConst; /** * Returns the commitment of a JSON transaction. */ From 65ea211c6671ceb79f0392ff09105f2f30be3935 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 16 Feb 2024 10:53:31 +0200 Subject: [PATCH 470/524] Tests refactoring. --- .../src/sign-zkapp-command.unit-test.ts | 78 +++++++++---------- 1 file changed, 36 insertions(+), 42 deletions(-) diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index f2b7fe9129..5161f2b410 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -1,14 +1,5 @@ import { expect } from 'expect'; -import { Ledger, Test, Pickles } from '../../snarky.js'; -import { - PrivateKey as PrivateKeySnarky, - PublicKey as PublicKeySnarky, -} from '../../lib/signature.js'; -import { - AccountUpdate as AccountUpdateSnarky, - ZkappCommand as ZkappCommandSnarky, -} from '../../lib/account-update.js'; -import { PrivateKey, PublicKey } from '../../provable/curve-bigint.js'; +import { mocks } from '../../bindings/crypto/constants.js'; import { AccountUpdate, Field, @@ -16,6 +7,28 @@ import { ZkappCommand, } from '../../bindings/mina-transaction/gen/transaction-bigint.js'; import * as TypesSnarky from '../../bindings/mina-transaction/gen/transaction.js'; +import { + AccountUpdate as AccountUpdateSnarky, + ZkappCommand as ZkappCommandSnarky, +} from '../../lib/account-update.js'; +import { FieldConst } from '../../lib/field.js'; +import { packToFields as packToFieldsSnarky } from '../../lib/hash.js'; +import { Network, setActiveInstance } from '../../lib/mina.js'; +import { Ml, MlHashInput } from '../../lib/ml/conversion.js'; +import { + PrivateKey as PrivateKeySnarky, + PublicKey as PublicKeySnarky, +} from '../../lib/signature.js'; +import { Random, test, withHardCoded } from '../../lib/testing/property.js'; +import { PrivateKey, PublicKey } from '../../provable/curve-bigint.js'; +import { + hashWithPrefix, + packToFields, + prefixes, +} from '../../provable/poseidon-bigint.js'; +import { Pickles, Test } from '../../snarky.js'; +import { Memo } from './memo.js'; +import { RandomTransaction } from './random-transaction.js'; import { accountUpdateFromFeePayer, accountUpdateHash, @@ -26,25 +39,12 @@ import { signZkappCommand, verifyZkappCommandSignature, } from './sign-zkapp-command.js'; -import { - hashWithPrefix, - packToFields, - prefixes, -} from '../../provable/poseidon-bigint.js'; -import { packToFields as packToFieldsSnarky } from '../../lib/hash.js'; -import { Memo } from './memo.js'; import { Signature, signFieldElement, verifyFieldElement, } from './signature.js'; -import { Random, test, withHardCoded } from '../../lib/testing/property.js'; -import { RandomTransaction } from './random-transaction.js'; -import { Ml, MlHashInput } from '../../lib/ml/conversion.js'; -import { FieldConst } from '../../lib/field.js'; -import { mocks } from '../../bindings/crypto/constants.js'; import { NetworkId } from './types.js'; -import { setActiveInstance, Network } from '../../lib/mina.js'; // monkey-patch bigint to json (BigInt.prototype as any).toJSON = function () { @@ -140,24 +140,18 @@ test(memoGenerator, (memoString) => { }); // zkapp transaction - basic properties & commitment -test( - RandomTransaction.zkappCommand, - RandomTransaction.networkId, - (zkappCommand, networkId, assert) => { - zkappCommand.accountUpdates.forEach(fixVerificationKey); +test(RandomTransaction.zkappCommand, (zkappCommand, assert) => { + zkappCommand.accountUpdates.forEach(fixVerificationKey); - assert(isCallDepthValid(zkappCommand)); - let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); - let ocamlCommitments = Test.hashFromJson.transactionCommitments( - JSON.stringify(zkappCommandJson) - ); - let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest, networkId); - expect(commitment).toEqual( - FieldConst.toBigint(ocamlCommitments.commitment) - ); - } -); + assert(isCallDepthValid(zkappCommand)); + let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); + let ocamlCommitments = Test.hashFromJson.transactionCommitments( + JSON.stringify(zkappCommandJson) + ); + let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); + let commitment = callForestHash(callForest, 'testnet'); + expect(commitment).toEqual(FieldConst.toBigint(ocamlCommitments.commitment)); +}); // invalid zkapp transactions test.negative( @@ -199,7 +193,7 @@ test( JSON.stringify(zkappCommandJson) ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest, networkId); + let commitment = callForestHash(callForest, 'testnet'); expect(commitment).toEqual( FieldConst.toBigint(ocamlCommitments.commitment) ); @@ -223,7 +217,7 @@ test( stringify(feePayerInput1.packed) ); - let feePayerDigest = feePayerHash(feePayer, networkId); + let feePayerDigest = feePayerHash(feePayer, 'testnet'); expect(feePayerDigest).toEqual( FieldConst.toBigint(ocamlCommitments.feePayerHash) ); From 921c584f81e8ab39bcfa96989ef1691114abf1a3 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Fri, 16 Feb 2024 12:21:03 +0200 Subject: [PATCH 471/524] Bindings pin update. --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 363a00e72c..447858757f 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 363a00e72c39b6e359538fd30211c487a531c6cd +Subproject commit 447858757faca1eabf09e97fae21ce6540726104 From 7d0b2ea5705ac501949e183ce843b928c440c9d8 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 16 Feb 2024 15:47:18 +0100 Subject: [PATCH 472/524] simplify network id hashing --- src/bindings | 2 +- src/mina-signer/src/sign-zkapp-command.ts | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/bindings b/src/bindings index 363a00e72c..9e68971065 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 363a00e72c39b6e359538fd30211c487a531c6cd +Subproject commit 9e689710651e9d0cf099efb494214007a2569285 diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index a42ff3ec3b..e5a6985f08 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -159,16 +159,23 @@ function accountUpdatesToCallForest( return forest; } +const zkAppBodyPrefix = (network: string) => { + switch (network) { + case 'mainnet': + return prefixes.zkappBodyMainnet; + case 'testnet': + return prefixes.zkappBodyTestnet; + + default: + return 'ZkappBody' + network; + } +}; + function accountUpdateHash(update: AccountUpdate, networkId: NetworkId) { assertAuthorizationKindValid(update); let input = AccountUpdate.toInput(update); let fields = packToFields(input); - return hashWithPrefix( - networkId === 'mainnet' - ? prefixes.zkappBodyMainnet - : prefixes.zkappBodyTestnet, - fields - ); + return hashWithPrefix(zkAppBodyPrefix(networkId), fields); } function callForestHash( From b516570f5b4ea7d6c7a69464927eb0eb8606cfc8 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 16 Feb 2024 15:47:28 +0100 Subject: [PATCH 473/524] simplify unit tests --- .../src/sign-zkapp-command.unit-test.ts | 65 ++++++++----------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 5161f2b410..b11d9a02c9 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -146,7 +146,8 @@ test(RandomTransaction.zkappCommand, (zkappCommand, assert) => { assert(isCallDepthValid(zkappCommand)); let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); let ocamlCommitments = Test.hashFromJson.transactionCommitments( - JSON.stringify(zkappCommandJson) + JSON.stringify(zkappCommandJson), + 'testnet' ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); let commitment = callForestHash(callForest, 'testnet'); @@ -190,10 +191,11 @@ test( // tx commitment let ocamlCommitments = Test.hashFromJson.transactionCommitments( - JSON.stringify(zkappCommandJson) + JSON.stringify(zkappCommandJson), + networkId ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest, 'testnet'); + let commitment = callForestHash(callForest, networkId); expect(commitment).toEqual( FieldConst.toBigint(ocamlCommitments.commitment) ); @@ -217,7 +219,7 @@ test( stringify(feePayerInput1.packed) ); - let feePayerDigest = feePayerHash(feePayer, 'testnet'); + let feePayerDigest = feePayerHash(feePayer, networkId); expect(feePayerDigest).toEqual( FieldConst.toBigint(ocamlCommitments.feePayerHash) ); @@ -232,55 +234,42 @@ test( ); // signature - let sigTestnet = signFieldElement(fullCommitment, feePayerKey, 'testnet'); - let sigMainnet = signFieldElement(fullCommitment, feePayerKey, 'mainnet'); - let sigTestnetOcaml = Test.signature.signFieldElement( - ocamlCommitments.fullCommitment, - Ml.fromPrivateKey(feePayerKeySnarky), - false + let sigFieldElements = signFieldElement( + fullCommitment, + feePayerKey, + networkId ); - let sigMainnetOcaml = Test.signature.signFieldElement( + let sigOCaml = Test.signature.signFieldElement( ocamlCommitments.fullCommitment, Ml.fromPrivateKey(feePayerKeySnarky), - true + networkId === 'mainnet' ? true : false ); - expect(Signature.toBase58(sigTestnet)).toEqual(sigTestnetOcaml); - expect(Signature.toBase58(sigMainnet)).toEqual(sigMainnetOcaml); + + expect(Signature.toBase58(sigFieldElements)).toEqual(sigOCaml); let verify = (s: Signature, id: NetworkId) => verifyFieldElement(s, fullCommitment, feePayerAddress, id); - expect(verify(sigTestnet, 'testnet')).toEqual(true); - expect(verify(sigTestnet, 'mainnet')).toEqual(false); - expect(verify(sigMainnet, 'testnet')).toEqual(false); - expect(verify(sigMainnet, 'mainnet')).toEqual(true); + + expect(verify(sigFieldElements, networkId)).toEqual(true); + expect( + verify(sigFieldElements, networkId === 'mainnet' ? 'testnet' : 'mainnet') + ).toEqual(false); // full end-to-end test: sign a zkapp transaction - let sTest = signZkappCommand( - zkappCommandJson, - feePayerKeyBase58, - 'testnet' - ); - expect(sTest.feePayer.authorization).toEqual(sigTestnetOcaml); - let sMain = signZkappCommand( - zkappCommandJson, - feePayerKeyBase58, - 'mainnet' - ); - expect(sMain.feePayer.authorization).toEqual(sigMainnetOcaml); + let sig = signZkappCommand(zkappCommandJson, feePayerKeyBase58, networkId); + expect(sig.feePayer.authorization).toEqual(sigOCaml); let feePayerAddressBase58 = PublicKey.toBase58(feePayerAddress); expect( - verifyZkappCommandSignature(sTest, feePayerAddressBase58, 'testnet') + verifyZkappCommandSignature(sig, feePayerAddressBase58, networkId) ).toEqual(true); expect( - verifyZkappCommandSignature(sTest, feePayerAddressBase58, 'mainnet') - ).toEqual(false); - expect( - verifyZkappCommandSignature(sMain, feePayerAddressBase58, 'testnet') + verifyZkappCommandSignature( + sig, + feePayerAddressBase58, + networkId === 'mainnet' ? 'testnet' : 'mainnet' + ) ).toEqual(false); - expect( - verifyZkappCommandSignature(sMain, feePayerAddressBase58, 'mainnet') - ).toEqual(true); } ); From f62a064cb273307f47b4ad6a9209d7d5e4273e70 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 16 Feb 2024 15:47:40 +0100 Subject: [PATCH 474/524] fix snarky interface --- src/snarky.d.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/snarky.d.ts b/src/snarky.d.ts index 79626cd3dc..51a43dcb40 100644 --- a/src/snarky.d.ts +++ b/src/snarky.d.ts @@ -663,7 +663,10 @@ declare const Test: { /** * Returns the commitment of a JSON transaction. */ - transactionCommitments(txJson: string): { + transactionCommitments( + txJson: string, + networkId: string + ): { commitment: FieldConst; fullCommitment: FieldConst; feePayerHash: FieldConst; From ae267191aeeabf9f22fe5440683fdf25e854714e Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Fri, 16 Feb 2024 15:54:44 +0100 Subject: [PATCH 475/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index 9e68971065..a6b6800186 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit 9e689710651e9d0cf099efb494214007a2569285 +Subproject commit a6b6800186752b3cf5c9a29b7eb167e494784286 From b7b5f14f057b2f87f4c19211ba114ba03e6d3cd4 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 17 Feb 2024 10:48:47 +0200 Subject: [PATCH 476/524] Fix for Mina-Signer exports. --- src/mina-signer/index.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mina-signer/index.d.ts b/src/mina-signer/index.d.ts index 96efc51dfd..4de98de40f 100644 --- a/src/mina-signer/index.d.ts +++ b/src/mina-signer/index.d.ts @@ -1,6 +1,6 @@ // this file is a wrapper for supporting types in both commonjs and esm projects import Client from './mina-signer.ts'; -export { type NetworkId } from './src/types.ts'; +import { NetworkId } from './src/types.ts'; -export = Client; +export { Client, NetworkId }; From aa3619dfdaca768a75c79dab4996d165cf2778e1 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 17 Feb 2024 10:49:31 +0200 Subject: [PATCH 477/524] Version bump. --- src/mina-signer/package-lock.json | 4 ++-- src/mina-signer/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mina-signer/package-lock.json b/src/mina-signer/package-lock.json index 7b97ce6a84..78be1e1d0a 100644 --- a/src/mina-signer/package-lock.json +++ b/src/mina-signer/package-lock.json @@ -1,12 +1,12 @@ { "name": "mina-signer", - "version": "3.0.1", + "version": "3.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mina-signer", - "version": "3.0.1", + "version": "3.0.2", "license": "Apache-2.0", "dependencies": { "blakejs": "^1.2.1", diff --git a/src/mina-signer/package.json b/src/mina-signer/package.json index f6efdbefab..d86abaf654 100644 --- a/src/mina-signer/package.json +++ b/src/mina-signer/package.json @@ -1,7 +1,7 @@ { "name": "mina-signer", "description": "Node API for signing transactions on various networks for Mina Protocol", - "version": "3.0.1", + "version": "3.0.2", "type": "module", "scripts": { "build": "tsc -p ../../tsconfig.mina-signer.json", From d362809aeecc58859258d96497be1f431c2e484a Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 17 Feb 2024 11:30:37 +0200 Subject: [PATCH 478/524] Default export. --- src/mina-signer/index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mina-signer/index.d.ts b/src/mina-signer/index.d.ts index 4de98de40f..1cb97651f7 100644 --- a/src/mina-signer/index.d.ts +++ b/src/mina-signer/index.d.ts @@ -3,4 +3,5 @@ import Client from './mina-signer.ts'; import { NetworkId } from './src/types.ts'; +export default Client; export { Client, NetworkId }; From 401d99c8ab8704d7df8b7e8f22edd6576942729d Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sat, 17 Feb 2024 16:38:46 +0200 Subject: [PATCH 479/524] Export improvements. --- package-lock.json | 4 ++-- package.json | 2 +- src/index.ts | 2 ++ src/mina-signer/index.cjs | 2 +- src/mina-signer/index.d.ts | 5 ++--- src/mina-signer/mina-signer.ts | 2 +- src/mina-signer/package-lock.json | 4 ++-- src/mina-signer/package.json | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5aab1ef0a8..5cba08e5e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "o1js", - "version": "0.16.1", + "version": "0.16.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "o1js", - "version": "0.16.1", + "version": "0.16.2", "license": "Apache-2.0", "dependencies": { "blakejs": "1.2.1", diff --git a/package.json b/package.json index 8d00104b7c..fb3856ef3a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "o1js", "description": "TypeScript framework for zk-SNARKs and zkApps", - "version": "0.16.1", + "version": "0.16.2", "license": "Apache-2.0", "homepage": "https://github.com/o1-labs/o1js/", "keywords": [ diff --git a/src/index.ts b/src/index.ts index 6e6e1e6e51..14cf5b4181 100644 --- a/src/index.ts +++ b/src/index.ts @@ -109,6 +109,8 @@ export { ZkProgram }; export { Crypto } from './lib/crypto.js'; +export type { NetworkId } from './mina-signer/mina-signer.js'; + // experimental APIs import { memoizeWitness } from './lib/provable.js'; export { Experimental }; diff --git a/src/mina-signer/index.cjs b/src/mina-signer/index.cjs index 600319e530..da037851c3 100644 --- a/src/mina-signer/index.cjs +++ b/src/mina-signer/index.cjs @@ -1,5 +1,5 @@ // this file is a wrapper for supporting commonjs imports -let Client = require('./mina-signer.js'); +const Client = require('./mina-signer.js'); module.exports = Client.default; diff --git a/src/mina-signer/index.d.ts b/src/mina-signer/index.d.ts index 1cb97651f7..c0417d103f 100644 --- a/src/mina-signer/index.d.ts +++ b/src/mina-signer/index.d.ts @@ -1,7 +1,6 @@ // this file is a wrapper for supporting types in both commonjs and esm projects -import Client from './mina-signer.ts'; -import { NetworkId } from './src/types.ts'; +import Client, { type NetworkId } from './mina-signer.ts'; export default Client; -export { Client, NetworkId }; +export { Client, type NetworkId }; diff --git a/src/mina-signer/mina-signer.ts b/src/mina-signer/mina-signer.ts index fe3739404f..1c7c9f55b4 100644 --- a/src/mina-signer/mina-signer.ts +++ b/src/mina-signer/mina-signer.ts @@ -34,7 +34,7 @@ import { import { sign, Signature, verify } from './src/signature.js'; import { createNullifier } from './src/nullifier.js'; -export { Client as default }; +export { Client as default, type NetworkId }; const defaultValidUntil = '4294967295'; diff --git a/src/mina-signer/package-lock.json b/src/mina-signer/package-lock.json index 78be1e1d0a..88342af0d7 100644 --- a/src/mina-signer/package-lock.json +++ b/src/mina-signer/package-lock.json @@ -1,12 +1,12 @@ { "name": "mina-signer", - "version": "3.0.2", + "version": "3.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mina-signer", - "version": "3.0.2", + "version": "3.0.3", "license": "Apache-2.0", "dependencies": { "blakejs": "^1.2.1", diff --git a/src/mina-signer/package.json b/src/mina-signer/package.json index d86abaf654..9c3523cf3a 100644 --- a/src/mina-signer/package.json +++ b/src/mina-signer/package.json @@ -1,7 +1,7 @@ { "name": "mina-signer", "description": "Node API for signing transactions on various networks for Mina Protocol", - "version": "3.0.2", + "version": "3.0.3", "type": "module", "scripts": { "build": "tsc -p ../../tsconfig.mina-signer.json", From 04d60856b065064f30cfa5e274a119b048f59f0b Mon Sep 17 00:00:00 2001 From: Florian Date: Sat, 17 Feb 2024 19:57:50 +0100 Subject: [PATCH 480/524] add custom network id --- src/mina-signer/mina-signer.ts | 21 ++++++++++++------- src/mina-signer/src/sign-zkapp-command.ts | 1 - .../src/test-vectors/legacySignatures.ts | 2 +- src/mina-signer/src/types.ts | 8 ++++++- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/mina-signer/mina-signer.ts b/src/mina-signer/mina-signer.ts index fe3739404f..ed40baeb09 100644 --- a/src/mina-signer/mina-signer.ts +++ b/src/mina-signer/mina-signer.ts @@ -39,16 +39,14 @@ export { Client as default }; const defaultValidUntil = '4294967295'; class Client { - private network: NetworkId; // TODO: Rename to "networkId" for consistency with remaining codebase. + private network: NetworkId; constructor(options: { network: NetworkId }) { if (!options?.network) { throw Error('Invalid Specified Network'); } const specifiedNetwork = options.network.toLowerCase(); - if (specifiedNetwork !== 'mainnet' && specifiedNetwork !== 'testnet') { - throw Error('Invalid Specified Network'); - } + this.network = specifiedNetwork; } @@ -122,9 +120,13 @@ class Client { * @param privateKey The private key used for signing * @returns The signed field elements */ - signFields(fields: bigint[], privateKey: Json.PrivateKey): Signed { + signFields( + fields: bigint[], + privateKey: Json.PrivateKey, + network?: NetworkId + ): Signed { let privateKey_ = PrivateKey.fromBase58(privateKey); - let signature = sign({ fields }, privateKey_, 'testnet'); + let signature = sign({ fields }, privateKey_, network ?? 'testnet'); return { signature: Signature.toBase58(signature), publicKey: PublicKey.toBase58(PrivateKey.toPublicKey(privateKey_)), @@ -139,12 +141,15 @@ class Client { * @returns True if the `signedFields` contains a valid signature matching * the fields and publicKey. */ - verifyFields({ data, signature, publicKey }: Signed) { + verifyFields( + { data, signature, publicKey }: Signed, + network?: NetworkId + ) { return verify( Signature.fromBase58(signature), { fields: data }, PublicKey.fromBase58(publicKey), - 'testnet' + network ?? 'testnet' ); } diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index e5a6985f08..05cc303768 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -165,7 +165,6 @@ const zkAppBodyPrefix = (network: string) => { return prefixes.zkappBodyMainnet; case 'testnet': return prefixes.zkappBodyTestnet; - default: return 'ZkappBody' + network; } diff --git a/src/mina-signer/src/test-vectors/legacySignatures.ts b/src/mina-signer/src/test-vectors/legacySignatures.ts index d31f538f58..7090587475 100644 --- a/src/mina-signer/src/test-vectors/legacySignatures.ts +++ b/src/mina-signer/src/test-vectors/legacySignatures.ts @@ -90,7 +90,7 @@ let strings = [ * - the 3 stake delegations, * - the 3 strings. */ -let signatures = { +let signatures: { [k: string]: { field: string; scalar: string }[] } = { testnet: [ { field: diff --git a/src/mina-signer/src/types.ts b/src/mina-signer/src/types.ts index d0e2c6991a..28ed239753 100644 --- a/src/mina-signer/src/types.ts +++ b/src/mina-signer/src/types.ts @@ -9,7 +9,13 @@ export type Field = number | bigint | string; export type PublicKey = string; export type PrivateKey = string; export type Signature = SignatureJson; -export type NetworkId = 'mainnet' | 'testnet'; +export type NetworkId = 'mainnet' | 'testnet' | string; + +export const NetworkID = { + Mainnet: 'mainnet', + Testnet: 'testnet', + Other: (other: string) => other, +}; export type Keypair = { readonly privateKey: PrivateKey; From 66f0699f3f7dd8e8152d9b9abc3537bcd53a9ac4 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 00:42:10 +0100 Subject: [PATCH 481/524] temp --- src/lib/account-update.ts | 35 ++++-- src/mina-signer/src/random-transaction.ts | 2 +- src/mina-signer/src/sign-zkapp-command.ts | 15 ++- .../src/sign-zkapp-command.unit-test.ts | 102 +++++++++--------- src/mina-signer/src/signature.ts | 41 +++++-- src/mina-signer/src/signature.unit-test.ts | 4 +- src/mina-signer/src/types.ts | 2 +- src/snarky.d.ts | 3 +- 8 files changed, 134 insertions(+), 70 deletions(-) diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index f8673c7ad4..b06e1bf2fe 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -65,6 +65,7 @@ import { } from './mina/smart-contract-context.js'; import { assert } from './util/assert.js'; import { RandomId } from './provable-types/auxiliary.js'; +import { max } from 'src/bindings/crypto/bigint-helpers.js'; // external API export { @@ -104,6 +105,32 @@ const TransactionVersion = { current: () => UInt32.from(protocolVersions.txnVersion), }; +// TODO FIX ME PLS + +const createCustomBodyPrefix = (prefix: string) => { + const maxLength = 20; + const paddingChar = '*'; + let length = prefix.length; + + if (length <= maxLength) { + let diff = maxLength - length; + return prefix + paddingChar.repeat(diff); + } else { + return prefix.substring(0, maxLength); + } +}; + +const zkAppBodyPrefix = (network: string) => { + switch (network) { + case 'mainnet': + return prefixes.zkappBodyMainnet; + case 'testnet': + return prefixes.zkappBodyTestnet; + default: + return createCustomBodyPrefix(network + 'ZkappBody'); + } +}; + type ZkappProverData = { transaction: ZkappCommand; accountUpdate: AccountUpdate; @@ -1018,9 +1045,7 @@ class AccountUpdate implements Types.AccountUpdate { if (Provable.inCheckedComputation()) { let input = Types.AccountUpdate.toInput(this); return hashWithPrefix( - activeInstance.getNetworkId() === 'mainnet' - ? prefixes.zkappBodyMainnet - : prefixes.zkappBodyTestnet, + zkAppBodyPrefix(activeInstance.getNetworkId()), packToFields(input) ); } else { @@ -1399,9 +1424,7 @@ class AccountUpdate implements Types.AccountUpdate { function hashAccountUpdate(update: AccountUpdate) { return genericHash( AccountUpdate, - activeInstance.getNetworkId() === 'mainnet' - ? prefixes.zkappBodyMainnet - : prefixes.zkappBodyTestnet, + zkAppBodyPrefix(activeInstance.getNetworkId()), update ); } diff --git a/src/mina-signer/src/random-transaction.ts b/src/mina-signer/src/random-transaction.ts index 6ac2ab78ea..4709b7e433 100644 --- a/src/mina-signer/src/random-transaction.ts +++ b/src/mina-signer/src/random-transaction.ts @@ -141,6 +141,6 @@ const RandomTransaction = { zkappCommand, zkappCommandAndFeePayerKey, zkappCommandJson, - networkId: Random.oneOf('testnet', 'mainnet'), + networkId: Random.oneOf('testnet', 'mainnet', 'other'), accountUpdateWithCallDepth: accountUpdate, }; diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 05cc303768..79f57ee932 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -159,6 +159,19 @@ function accountUpdatesToCallForest( return forest; } +const createCustomBodyPrefix = (prefix: string) => { + const maxLength = 20; + const paddingChar = '*'; + let length = prefix.length; + + if (length <= maxLength) { + let diff = maxLength - length; + return prefix + paddingChar.repeat(diff); + } else { + return prefix.substring(0, maxLength); + } +}; + const zkAppBodyPrefix = (network: string) => { switch (network) { case 'mainnet': @@ -166,7 +179,7 @@ const zkAppBodyPrefix = (network: string) => { case 'testnet': return prefixes.zkappBodyTestnet; default: - return 'ZkappBody' + network; + return createCustomBodyPrefix(network + 'ZkappBody'); } }; diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index b11d9a02c9..1345bc116a 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -82,42 +82,38 @@ expect(stringify(dummyInput.packed)).toEqual( stringify(dummyInputSnarky.packed) ); -test(Random.accountUpdate, (accountUpdate) => { - const testnetMinaInstance = Network({ - networkId: 'testnet', - mina: 'http://localhost:8080/graphql', - }); - const mainnetMinaInstance = Network({ - networkId: 'mainnet', - mina: 'http://localhost:8080/graphql', - }); - - fixVerificationKey(accountUpdate); - - // example account update - let accountUpdateJson: Json.AccountUpdate = - AccountUpdate.toJSON(accountUpdate); - - // account update hash - let accountUpdateSnarky = AccountUpdateSnarky.fromJSON(accountUpdateJson); - let inputSnarky = TypesSnarky.AccountUpdate.toInput(accountUpdateSnarky); - let input = AccountUpdate.toInput(accountUpdate); - expect(toJSON(input.fields)).toEqual(toJSON(inputSnarky.fields)); - expect(toJSON(input.packed)).toEqual(toJSON(inputSnarky.packed)); - - let packed = packToFields(input); - let packedSnarky = packToFieldsSnarky(inputSnarky); - expect(toJSON(packed)).toEqual(toJSON(packedSnarky)); - - let hashTestnet = accountUpdateHash(accountUpdate, 'testnet'); - let hashMainnet = accountUpdateHash(accountUpdate, 'mainnet'); - setActiveInstance(testnetMinaInstance); - let hashSnarkyTestnet = accountUpdateSnarky.hash(); - setActiveInstance(mainnetMinaInstance); - let hashSnarkyMainnet = accountUpdateSnarky.hash(); - expect(hashTestnet).toEqual(hashSnarkyTestnet.toBigInt()); - expect(hashMainnet).toEqual(hashSnarkyMainnet.toBigInt()); -}); +test( + Random.accountUpdate, + RandomTransaction.networkId, + (accountUpdate, networkId) => { + const minaInstance = Network({ + networkId, + mina: 'http://localhost:8080/graphql', + }); + + fixVerificationKey(accountUpdate); + + // example account update + let accountUpdateJson: Json.AccountUpdate = + AccountUpdate.toJSON(accountUpdate); + + // account update hash + let accountUpdateSnarky = AccountUpdateSnarky.fromJSON(accountUpdateJson); + let inputSnarky = TypesSnarky.AccountUpdate.toInput(accountUpdateSnarky); + let input = AccountUpdate.toInput(accountUpdate); + expect(toJSON(input.fields)).toEqual(toJSON(inputSnarky.fields)); + expect(toJSON(input.packed)).toEqual(toJSON(inputSnarky.packed)); + + let packed = packToFields(input); + let packedSnarky = packToFieldsSnarky(inputSnarky); + expect(toJSON(packed)).toEqual(toJSON(packedSnarky)); + + setActiveInstance(minaInstance); + let hashSnarky = accountUpdateSnarky.hash(); + let hash = accountUpdateHash(accountUpdate, networkId); + expect(hash).toEqual(hashSnarky.toBigInt()); + } +); // private key to/from base58 test(Random.json.privateKey, (feePayerKeyBase58) => { @@ -140,19 +136,25 @@ test(memoGenerator, (memoString) => { }); // zkapp transaction - basic properties & commitment -test(RandomTransaction.zkappCommand, (zkappCommand, assert) => { - zkappCommand.accountUpdates.forEach(fixVerificationKey); - - assert(isCallDepthValid(zkappCommand)); - let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); - let ocamlCommitments = Test.hashFromJson.transactionCommitments( - JSON.stringify(zkappCommandJson), - 'testnet' - ); - let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); - let commitment = callForestHash(callForest, 'testnet'); - expect(commitment).toEqual(FieldConst.toBigint(ocamlCommitments.commitment)); -}); +test( + RandomTransaction.zkappCommand, + RandomTransaction.networkId, + (zkappCommand, networkId, assert) => { + zkappCommand.accountUpdates.forEach(fixVerificationKey); + + assert(isCallDepthValid(zkappCommand)); + let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); + let ocamlCommitments = Test.hashFromJson.transactionCommitments( + JSON.stringify(zkappCommandJson), + networkId + ); + let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); + let commitment = callForestHash(callForest, networkId); + expect(commitment).toEqual( + FieldConst.toBigint(ocamlCommitments.commitment) + ); + } +); // invalid zkapp transactions test.negative( @@ -242,7 +244,7 @@ test( let sigOCaml = Test.signature.signFieldElement( ocamlCommitments.fullCommitment, Ml.fromPrivateKey(feePayerKeySnarky), - networkId === 'mainnet' ? true : false + networkId ); expect(Signature.toBase58(sigFieldElements)).toEqual(sigOCaml); diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 14a2096ec0..58eeb59cce 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -150,10 +150,10 @@ function deriveNonce( ): Scalar { let { x, y } = publicKey; let d = Field(privateKey); - let id = networkId === 'mainnet' ? networkIdMainnet : networkIdTestnet; + let id = getNetworkId(networkId); let input = HashInput.append(message, { fields: [x, y, d], - packed: [[id, 8]], + packed: [[id, 40]], }); let packedInput = packToFields(input); let inputBits = packedInput.map(Field.toBits).flat(); @@ -189,11 +189,12 @@ function hashMessage( ): Scalar { let { x, y } = publicKey; let input = HashInput.append(message, { fields: [x, y, r] }); - let prefix = - networkId === 'mainnet' - ? prefixes.signatureMainnet - : prefixes.signatureTestnet; - return hashWithPrefix(prefix, packToFields(input)); + + let chain = ''; + if (networkId === 'mainnet') chain = prefixes.signatureMainnet; + else if (networkId === 'testnet') chain = prefixes.signatureTestnet; + else chain = 'otherSignature******'; + return hashWithPrefix(chain, packToFields(input)); } /** @@ -280,7 +281,7 @@ function deriveNonceLegacy( ): Scalar { let { x, y } = publicKey; let scalarBits = Scalar.toBits(privateKey); - let id = networkId === 'mainnet' ? networkIdMainnet : networkIdTestnet; + let id = getNetworkId(networkId); let idBits = bytesToBits([Number(id)]); let input = HashInputLegacy.append(message, { fields: [x, y], @@ -317,3 +318,27 @@ function hashMessageLegacy( : prefixes.signatureTestnet; return HashLegacy.hashWithPrefix(prefix, packToFieldsLegacy(input)); } + +const toBytePadded = (b: number) => ('000000000' + b.toString(2)).substr(-8); + +function networkIdOfString(n: string) { + let l = n.length; + let acc = ''; + for (let i = l - 1; i >= 0; i--) { + let b = n.charCodeAt(i); + let padded = toBytePadded(b); + acc = acc.concat(padded); + } + return BigInt('0b' + [...acc].reverse().join('')); +} + +function getNetworkId(networkId: string) { + switch (networkId) { + case 'mainnet': + return networkIdMainnet; + case 'testnet': + return networkIdTestnet; + default: + return networkIdOfString(networkId); + } +} diff --git a/src/mina-signer/src/signature.unit-test.ts b/src/mina-signer/src/signature.unit-test.ts index c6ae459e04..e3161cbc2b 100644 --- a/src/mina-signer/src/signature.unit-test.ts +++ b/src/mina-signer/src/signature.unit-test.ts @@ -39,8 +39,8 @@ function checkConsistentSingle( // consistent with OCaml let msgMl = FieldConst.fromBigint(msg); let keyMl = Ml.fromPrivateKey(keySnarky); - let actualTest = Test.signature.signFieldElement(msgMl, keyMl, false); - let actualMain = Test.signature.signFieldElement(msgMl, keyMl, true); + let actualTest = Test.signature.signFieldElement(msgMl, keyMl, 'testnet'); + let actualMain = Test.signature.signFieldElement(msgMl, keyMl, 'mainnet'); expect(Signature.toBase58(sigTest)).toEqual(actualTest); expect(Signature.toBase58(sigMain)).toEqual(actualMain); } diff --git a/src/mina-signer/src/types.ts b/src/mina-signer/src/types.ts index 28ed239753..6591bcaf91 100644 --- a/src/mina-signer/src/types.ts +++ b/src/mina-signer/src/types.ts @@ -9,7 +9,7 @@ export type Field = number | bigint | string; export type PublicKey = string; export type PrivateKey = string; export type Signature = SignatureJson; -export type NetworkId = 'mainnet' | 'testnet' | string; +export type NetworkId = string; export const NetworkID = { Mainnet: 'mainnet', diff --git a/src/snarky.d.ts b/src/snarky.d.ts index 51a43dcb40..8af068222e 100644 --- a/src/snarky.d.ts +++ b/src/snarky.d.ts @@ -647,7 +647,7 @@ declare const Test: { signFieldElement( messageHash: FieldConst, privateKey: ScalarConst, - isMainnet: boolean + networkId: string ): string; /** * Returns a dummy signature. @@ -663,6 +663,7 @@ declare const Test: { /** * Returns the commitment of a JSON transaction. */ + test(networkid: string): void; transactionCommitments( txJson: string, networkId: string From 857946d1e0d698a0bcae0d444976fdb409410679 Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Sun, 18 Feb 2024 12:02:16 +0200 Subject: [PATCH 482/524] Addressing review comments. --- src/mina-signer/index.cjs | 1 + src/mina-signer/mina-signer.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mina-signer/index.cjs b/src/mina-signer/index.cjs index da037851c3..22639a9abe 100644 --- a/src/mina-signer/index.cjs +++ b/src/mina-signer/index.cjs @@ -3,3 +3,4 @@ const Client = require('./mina-signer.js'); module.exports = Client.default; +module.exports.Client = Client.default; diff --git a/src/mina-signer/mina-signer.ts b/src/mina-signer/mina-signer.ts index 1c7c9f55b4..b2d226affe 100644 --- a/src/mina-signer/mina-signer.ts +++ b/src/mina-signer/mina-signer.ts @@ -34,7 +34,7 @@ import { import { sign, Signature, verify } from './src/signature.js'; import { createNullifier } from './src/nullifier.js'; -export { Client as default, type NetworkId }; +export { Client, Client as default, type NetworkId }; const defaultValidUntil = '4294967295'; From d72446838ae6b0c36a185fc3f3b2a7cb2f28c793 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 13:56:25 +0100 Subject: [PATCH 483/524] make custom network deriver for signature --- src/mina-signer/src/signature.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 58eeb59cce..9a27c6df34 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -153,7 +153,7 @@ function deriveNonce( let id = getNetworkId(networkId); let input = HashInput.append(message, { fields: [x, y, d], - packed: [[id, 40]], + packed: [id], }); let packedInput = packToFields(input); let inputBits = packedInput.map(Field.toBits).flat(); @@ -321,7 +321,7 @@ function hashMessageLegacy( const toBytePadded = (b: number) => ('000000000' + b.toString(2)).substr(-8); -function networkIdOfString(n: string) { +function networkIdOfString(n: string): [bigint, number] { let l = n.length; let acc = ''; for (let i = l - 1; i >= 0; i--) { @@ -329,15 +329,15 @@ function networkIdOfString(n: string) { let padded = toBytePadded(b); acc = acc.concat(padded); } - return BigInt('0b' + [...acc].reverse().join('')); + return [BigInt('0b' + acc), acc.length]; } -function getNetworkId(networkId: string) { +function getNetworkId(networkId: string): [bigint, number] { switch (networkId) { case 'mainnet': - return networkIdMainnet; + return [networkIdMainnet, 8]; case 'testnet': - return networkIdTestnet; + return [networkIdTestnet, 8]; default: return networkIdOfString(networkId); } From 9081fb84b25b08f32893e78fb647d9f9659a2ef0 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 14:01:26 +0100 Subject: [PATCH 484/524] fix types --- src/snarky.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/snarky.d.ts b/src/snarky.d.ts index 8af068222e..3e7daca766 100644 --- a/src/snarky.d.ts +++ b/src/snarky.d.ts @@ -663,7 +663,6 @@ declare const Test: { /** * Returns the commitment of a JSON transaction. */ - test(networkid: string): void; transactionCommitments( txJson: string, networkId: string From 44070bdeccfe2ce6e733a8af32597c45d19cb6a4 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 14:29:19 +0100 Subject: [PATCH 485/524] refac --- src/lib/account-update.ts | 1 - src/mina-signer/src/sign-zkapp-command.ts | 5 +-- src/mina-signer/src/signature.ts | 24 +++++++------ src/mina-signer/src/signature.unit-test.ts | 41 +++++++++++++--------- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index b06e1bf2fe..1a9bab0920 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -65,7 +65,6 @@ import { } from './mina/smart-contract-context.js'; import { assert } from './util/assert.js'; import { RandomId } from './provable-types/auxiliary.js'; -import { max } from 'src/bindings/crypto/bigint-helpers.js'; // external API export { diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index 79f57ee932..cc0bcfde6b 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -35,6 +35,7 @@ export { accountUpdateFromFeePayer, isCallDepthValid, CallForest, + createCustomPrefix, }; function signZkappCommand( @@ -159,7 +160,7 @@ function accountUpdatesToCallForest( return forest; } -const createCustomBodyPrefix = (prefix: string) => { +const createCustomPrefix = (prefix: string) => { const maxLength = 20; const paddingChar = '*'; let length = prefix.length; @@ -179,7 +180,7 @@ const zkAppBodyPrefix = (network: string) => { case 'testnet': return prefixes.zkappBodyTestnet; default: - return createCustomBodyPrefix(network + 'ZkappBody'); + return createCustomPrefix(network + 'ZkappBody'); } }; diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 9a27c6df34..73f7844601 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -28,6 +28,7 @@ import { base58 } from '../../lib/base58.js'; import { versionBytes } from '../../bindings/crypto/constants.js'; import { Pallas } from '../../bindings/crypto/elliptic-curve.js'; import { NetworkId } from './types.js'; +import { createCustomPrefix } from './sign-zkapp-command.js'; export { sign, @@ -189,12 +190,7 @@ function hashMessage( ): Scalar { let { x, y } = publicKey; let input = HashInput.append(message, { fields: [x, y, r] }); - - let chain = ''; - if (networkId === 'mainnet') chain = prefixes.signatureMainnet; - else if (networkId === 'testnet') chain = prefixes.signatureTestnet; - else chain = 'otherSignature******'; - return hashWithPrefix(chain, packToFields(input)); + return hashWithPrefix(signaturePrefix(networkId), packToFields(input)); } /** @@ -312,10 +308,7 @@ function hashMessageLegacy( ): Scalar { let { x, y } = publicKey; let input = HashInputLegacy.append(message, { fields: [x, y, r], bits: [] }); - let prefix = - networkId === 'mainnet' - ? prefixes.signatureMainnet - : prefixes.signatureTestnet; + let prefix = signaturePrefix(networkId); return HashLegacy.hashWithPrefix(prefix, packToFieldsLegacy(input)); } @@ -342,3 +335,14 @@ function getNetworkId(networkId: string): [bigint, number] { return networkIdOfString(networkId); } } + +const signaturePrefix = (network: string) => { + switch (network) { + case 'mainnet': + return prefixes.signatureMainnet; + case 'testnet': + return prefixes.signatureTestnet; + default: + return createCustomPrefix(network + 'Signature'); + } +}; diff --git a/src/mina-signer/src/signature.unit-test.ts b/src/mina-signer/src/signature.unit-test.ts index e3161cbc2b..50999cc89f 100644 --- a/src/mina-signer/src/signature.unit-test.ts +++ b/src/mina-signer/src/signature.unit-test.ts @@ -23,26 +23,29 @@ function checkConsistentSingle( msg: Field, key: PrivateKey, keySnarky: PrivateKeySnarky, - pk: PublicKey + pk: PublicKey, + networkId: string ) { - let sigTest = signFieldElement(msg, key, 'testnet'); - let sigMain = signFieldElement(msg, key, 'mainnet'); + let sig = signFieldElement(msg, key, networkId); + // verify - let okTestnetTestnet = verifyFieldElement(sigTest, msg, pk, 'testnet'); - let okMainnetTestnet = verifyFieldElement(sigMain, msg, pk, 'testnet'); - let okTestnetMainnet = verifyFieldElement(sigTest, msg, pk, 'mainnet'); - let okMainnetMainnet = verifyFieldElement(sigMain, msg, pk, 'mainnet'); - expect(okTestnetTestnet).toEqual(true); - expect(okMainnetTestnet).toEqual(false); - expect(okTestnetMainnet).toEqual(false); - expect(okMainnetMainnet).toEqual(true); + expect(verifyFieldElement(sig, msg, pk, networkId)).toEqual(true); + + // verify against different network + expect( + verifyFieldElement( + sig, + msg, + pk, + networkId === 'mainnet' ? 'testnet' : 'mainnet' + ) + ).toEqual(false); + // consistent with OCaml let msgMl = FieldConst.fromBigint(msg); let keyMl = Ml.fromPrivateKey(keySnarky); - let actualTest = Test.signature.signFieldElement(msgMl, keyMl, 'testnet'); - let actualMain = Test.signature.signFieldElement(msgMl, keyMl, 'mainnet'); - expect(Signature.toBase58(sigTest)).toEqual(actualTest); - expect(Signature.toBase58(sigMain)).toEqual(actualMain); + let actualTest = Test.signature.signFieldElement(msgMl, keyMl, networkId); + expect(Signature.toBase58(sig)).toEqual(actualTest); } // check that various multi-field hash inputs can be verified @@ -96,12 +99,16 @@ for (let i = 0; i < 10; i++) { // hard coded single field elements let hardcoded = [0n, 1n, 2n, p - 1n]; for (let x of hardcoded) { - checkConsistentSingle(x, key, keySnarky, publicKey); + checkConsistentSingle(x, key, keySnarky, publicKey, 'testnet'); + checkConsistentSingle(x, key, keySnarky, publicKey, 'mainnet'); + checkConsistentSingle(x, key, keySnarky, publicKey, 'other'); } // random single field elements for (let i = 0; i < 10; i++) { let x = randomFields[i]; - checkConsistentSingle(x, key, keySnarky, publicKey); + checkConsistentSingle(x, key, keySnarky, publicKey, 'testnet'); + checkConsistentSingle(x, key, keySnarky, publicKey, 'mainnet'); + checkConsistentSingle(x, key, keySnarky, publicKey, 'other'); } // hard-coded multi-element hash inputs let messages: HashInput[] = [ From f4625c1e2c303dda0005f3cb3ddd8b16a1292e0f Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 14:34:50 +0100 Subject: [PATCH 486/524] fix legacy test --- src/mina-signer/src/signature.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 73f7844601..70e04dd7aa 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -277,7 +277,7 @@ function deriveNonceLegacy( ): Scalar { let { x, y } = publicKey; let scalarBits = Scalar.toBits(privateKey); - let id = getNetworkId(networkId); + let id = getNetworkId(networkId)[0]; let idBits = bytesToBits([Number(id)]); let input = HashInputLegacy.append(message, { fields: [x, y], From eef6849db3cfba187c6e7cc42eba741f26ab0c0f Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 14:40:59 +0100 Subject: [PATCH 487/524] bindings --- src/bindings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bindings b/src/bindings index a6b6800186..1beb2fec84 160000 --- a/src/bindings +++ b/src/bindings @@ -1 +1 @@ -Subproject commit a6b6800186752b3cf5c9a29b7eb167e494784286 +Subproject commit 1beb2fec847e18225adf6dd3687c3459550fe676 From 02450c482d447967b6fb82732fc56143f6bfb1fb Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 14:54:44 +0100 Subject: [PATCH 488/524] refac derivers --- src/lib/account-update.ts | 32 ++++------------------ src/mina-signer/src/sign-zkapp-command.ts | 26 +----------------- src/mina-signer/src/signature.ts | 33 ++++++++++++++++++++--- 3 files changed, 35 insertions(+), 56 deletions(-) diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index 1a9bab0920..5f20c2f20b 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -41,7 +41,11 @@ import { protocolVersions, } from '../bindings/crypto/constants.js'; import { MlArray } from './ml/base.js'; -import { Signature, signFieldElement } from '../mina-signer/src/signature.js'; +import { + Signature, + signFieldElement, + zkAppBodyPrefix, +} from '../mina-signer/src/signature.js'; import { MlFieldConstArray } from './ml/fields.js'; import { accountUpdatesToCallForest, @@ -104,32 +108,6 @@ const TransactionVersion = { current: () => UInt32.from(protocolVersions.txnVersion), }; -// TODO FIX ME PLS - -const createCustomBodyPrefix = (prefix: string) => { - const maxLength = 20; - const paddingChar = '*'; - let length = prefix.length; - - if (length <= maxLength) { - let diff = maxLength - length; - return prefix + paddingChar.repeat(diff); - } else { - return prefix.substring(0, maxLength); - } -}; - -const zkAppBodyPrefix = (network: string) => { - switch (network) { - case 'mainnet': - return prefixes.zkappBodyMainnet; - case 'testnet': - return prefixes.zkappBodyTestnet; - default: - return createCustomBodyPrefix(network + 'ZkappBody'); - } -}; - type ZkappProverData = { transaction: ZkappCommand; accountUpdate: AccountUpdate; diff --git a/src/mina-signer/src/sign-zkapp-command.ts b/src/mina-signer/src/sign-zkapp-command.ts index cc0bcfde6b..c70f8556ff 100644 --- a/src/mina-signer/src/sign-zkapp-command.ts +++ b/src/mina-signer/src/sign-zkapp-command.ts @@ -15,6 +15,7 @@ import { Signature, signFieldElement, verifyFieldElement, + zkAppBodyPrefix, } from './signature.js'; import { mocks } from '../../bindings/crypto/constants.js'; import { NetworkId } from './types.js'; @@ -35,7 +36,6 @@ export { accountUpdateFromFeePayer, isCallDepthValid, CallForest, - createCustomPrefix, }; function signZkappCommand( @@ -160,30 +160,6 @@ function accountUpdatesToCallForest( return forest; } -const createCustomPrefix = (prefix: string) => { - const maxLength = 20; - const paddingChar = '*'; - let length = prefix.length; - - if (length <= maxLength) { - let diff = maxLength - length; - return prefix + paddingChar.repeat(diff); - } else { - return prefix.substring(0, maxLength); - } -}; - -const zkAppBodyPrefix = (network: string) => { - switch (network) { - case 'mainnet': - return prefixes.zkappBodyMainnet; - case 'testnet': - return prefixes.zkappBodyTestnet; - default: - return createCustomPrefix(network + 'ZkappBody'); - } -}; - function accountUpdateHash(update: AccountUpdate, networkId: NetworkId) { assertAuthorizationKindValid(update); let input = AccountUpdate.toInput(update); diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 70e04dd7aa..37600b0e81 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -28,7 +28,6 @@ import { base58 } from '../../lib/base58.js'; import { versionBytes } from '../../bindings/crypto/constants.js'; import { Pallas } from '../../bindings/crypto/elliptic-curve.js'; import { NetworkId } from './types.js'; -import { createCustomPrefix } from './sign-zkapp-command.js'; export { sign, @@ -40,6 +39,8 @@ export { signLegacy, verifyLegacy, deriveNonce, + signaturePrefix, + zkAppBodyPrefix, }; const networkIdMainnet = 0x01n; @@ -151,7 +152,7 @@ function deriveNonce( ): Scalar { let { x, y } = publicKey; let d = Field(privateKey); - let id = getNetworkId(networkId); + let id = getNetworkIdHashInput(networkId); let input = HashInput.append(message, { fields: [x, y, d], packed: [id], @@ -277,7 +278,7 @@ function deriveNonceLegacy( ): Scalar { let { x, y } = publicKey; let scalarBits = Scalar.toBits(privateKey); - let id = getNetworkId(networkId)[0]; + let id = getNetworkIdHashInput(networkId)[0]; let idBits = bytesToBits([Number(id)]); let input = HashInputLegacy.append(message, { fields: [x, y], @@ -325,7 +326,7 @@ function networkIdOfString(n: string): [bigint, number] { return [BigInt('0b' + acc), acc.length]; } -function getNetworkId(networkId: string): [bigint, number] { +function getNetworkIdHashInput(networkId: string): [bigint, number] { switch (networkId) { case 'mainnet': return [networkIdMainnet, 8]; @@ -336,6 +337,19 @@ function getNetworkId(networkId: string): [bigint, number] { } } +const createCustomPrefix = (prefix: string) => { + const maxLength = 20; + const paddingChar = '*'; + let length = prefix.length; + + if (length <= maxLength) { + let diff = maxLength - length; + return prefix + paddingChar.repeat(diff); + } else { + return prefix.substring(0, maxLength); + } +}; + const signaturePrefix = (network: string) => { switch (network) { case 'mainnet': @@ -346,3 +360,14 @@ const signaturePrefix = (network: string) => { return createCustomPrefix(network + 'Signature'); } }; + +const zkAppBodyPrefix = (network: string) => { + switch (network) { + case 'mainnet': + return prefixes.zkappBodyMainnet; + case 'testnet': + return prefixes.zkappBodyTestnet; + default: + return createCustomPrefix(network + 'ZkappBody'); + } +}; From 33abba1cd6febfa660bb461e3afa25417a181b3b Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 15:03:22 +0100 Subject: [PATCH 489/524] remove explicit network id declartion --- src/mina-signer/src/types.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/mina-signer/src/types.ts b/src/mina-signer/src/types.ts index 6591bcaf91..e1b7a52c32 100644 --- a/src/mina-signer/src/types.ts +++ b/src/mina-signer/src/types.ts @@ -11,12 +11,6 @@ export type PrivateKey = string; export type Signature = SignatureJson; export type NetworkId = string; -export const NetworkID = { - Mainnet: 'mainnet', - Testnet: 'testnet', - Other: (other: string) => other, -}; - export type Keypair = { readonly privateKey: PrivateKey; readonly publicKey: PublicKey; From 90bb55800ae6a67d9b9e700840e08f6431a7440f Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Sun, 18 Feb 2024 15:10:37 +0100 Subject: [PATCH 490/524] dont use deprecated function --- src/mina-signer/src/signature.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 37600b0e81..1da40bfaa4 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -313,14 +313,14 @@ function hashMessageLegacy( return HashLegacy.hashWithPrefix(prefix, packToFieldsLegacy(input)); } -const toBytePadded = (b: number) => ('000000000' + b.toString(2)).substr(-8); +const numberToBytePadded = (b: number) => b.toString(2).padStart(8, '0'); function networkIdOfString(n: string): [bigint, number] { let l = n.length; let acc = ''; for (let i = l - 1; i >= 0; i--) { let b = n.charCodeAt(i); - let padded = toBytePadded(b); + let padded = numberToBytePadded(b); acc = acc.concat(padded); } return [BigInt('0b' + acc), acc.length]; From 3cb66fb27eabdec6191b9c926813b138ab4de72b Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Mon, 19 Feb 2024 13:22:26 +0100 Subject: [PATCH 491/524] add optional network parameter to signature --- src/lib/signature.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 261aab8cc2..9c80c0a834 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -235,7 +235,11 @@ class Signature extends CircuitValue { * Signs a message using a {@link PrivateKey}. * @returns a {@link Signature} */ - static create(privKey: PrivateKey, msg: Field[]): Signature { + static create( + privKey: PrivateKey, + msg: Field[], + networkId?: string + ): Signature { const publicKey = PublicKey.fromPrivateKey(privKey).toGroup(); const d = privKey.s; const kPrime = Scalar.fromBigInt( @@ -243,7 +247,7 @@ class Signature extends CircuitValue { { fields: msg.map((f) => f.toBigInt()) }, { x: publicKey.x.toBigInt(), y: publicKey.y.toBigInt() }, BigInt(d.toJSON()), - 'testnet' + networkId ?? 'testnet' ) ); let { x: r, y: ry } = Group.generator.scale(kPrime); From 105320dbd0b8fc31c6abbf022c6778a4324b9e08 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Mon, 19 Feb 2024 13:25:53 +0100 Subject: [PATCH 492/524] changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0cba5c1aa..2a59c77398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/3b5f7c7...HEAD) +### Added + +- Support for custom network identifiers other than `mainnet` or `testnet` https://github.com/o1-labs/o1js/pull/1444 + - New optional argument `networkId?: string` in `Signature.create(privKey: PrivateKey, msg: Field[], networkId?: string)` to reflect support for custom network identifiers. + ## [0.16.1](https://github.com/o1-labs/o1js/compare/834a44002...3b5f7c7) ### Breaking changes From 90764e564af70c0dd5d0e4f2b46ab8e2f3acd0f9 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Mon, 19 Feb 2024 13:31:40 +0100 Subject: [PATCH 493/524] fix verify --- CHANGELOG.md | 2 +- src/lib/signature.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a59c77398..556cd8e80c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Support for custom network identifiers other than `mainnet` or `testnet` https://github.com/o1-labs/o1js/pull/1444 - - New optional argument `networkId?: string` in `Signature.create(privKey: PrivateKey, msg: Field[], networkId?: string)` to reflect support for custom network identifiers. + - New optional argument `networkId?: string` toin `Signature.create()` and `Signature.verify()` to reflect support for custom network identifiers. ## [0.16.1](https://github.com/o1-labs/o1js/compare/834a44002...3b5f7c7) diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 9c80c0a834..30ee60d0e1 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -4,6 +4,7 @@ import { hashWithPrefix } from './hash.js'; import { deriveNonce, Signature as SignatureBigint, + signaturePrefix, } from '../mina-signer/src/signature.js'; import { Bool as BoolBigint } from '../provable/field-bigint.js'; import { @@ -253,7 +254,7 @@ class Signature extends CircuitValue { let { x: r, y: ry } = Group.generator.scale(kPrime); const k = ry.toBits()[0].toBoolean() ? kPrime.neg() : kPrime; let h = hashWithPrefix( - prefixes.signatureTestnet, + signaturePrefix(networkId ?? 'testnet'), msg.concat([publicKey.x, publicKey.y, r]) ); // TODO: Scalar.fromBits interprets the input as a "shifted scalar" @@ -267,10 +268,10 @@ class Signature extends CircuitValue { * Verifies the {@link Signature} using a message and the corresponding {@link PublicKey}. * @returns a {@link Bool} */ - verify(publicKey: PublicKey, msg: Field[]): Bool { + verify(publicKey: PublicKey, msg: Field[], networkId?: string): Bool { const point = publicKey.toGroup(); let h = hashWithPrefix( - prefixes.signatureTestnet, + signaturePrefix(networkId ?? 'testnet'), msg.concat([point.x, point.y, this.r]) ); // TODO: Scalar.fromBits interprets the input as a "shifted scalar" From 2c3a6f2e3c5036ed374e96b1e628c5401d8e8795 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Mon, 19 Feb 2024 13:31:54 +0100 Subject: [PATCH 494/524] typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 556cd8e80c..e290278623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Support for custom network identifiers other than `mainnet` or `testnet` https://github.com/o1-labs/o1js/pull/1444 - - New optional argument `networkId?: string` toin `Signature.create()` and `Signature.verify()` to reflect support for custom network identifiers. + - New optional argument `networkId?: string` to `Signature.create()` and `Signature.verify()` to reflect support for custom network identifiers. ## [0.16.1](https://github.com/o1-labs/o1js/compare/834a44002...3b5f7c7) From 80a84cc8865330f3495abcb0dbfaad8f63565a1b Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 16:57:36 +0100 Subject: [PATCH 495/524] move token() off of account update, deprecate on smart contract --- src/lib/account-update.ts | 94 ------------------------- src/lib/mina/token/token-contract.ts | 8 +++ src/lib/mina/token/token-methods.ts | 100 +++++++++++++++++++++++++++ src/lib/zkapp.ts | 7 +- 4 files changed, 113 insertions(+), 96 deletions(-) create mode 100644 src/lib/mina/token/token-methods.ts diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index f8673c7ad4..1447d24253 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -642,100 +642,6 @@ class AccountUpdate implements Types.AccountUpdate { return cloned; } - token() { - let thisAccountUpdate = this; - let tokenOwner = this.publicKey; - let parentTokenId = this.tokenId; - let id = TokenId.derive(tokenOwner, parentTokenId); - - function getApprovedAccountUpdate( - accountLike: PublicKey | AccountUpdate | SmartContract, - label: string - ) { - if (isSmartContract(accountLike)) { - accountLike = accountLike.self; - } - if (accountLike instanceof AccountUpdate) { - accountLike.tokenId.assertEquals(id); - thisAccountUpdate.approve(accountLike); - } - if (accountLike instanceof PublicKey) { - accountLike = AccountUpdate.defaultAccountUpdate(accountLike, id); - thisAccountUpdate.approve(accountLike); - } - if (!accountLike.label) - accountLike.label = `${ - thisAccountUpdate.label ?? 'Unlabeled' - }.${label}`; - return accountLike; - } - - return { - id, - parentTokenId, - tokenOwner, - - /** - * Mints token balance to `address`. Returns the mint account update. - */ - mint({ - address, - amount, - }: { - address: PublicKey | AccountUpdate | SmartContract; - amount: number | bigint | UInt64; - }) { - let receiver = getApprovedAccountUpdate(address, 'token.mint()'); - receiver.balance.addInPlace(amount); - return receiver; - }, - - /** - * Burn token balance on `address`. Returns the burn account update. - */ - burn({ - address, - amount, - }: { - address: PublicKey | AccountUpdate | SmartContract; - amount: number | bigint | UInt64; - }) { - let sender = getApprovedAccountUpdate(address, 'token.burn()'); - - // Sub the amount to burn from the sender's account - sender.balance.subInPlace(amount); - - // Require signature from the sender account being deducted - sender.body.useFullCommitment = Bool(true); - Authorization.setLazySignature(sender); - return sender; - }, - - /** - * Move token balance from `from` to `to`. Returns the `to` account update. - */ - send({ - from, - to, - amount, - }: { - from: PublicKey | AccountUpdate | SmartContract; - to: PublicKey | AccountUpdate | SmartContract; - amount: number | bigint | UInt64; - }) { - let sender = getApprovedAccountUpdate(from, 'token.send() (sender)'); - sender.balance.subInPlace(amount); - sender.body.useFullCommitment = Bool(true); - Authorization.setLazySignature(sender); - - let receiver = getApprovedAccountUpdate(to, 'token.send() (receiver)'); - receiver.balance.addInPlace(amount); - - return receiver; - }, - }; - } - get tokenId() { return this.body.tokenId; } diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 58a0984a0e..1c829e963f 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -10,6 +10,7 @@ import { } from '../../account-update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; import { TokenAccountUpdateIterator } from './forest-iterator.js'; +import { tokenMethods } from './token-methods.js'; export { TokenContract }; @@ -33,6 +34,13 @@ abstract class TokenContract extends SmartContract { }); } + /** + * Helper methods to use on a token contract. + */ + get token() { + return tokenMethods(this.self); + } + // APPROVABLE API has to be specified by subclasses, // but the hard part is `forEachUpdate()` diff --git a/src/lib/mina/token/token-methods.ts b/src/lib/mina/token/token-methods.ts new file mode 100644 index 0000000000..bfff2f0141 --- /dev/null +++ b/src/lib/mina/token/token-methods.ts @@ -0,0 +1,100 @@ +import { AccountUpdate, Authorization, TokenId } from '../../account-update.js'; +import { isSmartContract } from '../smart-contract-base.js'; +import { PublicKey } from '../../signature.js'; +import type { SmartContract } from '../../zkapp.js'; +import { UInt64 } from '../../int.js'; +import { Bool, Field } from '../../core.js'; + +export { tokenMethods }; + +function tokenMethods(self: AccountUpdate) { + let tokenOwner = self.publicKey; + let parentTokenId = self.tokenId; + let id = TokenId.derive(tokenOwner, parentTokenId); + + return { + id, + parentTokenId, + tokenOwner, + + /** + * Mints token balance to `address`. Returns the mint account update. + */ + mint({ + address, + amount, + }: { + address: PublicKey | AccountUpdate | SmartContract; + amount: number | bigint | UInt64; + }) { + let receiver = getApprovedUpdate(self, id, address, 'token.mint()'); + receiver.balance.addInPlace(amount); + return receiver; + }, + + /** + * Burn token balance on `address`. Returns the burn account update. + */ + burn({ + address, + amount, + }: { + address: PublicKey | AccountUpdate | SmartContract; + amount: number | bigint | UInt64; + }) { + let sender = getApprovedUpdate(self, id, address, 'token.burn()'); + + // Sub the amount to burn from the sender's account + sender.balance.subInPlace(amount); + + // Require signature from the sender account being deducted + sender.body.useFullCommitment = Bool(true); + Authorization.setLazySignature(sender); + return sender; + }, + + /** + * Move token balance from `from` to `to`. Returns the `to` account update. + */ + send({ + from, + to, + amount, + }: { + from: PublicKey | AccountUpdate | SmartContract; + to: PublicKey | AccountUpdate | SmartContract; + amount: number | bigint | UInt64; + }) { + let sender = getApprovedUpdate(self, id, from, 'token.send() (sender)'); + sender.balance.subInPlace(amount); + sender.body.useFullCommitment = Bool(true); + Authorization.setLazySignature(sender); + + let receiver = getApprovedUpdate(self, id, to, 'token.send() (receiver)'); + receiver.balance.addInPlace(amount); + + return receiver; + }, + }; +} + +function getApprovedUpdate( + self: AccountUpdate, + tokenId: Field, + child: PublicKey | AccountUpdate | SmartContract, + label: string +) { + if (isSmartContract(child)) { + child = child.self; + } + if (child instanceof AccountUpdate) { + child.tokenId.assertEquals(tokenId); + self.approve(child); + } + if (child instanceof PublicKey) { + child = AccountUpdate.defaultAccountUpdate(child, tokenId); + self.approve(child); + } + if (!child.label) child.label = `${self.label ?? 'Unlabeled'}.${label}`; + return child; +} diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index d927c490ae..309dc0a421 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -64,6 +64,7 @@ import { accountUpdateLayout, smartContractContext, } from './mina/smart-contract-context.js'; +import { tokenMethods } from './mina/token/token-methods.js'; // external API export { SmartContract, method, DeployArgs, declareMethods, Account, Reducer }; @@ -846,10 +847,12 @@ super.init(); return this.self.currentSlot; } /** - * Token of the {@link SmartContract}. + * @deprecated `SmartContract.token` will be removed, and token methods will only be available on `TokenContract`. + * + * For security reasons, it is recommended to use `TokenContract` as the base contract for tokens. */ get token() { - return this.self.token(); + return tokenMethods(this.self); } /** From d9751011374afcf6168e1991823d21b21dad7a40 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:18:30 +0100 Subject: [PATCH 496/524] additional refactor, rename to TokenContract.internal --- src/lib/mina/token/token-contract.ts | 19 +++++++++--- .../mina/token/token-contract.unit-test.ts | 2 +- src/lib/mina/token/token-methods.ts | 30 +++++++++++++------ src/lib/zkapp.ts | 6 ++-- 4 files changed, 40 insertions(+), 17 deletions(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 1c829e963f..e101efce1d 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -7,6 +7,7 @@ import { AccountUpdateForest, AccountUpdateTree, Permissions, + TokenId, } from '../../account-update.js'; import { DeployArgs, SmartContract } from '../../zkapp.js'; import { TokenAccountUpdateIterator } from './forest-iterator.js'; @@ -35,9 +36,16 @@ abstract class TokenContract extends SmartContract { } /** - * Helper methods to use on a token contract. + * The token ID of the token managed by this contract. */ - get token() { + deriveTokenId() { + return TokenId.derive(this.address, this.tokenId); + } + + /** + * Helper methods to use from within a token contract. + */ + get internal() { return tokenMethods(this.self); } @@ -55,7 +63,10 @@ abstract class TokenContract extends SmartContract { updates: AccountUpdateForest, callback: (update: AccountUpdate, usesToken: Bool) => void ) { - let iterator = TokenAccountUpdateIterator.create(updates, this.token.id); + let iterator = TokenAccountUpdateIterator.create( + updates, + this.deriveTokenId() + ); // iterate through the forest and apply user-defined logc for (let i = 0; i < MAX_ACCOUNT_UPDATES; i++) { @@ -119,7 +130,7 @@ abstract class TokenContract extends SmartContract { amount: UInt64 | number | bigint ) { // coerce the inputs to AccountUpdate and pass to `approveUpdates()` - let tokenId = this.token.id; + let tokenId = this.deriveTokenId(); if (from instanceof PublicKey) { from = AccountUpdate.defaultAccountUpdate(from, tokenId); from.requireSignature(); diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts index bcbe2bc882..a62ebf160c 100644 --- a/src/lib/mina/token/token-contract.unit-test.ts +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -25,7 +25,7 @@ class ExampleTokenContract extends TokenContract { super.init(); // mint the entire supply to the token account with the same address as this contract - this.token.mint({ address: this.address, amount: this.SUPPLY }); + this.internal.mint({ address: this.address, amount: this.SUPPLY }); // pay fees for opened account this.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); diff --git a/src/lib/mina/token/token-methods.ts b/src/lib/mina/token/token-methods.ts index bfff2f0141..701bf36d36 100644 --- a/src/lib/mina/token/token-methods.ts +++ b/src/lib/mina/token/token-methods.ts @@ -5,18 +5,10 @@ import type { SmartContract } from '../../zkapp.js'; import { UInt64 } from '../../int.js'; import { Bool, Field } from '../../core.js'; -export { tokenMethods }; +export { tokenMethods, deprecatedToken }; function tokenMethods(self: AccountUpdate) { - let tokenOwner = self.publicKey; - let parentTokenId = self.tokenId; - let id = TokenId.derive(tokenOwner, parentTokenId); - return { - id, - parentTokenId, - tokenOwner, - /** * Mints token balance to `address`. Returns the mint account update. */ @@ -27,6 +19,7 @@ function tokenMethods(self: AccountUpdate) { address: PublicKey | AccountUpdate | SmartContract; amount: number | bigint | UInt64; }) { + let id = TokenId.derive(self.publicKey, self.tokenId); let receiver = getApprovedUpdate(self, id, address, 'token.mint()'); receiver.balance.addInPlace(amount); return receiver; @@ -42,6 +35,7 @@ function tokenMethods(self: AccountUpdate) { address: PublicKey | AccountUpdate | SmartContract; amount: number | bigint | UInt64; }) { + let id = TokenId.derive(self.publicKey, self.tokenId); let sender = getApprovedUpdate(self, id, address, 'token.burn()'); // Sub the amount to burn from the sender's account @@ -65,6 +59,7 @@ function tokenMethods(self: AccountUpdate) { to: PublicKey | AccountUpdate | SmartContract; amount: number | bigint | UInt64; }) { + let id = TokenId.derive(self.publicKey, self.tokenId); let sender = getApprovedUpdate(self, id, from, 'token.send() (sender)'); sender.balance.subInPlace(amount); sender.body.useFullCommitment = Bool(true); @@ -78,6 +73,8 @@ function tokenMethods(self: AccountUpdate) { }; } +// helper + function getApprovedUpdate( self: AccountUpdate, tokenId: Field, @@ -98,3 +95,18 @@ function getApprovedUpdate( if (!child.label) child.label = `${self.label ?? 'Unlabeled'}.${label}`; return child; } + +// deprecated token interface for `SmartContract` + +function deprecatedToken(self: AccountUpdate) { + let tokenOwner = self.publicKey; + let parentTokenId = self.tokenId; + let id = TokenId.derive(tokenOwner, parentTokenId); + + return { + id, + parentTokenId, + tokenOwner, + ...tokenMethods(self), + }; +} diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 309dc0a421..aff97c04c3 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -64,7 +64,7 @@ import { accountUpdateLayout, smartContractContext, } from './mina/smart-contract-context.js'; -import { tokenMethods } from './mina/token/token-methods.js'; +import { deprecatedToken } from './mina/token/token-methods.js'; // external API export { SmartContract, method, DeployArgs, declareMethods, Account, Reducer }; @@ -847,12 +847,12 @@ super.init(); return this.self.currentSlot; } /** - * @deprecated `SmartContract.token` will be removed, and token methods will only be available on `TokenContract`. + * @deprecated `SmartContract.token` will be removed, and token methods will only be available on `TokenContract.internal`. * * For security reasons, it is recommended to use `TokenContract` as the base contract for tokens. */ get token() { - return tokenMethods(this.self); + return deprecatedToken(this.self); } /** From b7210573052b46b9552d0a65cb2e711bdbd6e3b5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:22:51 +0100 Subject: [PATCH 497/524] fix tests where immediately possible --- src/examples/zkapps/dex/dex.ts | 4 ++-- src/examples/zkapps/dex/erc20.ts | 4 ++-- src/examples/zkapps/token-with-proofs.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index 5799e6f80a..b909030af1 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -377,7 +377,7 @@ class TokenContract extends BaseTokenContract { * * we mint the max uint64 of tokens here, so that we can overflow it in tests if we just mint a bit more */ - let receiver = this.token.mint({ + let receiver = this.internal.mint({ address: this.address, amount: UInt64.MAXINT(), }); @@ -393,7 +393,7 @@ class TokenContract extends BaseTokenContract { * mint additional tokens to some user, so we can overflow token balances */ @method init2() { - let receiver = this.token.mint({ + let receiver = this.internal.mint({ address: addresses.user, amount: UInt64.from(10n ** 6n), }); diff --git a/src/examples/zkapps/dex/erc20.ts b/src/examples/zkapps/dex/erc20.ts index a9cc97781b..bbc7585fc0 100644 --- a/src/examples/zkapps/dex/erc20.ts +++ b/src/examples/zkapps/dex/erc20.ts @@ -92,7 +92,7 @@ class TrivialCoin extends TokenContract implements Erc20Like { // mint the entire supply to the token account with the same address as this contract let address = this.self.body.publicKey; - let receiver = this.token.mint({ address, amount: this.SUPPLY }); + let receiver = this.internal.mint({ address, amount: this.SUPPLY }); // assert that the receiving account is new, so this can be only done once receiver.account.isNew.requireEquals(Bool(true)); @@ -120,7 +120,7 @@ class TrivialCoin extends TokenContract implements Erc20Like { balanceOf(owner: PublicKey | AccountUpdate): UInt64 { let update = owner instanceof PublicKey - ? AccountUpdate.create(owner, this.token.id) + ? AccountUpdate.create(owner, this.deriveTokenId()) : owner; this.approveAccountUpdate(update); return update.account.balance.getAndRequireEquals(); diff --git a/src/examples/zkapps/token-with-proofs.ts b/src/examples/zkapps/token-with-proofs.ts index 33e7b3fd96..4911b0e519 100644 --- a/src/examples/zkapps/token-with-proofs.ts +++ b/src/examples/zkapps/token-with-proofs.ts @@ -18,12 +18,12 @@ class Token extends TokenContract { @method mint(receiverAddress: PublicKey) { let amount = 1_000_000; - this.token.mint({ address: receiverAddress, amount }); + this.internal.mint({ address: receiverAddress, amount }); } @method burn(receiverAddress: PublicKey) { let amount = 1_000; - this.token.burn({ address: receiverAddress, amount }); + this.internal.burn({ address: receiverAddress, amount }); } } From 56bd4c409539091500d13bcf400461870bb799f1 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:22:57 +0100 Subject: [PATCH 498/524] tweak comment --- src/lib/mina/token/token-contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index e101efce1d..2e39e2e118 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -36,7 +36,7 @@ abstract class TokenContract extends SmartContract { } /** - * The token ID of the token managed by this contract. + * Returns the `tokenId` of the token managed by this contract. */ deriveTokenId() { return TokenId.derive(this.address, this.tokenId); From 8f7546ce39eb5118df0aa7a25e2b1a09da8f85a3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:31:44 +0100 Subject: [PATCH 499/524] refactor dex with actions to be a token contract --- src/examples/zkapps/dex/dex-with-actions.ts | 42 +++++++++++---------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/examples/zkapps/dex/dex-with-actions.ts b/src/examples/zkapps/dex/dex-with-actions.ts index add16ae08b..6b8a7b3014 100644 --- a/src/examples/zkapps/dex/dex-with-actions.ts +++ b/src/examples/zkapps/dex/dex-with-actions.ts @@ -6,6 +6,7 @@ import { Account, AccountUpdate, + AccountUpdateForest, Field, InferProvable, Mina, @@ -16,6 +17,7 @@ import { SmartContract, State, Struct, + TokenContract, TokenId, UInt64, method, @@ -23,17 +25,23 @@ import { } from 'o1js'; import { randomAccounts } from './dex.js'; -import { TrivialCoin as TokenContract } from './erc20.js'; +import { TrivialCoin } from './erc20.js'; export { Dex, DexTokenHolder, addresses, getTokenBalances, keys, tokenIds }; class RedeemAction extends Struct({ address: PublicKey, dl: UInt64 }) {} -class Dex extends SmartContract { +class Dex extends TokenContract { // addresses of token contracts are constants tokenX = addresses.tokenX; tokenY = addresses.tokenY; + // Approvable API + + @method approveBase(forest: AccountUpdateForest) { + this.checkZeroBalanceChange(forest); + } + /** * state that keeps track of total lqXY supply -- this is needed to calculate what to return when redeeming liquidity * @@ -71,7 +79,7 @@ class Dex extends SmartContract { } @method createAccount() { - this.token.mint({ address: this.sender, amount: UInt64.from(0) }); + this.internal.mint({ address: this.sender, amount: UInt64.from(0) }); } /** @@ -86,14 +94,14 @@ class Dex extends SmartContract { */ @method supplyLiquidityBase(dx: UInt64, dy: UInt64): UInt64 { let user = this.sender; - let tokenX = new TokenContract(this.tokenX); - let tokenY = new TokenContract(this.tokenY); + let tokenX = new TrivialCoin(this.tokenX); + let tokenY = new TrivialCoin(this.tokenY); // get balances of X and Y token - let dexX = AccountUpdate.create(this.address, tokenX.token.id); + let dexX = AccountUpdate.create(this.address, tokenX.deriveTokenId()); let x = dexX.account.balance.getAndRequireEquals(); - let dexY = AccountUpdate.create(this.address, tokenY.token.id); + let dexY = AccountUpdate.create(this.address, tokenY.deriveTokenId()); let y = dexY.account.balance.getAndRequireEquals(); // // assert dy === [dx * y/x], or x === 0 @@ -108,7 +116,7 @@ class Dex extends SmartContract { // calculate liquidity token output simply as dl = dx + dx // => maintains ratio x/l, y/l let dl = dy.add(dx); - this.token.mint({ address: user, amount: dl }); + this.internal.mint({ address: user, amount: dl }); // update l supply let l = this.totalSupply.get(); @@ -157,7 +165,7 @@ class Dex extends SmartContract { */ @method redeemInitialize(dl: UInt64) { this.reducer.dispatch(new RedeemAction({ address: this.sender, dl })); - this.token.burn({ address: this.sender, amount: dl }); + this.internal.burn({ address: this.sender, amount: dl }); // TODO: preconditioning on the state here ruins concurrent interactions, // there should be another `finalize` DEX method which reduces actions & updates state this.totalSupply.set(this.totalSupply.getAndRequireEquals().sub(dl)); @@ -186,8 +194,8 @@ class Dex extends SmartContract { * the called methods which requires proof authorization. */ swapX(dx: UInt64): UInt64 { - let tokenY = new TokenContract(this.tokenY); - let dexY = new DexTokenHolder(this.address, tokenY.token.id); + let tokenY = new TrivialCoin(this.tokenY); + let dexY = new DexTokenHolder(this.address, tokenY.deriveTokenId()); let dy = dexY.swap(this.sender, dx, this.tokenX); tokenY.transfer(dexY.self, this.sender, dy); return dy; @@ -204,16 +212,12 @@ class Dex extends SmartContract { * the called methods which requires proof authorization. */ swapY(dy: UInt64): UInt64 { - let tokenX = new TokenContract(this.tokenX); - let dexX = new DexTokenHolder(this.address, tokenX.token.id); + let tokenX = new TrivialCoin(this.tokenX); + let dexX = new DexTokenHolder(this.address, tokenX.deriveTokenId()); let dx = dexX.swap(this.sender, dy, this.tokenY); tokenX.transfer(dexX.self, this.sender, dx); return dx; } - - @method transfer(from: PublicKey, to: PublicKey, amount: UInt64) { - this.token.send({ from, to, amount }); - } } class DexTokenHolder extends SmartContract { @@ -292,10 +296,10 @@ class DexTokenHolder extends SmartContract { ): UInt64 { // we're writing this as if our token === y and other token === x let dx = otherTokenAmount; - let tokenX = new TokenContract(otherTokenAddress); + let tokenX = new TrivialCoin(otherTokenAddress); // get balances of X and Y token - let dexX = AccountUpdate.create(this.address, tokenX.token.id); + let dexX = AccountUpdate.create(this.address, tokenX.deriveTokenId()); let x = dexX.account.balance.getAndRequireEquals(); let y = this.account.balance.getAndRequireEquals(); From 7b24cc09d06ac709525c361e85143c83d46840fa Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:44:05 +0100 Subject: [PATCH 500/524] refactor dex to token contract --- .../zkapps/dex/arbitrary-token-interaction.ts | 2 +- src/examples/zkapps/dex/dex.ts | 46 ++++++++++++------- src/examples/zkapps/dex/upgradability.ts | 6 ++- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/examples/zkapps/dex/arbitrary-token-interaction.ts b/src/examples/zkapps/dex/arbitrary-token-interaction.ts index 182beb95fa..27843a1a23 100644 --- a/src/examples/zkapps/dex/arbitrary-token-interaction.ts +++ b/src/examples/zkapps/dex/arbitrary-token-interaction.ts @@ -42,7 +42,7 @@ tx = await Mina.transaction(userAddress, () => { ); // 😈😈😈 mint any number of tokens to our account 😈😈😈 let tokenContract = new TokenContract(addresses.tokenX); - tokenContract.token.mint({ + tokenContract.internal.mint({ address: userAddress, amount: UInt64.from(1e18), }); diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index b909030af1..eec19763d7 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -25,11 +25,18 @@ class UInt64x2 extends Struct([UInt64, UInt64]) {} function createDex({ lockedLiquiditySlots, }: { lockedLiquiditySlots?: number } = {}) { - class Dex extends SmartContract { + class Dex extends BaseTokenContract { // addresses of token contracts are constants tokenX = addresses.tokenX; tokenY = addresses.tokenY; + // Approvable API + + @method + approveBase(forest: AccountUpdateForest) { + this.checkZeroBalanceChange(forest); + } + /** * state which keeps track of total lqXY supply -- this is needed to calculate what to return when redeeming liquidity * @@ -53,10 +60,16 @@ function createDex({ let tokenY = new TokenContract(this.tokenY); // get balances of X and Y token - let dexXUpdate = AccountUpdate.create(this.address, tokenX.token.id); + let dexXUpdate = AccountUpdate.create( + this.address, + tokenX.deriveTokenId() + ); let dexXBalance = dexXUpdate.account.balance.getAndRequireEquals(); - let dexYUpdate = AccountUpdate.create(this.address, tokenY.token.id); + let dexYUpdate = AccountUpdate.create( + this.address, + tokenY.deriveTokenId() + ); let dexYBalance = dexYUpdate.account.balance.getAndRequireEquals(); // assert dy === [dx * y/x], or x === 0 @@ -71,7 +84,7 @@ function createDex({ // calculate liquidity token output simply as dl = dx + dy // => maintains ratio x/l, y/l let dl = dy.add(dx); - let userUpdate = this.token.mint({ address: user, amount: dl }); + let userUpdate = this.internal.mint({ address: user, amount: dl }); if (lockedLiquiditySlots !== undefined) { /** * exercise the "timing" (vesting) feature to lock the received liquidity tokens. @@ -114,7 +127,7 @@ function createDex({ // calculate dy outside circuit let x = Account(this.address, TokenId.derive(this.tokenX)).balance.get(); let y = Account(this.address, TokenId.derive(this.tokenY)).balance.get(); - if (x.value.isConstant() && x.value.isZero().toBoolean()) { + if (x.value.isConstant() && x.value.equals(0).toBoolean()) { throw Error( 'Cannot call `supplyLiquidity` when reserves are zero. Use `supplyLiquidityBase`.' ); @@ -136,7 +149,7 @@ function createDex({ redeemLiquidity(dl: UInt64) { // call the token X holder inside a token X-approved callback let tokenX = new TokenContract(this.tokenX); - let dexX = new DexTokenHolder(this.address, tokenX.token.id); + let dexX = new DexTokenHolder(this.address, tokenX.deriveTokenId()); let dxdy = dexX.redeemLiquidity(this.sender, dl, this.tokenY); let dx = dxdy[0]; tokenX.transfer(dexX.self, this.sender, dx); @@ -152,7 +165,7 @@ function createDex({ */ @method swapX(dx: UInt64): UInt64 { let tokenY = new TokenContract(this.tokenY); - let dexY = new DexTokenHolder(this.address, tokenY.token.id); + let dexY = new DexTokenHolder(this.address, tokenY.deriveTokenId()); let dy = dexY.swap(this.sender, dx, this.tokenX); tokenY.transfer(dexY.self, this.sender, dy); return dy; @@ -167,7 +180,7 @@ function createDex({ */ @method swapY(dy: UInt64): UInt64 { let tokenX = new TokenContract(this.tokenX); - let dexX = new DexTokenHolder(this.address, tokenX.token.id); + let dexX = new DexTokenHolder(this.address, tokenX.deriveTokenId()); let dx = dexX.swap(this.sender, dy, this.tokenY); tokenX.transfer(dexX.self, this.sender, dx); return dx; @@ -186,22 +199,21 @@ function createDex({ */ @method burnLiquidity(user: PublicKey, dl: UInt64): UInt64 { // this makes sure there is enough l to burn (user balance stays >= 0), so l stays >= 0, so l was >0 before - this.token.burn({ address: user, amount: dl }); + this.internal.burn({ address: user, amount: dl }); let l = this.totalSupply.get(); this.totalSupply.requireEquals(l); this.totalSupply.set(l.sub(dl)); return l; } - - @method transfer(from: PublicKey, to: PublicKey, amount: UInt64) { - this.token.send({ from, to, amount }); - } } class ModifiedDex extends Dex { @method swapX(dx: UInt64): UInt64 { let tokenY = new TokenContract(this.tokenY); - let dexY = new ModifiedDexTokenHolder(this.address, tokenY.token.id); + let dexY = new ModifiedDexTokenHolder( + this.address, + tokenY.deriveTokenId() + ); let dy = dexY.swap(this.sender, dx, this.tokenX); tokenY.transfer(dexY.self, this.sender, dy); return dy; @@ -241,7 +253,7 @@ function createDex({ ): UInt64x2 { // first call the Y token holder, approved by the Y token contract; this makes sure we get dl, the user's lqXY let tokenY = new TokenContract(otherTokenAddress); - let dexY = new DexTokenHolder(this.address, tokenY.token.id); + let dexY = new DexTokenHolder(this.address, tokenY.deriveTokenId()); let result = dexY.redeemLiquidityPartial(user, dl); let l = result[0]; let dy = result[1]; @@ -267,7 +279,7 @@ function createDex({ let dx = otherTokenAmount; let tokenX = new TokenContract(otherTokenAddress); // get balances - let dexX = AccountUpdate.create(this.address, tokenX.token.id); + let dexX = AccountUpdate.create(this.address, tokenX.deriveTokenId()); let x = dexX.account.balance.getAndRequireEquals(); let y = this.account.balance.getAndRequireEquals(); // send x from user to us (i.e., to the same address as this but with the other token) @@ -292,7 +304,7 @@ function createDex({ let dx = otherTokenAmount; let tokenX = new TokenContract(otherTokenAddress); // get balances - let dexX = AccountUpdate.create(this.address, tokenX.token.id); + let dexX = AccountUpdate.create(this.address, tokenX.deriveTokenId()); let x = dexX.account.balance.getAndRequireEquals(); let y = this.account.balance.get(); this.account.balance.requireEquals(y); diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 6511e64aa3..0c9fba9b9b 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -378,11 +378,13 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { // Making sure that both token holder accounts have been updated with the new modified verification key expect( - Mina.getAccount(addresses.dex, tokenX.token.id).zkapp?.verificationKey?.data + Mina.getAccount(addresses.dex, tokenX.deriveTokenId()).zkapp + ?.verificationKey?.data ).toEqual(ModifiedDexTokenHolder._verificationKey?.data); expect( - Mina.getAccount(addresses.dex, tokenY.token.id).zkapp?.verificationKey?.data + Mina.getAccount(addresses.dex, tokenY.deriveTokenId()).zkapp + ?.verificationKey?.data ).toEqual(ModifiedDexTokenHolder._verificationKey?.data); // this is important; we have to re-enable proof production (and verification) to make sure the proofs are valid against the newly deployed VK From da7786e04ebc7fe854aeeea0e58f38d346c9b9c6 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:44:12 +0100 Subject: [PATCH 501/524] tweak comment --- src/lib/zkapp.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index aff97c04c3..0f39725950 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -848,8 +848,9 @@ super.init(); } /** * @deprecated `SmartContract.token` will be removed, and token methods will only be available on `TokenContract.internal`. + * Instead of `SmartContract.token.id`, use `TokenContract.deriveTokenId()`. * - * For security reasons, it is recommended to use `TokenContract` as the base contract for tokens. + * For security reasons, it is recommended to use `TokenContract` as the base contract for all tokens. */ get token() { return deprecatedToken(this.self); From f64e6ccf13dc4b3e4b96265ac90c6efbb5480afa Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:44:30 +0100 Subject: [PATCH 502/524] adapt example --- src/examples/zkapps/token-with-proofs.ts | 2 +- src/lib/token.test.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/examples/zkapps/token-with-proofs.ts b/src/examples/zkapps/token-with-proofs.ts index 4911b0e519..6ce61bc301 100644 --- a/src/examples/zkapps/token-with-proofs.ts +++ b/src/examples/zkapps/token-with-proofs.ts @@ -58,7 +58,7 @@ let zkAppBKey = PrivateKey.random(); let zkAppBAddress = zkAppBKey.toPublicKey(); let tokenZkApp = new Token(tokenZkAppAddress); -let tokenId = tokenZkApp.token.id; +let tokenId = tokenZkApp.deriveTokenId(); let zkAppB = new ZkAppB(zkAppBAddress, tokenId); let zkAppC = new ZkAppC(zkAppCAddress, tokenId); diff --git a/src/lib/token.test.ts b/src/lib/token.test.ts index e6c2ee1f5b..df138aefc1 100644 --- a/src/lib/token.test.ts +++ b/src/lib/token.test.ts @@ -18,6 +18,8 @@ import { const tokenSymbol = 'TOKEN'; +// TODO: Refactor to `TokenContract` + class TokenContract extends SmartContract { SUPPLY = UInt64.from(10n ** 18n); @state(UInt64) totalAmountInCirculation = State(); From cafbb52fc9faf20e289fed9706d6cfeb876f9ea9 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:52:36 +0100 Subject: [PATCH 503/524] isNew precondition --- src/lib/mina/token/token-contract.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 2e39e2e118..673524d1ef 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -29,10 +29,17 @@ abstract class TokenContract extends SmartContract { deploy(args?: DeployArgs) { super.deploy(args); + + // set access permission, to prevent unauthorized token operations this.account.permissions.set({ ...Permissions.default(), access: Permissions.proofOrSignature(), }); + + // assert that this account is new, to ensure unauthorized token operations + // are not possible before this contract is deployed + // see https://github.com/o1-labs/o1js/issues/1439 for details + this.account.isNew.requireEquals(Bool(true)); } /** From e5b28453796095ed9b2459c578e79ce8ee411966 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 19 Feb 2024 17:53:28 +0100 Subject: [PATCH 504/524] changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0cba5c1aa..6f34205241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,16 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/3b5f7c7...HEAD) +### Deprecated + +- `SmartContract.token` is deprecated in favor of new methods on `TokenContract` https://github.com/o1-labs/o1js/pull/1446 + - `TokenContract.deriveTokenId()` to get the ID of the managed token + - `TokenContract.internal.{send, mint, burn}` to perform token operations from within the contract + +### Fixed + +- Mitigate security hazard of deploying token contracts https://github.com/o1-labs/o1js/issues/1439 + ## [0.16.1](https://github.com/o1-labs/o1js/compare/834a44002...3b5f7c7) ### Breaking changes From cd43ef7994cfa8f6ab7cf60c9fe861b7080db370 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 20 Feb 2024 09:02:44 +0100 Subject: [PATCH 505/524] revert sig type --- CHANGELOG.md | 1 - src/lib/signature.ts | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e290278623..195463582c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Support for custom network identifiers other than `mainnet` or `testnet` https://github.com/o1-labs/o1js/pull/1444 - - New optional argument `networkId?: string` to `Signature.create()` and `Signature.verify()` to reflect support for custom network identifiers. ## [0.16.1](https://github.com/o1-labs/o1js/compare/834a44002...3b5f7c7) diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 30ee60d0e1..bdc0450248 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -243,18 +243,21 @@ class Signature extends CircuitValue { ): Signature { const publicKey = PublicKey.fromPrivateKey(privKey).toGroup(); const d = privKey.s; + // we chose an arbitrary prefix for the signature, and it happened to be 'testnet' + // there's no consequences in practice and the signatures can be used with any network + // if there needs to be a custom nonce, include it in the message itself const kPrime = Scalar.fromBigInt( deriveNonce( { fields: msg.map((f) => f.toBigInt()) }, { x: publicKey.x.toBigInt(), y: publicKey.y.toBigInt() }, BigInt(d.toJSON()), - networkId ?? 'testnet' + 'testnet' ) ); let { x: r, y: ry } = Group.generator.scale(kPrime); const k = ry.toBits()[0].toBoolean() ? kPrime.neg() : kPrime; let h = hashWithPrefix( - signaturePrefix(networkId ?? 'testnet'), + signaturePrefix('testnet'), msg.concat([publicKey.x, publicKey.y, r]) ); // TODO: Scalar.fromBits interprets the input as a "shifted scalar" @@ -270,8 +273,11 @@ class Signature extends CircuitValue { */ verify(publicKey: PublicKey, msg: Field[], networkId?: string): Bool { const point = publicKey.toGroup(); + // we chose an arbitrary prefix for the signature, and it happened to be 'testnet' + // there's no consequences in practice and the signatures can be used with any network + // if there needs to be a custom nonce, include it in the message itself let h = hashWithPrefix( - signaturePrefix(networkId ?? 'testnet'), + signaturePrefix('testnet'), msg.concat([point.x, point.y, this.r]) ); // TODO: Scalar.fromBits interprets the input as a "shifted scalar" From 1a60fd1012686f87465ddf7f3072e715369bc3c9 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 20 Feb 2024 10:14:23 +0100 Subject: [PATCH 506/524] revert sig type --- src/lib/signature.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/lib/signature.ts b/src/lib/signature.ts index bdc0450248..c830f21319 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -236,11 +236,7 @@ class Signature extends CircuitValue { * Signs a message using a {@link PrivateKey}. * @returns a {@link Signature} */ - static create( - privKey: PrivateKey, - msg: Field[], - networkId?: string - ): Signature { + static create(privKey: PrivateKey, msg: Field[]): Signature { const publicKey = PublicKey.fromPrivateKey(privKey).toGroup(); const d = privKey.s; // we chose an arbitrary prefix for the signature, and it happened to be 'testnet' @@ -271,7 +267,7 @@ class Signature extends CircuitValue { * Verifies the {@link Signature} using a message and the corresponding {@link PublicKey}. * @returns a {@link Bool} */ - verify(publicKey: PublicKey, msg: Field[], networkId?: string): Bool { + verify(publicKey: PublicKey, msg: Field[]): Bool { const point = publicKey.toGroup(); // we chose an arbitrary prefix for the signature, and it happened to be 'testnet' // there's no consequences in practice and the signatures can be used with any network From 3c799af8ed655b7d0171cccffae3b660401ef340 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 20 Feb 2024 10:19:31 +0100 Subject: [PATCH 507/524] fix build --- src/lib/account-update.ts | 8 +++---- src/mina-signer/mina-signer.ts | 24 +++++-------------- src/mina-signer/src/random-transaction.ts | 4 +++- src/mina-signer/src/sign-legacy.unit-test.ts | 3 ++- .../src/sign-zkapp-command.unit-test.ts | 6 ++--- src/mina-signer/src/signature.ts | 14 +++++------ src/mina-signer/src/signature.unit-test.ts | 13 ++++++---- src/mina-signer/src/types.ts | 2 +- 8 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index 5f20c2f20b..4c6220ad82 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -1019,18 +1019,16 @@ class AccountUpdate implements Types.AccountUpdate { // consistency between JS & OCaml hashing on *every single account update // proof* we create. It will give us 100% confidence that the two // implementations are equivalent, and catch regressions quickly + let networkId = activeInstance.getNetworkId(); if (Provable.inCheckedComputation()) { let input = Types.AccountUpdate.toInput(this); - return hashWithPrefix( - zkAppBodyPrefix(activeInstance.getNetworkId()), - packToFields(input) - ); + return hashWithPrefix(zkAppBodyPrefix(networkId), packToFields(input)); } else { let json = Types.AccountUpdate.toJSON(this); return Field( Test.hashFromJson.accountUpdate( JSON.stringify(json), - activeInstance.getNetworkId() + typeof networkId === 'string' ? networkId : networkId.custom ) ); } diff --git a/src/mina-signer/mina-signer.ts b/src/mina-signer/mina-signer.ts index b2e7ddf494..c1356b52e0 100644 --- a/src/mina-signer/mina-signer.ts +++ b/src/mina-signer/mina-signer.ts @@ -41,13 +41,8 @@ const defaultValidUntil = '4294967295'; class Client { private network: NetworkId; - constructor(options: { network: NetworkId }) { - if (!options?.network) { - throw Error('Invalid Specified Network'); - } - const specifiedNetwork = options.network.toLowerCase(); - - this.network = specifiedNetwork; + constructor({ network }: { network: NetworkId }) { + this.network = network; } /** @@ -120,13 +115,9 @@ class Client { * @param privateKey The private key used for signing * @returns The signed field elements */ - signFields( - fields: bigint[], - privateKey: Json.PrivateKey, - network?: NetworkId - ): Signed { + signFields(fields: bigint[], privateKey: Json.PrivateKey): Signed { let privateKey_ = PrivateKey.fromBase58(privateKey); - let signature = sign({ fields }, privateKey_, network ?? 'testnet'); + let signature = sign({ fields }, privateKey_, 'testnet'); return { signature: Signature.toBase58(signature), publicKey: PublicKey.toBase58(PrivateKey.toPublicKey(privateKey_)), @@ -141,15 +132,12 @@ class Client { * @returns True if the `signedFields` contains a valid signature matching * the fields and publicKey. */ - verifyFields( - { data, signature, publicKey }: Signed, - network?: NetworkId - ) { + verifyFields({ data, signature, publicKey }: Signed) { return verify( Signature.fromBase58(signature), { fields: data }, PublicKey.fromBase58(publicKey), - network ?? 'testnet' + 'testnet' ); } diff --git a/src/mina-signer/src/random-transaction.ts b/src/mina-signer/src/random-transaction.ts index 4709b7e433..d86270790b 100644 --- a/src/mina-signer/src/random-transaction.ts +++ b/src/mina-signer/src/random-transaction.ts @@ -141,6 +141,8 @@ const RandomTransaction = { zkappCommand, zkappCommandAndFeePayerKey, zkappCommandJson, - networkId: Random.oneOf('testnet', 'mainnet', 'other'), + networkId: Random.oneOf('testnet', 'mainnet', { + custom: 'other', + }), accountUpdateWithCallDepth: accountUpdate, }; diff --git a/src/mina-signer/src/sign-legacy.unit-test.ts b/src/mina-signer/src/sign-legacy.unit-test.ts index 403c57287d..f745914460 100644 --- a/src/mina-signer/src/sign-legacy.unit-test.ts +++ b/src/mina-signer/src/sign-legacy.unit-test.ts @@ -29,7 +29,8 @@ let networks: NetworkId[] = ['testnet', 'mainnet']; for (let network of networks) { let i = 0; - let reference = signatures[network]; + let reference = + signatures[typeof network === 'string' ? network : network.custom]; for (let payment of payments) { let signature = signPayment(payment, privateKey, network); diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 1345bc116a..34b34e9aeb 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -146,7 +146,7 @@ test( let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); let ocamlCommitments = Test.hashFromJson.transactionCommitments( JSON.stringify(zkappCommandJson), - networkId + typeof networkId === 'string' ? networkId : networkId.custom ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); let commitment = callForestHash(callForest, networkId); @@ -194,7 +194,7 @@ test( // tx commitment let ocamlCommitments = Test.hashFromJson.transactionCommitments( JSON.stringify(zkappCommandJson), - networkId + typeof networkId === 'string' ? networkId : networkId.custom ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); let commitment = callForestHash(callForest, networkId); @@ -244,7 +244,7 @@ test( let sigOCaml = Test.signature.signFieldElement( ocamlCommitments.fullCommitment, Ml.fromPrivateKey(feePayerKeySnarky), - networkId + typeof networkId === 'string' ? networkId : networkId.custom ); expect(Signature.toBase58(sigFieldElements)).toEqual(sigOCaml); diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 1da40bfaa4..03f11b0811 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -326,14 +326,14 @@ function networkIdOfString(n: string): [bigint, number] { return [BigInt('0b' + acc), acc.length]; } -function getNetworkIdHashInput(networkId: string): [bigint, number] { - switch (networkId) { +function getNetworkIdHashInput(network: NetworkId): [bigint, number] { + switch (typeof network === 'string' ? network : network.custom) { case 'mainnet': return [networkIdMainnet, 8]; case 'testnet': return [networkIdTestnet, 8]; default: - return networkIdOfString(networkId); + return networkIdOfString(network as string); } } @@ -350,8 +350,8 @@ const createCustomPrefix = (prefix: string) => { } }; -const signaturePrefix = (network: string) => { - switch (network) { +const signaturePrefix = (network: NetworkId) => { + switch (typeof network === 'string' ? network : network.custom) { case 'mainnet': return prefixes.signatureMainnet; case 'testnet': @@ -361,8 +361,8 @@ const signaturePrefix = (network: string) => { } }; -const zkAppBodyPrefix = (network: string) => { - switch (network) { +const zkAppBodyPrefix = (network: NetworkId) => { + switch (typeof network === 'string' ? network : network.custom) { case 'mainnet': return prefixes.zkappBodyMainnet; case 'testnet': diff --git a/src/mina-signer/src/signature.unit-test.ts b/src/mina-signer/src/signature.unit-test.ts index 50999cc89f..165f8fcfec 100644 --- a/src/mina-signer/src/signature.unit-test.ts +++ b/src/mina-signer/src/signature.unit-test.ts @@ -17,6 +17,7 @@ import { AccountUpdate } from '../../bindings/mina-transaction/gen/transaction-b import { HashInput } from '../../bindings/lib/provable-bigint.js'; import { Ml } from '../../lib/ml/conversion.js'; import { FieldConst } from '../../lib/field.js'; +import { NetworkId } from './types.js'; // check consistency with OCaml, where we expose the function to sign 1 field element with "testnet" function checkConsistentSingle( @@ -24,7 +25,7 @@ function checkConsistentSingle( key: PrivateKey, keySnarky: PrivateKeySnarky, pk: PublicKey, - networkId: string + networkId: NetworkId ) { let sig = signFieldElement(msg, key, networkId); @@ -44,7 +45,11 @@ function checkConsistentSingle( // consistent with OCaml let msgMl = FieldConst.fromBigint(msg); let keyMl = Ml.fromPrivateKey(keySnarky); - let actualTest = Test.signature.signFieldElement(msgMl, keyMl, networkId); + let actualTest = Test.signature.signFieldElement( + msgMl, + keyMl, + typeof networkId === 'string' ? networkId : networkId.custom + ); expect(Signature.toBase58(sig)).toEqual(actualTest); } @@ -101,14 +106,14 @@ for (let i = 0; i < 10; i++) { for (let x of hardcoded) { checkConsistentSingle(x, key, keySnarky, publicKey, 'testnet'); checkConsistentSingle(x, key, keySnarky, publicKey, 'mainnet'); - checkConsistentSingle(x, key, keySnarky, publicKey, 'other'); + checkConsistentSingle(x, key, keySnarky, publicKey, { custom: 'other' }); } // random single field elements for (let i = 0; i < 10; i++) { let x = randomFields[i]; checkConsistentSingle(x, key, keySnarky, publicKey, 'testnet'); checkConsistentSingle(x, key, keySnarky, publicKey, 'mainnet'); - checkConsistentSingle(x, key, keySnarky, publicKey, 'other'); + checkConsistentSingle(x, key, keySnarky, publicKey, { custom: 'other' }); } // hard-coded multi-element hash inputs let messages: HashInput[] = [ diff --git a/src/mina-signer/src/types.ts b/src/mina-signer/src/types.ts index e1b7a52c32..6133704784 100644 --- a/src/mina-signer/src/types.ts +++ b/src/mina-signer/src/types.ts @@ -9,7 +9,7 @@ export type Field = number | bigint | string; export type PublicKey = string; export type PrivateKey = string; export type Signature = SignatureJson; -export type NetworkId = string; +export type NetworkId = 'mainnet' | 'testnet' | { custom: string }; export type Keypair = { readonly privateKey: PrivateKey; From d311e926607e052f46840e282a10f8598e88d5ce Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 20 Feb 2024 10:23:24 +0100 Subject: [PATCH 508/524] add NetworkID.toString --- src/mina-signer/src/sign-legacy.unit-test.ts | 4 ++-- src/mina-signer/src/signature.ts | 6 +++--- src/mina-signer/src/types.ts | 6 ++++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/mina-signer/src/sign-legacy.unit-test.ts b/src/mina-signer/src/sign-legacy.unit-test.ts index f745914460..4a714f3287 100644 --- a/src/mina-signer/src/sign-legacy.unit-test.ts +++ b/src/mina-signer/src/sign-legacy.unit-test.ts @@ -21,6 +21,7 @@ import { Field } from '../../provable/field-bigint.js'; import { Random, test } from '../../lib/testing/property.js'; import { RandomTransaction } from './random-transaction.js'; import { NetworkId } from './types.js'; +import { Network } from 'src/lib/precondition.js'; let { privateKey, publicKey } = keypair; let networks: NetworkId[] = ['testnet', 'mainnet']; @@ -29,8 +30,7 @@ let networks: NetworkId[] = ['testnet', 'mainnet']; for (let network of networks) { let i = 0; - let reference = - signatures[typeof network === 'string' ? network : network.custom]; + let reference = signatures[NetworkId.toString(network)]; for (let payment of payments) { let signature = signPayment(payment, privateKey, network); diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 03f11b0811..c159de2e2e 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -327,7 +327,7 @@ function networkIdOfString(n: string): [bigint, number] { } function getNetworkIdHashInput(network: NetworkId): [bigint, number] { - switch (typeof network === 'string' ? network : network.custom) { + switch (NetworkId.toString(network)) { case 'mainnet': return [networkIdMainnet, 8]; case 'testnet': @@ -351,7 +351,7 @@ const createCustomPrefix = (prefix: string) => { }; const signaturePrefix = (network: NetworkId) => { - switch (typeof network === 'string' ? network : network.custom) { + switch (NetworkId.toString(network)) { case 'mainnet': return prefixes.signatureMainnet; case 'testnet': @@ -362,7 +362,7 @@ const signaturePrefix = (network: NetworkId) => { }; const zkAppBodyPrefix = (network: NetworkId) => { - switch (typeof network === 'string' ? network : network.custom) { + switch (NetworkId.toString(network)) { case 'mainnet': return prefixes.zkappBodyMainnet; case 'testnet': diff --git a/src/mina-signer/src/types.ts b/src/mina-signer/src/types.ts index 6133704784..87739e059b 100644 --- a/src/mina-signer/src/types.ts +++ b/src/mina-signer/src/types.ts @@ -11,6 +11,12 @@ export type PrivateKey = string; export type Signature = SignatureJson; export type NetworkId = 'mainnet' | 'testnet' | { custom: string }; +export const NetworkId = { + toString(network: NetworkId) { + return typeof network === 'string' ? network : network.custom; + }, +}; + export type Keypair = { readonly privateKey: PrivateKey; readonly publicKey: PublicKey; From 1685902836ee5ae62ec33a697a50bd8ef1ac8c51 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 20 Feb 2024 10:25:14 +0100 Subject: [PATCH 509/524] simplify --- src/lib/account-update.ts | 3 ++- src/mina-signer/src/sign-zkapp-command.unit-test.ts | 6 +++--- src/mina-signer/src/signature.unit-test.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index 4c6220ad82..e36f51078c 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -69,6 +69,7 @@ import { } from './mina/smart-contract-context.js'; import { assert } from './util/assert.js'; import { RandomId } from './provable-types/auxiliary.js'; +import { NetworkId } from '../mina-signer/src/types.js'; // external API export { @@ -1028,7 +1029,7 @@ class AccountUpdate implements Types.AccountUpdate { return Field( Test.hashFromJson.accountUpdate( JSON.stringify(json), - typeof networkId === 'string' ? networkId : networkId.custom + NetworkId.toString(networkId) ) ); } diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 34b34e9aeb..04f2946bc6 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -146,7 +146,7 @@ test( let zkappCommandJson = ZkappCommand.toJSON(zkappCommand); let ocamlCommitments = Test.hashFromJson.transactionCommitments( JSON.stringify(zkappCommandJson), - typeof networkId === 'string' ? networkId : networkId.custom + NetworkId.toString(networkId) ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); let commitment = callForestHash(callForest, networkId); @@ -194,7 +194,7 @@ test( // tx commitment let ocamlCommitments = Test.hashFromJson.transactionCommitments( JSON.stringify(zkappCommandJson), - typeof networkId === 'string' ? networkId : networkId.custom + NetworkId.toString(networkId) ); let callForest = accountUpdatesToCallForest(zkappCommand.accountUpdates); let commitment = callForestHash(callForest, networkId); @@ -244,7 +244,7 @@ test( let sigOCaml = Test.signature.signFieldElement( ocamlCommitments.fullCommitment, Ml.fromPrivateKey(feePayerKeySnarky), - typeof networkId === 'string' ? networkId : networkId.custom + NetworkId.toString(networkId) ); expect(Signature.toBase58(sigFieldElements)).toEqual(sigOCaml); diff --git a/src/mina-signer/src/signature.unit-test.ts b/src/mina-signer/src/signature.unit-test.ts index 165f8fcfec..ae8384cd24 100644 --- a/src/mina-signer/src/signature.unit-test.ts +++ b/src/mina-signer/src/signature.unit-test.ts @@ -48,7 +48,7 @@ function checkConsistentSingle( let actualTest = Test.signature.signFieldElement( msgMl, keyMl, - typeof networkId === 'string' ? networkId : networkId.custom + NetworkId.toString(networkId) ); expect(Signature.toBase58(sig)).toEqual(actualTest); } From ecf67e5c9c00c715e1c0ae6b2a528e691664da44 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 20 Feb 2024 10:38:14 +0100 Subject: [PATCH 510/524] cleanup --- src/mina-signer/src/sign-legacy.unit-test.ts | 1 - src/mina-signer/src/sign-zkapp-command.unit-test.ts | 8 ++++++++ src/mina-signer/src/signature.ts | 11 +++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/mina-signer/src/sign-legacy.unit-test.ts b/src/mina-signer/src/sign-legacy.unit-test.ts index 4a714f3287..61a9de37d6 100644 --- a/src/mina-signer/src/sign-legacy.unit-test.ts +++ b/src/mina-signer/src/sign-legacy.unit-test.ts @@ -21,7 +21,6 @@ import { Field } from '../../provable/field-bigint.js'; import { Random, test } from '../../lib/testing/property.js'; import { RandomTransaction } from './random-transaction.js'; import { NetworkId } from './types.js'; -import { Network } from 'src/lib/precondition.js'; let { privateKey, publicKey } = keypair; let networks: NetworkId[] = ['testnet', 'mainnet']; diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index 04f2946bc6..ced276d9ed 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -112,6 +112,14 @@ test( let hashSnarky = accountUpdateSnarky.hash(); let hash = accountUpdateHash(accountUpdate, networkId); expect(hash).toEqual(hashSnarky.toBigInt()); + /* + // check against different network hash + expect(hash).not.toEqual( + accountUpdateHash( + accountUpdate, + NetworkId.toString(networkId) === 'mainnet' ? 'testnet' : 'mainnet' + ) + ); */ } ); diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index c159de2e2e..9a94f01cd4 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -327,13 +327,14 @@ function networkIdOfString(n: string): [bigint, number] { } function getNetworkIdHashInput(network: NetworkId): [bigint, number] { - switch (NetworkId.toString(network)) { + let s = NetworkId.toString(network); + switch (s) { case 'mainnet': return [networkIdMainnet, 8]; case 'testnet': return [networkIdTestnet, 8]; default: - return networkIdOfString(network as string); + return networkIdOfString(s); } } @@ -351,7 +352,8 @@ const createCustomPrefix = (prefix: string) => { }; const signaturePrefix = (network: NetworkId) => { - switch (NetworkId.toString(network)) { + let s = NetworkId.toString(network); + switch (s) { case 'mainnet': return prefixes.signatureMainnet; case 'testnet': @@ -362,7 +364,8 @@ const signaturePrefix = (network: NetworkId) => { }; const zkAppBodyPrefix = (network: NetworkId) => { - switch (NetworkId.toString(network)) { + let s = NetworkId.toString(network); + switch (s) { case 'mainnet': return prefixes.zkappBodyMainnet; case 'testnet': From 5840e767d3ef57b48accce4315c1fb1529af8aa3 Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 20 Feb 2024 10:43:19 +0100 Subject: [PATCH 511/524] fix tests --- src/lib/account-update.ts | 8 +++++--- src/mina-signer/src/signature.ts | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/lib/account-update.ts b/src/lib/account-update.ts index e36f51078c..beb297c26c 100644 --- a/src/lib/account-update.ts +++ b/src/lib/account-update.ts @@ -1020,16 +1020,18 @@ class AccountUpdate implements Types.AccountUpdate { // consistency between JS & OCaml hashing on *every single account update // proof* we create. It will give us 100% confidence that the two // implementations are equivalent, and catch regressions quickly - let networkId = activeInstance.getNetworkId(); if (Provable.inCheckedComputation()) { let input = Types.AccountUpdate.toInput(this); - return hashWithPrefix(zkAppBodyPrefix(networkId), packToFields(input)); + return hashWithPrefix( + zkAppBodyPrefix(activeInstance.getNetworkId()), + packToFields(input) + ); } else { let json = Types.AccountUpdate.toJSON(this); return Field( Test.hashFromJson.accountUpdate( JSON.stringify(json), - NetworkId.toString(networkId) + NetworkId.toString(activeInstance.getNetworkId()) ) ); } diff --git a/src/mina-signer/src/signature.ts b/src/mina-signer/src/signature.ts index 9a94f01cd4..a80ef3e80e 100644 --- a/src/mina-signer/src/signature.ts +++ b/src/mina-signer/src/signature.ts @@ -359,7 +359,7 @@ const signaturePrefix = (network: NetworkId) => { case 'testnet': return prefixes.signatureTestnet; default: - return createCustomPrefix(network + 'Signature'); + return createCustomPrefix(s + 'Signature'); } }; @@ -371,6 +371,6 @@ const zkAppBodyPrefix = (network: NetworkId) => { case 'testnet': return prefixes.zkappBodyTestnet; default: - return createCustomPrefix(network + 'ZkappBody'); + return createCustomPrefix(s + 'ZkappBody'); } }; From 5b9bdcfebb8ee738a3d3207632240449ae1abfaf Mon Sep 17 00:00:00 2001 From: Florian Kluge Date: Tue, 20 Feb 2024 10:44:28 +0100 Subject: [PATCH 512/524] add check against different network hash --- src/mina-signer/src/sign-zkapp-command.unit-test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mina-signer/src/sign-zkapp-command.unit-test.ts b/src/mina-signer/src/sign-zkapp-command.unit-test.ts index ced276d9ed..a6db9a9327 100644 --- a/src/mina-signer/src/sign-zkapp-command.unit-test.ts +++ b/src/mina-signer/src/sign-zkapp-command.unit-test.ts @@ -112,14 +112,14 @@ test( let hashSnarky = accountUpdateSnarky.hash(); let hash = accountUpdateHash(accountUpdate, networkId); expect(hash).toEqual(hashSnarky.toBigInt()); - /* + // check against different network hash expect(hash).not.toEqual( accountUpdateHash( accountUpdate, NetworkId.toString(networkId) === 'mainnet' ? 'testnet' : 'mainnet' ) - ); */ + ); } ); From be9619101f02ef2e3488cb2616deca6af537fe59 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 20 Feb 2024 11:31:30 +0100 Subject: [PATCH 513/524] method to generate random keypair --- src/lib/signature.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/lib/signature.ts b/src/lib/signature.ts index 261aab8cc2..42cf340dda 100644 --- a/src/lib/signature.ts +++ b/src/lib/signature.ts @@ -32,9 +32,13 @@ class PrivateKey extends CircuitValue { } /** - * You can use this method to generate a private key. You can then obtain - * the associated public key via {@link toPublicKey}. And generate signatures - * via {@link Signature.create}. + * Generate a random private key. + * + * You can obtain the associated public key via {@link toPublicKey}. + * And generate signatures via {@link Signature.create}. + * + * Note: This uses node or browser built-in APIs to obtain cryptographically strong randomness, + * and can be safely used to generate a real private key. * * @returns a new {@link PrivateKey}. */ @@ -42,6 +46,17 @@ class PrivateKey extends CircuitValue { return new PrivateKey(Scalar.random()); } + /** + * Create a random keypair `{ privateKey: PrivateKey, publicKey: PublicKey }`. + * + * Note: This uses node or browser built-in APIs to obtain cryptographically strong randomness, + * and can be safely used to generate a real keypair. + */ + static randomKeypair() { + let privateKey = PrivateKey.random(); + return { privateKey, publicKey: privateKey.toPublicKey() }; + } + /** * Deserializes a list of bits into a {@link PrivateKey}. * From 1b89d8160797850839343d54ba9c5c4aac0b06aa Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 20 Feb 2024 11:32:11 +0100 Subject: [PATCH 514/524] fix token contract test --- src/lib/mina/token/token-contract.unit-test.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lib/mina/token/token-contract.unit-test.ts b/src/lib/mina/token/token-contract.unit-test.ts index a62ebf160c..3301f9380a 100644 --- a/src/lib/mina/token/token-contract.unit-test.ts +++ b/src/lib/mina/token/token-contract.unit-test.ts @@ -7,6 +7,7 @@ import { AccountUpdateForest, TokenContract, Int64, + PrivateKey, } from '../../../index.js'; class ExampleTokenContract extends TokenContract { @@ -26,9 +27,6 @@ class ExampleTokenContract extends TokenContract { // mint the entire supply to the token account with the same address as this contract this.internal.mint({ address: this.address, amount: this.SUPPLY }); - - // pay fees for opened account - this.balance.subInPlace(Mina.getNetworkConstants().accountCreationFee); } } @@ -39,15 +37,19 @@ Mina.setActiveInstance(Local); let [ { publicKey: sender, privateKey: senderKey }, - { publicKey: tokenAddress, privateKey: tokenKey }, { publicKey: otherAddress, privateKey: otherKey }, ] = Local.testAccounts; +let { publicKey: tokenAddress, privateKey: tokenKey } = + PrivateKey.randomKeypair(); let token = new ExampleTokenContract(tokenAddress); -let tokenId = token.token.id; +let tokenId = token.deriveTokenId(); // deploy token contract -let deployTx = await Mina.transaction(sender, () => token.deploy()); +let deployTx = await Mina.transaction(sender, () => { + AccountUpdate.fundNewAccount(sender, 2); + token.deploy(); +}); await deployTx.prove(); await deployTx.sign([tokenKey, senderKey]).send(); From 358aa27c2cf4233c2a3a8779af5ac5ecc7c9160d Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 20 Feb 2024 11:41:26 +0100 Subject: [PATCH 515/524] fix dex deploy scripts to respect isNew precondition --- src/examples/zkapps/dex/happy-path-with-actions.ts | 12 ++++++------ src/examples/zkapps/dex/happy-path-with-proofs.ts | 12 ++++++------ src/examples/zkapps/dex/run-live.ts | 12 ++++++------ src/examples/zkapps/dex/run.ts | 11 ++++++----- src/examples/zkapps/dex/upgradability.ts | 10 +++++----- 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/examples/zkapps/dex/happy-path-with-actions.ts b/src/examples/zkapps/dex/happy-path-with-actions.ts index 47ca1078bf..78a07961eb 100644 --- a/src/examples/zkapps/dex/happy-path-with-actions.ts +++ b/src/examples/zkapps/dex/happy-path-with-actions.ts @@ -43,14 +43,14 @@ let dexTokenHolderY = new DexTokenHolder(addresses.dex, tokenIds.Y); tic('deploy & init token contracts'); tx = await Mina.transaction(feePayerAddress, () => { - const accountFee = Mina.getNetworkConstants().accountCreationFee; - // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves - let feePayerUpdate = AccountUpdate.createSigned(feePayerAddress); - feePayerUpdate.balance.subInPlace(accountFee.mul(2)); - feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee }); - feePayerUpdate.send({ to: addresses.tokenY, amount: accountFee }); tokenX.deploy(); tokenY.deploy(); + + // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves + const accountFee = Mina.getNetworkConstants().accountCreationFee; + let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); + feePayerUpdate.send({ to: tokenX.self, amount: accountFee }); + feePayerUpdate.send({ to: tokenY.self, amount: accountFee }); }); await tx.prove(); await tx.sign([feePayerKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/happy-path-with-proofs.ts b/src/examples/zkapps/dex/happy-path-with-proofs.ts index 6f44280549..2b6fae11b5 100644 --- a/src/examples/zkapps/dex/happy-path-with-proofs.ts +++ b/src/examples/zkapps/dex/happy-path-with-proofs.ts @@ -45,14 +45,14 @@ let dexTokenHolderY = new DexTokenHolder(addresses.dex, tokenIds.Y); tic('deploy & init token contracts'); tx = await Mina.transaction(feePayerAddress, () => { - const accountFee = Mina.getNetworkConstants().accountCreationFee; - // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves - let feePayerUpdate = AccountUpdate.createSigned(feePayerAddress); - feePayerUpdate.balance.subInPlace(accountFee.mul(2)); - feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee }); - feePayerUpdate.send({ to: addresses.tokenY, amount: accountFee }); tokenX.deploy(); tokenY.deploy(); + + // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves + const accountFee = Mina.getNetworkConstants().accountCreationFee; + let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); + feePayerUpdate.send({ to: tokenX.self, amount: accountFee }); + feePayerUpdate.send({ to: tokenY.self, amount: accountFee }); }); await tx.prove(); await tx.sign([feePayerKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/run-live.ts b/src/examples/zkapps/dex/run-live.ts index 22c013966f..6db2631acc 100644 --- a/src/examples/zkapps/dex/run-live.ts +++ b/src/examples/zkapps/dex/run-live.ts @@ -73,14 +73,14 @@ let userSpec = { sender: addresses.user, fee: 0.1e9 }; if (successfulTransactions <= 0) { tic('deploy & init token contracts'); tx = await Mina.transaction(senderSpec, () => { - const accountFee = Mina.getNetworkConstants().accountCreationFee; - // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves - let feePayerUpdate = AccountUpdate.createSigned(sender); - feePayerUpdate.balance.subInPlace(accountFee.mul(2)); - feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee }); - feePayerUpdate.send({ to: addresses.tokenY, amount: accountFee }); tokenX.deploy(); tokenY.deploy(); + + // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves + const accountFee = Mina.getNetworkConstants().accountCreationFee; + let feePayerUpdate = AccountUpdate.fundNewAccount(sender, 2); + feePayerUpdate.send({ to: tokenX.self, amount: accountFee }); + feePayerUpdate.send({ to: tokenY.self, amount: accountFee }); }); await tx.prove(); pendingTx = await tx.sign([senderKey, keys.tokenX, keys.tokenY]).send(); diff --git a/src/examples/zkapps/dex/run.ts b/src/examples/zkapps/dex/run.ts index ca831647e3..0244ae541e 100644 --- a/src/examples/zkapps/dex/run.ts +++ b/src/examples/zkapps/dex/run.ts @@ -76,13 +76,14 @@ async function main({ withVesting }: { withVesting: boolean }) { console.log('deploy & init token contracts...'); tx = await Mina.transaction(feePayerAddress, () => { - const accountFee = Mina.getNetworkConstants().accountCreationFee; - // pay fees for creating 2 token contract accounts, and fund them so each can create 2 accounts themselves - let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); - feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee.mul(2) }); - feePayerUpdate.send({ to: addresses.tokenY, amount: accountFee.mul(2) }); tokenX.deploy(); tokenY.deploy(); + + // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves + const accountFee = Mina.getNetworkConstants().accountCreationFee; + let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); + feePayerUpdate.send({ to: tokenX.self, amount: accountFee.mul(2) }); + feePayerUpdate.send({ to: tokenY.self, amount: accountFee.mul(2) }); }); await tx.prove(); tx.sign([feePayerKey, keys.tokenX, keys.tokenY]); diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 0c9fba9b9b..4f8cf4a7ec 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -267,14 +267,14 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { console.log('deploy & init token contracts...'); tx = await Mina.transaction(feePayerAddress, () => { - const accountFee = Mina.getNetworkConstants().accountCreationFee; + tokenX.deploy(); + tokenY.deploy(); + // pay fees for creating 2 token contract accounts, and fund them so each can create 2 accounts themselves - let feePayerUpdate = AccountUpdate.createSigned(feePayerAddress); - feePayerUpdate.balance.subInPlace(accountFee.mul(2)); + const accountFee = Mina.getNetworkConstants().accountCreationFee; + let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee.mul(2) }); feePayerUpdate.send({ to: addresses.tokenY, amount: accountFee.mul(2) }); - tokenX.deploy(); - tokenY.deploy(); }); await tx.prove(); tx.sign([feePayerKey, keys.tokenX, keys.tokenY]); From 8e9a6161755ee9dfd5fd13e874646ff5210cefd8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 20 Feb 2024 11:42:49 +0100 Subject: [PATCH 516/524] changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f34205241..255351b785 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Unreleased](https://github.com/o1-labs/o1js/compare/3b5f7c7...HEAD) +### Added + +- `PrivateKey.randomKeypair()` to generate private and public key in one command https://github.com/o1-labs/o1js/pull/1446 + ### Deprecated - `SmartContract.token` is deprecated in favor of new methods on `TokenContract` https://github.com/o1-labs/o1js/pull/1446 From 71087acda48d7fe962f4a6e09a5ce6af93dfd13c Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 20 Feb 2024 11:43:11 +0100 Subject: [PATCH 517/524] dump vks --- tests/vk-regression/vk-regression.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index 6e8a9a738d..2c0384dd01 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -84,8 +84,12 @@ } }, "Dex": { - "digest": "1adce234400fe0b0ea8b1e625256538660d9ed6e15767e2ac78bb7a59d83a3af", + "digest": "36685f69aa608d71cbd88eddf77cc2271d0ad781182e7f8ce7ceab746cde896b", "methods": { + "approveBase": { + "rows": 13244, + "digest": "4dd81f1cc1a06b617677e08883e50fbd" + }, "supplyLiquidityBase": { "rows": 2882, "digest": "9930f9a0b82eff6247cded6430c6356f" @@ -101,15 +105,11 @@ "burnLiquidity": { "rows": 718, "digest": "6c406099fe2d2493bd216f9bbe3ba934" - }, - "transfer": { - "rows": 1044, - "digest": "1df1d01485d388ee364156f940214d23" } }, "verificationKey": { - "data": "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", - "hash": "23594323045578615602880853374590447788338441806100547122393736875331781522763" + "data": "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", + "hash": "27190148268093452485039136201330807645985391900537471731621996478489554499244" } }, "Group Primitive": { From a6a06e44c99ee2bd26c3f23000e7f0de72f64858 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 20 Feb 2024 11:45:54 +0100 Subject: [PATCH 518/524] remove obsolete example --- .../zkapps/dex/arbitrary-token-interaction.ts | 57 ------------------- 1 file changed, 57 deletions(-) delete mode 100644 src/examples/zkapps/dex/arbitrary-token-interaction.ts diff --git a/src/examples/zkapps/dex/arbitrary-token-interaction.ts b/src/examples/zkapps/dex/arbitrary-token-interaction.ts deleted file mode 100644 index 27843a1a23..0000000000 --- a/src/examples/zkapps/dex/arbitrary-token-interaction.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { AccountUpdate, Mina, TokenId, UInt64 } from 'o1js'; -import { TokenContract, addresses, keys, tokenIds } from './dex.js'; - -let doProofs = true; -let Local = Mina.LocalBlockchain({ proofsEnabled: doProofs }); -Mina.setActiveInstance(Local); - -let [{ privateKey: userKey, publicKey: userAddress }] = Local.testAccounts; -let tx; - -console.log('-------------------------------------------------'); -console.log('TOKEN X ADDRESS\t', addresses.tokenX.toBase58()); -console.log('USER ADDRESS\t', userAddress.toBase58()); -console.log('-------------------------------------------------'); -console.log('TOKEN X ID\t', TokenId.toBase58(tokenIds.X)); -console.log('-------------------------------------------------'); - -// compile & deploy all 5 zkApps -console.log('compile (token)...'); -await TokenContract.compile(); - -let tokenX = new TokenContract(addresses.tokenX); - -console.log('deploy & init token contracts...'); -tx = await Mina.transaction(userAddress, () => { - // pay fees for creating 2 token contract accounts, and fund them so each can create 1 account themselves - let feePayerUpdate = AccountUpdate.createSigned(userAddress); - feePayerUpdate.balance.subInPlace( - Mina.getNetworkConstants().accountCreationFee.mul(1) - ); - tokenX.deploy(); -}); -await tx.prove(); -tx.sign([keys.tokenX]); -await tx.send(); - -console.log('arbitrary token minting...'); -tx = await Mina.transaction(userAddress, () => { - // pay fees for creating user's token X account - AccountUpdate.createSigned(userAddress).balance.subInPlace( - Mina.getNetworkConstants().accountCreationFee.mul(1) - ); - // 😈😈😈 mint any number of tokens to our account 😈😈😈 - let tokenContract = new TokenContract(addresses.tokenX); - tokenContract.internal.mint({ - address: userAddress, - amount: UInt64.from(1e18), - }); -}); -await tx.prove(); -console.log(tx.toPretty()); -await tx.send(); - -console.log( - 'User tokens: ', - Mina.getBalance(userAddress, tokenIds.X).value.toBigInt() -); From 40ebf95c8d5b3f2a4631564178b056b2763cc2b5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 20 Feb 2024 12:09:51 +0100 Subject: [PATCH 519/524] fixup upgradability test --- src/examples/zkapps/dex/dex.ts | 6 ++++++ src/examples/zkapps/dex/upgradability.ts | 4 ++-- src/lib/precondition.ts | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/examples/zkapps/dex/dex.ts b/src/examples/zkapps/dex/dex.ts index eec19763d7..84dec73c87 100644 --- a/src/examples/zkapps/dex/dex.ts +++ b/src/examples/zkapps/dex/dex.ts @@ -208,6 +208,12 @@ function createDex({ } class ModifiedDex extends Dex { + deploy() { + super.deploy(); + // override the isNew requirement for re-deploying + this.account.isNew.requireNothing(); + } + @method swapX(dx: UInt64): UInt64 { let tokenY = new TokenContract(this.tokenY); let dexY = new ModifiedDexTokenHolder( diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 4f8cf4a7ec..8358b45364 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -273,8 +273,8 @@ async function upgradeabilityTests({ withVesting }: { withVesting: boolean }) { // pay fees for creating 2 token contract accounts, and fund them so each can create 2 accounts themselves const accountFee = Mina.getNetworkConstants().accountCreationFee; let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); - feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee.mul(2) }); - feePayerUpdate.send({ to: addresses.tokenY, amount: accountFee.mul(2) }); + feePayerUpdate.send({ to: tokenX.self, amount: accountFee.mul(2) }); + feePayerUpdate.send({ to: tokenY.self, amount: accountFee.mul(2) }); }); await tx.prove(); tx.sign([feePayerKey, keys.tokenX, keys.tokenY]); diff --git a/src/lib/precondition.ts b/src/lib/precondition.ts index ff54c2c835..8800d5b344 100644 --- a/src/lib/precondition.ts +++ b/src/lib/precondition.ts @@ -426,6 +426,13 @@ function preconditionSubclass< this.requireEquals(value); }, requireNothing() { + let property = getPath( + accountUpdate.body.preconditions, + longKey + ) as AnyCondition; + if ('isSome' in property) { + property.isSome = Bool(false); + } context.constrained.add(longKey); }, assertNothing() { From 6be807ba93965647f5fd8f19117466107dd842a2 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 20 Feb 2024 12:14:46 +0100 Subject: [PATCH 520/524] another fix --- src/examples/zkapps/dex/upgradability.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/examples/zkapps/dex/upgradability.ts b/src/examples/zkapps/dex/upgradability.ts index 8358b45364..366ca2c467 100644 --- a/src/examples/zkapps/dex/upgradability.ts +++ b/src/examples/zkapps/dex/upgradability.ts @@ -57,13 +57,14 @@ async function atomicActionsTest({ withVesting }: { withVesting: boolean }) { console.log('deploy & init token contracts...'); tx = await Mina.transaction(feePayerAddress, () => { - const accountFee = Mina.getNetworkConstants().accountCreationFee; - // pay fees for creating 2 token contract accounts, and fund them so each can create 2 accounts themselves - let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); - feePayerUpdate.send({ to: addresses.tokenX, amount: accountFee.mul(2) }); - feePayerUpdate.send({ to: addresses.tokenY, amount: accountFee.mul(2) }); tokenX.deploy(); tokenY.deploy(); + + // pay fees for creating 2 token contract accounts, and fund them so each can create 2 accounts themselves + const accountFee = Mina.getNetworkConstants().accountCreationFee; + let feePayerUpdate = AccountUpdate.fundNewAccount(feePayerAddress, 2); + feePayerUpdate.send({ to: tokenX.self, amount: accountFee.mul(2) }); + feePayerUpdate.send({ to: tokenY.self, amount: accountFee.mul(2) }); }); await tx.prove(); tx.sign([feePayerKey, keys.tokenX, keys.tokenY]); From a5332b12d7caa58c1981ff155644e904e8ee083d Mon Sep 17 00:00:00 2001 From: Serhii Shymkiv Date: Wed, 21 Feb 2024 11:22:40 +0200 Subject: [PATCH 521/524] Export Mina-Signer types. --- src/mina-signer/package-lock.json | 4 ++-- src/mina-signer/package.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mina-signer/package-lock.json b/src/mina-signer/package-lock.json index 88342af0d7..4c7579b351 100644 --- a/src/mina-signer/package-lock.json +++ b/src/mina-signer/package-lock.json @@ -1,12 +1,12 @@ { "name": "mina-signer", - "version": "3.0.3", + "version": "3.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mina-signer", - "version": "3.0.3", + "version": "3.0.4", "license": "Apache-2.0", "dependencies": { "blakejs": "^1.2.1", diff --git a/src/mina-signer/package.json b/src/mina-signer/package.json index 9c3523cf3a..cd4e0aedb1 100644 --- a/src/mina-signer/package.json +++ b/src/mina-signer/package.json @@ -1,7 +1,7 @@ { "name": "mina-signer", "description": "Node API for signing transactions on various networks for Mina Protocol", - "version": "3.0.3", + "version": "3.0.4", "type": "module", "scripts": { "build": "tsc -p ../../tsconfig.mina-signer.json", @@ -23,6 +23,7 @@ "main": "dist/node/mina-signer/mina-signer.js", "types": "dist/node/mina-signer/index.d.ts", "exports": { + "types": "./dist/node/mina-signer/mina-signer.d.ts", "web": "./dist/web/index.js", "require": "./dist/node/mina-signer/index.cjs", "node": "./dist/node/mina-signer/mina-signer.js", From 88d9a70f66ebe2c92c3770cfc3317b92a202b6b8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Feb 2024 10:58:58 +0100 Subject: [PATCH 522/524] document token contract deploy --- src/lib/mina/token/token-contract.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/lib/mina/token/token-contract.ts b/src/lib/mina/token/token-contract.ts index 673524d1ef..793afcbfb9 100644 --- a/src/lib/mina/token/token-contract.ts +++ b/src/lib/mina/token/token-contract.ts @@ -27,6 +27,27 @@ const MAX_ACCOUNT_UPDATES = 20; abstract class TokenContract extends SmartContract { // change default permissions - important that token contracts use an access permission + /** + * Deploys a {@link TokenContract}. + * + * In addition to base smart contract deployment, this adds two steps: + * - set the `access` permission to `proofOrSignature()`, to prevent against unauthorized token operations + * - not doing this would imply that anyone can bypass token contract authorization and simply mint themselves tokens + * - require the zkapp account to be new, using the `isNew` precondition. + * this guarantees that the access permission is set from the very start of the existence of this account. + * creating the zkapp account before deployment would otherwise be a security vulnerability that is too easy to introduce. + * + * Note that because of the `isNew` precondition, the zkapp account must not be created prior to calling `deploy()`. + * + * If the contract needs to be re-deployed, you can switch off this behaviour by overriding the `isNew` precondition: + * ```ts + * deploy() { + * super.deploy(); + * // DON'T DO THIS ON THE INITIAL DEPLOYMENT! + * this.account.isNew.requireNothing(); + * } + * ``` + */ deploy(args?: DeployArgs) { super.deploy(args); From d8079fde52da47bfcc2b6ba1865ec39c231f7a79 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Feb 2024 11:00:02 +0100 Subject: [PATCH 523/524] minor --- src/examples/simple-zkapp.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/examples/simple-zkapp.ts b/src/examples/simple-zkapp.ts index bd17d6eae4..8d6e855eb9 100644 --- a/src/examples/simple-zkapp.ts +++ b/src/examples/simple-zkapp.ts @@ -99,10 +99,10 @@ console.log('deploy'); let tx = await Mina.transaction(sender, () => { let senderUpdate = AccountUpdate.fundNewAccount(sender); senderUpdate.send({ to: zkappAddress, amount: initialBalance }); - zkapp.deploy({ zkappKey }); + zkapp.deploy(); }); await tx.prove(); -await tx.sign([senderKey]).send(); +await tx.sign([senderKey, zkappKey]).send(); console.log('initial state: ' + zkapp.x.get()); console.log(`initial balance: ${zkapp.account.balance.get().div(1e9)} MINA`); From 01614a5f85fc8045fe22c9563e4ba02fc42909a3 Mon Sep 17 00:00:00 2001 From: Gregor Date: Wed, 21 Feb 2024 11:01:50 +0100 Subject: [PATCH 524/524] link in comment --- src/lib/zkapp.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 0f39725950..de5386fa21 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -65,6 +65,7 @@ import { smartContractContext, } from './mina/smart-contract-context.js'; import { deprecatedToken } from './mina/token/token-methods.js'; +import type { TokenContract } from './mina/token/token-contract.js'; // external API export { SmartContract, method, DeployArgs, declareMethods, Account, Reducer }; @@ -847,10 +848,11 @@ super.init(); return this.self.currentSlot; } /** - * @deprecated `SmartContract.token` will be removed, and token methods will only be available on `TokenContract.internal`. + * @deprecated + * `SmartContract.token` will be removed, and token methods will only be available on `TokenContract.internal`. * Instead of `SmartContract.token.id`, use `TokenContract.deriveTokenId()`. * - * For security reasons, it is recommended to use `TokenContract` as the base contract for all tokens. + * For security reasons, it is recommended to use {@link TokenContract} as the base contract for all tokens. */ get token() { return deprecatedToken(this.self);