@neo-one/node-blockchain-esnext-esm
Version:
NEO•ONE NEO blockchain implementation.
558 lines (556 loc) • 20.3 kB
JavaScript
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