From 89cb889fa9ed77cda24d91484dd381dbca8f23b8 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 4 Apr 2023 15:53:05 -0700 Subject: [PATCH 1/6] Add endActionHash and fromActionHash to fetchActions --- src/lib/fetch.ts | 69 +++++++++++++++++++++++++++++++++++------------- src/lib/mina.ts | 37 +++++++++++++++++++++----- src/lib/zkapp.ts | 9 ++++++- 3 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index b9769a5295..36dc26e7ca 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -5,6 +5,7 @@ import { SequenceEvents, TokenId } from './account_update.js'; import { PublicKey } from './signature.js'; import { NetworkValue } from './precondition.js'; import { Types } from '../provable/types.js'; +import { ActionHashes } from './mina.js'; import * as Encoding from './encoding.js'; import { Account, @@ -173,7 +174,12 @@ let accountsToFetch = {} as Record< let networksToFetch = {} as Record; let actionsToFetch = {} as Record< string, - { publicKey: string; tokenId: string; graphqlEndpoint: string } + { + publicKey: string; + tokenId: string; + actionHashes: { fromActionHash?: string; endActionHash?: string }; + graphqlEndpoint: string; + } >; function markAccountToBeFetched( @@ -194,14 +200,27 @@ function markNetworkToBeFetched(graphqlEndpoint: string) { } function markActionsToBeFetched( publicKey: PublicKey, + actionHashes: ActionHashes, tokenId: Field, graphqlEndpoint: string ) { let publicKeyBase58 = publicKey.toBase58(); let tokenBase58 = TokenId.toBase58(tokenId); + let { fromActionHash, endActionHash } = actionHashes; + let fromActionHashBase58 = fromActionHash + ? fromActionHash.toString() + : undefined; + let endActionHashBase58 = endActionHash + ? endActionHash.toString() + : undefined; + actionsToFetch[`${publicKeyBase58};${tokenBase58};${graphqlEndpoint}`] = { publicKey: publicKeyBase58, tokenId: tokenBase58, + actionHashes: { + fromActionHash: fromActionHashBase58, + endActionHash: endActionHashBase58, + }, graphqlEndpoint, }; } @@ -220,9 +239,9 @@ async function fetchMissingData( } ); let actionPromises = Object.entries(actionsToFetch).map( - async ([key, { publicKey, tokenId }]) => { + async ([key, { publicKey, actionHashes, tokenId }]) => { let response = await fetchActions( - { publicKey, tokenId }, + { publicKey, actionHashes, tokenId }, archiveEndpoint ); if (!('error' in response) || response.error === undefined) @@ -555,7 +574,10 @@ type FetchedEvents = { }[]; }; type FetchedActions = { - actionState: string; + actionState: { + actionStateOne: string; + actionStateTwo: string; + }; actionData: { accountUpdateId: string; data: string[]; @@ -603,20 +625,24 @@ const getEventsQuery = ( }; const getActionsQuery = ( publicKey: string, + actionHashes: { fromActionHash?: string; endActionHash?: string }, tokenId: string, - filterOptions?: EventActionFilterOptions + _filterOptions?: EventActionFilterOptions ) => { - const { to, from } = filterOptions ?? {}; + const { fromActionHash, endActionHash } = actionHashes ?? {}; let input = `address: "${publicKey}", tokenId: "${tokenId}"`; - if (to !== undefined) { - input += `, to: ${to}`; + if (fromActionHash !== undefined) { + input += `, fromActionHash: "${fromActionHash}"`; } - if (from !== undefined) { - input += `, from: ${from}`; + if (endActionHash !== undefined) { + input += `, endActionHash: "${endActionHash}"`; } return `{ actions(input: { ${input} }) { - actionState + actionState { + actionStateOne + actionStateTwo + } actionData { accountUpdateId data @@ -703,20 +729,23 @@ async function fetchEvents( } async function fetchActions( - accountInfo: { publicKey: string; tokenId?: string }, - graphqlEndpoint = archiveGraphqlEndpoint, - filterOptions: EventActionFilterOptions = {} + accountInfo: { + publicKey: string; + actionHashes: { fromActionHash?: string; endActionHash?: string }; + tokenId?: string; + }, + graphqlEndpoint = archiveGraphqlEndpoint ) { if (!graphqlEndpoint) throw new Error( 'fetchEvents: Specified GraphQL endpoint is undefined. Please specify a valid endpoint.' ); - const { publicKey, tokenId } = accountInfo; + const { publicKey, actionHashes, tokenId } = accountInfo; let [response, error] = await makeGraphqlRequest( getActionsQuery( publicKey, - tokenId ?? TokenId.toBase58(TokenId.default), - filterOptions + actionHashes, + tokenId ?? TokenId.toBase58(TokenId.default) ), graphqlEndpoint ); @@ -744,10 +773,12 @@ async function fetchActions( // Archive Node API returns actions in the latest order, so we reverse the array to get the actions in chronological order. fetchedActions.reverse(); let actionsList: { actions: string[][]; hash: string }[] = []; - let latestActionsHash = SequenceEvents.emptySequenceState(); fetchedActions.forEach((fetchedAction) => { - const { actionState, actionData } = fetchedAction; + const { actionData } = fetchedAction; + let latestActionsHash = Field(fetchedAction.actionState.actionStateTwo); + let actionState = Field(fetchedAction.actionState.actionStateOne); + if (actionData.length === 0) throw new Error( `No action data was found for the account ${publicKey} with the latest action state ${actionState}` diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 832da16bb8..b2de9ab158 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -49,6 +49,7 @@ export { fetchEvents, getActions, FeePayerSpec, + ActionHashes, faucet, waitForFunding, getProofsEnabled, @@ -148,6 +149,11 @@ type DeprecatedFeePayerSpec = }) | undefined; +type ActionHashes = { + fromActionHash?: Field; + endActionHash?: Field; +}; + function reportGetAccountError(publicKey: string, tokenId: string) { if (tokenId === TokenId.toBase58(TokenId.default)) { return `getAccount: Could not find account for public key ${publicKey}`; @@ -340,6 +346,7 @@ interface Mina { ) => ReturnType; getActions: ( publicKey: PublicKey, + actionHashes: ActionHashes, tokenId?: Field ) => { hash: string; actions: string[][] }[]; proofsEnabled: boolean; @@ -574,6 +581,7 @@ function LocalBlockchain({ }, getActions( publicKey: PublicKey, + _actionHashes: ActionHashes, tokenId: Field = TokenId.default ): { hash: string; actions: string[][] }[] { return ( @@ -810,9 +818,18 @@ function Network(input: { mina: string; archive: string } | string): Mina { filterOptions ); }, - getActions(publicKey: PublicKey, tokenId: Field = TokenId.default) { + getActions( + publicKey: PublicKey, + actionHashes: ActionHashes, + tokenId: Field = TokenId.default + ) { if (currentTransaction()?.fetchMode === 'test') { - Fetch.markActionsToBeFetched(publicKey, tokenId, archiveEndpoint); + Fetch.markActionsToBeFetched( + publicKey, + actionHashes, + tokenId, + archiveEndpoint + ); let actions = Fetch.getCachedActions(publicKey, tokenId); return actions ?? []; } @@ -899,10 +916,14 @@ let activeInstance: Mina = { async transaction(sender: DeprecatedFeePayerSpec, f: () => void) { return createTransaction(sender, f, 0); }, - fetchEvents(publicKey: PublicKey, tokenId: Field = TokenId.default) { + fetchEvents(_publicKey: PublicKey, _tokenId: Field = TokenId.default) { throw Error('must call Mina.setActiveInstance first'); }, - getActions(publicKey: PublicKey, tokenId: Field = TokenId.default) { + getActions( + _publicKey: PublicKey, + _actionHashes: ActionHashes, + _tokenId: Field = TokenId.default + ) { throw Error('must call Mina.setActiveInstance first'); }, proofsEnabled: true, @@ -1048,8 +1069,12 @@ async function fetchEvents( /** * @return A list of emitted sequencing actions associated to the given public key. */ -function getActions(publicKey: PublicKey, tokenId?: Field) { - return activeInstance.getActions(publicKey, tokenId); +function getActions( + publicKey: PublicKey, + actionHashes: ActionHashes, + tokenId?: Field +) { + return activeInstance.getActions(publicKey, actionHashes, tokenId); } function getProofsEnabled() { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 401437f480..84ac02d1db 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1396,7 +1396,14 @@ Use the optional \`maxTransactionsWithActions\` argument to increase this number ? Ledger.fieldToBase58(endActionHash) : undefined; - let actions = Mina.getActions(contract.address, contract.self.tokenId); + let actions = Mina.getActions( + contract.address, + { + fromActionHash, + endActionHash, + }, + contract.self.tokenId + ); // gets the start/end indices of our array slice let startIndex = start From f5f4edc38173710a1ded8be3d2753a6f2da70dc4 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Tue, 4 Apr 2023 16:11:57 -0700 Subject: [PATCH 2/6] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index befb7417e6..d5e413c0f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - The internal event type now includes event data and transaction information as separate objects, allowing for more accurate information about each event and its associated transaction. - Removed multiple best tip blocks when fetching action data https://github.com/o1-labs/snarkyjs/pull/817 - Implemented a temporary fix that filters out multiple best tip blocks, if they exist, while fetching actions. This fix will be removed once the related issue in the Archive-Node-API repository (https://github.com/o1-labs/Archive-Node-API/issues/7) is resolved. +- New `fromActionHash` and `endActionHash` parameters for fetchActions function in SnarkyJS https://github.com/o1-labs/snarkyjs/pull/828 + - Allows fetching only necessary actions to compute the latest actions hash + - Eliminates the need to retrieve the entire actions history of a zkApp + - Utilizes `actionStateTwo` field returned by Archive Node API as a safe starting point for deriving the most recent action hash ## [0.9.5](https://github.com/o1-labs/snarkyjs/compare/21de489...4573252d) From 6aacbc1f516b565617f0de0afe1f2a56b88618b9 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Wed, 5 Apr 2023 09:14:32 -0700 Subject: [PATCH 3/6] Refactor to use actionStates instead of actionHashes --- CHANGELOG.md | 4 +-- src/lib/fetch.ts | 49 ++++++++++++++++++---------------- src/lib/mina.ts | 69 +++++++++++++++++++++++++++++++++++++----------- src/lib/zkapp.ts | 37 +++++--------------------- 4 files changed, 88 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e413c0f7..0c87c20b08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,8 +30,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - The internal event type now includes event data and transaction information as separate objects, allowing for more accurate information about each event and its associated transaction. - Removed multiple best tip blocks when fetching action data https://github.com/o1-labs/snarkyjs/pull/817 - Implemented a temporary fix that filters out multiple best tip blocks, if they exist, while fetching actions. This fix will be removed once the related issue in the Archive-Node-API repository (https://github.com/o1-labs/Archive-Node-API/issues/7) is resolved. -- New `fromActionHash` and `endActionHash` parameters for fetchActions function in SnarkyJS https://github.com/o1-labs/snarkyjs/pull/828 - - Allows fetching only necessary actions to compute the latest actions hash +- New `fromActionState` and `endActionState` parameters for fetchActions function in SnarkyJS https://github.com/o1-labs/snarkyjs/pull/828 + - Allows fetching only necessary actions to compute the latest actions state - Eliminates the need to retrieve the entire actions history of a zkApp - Utilizes `actionStateTwo` field returned by Archive Node API as a safe starting point for deriving the most recent action hash diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index e8873acf7e..38843b2b1d 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -5,7 +5,7 @@ import { SequenceEvents, TokenId } from './account_update.js'; import { PublicKey } from './signature.js'; import { NetworkValue } from './precondition.js'; import { Types } from '../provable/types.js'; -import { ActionHashes } from './mina.js'; +import { ActionStates } from './mina.js'; import * as Encoding from './encoding.js'; import { Account, @@ -140,6 +140,9 @@ type FetchError = { statusCode: number; statusText: string; }; +type ActionStatesStringified = { + [K in keyof ActionStates]: string; +}; // Specify 30s as the default timeout const defaultTimeout = 30000; @@ -177,7 +180,7 @@ let actionsToFetch = {} as Record< { publicKey: string; tokenId: string; - actionHashes: { fromActionHash?: string; endActionHash?: string }; + actionStates: ActionStatesStringified; graphqlEndpoint: string; } >; @@ -200,26 +203,26 @@ function markNetworkToBeFetched(graphqlEndpoint: string) { } function markActionsToBeFetched( publicKey: PublicKey, - actionHashes: ActionHashes, tokenId: Field, - graphqlEndpoint: string + graphqlEndpoint: string, + actionStates: ActionStates = {} ) { let publicKeyBase58 = publicKey.toBase58(); let tokenBase58 = TokenId.toBase58(tokenId); - let { fromActionHash, endActionHash } = actionHashes; - let fromActionHashBase58 = fromActionHash - ? fromActionHash.toString() + let { fromActionState, endActionState } = actionStates; + let fromActionStateBase58 = fromActionState + ? fromActionState.toString() : undefined; - let endActionHashBase58 = endActionHash - ? endActionHash.toString() + let endActionStateBase58 = endActionState + ? endActionState.toString() : undefined; actionsToFetch[`${publicKeyBase58};${tokenBase58};${graphqlEndpoint}`] = { publicKey: publicKeyBase58, tokenId: tokenBase58, - actionHashes: { - fromActionHash: fromActionHashBase58, - endActionHash: endActionHashBase58, + actionStates: { + fromActionState: fromActionStateBase58, + endActionState: endActionStateBase58, }, graphqlEndpoint, }; @@ -239,9 +242,9 @@ async function fetchMissingData( } ); let actionPromises = Object.entries(actionsToFetch).map( - async ([key, { publicKey, actionHashes, tokenId }]) => { + async ([key, { publicKey, actionStates, tokenId }]) => { let response = await fetchActions( - { publicKey, actionHashes, tokenId }, + { publicKey, actionStates, tokenId }, archiveEndpoint ); if (!('error' in response) || response.error === undefined) @@ -628,17 +631,17 @@ const getEventsQuery = ( }; const getActionsQuery = ( publicKey: string, - actionHashes: { fromActionHash?: string; endActionHash?: string }, + actionStates: ActionStatesStringified, tokenId: string, _filterOptions?: EventActionFilterOptions ) => { - const { fromActionHash, endActionHash } = actionHashes ?? {}; + const { fromActionState, endActionState } = actionStates ?? {}; let input = `address: "${publicKey}", tokenId: "${tokenId}"`; - if (fromActionHash !== undefined) { - input += `, fromActionHash: "${fromActionHash}"`; + if (fromActionState !== undefined) { + input += `, fromActionState: "${fromActionState}"`; } - if (endActionHash !== undefined) { - input += `, endActionHash: "${endActionHash}"`; + if (endActionState !== undefined) { + input += `, endActionHash: "${endActionState}"`; } return `{ actions(input: { ${input} }) { @@ -739,7 +742,7 @@ async function fetchEvents( async function fetchActions( accountInfo: { publicKey: string; - actionHashes: { fromActionHash?: string; endActionHash?: string }; + actionStates: ActionStatesStringified; tokenId?: string; }, graphqlEndpoint = archiveGraphqlEndpoint @@ -748,11 +751,11 @@ async function fetchActions( throw new Error( 'fetchEvents: Specified GraphQL endpoint is undefined. Please specify a valid endpoint.' ); - const { publicKey, actionHashes, tokenId } = accountInfo; + const { publicKey, actionStates, tokenId } = accountInfo; let [response, error] = await makeGraphqlRequest( getActionsQuery( publicKey, - actionHashes, + actionStates, tokenId ?? TokenId.toBase58(TokenId.default) ), graphqlEndpoint diff --git a/src/lib/mina.ts b/src/lib/mina.ts index 1c3785277e..fcdd45e6aa 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -49,7 +49,7 @@ export { fetchEvents, getActions, FeePayerSpec, - ActionHashes, + ActionStates, faucet, waitForFunding, getProofsEnabled, @@ -149,9 +149,9 @@ type DeprecatedFeePayerSpec = }) | undefined; -type ActionHashes = { - fromActionHash?: Field; - endActionHash?: Field; +type ActionStates = { + fromActionState?: Field; + endActionState?: Field; }; function reportGetAccountError(publicKey: string, tokenId: string) { @@ -346,7 +346,7 @@ interface Mina { ) => ReturnType; getActions: ( publicKey: PublicKey, - actionHashes: ActionHashes, + actionStates?: ActionStates, tokenId?: Field ) => { hash: string; actions: string[][] }[]; proofsEnabled: boolean; @@ -588,12 +588,51 @@ function LocalBlockchain({ }, getActions( publicKey: PublicKey, - _actionHashes: ActionHashes, + actionStates?: ActionStates, tokenId: Field = TokenId.default ): { hash: string; actions: string[][] }[] { - return ( - actions?.[publicKey.toBase58()]?.[Ledger.fieldToBase58(tokenId)] ?? [] - ); + let { fromActionState, endActionState } = actionStates ?? {}; + let actionsForAccount: { hash: string; actions: string[][] }[] = []; + + Circuit.asProver(() => { + // if the fromActionState is the empty state, we fetch all events + fromActionState = fromActionState + ?.equals(SequenceEvents.emptySequenceState()) + .toBoolean() + ? undefined + : fromActionState; + + // used to determine start and end values in string + let start: string | undefined = fromActionState + ? Ledger.fieldToBase58(fromActionState) + : undefined; + let end: string | undefined = endActionState + ? Ledger.fieldToBase58(endActionState) + : undefined; + + let currentActions = + actions?.[publicKey.toBase58()]?.[Ledger.fieldToBase58(tokenId)] ?? + []; + + // gets the start/end indices of our array slice + let startIndex = start + ? currentActions.findIndex( + (e: { hash: string; actions: string[] }) => e.hash === start + ) + 1 + : 0; + + let endIndex = end + ? currentActions.findIndex( + (e: { hash: string; actions: string[] }) => e.hash === end + ) + 1 + : undefined; + + actionsForAccount = actions.slice( + startIndex, + endIndex === 0 ? undefined : endIndex + ); + }); + return actionsForAccount; }, addAccount, /** @@ -827,15 +866,15 @@ function Network(input: { mina: string; archive: string } | string): Mina { }, getActions( publicKey: PublicKey, - actionHashes: ActionHashes, + actionStates?: ActionStates, tokenId: Field = TokenId.default ) { if (currentTransaction()?.fetchMode === 'test') { Fetch.markActionsToBeFetched( publicKey, - actionHashes, tokenId, - archiveEndpoint + archiveEndpoint, + actionStates ); let actions = Fetch.getCachedActions(publicKey, tokenId); return actions ?? []; @@ -928,7 +967,7 @@ let activeInstance: Mina = { }, getActions( _publicKey: PublicKey, - _actionHashes: ActionHashes, + _actionStates?: ActionStates, _tokenId: Field = TokenId.default ) { throw Error('must call Mina.setActiveInstance first'); @@ -1078,10 +1117,10 @@ async function fetchEvents( */ function getActions( publicKey: PublicKey, - actionHashes: ActionHashes, + actionStates: ActionStates, tokenId?: Field ) { - return activeInstance.getActions(publicKey, actionHashes, tokenId); + return activeInstance.getActions(publicKey, actionStates, tokenId); } function getProofsEnabled() { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index d55205f3d9..673f8c6f50 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1399,49 +1399,24 @@ Use the optional \`maxTransactionsWithActions\` argument to increase this number }): A[][] { let actionsForAccount: A[][] = []; Circuit.asProver(() => { - // if the fromActionHash is the empty state, we fetch all events - fromActionHash = fromActionHash - ?.equals(SequenceEvents.emptySequenceState()) - .toBoolean() - ? undefined - : fromActionHash; - - // used to determine start and end values in string - let start: string | undefined = fromActionHash - ? Ledger.fieldToBase58(fromActionHash) - : undefined; - let end: string | undefined = endActionHash - ? Ledger.fieldToBase58(endActionHash) - : undefined; - let actions = Mina.getActions( contract.address, { - fromActionHash, - endActionHash, + fromActionState: fromActionHash, + endActionState: endActionHash, }, contract.self.tokenId ); - // gets the start/end indices of our array slice - let startIndex = start - ? actions.findIndex((e) => e.hash === start) + 1 - : 0; - let endIndex = end - ? actions.findIndex((e) => e.hash === end) + 1 - : undefined; - - // slices the array so we only get the wanted range between fromActionHash and endActionHash - actionsForAccount = actions - .slice(startIndex, endIndex === 0 ? undefined : endIndex) - .map((event: { hash: string; actions: string[][] }) => - // putting our string-Fields back into the original action type + // putting our string-Fields back into the original action type + actionsForAccount = actions.map( + (event: { hash: string; actions: string[][] }) => event.actions.map((action: string[]) => (reducer.actionType as ProvablePure).fromFields( action.map((fieldAsString: string) => Field(fieldAsString)) ) ) - ); + ); }); return actionsForAccount; From 48ecbd8a7489e2bc383c7dac91baf4b637a1a247 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Wed, 5 Apr 2023 09:45:19 -0700 Subject: [PATCH 4/6] Fix broken voting test --- src/lib/mina.ts | 67 ++++++++++++++++++++---------------------------- src/lib/zkapp.ts | 2 +- 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/lib/mina.ts b/src/lib/mina.ts index fcdd45e6aa..c490bd7e16 100644 --- a/src/lib/mina.ts +++ b/src/lib/mina.ts @@ -591,48 +591,37 @@ function LocalBlockchain({ actionStates?: ActionStates, tokenId: Field = TokenId.default ): { hash: string; actions: string[][] }[] { + let currentActions: { hash: string; actions: string[][] }[] = + actions?.[publicKey.toBase58()]?.[Ledger.fieldToBase58(tokenId)] ?? []; let { fromActionState, endActionState } = actionStates ?? {}; - let actionsForAccount: { hash: string; actions: string[][] }[] = []; - - Circuit.asProver(() => { - // if the fromActionState is the empty state, we fetch all events - fromActionState = fromActionState - ?.equals(SequenceEvents.emptySequenceState()) - .toBoolean() - ? undefined - : fromActionState; - - // used to determine start and end values in string - let start: string | undefined = fromActionState - ? Ledger.fieldToBase58(fromActionState) - : undefined; - let end: string | undefined = endActionState - ? Ledger.fieldToBase58(endActionState) - : undefined; - - let currentActions = - actions?.[publicKey.toBase58()]?.[Ledger.fieldToBase58(tokenId)] ?? - []; - - // gets the start/end indices of our array slice - let startIndex = start - ? currentActions.findIndex( - (e: { hash: string; actions: string[] }) => e.hash === start - ) + 1 - : 0; - - let endIndex = end - ? currentActions.findIndex( - (e: { hash: string; actions: string[] }) => e.hash === end - ) + 1 - : undefined; - - actionsForAccount = actions.slice( + + fromActionState = fromActionState + ?.equals(SequenceEvents.emptySequenceState()) + .toBoolean() + ? undefined + : fromActionState; + + // used to determine start and end values in string + let start: string | undefined = fromActionState + ? Ledger.fieldToBase58(fromActionState) + : undefined; + let end: string | undefined = endActionState + ? Ledger.fieldToBase58(endActionState) + : undefined; + + let startIndex = start + ? currentActions.findIndex((e) => e.hash === start) + 1 + : 0; + let endIndex = end + ? currentActions.findIndex((e) => e.hash === end) + 1 + : undefined; + + return ( + currentActions?.slice( startIndex, endIndex === 0 ? undefined : endIndex - ); - }); - return actionsForAccount; + ) ?? [] + ); }, addAccount, /** diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index 673f8c6f50..bacc8949ed 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1408,9 +1408,9 @@ Use the optional \`maxTransactionsWithActions\` argument to increase this number contract.self.tokenId ); - // putting our string-Fields back into the original action type actionsForAccount = actions.map( (event: { hash: string; actions: string[][] }) => + // putting our string-Fields back into the original action type event.actions.map((action: string[]) => (reducer.actionType as ProvablePure).fromFields( action.map((fieldAsString: string) => Field(fieldAsString)) From 5314b1b7bdcfb134c7581e0e3912d6c2005241ea Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Wed, 5 Apr 2023 12:44:37 -0700 Subject: [PATCH 5/6] Rename all occurences of fromActionHash & endActionHash to use state instead --- src/examples/zkapps/reducer/reducer.ts | 2 +- .../zkapps/reducer/reducer_composite.ts | 2 +- src/examples/zkapps/voting/membership.ts | 4 ++-- src/examples/zkapps/voting/voting.ts | 2 +- src/lib/fetch.ts | 2 +- src/lib/zkapp.ts | 24 +++++++++---------- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/examples/zkapps/reducer/reducer.ts b/src/examples/zkapps/reducer/reducer.ts index af2f9fa513..e80f44c023 100644 --- a/src/examples/zkapps/reducer/reducer.ts +++ b/src/examples/zkapps/reducer/reducer.ts @@ -39,7 +39,7 @@ class CounterZkapp extends SmartContract { // compute the new counter and hash from pending actions let pendingActions = this.reducer.getActions({ - fromActionHash: actionsHash, + fromActionState: actionsHash, }); let { state: newCounter, actionsHash: newActionsHash } = diff --git a/src/examples/zkapps/reducer/reducer_composite.ts b/src/examples/zkapps/reducer/reducer_composite.ts index d12e1bb8d2..8116d71371 100644 --- a/src/examples/zkapps/reducer/reducer_composite.ts +++ b/src/examples/zkapps/reducer/reducer_composite.ts @@ -50,7 +50,7 @@ class CounterZkapp extends SmartContract { // compute the new counter and hash from pending actions let pendingActions = this.reducer.getActions({ - fromActionHash: actionsHash, + fromActionState: actionsHash, }); let { state: newCounter, actionsHash: newActionsHash } = diff --git a/src/examples/zkapps/voting/membership.ts b/src/examples/zkapps/voting/membership.ts index 3407ff4df2..553cc71924 100644 --- a/src/examples/zkapps/voting/membership.ts +++ b/src/examples/zkapps/voting/membership.ts @@ -115,7 +115,7 @@ export class Membership_ extends SmartContract { // checking if the member already exists within the accumulator let { state: exists } = this.reducer.reduce( this.reducer.getActions({ - fromActionHash: accumulatedMembers, + fromActionState: accumulatedMembers, }), Bool, (state: Bool, action: Member) => { @@ -168,7 +168,7 @@ export class Membership_ extends SmartContract { this.committedMembers.assertEquals(committedMembers); let pendingActions = this.reducer.getActions({ - fromActionHash: accumulatedMembers, + fromActionState: accumulatedMembers, }); let { state: newCommittedMembers, actionsHash: newAccumulatedMembers } = diff --git a/src/examples/zkapps/voting/voting.ts b/src/examples/zkapps/voting/voting.ts index ebb12fd8ea..b66f54f0ad 100644 --- a/src/examples/zkapps/voting/voting.ts +++ b/src/examples/zkapps/voting/voting.ts @@ -273,7 +273,7 @@ export class Voting_ extends SmartContract { let { state: newCommittedVotes, actionsHash: newAccumulatedVotes } = this.reducer.reduce( - this.reducer.getActions({ fromActionHash: accumulatedVotes }), + this.reducer.getActions({ fromActionState: accumulatedVotes }), Field, (state: Field, action: Member) => { // apply one vote diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index 38843b2b1d..927d4065bc 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -641,7 +641,7 @@ const getActionsQuery = ( input += `, fromActionState: "${fromActionState}"`; } if (endActionState !== undefined) { - input += `, endActionHash: "${endActionState}"`; + input += `, endActionState: "${endActionState}"`; } return `{ actions(input: { ${input} }) { diff --git a/src/lib/zkapp.ts b/src/lib/zkapp.ts index bacc8949ed..dcf9abf43f 100644 --- a/src/lib/zkapp.ts +++ b/src/lib/zkapp.ts @@ -1262,7 +1262,7 @@ type ReducerReturn = { * * ```ts * let pendingActions = this.reducer.getActions({ - * fromActionHash: actionsHash, + * fromActionState: actionsHash, * }); * * let { state: newState, actionsHash: newActionsHash } = @@ -1288,16 +1288,16 @@ type ReducerReturn = { * Fetches the list of previously emitted {@link Action}s by this {@link SmartContract}. * ```ts * let pendingActions = this.reducer.getActions({ - * fromActionHash: actionsHash, + * fromActionState: actionsHash, * }); * ``` */ getActions({ - fromActionHash, - endActionHash, + fromActionState, + endActionState, }: { - fromActionHash?: Field; - endActionHash?: Field; + fromActionState?: Field; + endActionState?: Field; }): Action[][]; }; @@ -1391,19 +1391,19 @@ Use the optional \`maxTransactionsWithActions\` argument to increase this number return { state, actionsHash }; }, getActions({ - fromActionHash, - endActionHash, + fromActionState, + endActionState, }: { - fromActionHash?: Field; - endActionHash?: Field; + fromActionState?: Field; + endActionState?: Field; }): A[][] { let actionsForAccount: A[][] = []; Circuit.asProver(() => { let actions = Mina.getActions( contract.address, { - fromActionState: fromActionHash, - endActionState: endActionHash, + fromActionState, + endActionState, }, contract.self.tokenId ); From 4f9870ae3b33e4096eba1400120bb683a9196cb6 Mon Sep 17 00:00:00 2001 From: Martin Minkov Date: Wed, 5 Apr 2023 12:47:39 -0700 Subject: [PATCH 6/6] Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c87c20b08..fa06f42868 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Improve number of constraints needed for Merkle tree hashing https://github.com/o1-labs/snarkyjs/pull/820 - This breaks deployed zkApps which use `MerkleWitness.calculateRoot()`, because the circuit is changed - You can make your existing contracts compatible again by switching to `MerkleWitness.calculateRootSlow()`, which has the old circuit +- Renamed Function Parameters: The `getAction` function now accepts a new object structure for its parameters. https://github.com/o1-labs/snarkyjs/pull/828 + - The previous object keys, `fromActionHash` and `endActionHash`, have been replaced by `fromActionState` and `endActionState`. ### Fixed