Skip to content

Commit

Permalink
Fetch many blocks at once
Browse files Browse the repository at this point in the history
Signed-off-by: lovesh <[email protected]>
  • Loading branch information
lovesh committed Jun 17, 2024
1 parent 8cbce41 commit 050e780
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 37 deletions.
104 changes: 67 additions & 37 deletions src/modules/accumulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { hexToU8a, isHex, u8aToHex } from '@polkadot/util';
import { KBUniversalAccumulatorValue, VBWitnessUpdateInfo } from '@docknetwork/crypto-wasm-ts';
import { getDidNonce, getStateChange } from '../utils/misc';
import { getDidNonce, getStateChange, inclusiveRange } from '../utils/misc';
import WithParamsAndPublicKeys from './WithParamsAndPublicKeys';
import { getAllExtrinsicsFromBlock } from '../utils/chain-ops';
import { createDidSig, typedHexDID } from '../utils/did';
Expand Down Expand Up @@ -799,40 +799,66 @@ export default class AccumulatorModule extends WithParamsAndPublicKeys {
blockNoOrBlockHash,
false,
);
const updates = [];
extrinsics.forEach((ext) => {
if (
ext.method
&& ext.method.section === 'accumulator'
&& ext.method.method === 'updateAccumulator'
) {
const update = this.api.createType(
'UpdateAccumulator',
ext.method.args[0],
);
if (u8aToHex(update.id) === accumulatorId) {
// The following commented line produces truncated hex strings. Don't know why
// const additions = update.additions.isSome ? update.additions.unwrap().map(u8aToHex) : null;
const additions = update.additions.isSome
? update.additions.unwrap().map((i) => u8aToHex(i))
: null;
const removals = update.removals.isSome
? update.removals.unwrap().map((i) => u8aToHex(i))
: null;
const witnessUpdateInfo = update.witnessUpdateInfo.isSome
? u8aToHex(update.witnessUpdateInfo.unwrap())
: null;

updates.push({
newAccumulated: u8aToHex(update.newAccumulated),
additions,
removals,
witnessUpdateInfo,
});
}
return extrinsics.map((e) => this.getUpdatesFromExtrinsic(e, accumulatorId)).filter((u) => u !== undefined);
}

/**
* Fetch blocks corresponding to the given block numbers or hashes and get all accumulator updates made in those blocks' extrinsics corresponding to accumulator id `accumulatorId`
* @param accumulatorId
* @param blockNosOrBlockHashes {number[]|string[]}
* @returns {Promise<object[]>} - Resolves to an array of `update`s where each `update` is an object with keys
* `newAccumulated`, `additions`, `removals` and `witnessUpdateInfo`. The last keys have value null if they were
* not provided in the extrinsic.
*/
async getUpdatesFromBlocks(accumulatorId, blockNosOrBlockHashes) {
// NOTE: polkadot-js doesn't allow to fetch more than one block in 1 RPC call.
const extrinsics = await Promise.all(blockNosOrBlockHashes.map(async (b) => await getAllExtrinsicsFromBlock(
this.api,
b,
false,
)));
return extrinsics.flat().map((e) => this.getUpdatesFromExtrinsic(e, accumulatorId)).filter((u) => u !== undefined);
}

/**
* Get accumulator updates corresponding to accumulator id `accumulatorId`
* @param ext
* @param accumulatorId
* @returns {Promise<object|undefined>} - Resolves to an `update` object with keys `newAccumulated`, `additions`, `removals` and `witnessUpdateInfo`.
* The last keys have value null if they were not provided in the extrinsic.
*/
getUpdatesFromExtrinsic(ext, accumulatorId) {
if (
ext.method
&& ext.method.section === 'accumulator'
&& ext.method.method === 'updateAccumulator'
) {
const update = this.api.createType(
'UpdateAccumulator',
ext.method.args[0],
);
if (u8aToHex(update.id) === accumulatorId) {
// The following commented line produces truncated hex strings. Don't know why
// const additions = update.additions.isSome ? update.additions.unwrap().map(u8aToHex) : null;
const additions = update.additions.isSome
? update.additions.unwrap().map((i) => u8aToHex(i))
: null;
const removals = update.removals.isSome
? update.removals.unwrap().map((i) => u8aToHex(i))
: null;
const witnessUpdateInfo = update.witnessUpdateInfo.isSome
? u8aToHex(update.witnessUpdateInfo.unwrap())
: null;

return {
newAccumulated: u8aToHex(update.newAccumulated),
additions,
removals,
witnessUpdateInfo,
};
}
});
return updates;
}
return undefined;
}

