'use strict';

var inMemoryPersistence = require('@docknetwork/crypto-wasm-ts/lib/accumulator/in-memory-persistence');
require('../../utils/types/array.cjs');
var utils_types_bytes = require('../../utils/types/bytes.cjs');
require('bs58');
require('multiformats/bases/base58');
require('varint');
require('../../utils/encoding/ss-58.cjs');
var index_js = require('@docknetwork/crypto-wasm-ts/lib/index.js');
var modules_abstract_accumulator_module = require('../abstract/accumulator/module.cjs');
require('../../types/blob/index.cjs');
require('../../types/attest/index.cjs');
require('../../types/did/index.cjs');
require('../../types/generic/typed-enum.cjs');
require('../../types/generic/typed-bytes.cjs');
require('../../types/generic/typed-string.cjs');
require('../../types/generic/typed-array.cjs');
require('../../types/generic/typed-tuple.cjs');
require('../../types/generic/typed-null.cjs');
require('../../types/generic/typed-number.cjs');
require('../../types/generic/typed-set.cjs');
require('../../types/generic/typed-map.cjs');
require('../../types/generic/typed-struct.cjs');
require('uuid');
require('js-sha256');
require('../../types/offchain-signatures/params/index.cjs');
require('../../types/offchain-signatures/public-keys/index.cjs');
require('../../types/offchain-signatures/curve-type.cjs');
require('../../types/payload/cheqd.cjs');
require('../../types/policy/index.cjs');
require('../../types/public-keys/public-key.cjs');
require('../../types/public-keys/public-key-sr25519-value.cjs');
require('../../types/public-keys/public-key-ed25519-value.cjs');
require('../../types/public-keys/public-key-secp256k1-value.cjs');
require('../../types/public-keys/public-key-x25519-value.cjs');
require('../../types/signatures/signature-value.cjs');
require('../../types/signatures/signature.cjs');
require('../../types/status-list-credential/index.cjs');
require('../../types/anchor/index.cjs');
require('../../types/accumulator/keys.cjs');
var types_accumulator_params = require('../../types/accumulator/params.cjs');
require('../../types/accumulator/public-key.cjs');
require('../../types/accumulator/accumulator.cjs');
require('../../types/accumulator/id.cjs');
require('../../types/trust-registry/id.cjs');
require('../../keypairs/dock-keypair.cjs');
var keypairs_keypairEd25519 = require('../../keypairs/keypair-ed25519.cjs');
require('../../keypairs/keypair-secp256k1.cjs');
var keypairs_didKeypair = require('../../keypairs/did-keypair.cjs');
var types_did_document_index = require('../../types/did/document/index.cjs');
require('../../utils/inheritance.cjs');
require('../../utils/types/object.cjs');
require('../../utils/types/iterable.cjs');
require('../../utils/types/set.cjs');
require('crypto');
require('../../utils/interfaces.cjs');
require('../../utils/assert.cjs');
require('../../utils/error.cjs');
require('../../utils/types/string.cjs');
require('blake2b');
require('@docknetwork/crypto-wasm-ts');
require('../abstract/common/abstract-api-provider.cjs');
require('../abstract/common/abstract-base-module.cjs');
require('../abstract/common/with-abstract-params.cjs');
require('../abstract/common/with-abstract-public-keys.cjs');
require('../../types/generic/with-prop.cjs');
require('../../types/generic/with-props.cjs');
require('../../types/generic/with-base.cjs');
require('../../types/generic/with-catch-null.cjs');
require('../../types/generic/with-eq.cjs');
require('../../types/generic/any-of.cjs');
require('../../types/generic/with-from.cjs');
require('../../types/generic/with-from-json-bytes.cjs');
require('../../types/generic/create-placeholder.cjs');
require('../../types/blob/id.cjs');
require('../../types/generic/sized.cjs');
require('../../utils/types/number.cjs');
require('../../types/generic/typed-uuid.cjs');
require('../../types/generic/with-qualifier.cjs');
require('../../types/generic/with-from-dock-id.cjs');
require('../../types/did/onchain/typed-did/index.cjs');
require('../../types/did/onchain/typed-did/did-method-key/did-method-key-public-key.cjs');
require('../../utils/encoding/base-58-btc.cjs');
require('../../types/did/onchain/constants.cjs');
require('../../types/did/onchain/typed-did/signature.cjs');
require('../../types/did/onchain/typed-did/did-method-key/did-method-key-signature.cjs');
require('../../types/signatures/signature-ed25519-value.cjs');
require('../../vc/crypto/constants.cjs');
require('../../types/signatures/signature-secp256k1-value.cjs');
require('../../types/signatures/signature-sr25519-value.cjs');
require('../../types/did/onchain/typed-did/dock-did-value.cjs');
require('../../types/generic/utils.cjs');
require('../../types/generic/typed-bytes-array.cjs');
require('../../types/did/onchain/typed-did/cheqd-did.cjs');
require('../../types/blob/const.cjs');
require('../../utils/mappings/dock-did-by-id.cjs');
require('../../types/did/offchain/index.cjs');
require('../../types/did/offchain/doc-ref.cjs');
require('../../types/did/onchain/index.cjs');
require('../../types/did/onchain/controllers.cjs');
require('../../types/did/onchain/verification-relationship.cjs');
require('../../types/did/onchain/did-key.cjs');
require('../../vc/crypto/Bls12381BBDT16KeyPairDock2024.cjs');
require('../../vc/crypto/common/DockCryptoKeyPair.cjs');
require('../../vc/crypto/Bls12381BBDT16MACDock2024.cjs');
require('../../vc/crypto/common/DockCryptoSignature.cjs');
require('jsonld-signatures');
require('json-stringify-deterministic');
require('semver/preload.js');
require('../../vc/crypto/common/CustomLinkedDataSignature.cjs');
require('base64url');
require('jsonld');
require('../../types/did/document/verification-method-ref.cjs');
require('../../vc/jws.cjs');
require('../../vc/crypto/Bls12381BBDT16MACProofDock2024.cjs');
require('../../vc/crypto/common/DockCryptoSignatureProof.cjs');
require('../../vc/crypto/Bls12381BBSKeyPairDock2023.cjs');
require('../../vc/crypto/Bls12381BBSSignatureDock2022.cjs');
require('../../vc/crypto/Bls12381G2KeyPairDock2022.cjs');
require('../../vc/crypto/Bls12381BBSSignatureDock2023.cjs');
require('../../vc/crypto/Bls12381BBSSignatureProofDock2022.cjs');
require('../../vc/crypto/Bls12381BBSSignatureProofDock2023.cjs');
require('../../vc/crypto/Bls12381BDDT16KeyPairDock2024.cjs');
require('../../vc/crypto/Bls12381BDDT16MACDock2024.cjs');
require('../../vc/crypto/Bls12381BDDT16MACProofDock2024.cjs');
require('../../vc/crypto/Bls12381PSKeyPairDock2023.cjs');
require('../../vc/crypto/Bls12381PSSignatureDock2023.cjs');
require('../../vc/crypto/Bls12381PSSignatureProofDock2023.cjs');
require('@juanelas/base64');
require('@stablelib/ed25519');
require('@transmute/json-web-signature');
require('../../types/offchain-signatures/public-keys/value.cjs');
require('../../types/generic/option.cjs');
require('../../types/offchain-signatures/public-keys/ref.cjs');
require('../../types/offchain-signatures/params/id.cjs');
require('../../types/offchain-signatures/params/value.cjs');
require('../../types/offchain-signatures/public-keys/id.cjs');
require('elliptic');
require('../../types/did/onchain/verification-method-signature.cjs');
require('../../types/did/document/verification-method-ref-or-ident-ref.cjs');
require('../../types/did/document/ident-ref.cjs');
require('../../types/did/document/service-endpoint.cjs');
require('../../types/did/document/verification-method.cjs');
require('../../utils/encoding/base-58.cjs');
require('../../types/generic/with-base58.cjs');
require('../../types/generic/with-base58btc.cjs');
require('../../types/generic/with-multibase.cjs');
require('../../utils/encoding/multibase.cjs');
require('../../types/did/document/verification-method-type.cjs');
require('../../types/did/document/verification-method-ref-or-cheqd-verification-method.cjs');
require('../../types/did/document/const.cjs');
require('../../types/blob/blob.cjs');
require('../../types/policy/one-of-policy-value.cjs');
require('../../types/status-list-credential/status-list-credential-value.cjs');
require('../../vc/status-list2021-credential.cjs');
require('@digitalcredentials/vc-status-list');
require('pako');
require('../../vc/verifiable-credential.cjs');
require('../../vc/helpers.cjs');
require('../../vc/document-loader.cjs');
require('../../vc/contexts.cjs');
require('../../vc/contexts/vc-examples-v1.cjs');
require('../../vc/contexts/security_context.cjs');
require('../../vc/contexts/status-list-21.cjs');
require('../../vc/contexts/private-status-list-21.cjs');
require('../../utils/json-fetch.cjs');
require('../../vc/crypto/EcdsaSecp256k1Signature2019.cjs');
require('../../vc/crypto/EcdsaSecp256k1VerificationKey2019.cjs');
require('../../vc/crypto/Ed25519Signature2018.cjs');
require('../../vc/crypto/Ed25519VerificationKey2018.cjs');
require('../../vc/crypto/Ed25519Signature2020.cjs');
require('../../vc/crypto/Ed25519VerificationKey2020.cjs');
require('../../vc/crypto/JsonWebSignature2020.cjs');
require('../../vc/credentials.cjs');
require('../../vc/CredentialIssuancePurpose.cjs');
require('../../vc/schema.cjs');
require('jsonschema');
require('../../vc/constants.cjs');
require('../../vc/revocation.cjs');
require('blakejs');
require('../../utils/ident/ensure-ident.cjs');
require('../../utils/ident/matchers.cjs');
require('../../types/status-list-credential/id.cjs');
require('../../types/accumulator/variants.cjs');

