Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/database storage #343

Merged
merged 15 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@
},
"[jsonc]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[typescript]": {
"editor.defaultFormatter": "vscode.typescript-language-features"
}
}
13 changes: 13 additions & 0 deletions TEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Manual Test Procedures

These instructions are different procedures to test various parts of the software. Developers should attempt to routinely go through these tests upon each release.

## Create a new clean wallet

1. Bla, bla


## Restore an existing wallet with history

1. Bla, bla

2 changes: 1 addition & 1 deletion angular/src/app/account/identity/identity.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export class IdentityComponent implements OnInit, OnDestroy {
const identityNode = this.identityService.getIdentityNode(this.walletManager.activeWallet, this.walletManager.activeAccount);

this.privateKey = this.cryptoUtility.convertToBech32(identityNode.privateKey, 'nsec');
console.log(secp.utils.bytesToHex(identityNode.privateKey));
// console.log(secp.utils.bytesToHex(identityNode.privateKey));
//this.privateKey = secp.utils.bytesToHex(identityNode.privateKey);

this.qrCodePrivateKey = await QRCode.toDataURL('nostr:' + this.privateKey, {
Expand Down
3 changes: 3 additions & 0 deletions angular/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AppUpdateService } from './services/app-update.service';
import { ActionService } from './services/action.service';
import { DisableRightClickService } from './services/disable-right-click.service';
import { MessageService } from 'src/shared';
import { Database } from 'src/shared/store/storage';

@Component({
selector: 'app-root',
Expand Down Expand Up @@ -86,6 +87,8 @@ export class AppComponent implements OnInit {
async ngOnInit() {
this.uiState.manifest = await this.runtime.getManifest();

// await Database.Instance.open();

// let qs = new URLSearchParams(location.search);
// let id = qs.get('id');
// let host = qs.get('host');
Expand Down
1 change: 0 additions & 1 deletion angular/src/app/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export class DashboardComponent implements OnInit, OnDestroy {
if (walletId) {
// Set the active wallet if different from before.
if (this.walletManager.activeWalletId != walletId) {
console.log('DASHBOARD SET ACTIVE WALLET!');
await this.walletManager.setActiveWallet(walletId);

// Check if the new wallet is unlocked, if not, go to home and unlock.
Expand Down
3 changes: 3 additions & 0 deletions angular/src/app/services/application-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MessageService, NetworkLoader } from 'src/shared';
import { MINUTE } from '../shared/constants';
import { SecureStateService, CryptoUtility, DataSyncService, CommunicationService, WalletManager } from './';
import { StateService } from './state.service';
import { Database } from 'src/shared/store/storage';

@Injectable({
providedIn: 'root',
Expand All @@ -23,6 +24,8 @@ export class AppManager {
async initialize() {
await this.communication.initialize();

await Database.Instance.open();

// Load all the stores.
await this.state.load();

Expand Down
14 changes: 10 additions & 4 deletions angular/src/app/settings/permissions/permissions.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,23 @@ export class PermissionsComponent implements OnDestroy, OnInit {
this.executions = this.permissionExecutionStore.all();
}

executedCount(permission: Permission)
{
executedCount(permission: Permission) {
const key = PermissionStore.permissionKey(permission);
return this.permissionExecutionStore.get(key).executions;

const val = this.permissionExecutionStore.get(key);

if (val == null) {
return 0;
}

return val.executions;
}

toArray(items: any): Permission[][] {
return Object.values(items);
}

ngOnDestroy() {}
ngOnDestroy() { }

cancel() {
this.location.back();
Expand Down
4 changes: 1 addition & 3 deletions angular/src/shared/permission.service.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { ActionMessage, Permission, PermissionDomain } from './interfaces';
import { PermissionExecutionStore } from './store/permission-execution-store';
import { PermissionStore } from './store/permission-store';
import { Database } from './store/storage';

export class PermissionServiceShared {
private store: PermissionStore;
private storeExecutions: PermissionExecutionStore;

constructor() {
this.store = new PermissionStore();
this.store.load();

this.storeExecutions = new PermissionExecutionStore();
this.storeExecutions.load();
}

get(app: string) {
Expand Down
44 changes: 36 additions & 8 deletions angular/src/shared/persistence.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Database } from "./store/storage";

/** Abstracts the storage API and relies on localStorage for unit tests/fallback. */
export class Persistence {
db = Database.Instance;

/** The consumer of this API is responsible to ensure the value can be serialized to JSON. */
async set(key: string, value: any) {
await this.db.putBucket(key, value);

if (globalThis.chrome && globalThis.chrome.storage) {
await globalThis.chrome.storage.local.set({ [key]: value });
} else {
Expand All @@ -10,21 +16,43 @@ export class Persistence {
}

async get<T>(key: string): Promise<T> {
if (globalThis.chrome && globalThis.chrome.storage) {
const value = await globalThis.chrome.storage.local.get(key);
return value[key];
} else {
let value = globalThis.localStorage.getItem(key);
const dbValue = await this.db.getBucket(key);

if (dbValue) {
return dbValue.value;
}

if (value) {
return JSON.parse(value);
// If we can't find in the database, try to find in the storage.
if (!dbValue) {
if (globalThis.chrome && globalThis.chrome.storage) {
const value = await globalThis.chrome.storage.local.get(key);

// If it exists in the storage, put it in the database.
if (value[key]) {
await this.db.putBucket(key, value[key]);
}

return value[key];
} else {
return undefined;
let value = globalThis.localStorage.getItem(key);

if (value) {
// If it exists in the storage, put it in the database.
await this.db.putBucket(key, JSON.parse(value));

return JSON.parse(value);
} else {
return undefined;
}
}
} else {
return undefined;
}
}

async remove(key: string) {
await this.db.deleteBucket(key);

if (globalThis.chrome && globalThis.chrome.storage) {
await globalThis.chrome.storage.local.remove(key);
} else {
Expand Down
6 changes: 5 additions & 1 deletion angular/src/shared/shared.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ import { Wallet } from './interfaces';
import { AccountHistoryStore, SettingStore } from './store';
import { AddressStore } from './store/address-store';
import { NetworkLoader } from './network-loader';
import { Database } from './store/storage';

describe('SharedTests', () => {
beforeEach(() => {});
beforeEach(async () => {
// Ensure that the database has been opened before each test.
await Database.Instance.open();
});

// it('Validate the StateStore', async () => {
// const stateStore = new StateStore();
Expand Down
3 changes: 3 additions & 0 deletions angular/src/shared/storage.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { RuntimeService } from "./runtime.service";

// This service is primarily only used for "active" and "timeout" keys.
export class StorageService {

constructor(public runtime: RuntimeService) {
}

async set(key: string, value: any, persisted: boolean) {
// console.log(`SET: ${key} PERSISTED: ${persisted}`);
if (this.runtime.isExtension) {
if (persisted) {
await globalThis.chrome.storage.local.set({ [key]: value });
Expand All @@ -25,6 +27,7 @@ export class StorageService {
}

async get(key: string, persisted: boolean) {
// console.log(`GET: ${key} PERSISTED: ${persisted}`);
if (this.runtime.isExtension) {
if (persisted) {
let { keys } = await globalThis.chrome.storage.local.get([key]);
Expand Down
82 changes: 82 additions & 0 deletions angular/src/shared/store/storage.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Injectable } from '@angular/core';
import { Database, Storage } from './storage';
import * as moment from 'moment';

@Injectable({
providedIn: 'root',
})
export class StorageService {
storage!: Storage;
state!: any;

constructor() {}

async initialize(databaseName: string) {
// Open the new storage database.
this.storage = Database.Instance; // new Storage('blockcore-wallet');
await this.storage.open();

let state = await this.storage.getState();

if (!state) {
// The initial since we will use is two days past.
const timeAgo = moment().subtract(2, 'days').unix();

state = {
id: 1,
since: timeAgo,
mediaQueue: []
};
}

this.state = state;

// Update the state on interval.
setTimeout(async () => {
console.log('Persisting state...');

// The since will always be set slightly back in time to counteract difference in clocks
// for different event creators on the nostr network.
const timeAgo = moment().subtract(10, 'minutes').unix();
this.state.since = timeAgo;

await this.saveState();
}, 60 * 1000);

// TODO: Remove, old code.
// this.db = new DatabaseService(databaseName);
// return this.db.open();
}

async clearAndReload() {
console.log('Deleting storage...');

setTimeout(() => {
console.log('Reloading!');
location.reload();
}, 1000);

try {
// this.db.close();
await this.delete();
} catch (err) {
console.error(err);
}
}

async saveState() {
await this.storage.putState(this.state);
}

close() {
try {
this.storage.close();
} catch (err) {
console.error('Failed to close storage.', err);
}
}

async delete() {
await this.storage.deleteDatabase();
}
}
56 changes: 56 additions & 0 deletions angular/src/shared/store/storage.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// import { storage } from 'webextension-polyfill';
// import { Storage, TableAccountState, TableWallet } from './storage';
// import { str } from '@scure/base';
// import exp from 'constants';

// describe('Storage', () => {
// let storage: Storage;

// beforeEach( async () => {
// storage = new Storage('blockcore-wallet-test');
// await storage.open();
// });

// it('Should be able to create the database', async () => {
// expect(storage).toBeDefined();
// });

// it('Should persist account state', async () => {

// var doc: TableAccountState = {
// id: '12345',
// balance: 110
// };

// await storage.putAccountState(doc);

// var result = await storage.getAccountState('12345');
// expect(doc.balance).toBe(result.balance);
// // await storage.delete();
// });

// it('Should persist and delete wallet state', async () => {
// var doc: TableWallet = {
// id: '123',
// name: 'testWallet',
// restored: false,
// mnemonic: '',
// accounts: [],
// extensionWords: '',
// biometrics: false
// };

// await storage.putWallet(doc);

// let result = await storage.getWallet('1234');
// expect(result).toBeUndefined();
// result = await storage.getWallet('123');
// expect(result).toBeDefined();
// expect(result.name).toBe('testWallet');

// await storage.deleteWallet(doc.id);

// result = await storage.getWallet(doc.id);
// expect(result).toBeUndefined();
// })
// });
Loading