/**
Expand Down Expand Up @@ -934,20 +960,24 @@ export default class AccumulatorModule extends WithParamsAndPublicKeys {
* @param witness - this will be updated to the latest witness
* @param startBlock - block number to start from
* @param endBlock - block number to end at. If not specified, it will pick the `lastUpdated` field of the accumulator.
* @param batchSize - the number of blocks to fetch in one go
* @returns {Promise<void>}
*/
// eslint-disable-next-line sonarjs/cognitive-complexity
async updateVbAccumulatorWitnessFromUpdatesInBlocks(accumulatorId, member, witness, startBlock, endBlock = undefined) {
async updateVbAccumulatorWitnessFromUpdatesInBlocks(accumulatorId, member, witness, startBlock, endBlock = undefined, batchSize = 10) {
if (endBlock === undefined) {
const accum = await this.accumulatorModule.getAccumulator(accumulatorId, false);
// eslint-disable-next-line no-param-reassign
endBlock = accum.lastModified;
}
// If endBlock < startBlock, it won't throw an error but won't fetch any updates and witness won't be updated.
console.debug(`Will start updating witness from block ${startBlock} to ${endBlock}`);
let current = startBlock;
while (current <= endBlock) {
const till = (current + batchSize) <= endBlock ? (current + batchSize) : endBlock;
// Get updates from blocks [current, current + 1, current + 2, ..., till]
// eslint-disable-next-line no-await-in-loop
const updates = await this.getUpdatesFromBlock(accumulatorId, current);
const updates = await this.getUpdatesFromBlocks(accumulatorId, inclusiveRange(current, till, 1));
for (const update of updates) {
const additions = [];
const removals = [];
Expand All @@ -966,7 +996,7 @@ export default class AccumulatorModule extends WithParamsAndPublicKeys {

witness.updateUsingPublicInfoPostBatchUpdate(member, additions, removals, queriedWitnessInfo);
}
current++;
current = till + 1;
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/utils/misc.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,12 @@ export async function getDidNonce(
* @param value
*/
export const ensureMatchesPattern = (pattern, value) => new PatternMatcher().check(pattern, value);

/**
* Get a list of numbers in the range [start, stop], i.e. both are inclusive. Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#sequence_generator_range
* @param start
* @param stop
* @param step
* @returns {number[]}
*/
export const inclusiveRange = (start, stop, step) => Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step);
16 changes: 16 additions & 0 deletions tests/integration/anoncreds/prefilled-positive-accumulator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
AccumulatorPublicKey,
PositiveAccumulator,
VBWitnessUpdateInfo,
VBMembershipWitness,
} from '@docknetwork/crypto-wasm-ts';
import { randomAsHex } from '@polkadot/util-crypto';
import { InMemoryState } from '@docknetwork/crypto-wasm-ts/lib/accumulator/in-memory-persistence';
Expand Down Expand Up @@ -229,11 +230,26 @@ describe('Prefilled positive accumulator', () => {
// Old witness doesn't verify with new accumulator
expect(verifAccumulator.verifyMembershipWitness(member, witness, accumPk, accumParams)).toEqual(false);

const oldWitness1 = new VBMembershipWitness(witness.value);
const oldWitness2 = new VBMembershipWitness(witness.value);
const oldWitness3 = new VBMembershipWitness(witness.value);

// Update witness by downloading necessary blocks and applying the updates if found
await dock.accumulatorModule.updateVbAccumulatorWitnessFromUpdatesInBlocks(accumulatorId, member, witness, blockNoToUpdateFrom, queriedAccum.lastModified);

// Updated witness verifies with new accumulator
expect(verifAccumulator.verifyMembershipWitness(member, witness, accumPk, accumParams)).toEqual(true);

// Test again with a batch size bigger than the total number of blocks
await dock.accumulatorModule.updateVbAccumulatorWitnessFromUpdatesInBlocks(accumulatorId, member, oldWitness1, blockNoToUpdateFrom, queriedAccum.lastModified, queriedAccum.lastModified - blockNoToUpdateFrom + 10);
expect(verifAccumulator.verifyMembershipWitness(member, oldWitness1, accumPk, accumParams)).toEqual(true);

// Test again with few other batch sizes
await dock.accumulatorModule.updateVbAccumulatorWitnessFromUpdatesInBlocks(accumulatorId, member, oldWitness2, blockNoToUpdateFrom, queriedAccum.lastModified, 3);
expect(verifAccumulator.verifyMembershipWitness(member, oldWitness2, accumPk, accumParams)).toEqual(true);

await dock.accumulatorModule.updateVbAccumulatorWitnessFromUpdatesInBlocks(accumulatorId, member, oldWitness3, blockNoToUpdateFrom, queriedAccum.lastModified, 4);
expect(verifAccumulator.verifyMembershipWitness(member, oldWitness3, accumPk, accumParams)).toEqual(true);
}, 60000);

afterAll(async () => {
Expand Down

0 comments on commit 050e780

Please sign in to comment.