UNPKG

@neo-one/node-blockchain-esnext-esm

Version:

NEO•ONE NEO blockchain implementation.

558 lines (556 loc) 20.3 kB
import { common } from '@neo-one/client-common-esnext-esm'; import { utils as commonUtils } from '@neo-one/utils-esnext-esm'; import { concat, defer, EMPTY, of as _of } from 'rxjs'; import { concatMap } from 'rxjs/operators'; function createGet({ tryGetTracked, readStorage, }) { return async (key) => { const trackedChange = tryGetTracked(key); if (trackedChange !== undefined) { if (trackedChange.type === 'delete') { throw new Error('Not found'); } return trackedChange.value; } return readStorage().get(key); }; } function createTryGet({ tryGetTracked, readStorage, }) { return async (key) => { const trackedChange = tryGetTracked(key); if (trackedChange !== undefined) { if (trackedChange.type === 'delete') { return undefined; } return trackedChange.value; } return readStorage().tryGet(key); }; } export class BaseReadStorageCache { constructor(options) { this.readStorage = options.readStorage; this.name = options.name; this.createAddChange = options.createAddChange; this.createDeleteChange = options.createDeleteChange; this.onAdd = options.onAdd; this.mutableValues = {}; this.get = createGet({ readStorage: this.readStorage, tryGetTracked: this.tryGetTracked.bind(this), }); this.tryGet = createTryGet({ readStorage: this.readStorage, tryGetTracked: this.tryGetTracked.bind(this), }); this.tryGetValue = (key) => this.readStorage().tryGet(key); } getChangeSet() { const createDeleteChange = this.createDeleteChange; return Object.values(this.mutableValues).map((value) => { if (value.type === 'delete') { if (createDeleteChange === undefined) { throw new Error('Invalid delete'); } return { type: 'delete', change: createDeleteChange(value.key) }; } return { type: 'add', change: this.createAddChange(value.addValue), subType: value.subType }; }); } getTrackedChangeSet() { const createDeleteChange = this.createDeleteChange; return Object.entries(this.mutableValues).map(([key, value]) => { if (value.type === 'delete') { if (createDeleteChange === undefined) { throw new Error('Invalid delete'); } return { type: createDeleteChange(value.key).type, key, value }; } return { type: this.createAddChange(value.addValue).type, key, value }; }); } tryGetTracked(_key) { throw new Error('Not Implemented'); } } class ReadStorageCache extends BaseReadStorageCache { constructor(options) { super({ readStorage: options.readStorage, name: options.name, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, }); this.getKeyString = options.getKeyString; } tryGetTracked(key) { return this.mutableValues[this.getKeyString(key)]; } addTrackedChange(key, value) { this.mutableValues[key] = value; } } class ReadAllStorageCache extends ReadStorageCache { constructor(options) { super({ readStorage: () => ({ get: options.readAllStorage().get, tryGet: options.readAllStorage().tryGet, }), name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, }); this.readAllStorage = options.readAllStorage; this.getKeyFromValue = options.getKeyFromValue; this.all$ = concat(defer(() => this.readAllStorage().all$.pipe(concatMap((value) => { const trackedChange = this.tryGetTracked(this.getKeyFromValue(value)); if (trackedChange !== undefined) { return EMPTY; } return _of(value); }))), defer(() => _of(...Object.values(this.mutableValues) .map((value) => (value.type === 'add' ? value.value : undefined)) .filter(commonUtils.notNull)))); } } class ReadGetAllStorageCache extends ReadStorageCache { constructor(options) { super({ readStorage: () => ({ get: options.readGetAllStorage().get, tryGet: options.readGetAllStorage().tryGet, }), name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, }); this.readGetAllStorage = options.readGetAllStorage; this.getKeyFromValue = options.getKeyFromValue; this.matchesPartialKey = options.matchesPartialKey; this.getAll$ = (key) => concat(defer(() => this.readGetAllStorage() .getAll$(key) .pipe(concatMap((value) => { const trackedChange = this.tryGetTracked(this.getKeyFromValue(value)); if (trackedChange !== undefined) { return EMPTY; } return _of(value); }))), defer(() => _of(...Object.values(this.mutableValues) .map((value) => value.type === 'add' && this.matchesPartialKey(value.value, key) ? value.value : undefined) .filter(commonUtils.notNull)))); } } function createAdd({ cache, getKeyFromValue, getKeyString, allowDupes, }) { return async (value) => { const key = getKeyFromValue(value); if (!allowDupes) { const currentValue = await cache.tryGet(key); if (currentValue !== undefined) { throw new Error(`Attempted to add an already existing object for key ` + `${cache.name}:${getKeyString(key)}.`); } } if (cache.onAdd !== undefined) { await cache.onAdd(value); } const trackedChange = cache.tryGetTracked(key); cache.mutableValues[cache.getKeyString(key)] = { type: 'add', addValue: value, value, subType: trackedChange === undefined ? 'add' : 'update', }; }; } function createUpdate({ cache, update: updateFunc, getKeyFromValue, }) { return async (value, update) => { const key = getKeyFromValue(value); const updatedValue = updateFunc(value, update); const trackedChange = cache.tryGetTracked(key); cache.mutableValues[cache.getKeyString(key)] = { type: 'add', addValue: updatedValue, value: updatedValue, subType: trackedChange === undefined || trackedChange.type === 'delete' || trackedChange.subType === 'update' ? 'update' : 'add', }; return updatedValue; }; } function createDelete({ cache }) { return async (key) => { const currentValue = await cache.tryGetValue(key); if (currentValue === undefined) { delete cache.mutableValues[cache.getKeyString(key)]; } else { cache.mutableValues[cache.getKeyString(key)] = { type: 'delete', key }; } }; } export class ReadAddUpdateDeleteStorageCache extends ReadStorageCache { constructor(options) { super({ readStorage: options.readStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, }); this.update = createUpdate({ cache: this, update: options.update, getKeyFromValue: options.getKeyFromValue, }); this.delete = createDelete({ cache: this }); } } export class ReadAddUpdateStorageCache extends ReadStorageCache { constructor(options) { super({ readStorage: options.readStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, allowDupes: options.allowDupes, }); this.update = createUpdate({ cache: this, update: options.update, getKeyFromValue: options.getKeyFromValue, }); } } export class ReadAddDeleteStorageCache extends ReadStorageCache { constructor(options) { super({ readStorage: options.readStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, }); this.delete = createDelete({ cache: this }); } } export class ReadAddStorageCache extends ReadStorageCache { constructor(options) { super({ readStorage: options.readStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, allowDupes: options.allowDupes, }); } } export class ReadGetAllAddDeleteStorageCache extends ReadGetAllStorageCache { constructor(options) { super({ readGetAllStorage: options.readGetAllStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, getKeyFromValue: options.getKeyFromValue, matchesPartialKey: options.matchesPartialKey, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, }); this.delete = createDelete({ cache: this }); } } export class ReadGetAllAddUpdateDeleteStorageCache extends ReadGetAllStorageCache { constructor(options) { super({ readGetAllStorage: options.readGetAllStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, getKeyFromValue: options.getKeyFromValue, matchesPartialKey: options.matchesPartialKey, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, }); this.update = createUpdate({ cache: this, update: options.update, getKeyFromValue: options.getKeyFromValue, }); this.delete = createDelete({ cache: this }); } } export class ReadGetAllAddStorageCache extends ReadGetAllStorageCache { constructor(options) { super({ readGetAllStorage: options.readGetAllStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, getKeyFromValue: options.getKeyFromValue, matchesPartialKey: options.matchesPartialKey, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, }); } } export class ReadAllAddUpdateDeleteStorageCache extends ReadAllStorageCache { constructor(options) { super({ readAllStorage: options.readAllStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, getKeyFromValue: options.getKeyFromValue, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, }); this.update = createUpdate({ cache: this, update: options.update, getKeyFromValue: options.getKeyFromValue, }); this.delete = createDelete({ cache: this }); } } export class ReadAllAddStorageCache extends ReadAllStorageCache { constructor(options) { super({ readAllStorage: options.readAllStorage, name: options.name, getKeyString: options.getKeyString, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, getKeyFromValue: options.getKeyFromValue, }); this.add = createAdd({ cache: this, getKeyFromValue: options.getKeyFromValue, getKeyString: options.getKeyString, }); } } export class BlockLikeStorageCache extends BaseReadStorageCache { constructor(options) { super({ readStorage: options.readStorage, name: options.name, createAddChange: options.createAddChange, }); this.mutableIndexValues = {}; } async add(value) { const currentValue = await this.tryGet({ hashOrIndex: value.index }); if (currentValue !== undefined) { throw new Error('Attempted to add an already existing object.'); } const addValue = { type: 'add', addValue: value, value, subType: 'add' }; this.mutableValues[common.uInt256ToString(value.hash)] = addValue; this.mutableIndexValues[`${value.index}`] = addValue; } tryGetTracked(key) { if (typeof key.hashOrIndex !== 'number') { return this.mutableValues[common.uInt256ToString(key.hashOrIndex)]; } return this.mutableIndexValues[`${key.hashOrIndex}`]; } addTrackedChange(key, value) { this.mutableValues[key] = value; } } const getOutputValueKeyString = (key) => `${common.uInt256ToHex(key.hash)}:${key.index}`; export class OutputStorageCache extends ReadStorageCache { constructor(readStorage) { super({ readStorage, name: 'output', getKeyString: getOutputValueKeyString, createAddChange: (value) => ({ type: 'output', value }), }); this.add = async (value) => { const key = { hash: value.hash, index: value.index }; const currentValue = await this.tryGet(key); if (currentValue !== undefined) { throw new Error(`Attempted to add an already existing object for key ` + `${this.name}:${this.getKeyString(key)}.`); } this.mutableValues[this.getKeyString(key)] = { type: 'add', addValue: value, value: value.output, subType: 'add', }; }; } } function createGetMetadata({ tryGetTracked, readStorage, }) { return async () => { const trackedChange = tryGetTracked(); if (trackedChange !== undefined) { if (trackedChange.type === 'delete') { throw new Error('Not found'); } return trackedChange.value; } return readStorage().get(); }; } function createTryGetMetadata({ tryGetTracked, readStorage, }) { return async () => { const trackedChange = tryGetTracked(); if (trackedChange !== undefined) { if (trackedChange.type === 'delete') { return undefined; } return trackedChange.value; } return readStorage().tryGet(); }; } export class BaseReadMetadataStorageCache { constructor(options) { this.readStorage = options.readStorage; this.name = options.name; this.createAddChange = options.createAddChange; this.createDeleteChange = options.createDeleteChange; this.onAdd = options.onAdd; this.get = createGetMetadata({ readStorage: this.readStorage, tryGetTracked: this.tryGetTracked.bind(this), }); this.tryGet = createTryGetMetadata({ readStorage: this.readStorage, tryGetTracked: this.tryGetTracked.bind(this), }); } getChangeSet() { const createDeleteChange = this.createDeleteChange; const value = this.mutableValue; if (value === undefined) { return []; } if (value.type === 'delete') { if (createDeleteChange === undefined) { throw new Error('Invalid delete'); } return [{ type: 'delete', change: createDeleteChange() }]; } return [{ type: 'add', change: this.createAddChange(value.addValue), subType: value.subType }]; } getTrackedChangeSet() { const createDeleteChange = this.createDeleteChange; const value = this.mutableValue; if (value === undefined) { return []; } if (value.type === 'delete') { if (createDeleteChange === undefined) { throw new Error('Invalid delete'); } return [{ type: createDeleteChange().type, key: 'metadata', value: { ...value, key: 'metadata' } }]; } return [{ type: this.createAddChange(value.addValue).type, key: 'metadata', value }]; } tryGetTracked() { return this.mutableValue; } addTrackedChange(_key, value) { this.mutableValue = value; } } class ReadMetadataStorageCache extends BaseReadMetadataStorageCache { } function createAddMetadata({ cache, }) { return async (value) => { if (cache.onAdd !== undefined) { await cache.onAdd(value); } cache.mutableValue = { type: 'add', addValue: value, value, subType: 'add', }; }; } function createUpdateMetadata({ cache, update: updateFunc, }) { return async (value, update) => { const updatedValue = updateFunc(value, update); cache.mutableValue = { type: 'add', addValue: updatedValue, value: updatedValue, subType: 'update', }; return updatedValue; }; } export class ReadAddUpdateMetadataStorageCache extends ReadMetadataStorageCache { constructor(options) { super({ readStorage: options.readStorage, name: options.name, createAddChange: options.createAddChange, createDeleteChange: options.createDeleteChange, onAdd: options.onAdd, }); this.add = createAddMetadata({ cache: this, }); this.update = createUpdateMetadata({ cache: this, update: options.update, }); } } //# sourceMappingURL=StorageCache.js.map