From 15e6bf728d6c0eba20cdde24318becb74abbedea Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Tue, 30 May 2023 22:14:20 +0530 Subject: [PATCH 01/33] Adding controller --- jest.config.js | 8 + package.json | 6 + src/index.ts | 3 + src/ppom-controller.test.ts | 202 ++++++++++ src/ppom-controller.ts | 387 +++++++++++++++++++ src/ppom-storage.test.ts | 47 +-- src/ppom-storage.ts | 25 +- src/ppom.d.ts | 17 + src/ppom.ts | 3 +- test/test-utils.ts | 40 ++ yarn.lock | 741 +++++++++++++++++++++++++++++++++++- 11 files changed, 1432 insertions(+), 47 deletions(-) create mode 100644 src/index.ts create mode 100644 src/ppom-controller.test.ts create mode 100644 src/ppom-controller.ts create mode 100644 src/ppom.d.ts create mode 100644 test/test-utils.ts diff --git a/jest.config.js b/jest.config.js index 41ae9b2e..9db735cd 100644 --- a/jest.config.js +++ b/jest.config.js @@ -28,7 +28,15 @@ module.exports = { coverageDirectory: 'coverage', // An array of regexp pattern strings used to skip coverage collection +<<<<<<< HEAD coveragePathIgnorePatterns: ['/node_modules/', '/src/ppom.ts'], +======= + coveragePathIgnorePatterns: [ + '/node_modules/', + 'src/ppom.d.ts', + 'src/index.ts', + ], +>>>>>>> 6ae1fc4 (Adding controller) // Indicates which provider should be used to instrument code for coverage coverageProvider: 'babel', diff --git a/package.json b/package.json index 75a804e1..59b5502b 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,12 @@ "test": "jest && jest-it-up", "test:watch": "jest --watch" }, + "dependencies": { + "@metamask/base-controller": "^3.0.0", + "@metamask/controller-utils": "^4.0.0", + "@types/whatwg-fetch": "^0.0.33", + "await-semaphore": "^0.1.3" + }, "devDependencies": { "@lavamoat/allow-scripts": "^2.3.1", "@lavamoat/preinstall-always-fail": "^1.0.0", diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 00000000..5b271c24 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from './ppom-controller'; + +export type { StorageBackend, StorageKey } from './ppom-storage'; diff --git a/src/ppom-controller.test.ts b/src/ppom-controller.test.ts new file mode 100644 index 00000000..b0df9577 --- /dev/null +++ b/src/ppom-controller.test.ts @@ -0,0 +1,202 @@ +import { VERSION_INFO, storageBackendReturningData } from '../test/test-utils'; +import { PPOM } from './ppom'; +import { PPOMController } from './ppom-controller'; + +Object.defineProperty(globalThis, 'fetch', { + writable: true, + value: () => undefined, +}); + +jest.mock('./ppom.js', () => ({ + PPOM: class PPOMClass { + #jsonRpcRequest; + + constructor(jsonRpcRequest: any) { + this.#jsonRpcRequest = jsonRpcRequest; + } + + validateJsonRpc = async () => { + return Promise.resolve(); + }; + + free = () => undefined; + + testJsonRPCRequest = () => this.#jsonRpcRequest(); + }, + ppomInit: () => undefined, +})); + +const PPOM_VERSION_PATH = + 'https://storage.googleapis.com/ppom-cdn/ppom_version.json'; + +const buildFetchSpy = ( + versionData: any = { + status: 200, + json: () => VERSION_INFO, + }, + blobData: any = { + status: 200, + arrayBuffer: () => new ArrayBuffer(123), + }, +) => { + return jest + .spyOn(globalThis, 'fetch' as any) + .mockImplementation((url: any) => { + if (url === PPOM_VERSION_PATH) { + return versionData; + } + return blobData; + }); +}; + +describe('PPOMController', () => { + describe('use', () => { + let ppomController: any; + beforeEach(() => { + ppomController = new PPOMController({ + storageBackend: storageBackendReturningData, + provider: { sendAsync: Promise.resolve() }, + chainId: '0x1', + onNetworkChange: (_callback) => undefined, + }); + }); + + it('should be able to invoke use', async () => { + buildFetchSpy(); + + await ppomController.use(async (ppom: PPOM) => { + expect(ppom).toBeDefined(); + return Promise.resolve(); + }); + }); + + it('should not fetch file if chainId is different from current chainId', async () => { + const spy = buildFetchSpy({ + status: 200, + json: () => [ + ...VERSION_INFO, + { + name: 'data', + chainId: '0x2', + version: '1.0.3', + checksum: + '409a7f83ac6b31dc8c77e3ec18038f209bd2f545e0f4177c2e2381aa4e067b49', + filePath: 'data', + }, + ], + }); + + await ppomController.use(async () => { + return Promise.resolve(); + }); + + expect(spy).toHaveBeenCalledTimes(3); + }); + + it('should throw error', async () => { + buildFetchSpy({ + status: 500, + }); + + await expect(async () => { + await ppomController.use(async () => { + return Promise.resolve(); + }); + }).rejects.toThrow('Failed to fetch version info'); + }); + + it('should throw error if fetch for blob return 500', async () => { + buildFetchSpy(undefined, { + status: 500, + }); + + await expect(async () => { + await ppomController.use(async () => { + return Promise.resolve(); + }); + }).rejects.toThrow('Failed to fetch file data'); + }); + + it('should refresh data if refreshInterval is passed', async () => { + const spy = buildFetchSpy(); + + await ppomController.use(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + await ppomController.use(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + + ppomController.setRefreshInterval(0); + await ppomController.use(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(4); + }); + + it('should refresh data if network is changed', async () => { + let callBack: any; + ppomController = new PPOMController({ + storageBackend: storageBackendReturningData, + provider: { sendAsync: Promise.resolve() }, + chainId: '0x1', + onNetworkChange: (func: any) => { + callBack = func; + }, + }); + const spy = buildFetchSpy({ + status: 200, + json: () => [ + ...VERSION_INFO, + { + name: 'data', + chainId: '0x2', + version: '1.0.3', + checksum: + '409a7f83ac6b31dc8c77e3ec18038f209bd2f545e0f4177c2e2381aa4e067b49', + filePath: 'data', + }, + ], + }); + + await ppomController.use(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + await ppomController.use(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + + callBack('0x2'); + await ppomController.use(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(4); + }); + }); + + describe('PPOM', () => { + it('should be able to send JSON RPC request to provider', async () => { + const sendAsync = (_arg1: any, arg2: any) => { + arg2(undefined, 'DUMMY_VALUE'); + }; + const ppomController = new PPOMController({ + storageBackend: storageBackendReturningData, + provider: { sendAsync }, + chainId: '0x1', + onNetworkChange: (_callback) => undefined, + }); + + buildFetchSpy(); + + await ppomController.use(async (ppom: PPOM) => { + expect(ppom).toBeDefined(); + const result = await (ppom as any).testJsonRPCRequest({}); + expect(result).toBe('DUMMY_VALUE'); + }); + }); + }); +}); diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts new file mode 100644 index 00000000..51badd34 --- /dev/null +++ b/src/ppom-controller.ts @@ -0,0 +1,387 @@ +import { + BaseController, + BaseConfig, + BaseState, +} from '@metamask/base-controller'; +import { safelyExecute } from '@metamask/controller-utils'; +import { Mutex } from 'await-semaphore'; + +import { ppomInit, PPOM } from './ppom'; +import { + StorageBackend, + PPOMStorage, + PPOMFileMetadata, + FileInfo, +} from './ppom-storage'; + +const DAY = 1000 * 60 * 60 * 24; + +/** + * @type PPOMFileVersion + * @augments FileInfo + * @property filePath - Path of the file in CDN. + */ +type PPOMFileVersion = FileInfo & { + filePath: string; +}; + +/** + * @type PPOMVersionResponse - array of objects of type PPOMFileVersion + */ +type PPOMVersionResponse = PPOMFileVersion[]; + +/** + * @type PPOMState + * + * Controller state + * @property lastFetched - Time when files were last updated. + * @property lastChainId - ChainId for which files were last updated. + * @property newChainId - ChainIf of currently selected network. + * @property versionInfo - Version information fetched from CDN. + * @property storageMetadata - Metadata of files storaged in storage. + */ +export type PPOMState = BaseState & { + lastFetched: number; + lastChainId: string; + newChainId: string; + versionInfo: PPOMVersionResponse; + storageMetadata: PPOMFileMetadata; +}; + +/** + * @type PPOMControllerConfig + * + * Controller configuration + * @property refreshInterval - Polling interval used to fetch new PPOM lists + */ +export type PPOMControllerConfig = BaseConfig & { + refreshInterval: number; +}; + +const PPOM_DATA_NAME = 'data'; +const PPOM_BLOB_NAME = 'blob'; + +// TODO: replace with metamask cdn +const PPOM_CDN_BASE_URL = 'https://storage.googleapis.com/ppom-cdn/'; +const PPOM_VERSION = 'ppom_version.json'; +const PPOM_VERSION_PATH = `${PPOM_CDN_BASE_URL}${PPOM_VERSION}`; + +/** + * PPOMController + * Controller responsible for managing the PPOM + * + * @property config - The controller configuration + * @property state - The controller state + * @property storage - The controller storage + * @property ppom - The PPOM instance + * @property provider - The provider used to create the PPOM instance + */ +export class PPOMController extends BaseController< + PPOMControllerConfig, + PPOMState +> { + /** + * Name of this controller used during composition + */ + override name = 'PPOMController'; + + #ppom: PPOM | undefined; + + #provider: any; + + #storage: PPOMStorage; + + /* + * This mutex is used to prevent concurrent usage of the PPOM instance + * and protect the PPOM instance from being used while it is being initialized/updated + */ + #ppomMutex: Mutex; + + /** + * Creates a PPOMController instance. + * + * @param options - Constructor options. + * @param options.storageBackend - The storage backend to use for storing PPOM data. + * @param options.provider - The provider used to create the PPOM instance. + * @param options.chainId - Id of current chain. + * @param options.onNetworkChange - Callback tobe invoked when network changes. + * @param options.config - The controller configuration. + * @param options.state - The controller state. + * @returns The PPOMController instance. + */ + constructor({ + storageBackend, + provider, + chainId, + onNetworkChange, + config, + state, + }: { + storageBackend: StorageBackend; + provider: any; + chainId: string; + onNetworkChange: (callback: (chainId: string) => void) => void; + config?: PPOMControllerConfig; + state?: PPOMState; + }) { + const defaultConfig = { + refreshInterval: DAY, + }; + const defaultState = { + lastFetched: 0, + versionInfo: [], + storageMetadata: [], + lastChainId: '', + newChainId: chainId, + }; + super(config ?? defaultConfig, state ?? defaultState); + + this.defaultConfig = defaultConfig; + this.defaultState = defaultState; + + this.#provider = provider; + this.#storage = new PPOMStorage({ + storageBackend, + readMetadata: () => { + return this.state.storageMetadata; + }, + writeMetadata: (metadata) => { + this.update({ storageMetadata: metadata }); + }, + }); + this.#ppomMutex = new Mutex(); + + onNetworkChange((id: string) => { + this.update({ newChainId: id }); + }); + + this.initialize(); + } + + /** + * Set the interval at which the ppom version info will be fetched. + * Fetching will only occur on the next call to test/bypass. + * For immediate update to the ppom lists, call updatePPOM directly. + * + * @param interval - The new interval in ms. + */ + setRefreshInterval(interval: number) { + this.configure({ refreshInterval: interval }, false, false); + } + + /** + * Determine if an update to the ppom configuration is needed. + * The function will return true if + * - the chainId has changed + * - the ppom is out of date + * - the ppom is not initialized. + * + * @returns True if PPOM data requires update. + */ + async #shouldUpdate(): Promise { + if ( + this.state.newChainId !== this.state.lastChainId || + this.#isOutOfDate() + ) { + return true; + } + + return this.#ppom === undefined; + } + + /* + * check if the ppom is out of date + */ + #isOutOfDate(): boolean { + return Date.now() - this.state.lastFetched >= this.config.refreshInterval; + } + + /** + * Update the PPOM configuration. + * This function will fetch the latest version info when needed, and update the PPOM storage. + */ + async updatePPOM() { + if (this.#ppom) { + this.#ppom.free(); + this.#ppom = undefined; + } + + if (this.#isOutOfDate()) { + await this.#updateVersionInfo(); + } + + this.update({ lastChainId: this.state.newChainId }); + + const storageMetadata = await this.#storage.syncMetadata( + this.state.versionInfo, + ); + const newFiles = await this.#getNewFiles( + this.state.newChainId, + storageMetadata, + ); + + for (const file of newFiles) { + await this.#storage.writeFile(file); + } + } + + /** + * Returns an array of new files that should be downloaded and saved to storage. + * + * @param chainId - The chain ID to check for files. + * @param storageMetadata - An array of file metadata objects already in storage. + * @returns A promise that resolves to an array of new files to download and save to storage. + */ + async #getNewFiles(chainId: string, storageMetadata: any[]): Promise { + const newFiles: any[] = []; + + for (const fileVersionInfo of this.state.versionInfo) { + // download all files for the current chain + generally required files. + if (fileVersionInfo.chainId && fileVersionInfo.chainId !== chainId) { + continue; + } + + // check if file is already in storage + if ( + storageMetadata.find( + (file) => + file.name === fileVersionInfo.name && + file.chainId === fileVersionInfo.chainId && + file.version === fileVersionInfo.version && + file.checksum === fileVersionInfo.checksum, + ) + ) { + continue; + } + + const fileUrl = `${PPOM_CDN_BASE_URL}${fileVersionInfo.filePath}`; + const fileData = await this.#fetchBlob(fileUrl); + if (!fileData) { + throw new Error('Failed to fetch file data'); + } + + newFiles.push({ + data: fileData, + ...fileVersionInfo, + }); + } + + return newFiles; + } + + /* + * Fetch the version info from the PPOM cdn. + * update the version info in state. + */ + async #updateVersionInfo() { + const versionInfo = await this.#fetchVersionInfo(PPOM_VERSION_PATH); + if (!versionInfo) { + throw new Error('Failed to fetch version info'); + } + + this.update({ + versionInfo, + lastFetched: Date.now(), + }); + } + + /** + * Conditionally update the ppom configuration. + * + * If the ppom configuration is out of date, this function will call `updatePPOM` + * to update the configuration. + */ + async #maybeUpdatePPOM() { + if (await this.#shouldUpdate()) { + await this.updatePPOM(); + } + + this.update(this.state); + } + + /* + * Fetch the blob from the PPOM cdn. + */ + async #fetchBlob(input: string): Promise { + const response = await safelyExecute( + () => fetch(input, { cache: 'no-cache' }), + true, + ); + + switch (response?.status) { + case 200: { + return await response.arrayBuffer(); + } + + default: { + return null; + } + } + } + + /* + * Fetch the version info from the PPOM cdn. + */ + async #fetchVersionInfo(input: string): Promise { + const response = await safelyExecute( + () => fetch(input, { cache: 'no-cache' }), + true, + ); + switch (response?.status) { + case 200: { + return response.json(); + } + + default: { + return null; + } + } + } + + /* + * Send a JSON RPC request to the provider. + * This method is used by the PPOM to make requests to the provider. + */ + async #jsonRpcRequest(req: any): Promise { + return new Promise((resolve) => { + this.#provider.sendAsync(req, (_err: any, res: any) => { + resolve(res); + }); + }); + } + + /* + * Initialize the PPOM. + * This function will be called when the PPOM is first used. + * or when the PPOM is out of date. + * It will load the PPOM data from storage and initialize the PPOM. + */ + async #getPPOM(): Promise { + await ppomInit(this.#storage.readFile(PPOM_BLOB_NAME, '')); + const data = await this.#storage.readFile( + PPOM_DATA_NAME, + this.state.newChainId, + ); + + return new PPOM(this.#jsonRpcRequest.bind(this), new Uint8Array(data)); + } + + /** + * Use the PPOM. + * This function receives a callback that will be called with the PPOM. + * The callback will be called with the PPOM after it has been initialized. + * + * @param callback - Callback to be invoked with PPOM. + */ + async use(callback: (ppom: PPOM) => Promise): Promise { + return await this.#ppomMutex.use(async () => { + await this.#maybeUpdatePPOM(); + + if (!this.#ppom) { + this.#ppom = await this.#getPPOM(); + } + + return await callback(this.#ppom); + }); + } +} diff --git a/src/ppom-storage.test.ts b/src/ppom-storage.test.ts index 2bf789f6..bfea4375 100644 --- a/src/ppom-storage.test.ts +++ b/src/ppom-storage.test.ts @@ -1,27 +1,14 @@ +import { + DUMMY_ARRAY_BUFFER_DATA, + buildStorageBackend, + simpleStorageBackend, + storageBackendReturningData, +} from '../test/test-utils'; import { PPOMStorage, StorageKey } from './ppom-storage'; -const buildStorageBackend = (obj = {}) => { - return { - read: async (_key: StorageKey): Promise => Promise.resolve(), - write: async (_key: StorageKey, _data: any): Promise => - Promise.resolve(), - delete: async (_key: StorageKey): Promise => Promise.resolve(), - dir: async (): Promise => Promise.resolve([]), - ...obj, - }; -}; - -const simpleStorageBackend = buildStorageBackend(); - -const buildStorageBackendReturningData = (data: ArrayBuffer) => - buildStorageBackend({ - read: async (_key: StorageKey): Promise => Promise.resolve(data), - }); - const DUMMY_CHECKSUM = 'DUMMY_CHECKSUM'; -const DUMMY_NAME = 'DUMMY'; +const DUMMY_NAME = 'DUMMY_NAME'; const DUMMY_CHAINID = '1'; -const ARRAY_BUFFER_DATA = new ArrayBuffer(123); const getFileData = (data = {}) => ({ chainId: DUMMY_CHAINID, @@ -37,17 +24,17 @@ describe('PPOMStorage', () => { describe('readFile', () => { it('should return data', async () => { const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + storageBackend: storageBackendReturningData, readMetadata: () => [simpleFileData], writeMetadata: () => undefined, }); const data = await ppomStorage.readFile(DUMMY_NAME, DUMMY_CHAINID); - expect(data).toStrictEqual(ARRAY_BUFFER_DATA); + expect(data).toStrictEqual(DUMMY_ARRAY_BUFFER_DATA); }); it('should throw error if file metadata not found', async () => { const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + storageBackend: storageBackendReturningData, readMetadata: () => [], writeMetadata: () => undefined, }); @@ -81,7 +68,7 @@ describe('PPOMStorage', () => { writeMetadata: () => undefined, }); await ppomStorage.writeFile({ - data: ARRAY_BUFFER_DATA, + data: DUMMY_ARRAY_BUFFER_DATA, ...simpleFileData, }); expect(mockWrite).toHaveBeenCalledTimes(1); @@ -95,7 +82,7 @@ describe('PPOMStorage', () => { writeMetadata: mockWriteMetadata, }); await ppomStorage.writeFile({ - data: ARRAY_BUFFER_DATA, + data: DUMMY_ARRAY_BUFFER_DATA, ...simpleFileData, }); expect(mockWriteMetadata).toHaveBeenCalledWith([simpleFileData]); @@ -109,7 +96,7 @@ describe('PPOMStorage', () => { writeMetadata: mockWriteMetadata, }); await ppomStorage.writeFile({ - data: ARRAY_BUFFER_DATA, + data: DUMMY_ARRAY_BUFFER_DATA, ...simpleFileData, }); expect(mockWriteMetadata).toHaveBeenCalledWith([simpleFileData]); @@ -120,7 +107,7 @@ describe('PPOMStorage', () => { it('should return metadata of file if updated file is found in storage', async () => { const mockWriteMetadata = jest.fn(); const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + storageBackend: storageBackendReturningData, readMetadata: () => [simpleFileData], writeMetadata: mockWriteMetadata, }); @@ -151,7 +138,7 @@ describe('PPOMStorage', () => { const ppomStorage = new PPOMStorage({ storageBackend: buildStorageBackend({ read: async (_key: StorageKey): Promise => - Promise.resolve(ARRAY_BUFFER_DATA), + Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), dir: async () => Promise.resolve([storageFileData]), delete: mockDelete, }), @@ -178,7 +165,7 @@ describe('PPOMStorage', () => { const ppomStorage = new PPOMStorage({ storageBackend: buildStorageBackend({ read: async (_key: StorageKey): Promise => - Promise.resolve(ARRAY_BUFFER_DATA), + Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), dir: async () => Promise.resolve([fileDataInStorage]), delete: mockDelete, }), @@ -203,7 +190,7 @@ describe('PPOMStorage', () => { const ppomStorage = new PPOMStorage({ storageBackend: buildStorageBackend({ read: async (_key: StorageKey): Promise => - Promise.resolve(ARRAY_BUFFER_DATA), + Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), dir: async () => Promise.resolve([fileDataInStorage]), delete: mockDelete, }), diff --git a/src/ppom-storage.ts b/src/ppom-storage.ts index 95b653a7..a5946829 100644 --- a/src/ppom-storage.ts +++ b/src/ppom-storage.ts @@ -1,8 +1,12 @@ /** - * FileMetadata Type + * @type FileInfo * Defined type for information about file saved in storage backend. + * @property name - Name of the file. + * @property chainId - ChainId for file. + * @property version - File version. + * @property checksum - Checksum of file data. */ -type FileMetadata = { +export type FileInfo = { name: string; chainId: string; version: string; @@ -10,15 +14,18 @@ type FileMetadata = { }; /** - * FileMetadataList + * @type PPOMFileMetadata + * Array of objects of type FileInfo * This is type of metadata about files saved in storage, * this information is saved in PPOMController state. */ export type FileMetadataList = FileMetadata[]; /** - * StorageKey Type + * @type StorageKey * This defines type of key that is used for indexing file data saved in StorageBackend. + * @property name - Name of the file. + * @property chainId - ChainId for file. */ export type StorageKey = { name: string; @@ -26,11 +33,15 @@ export type StorageKey = { }; /** - * StorageBackend Type + * @type StorageBackend * This defines type for storage backend implementation. * There will be different storage implementations depending on platform: * 1. extension - indexDB * 2. mobile app - + * @property read - Read file from storage. + * @property write - Write file to storage. + * @property delete - Delete file from storage. + * @property dir - Get list of all files in storage. */ export type StorageBackend = { read(key: StorageKey, checksum: string): Promise; @@ -40,7 +51,7 @@ export type StorageBackend = { }; /** - * PPOMStorage class + * @class PPOMStorage * This class is responsible for managing the local storage * It provides the following functionalities: * 1. Sync the metadata with the version info from the cdn @@ -91,7 +102,7 @@ export class PPOMStorage { */ async syncMetadata(versionInfo: FileMetadataList): Promise { const metadata = this.#readMetadata(); - const syncedMetadata = []; + const syncedMetadata: PPOMFileMetadata = []; for (const fileMetadata of metadata) { // check if the file is readable (e.g. corrupted or deleted) diff --git a/src/ppom.d.ts b/src/ppom.d.ts new file mode 100644 index 00000000..bb5e0319 --- /dev/null +++ b/src/ppom.d.ts @@ -0,0 +1,17 @@ +export class PPOM { + free(): void; + + constructor(jsonRpcCallback: any, data: Uint8Array); + + test(): Promise; + + validateJsonRpc(request: any): Promise; +} + +type InitInput = any; + +type InitOutput = any; + +export function ppomInit( + moduleOrPath: InitInput | Promise, +): Promise; diff --git a/src/ppom.ts b/src/ppom.ts index d9c6be70..f63fa025 100644 --- a/src/ppom.ts +++ b/src/ppom.ts @@ -481,5 +481,4 @@ async function init(input) { return finalizeInit(instance, module); } -export { initSync }; -export default init; +export { initSync, init as ppomInit }; diff --git a/test/test-utils.ts b/test/test-utils.ts new file mode 100644 index 00000000..ebee1726 --- /dev/null +++ b/test/test-utils.ts @@ -0,0 +1,40 @@ +import { StorageKey } from '../src/ppom-storage'; + +export const buildStorageBackend = (obj = {}) => { + return { + read: async (_key: StorageKey): Promise => Promise.resolve(), + write: async (_key: StorageKey, _data: any): Promise => + Promise.resolve(), + delete: async (_key: StorageKey): Promise => Promise.resolve(), + dir: async (): Promise => Promise.resolve([]), + ...obj, + }; +}; + +export const simpleStorageBackend = buildStorageBackend(); + +export const DUMMY_ARRAY_BUFFER_DATA = new ArrayBuffer(123); + +export const storageBackendReturningData = buildStorageBackend({ + read: async (_key: StorageKey): Promise => + Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), +}); + +export const VERSION_INFO = [ + { + name: 'blob', + chainId: '', + version: '1.0.0', + checksum: + '409a7f83ac6b31dc8c77e3ec18038f209bd2f545e0f4177c2e2381aa4e067b49', + filePath: 'blob', + }, + { + name: 'data', + chainId: '0x1', + version: '1.0.3', + checksum: + '409a7f83ac6b31dc8c77e3ec18038f209bd2f545e0f4177c2e2381aa4e067b49', + filePath: 'data', + }, +]; diff --git a/yarn.lock b/yarn.lock index 7023f527..38f600c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -405,6 +405,33 @@ __metadata: languageName: node linkType: hard +"@chainsafe/as-sha256@npm:^0.4.1": + version: 0.4.1 + resolution: "@chainsafe/as-sha256@npm:0.4.1" + checksum: 6d86975e648ecdafd366802278ac15b392b252e967f3681412ec48b5a3518b936cc5e977517499882b084991446d25787d98f8f585891943688cc81549a44e9a + languageName: node + linkType: hard + +"@chainsafe/persistent-merkle-tree@npm:^0.6.1": + version: 0.6.1 + resolution: "@chainsafe/persistent-merkle-tree@npm:0.6.1" + dependencies: + "@chainsafe/as-sha256": ^0.4.1 + "@noble/hashes": ^1.3.0 + checksum: 74614b8d40970dc930d5bf741619498b0bbbde5ff24ce45fce6ad122143aa77bf57249a28175b1b972cf56bff57d529a4258b7222ab4e60c1261119b5986c51b + languageName: node + linkType: hard + +"@chainsafe/ssz@npm:^0.11.1": + version: 0.11.1 + resolution: "@chainsafe/ssz@npm:0.11.1" + dependencies: + "@chainsafe/as-sha256": ^0.4.1 + "@chainsafe/persistent-merkle-tree": ^0.6.1 + checksum: e3c2928f9ab4a0544e645f0302b9535046d1e6e1d4b3bd1c3dd6bc8e6302fddad6036d65e7900d1446f285f496051da05fa14c1bde590b511d03033907175c8f + languageName: node + linkType: hard + "@cspotcode/source-map-consumer@npm:0.8.0": version: 0.8.0 resolution: "@cspotcode/source-map-consumer@npm:0.8.0" @@ -449,6 +476,55 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/common@npm:^3.1.2": + version: 3.1.2 + resolution: "@ethereumjs/common@npm:3.1.2" + dependencies: + "@ethereumjs/util": ^8.0.6 + crc-32: ^1.2.0 + checksum: e80a8bc86476f1ce878bacb1915d91681671bb5303291cdcece26e456ac13a6158f0f59625cb02a1cfbdd7c9a7dc8b175f8d8f0fee596b3eb9dfb965465ad43d + languageName: node + linkType: hard + +"@ethereumjs/rlp@npm:^4.0.1": + version: 4.0.1 + resolution: "@ethereumjs/rlp@npm:4.0.1" + bin: + rlp: bin/rlp + checksum: 30db19c78faa2b6ff27275ab767646929207bb207f903f09eb3e4c273ce2738b45f3c82169ddacd67468b4f063d8d96035f2bf36f02b6b7e4d928eefe2e3ecbc + languageName: node + linkType: hard + +"@ethereumjs/tx@npm:^4.1.2": + version: 4.1.2 + resolution: "@ethereumjs/tx@npm:4.1.2" + dependencies: + "@chainsafe/ssz": ^0.11.1 + "@ethereumjs/common": ^3.1.2 + "@ethereumjs/rlp": ^4.0.1 + "@ethereumjs/util": ^8.0.6 + ethereum-cryptography: ^2.0.0 + peerDependencies: + c-kzg: ^1.0.8 + peerDependenciesMeta: + c-kzg: + optional: true + checksum: ad2fb692c3746cd5935b01c98b6b54046ae2a1fccff57ad2209e10446f3b279a204d7477accf05b27078445b14379314077769662142ac07117c45a5a1ea427f + languageName: node + linkType: hard + +"@ethereumjs/util@npm:^8.0.6": + version: 8.0.6 + resolution: "@ethereumjs/util@npm:8.0.6" + dependencies: + "@chainsafe/ssz": ^0.11.1 + "@ethereumjs/rlp": ^4.0.1 + ethereum-cryptography: ^2.0.0 + micro-ftch: ^0.3.1 + checksum: 034e06cddec27417318434a1a7cd7a9dc0f0b447c1f54423c515d8809c9697386eee6429d0a1c13517a85c696e6fdba570b243d882e65764c274859606027015 + languageName: node + linkType: hard + "@gar/promisify@npm:^1.1.3": version: 1.1.3 resolution: "@gar/promisify@npm:1.1.3" @@ -859,6 +935,31 @@ __metadata: languageName: node linkType: hard +"@metamask/base-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "@metamask/base-controller@npm:3.0.0" + dependencies: + "@metamask/utils": ^5.0.2 + immer: ^9.0.6 + checksum: a0853d90b024466c4108531cbf4459bd2f66fa6e0b912e42bd27cdf54262411a5601117649b6061424475ffa6b9714c5199d686c21e4d07c3b7b1ee0b4c17caa + languageName: node + linkType: hard + +"@metamask/controller-utils@npm:^4.0.0": + version: 4.0.0 + resolution: "@metamask/controller-utils@npm:4.0.0" + dependencies: + "@metamask/utils": ^5.0.2 + "@spruceid/siwe-parser": 1.1.3 + eth-ens-namehash: ^2.0.8 + eth-rpc-errors: ^4.0.2 + ethereumjs-util: ^7.0.10 + ethjs-unit: ^0.1.6 + fast-deep-equal: ^3.1.3 + checksum: 3efdaf9b0c9f6d3bb633eeb926e78bedd637b0e042bb1e3974b9829e38456709a2f26d02405499934239351be8bff1854e267df8d0a522738d630bac0aadb732 + languageName: node + linkType: hard + "@metamask/eslint-config-jest@npm:^11.0.0": version: 11.1.0 resolution: "@metamask/eslint-config-jest@npm:11.1.0" @@ -915,14 +1016,18 @@ __metadata: "@lavamoat/allow-scripts": ^2.3.1 "@lavamoat/preinstall-always-fail": ^1.0.0 "@metamask/auto-changelog": ^3.1.0 + "@metamask/base-controller": ^3.0.0 + "@metamask/controller-utils": ^4.0.0 "@metamask/eslint-config": ^11.0.1 "@metamask/eslint-config-jest": ^11.0.0 "@metamask/eslint-config-nodejs": ^11.0.1 "@metamask/eslint-config-typescript": ^11.0.0 "@types/jest": ^28.1.6 "@types/node": ^16 + "@types/whatwg-fetch": ^0.0.33 "@typescript-eslint/eslint-plugin": ^5.43.0 "@typescript-eslint/parser": ^5.43.0 + await-semaphore: ^0.1.3 depcheck: ^1.4.3 eslint: ^8.27.0 eslint-config-prettier: ^8.5.0 @@ -943,6 +1048,35 @@ __metadata: languageName: unknown linkType: soft +"@metamask/utils@npm:^5.0.2": + version: 5.0.2 + resolution: "@metamask/utils@npm:5.0.2" + dependencies: + "@ethereumjs/tx": ^4.1.2 + "@types/debug": ^4.1.7 + debug: ^4.3.4 + semver: ^7.3.8 + superstruct: ^1.0.3 + checksum: eca82e42911b2840deb4f32f0f215c5ffd14d22d68afbbe92d3180e920e509e310777b15eab29def3448f3535b66596ceb4c23666ec846adacc8e1bb093ff882 + languageName: node + linkType: hard + +"@noble/curves@npm:1.0.0, @noble/curves@npm:~1.0.0": + version: 1.0.0 + resolution: "@noble/curves@npm:1.0.0" + dependencies: + "@noble/hashes": 1.3.0 + checksum: 6bcef44d626c640dc8961819d68dd67dffb907e3b973b7c27efe0ecdd9a5c6ce62c7b9e3dfc930c66605dced7f1ec0514d191c09a2ce98d6d52b66e3315ffa79 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.0, @noble/hashes@npm:^1.3.0, @noble/hashes@npm:~1.3.0": + version: 1.3.0 + resolution: "@noble/hashes@npm:1.3.0" + checksum: d7ddb6d7c60f1ce1f87facbbef5b724cdea536fc9e7f59ae96e0fc9de96c8f1a2ae2bdedbce10f7dcc621338dfef8533daa73c873f2b5c87fa1a4e05a95c2e2e + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -1019,6 +1153,34 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.0": + version: 1.1.1 + resolution: "@scure/base@npm:1.1.1" + checksum: b4fc810b492693e7e8d0107313ac74c3646970c198bbe26d7332820886fa4f09441991023ec9aa3a2a51246b74409ab5ebae2e8ef148bbc253da79ac49130309 + languageName: node + linkType: hard + +"@scure/bip32@npm:1.3.0": + version: 1.3.0 + resolution: "@scure/bip32@npm:1.3.0" + dependencies: + "@noble/curves": ~1.0.0 + "@noble/hashes": ~1.3.0 + "@scure/base": ~1.1.0 + checksum: 6eae997f9bdf41fe848134898960ac48e645fa10e63d579be965ca331afd0b7c1b8ebac170770d237ab4099dafc35e5a82995384510025ccf2abe669f85e8918 + languageName: node + linkType: hard + +"@scure/bip39@npm:1.2.0": + version: 1.2.0 + resolution: "@scure/bip39@npm:1.2.0" + dependencies: + "@noble/hashes": ~1.3.0 + "@scure/base": ~1.1.0 + checksum: 980d761f53e63de04a9e4db840eb13bfb1bd1b664ecb04a71824c12c190f4972fd84146f3ed89b2a8e4c6bd2c17c15f8b592b7ac029e903323b0f9e2dae6916b + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.24.1": version: 0.24.28 resolution: "@sinclair/typebox@npm:0.24.28" @@ -1044,6 +1206,15 @@ __metadata: languageName: node linkType: hard +"@spruceid/siwe-parser@npm:1.1.3": + version: 1.1.3 + resolution: "@spruceid/siwe-parser@npm:1.1.3" + dependencies: + apg-js: ^4.1.1 + checksum: 708786ba2f10987c45c1fd8a6243ba6572ee7f320531616d71ff66044828bc24af66f5537ce09c9272bdae93fcc35b566a7804fe7997284f2ee5445a36e6add2 + languageName: node + linkType: hard + "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -1120,6 +1291,15 @@ __metadata: languageName: node linkType: hard +"@types/bn.js@npm:^5.1.0": + version: 5.1.1 + resolution: "@types/bn.js@npm:5.1.1" + dependencies: + "@types/node": "*" + checksum: e50ed2dd3abe997e047caf90e0352c71e54fc388679735217978b4ceb7e336e51477791b715f49fd77195ac26dd296c7bad08a3be9750e235f9b2e1edb1b51c2 + languageName: node + linkType: hard + "@types/color-name@npm:^1.1.1": version: 1.1.1 resolution: "@types/color-name@npm:1.1.1" @@ -1127,6 +1307,15 @@ __metadata: languageName: node linkType: hard +"@types/debug@npm:^4.1.7": + version: 4.1.8 + resolution: "@types/debug@npm:4.1.8" + dependencies: + "@types/ms": "*" + checksum: a9a9bb40a199e9724aa944e139a7659173a9b274798ea7efbc277cb084bc37d32fc4c00877c3496fac4fed70a23243d284adb75c00b5fdabb38a22154d18e5df + languageName: node + linkType: hard + "@types/glob@npm:^7.1.1": version: 7.1.3 resolution: "@types/glob@npm:7.1.3" @@ -1202,6 +1391,13 @@ __metadata: languageName: node linkType: hard +"@types/ms@npm:*": + version: 0.7.31 + resolution: "@types/ms@npm:0.7.31" + checksum: daadd354aedde024cce6f5aa873fefe7b71b22cd0e28632a69e8b677aeb48ae8caa1c60e5919bb781df040d116b01cb4316335167a3fc0ef6a63fa3614c0f6da + languageName: node + linkType: hard + "@types/node@npm:*, @types/node@npm:^16": version: 16.18.31 resolution: "@types/node@npm:16.18.31" @@ -1216,6 +1412,15 @@ __metadata: languageName: node linkType: hard +"@types/pbkdf2@npm:^3.0.0": + version: 3.1.0 + resolution: "@types/pbkdf2@npm:3.1.0" + dependencies: + "@types/node": "*" + checksum: d15024b1957c21cf3b8887329d9bd8dfde754cf13a09d76ae25f1391cfc62bb8b8d7b760773c5dbaa748172fba8b3e0c3dbe962af6ccbd69b76df12a48dfba40 + languageName: node + linkType: hard + "@types/prettier@npm:^2.1.5": version: 2.4.4 resolution: "@types/prettier@npm:2.4.4" @@ -1223,6 +1428,15 @@ __metadata: languageName: node linkType: hard +"@types/secp256k1@npm:^4.0.1": + version: 4.0.3 + resolution: "@types/secp256k1@npm:4.0.3" + dependencies: + "@types/node": "*" + checksum: 1bd10b9afa724084b655dc81b7b315def3d2d0e272014ef16009fa76e17537411c07c0695fdea412bc7b36d2a02687f5fea33522d55b8ef29eda42992f812913 + languageName: node + linkType: hard + "@types/semver@npm:^7.3.12": version: 7.3.13 resolution: "@types/semver@npm:7.3.13" @@ -1237,6 +1451,22 @@ __metadata: languageName: node linkType: hard +"@types/whatwg-fetch@npm:^0.0.33": + version: 0.0.33 + resolution: "@types/whatwg-fetch@npm:0.0.33" + dependencies: + "@types/whatwg-streams": "*" + checksum: 59dae90e3e22cd783c61ad57f107e36de7d4c30bde7276d3c83a3bc2f4b219c533f5f54bf8a310734ff3a71aed81d48b91c7efaf375cefdedf8879f12dd867df + languageName: node + linkType: hard + +"@types/whatwg-streams@npm:*": + version: 0.0.7 + resolution: "@types/whatwg-streams@npm:0.0.7" + checksum: 324b535e0d45fc2e47c5b5132f3d10249db893f702e27c2496c927e85055ab95cd267205d01a6dfb638f1c3c46eb76a8ccace2fc7bbd099a7e4009549933f0c7 + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 15.0.0 resolution: "@types/yargs-parser@npm:15.0.0" @@ -1576,6 +1806,13 @@ __metadata: languageName: node linkType: hard +"apg-js@npm:^4.1.1": + version: 4.1.3 + resolution: "apg-js@npm:4.1.3" + checksum: fa815838fc3c4b2fa5801419d050c5cf0ac8b5dbbc7476a9c9b8e1ae5fc78feccf01ff3ff52ef1e80c72f8b7bf39f589f1b8aaad5f5aeba399523a9ba18da127 + languageName: node + linkType: hard + "aproba@npm:^1.0.3 || ^2.0.0": version: 2.0.0 resolution: "aproba@npm:2.0.0" @@ -1662,6 +1899,13 @@ __metadata: languageName: node linkType: hard +"await-semaphore@npm:^0.1.3": + version: 0.1.3 + resolution: "await-semaphore@npm:0.1.3" + checksum: 334c86541e446378dd832168de431327a77146f70cd80b57c99cd483ce5996e3bfdadea9d795e36f0b4faacb5121f5f7a99d94297ac2bdafbc690e5b0aa5cc32 + languageName: node + linkType: hard + "babel-jest@npm:^28.1.3": version: 28.1.3 resolution: "babel-jest@npm:28.1.3" @@ -1745,6 +1989,15 @@ __metadata: languageName: node linkType: hard +"base-x@npm:^3.0.2": + version: 3.0.9 + resolution: "base-x@npm:3.0.9" + dependencies: + safe-buffer: ^5.0.1 + checksum: 957101d6fd09e1903e846fd8f69fd7e5e3e50254383e61ab667c725866bec54e5ece5ba49ce385128ae48f9ec93a26567d1d5ebb91f4d56ef4a9cc0d5a5481e8 + languageName: node + linkType: hard + "bin-links@npm:4.0.1": version: 4.0.1 resolution: "bin-links@npm:4.0.1" @@ -1764,6 +2017,34 @@ __metadata: languageName: node linkType: hard +"blakejs@npm:^1.1.0": + version: 1.2.1 + resolution: "blakejs@npm:1.2.1" + checksum: d699ba116cfa21d0b01d12014a03e484dd76d483133e6dc9eb415aa70a119f08beb3bcefb8c71840106a00b542cba77383f8be60cd1f0d4589cb8afb922eefbe + languageName: node + linkType: hard + +"bn.js@npm:4.11.6": + version: 4.11.6 + resolution: "bn.js@npm:4.11.6" + checksum: db23047bf06fdf9cf74401c8e76bca9f55313c81df382247d2c753868b368562e69171716b81b7038ada8860af18346fd4bcd1cf9d4963f923fe8e54e61cb58a + languageName: node + linkType: hard + +"bn.js@npm:^4.11.9": + version: 4.12.0 + resolution: "bn.js@npm:4.12.0" + checksum: 39afb4f15f4ea537b55eaf1446c896af28ac948fdcf47171961475724d1bb65118cca49fa6e3d67706e4790955ec0e74de584e45c8f1ef89f46c812bee5b5a12 + languageName: node + linkType: hard + +"bn.js@npm:^5.1.2, bn.js@npm:^5.2.0": + version: 5.2.1 + resolution: "bn.js@npm:5.2.1" + checksum: 3dd8c8d38055fedfa95c1d5fc3c99f8dd547b36287b37768db0abab3c239711f88ff58d18d155dd8ad902b0b0cee973747b7ae20ea12a09473272b0201c9edd3 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -1792,6 +2073,27 @@ __metadata: languageName: node linkType: hard +"brorand@npm:^1.1.0": + version: 1.1.0 + resolution: "brorand@npm:1.1.0" + checksum: 8a05c9f3c4b46572dec6ef71012b1946db6cae8c7bb60ccd4b7dd5a84655db49fe043ecc6272e7ef1f69dc53d6730b9e2a3a03a8310509a3d797a618cbee52be + languageName: node + linkType: hard + +"browserify-aes@npm:^1.2.0": + version: 1.2.0 + resolution: "browserify-aes@npm:1.2.0" + dependencies: + buffer-xor: ^1.0.3 + cipher-base: ^1.0.0 + create-hash: ^1.1.0 + evp_bytestokey: ^1.0.3 + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + checksum: 4a17c3eb55a2aa61c934c286f34921933086bf6d67f02d4adb09fcc6f2fc93977b47d9d884c25619144fccd47b3b3a399e1ad8b3ff5a346be47270114bcf7104 + languageName: node + linkType: hard + "browserslist@npm:^4.20.2": version: 4.21.3 resolution: "browserslist@npm:4.21.3" @@ -1815,6 +2117,26 @@ __metadata: languageName: node linkType: hard +"bs58@npm:^4.0.0": + version: 4.0.1 + resolution: "bs58@npm:4.0.1" + dependencies: + base-x: ^3.0.2 + checksum: b3c5365bb9e0c561e1a82f1a2d809a1a692059fae016be233a6127ad2f50a6b986467c3a50669ce4c18929dcccb297c5909314dd347a25a68c21b68eb3e95ac2 + languageName: node + linkType: hard + +"bs58check@npm:^2.1.2": + version: 2.1.2 + resolution: "bs58check@npm:2.1.2" + dependencies: + bs58: ^4.0.0 + create-hash: ^1.1.0 + safe-buffer: ^5.1.2 + checksum: 43bdf08a5dd04581b78f040bc4169480e17008da482ffe2a6507327bbc4fc5c28de0501f7faf22901cfe57fbca79cbb202ca529003fedb4cb8dccd265b38e54d + languageName: node + linkType: hard + "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -1831,6 +2153,13 @@ __metadata: languageName: node linkType: hard +"buffer-xor@npm:^1.0.3": + version: 1.0.3 + resolution: "buffer-xor@npm:1.0.3" + checksum: 10c520df29d62fa6e785e2800e586a20fc4f6dfad84bcdbd12e1e8a83856de1cb75c7ebd7abe6d036bbfab738a6cf18a3ae9c8e5a2e2eb3167ca7399ce65373a + languageName: node + linkType: hard + "cacache@npm:^16.1.0": version: 16.1.1 resolution: "cacache@npm:16.1.1" @@ -1956,6 +2285,16 @@ __metadata: languageName: node linkType: hard +"cipher-base@npm:^1.0.0, cipher-base@npm:^1.0.1, cipher-base@npm:^1.0.3": + version: 1.0.4 + resolution: "cipher-base@npm:1.0.4" + dependencies: + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + checksum: 47d3568dbc17431a339bad1fe7dff83ac0891be8206911ace3d3b818fc695f376df809bea406e759cdea07fff4b454fa25f1013e648851bec790c1d75763032e + languageName: node + linkType: hard + "cjs-module-lexer@npm:^1.0.0": version: 1.2.2 resolution: "cjs-module-lexer@npm:1.2.2" @@ -2107,6 +2446,42 @@ __metadata: languageName: node linkType: hard +"crc-32@npm:^1.2.0": + version: 1.2.2 + resolution: "crc-32@npm:1.2.2" + bin: + crc32: bin/crc32.njs + checksum: ad2d0ad0cbd465b75dcaeeff0600f8195b686816ab5f3ba4c6e052a07f728c3e70df2e3ca9fd3d4484dc4ba70586e161ca5a2334ec8bf5a41bf022a6103ff243 + languageName: node + linkType: hard + +"create-hash@npm:^1.1.0, create-hash@npm:^1.1.2, create-hash@npm:^1.2.0": + version: 1.2.0 + resolution: "create-hash@npm:1.2.0" + dependencies: + cipher-base: ^1.0.1 + inherits: ^2.0.1 + md5.js: ^1.3.4 + ripemd160: ^2.0.1 + sha.js: ^2.4.0 + checksum: 02a6ae3bb9cd4afee3fabd846c1d8426a0e6b495560a977ba46120c473cb283be6aa1cace76b5f927cf4e499c6146fb798253e48e83d522feba807d6b722eaa9 + languageName: node + linkType: hard + +"create-hmac@npm:^1.1.4, create-hmac@npm:^1.1.7": + version: 1.1.7 + resolution: "create-hmac@npm:1.1.7" + dependencies: + cipher-base: ^1.0.3 + create-hash: ^1.1.0 + inherits: ^2.0.1 + ripemd160: ^2.0.0 + safe-buffer: ^5.0.1 + sha.js: ^2.4.8 + checksum: ba12bb2257b585a0396108c72830e85f882ab659c3320c83584b1037f8ab72415095167ced80dc4ce8e446a8ecc4b2acf36d87befe0707d73b26cf9dc77440ed + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -2309,6 +2684,21 @@ __metadata: languageName: node linkType: hard +"elliptic@npm:^6.5.4": + version: 6.5.4 + resolution: "elliptic@npm:6.5.4" + dependencies: + bn.js: ^4.11.9 + brorand: ^1.1.0 + hash.js: ^1.0.0 + hmac-drbg: ^1.0.1 + inherits: ^2.0.4 + minimalistic-assert: ^1.0.1 + minimalistic-crypto-utils: ^1.0.1 + checksum: d56d21fd04e97869f7ffcc92e18903b9f67f2d4637a23c860492fbbff5a3155fd9ca0184ce0c865dd6eb2487d234ce9551335c021c376cd2d3b7cb749c7d10f4 + languageName: node + linkType: hard + "emittery@npm:^0.10.2": version: 0.10.2 resolution: "emittery@npm:0.10.2" @@ -2744,6 +3134,94 @@ __metadata: languageName: node linkType: hard +"eth-ens-namehash@npm:^2.0.8": + version: 2.0.8 + resolution: "eth-ens-namehash@npm:2.0.8" + dependencies: + idna-uts46-hx: ^2.3.1 + js-sha3: ^0.5.7 + checksum: 40ce4aeedaa4e7eb4485c8d8857457ecc46a4652396981d21b7e3a5f922d5beff63c71cb4b283c935293e530eba50b329d9248be3c433949c6bc40c850c202a3 + languageName: node + linkType: hard + +"eth-rpc-errors@npm:^4.0.2": + version: 4.0.3 + resolution: "eth-rpc-errors@npm:4.0.3" + dependencies: + fast-safe-stringify: ^2.0.6 + checksum: 5fa31d1a10fdb340733b9a55e38e7687222c501052ca20743cef4d0c911a9bbcc0cad54aa6bf3e4b428604c071ff519803060e1cbc79ddb7c9257c11d407d32a + languageName: node + linkType: hard + +"ethereum-cryptography@npm:^0.1.3": + version: 0.1.3 + resolution: "ethereum-cryptography@npm:0.1.3" + dependencies: + "@types/pbkdf2": ^3.0.0 + "@types/secp256k1": ^4.0.1 + blakejs: ^1.1.0 + browserify-aes: ^1.2.0 + bs58check: ^2.1.2 + create-hash: ^1.2.0 + create-hmac: ^1.1.7 + hash.js: ^1.1.7 + keccak: ^3.0.0 + pbkdf2: ^3.0.17 + randombytes: ^2.1.0 + safe-buffer: ^5.1.2 + scrypt-js: ^3.0.0 + secp256k1: ^4.0.1 + setimmediate: ^1.0.5 + checksum: 54bae7a4a96bd81398cdc35c91cfcc74339f71a95ed1b5b694663782e69e8e3afd21357de3b8bac9ff4877fd6f043601e200a7ad9133d94be6fd7d898ee0a449 + languageName: node + linkType: hard + +"ethereum-cryptography@npm:^2.0.0": + version: 2.0.0 + resolution: "ethereum-cryptography@npm:2.0.0" + dependencies: + "@noble/curves": 1.0.0 + "@noble/hashes": 1.3.0 + "@scure/bip32": 1.3.0 + "@scure/bip39": 1.2.0 + checksum: 958f8aab2d1b32aa759fb27a27877b3647410e8bb9aca7d65d1d477db4864cf7fc46b918eb52a1e246c25e98ee0a35a632c88b496aeaefa13469ee767a76c8db + languageName: node + linkType: hard + +"ethereumjs-util@npm:^7.0.10": + version: 7.1.5 + resolution: "ethereumjs-util@npm:7.1.5" + dependencies: + "@types/bn.js": ^5.1.0 + bn.js: ^5.1.2 + create-hash: ^1.1.2 + ethereum-cryptography: ^0.1.3 + rlp: ^2.2.4 + checksum: 27a3c79d6e06b2df34b80d478ce465b371c8458b58f5afc14d91c8564c13363ad336e6e83f57eb0bd719fde94d10ee5697ceef78b5aa932087150c5287b286d1 + languageName: node + linkType: hard + +"ethjs-unit@npm:^0.1.6": + version: 0.1.6 + resolution: "ethjs-unit@npm:0.1.6" + dependencies: + bn.js: 4.11.6 + number-to-bn: 1.7.0 + checksum: df6b4752ff7461a59a20219f4b1684c631ea601241c39660e3f6c6bd63c950189723841c22b3c6c0ebeb3c9fc99e0e803e3c613101206132603705fcbcf4def5 + languageName: node + linkType: hard + +"evp_bytestokey@npm:^1.0.3": + version: 1.0.3 + resolution: "evp_bytestokey@npm:1.0.3" + dependencies: + md5.js: ^1.3.4 + node-gyp: latest + safe-buffer: ^5.1.1 + checksum: ad4e1577f1a6b721c7800dcc7c733fe01f6c310732bb5bf2240245c2a5b45a38518b91d8be2c610611623160b9d1c0e91f1ce96d639f8b53e8894625cf20fa45 + languageName: node + linkType: hard + "execa@npm:^5.0.0, execa@npm:^5.1.1": version: 5.1.1 resolution: "execa@npm:5.1.1" @@ -2822,6 +3300,13 @@ __metadata: languageName: node linkType: hard +"fast-safe-stringify@npm:^2.0.6": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: a851cbddc451745662f8f00ddb622d6766f9bd97642dabfd9a405fb0d646d69fc0b9a1243cbf67f5f18a39f40f6fa821737651ff1bceeba06c9992ca2dc5bd3d + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.11.0 resolution: "fastq@npm:1.11.0" @@ -3195,6 +3680,38 @@ __metadata: languageName: node linkType: hard +"hash-base@npm:^3.0.0": + version: 3.1.0 + resolution: "hash-base@npm:3.1.0" + dependencies: + inherits: ^2.0.4 + readable-stream: ^3.6.0 + safe-buffer: ^5.2.0 + checksum: 26b7e97ac3de13cb23fc3145e7e3450b0530274a9562144fc2bf5c1e2983afd0e09ed7cc3b20974ba66039fad316db463da80eb452e7373e780cbee9a0d2f2dc + languageName: node + linkType: hard + +"hash.js@npm:^1.0.0, hash.js@npm:^1.0.3, hash.js@npm:^1.1.7": + version: 1.1.7 + resolution: "hash.js@npm:1.1.7" + dependencies: + inherits: ^2.0.3 + minimalistic-assert: ^1.0.1 + checksum: e350096e659c62422b85fa508e4b3669017311aa4c49b74f19f8e1bc7f3a54a584fdfd45326d4964d6011f2b2d882e38bea775a96046f2a61b7779a979629d8f + languageName: node + linkType: hard + +"hmac-drbg@npm:^1.0.1": + version: 1.0.1 + resolution: "hmac-drbg@npm:1.0.1" + dependencies: + hash.js: ^1.0.3 + minimalistic-assert: ^1.0.0 + minimalistic-crypto-utils: ^1.0.1 + checksum: bd30b6a68d7f22d63f10e1888aee497d7c2c5c0bb469e66bbdac99f143904d1dfe95f8131f95b3e86c86dd239963c9d972fcbe147e7cffa00e55d18585c43fe0 + languageName: node + linkType: hard + "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -3255,6 +3772,15 @@ __metadata: languageName: node linkType: hard +"idna-uts46-hx@npm:^2.3.1": + version: 2.3.1 + resolution: "idna-uts46-hx@npm:2.3.1" + dependencies: + punycode: 2.1.0 + checksum: d434c3558d2bc1090eb90f978f995101f469cb26593414ac57aa082c9352e49972b332c6e4188b9b15538172ccfeae3121e5a19b96972a97e6aeb0676d86639c + languageName: node + linkType: hard + "ignore@npm:^5.1.1, ignore@npm:^5.1.8, ignore@npm:^5.2.0": version: 5.2.1 resolution: "ignore@npm:5.2.1" @@ -3262,6 +3788,13 @@ __metadata: languageName: node linkType: hard +"immer@npm:^9.0.6": + version: 9.0.21 + resolution: "immer@npm:9.0.21" + checksum: 70e3c274165995352f6936695f0ef4723c52c92c92dd0e9afdfe008175af39fa28e76aafb3a2ca9d57d1fb8f796efc4dd1e1cc36f18d33fa5b74f3dfb0375432 + languageName: node + linkType: hard + "immutable@npm:^4.0.0": version: 4.1.0 resolution: "immutable@npm:4.1.0" @@ -3322,7 +3855,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.3": +"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -3441,6 +3974,13 @@ __metadata: languageName: node linkType: hard +"is-hex-prefixed@npm:1.0.0": + version: 1.0.0 + resolution: "is-hex-prefixed@npm:1.0.0" + checksum: 5ac58e6e528fb029cc43140f6eeb380fad23d0041cc23154b87f7c9a1b728bcf05909974e47248fd0b7fcc11ba33cf7e58d64804883056fabd23e2b898be41de + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -4054,6 +4594,13 @@ __metadata: languageName: node linkType: hard +"js-sha3@npm:^0.5.7": + version: 0.5.7 + resolution: "js-sha3@npm:0.5.7" + checksum: 973a28ea4b26cc7f12d2ab24f796e24ee4a71eef45a6634a052f6eb38cf8b2333db798e896e6e094ea6fa4dfe8e42a2a7942b425cf40da3f866623fd05bb91ea + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -4155,6 +4702,18 @@ __metadata: languageName: node linkType: hard +"keccak@npm:^3.0.0": + version: 3.0.3 + resolution: "keccak@npm:3.0.3" + dependencies: + node-addon-api: ^2.0.0 + node-gyp: latest + node-gyp-build: ^4.2.0 + readable-stream: ^3.6.0 + checksum: f08f04f5cc87013a3fc9e87262f761daff38945c86dd09c01a7f7930a15ae3e14f93b310ef821dcc83675a7b814eb1c983222399a2f263ad980251201d1b9a99 + languageName: node + linkType: hard + "kleur@npm:^3.0.3": version: 3.0.3 resolution: "kleur@npm:3.0.3" @@ -4326,6 +4885,17 @@ __metadata: languageName: node linkType: hard +"md5.js@npm:^1.3.4": + version: 1.3.5 + resolution: "md5.js@npm:1.3.5" + dependencies: + hash-base: ^3.0.0 + inherits: ^2.0.1 + safe-buffer: ^5.1.2 + checksum: 098494d885684bcc4f92294b18ba61b7bd353c23147fbc4688c75b45cb8590f5a95fd4584d742415dcc52487f7a1ef6ea611cfa1543b0dc4492fe026357f3f0c + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -4340,6 +4910,13 @@ __metadata: languageName: node linkType: hard +"micro-ftch@npm:^0.3.1": + version: 0.3.1 + resolution: "micro-ftch@npm:0.3.1" + checksum: 0e496547253a36e98a83fb00c628c53c3fb540fa5aaeaf718438873785afd193244988c09d219bb1802984ff227d04938d9571ef90fe82b48bd282262586aaff + languageName: node + linkType: hard + "micromatch@npm:^4.0.4": version: 4.0.4 resolution: "micromatch@npm:4.0.4" @@ -4357,6 +4934,20 @@ __metadata: languageName: node linkType: hard +"minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": + version: 1.0.1 + resolution: "minimalistic-assert@npm:1.0.1" + checksum: cc7974a9268fbf130fb055aff76700d7e2d8be5f761fb5c60318d0ed010d839ab3661a533ad29a5d37653133385204c503bfac995aaa4236f4e847461ea32ba7 + languageName: node + linkType: hard + +"minimalistic-crypto-utils@npm:^1.0.1": + version: 1.0.1 + resolution: "minimalistic-crypto-utils@npm:1.0.1" + checksum: 6e8a0422b30039406efd4c440829ea8f988845db02a3299f372fceba56ffa94994a9c0f2fd70c17f9969eedfbd72f34b5070ead9656a34d3f71c0bd72583a0ed + languageName: node + linkType: hard + "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -4532,6 +5123,26 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^2.0.0": + version: 2.0.2 + resolution: "node-addon-api@npm:2.0.2" + dependencies: + node-gyp: latest + checksum: 31fb22d674648204f8dd94167eb5aac896c841b84a9210d614bf5d97c74ef059cc6326389cf0c54d2086e35312938401d4cc82e5fcd679202503eb8ac84814f8 + languageName: node + linkType: hard + +"node-gyp-build@npm:^4.2.0": + version: 4.6.0 + resolution: "node-gyp-build@npm:4.6.0" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 25d78c5ef1f8c24291f4a370c47ba52fcea14f39272041a90a7894cd50d766f7c8cb8fb06c0f42bf6f69b204b49d9be3c8fc344aac09714d5bdb95965499eb15 + languageName: node + linkType: hard + "node-gyp@npm:^9.0.0": version: 9.3.1 resolution: "node-gyp@npm:9.3.1" @@ -4643,6 +5254,16 @@ __metadata: languageName: node linkType: hard +"number-to-bn@npm:1.7.0": + version: 1.7.0 + resolution: "number-to-bn@npm:1.7.0" + dependencies: + bn.js: 4.11.6 + strip-hex-prefix: 1.0.0 + checksum: 5b8c9dbe7b49dc7a069e5f0ba4e197257c89db11463478cb002fee7a34dc8868636952bd9f6310e5fdf22b266e0e6dffb5f9537c741734718107e90ae59b3de4 + languageName: node + linkType: hard + "object-inspect@npm:^1.12.0, object-inspect@npm:^1.9.0": version: 1.12.2 resolution: "object-inspect@npm:1.12.2" @@ -4820,6 +5441,19 @@ __metadata: languageName: node linkType: hard +"pbkdf2@npm:^3.0.17": + version: 3.1.2 + resolution: "pbkdf2@npm:3.1.2" + dependencies: + create-hash: ^1.1.2 + create-hmac: ^1.1.4 + ripemd160: ^2.0.1 + safe-buffer: ^5.0.1 + sha.js: ^2.4.8 + checksum: 2c950a100b1da72123449208e231afc188d980177d021d7121e96a2de7f2abbc96ead2b87d03d8fe5c318face097f203270d7e27908af9f471c165a4e8e69c92 + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -4948,6 +5582,13 @@ __metadata: languageName: node linkType: hard +"punycode@npm:2.1.0": + version: 2.1.0 + resolution: "punycode@npm:2.1.0" + checksum: d125d8f86cd89303c33bad829388c49ca23197e16ccf8cd398dcbd81b026978f6543f5066c66825b25b1dfea7790a42edbeea82908e103474931789714ab86cd + languageName: node + linkType: hard + "punycode@npm:^2.1.0": version: 2.1.1 resolution: "punycode@npm:2.1.1" @@ -4972,6 +5613,15 @@ __metadata: languageName: node linkType: hard +"randombytes@npm:^2.1.0": + version: 2.1.0 + resolution: "randombytes@npm:2.1.0" + dependencies: + safe-buffer: ^5.1.0 + checksum: d779499376bd4cbb435ef3ab9a957006c8682f343f14089ed5f27764e4645114196e75b7f6abf1cbd84fd247c0cb0651698444df8c9bf30e62120fbbc52269d6 + languageName: node + linkType: hard + "react-is@npm:^18.0.0": version: 18.2.0 resolution: "react-is@npm:18.2.0" @@ -5129,6 +5779,27 @@ __metadata: languageName: node linkType: hard +"ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": + version: 2.0.2 + resolution: "ripemd160@npm:2.0.2" + dependencies: + hash-base: ^3.0.0 + inherits: ^2.0.1 + checksum: 006accc40578ee2beae382757c4ce2908a826b27e2b079efdcd2959ee544ddf210b7b5d7d5e80467807604244e7388427330f5c6d4cd61e6edaddc5773ccc393 + languageName: node + linkType: hard + +"rlp@npm:^2.2.4": + version: 2.2.7 + resolution: "rlp@npm:2.2.7" + dependencies: + bn.js: ^5.2.0 + bin: + rlp: bin/rlp + checksum: 3db4dfe5c793f40ac7e0be689a1f75d05e6f2ca0c66189aeb62adab8c436b857ab4420a419251ee60370d41d957a55698fc5e23ab1e1b41715f33217bc4bb558 + languageName: node + linkType: hard + "run-async@npm:^2.3.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -5145,6 +5816,13 @@ __metadata: languageName: node linkType: hard +"safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 + languageName: node + linkType: hard + "safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" @@ -5152,13 +5830,6 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:~5.2.0": - version: 5.2.1 - resolution: "safe-buffer@npm:5.2.1" - checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 - languageName: node - linkType: hard - "safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -5179,6 +5850,13 @@ __metadata: languageName: node linkType: hard +"scrypt-js@npm:^3.0.0": + version: 3.0.1 + resolution: "scrypt-js@npm:3.0.1" + checksum: b7c7d1a68d6ca946f2fbb0778e0c4ec63c65501b54023b2af7d7e9f48fdb6c6580d6f7675cd53bda5944c5ebc057560d5a6365079752546865defb3b79dea454 + languageName: node + linkType: hard + "scss-parser@npm:^1.0.4": version: 1.0.6 resolution: "scss-parser@npm:1.0.6" @@ -5189,6 +5867,18 @@ __metadata: languageName: node linkType: hard +"secp256k1@npm:^4.0.1": + version: 4.0.3 + resolution: "secp256k1@npm:4.0.3" + dependencies: + elliptic: ^6.5.4 + node-addon-api: ^2.0.0 + node-gyp: latest + node-gyp-build: ^4.2.0 + checksum: 21e219adc0024fbd75021001358780a3cc6ac21273c3fcaef46943af73969729709b03f1df7c012a0baab0830fb9a06ccc6b42f8d50050c665cb98078eab477b + languageName: node + linkType: hard + "semver-compare@npm:^1.0.0": version: 1.0.0 resolution: "semver-compare@npm:1.0.0" @@ -5223,6 +5913,25 @@ __metadata: languageName: node linkType: hard +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd + languageName: node + linkType: hard + +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": + version: 2.4.11 + resolution: "sha.js@npm:2.4.11" + dependencies: + inherits: ^2.0.1 + safe-buffer: ^5.0.1 + bin: + sha.js: ./bin.js + checksum: ebd3f59d4b799000699097dadb831c8e3da3eb579144fd7eb7a19484cbcbb7aca3c68ba2bb362242eb09e33217de3b4ea56e4678184c334323eca24a58e3ad07 + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -5502,6 +6211,15 @@ __metadata: languageName: node linkType: hard +"strip-hex-prefix@npm:1.0.0": + version: 1.0.0 + resolution: "strip-hex-prefix@npm:1.0.0" + dependencies: + is-hex-prefixed: 1.0.0 + checksum: 4cafe7caee1d281d3694d14920fd5d3c11adf09371cef7e2ccedd5b83efd9e9bd2219b5d6ce6e809df6e0f437dc9d30db1192116580875698aad164a6d6b285b + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -5509,6 +6227,13 @@ __metadata: languageName: node linkType: hard +"superstruct@npm:^1.0.3": + version: 1.0.3 + resolution: "superstruct@npm:1.0.3" + checksum: 761790bb111e6e21ddd608299c252f3be35df543263a7ebbc004e840d01fcf8046794c274bcb351bdf3eae4600f79d317d085cdbb19ca05803a4361840cc9bb1 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" From 4717b6239b9b8ef88b9a36674087f4ce0ae7cef7 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 31 May 2023 14:29:46 +0530 Subject: [PATCH 02/33] Fix build --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 59b5502b..5d29cd5a 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,9 @@ }, "lavamoat": { "allowScripts": { - "@lavamoat/preinstall-always-fail": false + "@lavamoat/preinstall-always-fail": false, + "@metamask/controller-utils>ethereumjs-util>ethereum-cryptography>keccak": false, + "@metamask/controller-utils>ethereumjs-util>ethereum-cryptography>secp256k1": false } } } From 3b35aa7e70344a957f67047a44c246e6392d7a78 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 31 May 2023 14:55:16 +0530 Subject: [PATCH 03/33] fix --- global.d.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 global.d.ts diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 00000000..31e0a073 --- /dev/null +++ b/global.d.ts @@ -0,0 +1,4 @@ +export const Blob: any; +export const ReadableStream: any; +export const window: any; +export const FormData: any; From e39ec39da3f7aba62a48de5f1b48bf9f2dae02ef Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 31 May 2023 14:55:29 +0530 Subject: [PATCH 04/33] fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5d29cd5a..43320fe3 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "dependencies": { "@metamask/base-controller": "^3.0.0", "@metamask/controller-utils": "^4.0.0", - "@types/whatwg-fetch": "^0.0.33", "await-semaphore": "^0.1.3" }, "devDependencies": { @@ -46,6 +45,7 @@ "@metamask/eslint-config-typescript": "^11.0.0", "@types/jest": "^28.1.6", "@types/node": "^16", + "@types/whatwg-fetch": "^0.0.33", "@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/parser": "^5.43.0", "depcheck": "^1.4.3", From a81b9a13cf7e7a2c596715622462522e895028ac Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 31 May 2023 14:57:51 +0530 Subject: [PATCH 05/33] fix --- global.d.ts | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 global.d.ts diff --git a/global.d.ts b/global.d.ts deleted file mode 100644 index 31e0a073..00000000 --- a/global.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const Blob: any; -export const ReadableStream: any; -export const window: any; -export const FormData: any; From 828f2f9ff8a04e75e7a8627c3bc0f433d5515113 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 1 Jun 2023 12:12:42 +0530 Subject: [PATCH 06/33] Fix build --- package.json | 1 - tsconfig.json | 2 +- yarn.lock | 17 ----------------- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/package.json b/package.json index 43320fe3..d2578461 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "@metamask/eslint-config-typescript": "^11.0.0", "@types/jest": "^28.1.6", "@types/node": "^16", - "@types/whatwg-fetch": "^0.0.33", "@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/parser": "^5.43.0", "depcheck": "^1.4.3", diff --git a/tsconfig.json b/tsconfig.json index 23c8ceb7..7483086d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "esModuleInterop": true, "exactOptionalPropertyTypes": true, "forceConsistentCasingInFileNames": true, - "lib": ["ES2020"], + "lib": ["ES2020", "DOM"], "module": "CommonJS", "moduleResolution": "node", "noEmit": true, diff --git a/yarn.lock b/yarn.lock index 38f600c9..d9fa283d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1024,7 +1024,6 @@ __metadata: "@metamask/eslint-config-typescript": ^11.0.0 "@types/jest": ^28.1.6 "@types/node": ^16 - "@types/whatwg-fetch": ^0.0.33 "@typescript-eslint/eslint-plugin": ^5.43.0 "@typescript-eslint/parser": ^5.43.0 await-semaphore: ^0.1.3 @@ -1451,22 +1450,6 @@ __metadata: languageName: node linkType: hard -"@types/whatwg-fetch@npm:^0.0.33": - version: 0.0.33 - resolution: "@types/whatwg-fetch@npm:0.0.33" - dependencies: - "@types/whatwg-streams": "*" - checksum: 59dae90e3e22cd783c61ad57f107e36de7d4c30bde7276d3c83a3bc2f4b219c533f5f54bf8a310734ff3a71aed81d48b91c7efaf375cefdedf8879f12dd867df - languageName: node - linkType: hard - -"@types/whatwg-streams@npm:*": - version: 0.0.7 - resolution: "@types/whatwg-streams@npm:0.0.7" - checksum: 324b535e0d45fc2e47c5b5132f3d10249db893f702e27c2496c927e85055ab95cd267205d01a6dfb638f1c3c46eb76a8ccace2fc7bbd099a7e4009549933f0c7 - languageName: node - linkType: hard - "@types/yargs-parser@npm:*": version: 15.0.0 resolution: "@types/yargs-parser@npm:15.0.0" From b66ccd69a2e9d9e2c944b5db362770961cf50ec6 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 1 Jun 2023 12:20:53 +0530 Subject: [PATCH 07/33] Fix build --- src/ppom-controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index 51badd34..a0af38b6 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -304,7 +304,7 @@ export class PPOMController extends BaseController< */ async #fetchBlob(input: string): Promise { const response = await safelyExecute( - () => fetch(input, { cache: 'no-cache' }), + async () => fetch(input, { cache: 'no-cache' }), true, ); @@ -324,7 +324,7 @@ export class PPOMController extends BaseController< */ async #fetchVersionInfo(input: string): Promise { const response = await safelyExecute( - () => fetch(input, { cache: 'no-cache' }), + async () => fetch(input, { cache: 'no-cache' }), true, ); switch (response?.status) { From 680683a9df22b62f549de2c8b3922237be436bde Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 5 Jun 2023 14:58:27 +0530 Subject: [PATCH 08/33] Update --- jest.config.js | 4 ---- src/ppom.ts | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/jest.config.js b/jest.config.js index 9db735cd..2ae5ebed 100644 --- a/jest.config.js +++ b/jest.config.js @@ -28,15 +28,11 @@ module.exports = { coverageDirectory: 'coverage', // An array of regexp pattern strings used to skip coverage collection -<<<<<<< HEAD - coveragePathIgnorePatterns: ['/node_modules/', '/src/ppom.ts'], -======= coveragePathIgnorePatterns: [ '/node_modules/', 'src/ppom.d.ts', 'src/index.ts', ], ->>>>>>> 6ae1fc4 (Adding controller) // Indicates which provider should be used to instrument code for coverage coverageProvider: 'babel', diff --git a/src/ppom.ts b/src/ppom.ts index f63fa025..bd742429 100644 --- a/src/ppom.ts +++ b/src/ppom.ts @@ -292,7 +292,10 @@ async function load(module, imports) { function getImports() { const imports = {}; - imports.wbg = {}; + (imports as any).wbg = {}; + imports.wbg.__wbindgen_object_drop_ref = function (arg0) { + takeObject(arg0); + }; imports.wbg.__wbg_log_51761b27de744db2 = function (arg0, arg1) { console.log(getStringFromWasm0(arg0, arg1)); }; From 5cb4bb118a4baf786ab5f00aff482d8655bd23d7 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 5 Jun 2023 15:00:13 +0530 Subject: [PATCH 09/33] Update --- jest.config.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/jest.config.js b/jest.config.js index 2ae5ebed..09567038 100644 --- a/jest.config.js +++ b/jest.config.js @@ -161,9 +161,7 @@ module.exports = { // ], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], + testPathIgnorePatterns: ['src/ppom.js'], // The regexp pattern or array of patterns that Jest uses to detect test files // testRegex: [], From 1fe518762761235ed39fbea39411e0db53b4c7df Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 5 Jun 2023 15:01:45 +0530 Subject: [PATCH 10/33] Update --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 09567038..b061a735 100644 --- a/jest.config.js +++ b/jest.config.js @@ -161,7 +161,7 @@ module.exports = { // ], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - testPathIgnorePatterns: ['src/ppom.js'], + testPathIgnorePatterns: ['src/ppom.ts'], // The regexp pattern or array of patterns that Jest uses to detect test files // testRegex: [], From 00964f52b89fde1607b717492cf76796de930b22 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 5 Jun 2023 15:05:25 +0530 Subject: [PATCH 11/33] Update --- jest.config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index b061a735..2ae5ebed 100644 --- a/jest.config.js +++ b/jest.config.js @@ -161,7 +161,9 @@ module.exports = { // ], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - testPathIgnorePatterns: ['src/ppom.ts'], + // testPathIgnorePatterns: [ + // "/node_modules/" + // ], // The regexp pattern or array of patterns that Jest uses to detect test files // testRegex: [], From 3ee76406945c0a39db18f7e6142daf2e741c5a34 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 5 Jun 2023 17:47:38 +0530 Subject: [PATCH 12/33] Fix --- src/ppom-controller.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ppom-controller.test.ts b/src/ppom-controller.test.ts index b0df9577..f1a8334a 100644 --- a/src/ppom-controller.test.ts +++ b/src/ppom-controller.test.ts @@ -7,7 +7,7 @@ Object.defineProperty(globalThis, 'fetch', { value: () => undefined, }); -jest.mock('./ppom.js', () => ({ +jest.mock('./ppom.ts', () => ({ PPOM: class PPOMClass { #jsonRpcRequest; From c00b3a0041ffe4052294a61fcc33c31993c0eb4a Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 8 Jun 2023 15:57:28 +0530 Subject: [PATCH 13/33] Update --- src/ppom.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ppom.ts b/src/ppom.ts index bd742429..d9c6be70 100644 --- a/src/ppom.ts +++ b/src/ppom.ts @@ -292,10 +292,7 @@ async function load(module, imports) { function getImports() { const imports = {}; - (imports as any).wbg = {}; - imports.wbg.__wbindgen_object_drop_ref = function (arg0) { - takeObject(arg0); - }; + imports.wbg = {}; imports.wbg.__wbg_log_51761b27de744db2 = function (arg0, arg1) { console.log(getStringFromWasm0(arg0, arg1)); }; @@ -484,4 +481,5 @@ async function init(input) { return finalizeInit(instance, module); } -export { initSync, init as ppomInit }; +export { initSync }; +export default init; From f035ab13b28366c9266d58b4682b6d03d44545ab Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Mon, 29 May 2023 11:12:52 +0530 Subject: [PATCH 14/33] Update src/ppom-storage.ts Co-authored-by: Elliot Winkler --- src/ppom-storage.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ppom-storage.ts b/src/ppom-storage.ts index a5946829..ebaa543c 100644 --- a/src/ppom-storage.ts +++ b/src/ppom-storage.ts @@ -44,8 +44,8 @@ export type StorageKey = { * @property dir - Get list of all files in storage. */ export type StorageBackend = { - read(key: StorageKey, checksum: string): Promise; - write(key: StorageKey, data: ArrayBuffer, checksum: string): Promise; + read(key: StorageKey): Promise; + write(key: StorageKey, data: ArrayBuffer): Promise; delete(key: StorageKey): Promise; dir(): Promise; }; From 855747ff735a8581d41abc942601ca3ba3be6371 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 31 May 2023 11:35:08 +0530 Subject: [PATCH 15/33] Remove checksum --- src/ppom-storage.test.ts | 7 ++++--- src/ppom-storage.ts | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ppom-storage.test.ts b/src/ppom-storage.test.ts index bfea4375..ec7167c5 100644 --- a/src/ppom-storage.test.ts +++ b/src/ppom-storage.test.ts @@ -9,6 +9,7 @@ import { PPOMStorage, StorageKey } from './ppom-storage'; const DUMMY_CHECKSUM = 'DUMMY_CHECKSUM'; const DUMMY_NAME = 'DUMMY_NAME'; const DUMMY_CHAINID = '1'; +const ARRAY_BUFFER_DATA = new ArrayBuffer(123); const getFileData = (data = {}) => ({ chainId: DUMMY_CHAINID, @@ -24,7 +25,7 @@ describe('PPOMStorage', () => { describe('readFile', () => { it('should return data', async () => { const ppomStorage = new PPOMStorage({ - storageBackend: storageBackendReturningData, + storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), readMetadata: () => [simpleFileData], writeMetadata: () => undefined, }); @@ -34,7 +35,7 @@ describe('PPOMStorage', () => { it('should throw error if file metadata not found', async () => { const ppomStorage = new PPOMStorage({ - storageBackend: storageBackendReturningData, + storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), readMetadata: () => [], writeMetadata: () => undefined, }); @@ -107,7 +108,7 @@ describe('PPOMStorage', () => { it('should return metadata of file if updated file is found in storage', async () => { const mockWriteMetadata = jest.fn(); const ppomStorage = new PPOMStorage({ - storageBackend: storageBackendReturningData, + storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), readMetadata: () => [simpleFileData], writeMetadata: mockWriteMetadata, }); diff --git a/src/ppom-storage.ts b/src/ppom-storage.ts index ebaa543c..a5946829 100644 --- a/src/ppom-storage.ts +++ b/src/ppom-storage.ts @@ -44,8 +44,8 @@ export type StorageKey = { * @property dir - Get list of all files in storage. */ export type StorageBackend = { - read(key: StorageKey): Promise; - write(key: StorageKey, data: ArrayBuffer): Promise; + read(key: StorageKey, checksum: string): Promise; + write(key: StorageKey, data: ArrayBuffer, checksum: string): Promise; delete(key: StorageKey): Promise; dir(): Promise; }; From ef6232e7508eca68ef90a1b10876aae8f6c6d6f3 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 8 Jun 2023 15:59:32 +0530 Subject: [PATCH 16/33] Update src/ppom-controller.ts Co-authored-by: Olusegun Akintayo --- src/ppom-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index a0af38b6..afe6400c 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -14,7 +14,7 @@ import { FileInfo, } from './ppom-storage'; -const DAY = 1000 * 60 * 60 * 24; +const DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24; /** * @type PPOMFileVersion From 93269ef0c28b26d0f56157dd080a4f01e7797a7b Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 8 Jun 2023 17:44:32 +0530 Subject: [PATCH 17/33] Updates --- src/ppom-controller.ts | 286 ++++++++++++++++++++++++++--------------- 1 file changed, 179 insertions(+), 107 deletions(-) diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index afe6400c..4eb8e002 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -1,7 +1,6 @@ import { - BaseController, - BaseConfig, - BaseState, + BaseControllerV2, + RestrictedControllerMessenger, } from '@metamask/base-controller'; import { safelyExecute } from '@metamask/controller-utils'; import { Mutex } from 'await-semaphore'; @@ -31,7 +30,7 @@ type PPOMFileVersion = FileInfo & { type PPOMVersionResponse = PPOMFileVersion[]; /** - * @type PPOMState + * @type PPOMControllerState * * Controller state * @property lastFetched - Time when files were last updated. @@ -40,22 +39,22 @@ type PPOMVersionResponse = PPOMFileVersion[]; * @property versionInfo - Version information fetched from CDN. * @property storageMetadata - Metadata of files storaged in storage. */ -export type PPOMState = BaseState & { +export type PPOMControllerState = { lastFetched: number; lastChainId: string; newChainId: string; versionInfo: PPOMVersionResponse; storageMetadata: PPOMFileMetadata; + refreshInterval: number; }; -/** - * @type PPOMControllerConfig - * - * Controller configuration - * @property refreshInterval - Polling interval used to fetch new PPOM lists - */ -export type PPOMControllerConfig = BaseConfig & { - refreshInterval: number; +const stateMetaData = { + lastFetched: { persist: false, anonymous: false }, + lastChainId: { persist: false, anonymous: false }, + newChainId: { persist: false, anonymous: false }, + versionInfo: { persist: false, anonymous: false }, + storageMetadata: { persist: false, anonymous: false }, + refreshInterval: { persist: false, anonymous: false }, }; const PPOM_DATA_NAME = 'data'; @@ -66,6 +65,42 @@ const PPOM_CDN_BASE_URL = 'https://storage.googleapis.com/ppom-cdn/'; const PPOM_VERSION = 'ppom_version.json'; const PPOM_VERSION_PATH = `${PPOM_CDN_BASE_URL}${PPOM_VERSION}`; +const controllerName = 'PPOMController'; + +export type Clear = { + type: `${typeof controllerName}:clear`; + handler: () => void; +}; + +export type UsePPOM = { + type: `${typeof controllerName}:usePPOM`; + handler: (callback: (ppom: PPOM) => Promise) => Promise; +}; + +export type SetRefreshInterval = { + type: `${typeof controllerName}:setRefreshInterval`; + handler: (interval: number) => void; +}; + +export type UpdatePPOM = { + type: `${typeof controllerName}:updatePPOM`; + handler: () => void; +}; + +export type PPOMControllerActions = + | Clear + | UsePPOM + | SetRefreshInterval + | UpdatePPOM; + +export type PPOMControllerMessenger = RestrictedControllerMessenger< + typeof controllerName, + PPOMControllerActions, + never, + never, + never +>; + /** * PPOMController * Controller responsible for managing the PPOM @@ -76,15 +111,11 @@ const PPOM_VERSION_PATH = `${PPOM_CDN_BASE_URL}${PPOM_VERSION}`; * @property ppom - The PPOM instance * @property provider - The provider used to create the PPOM instance */ -export class PPOMController extends BaseController< - PPOMControllerConfig, - PPOMState +export class PPOMController extends BaseControllerV2< + typeof controllerName, + PPOMControllerState, + PPOMControllerMessenger > { - /** - * Name of this controller used during composition - */ - override name = 'PPOMController'; - #ppom: PPOM | undefined; #provider: any; @@ -97,47 +128,51 @@ export class PPOMController extends BaseController< */ #ppomMutex: Mutex; + #defaultState: PPOMControllerState; + /** * Creates a PPOMController instance. * * @param options - Constructor options. - * @param options.storageBackend - The storage backend to use for storing PPOM data. - * @param options.provider - The provider used to create the PPOM instance. * @param options.chainId - Id of current chain. + * @param options.messenger - Controller messenger. * @param options.onNetworkChange - Callback tobe invoked when network changes. - * @param options.config - The controller configuration. + * @param options.provider - The provider used to create the PPOM instance. * @param options.state - The controller state. + * @param options.storageBackend - The storage backend to use for storing PPOM data. * @returns The PPOMController instance. */ constructor({ - storageBackend, - provider, chainId, + messenger, onNetworkChange, - config, + provider, state, + storageBackend, }: { - storageBackend: StorageBackend; - provider: any; chainId: string; + messenger: PPOMControllerMessenger; onNetworkChange: (callback: (chainId: string) => void) => void; - config?: PPOMControllerConfig; - state?: PPOMState; + provider: any; + state?: PPOMControllerState; + storageBackend: StorageBackend; }) { - const defaultConfig = { - refreshInterval: DAY, - }; const defaultState = { lastFetched: 0, versionInfo: [], storageMetadata: [], lastChainId: '', newChainId: chainId, + refreshInterval: DAY_IN_MILLISECONDS, }; - super(config ?? defaultConfig, state ?? defaultState); + super({ + name: controllerName, + metadata: stateMetaData, + messenger, + state: state ?? defaultState, + }); - this.defaultConfig = defaultConfig; - this.defaultState = defaultState; + this.#defaultState = defaultState; this.#provider = provider; this.#storage = new PPOMStorage({ @@ -146,16 +181,27 @@ export class PPOMController extends BaseController< return this.state.storageMetadata; }, writeMetadata: (metadata) => { - this.update({ storageMetadata: metadata }); + this.update((draftState) => { + draftState.storageMetadata = metadata; + }); }, }); this.#ppomMutex = new Mutex(); onNetworkChange((id: string) => { - this.update({ newChainId: id }); + this.update((draftState) => { + draftState.newChainId = id; + }); }); - this.initialize(); + this.#registerMessageHandlers(); + } + + /** + * Clear the controller state. + */ + clear(): void { + this.update(() => this.#defaultState); } /** @@ -166,34 +212,9 @@ export class PPOMController extends BaseController< * @param interval - The new interval in ms. */ setRefreshInterval(interval: number) { - this.configure({ refreshInterval: interval }, false, false); - } - - /** - * Determine if an update to the ppom configuration is needed. - * The function will return true if - * - the chainId has changed - * - the ppom is out of date - * - the ppom is not initialized. - * - * @returns True if PPOM data requires update. - */ - async #shouldUpdate(): Promise { - if ( - this.state.newChainId !== this.state.lastChainId || - this.#isOutOfDate() - ) { - return true; - } - - return this.#ppom === undefined; - } - - /* - * check if the ppom is out of date - */ - #isOutOfDate(): boolean { - return Date.now() - this.state.lastFetched >= this.config.refreshInterval; + this.update((draftState) => { + draftState.refreshInterval = interval; + }); } /** @@ -210,7 +231,9 @@ export class PPOMController extends BaseController< await this.#updateVersionInfo(); } - this.update({ lastChainId: this.state.newChainId }); + this.update((draftState) => { + draftState.lastChainId = this.state.newChainId; + }); const storageMetadata = await this.#storage.syncMetadata( this.state.versionInfo, @@ -225,6 +248,78 @@ export class PPOMController extends BaseController< } } + /** + * Use the PPOM. + * This function receives a callback that will be called with the PPOM. + * The callback will be called with the PPOM after it has been initialized. + * + * @param callback - Callback to be invoked with PPOM. + */ + async usePPOM(callback: (ppom: PPOM) => Promise): Promise { + return await this.#ppomMutex.use(async () => { + await this.#maybeUpdatePPOM(); + + if (!this.#ppom) { + this.#ppom = await this.#getPPOM(); + } + + return await callback(this.#ppom); + }); + } + + /** + * Constructor helper for registering this controller's messaging system + * actions. + */ + #registerMessageHandlers(): void { + this.messagingSystem.registerActionHandler( + `${controllerName}:clear` as const, + this.clear.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:usePPOM` as const, + this.usePPOM.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:setRefreshInterval` as const, + this.setRefreshInterval.bind(this), + ); + + this.messagingSystem.registerActionHandler( + `${controllerName}:updatePPOM` as const, + this.updatePPOM.bind(this), + ); + } + + /** + * Determine if an update to the ppom configuration is needed. + * The function will return true if + * - the chainId has changed + * - the ppom is out of date + * - the ppom is not initialized. + * + * @returns True if PPOM data requires update. + */ + async #shouldUpdate(): Promise { + if ( + this.state.newChainId !== this.state.lastChainId || + this.#isOutOfDate() + ) { + return true; + } + + return this.#ppom === undefined; + } + + /* + * check if the ppom is out of date + */ + #isOutOfDate(): boolean { + return Date.now() - this.state.lastFetched >= this.state.refreshInterval; + } + /** * Returns an array of new files that should be downloaded and saved to storage. * @@ -256,9 +351,6 @@ export class PPOMController extends BaseController< const fileUrl = `${PPOM_CDN_BASE_URL}${fileVersionInfo.filePath}`; const fileData = await this.#fetchBlob(fileUrl); - if (!fileData) { - throw new Error('Failed to fetch file data'); - } newFiles.push({ data: fileData, @@ -275,13 +367,10 @@ export class PPOMController extends BaseController< */ async #updateVersionInfo() { const versionInfo = await this.#fetchVersionInfo(PPOM_VERSION_PATH); - if (!versionInfo) { - throw new Error('Failed to fetch version info'); - } - this.update({ - versionInfo, - lastFetched: Date.now(), + this.update((draftState) => { + draftState.versionInfo = versionInfo; + draftState.lastFetched = Date.now(); }); } @@ -295,16 +384,14 @@ export class PPOMController extends BaseController< if (await this.#shouldUpdate()) { await this.updatePPOM(); } - - this.update(this.state); } /* * Fetch the blob from the PPOM cdn. */ - async #fetchBlob(input: string): Promise { + async #fetchBlob(fileUrl: string): Promise { const response = await safelyExecute( - async () => fetch(input, { cache: 'no-cache' }), + async () => fetch(fileUrl, { cache: 'no-cache' }), true, ); @@ -314,7 +401,7 @@ export class PPOMController extends BaseController< } default: { - return null; + throw new Error(`Failed to fetch file with url ${fileUrl}`); } } } @@ -322,9 +409,9 @@ export class PPOMController extends BaseController< /* * Fetch the version info from the PPOM cdn. */ - async #fetchVersionInfo(input: string): Promise { + async #fetchVersionInfo(url: string): Promise { const response = await safelyExecute( - async () => fetch(input, { cache: 'no-cache' }), + async () => fetch(url, { cache: 'no-cache' }), true, ); switch (response?.status) { @@ -333,7 +420,7 @@ export class PPOMController extends BaseController< } default: { - return null; + throw new Error(`Failed to fetch version info url: ${url}`); } } } @@ -343,9 +430,13 @@ export class PPOMController extends BaseController< * This method is used by the PPOM to make requests to the provider. */ async #jsonRpcRequest(req: any): Promise { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { this.#provider.sendAsync(req, (_err: any, res: any) => { - resolve(res); + if (_err) { + reject(_err); + } else { + resolve(res); + } }); }); } @@ -365,23 +456,4 @@ export class PPOMController extends BaseController< return new PPOM(this.#jsonRpcRequest.bind(this), new Uint8Array(data)); } - - /** - * Use the PPOM. - * This function receives a callback that will be called with the PPOM. - * The callback will be called with the PPOM after it has been initialized. - * - * @param callback - Callback to be invoked with PPOM. - */ - async use(callback: (ppom: PPOM) => Promise): Promise { - return await this.#ppomMutex.use(async () => { - await this.#maybeUpdatePPOM(); - - if (!this.#ppom) { - this.#ppom = await this.#getPPOM(); - } - - return await callback(this.#ppom); - }); - } } From cbc83c10604a7c1b9db6d95ba34518b467b57002 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 8 Jun 2023 19:11:01 +0530 Subject: [PATCH 18/33] Updates --- src/ppom-controller.test.ts | 123 +++++++++++++------------ src/ppom-controller.ts | 8 +- src/ppom.ts | 179 +++++++++++++----------------------- 3 files changed, 132 insertions(+), 178 deletions(-) diff --git a/src/ppom-controller.test.ts b/src/ppom-controller.test.ts index f1a8334a..03d76cfd 100644 --- a/src/ppom-controller.test.ts +++ b/src/ppom-controller.test.ts @@ -1,6 +1,8 @@ +import { ControllerMessenger } from '@metamask/base-controller'; + import { VERSION_INFO, storageBackendReturningData } from '../test/test-utils'; import { PPOM } from './ppom'; -import { PPOMController } from './ppom-controller'; +import { PPOMController, DAY_IN_MILLISECONDS } from './ppom-controller'; Object.defineProperty(globalThis, 'fetch', { writable: true, @@ -49,22 +51,35 @@ const buildFetchSpy = ( }); }; +const controllerMessenger = new ControllerMessenger(); + describe('PPOMController', () => { - describe('use', () => { - let ppomController: any; + const sendAsync = (_arg1: any, arg2: any) => { + arg2(undefined, 'DUMMY_VALUE'); + }; + let callBack: any; + + const ppomController = new PPOMController({ + storageBackend: storageBackendReturningData, + provider: { sendAsync }, + chainId: '0x1', + onNetworkChange: (func: any) => { + callBack = func; + }, + messenger: controllerMessenger.getRestricted({ + name: 'PPOMController', + }), + }); + + describe('usePPOM', () => { beforeEach(() => { - ppomController = new PPOMController({ - storageBackend: storageBackendReturningData, - provider: { sendAsync: Promise.resolve() }, - chainId: '0x1', - onNetworkChange: (_callback) => undefined, - }); + ppomController.clear(); }); - it('should be able to invoke use', async () => { + it('should be able to invoke usePPOM', async () => { buildFetchSpy(); - await ppomController.use(async (ppom: PPOM) => { + await ppomController.usePPOM(async (ppom: PPOM) => { expect(ppom).toBeDefined(); return Promise.resolve(); }); @@ -86,20 +101,20 @@ describe('PPOMController', () => { ], }); - await ppomController.use(async () => { + await ppomController.usePPOM(async () => { return Promise.resolve(); }); expect(spy).toHaveBeenCalledTimes(3); }); - it('should throw error', async () => { + it('should throw error if fetch for version info return 500', async () => { buildFetchSpy({ status: 500, }); await expect(async () => { - await ppomController.use(async () => { + await ppomController.usePPOM(async () => { return Promise.resolve(); }); }).rejects.toThrow('Failed to fetch version info'); @@ -111,41 +126,15 @@ describe('PPOMController', () => { }); await expect(async () => { - await ppomController.use(async () => { + await ppomController.usePPOM(async () => { return Promise.resolve(); }); - }).rejects.toThrow('Failed to fetch file data'); - }); - - it('should refresh data if refreshInterval is passed', async () => { - const spy = buildFetchSpy(); - - await ppomController.use(async () => { - return Promise.resolve(); - }); - expect(spy).toHaveBeenCalledTimes(3); - await ppomController.use(async () => { - return Promise.resolve(); - }); - expect(spy).toHaveBeenCalledTimes(3); - - ppomController.setRefreshInterval(0); - await ppomController.use(async () => { - return Promise.resolve(); - }); - expect(spy).toHaveBeenCalledTimes(4); + }).rejects.toThrow( + 'Failed to fetch file with url https://storage.googleapis.com/ppom-cdn/blob', + ); }); it('should refresh data if network is changed', async () => { - let callBack: any; - ppomController = new PPOMController({ - storageBackend: storageBackendReturningData, - provider: { sendAsync: Promise.resolve() }, - chainId: '0x1', - onNetworkChange: (func: any) => { - callBack = func; - }, - }); const spy = buildFetchSpy({ status: 200, json: () => [ @@ -161,42 +150,56 @@ describe('PPOMController', () => { ], }); - await ppomController.use(async () => { + await ppomController.usePPOM(async () => { return Promise.resolve(); }); expect(spy).toHaveBeenCalledTimes(3); - await ppomController.use(async () => { + await ppomController.usePPOM(async () => { return Promise.resolve(); }); expect(spy).toHaveBeenCalledTimes(3); callBack('0x2'); - await ppomController.use(async () => { + await ppomController.usePPOM(async () => { return Promise.resolve(); }); expect(spy).toHaveBeenCalledTimes(4); + callBack('0x1'); }); - }); - describe('PPOM', () => { it('should be able to send JSON RPC request to provider', async () => { - const sendAsync = (_arg1: any, arg2: any) => { - arg2(undefined, 'DUMMY_VALUE'); - }; - const ppomController = new PPOMController({ - storageBackend: storageBackendReturningData, - provider: { sendAsync }, - chainId: '0x1', - onNetworkChange: (_callback) => undefined, - }); - buildFetchSpy(); - await ppomController.use(async (ppom: PPOM) => { + await ppomController.usePPOM(async (ppom: PPOM) => { expect(ppom).toBeDefined(); const result = await (ppom as any).testJsonRPCRequest({}); expect(result).toBe('DUMMY_VALUE'); }); }); }); + + describe('setRefreshInterval', () => { + it('should refresh data if refreshInterval is passed', async () => { + ppomController.clear(); + ppomController.setRefreshInterval(0); + const spy = buildFetchSpy(); + + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + ppomController.setRefreshInterval(0); + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(4); + + ppomController.setRefreshInterval(1000); + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(4); + ppomController.setRefreshInterval(DAY_IN_MILLISECONDS); + }); + }); }); diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index 4eb8e002..37760d1c 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -13,7 +13,7 @@ import { FileInfo, } from './ppom-storage'; -const DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24; +export const DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24; /** * @type PPOMFileVersion @@ -169,7 +169,7 @@ export class PPOMController extends BaseControllerV2< name: controllerName, metadata: stateMetaData, messenger, - state: state ?? defaultState, + state: { ...defaultState, ...state }, }); this.#defaultState = defaultState; @@ -178,7 +178,7 @@ export class PPOMController extends BaseControllerV2< this.#storage = new PPOMStorage({ storageBackend, readMetadata: () => { - return this.state.storageMetadata; + return JSON.parse(JSON.stringify(this.state.storageMetadata)); }, writeMetadata: (metadata) => { this.update((draftState) => { @@ -390,11 +390,13 @@ export class PPOMController extends BaseControllerV2< * Fetch the blob from the PPOM cdn. */ async #fetchBlob(fileUrl: string): Promise { + console.log('---------------------- fetch blob'); const response = await safelyExecute( async () => fetch(fileUrl, { cache: 'no-cache' }), true, ); + console.log(' response?.status = ', response?.status); switch (response?.status) { case 200: { return await response.arrayBuffer(); diff --git a/src/ppom.ts b/src/ppom.ts index d9c6be70..d1718d2f 100644 --- a/src/ppom.ts +++ b/src/ppom.ts @@ -1,8 +1,6 @@ /* eslint-disable */ // @ts-nocheck -// IMPORTANT: this is an file autogenerated for wasm code and should not be changed manually. - let wasm; const heap = new Array(128).fill(undefined); @@ -13,6 +11,20 @@ function getObject(idx) { return heap[idx]; } +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + let WASM_VECTOR_LEN = 0; let cachedUint8Memory0 = null; @@ -60,9 +72,7 @@ function passStringToWasm0(arg, malloc, realloc) { for (; offset < len; offset++) { const code = arg.charCodeAt(offset); - if (code > 0x7f) { - break; - } + if (code > 0x7f) break; mem[ptr + offset] = code; } @@ -94,22 +104,6 @@ function getInt32Memory0() { return cachedInt32Memory0; } -let heap_next = heap.length; - -function dropObject(idx) { - if (idx < 132) { - return; - } - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true, @@ -121,17 +115,6 @@ function getStringFromWasm0(ptr, len) { return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } -function addHeapObject(obj) { - if (heap_next === heap.length) { - heap.push(heap.length + 1); - } - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} - function makeMutClosure(arg0, arg1, dtor, f) { const state = { a: arg0, b: arg1, cnt: 1 }; const real = (...args) => { @@ -139,7 +122,7 @@ function makeMutClosure(arg0, arg1, dtor, f) { // count. This ensures that the Rust closure environment won't // be deallocated while we're invoking it. state.cnt++; - const { a } = state; + const a = state.a; state.a = 0; try { return f(a, state.b, ...args); @@ -155,7 +138,16 @@ function makeMutClosure(arg0, arg1, dtor, f) { return real; } -function __wbg_adapter_14(arg0, arg1, arg2) { + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} +function __wbg_adapter_12(arg0, arg1, arg2) { wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke( arg0, arg1, @@ -163,38 +155,25 @@ function __wbg_adapter_14(arg0, arg1, arg2) { ); } -function __wbg_adapter_15(arg0, arg1) { +function __wbg_adapter_13(arg0, arg1) { wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__destroy( arg0, arg1, ); } +function passArray8ToWasm0(arg, malloc) { + const ptr = malloc(arg.length * 1); + getUint8Memory0().set(arg, ptr / 1); + WASM_VECTOR_LEN = arg.length; + return ptr; +} /** */ export function main() { wasm.main(); } -let cachedUint32Memory0 = null; - -function getUint32Memory0() { - if (cachedUint32Memory0 === null || cachedUint32Memory0.byteLength === 0) { - cachedUint32Memory0 = new Uint32Array(wasm.memory.buffer); - } - return cachedUint32Memory0; -} - -function passArrayJsValueToWasm0(array, malloc) { - const ptr = malloc(array.length * 4); - const mem = getUint32Memory0(); - for (let i = 0; i < array.length; i++) { - mem[ptr / 4 + i] = addHeapObject(array[i]); - } - WASM_VECTOR_LEN = array.length; - return ptr; -} - function handleError(f, args) { try { return f.apply(this, args); @@ -202,7 +181,7 @@ function handleError(f, args) { wasm.__wbindgen_exn_store(addHeapObject(e)); } } -function __wbg_adapter_34(arg0, arg1, arg2, arg3) { +function __wbg_adapter_28(arg0, arg1, arg2, arg3) { wasm.wasm_bindgen__convert__closures__invoke2_mut( arg0, arg1, @@ -222,7 +201,7 @@ export class PPOM { } __destroy_into_raw() { - const { ptr } = this; + const ptr = this.ptr; this.ptr = 0; return ptr; @@ -232,18 +211,16 @@ export class PPOM { const ptr = this.__destroy_into_raw(); wasm.__wbg_ppom_free(ptr); } - /** * @param {Function} json_rpc_callback - * @param {any[]} files + * @param {Uint8Array} data */ - constructor(json_rpc_callback, files) { - const ptr0 = passArrayJsValueToWasm0(files, wasm.__wbindgen_malloc); + constructor(json_rpc_callback, data) { + const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); const len0 = WASM_VECTOR_LEN; const ret = wasm.ppom_new(addHeapObject(json_rpc_callback), ptr0, len0); return PPOM.__wrap(ret); } - /** * @returns {Promise} */ @@ -251,7 +228,6 @@ export class PPOM { const ret = wasm.ppom_test(this.ptr); return takeObject(ret); } - /** * @param {any} request * @returns {Promise} @@ -281,18 +257,23 @@ async function load(module, imports) { const bytes = await module.arrayBuffer(); return await WebAssembly.instantiate(bytes, imports); - } - const instance = await WebAssembly.instantiate(module, imports); + } else { + const instance = await WebAssembly.instantiate(module, imports); - if (instance instanceof WebAssembly.Instance) { - return { instance, module }; + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + } else { + return instance; + } } - return instance; } function getImports() { const imports = {}; - imports.wbg = {}; + (imports as any).wbg = {}; + imports.wbg.__wbindgen_object_drop_ref = function (arg0) { + takeObject(arg0); + }; imports.wbg.__wbg_log_51761b27de744db2 = function (arg0, arg1) { console.log(getStringFromWasm0(arg0, arg1)); }; @@ -300,19 +281,6 @@ function getImports() { const ret = getObject(arg0) === undefined; return ret; }; - imports.wbg.__wbindgen_string_get = function (arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof obj === 'string' ? obj : undefined; - const ptr0 = isLikeNone(ret) - ? 0 - : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; - }; - imports.wbg.__wbindgen_object_drop_ref = function (arg0) { - takeObject(arg0); - }; imports.wbg.__wbg_new_abda76e883ba8a5f = function () { const ret = new Error(); return addHeapObject(ret); @@ -344,14 +312,6 @@ function getImports() { const ret = false; return ret; }; - imports.wbg.__wbg_get_27fe3dac1c4d0224 = function (arg0, arg1) { - const ret = getObject(arg0)[arg1 >>> 0]; - return addHeapObject(ret); - }; - imports.wbg.__wbg_from_67ca20fa722467e6 = function (arg0) { - const ret = Array.from(getObject(arg0)); - return addHeapObject(ret); - }; imports.wbg.__wbg_call_9495de66fdbe016b = function () { return handleError(function (arg0, arg1, arg2) { const ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); @@ -361,11 +321,11 @@ function getImports() { imports.wbg.__wbg_new_9d3a9ce4282a18a8 = function (arg0, arg1) { try { var state0 = { a: arg0, b: arg1 }; - const cb0 = (arg0, arg1) => { - const { a } = state0; + var cb0 = (arg0, arg1) => { + const a = state0.a; state0.a = 0; try { - return __wbg_adapter_34(a, state0.b, arg0, arg1); + return __wbg_adapter_28(a, state0.b, arg0, arg1); } finally { state0.a = a; } @@ -388,20 +348,15 @@ function getImports() { const ret = getObject(arg0).then(getObject(arg1), getObject(arg2)); return addHeapObject(ret); }; - imports.wbg.__wbg_buffer_cf65c07de34b9a08 = function (arg0) { - const ret = getObject(arg0).buffer; - return addHeapObject(ret); - }; - imports.wbg.__wbg_new_537b7341ce90bb31 = function (arg0) { - const ret = new Uint8Array(getObject(arg0)); - return addHeapObject(ret); - }; - imports.wbg.__wbg_set_17499e8aa4003ebd = function (arg0, arg1, arg2) { - getObject(arg0).set(getObject(arg1), arg2 >>> 0); - }; - imports.wbg.__wbg_length_27a2afe8ab42b09f = function (arg0) { - const ret = getObject(arg0).length; - return ret; + imports.wbg.__wbindgen_string_get = function (arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === 'string' ? obj : undefined; + var ptr0 = isLikeNone(ret) + ? 0 + : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; }; imports.wbg.__wbg_parse_3ac95b51fc312db8 = function () { return handleError(function (arg0, arg1) { @@ -418,17 +373,13 @@ function getImports() { imports.wbg.__wbindgen_throw = function (arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); }; - imports.wbg.__wbindgen_memory = function () { - const ret = wasm.memory; - return addHeapObject(ret); - }; imports.wbg.__wbindgen_closure_wrapper_wasm_bindgen__closure__Closure_T___wrap__breaks_if_inlined = function (arg0, arg1, arg2) { const ret = makeMutClosure( arg0, arg1, - __wbg_adapter_15, - __wbg_adapter_14, + __wbg_adapter_13, + __wbg_adapter_12, ); return addHeapObject(ret); }; @@ -442,7 +393,6 @@ function finalizeInit(instance, module) { wasm = instance.exports; init.__wbindgen_wasm_module = module; cachedInt32Memory0 = null; - cachedUint32Memory0 = null; cachedUint8Memory0 = null; wasm.__wbindgen_start(); @@ -481,5 +431,4 @@ async function init(input) { return finalizeInit(instance, module); } -export { initSync }; -export default init; +export { initSync, init as ppomInit }; From 8ebb1ba4ce294501196138ab4b0b3a5ce86035c3 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 8 Jun 2023 23:04:29 +0530 Subject: [PATCH 19/33] Update --- src/ppom-controller.test.ts | 88 ++++++++++++++++++++++++++++--------- src/ppom-controller.ts | 8 ++-- 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/src/ppom-controller.test.ts b/src/ppom-controller.test.ts index 03d76cfd..a79ed570 100644 --- a/src/ppom-controller.test.ts +++ b/src/ppom-controller.test.ts @@ -23,7 +23,7 @@ jest.mock('./ppom.ts', () => ({ free = () => undefined; - testJsonRPCRequest = () => this.#jsonRpcRequest(); + testJsonRPCRequest = async () => await this.#jsonRpcRequest(); }, ppomInit: () => undefined, })); @@ -51,32 +51,25 @@ const buildFetchSpy = ( }); }; -const controllerMessenger = new ControllerMessenger(); - -describe('PPOMController', () => { - const sendAsync = (_arg1: any, arg2: any) => { - arg2(undefined, 'DUMMY_VALUE'); - }; - let callBack: any; - +const buildPPOMController = (args?: any) => { + const controllerMessenger = new ControllerMessenger(); const ppomController = new PPOMController({ storageBackend: storageBackendReturningData, - provider: { sendAsync }, + provider: () => undefined, chainId: '0x1', - onNetworkChange: (func: any) => { - callBack = func; - }, + onNetworkChange: () => undefined, messenger: controllerMessenger.getRestricted({ name: 'PPOMController', }), + ...args, }); + return ppomController; +}; +describe('PPOMController', () => { describe('usePPOM', () => { - beforeEach(() => { - ppomController.clear(); - }); - it('should be able to invoke usePPOM', async () => { + const ppomController = buildPPOMController(); buildFetchSpy(); await ppomController.usePPOM(async (ppom: PPOM) => { @@ -101,6 +94,7 @@ describe('PPOMController', () => { ], }); + const ppomController = buildPPOMController(); await ppomController.usePPOM(async () => { return Promise.resolve(); }); @@ -113,6 +107,7 @@ describe('PPOMController', () => { status: 500, }); + const ppomController = buildPPOMController(); await expect(async () => { await ppomController.usePPOM(async () => { return Promise.resolve(); @@ -125,6 +120,7 @@ describe('PPOMController', () => { status: 500, }); + const ppomController = buildPPOMController(); await expect(async () => { await ppomController.usePPOM(async () => { return Promise.resolve(); @@ -150,6 +146,12 @@ describe('PPOMController', () => { ], }); + let callBack: any; + const ppomController = buildPPOMController({ + onNetworkChange: (func: any) => { + callBack = func; + }, + }); await ppomController.usePPOM(async () => { return Promise.resolve(); }); @@ -170,18 +172,41 @@ describe('PPOMController', () => { it('should be able to send JSON RPC request to provider', async () => { buildFetchSpy(); + const ppomController = buildPPOMController({ + provider: { + sendAsync: (_arg1: any, arg2: any) => { + arg2(undefined, 'DUMMY_VALUE'); + }, + }, + }); + await ppomController.usePPOM(async (ppom: PPOM) => { - expect(ppom).toBeDefined(); const result = await (ppom as any).testJsonRPCRequest({}); expect(result).toBe('DUMMY_VALUE'); }); }); + + it('should throw error if JSON RPC request on provider fails', async () => { + const ppomController = buildPPOMController({ + provider: { + sendAsync: (_arg1: any, arg2: any) => { + arg2('DUMMY_ERROR'); + }, + }, + }); + buildFetchSpy(); + await ppomController.usePPOM(async (ppom: PPOM) => { + (ppom as any).testJsonRPCRequest({}).catch((exp: any) => { + // eslint-disable-next-line jest/no-conditional-expect + expect(exp).toBe('DUMMY_ERROR'); + }); + }); + }); }); describe('setRefreshInterval', () => { - it('should refresh data if refreshInterval is passed', async () => { - ppomController.clear(); - ppomController.setRefreshInterval(0); + it('should refresh data if refreshInterval is passed the time', async () => { + const ppomController = buildPPOMController(); const spy = buildFetchSpy(); await ppomController.usePPOM(async () => { @@ -202,4 +227,25 @@ describe('PPOMController', () => { ppomController.setRefreshInterval(DAY_IN_MILLISECONDS); }); }); + + describe('clear', () => { + it('should clear controller state', async () => { + const ppomController = buildPPOMController(); + const spy = buildFetchSpy(); + + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + ppomController.clear(); + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(6); + }); + }); }); diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index 37760d1c..95f2eaa7 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -390,13 +390,11 @@ export class PPOMController extends BaseControllerV2< * Fetch the blob from the PPOM cdn. */ async #fetchBlob(fileUrl: string): Promise { - console.log('---------------------- fetch blob'); const response = await safelyExecute( async () => fetch(fileUrl, { cache: 'no-cache' }), true, ); - console.log(' response?.status = ', response?.status); switch (response?.status) { case 200: { return await response.arrayBuffer(); @@ -433,9 +431,9 @@ export class PPOMController extends BaseControllerV2< */ async #jsonRpcRequest(req: any): Promise { return new Promise((resolve, reject) => { - this.#provider.sendAsync(req, (_err: any, res: any) => { - if (_err) { - reject(_err); + this.#provider.sendAsync(req, (error: any, res: any) => { + if (error) { + reject(error); } else { resolve(res); } From 8133afcda40271ccd380dcbb15aad13f6b0ece01 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 9 Jun 2023 11:56:24 +0530 Subject: [PATCH 20/33] Updates --- src/ppom-controller.test.ts | 132 ++++++++++++++++++++++-------------- src/ppom-controller.ts | 2 +- 2 files changed, 82 insertions(+), 52 deletions(-) diff --git a/src/ppom-controller.test.ts b/src/ppom-controller.test.ts index a79ed570..eb76ee27 100644 --- a/src/ppom-controller.test.ts +++ b/src/ppom-controller.test.ts @@ -68,7 +68,7 @@ const buildPPOMController = (args?: any) => { describe('PPOMController', () => { describe('usePPOM', () => { - it('should be able to invoke usePPOM', async () => { + it('should provide instance of ppom to the passed ballback', async () => { const ppomController = buildPPOMController(); buildFetchSpy(); @@ -78,56 +78,15 @@ describe('PPOMController', () => { }); }); - it('should not fetch file if chainId is different from current chainId', async () => { - const spy = buildFetchSpy({ - status: 200, - json: () => [ - ...VERSION_INFO, - { - name: 'data', - chainId: '0x2', - version: '1.0.3', - checksum: - '409a7f83ac6b31dc8c77e3ec18038f209bd2f545e0f4177c2e2381aa4e067b49', - filePath: 'data', - }, - ], - }); - + it('should return the value returned by callback', async () => { const ppomController = buildPPOMController(); - await ppomController.usePPOM(async () => { - return Promise.resolve(); - }); - - expect(spy).toHaveBeenCalledTimes(3); - }); - - it('should throw error if fetch for version info return 500', async () => { - buildFetchSpy({ - status: 500, - }); - - const ppomController = buildPPOMController(); - await expect(async () => { - await ppomController.usePPOM(async () => { - return Promise.resolve(); - }); - }).rejects.toThrow('Failed to fetch version info'); - }); + buildFetchSpy(); - it('should throw error if fetch for blob return 500', async () => { - buildFetchSpy(undefined, { - status: 500, + const result = await ppomController.usePPOM(async (ppom: PPOM) => { + expect(ppom).toBeDefined(); + return Promise.resolve('DUMMY_VALUE'); }); - - const ppomController = buildPPOMController(); - await expect(async () => { - await ppomController.usePPOM(async () => { - return Promise.resolve(); - }); - }).rejects.toThrow( - 'Failed to fetch file with url https://storage.googleapis.com/ppom-cdn/blob', - ); + expect(result).toBe('DUMMY_VALUE'); }); it('should refresh data if network is changed', async () => { @@ -169,7 +128,7 @@ describe('PPOMController', () => { callBack('0x1'); }); - it('should be able to send JSON RPC request to provider', async () => { + it('should pass instance of provider to ppom to enable it to send JSON RPC request on it', async () => { buildFetchSpy(); const ppomController = buildPPOMController({ @@ -186,7 +145,7 @@ describe('PPOMController', () => { }); }); - it('should throw error if JSON RPC request on provider fails', async () => { + it('should propogate to ppom if JSON RPC request on provider fails', async () => { const ppomController = buildPPOMController({ provider: { sendAsync: (_arg1: any, arg2: any) => { @@ -204,11 +163,81 @@ describe('PPOMController', () => { }); }); + describe('updatePPOM', () => { + it('should not fetch file if chainId of the file is different from current chainId', async () => { + const spy = buildFetchSpy({ + status: 200, + json: () => [ + ...VERSION_INFO, + { + name: 'data', + chainId: '0x2', + version: '1.0.3', + checksum: + '409a7f83ac6b31dc8c77e3ec18038f209bd2f545e0f4177c2e2381aa4e067b49', + filePath: 'data', + }, + ], + }); + + const ppomController = buildPPOMController(); + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + + expect(spy).toHaveBeenCalledTimes(3); + }); + + it('should not fetch file if it already exists', async () => { + const spy = buildFetchSpy(); + + const ppomController = buildPPOMController(); + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + expect(spy).toHaveBeenCalledTimes(3); + }); + + it('should throw error if fetch for version info return 500', async () => { + buildFetchSpy({ + status: 500, + }); + + const ppomController = buildPPOMController(); + await expect(async () => { + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + }).rejects.toThrow('Failed to fetch version info'); + }); + + it('should throw error if fetch for blob return 500', async () => { + buildFetchSpy(undefined, { + status: 500, + }); + + const ppomController = buildPPOMController(); + await expect(async () => { + await ppomController.usePPOM(async () => { + return Promise.resolve(); + }); + }).rejects.toThrow( + 'Failed to fetch file with url https://storage.googleapis.com/ppom-cdn/blob', + ); + }); + }); + describe('setRefreshInterval', () => { - it('should refresh data if refreshInterval is passed the time', async () => { + it('should update refresh interval', async () => { const ppomController = buildPPOMController(); const spy = buildFetchSpy(); + // controller fetches new data files is difference from last updated time + // is greater than refresh interval. await ppomController.usePPOM(async () => { return Promise.resolve(); }); @@ -233,6 +262,7 @@ describe('PPOMController', () => { const ppomController = buildPPOMController(); const spy = buildFetchSpy(); + // controller fetches new data files when state is cleared await ppomController.usePPOM(async () => { return Promise.resolve(); }); diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index 95f2eaa7..b9fd1e70 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -255,7 +255,7 @@ export class PPOMController extends BaseControllerV2< * * @param callback - Callback to be invoked with PPOM. */ - async usePPOM(callback: (ppom: PPOM) => Promise): Promise { + async usePPOM(callback: (ppom: PPOM) => Promise): Promise { return await this.#ppomMutex.use(async () => { await this.#maybeUpdatePPOM(); From c3f71bea3efc14295a15bb4cfc94f1cfd60e45e8 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 9 Jun 2023 13:51:07 +0530 Subject: [PATCH 21/33] Updates --- src/ppom.ts | 175 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 63 deletions(-) diff --git a/src/ppom.ts b/src/ppom.ts index d1718d2f..f867169b 100644 --- a/src/ppom.ts +++ b/src/ppom.ts @@ -1,6 +1,7 @@ /* eslint-disable */ // @ts-nocheck +/* eslint-disable */ let wasm; const heap = new Array(128).fill(undefined); @@ -11,20 +12,6 @@ function getObject(idx) { return heap[idx]; } -let heap_next = heap.length; - -function dropObject(idx) { - if (idx < 132) return; - heap[idx] = heap_next; - heap_next = idx; -} - -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} - let WASM_VECTOR_LEN = 0; let cachedUint8Memory0 = null; @@ -72,7 +59,9 @@ function passStringToWasm0(arg, malloc, realloc) { for (; offset < len; offset++) { const code = arg.charCodeAt(offset); - if (code > 0x7f) break; + if (code > 0x7f) { + break; + } mem[ptr + offset] = code; } @@ -104,6 +93,22 @@ function getInt32Memory0() { return cachedInt32Memory0; } +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) { + return; + } + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true, @@ -115,6 +120,17 @@ function getStringFromWasm0(ptr, len) { return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } +function addHeapObject(obj) { + if (heap_next === heap.length) { + heap.push(heap.length + 1); + } + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + function makeMutClosure(arg0, arg1, dtor, f) { const state = { a: arg0, b: arg1, cnt: 1 }; const real = (...args) => { @@ -122,7 +138,7 @@ function makeMutClosure(arg0, arg1, dtor, f) { // count. This ensures that the Rust closure environment won't // be deallocated while we're invoking it. state.cnt++; - const a = state.a; + const { a } = state; state.a = 0; try { return f(a, state.b, ...args); @@ -138,16 +154,7 @@ function makeMutClosure(arg0, arg1, dtor, f) { return real; } - -function addHeapObject(obj) { - if (heap_next === heap.length) heap.push(heap.length + 1); - const idx = heap_next; - heap_next = heap[idx]; - - heap[idx] = obj; - return idx; -} -function __wbg_adapter_12(arg0, arg1, arg2) { +function __wbg_adapter_14(arg0, arg1, arg2) { wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke( arg0, arg1, @@ -155,25 +162,38 @@ function __wbg_adapter_12(arg0, arg1, arg2) { ); } -function __wbg_adapter_13(arg0, arg1) { +function __wbg_adapter_15(arg0, arg1) { wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__destroy( arg0, arg1, ); } -function passArray8ToWasm0(arg, malloc) { - const ptr = malloc(arg.length * 1); - getUint8Memory0().set(arg, ptr / 1); - WASM_VECTOR_LEN = arg.length; - return ptr; -} /** */ export function main() { wasm.main(); } +let cachedUint32Memory0 = null; + +function getUint32Memory0() { + if (cachedUint32Memory0 === null || cachedUint32Memory0.byteLength === 0) { + cachedUint32Memory0 = new Uint32Array(wasm.memory.buffer); + } + return cachedUint32Memory0; +} + +function passArrayJsValueToWasm0(array, malloc) { + const ptr = malloc(array.length * 4); + const mem = getUint32Memory0(); + for (let i = 0; i < array.length; i++) { + mem[ptr / 4 + i] = addHeapObject(array[i]); + } + WASM_VECTOR_LEN = array.length; + return ptr; +} + function handleError(f, args) { try { return f.apply(this, args); @@ -181,7 +201,7 @@ function handleError(f, args) { wasm.__wbindgen_exn_store(addHeapObject(e)); } } -function __wbg_adapter_28(arg0, arg1, arg2, arg3) { +function __wbg_adapter_34(arg0, arg1, arg2, arg3) { wasm.wasm_bindgen__convert__closures__invoke2_mut( arg0, arg1, @@ -201,7 +221,7 @@ export class PPOM { } __destroy_into_raw() { - const ptr = this.ptr; + const { ptr } = this; this.ptr = 0; return ptr; @@ -211,16 +231,18 @@ export class PPOM { const ptr = this.__destroy_into_raw(); wasm.__wbg_ppom_free(ptr); } + /** * @param {Function} json_rpc_callback - * @param {Uint8Array} data + * @param {any[]} files */ - constructor(json_rpc_callback, data) { - const ptr0 = passArray8ToWasm0(data, wasm.__wbindgen_malloc); + constructor(json_rpc_callback, files) { + const ptr0 = passArrayJsValueToWasm0(files, wasm.__wbindgen_malloc); const len0 = WASM_VECTOR_LEN; const ret = wasm.ppom_new(addHeapObject(json_rpc_callback), ptr0, len0); return PPOM.__wrap(ret); } + /** * @returns {Promise} */ @@ -228,6 +250,7 @@ export class PPOM { const ret = wasm.ppom_test(this.ptr); return takeObject(ret); } + /** * @param {any} request * @returns {Promise} @@ -257,23 +280,18 @@ async function load(module, imports) { const bytes = await module.arrayBuffer(); return await WebAssembly.instantiate(bytes, imports); - } else { - const instance = await WebAssembly.instantiate(module, imports); + } + const instance = await WebAssembly.instantiate(module, imports); - if (instance instanceof WebAssembly.Instance) { - return { instance, module }; - } else { - return instance; - } + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; } + return instance; } function getImports() { const imports = {}; - (imports as any).wbg = {}; - imports.wbg.__wbindgen_object_drop_ref = function (arg0) { - takeObject(arg0); - }; + imports.wbg = {}; imports.wbg.__wbg_log_51761b27de744db2 = function (arg0, arg1) { console.log(getStringFromWasm0(arg0, arg1)); }; @@ -281,6 +299,19 @@ function getImports() { const ret = getObject(arg0) === undefined; return ret; }; + imports.wbg.__wbindgen_string_get = function (arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === 'string' ? obj : undefined; + const ptr0 = isLikeNone(ret) + ? 0 + : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len0 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len0; + getInt32Memory0()[arg0 / 4 + 0] = ptr0; + }; + imports.wbg.__wbindgen_object_drop_ref = function (arg0) { + takeObject(arg0); + }; imports.wbg.__wbg_new_abda76e883ba8a5f = function () { const ret = new Error(); return addHeapObject(ret); @@ -312,6 +343,14 @@ function getImports() { const ret = false; return ret; }; + imports.wbg.__wbg_get_27fe3dac1c4d0224 = function (arg0, arg1) { + const ret = getObject(arg0)[arg1 >>> 0]; + return addHeapObject(ret); + }; + imports.wbg.__wbg_from_67ca20fa722467e6 = function (arg0) { + const ret = Array.from(getObject(arg0)); + return addHeapObject(ret); + }; imports.wbg.__wbg_call_9495de66fdbe016b = function () { return handleError(function (arg0, arg1, arg2) { const ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); @@ -321,11 +360,11 @@ function getImports() { imports.wbg.__wbg_new_9d3a9ce4282a18a8 = function (arg0, arg1) { try { var state0 = { a: arg0, b: arg1 }; - var cb0 = (arg0, arg1) => { - const a = state0.a; + const cb0 = (arg0, arg1) => { + const { a } = state0; state0.a = 0; try { - return __wbg_adapter_28(a, state0.b, arg0, arg1); + return __wbg_adapter_34(a, state0.b, arg0, arg1); } finally { state0.a = a; } @@ -348,15 +387,20 @@ function getImports() { const ret = getObject(arg0).then(getObject(arg1), getObject(arg2)); return addHeapObject(ret); }; - imports.wbg.__wbindgen_string_get = function (arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof obj === 'string' ? obj : undefined; - var ptr0 = isLikeNone(ret) - ? 0 - : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len0 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len0; - getInt32Memory0()[arg0 / 4 + 0] = ptr0; + imports.wbg.__wbg_buffer_cf65c07de34b9a08 = function (arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_537b7341ce90bb31 = function (arg0) { + const ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_set_17499e8aa4003ebd = function (arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); + }; + imports.wbg.__wbg_length_27a2afe8ab42b09f = function (arg0) { + const ret = getObject(arg0).length; + return ret; }; imports.wbg.__wbg_parse_3ac95b51fc312db8 = function () { return handleError(function (arg0, arg1) { @@ -373,13 +417,17 @@ function getImports() { imports.wbg.__wbindgen_throw = function (arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); }; + imports.wbg.__wbindgen_memory = function () { + const ret = wasm.memory; + return addHeapObject(ret); + }; imports.wbg.__wbindgen_closure_wrapper_wasm_bindgen__closure__Closure_T___wrap__breaks_if_inlined = function (arg0, arg1, arg2) { const ret = makeMutClosure( arg0, arg1, - __wbg_adapter_13, - __wbg_adapter_12, + __wbg_adapter_15, + __wbg_adapter_14, ); return addHeapObject(ret); }; @@ -393,6 +441,7 @@ function finalizeInit(instance, module) { wasm = instance.exports; init.__wbindgen_wasm_module = module; cachedInt32Memory0 = null; + cachedUint32Memory0 = null; cachedUint8Memory0 = null; wasm.__wbindgen_start(); From 1036aefaf918cbe246b6088ce6e6d4142fc072ab Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 9 Jun 2023 14:09:10 +0530 Subject: [PATCH 22/33] Updates --- src/ppom-controller.ts | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index b9fd1e70..f6e3c138 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -24,6 +24,16 @@ type PPOMFileVersion = FileInfo & { filePath: string; }; +/** + * @type PPOMFileVersion + * @augments FileInfo + * @property filePath - Path of the file in CDN. + */ +type PPOMFile = FileInfo & { + filePath: string; + data: ArrayBuffer; +}; + /** * @type PPOMVersionResponse - array of objects of type PPOMFileVersion */ @@ -327,8 +337,11 @@ export class PPOMController extends BaseControllerV2< * @param storageMetadata - An array of file metadata objects already in storage. * @returns A promise that resolves to an array of new files to download and save to storage. */ - async #getNewFiles(chainId: string, storageMetadata: any[]): Promise { - const newFiles: any[] = []; + async #getNewFiles( + chainId: string, + storageMetadata: PPOMFileMetadata, + ): Promise { + const newFiles: PPOMFile[] = []; for (const fileVersionInfo of this.state.versionInfo) { // download all files for the current chain + generally required files. @@ -448,12 +461,19 @@ export class PPOMController extends BaseControllerV2< * It will load the PPOM data from storage and initialize the PPOM. */ async #getPPOM(): Promise { - await ppomInit(this.#storage.readFile(PPOM_BLOB_NAME, '')); - const data = await this.#storage.readFile( - PPOM_DATA_NAME, - this.state.newChainId, + await ppomInit('./ppom.wasm'); + + const chainId = this.state.lastChainId; + + const files = await Promise.all( + this.state.versionInfo + .filter((file) => !file.chainId || file.chainId === chainId) + .map(async (file) => { + const data = await this.#storage.readFile(file.name, file.chainId); + return [file.name, new Uint8Array(data)]; + }), ); - return new PPOM(this.#jsonRpcRequest.bind(this), new Uint8Array(data)); + return new PPOM(this.#jsonRpcRequest.bind(this), files); } } From 5878f0bbb08d0c1bbd21f3f4889a7ddb70052a73 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Fri, 9 Jun 2023 14:24:20 +0530 Subject: [PATCH 23/33] Updates --- src/ppom-controller.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index f6e3c138..5db3bad7 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -67,9 +67,6 @@ const stateMetaData = { refreshInterval: { persist: false, anonymous: false }, }; -const PPOM_DATA_NAME = 'data'; -const PPOM_BLOB_NAME = 'blob'; - // TODO: replace with metamask cdn const PPOM_CDN_BASE_URL = 'https://storage.googleapis.com/ppom-cdn/'; const PPOM_VERSION = 'ppom_version.json'; From b63fdf7f8ca2dd42db33572fc0db324a919ce162 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:09:19 +0530 Subject: [PATCH 24/33] fix --- src/ppom-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index 5db3bad7..e2dff50f 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -9,7 +9,7 @@ import { ppomInit, PPOM } from './ppom'; import { StorageBackend, PPOMStorage, - PPOMFileMetadata, + FileInfoList, FileInfo, } from './ppom-storage'; From 5d0f3ce6caa1da03dd293d2a58ca25cdf205d6c5 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:18:14 +0530 Subject: [PATCH 25/33] Updates --- src/ppom-controller.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index e2dff50f..f40ca63e 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -9,27 +9,27 @@ import { ppomInit, PPOM } from './ppom'; import { StorageBackend, PPOMStorage, - FileInfoList, - FileInfo, + FileMetadataList, + FileMetadata, } from './ppom-storage'; export const DAY_IN_MILLISECONDS = 1000 * 60 * 60 * 24; /** * @type PPOMFileVersion - * @augments FileInfo + * @augments FileMetadata * @property filePath - Path of the file in CDN. */ -type PPOMFileVersion = FileInfo & { +type PPOMFileVersion = FileMetadata & { filePath: string; }; /** * @type PPOMFileVersion - * @augments FileInfo + * @augments FileMetadata * @property filePath - Path of the file in CDN. */ -type PPOMFile = FileInfo & { +type PPOMFile = FileMetadata & { filePath: string; data: ArrayBuffer; }; @@ -54,7 +54,7 @@ export type PPOMControllerState = { lastChainId: string; newChainId: string; versionInfo: PPOMVersionResponse; - storageMetadata: PPOMFileMetadata; + storageMetadata: FileMetadataList; refreshInterval: number; }; @@ -336,7 +336,7 @@ export class PPOMController extends BaseControllerV2< */ async #getNewFiles( chainId: string, - storageMetadata: PPOMFileMetadata, + storageMetadata: FileMetadataList, ): Promise { const newFiles: PPOMFile[] = []; @@ -359,7 +359,9 @@ export class PPOMController extends BaseControllerV2< continue; } - const fileUrl = `${PPOM_CDN_BASE_URL}${fileVersionInfo.filePath}`; + const fileUrl = `${PPOM_CDN_BASE_URL}${ + fileVersionInfo.filePath as string + }`; const fileData = await this.#fetchBlob(fileUrl); newFiles.push({ From fd6c64012f6cd1d56de48469b5d83215c046e37f Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:20:36 +0530 Subject: [PATCH 26/33] Updates --- src/ppom-controller.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ppom-controller.ts b/src/ppom-controller.ts index f40ca63e..5b0c9893 100644 --- a/src/ppom-controller.ts +++ b/src/ppom-controller.ts @@ -359,9 +359,7 @@ export class PPOMController extends BaseControllerV2< continue; } - const fileUrl = `${PPOM_CDN_BASE_URL}${ - fileVersionInfo.filePath as string - }`; + const fileUrl = `${PPOM_CDN_BASE_URL}${fileVersionInfo.filePath}`; const fileData = await this.#fetchBlob(fileUrl); newFiles.push({ From d359a874599b5727a04dc404159b1dbe504f900a Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:46:36 +0530 Subject: [PATCH 27/33] fix --- jest.config.js | 6 +- src/ppom-storage.test.ts | 424 ++++++++++++++++++++------------------- src/ppom-storage.ts | 9 +- 3 files changed, 220 insertions(+), 219 deletions(-) diff --git a/jest.config.js b/jest.config.js index 2ae5ebed..8a41f8ba 100644 --- a/jest.config.js +++ b/jest.config.js @@ -28,11 +28,7 @@ module.exports = { coverageDirectory: 'coverage', // An array of regexp pattern strings used to skip coverage collection - coveragePathIgnorePatterns: [ - '/node_modules/', - 'src/ppom.d.ts', - 'src/index.ts', - ], + coveragePathIgnorePatterns: ['/node_modules/', 'src/ppom.ts', 'src/index.ts'], // Indicates which provider should be used to instrument code for coverage coverageProvider: 'babel', diff --git a/src/ppom-storage.test.ts b/src/ppom-storage.test.ts index ec7167c5..1f74efaa 100644 --- a/src/ppom-storage.test.ts +++ b/src/ppom-storage.test.ts @@ -1,209 +1,215 @@ -import { - DUMMY_ARRAY_BUFFER_DATA, - buildStorageBackend, - simpleStorageBackend, - storageBackendReturningData, -} from '../test/test-utils'; -import { PPOMStorage, StorageKey } from './ppom-storage'; - -const DUMMY_CHECKSUM = 'DUMMY_CHECKSUM'; -const DUMMY_NAME = 'DUMMY_NAME'; -const DUMMY_CHAINID = '1'; -const ARRAY_BUFFER_DATA = new ArrayBuffer(123); - -const getFileData = (data = {}) => ({ - chainId: DUMMY_CHAINID, - name: DUMMY_NAME, - checksum: DUMMY_CHECKSUM, - version: '0', - ...data, -}); - -const simpleFileData = getFileData(); - -describe('PPOMStorage', () => { - describe('readFile', () => { - it('should return data', async () => { - const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), - readMetadata: () => [simpleFileData], - writeMetadata: () => undefined, - }); - const data = await ppomStorage.readFile(DUMMY_NAME, DUMMY_CHAINID); - expect(data).toStrictEqual(DUMMY_ARRAY_BUFFER_DATA); - }); - - it('should throw error if file metadata not found', async () => { - const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), - readMetadata: () => [], - writeMetadata: () => undefined, - }); - await expect(async () => { - await ppomStorage.readFile(DUMMY_NAME, DUMMY_CHAINID); - }).rejects.toThrow( - `File metadata (${DUMMY_NAME}, ${DUMMY_CHAINID}) not found`, - ); - }); - - it('should throw error if file is not found in storage', async () => { - const ppomStorage = new PPOMStorage({ - storageBackend: simpleStorageBackend, - readMetadata: () => [simpleFileData], - writeMetadata: () => undefined, - }); - await expect(async () => { - await ppomStorage.readFile(DUMMY_NAME, DUMMY_CHAINID); - }).rejects.toThrow( - `Storage File (${DUMMY_NAME}, ${DUMMY_CHAINID}) not found`, - ); - }); - }); - - describe('writeFile', () => { - it('should call storageBackend.write', async () => { - const mockWrite = jest.fn().mockResolvedValue('test'); - const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackend({ write: mockWrite }), - readMetadata: () => [], - writeMetadata: () => undefined, - }); - await ppomStorage.writeFile({ - data: DUMMY_ARRAY_BUFFER_DATA, - ...simpleFileData, - }); - expect(mockWrite).toHaveBeenCalledTimes(1); - }); - - it('should invoke writeMetadata if file metadata exists', async () => { - const mockWriteMetadata = jest.fn(); - const ppomStorage = new PPOMStorage({ - storageBackend: simpleStorageBackend, - readMetadata: () => [simpleFileData], - writeMetadata: mockWriteMetadata, - }); - await ppomStorage.writeFile({ - data: DUMMY_ARRAY_BUFFER_DATA, - ...simpleFileData, - }); - expect(mockWriteMetadata).toHaveBeenCalledWith([simpleFileData]); - }); - - it('should invoke writeMetadata with data passed', async () => { - const mockWriteMetadata = jest.fn(); - const ppomStorage = new PPOMStorage({ - storageBackend: simpleStorageBackend, - readMetadata: () => [], - writeMetadata: mockWriteMetadata, - }); - await ppomStorage.writeFile({ - data: DUMMY_ARRAY_BUFFER_DATA, - ...simpleFileData, - }); - expect(mockWriteMetadata).toHaveBeenCalledWith([simpleFileData]); - }); - }); - - describe('syncMetadata', () => { - it('should return metadata of file if updated file is found in storage', async () => { - const mockWriteMetadata = jest.fn(); - const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), - readMetadata: () => [simpleFileData], - writeMetadata: mockWriteMetadata, - }); - - const result = await ppomStorage.syncMetadata([simpleFileData]); - expect(mockWriteMetadata).toHaveBeenCalledWith([simpleFileData]); - expect(result).toStrictEqual([simpleFileData]); - }); - - it('should not return data if file is not found in storage', async () => { - const mockWriteMetadata = jest.fn(); - const ppomStorage = new PPOMStorage({ - storageBackend: simpleStorageBackend, - readMetadata: () => [simpleFileData], - writeMetadata: mockWriteMetadata, - }); - - const result = await ppomStorage.syncMetadata([simpleFileData]); - expect(mockWriteMetadata).toHaveBeenCalledWith([]); - expect(result).toStrictEqual([]); - }); - - it('should not return metadata of file if file version in storage is outdated', async () => { - const storageFileData = { ...simpleFileData, version: '1' }; - const mockWriteMetadata = jest.fn(); - const mockDelete = jest.fn().mockResolvedValue(''); - - const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackend({ - read: async (_key: StorageKey): Promise => - Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), - dir: async () => Promise.resolve([storageFileData]), - delete: mockDelete, - }), - readMetadata: () => [simpleFileData], - writeMetadata: mockWriteMetadata, - }); - - const result = await ppomStorage.syncMetadata([storageFileData]); - expect(mockDelete).toHaveBeenCalledWith({ - name: DUMMY_NAME, - chainId: DUMMY_CHAINID, - }); - expect(mockWriteMetadata).toHaveBeenCalledWith([]); - expect(result).toStrictEqual([]); - }); - - it('should delete file from storage backend if its name is not found in file version info passed', async () => { - const fileDataInStorage = getFileData({ - name: 'dummy_2', - }); - const mockWriteMetadata = jest.fn(); - const mockDelete = jest.fn().mockResolvedValue(''); - - const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackend({ - read: async (_key: StorageKey): Promise => - Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), - dir: async () => Promise.resolve([fileDataInStorage]), - delete: mockDelete, - }), - readMetadata: () => [simpleFileData], - writeMetadata: mockWriteMetadata, - }); - - await ppomStorage.syncMetadata([simpleFileData]); - expect(mockDelete).toHaveBeenCalledWith({ - name: 'dummy_2', - chainId: DUMMY_CHAINID, - }); - }); - - it('should delete file from storage backend if its version info is not passed', async () => { - const fileDataInStorage = getFileData({ - chainId: '5', - }); - const mockWriteMetadata = jest.fn(); - const mockDelete = jest.fn().mockResolvedValue(''); - - const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackend({ - read: async (_key: StorageKey): Promise => - Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), - dir: async () => Promise.resolve([fileDataInStorage]), - delete: mockDelete, - }), - readMetadata: () => [simpleFileData], - writeMetadata: mockWriteMetadata, - }); - - await ppomStorage.syncMetadata([simpleFileData]); - expect(mockDelete).toHaveBeenCalledWith({ - name: DUMMY_NAME, - chainId: '5', - }); - }); - }); -}); +/** + * @type FileMetadata + * Defined type for information about file saved in storage backend. + * @property name - Name of the file. + * @property chainId - ChainId for file. + * @property version - File version. + * @property checksum - Checksum of file data. + */ +export type FileMetadata = { + name: string; + chainId: string; + version: string; + checksum: string; +}; + +/** + * @type FileMetadataList + * This is type of metadata about files saved in storage, + * this information is saved in PPOMController state. + */ +export type FileMetadataList = FileMetadata[]; + +/** + * @type StorageKey + * This defines type of key that is used for indexing file data saved in StorageBackend. + * @property name - Name of the file. + * @property chainId - ChainId for file. + */ +export type StorageKey = { + name: string; + chainId: string; +}; + +/** + * @type StorageBackend + * This defines type for storage backend implementation. + * There will be different storage implementations depending on platform: + * 1. extension - indexDB + * 2. mobile app - + * @property read - Read file from storage. + * @property write - Write file to storage. + * @property delete - Delete file from storage. + * @property dir - Get list of all files in storage. + */ +export type StorageBackend = { + read(key: StorageKey, checksum: string): Promise; + write(key: StorageKey, data: ArrayBuffer, checksum: string): Promise; + delete(key: StorageKey): Promise; + dir(): Promise; +}; + +/** + * @class PPOMStorage + * This class is responsible for managing the local storage + * It provides the following functionalities: + * 1. Sync the metadata with the version info from the cdn + * 2. Read a file from the local storage + * 3. Write a file to the local storage + * + * It also validates the checksum of the file when reading and writing in order to + * detect corrupted files or files that are not up to date + */ +export class PPOMStorage { + readonly #storageBackend: StorageBackend; + + readonly #readMetadata: () => FileMetadataList; + + readonly #writeMetadata: (metadata: FileMetadataList) => void; + + /** + * Creates a PPOMStorage instance. + * + * @param options - The options passed to the function. + * @param options.storageBackend - The storage backend to use for the local storage. + * @param options.readMetadata - A function to read the metadata from the local storage. + * @param options.writeMetadata - A function to write the metadata to the local storage. + */ + constructor({ + storageBackend, + readMetadata, + writeMetadata, + }: { + storageBackend: StorageBackend; + readMetadata: () => FileMetadataList; + writeMetadata: (metadata: FileMetadataList) => void; + }) { + this.#storageBackend = storageBackend; + this.#readMetadata = readMetadata; + this.#writeMetadata = writeMetadata; + } + + /** + * Sync the metadata with the version info from the cdn. + * 1. Remove the files that are not readable (e.g. corrupted or deleted). + * 2. Remove the files that are not in the cdn anymore. + * 3. Remove the files that are not up to date in the cdn. + * 4. Remove the files that are not in the local storage from the metadata. + * 5. Delete the files that are not in the metadata from the local storage. + * + * @param versionInfo - Version information of metadata files. + */ + async syncMetadata(versionInfo: FileMetadataList): Promise { + const metadata = this.#readMetadata(); + const syncedMetadata: FileMetadataList = []; + + for (const fileMetadata of metadata) { + // check if the file is readable (e.g. corrupted or deleted) + try { + await this.readFile(fileMetadata.name, fileMetadata.chainId); + } catch (exp: any) { + console.error('Error: ', exp); + continue; + } + + // check if the file exits and up to date in the storage + if ( + !versionInfo.find( + (file) => + file.name === fileMetadata.name && + file.chainId === fileMetadata.chainId && + file.version === fileMetadata.version && + file.checksum === fileMetadata.checksum, + ) + ) { + continue; + } + + syncedMetadata.push(fileMetadata); + } + + const filesInDB = await this.#storageBackend.dir(); + for (const { name, chainId } of filesInDB) { + if ( + !syncedMetadata.find( + (file) => file.name === name && file.chainId === chainId, + ) + ) { + await this.#storageBackend.delete({ name, chainId }); + } + } + + this.#writeMetadata(syncedMetadata); + return syncedMetadata; + } + + /** + * Read the file from the local storage. + * 1. Check if the file exists in the local storage. + * 2. Check if the file exists in the metadata. + * + * @param name - Name assigned to storage. + * @param chainId - ChainId for which file is queried. + */ + async readFile(name: string, chainId: string): Promise { + const metadata = this.#readMetadata(); + const fileMetadata = metadata.find( + (file) => file.name === name && file.chainId === chainId, + ); + if (!fileMetadata) { + throw new Error(`File metadata (${name}, ${chainId}) not found`); + } + + const data = await this.#storageBackend.read( + { name, chainId }, + fileMetadata.checksum, + ); + if (!data) { + throw new Error(`Storage File (${name}, ${chainId}) not found`); + } + + return data; + } + + /** + * Write the file to the local storage. + * 1. Write the file to the local storage. + * 2. Update the metadata. + * + * @param options - Object passed to write to storage. + * @param options.data - File data to be written. + * @param options.name - Name to be assigned to the storage. + * @param options.chainId - Current ChainId. + * @param options.version - Version of file. + * @param options.checksum - Checksum of file. + */ + async writeFile({ + data, + name, + chainId, + version, + checksum, + }: { + data: ArrayBuffer; + name: string; + chainId: string; + version: string; + checksum: string; + }): Promise { + await this.#storageBackend.write({ name, chainId }, data, checksum); + + const metadata = this.#readMetadata(); + const fileMetadata = metadata.find( + (file) => file.name === name && file.chainId === chainId, + ); + + if (fileMetadata) { + fileMetadata.version = version; + fileMetadata.checksum = checksum; + } else { + metadata.push({ name, chainId, version, checksum }); + } + + this.#writeMetadata(metadata); + } +} diff --git a/src/ppom-storage.ts b/src/ppom-storage.ts index a5946829..1f74efaa 100644 --- a/src/ppom-storage.ts +++ b/src/ppom-storage.ts @@ -1,12 +1,12 @@ /** - * @type FileInfo + * @type FileMetadata * Defined type for information about file saved in storage backend. * @property name - Name of the file. * @property chainId - ChainId for file. * @property version - File version. * @property checksum - Checksum of file data. */ -export type FileInfo = { +export type FileMetadata = { name: string; chainId: string; version: string; @@ -14,8 +14,7 @@ export type FileInfo = { }; /** - * @type PPOMFileMetadata - * Array of objects of type FileInfo + * @type FileMetadataList * This is type of metadata about files saved in storage, * this information is saved in PPOMController state. */ @@ -102,7 +101,7 @@ export class PPOMStorage { */ async syncMetadata(versionInfo: FileMetadataList): Promise { const metadata = this.#readMetadata(); - const syncedMetadata: PPOMFileMetadata = []; + const syncedMetadata: FileMetadataList = []; for (const fileMetadata of metadata) { // check if the file is readable (e.g. corrupted or deleted) From 0eef176b1d0ccc6fa0e9cd5d0d61b08d16cabd01 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:48:17 +0530 Subject: [PATCH 28/33] fix --- src/ppom.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ppom.ts b/src/ppom.ts index f867169b..d9c6be70 100644 --- a/src/ppom.ts +++ b/src/ppom.ts @@ -1,7 +1,8 @@ /* eslint-disable */ // @ts-nocheck -/* eslint-disable */ +// IMPORTANT: this is an file autogenerated for wasm code and should not be changed manually. + let wasm; const heap = new Array(128).fill(undefined); @@ -480,4 +481,5 @@ async function init(input) { return finalizeInit(instance, module); } -export { initSync, init as ppomInit }; +export { initSync }; +export default init; From c96f5ac2338a45a9c3a850c41a494ad6af28e47d Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:48:29 +0530 Subject: [PATCH 29/33] fix --- src/ppom.d.ts | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 src/ppom.d.ts diff --git a/src/ppom.d.ts b/src/ppom.d.ts deleted file mode 100644 index bb5e0319..00000000 --- a/src/ppom.d.ts +++ /dev/null @@ -1,17 +0,0 @@ -export class PPOM { - free(): void; - - constructor(jsonRpcCallback: any, data: Uint8Array); - - test(): Promise; - - validateJsonRpc(request: any): Promise; -} - -type InitInput = any; - -type InitOutput = any; - -export function ppomInit( - moduleOrPath: InitInput | Promise, -): Promise; From 6004210615b0ac211def630d36fbed35d4d309d4 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:51:01 +0530 Subject: [PATCH 30/33] fix --- src/ppom-storage.test.ts | 424 +++++++++++++++++++-------------------- 1 file changed, 209 insertions(+), 215 deletions(-) diff --git a/src/ppom-storage.test.ts b/src/ppom-storage.test.ts index 1f74efaa..ec7167c5 100644 --- a/src/ppom-storage.test.ts +++ b/src/ppom-storage.test.ts @@ -1,215 +1,209 @@ -/** - * @type FileMetadata - * Defined type for information about file saved in storage backend. - * @property name - Name of the file. - * @property chainId - ChainId for file. - * @property version - File version. - * @property checksum - Checksum of file data. - */ -export type FileMetadata = { - name: string; - chainId: string; - version: string; - checksum: string; -}; - -/** - * @type FileMetadataList - * This is type of metadata about files saved in storage, - * this information is saved in PPOMController state. - */ -export type FileMetadataList = FileMetadata[]; - -/** - * @type StorageKey - * This defines type of key that is used for indexing file data saved in StorageBackend. - * @property name - Name of the file. - * @property chainId - ChainId for file. - */ -export type StorageKey = { - name: string; - chainId: string; -}; - -/** - * @type StorageBackend - * This defines type for storage backend implementation. - * There will be different storage implementations depending on platform: - * 1. extension - indexDB - * 2. mobile app - - * @property read - Read file from storage. - * @property write - Write file to storage. - * @property delete - Delete file from storage. - * @property dir - Get list of all files in storage. - */ -export type StorageBackend = { - read(key: StorageKey, checksum: string): Promise; - write(key: StorageKey, data: ArrayBuffer, checksum: string): Promise; - delete(key: StorageKey): Promise; - dir(): Promise; -}; - -/** - * @class PPOMStorage - * This class is responsible for managing the local storage - * It provides the following functionalities: - * 1. Sync the metadata with the version info from the cdn - * 2. Read a file from the local storage - * 3. Write a file to the local storage - * - * It also validates the checksum of the file when reading and writing in order to - * detect corrupted files or files that are not up to date - */ -export class PPOMStorage { - readonly #storageBackend: StorageBackend; - - readonly #readMetadata: () => FileMetadataList; - - readonly #writeMetadata: (metadata: FileMetadataList) => void; - - /** - * Creates a PPOMStorage instance. - * - * @param options - The options passed to the function. - * @param options.storageBackend - The storage backend to use for the local storage. - * @param options.readMetadata - A function to read the metadata from the local storage. - * @param options.writeMetadata - A function to write the metadata to the local storage. - */ - constructor({ - storageBackend, - readMetadata, - writeMetadata, - }: { - storageBackend: StorageBackend; - readMetadata: () => FileMetadataList; - writeMetadata: (metadata: FileMetadataList) => void; - }) { - this.#storageBackend = storageBackend; - this.#readMetadata = readMetadata; - this.#writeMetadata = writeMetadata; - } - - /** - * Sync the metadata with the version info from the cdn. - * 1. Remove the files that are not readable (e.g. corrupted or deleted). - * 2. Remove the files that are not in the cdn anymore. - * 3. Remove the files that are not up to date in the cdn. - * 4. Remove the files that are not in the local storage from the metadata. - * 5. Delete the files that are not in the metadata from the local storage. - * - * @param versionInfo - Version information of metadata files. - */ - async syncMetadata(versionInfo: FileMetadataList): Promise { - const metadata = this.#readMetadata(); - const syncedMetadata: FileMetadataList = []; - - for (const fileMetadata of metadata) { - // check if the file is readable (e.g. corrupted or deleted) - try { - await this.readFile(fileMetadata.name, fileMetadata.chainId); - } catch (exp: any) { - console.error('Error: ', exp); - continue; - } - - // check if the file exits and up to date in the storage - if ( - !versionInfo.find( - (file) => - file.name === fileMetadata.name && - file.chainId === fileMetadata.chainId && - file.version === fileMetadata.version && - file.checksum === fileMetadata.checksum, - ) - ) { - continue; - } - - syncedMetadata.push(fileMetadata); - } - - const filesInDB = await this.#storageBackend.dir(); - for (const { name, chainId } of filesInDB) { - if ( - !syncedMetadata.find( - (file) => file.name === name && file.chainId === chainId, - ) - ) { - await this.#storageBackend.delete({ name, chainId }); - } - } - - this.#writeMetadata(syncedMetadata); - return syncedMetadata; - } - - /** - * Read the file from the local storage. - * 1. Check if the file exists in the local storage. - * 2. Check if the file exists in the metadata. - * - * @param name - Name assigned to storage. - * @param chainId - ChainId for which file is queried. - */ - async readFile(name: string, chainId: string): Promise { - const metadata = this.#readMetadata(); - const fileMetadata = metadata.find( - (file) => file.name === name && file.chainId === chainId, - ); - if (!fileMetadata) { - throw new Error(`File metadata (${name}, ${chainId}) not found`); - } - - const data = await this.#storageBackend.read( - { name, chainId }, - fileMetadata.checksum, - ); - if (!data) { - throw new Error(`Storage File (${name}, ${chainId}) not found`); - } - - return data; - } - - /** - * Write the file to the local storage. - * 1. Write the file to the local storage. - * 2. Update the metadata. - * - * @param options - Object passed to write to storage. - * @param options.data - File data to be written. - * @param options.name - Name to be assigned to the storage. - * @param options.chainId - Current ChainId. - * @param options.version - Version of file. - * @param options.checksum - Checksum of file. - */ - async writeFile({ - data, - name, - chainId, - version, - checksum, - }: { - data: ArrayBuffer; - name: string; - chainId: string; - version: string; - checksum: string; - }): Promise { - await this.#storageBackend.write({ name, chainId }, data, checksum); - - const metadata = this.#readMetadata(); - const fileMetadata = metadata.find( - (file) => file.name === name && file.chainId === chainId, - ); - - if (fileMetadata) { - fileMetadata.version = version; - fileMetadata.checksum = checksum; - } else { - metadata.push({ name, chainId, version, checksum }); - } - - this.#writeMetadata(metadata); - } -} +import { + DUMMY_ARRAY_BUFFER_DATA, + buildStorageBackend, + simpleStorageBackend, + storageBackendReturningData, +} from '../test/test-utils'; +import { PPOMStorage, StorageKey } from './ppom-storage'; + +const DUMMY_CHECKSUM = 'DUMMY_CHECKSUM'; +const DUMMY_NAME = 'DUMMY_NAME'; +const DUMMY_CHAINID = '1'; +const ARRAY_BUFFER_DATA = new ArrayBuffer(123); + +const getFileData = (data = {}) => ({ + chainId: DUMMY_CHAINID, + name: DUMMY_NAME, + checksum: DUMMY_CHECKSUM, + version: '0', + ...data, +}); + +const simpleFileData = getFileData(); + +describe('PPOMStorage', () => { + describe('readFile', () => { + it('should return data', async () => { + const ppomStorage = new PPOMStorage({ + storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + readMetadata: () => [simpleFileData], + writeMetadata: () => undefined, + }); + const data = await ppomStorage.readFile(DUMMY_NAME, DUMMY_CHAINID); + expect(data).toStrictEqual(DUMMY_ARRAY_BUFFER_DATA); + }); + + it('should throw error if file metadata not found', async () => { + const ppomStorage = new PPOMStorage({ + storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + readMetadata: () => [], + writeMetadata: () => undefined, + }); + await expect(async () => { + await ppomStorage.readFile(DUMMY_NAME, DUMMY_CHAINID); + }).rejects.toThrow( + `File metadata (${DUMMY_NAME}, ${DUMMY_CHAINID}) not found`, + ); + }); + + it('should throw error if file is not found in storage', async () => { + const ppomStorage = new PPOMStorage({ + storageBackend: simpleStorageBackend, + readMetadata: () => [simpleFileData], + writeMetadata: () => undefined, + }); + await expect(async () => { + await ppomStorage.readFile(DUMMY_NAME, DUMMY_CHAINID); + }).rejects.toThrow( + `Storage File (${DUMMY_NAME}, ${DUMMY_CHAINID}) not found`, + ); + }); + }); + + describe('writeFile', () => { + it('should call storageBackend.write', async () => { + const mockWrite = jest.fn().mockResolvedValue('test'); + const ppomStorage = new PPOMStorage({ + storageBackend: buildStorageBackend({ write: mockWrite }), + readMetadata: () => [], + writeMetadata: () => undefined, + }); + await ppomStorage.writeFile({ + data: DUMMY_ARRAY_BUFFER_DATA, + ...simpleFileData, + }); + expect(mockWrite).toHaveBeenCalledTimes(1); + }); + + it('should invoke writeMetadata if file metadata exists', async () => { + const mockWriteMetadata = jest.fn(); + const ppomStorage = new PPOMStorage({ + storageBackend: simpleStorageBackend, + readMetadata: () => [simpleFileData], + writeMetadata: mockWriteMetadata, + }); + await ppomStorage.writeFile({ + data: DUMMY_ARRAY_BUFFER_DATA, + ...simpleFileData, + }); + expect(mockWriteMetadata).toHaveBeenCalledWith([simpleFileData]); + }); + + it('should invoke writeMetadata with data passed', async () => { + const mockWriteMetadata = jest.fn(); + const ppomStorage = new PPOMStorage({ + storageBackend: simpleStorageBackend, + readMetadata: () => [], + writeMetadata: mockWriteMetadata, + }); + await ppomStorage.writeFile({ + data: DUMMY_ARRAY_BUFFER_DATA, + ...simpleFileData, + }); + expect(mockWriteMetadata).toHaveBeenCalledWith([simpleFileData]); + }); + }); + + describe('syncMetadata', () => { + it('should return metadata of file if updated file is found in storage', async () => { + const mockWriteMetadata = jest.fn(); + const ppomStorage = new PPOMStorage({ + storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + readMetadata: () => [simpleFileData], + writeMetadata: mockWriteMetadata, + }); + + const result = await ppomStorage.syncMetadata([simpleFileData]); + expect(mockWriteMetadata).toHaveBeenCalledWith([simpleFileData]); + expect(result).toStrictEqual([simpleFileData]); + }); + + it('should not return data if file is not found in storage', async () => { + const mockWriteMetadata = jest.fn(); + const ppomStorage = new PPOMStorage({ + storageBackend: simpleStorageBackend, + readMetadata: () => [simpleFileData], + writeMetadata: mockWriteMetadata, + }); + + const result = await ppomStorage.syncMetadata([simpleFileData]); + expect(mockWriteMetadata).toHaveBeenCalledWith([]); + expect(result).toStrictEqual([]); + }); + + it('should not return metadata of file if file version in storage is outdated', async () => { + const storageFileData = { ...simpleFileData, version: '1' }; + const mockWriteMetadata = jest.fn(); + const mockDelete = jest.fn().mockResolvedValue(''); + + const ppomStorage = new PPOMStorage({ + storageBackend: buildStorageBackend({ + read: async (_key: StorageKey): Promise => + Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), + dir: async () => Promise.resolve([storageFileData]), + delete: mockDelete, + }), + readMetadata: () => [simpleFileData], + writeMetadata: mockWriteMetadata, + }); + + const result = await ppomStorage.syncMetadata([storageFileData]); + expect(mockDelete).toHaveBeenCalledWith({ + name: DUMMY_NAME, + chainId: DUMMY_CHAINID, + }); + expect(mockWriteMetadata).toHaveBeenCalledWith([]); + expect(result).toStrictEqual([]); + }); + + it('should delete file from storage backend if its name is not found in file version info passed', async () => { + const fileDataInStorage = getFileData({ + name: 'dummy_2', + }); + const mockWriteMetadata = jest.fn(); + const mockDelete = jest.fn().mockResolvedValue(''); + + const ppomStorage = new PPOMStorage({ + storageBackend: buildStorageBackend({ + read: async (_key: StorageKey): Promise => + Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), + dir: async () => Promise.resolve([fileDataInStorage]), + delete: mockDelete, + }), + readMetadata: () => [simpleFileData], + writeMetadata: mockWriteMetadata, + }); + + await ppomStorage.syncMetadata([simpleFileData]); + expect(mockDelete).toHaveBeenCalledWith({ + name: 'dummy_2', + chainId: DUMMY_CHAINID, + }); + }); + + it('should delete file from storage backend if its version info is not passed', async () => { + const fileDataInStorage = getFileData({ + chainId: '5', + }); + const mockWriteMetadata = jest.fn(); + const mockDelete = jest.fn().mockResolvedValue(''); + + const ppomStorage = new PPOMStorage({ + storageBackend: buildStorageBackend({ + read: async (_key: StorageKey): Promise => + Promise.resolve(DUMMY_ARRAY_BUFFER_DATA), + dir: async () => Promise.resolve([fileDataInStorage]), + delete: mockDelete, + }), + readMetadata: () => [simpleFileData], + writeMetadata: mockWriteMetadata, + }); + + await ppomStorage.syncMetadata([simpleFileData]); + expect(mockDelete).toHaveBeenCalledWith({ + name: DUMMY_NAME, + chainId: '5', + }); + }); + }); +}); From 818c6643161bfc3a01a575f37e900eba1f1b10f1 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:52:03 +0530 Subject: [PATCH 31/33] fix --- src/ppom.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ppom.ts b/src/ppom.ts index d9c6be70..f63fa025 100644 --- a/src/ppom.ts +++ b/src/ppom.ts @@ -481,5 +481,4 @@ async function init(input) { return finalizeInit(instance, module); } -export { initSync }; -export default init; +export { initSync, init as ppomInit }; From 7305cbd267f21eb3b10bf2fd1fe66d59fcbdb22c Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:52:40 +0530 Subject: [PATCH 32/33] fix --- src/ppom-storage.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ppom-storage.test.ts b/src/ppom-storage.test.ts index ec7167c5..3e2e798f 100644 --- a/src/ppom-storage.test.ts +++ b/src/ppom-storage.test.ts @@ -25,7 +25,7 @@ describe('PPOMStorage', () => { describe('readFile', () => { it('should return data', async () => { const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + storageBackend: storageBackendReturningData, readMetadata: () => [simpleFileData], writeMetadata: () => undefined, }); @@ -35,7 +35,7 @@ describe('PPOMStorage', () => { it('should throw error if file metadata not found', async () => { const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + storageBackend: storageBackendReturningData, readMetadata: () => [], writeMetadata: () => undefined, }); @@ -108,7 +108,7 @@ describe('PPOMStorage', () => { it('should return metadata of file if updated file is found in storage', async () => { const mockWriteMetadata = jest.fn(); const ppomStorage = new PPOMStorage({ - storageBackend: buildStorageBackendReturningData(ARRAY_BUFFER_DATA), + storageBackend: storageBackendReturningData, readMetadata: () => [simpleFileData], writeMetadata: mockWriteMetadata, }); From 3437bfbb9c0183fc6ed9409f24e2674143694c2b Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Thu, 15 Jun 2023 11:54:43 +0530 Subject: [PATCH 33/33] fix --- src/ppom-storage.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ppom-storage.test.ts b/src/ppom-storage.test.ts index 3e2e798f..bfea4375 100644 --- a/src/ppom-storage.test.ts +++ b/src/ppom-storage.test.ts @@ -9,7 +9,6 @@ import { PPOMStorage, StorageKey } from './ppom-storage'; const DUMMY_CHECKSUM = 'DUMMY_CHECKSUM'; const DUMMY_NAME = 'DUMMY_NAME'; const DUMMY_CHAINID = '1'; -const ARRAY_BUFFER_DATA = new ArrayBuffer(123); const getFileData = (data = {}) => ({ chainId: DUMMY_CHAINID,