// eslint-disable-next-line jest/no-export
function generateAccumulatorModuleTests(
  { did: didModule, accumulator: accumulatorModule },
  {
    Did,
    AccumulatorId,
    AccumulatorCommon,
    AccumulatorPublicKey: StoredAccumulatorPublicKey,
  },
) {
  describe(`Using ${didModule.constructor.name} and ${accumulatorModule.constructor.name} with ${Did.name}`, () => {
    // Incase updating an accumulator is expensive like making a blockchain txn, a cheaper strategy
    // is to add the members to the accumulator beforehand but not giving out the witnesses yet.
    // Eg. accumulator manager wants to add a million members over an year, rather than publishing
    // the new accumulator after each addition, the manager can initialize the accumulator with a million
    // member ids (member ids are either predictable like monotonically increasing numbers or the manager
    // can internally keep a map of random ids like UUIDs to a number). Now when the manager actually
    // wants to allow a member to prove membership, he can create a witness for that member but the
    // accumulator value remains same and thus the witness for existing members also remain same. It
    // should be noted though that changing the accumulator
    // value causes change in all existing witnesses and thus its better to make a good estimate
    // of the number of members during prefill stage

    // Manager estimates that he will have `total_members` members over the course of time

    const totalMembers = 100;
    const members = [];
    const seedAccum = utils_types_bytes.randomAsHex(32);

    const did = Did.random();
    const pair = new keypairs_didKeypair([did, 1], keypairs_keypairEd25519.random());

    const accumulatorModuleClass = accumulatorModule.constructor;

    let keypair;
    let accumulatorId = AccumulatorId.random(did);
    let accumulator;
    let params1Id;
    let pk1Id;
    const accumState = new inMemoryPersistence.InMemoryState();

    beforeAll(async () => {
      await index_js.initializeWasm();
      await didModule.createDocument(
        types_did_document_index.DIDDocument.create(did, [pair.didKey()]),
        pair,
      );
    });

    test('Prefill', async () => {
      const label = utils_types_bytes.stringToHex('accumulator-params-label');
      const params = index_js.Accumulator.generateParams(utils_types_bytes.hexToU8a(label));
      const bytes1 = utils_types_bytes.u8aToHex(params.bytes);
      const params1 = new index_js.AccumulatorParams(bytes1, label);
      params1Id = await accumulatorModule.nextParamsId(did);
      await accumulatorModule.addParams(params1Id, params1, did, pair);

      expect(
        (await accumulatorModule.getParams(did, params1Id)).toJSON(),
      ).toEqual(types_accumulator_params.AccumulatorParams.from(params1).toJSON());
      expect((await accumulatorModule.getAllParamsByDid(did)).toJSON()).toEqual(
        [[params1Id.toJSON(), types_accumulator_params.AccumulatorParams.from(params1).toJSON()]],
      );

      keypair = index_js.Accumulator.generateKeypair(params, seedAccum);
      const bytes2 = utils_types_bytes.u8aToHex(keypair.publicKey.bytes);
      const pk1 = new StoredAccumulatorPublicKey(bytes2, [did, params1Id]);
      pk1Id = await accumulatorModule.nextPublicKeyId(did);
      await accumulatorModule.addPublicKey(pk1Id, pk1, did, pair);

      expect(
        (await accumulatorModule.getPublicKey(did, pk1Id)).toJSON(),
      ).toEqual(pk1.toJSON());
      expect(
        (await accumulatorModule.getAllPublicKeysByDid(did)).toJSON(),
      ).toEqual([[pk1Id.toJSON(), pk1.toJSON()]]);

      const pk1WithParams = await pk1.withParams(accumulatorModule);
      expect(pk1WithParams.params).toBeInstanceOf(types_accumulator_params.AccumulatorParams);

      expect(
        (await accumulatorModule.getPublicKey(did, pk1Id, true)).toJSON(),
      ).toEqual(pk1WithParams.toJSON());
      expect(
        (await accumulatorModule.getAllPublicKeysByDid(did, true)).toJSON(),
      ).toEqual([[pk1Id.toJSON(), pk1WithParams.toJSON()]]);

      accumulator = index_js.PositiveAccumulator.initialize(params, keypair.secretKey);

      for (let i = 1; i <= totalMembers; i++) {
        members.push(index_js.Accumulator.encodePositiveNumberAsAccumulatorMember(i));
      }
      await accumulator.addBatch(members, keypair.secretKey, accumState);
      expect(accumState.state.size).toEqual(totalMembers);

      accumulatorId = AccumulatorId.random(did);
      const accumulated = accumulatorModuleClass.accumulatedAsHex(
        accumulator.accumulated,
        modules_abstract_accumulator_module.AccumulatorType.VBPos,
      );

      await accumulatorModule.addPositiveAccumulator(
        accumulatorId,
        accumulated,
        [did, pk1Id],
        pair,
      );

      const queriedAccum = await accumulatorModule.getAccumulator(
        accumulatorId,
        true,
        true,
      );

      expect(queriedAccum.accumulated.value).toEqual(accumulated);
    });

    test('Can create/update all types of accumulators', async () => {
      const acc1Id = AccumulatorId.random(did);

      await accumulatorModule.addPositiveAccumulator(
        acc1Id,
        utils_types_bytes.hexToU8a('0xff'),
        [did, pk1Id],
        pair,
      );

      let queriedAccum = await accumulatorModule.getAccumulator(acc1Id);

      expect(queriedAccum.accumulator.value.toJSON()).toEqual(
        new AccumulatorCommon(utils_types_bytes.hexToU8a('0xff'), [did, pk1Id]).toJSON(),
      );

      await accumulatorModule.updatePositiveAccumulator(
        acc1Id,
        utils_types_bytes.hexToU8a('0xfa'),
        {
          additions: [utils_types_bytes.hexToU8a('0xfe')],
          removals: [utils_types_bytes.hexToU8a('0xde')],
          witnessUpdateInfo: utils_types_bytes.hexToU8a('0xef'),
        },
        [did, pk1Id],
        pair,
      );

      queriedAccum = await accumulatorModule.getAccumulator(acc1Id);

      expect(queriedAccum.accumulator.value.toJSON()).toEqual(
        new AccumulatorCommon(utils_types_bytes.hexToU8a('0xfa'), [did, pk1Id]).toJSON(),
      );

      const acc2Id = AccumulatorId.random(did);

      await accumulatorModule.addKBUniversalAccumulator(
        acc2Id,
        utils_types_bytes.hexToU8a('0xff'),
        [did, pk1Id],
        pair,
      );

      queriedAccum = await accumulatorModule.getAccumulator(acc2Id);

      expect(queriedAccum.accumulator.value.toJSON()).toEqual(
        new AccumulatorCommon(utils_types_bytes.hexToU8a('0xff'), [did, pk1Id]).toJSON(),
      );

      await accumulatorModule.updateKBUniversalAccumulator(
        acc2Id,
        utils_types_bytes.hexToU8a('0xfa'),
        {
          additions: [utils_types_bytes.hexToU8a('0xfe')],
          removals: [utils_types_bytes.hexToU8a('0xde')],
          witnessUpdateInfo: utils_types_bytes.hexToU8a('0xef'),
        },
        [did, pk1Id],
        pair,
      );

      queriedAccum = await accumulatorModule.getAccumulator(acc2Id);

      expect(queriedAccum.accumulator.value.toJSON()).toEqual(
        new AccumulatorCommon(utils_types_bytes.hexToU8a('0xfa'), [did, pk1Id]).toJSON(),
      );

      const acc3Id = AccumulatorId.random(did);

      await accumulatorModule.addUniversalAccumulator(
        acc3Id,
        utils_types_bytes.hexToU8a('0xff'),
        [did, pk1Id],
        10,
        pair,
      );

      queriedAccum = await accumulatorModule.getAccumulator(acc3Id);

      expect(queriedAccum.accumulator.value.common.toJSON()).toEqual(
        new AccumulatorCommon(utils_types_bytes.hexToU8a('0xff'), [did, pk1Id]).toJSON(),
      );

      await accumulatorModule.updateUniversalAccumulator(
        acc3Id,
        utils_types_bytes.hexToU8a('0xfa'),
        {
          additions: [utils_types_bytes.hexToU8a('0xfe')],
          removals: [utils_types_bytes.hexToU8a('0xde')],
          witnessUpdateInfo: utils_types_bytes.hexToU8a('0xef'),
        },
        [did, pk1Id],
        10,
        pair,
      );

      queriedAccum = await accumulatorModule.getAccumulator(acc3Id);

      expect(queriedAccum.accumulator.value.common.toJSON()).toEqual(
        new AccumulatorCommon(utils_types_bytes.hexToU8a('0xfa'), [did, pk1Id]).toJSON(),
      );

      await accumulatorModule.removeAccumulator(acc3Id, pair);
      expect(await accumulatorModule.getAccumulator(acc3Id)).toBe(null);
    }, 60000);

    test('Witness creation, verification should work', async () => {
      let queriedAccum = await accumulatorModule.getAccumulator(
        accumulatorId,
        true,
        true,
      );
      let verifAccumulator = index_js.PositiveAccumulator.fromAccumulated(
        accumulatorModuleClass.accumulatedFromHex(
          queriedAccum.accumulated,
          modules_abstract_accumulator_module.AccumulatorType.VBPos,
        ),
      );

      // Witness created for member 1
      const member1 = members[10];
      const witness1 = await accumulator.membershipWitness(
        member1,
        keypair.secretKey,
        accumState,
      );

      let accumPk = new index_js.AccumulatorPublicKey(
        queriedAccum.publicKey.bytes.bytes,
      );

      let accumParams = new index_js.AccumulatorParams(
        queriedAccum.publicKey.params.bytes.bytes,
      );

      expect(
        verifAccumulator.verifyMembershipWitness(
          member1,
          witness1,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);

      // Witness created for member 2
      const member2 = members[25];
      const witness2 = await accumulator.membershipWitness(
        member2,
        keypair.secretKey,
        accumState,
      );
      expect(
        verifAccumulator.verifyMembershipWitness(
          member2,
          witness2,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);

      // Witness created for member 3
      const member3 = members[79];
      const witness3 = await accumulator.membershipWitness(
        member3,
        keypair.secretKey,
        accumState,
      );
      expect(
        verifAccumulator.verifyMembershipWitness(
          member3,
          witness3,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);

      // Previous users' witness still works
      expect(
        verifAccumulator.verifyMembershipWitness(
          member1,
          witness1,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);
      expect(
        verifAccumulator.verifyMembershipWitness(
          member2,
          witness2,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);

      // Manager decides to remove a member, the new accumulated value will be published along with witness update info
      const witnessUpdInfo = index_js.VBWitnessUpdateInfo.new(
        accumulator.accumulated,
        [],
        [member2],
        keypair.secretKey,
      );
      await accumulator.remove(member2, keypair.secretKey, accumState);

      let accum = await accumulatorModule.getAccumulator(accumulatorId, false);
      const accumulated = accumulatorModuleClass.accumulatedAsHex(
        accumulator.accumulated,
        modules_abstract_accumulator_module.AccumulatorType.VBPos,
      );
      const witUpdBytes = utils_types_bytes.u8aToHex(witnessUpdInfo.value);
      await accumulatorModule.updatePositiveAccumulator(
        accumulatorId,
        accumulated,
        { removals: [utils_types_bytes.u8aToHex(member2)], witnessUpdateInfo: witUpdBytes },
        [did, pk1Id],
        pair,
      );

      queriedAccum = await accumulatorModule.getAccumulator(
        accumulatorId,
        true,
        true,
      );
      expect(queriedAccum.accumulated.value).toEqual(accumulated);

      verifAccumulator = index_js.PositiveAccumulator.fromAccumulated(
        accumulatorModuleClass.accumulatedFromHex(
          queriedAccum.accumulated,
          modules_abstract_accumulator_module.AccumulatorType.VBPos,
        ),
      );

      accumPk = new index_js.AccumulatorPublicKey(queriedAccum.publicKey.bytes.bytes);
      accumParams = new index_js.AccumulatorParams(
        queriedAccum.publicKey.params.bytes.bytes,
      );

      // Witness created for member 3
      const member4 = members[52];
      const witness4 = await accumulator.membershipWitness(
        member4,
        keypair.secretKey,
        accumState,
      );
      expect(
        verifAccumulator.verifyMembershipWitness(
          member4,
          witness4,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);

      // Older witnesses need to be updated
      accum = await accumulatorModule.getAccumulator(accumulatorId);

      await accumulatorModule.updateWitness(
        accumulatorId,
        member1,
        witness1,
        accum.lastModified,
        accum.lastModified,
      );

      await accumulatorModule.updateWitness(
        accumulatorId,
        member3,
        witness3,
        accum.lastModified,
        accum.lastModified,
      );

      expect(
        verifAccumulator.verifyMembershipWitness(
          member1,
          witness1,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);

      expect(
        verifAccumulator.verifyMembershipWitness(
          member3,
          witness3,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);
    });

    test('Witness update after several batch upgrades', async () => {
      let queriedAccum = await accumulatorModule.getAccumulator(
        accumulatorId,
        true,
        true,
      );
      let verifAccumulator = index_js.PositiveAccumulator.fromAccumulated(
        accumulatorModuleClass.accumulatedFromHex(
          queriedAccum.accumulated,
          modules_abstract_accumulator_module.AccumulatorType.VBPos,
        ),
      );
      const member = members[10];
      let witness = await accumulator.membershipWitness(
        member,
        keypair.secretKey,
        accumState,
      );

      const accumPk = new index_js.AccumulatorPublicKey(
        queriedAccum.publicKey.bytes.bytes,
      );
      const accumParams = new index_js.AccumulatorParams(
        queriedAccum.publicKey.params.bytes.bytes,
      );
      expect(
        verifAccumulator.verifyMembershipWitness(
          member,
          witness,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);

      // Do some updates to the accumulator

      const removals1 = [members[85], members[86]];
      const witnessUpdInfo1 = index_js.VBWitnessUpdateInfo.new(
        accumulator.accumulated,
        [],
        removals1,
        keypair.secretKey,
      );
      await accumulator.removeBatch(removals1, keypair.secretKey, accumState);
      const accumulated1 = accumulatorModuleClass.accumulatedAsHex(
        accumulator.accumulated,
        modules_abstract_accumulator_module.AccumulatorType.VBPos,
      );
      const witUpdBytes1 = utils_types_bytes.u8aToHex(witnessUpdInfo1.value);
      await accumulatorModule.updatePositiveAccumulator(
        accumulatorId,
        accumulated1,
        {
          removals: removals1.map((r) => utils_types_bytes.u8aToHex(r)),
          witnessUpdateInfo: witUpdBytes1,
        },
        [did, pk1Id],
        pair,
      );

      const removals2 = [members[87], members[88]];
      const witnessUpdInfo2 = index_js.VBWitnessUpdateInfo.new(
        accumulator.accumulated,
        [],
        removals2,
        keypair.secretKey,
      );
      await accumulator.removeBatch(removals2, keypair.secretKey, accumState);
      const accumulated2 = accumulatorModuleClass.accumulatedAsHex(
        accumulator.accumulated,
        modules_abstract_accumulator_module.AccumulatorType.VBPos,
      );
      const witUpdBytes2 = utils_types_bytes.u8aToHex(witnessUpdInfo2.value);
      await accumulatorModule.updatePositiveAccumulator(
        accumulatorId,
        accumulated2,
        {
          removals: removals2.map((r) => utils_types_bytes.u8aToHex(r)),
          witnessUpdateInfo: witUpdBytes2,
        },
        [did, pk1Id],
        pair,
      );

      const removals3 = [members[89], members[90], members[91]];
      const witnessUpdInfo3 = index_js.VBWitnessUpdateInfo.new(
        accumulator.accumulated,
        [],
        removals3,
        keypair.secretKey,
      );
      await accumulator.removeBatch(removals3, keypair.secretKey, accumState);
      const accumulated3 = accumulatorModuleClass.accumulatedAsHex(
        accumulator.accumulated,
        modules_abstract_accumulator_module.AccumulatorType.VBPos,
      );
      const witUpdBytes3 = utils_types_bytes.u8aToHex(witnessUpdInfo3.value);
      await accumulatorModule.updatePositiveAccumulator(
        accumulatorId,
        accumulated3,
        {
          removals: removals3.map((r) => utils_types_bytes.u8aToHex(r)),
          witnessUpdateInfo: witUpdBytes3,
        },
        [did, pk1Id],
        pair,
      );

      // Get a witness from the accumulator manager. This will be updated after the following updates.
      witness = await accumulator.membershipWitness(
        member,
        keypair.secretKey,
        accumState,
      );

      // Do some more updates to the accumulator
      const removals4 = [members[92], members[93]];
      const witnessUpdInfo4 = index_js.VBWitnessUpdateInfo.new(
        accumulator.accumulated,
        [],
        removals4,
        keypair.secretKey,
      );
      await accumulator.removeBatch(removals4, keypair.secretKey, accumState);
      const accumulated4 = accumulatorModuleClass.accumulatedAsHex(
        accumulator.accumulated,
        modules_abstract_accumulator_module.AccumulatorType.VBPos,
      );
      const witUpdBytes4 = utils_types_bytes.u8aToHex(witnessUpdInfo4.value);
      await accumulatorModule.updatePositiveAccumulator(
        accumulatorId,
        accumulated4,
        {
          removals: removals4.map((r) => utils_types_bytes.u8aToHex(r)),
          witnessUpdateInfo: witUpdBytes4,
        },
        [did, pk1Id],
        pair,
      );

      const { lastModified: startFrom } = await accumulatorModule.getAccumulator(accumulatorId);

      const removals5 = [members[94], members[95], members[96]];
      const witnessUpdInfo5 = index_js.VBWitnessUpdateInfo.new(
        accumulator.accumulated,
        [],
        removals5,
        keypair.secretKey,
      );
      await accumulator.removeBatch(removals5, keypair.secretKey, accumState);
      const accumulated5 = accumulatorModuleClass.accumulatedAsHex(
        accumulator.accumulated,
        modules_abstract_accumulator_module.AccumulatorType.VBPos,
      );
      const witUpdBytes5 = utils_types_bytes.u8aToHex(witnessUpdInfo5.value);
      await accumulatorModule.updatePositiveAccumulator(
        accumulatorId,
        accumulated5,
        {
          removals: removals5.map((r) => utils_types_bytes.u8aToHex(r)),
          witnessUpdateInfo: witUpdBytes5,
        },
        [did, pk1Id],
        pair,
      );

      queriedAccum = await accumulatorModule.getAccumulator(
        accumulatorId,
        true,
        true,
      );
      verifAccumulator = index_js.PositiveAccumulator.fromAccumulated(
        accumulatorModuleClass.accumulatedFromHex(
          queriedAccum.accumulated,
          modules_abstract_accumulator_module.AccumulatorType.VBPos,
        ),
      );
      // Old witness doesn't verify with new accumulator
      expect(
        verifAccumulator.verifyMembershipWitness(
          member,
          witness,
          accumPk,
          accumParams,
        ),
      ).toEqual(false);

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

      // Update witness by downloading necessary blocks and applying the updates if found
      await accumulatorModule.updateWitness(
        accumulatorId,
        member,
        witness,
        startFrom,
        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 accumulatorModule.updateWitness(
        accumulatorId,
        member,
        oldWitness1,
        startFrom,
        queriedAccum.lastModified,
        queriedAccum.lastModified - startFrom + 10,
      );
      expect(
        verifAccumulator.verifyMembershipWitness(
          member,
          oldWitness1,
          accumPk,
          accumParams,
        ),
      ).toEqual(true);

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

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

    test('Accumulator versions', async () => {
      const accId = AccumulatorId.random(did);
      const pkId = await accumulatorModule.lastPublicKeyId(did);

      await accumulatorModule.addUniversalAccumulator(
        accId,
        utils_types_bytes.hexToU8a('0xff'),
        [did, pkId],
        10,
        pair,
      );

      await accumulatorModule.updateUniversalAccumulator(
        accId,
        utils_types_bytes.hexToU8a('0xfa'),
        { additions: [utils_types_bytes.hexToU8a('0xfa')], witnessUpdateInfo: utils_types_bytes.hexToU8a('0xfa') },
        [did, pkId],
        10,
        pair,
      );

      await accumulatorModule.updateUniversalAccumulator(
        accId,
        utils_types_bytes.hexToU8a('0xfe'),
        { removals: [utils_types_bytes.hexToU8a('0xfa')], witnessUpdateInfo: utils_types_bytes.hexToU8a('0xfa') },
        [did, pkId],
        10,
        pair,
      );

      const versions = await accumulatorModule.accumulatorVersions(accId);
      expect(versions.length).toBe(3);
      const { created, updates } = await accumulatorModule.accumulatorHistory(
        accId,
      );
      expect(utils_types_bytes.valueBytes(created.accumulated)).toEqual(utils_types_bytes.hexToU8a('0xff'));
      expect(utils_types_bytes.valueBytes(updates[0].accumulated)).toEqual(utils_types_bytes.hexToU8a('0xfa'));
      expect(utils_types_bytes.valueBytes(updates[1].accumulated)).toEqual(utils_types_bytes.hexToU8a('0xfe'));
      expect(updates.length).toBe(2);
    });
  });
}

module.exports = generateAccumulatorModuleTests;
//# sourceMappingURL=accumulator-module.cjs.map
