Very WIP changes from DeSo structs to LBRY structs
This commit is contained in:
parent
6b6efe6ce0
commit
a3023dc40c
10 changed files with 113 additions and 117 deletions
|
@ -1,18 +1,14 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {CryptoService} from './crypto.service';
|
||||
import {GlobalVarsService} from './global-vars.service';
|
||||
import {AccessLevel, Network, PrivateUserInfo, PublicUserInfo} from '../types/identity';
|
||||
import HDKey from 'hdkey';
|
||||
import {AccessLevel, PrivateAccountInfo, PublicAccountInfo} from '../types/identity';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AccountService {
|
||||
private static usersStorageKey = 'users';
|
||||
private static levelsStorageKey = 'levels';
|
||||
|
||||
private static publicKeyRegex = /^[a-zA-Z0-9]{54,55}$/;
|
||||
|
||||
constructor(
|
||||
private cryptoService: CryptoService,
|
||||
private globalVars: GlobalVarsService,
|
||||
|
@ -20,41 +16,70 @@ export class AccountService {
|
|||
|
||||
// Public Getters
|
||||
|
||||
getPublicKeys(): any {
|
||||
return Object.keys(this.getPrivateUsers());
|
||||
getAccountNames(): any {
|
||||
// TODO - maybe write this in a safer, more future-perfect way since it's converting
|
||||
// private to public
|
||||
return Object.keys(this.getWalletAccounts());
|
||||
}
|
||||
|
||||
getEncryptedUsers(): {[key: string]: PublicUserInfo} {
|
||||
// TODO - As of this writing, we want to share channel claim ids with the
|
||||
// account on login, and spending addresses on request (probably with
|
||||
// explicit permission)
|
||||
//
|
||||
// This is in a state in between what DeSo had and what
|
||||
// we want ultimately for LBRY.
|
||||
getPublicAccounts(): {[key: string]: PublicAccountInfo} {
|
||||
const hostname = this.globalVars.hostname;
|
||||
const privateUsers = this.getPrivateUsers();
|
||||
const publicUsers: {[key: string]: PublicUserInfo} = {};
|
||||
const privateAccounts = this.getWalletAccounts();
|
||||
const publicAccounts: {[key: string]: PublicAccountInfo} = {};
|
||||
|
||||
for (const publicKey of Object.keys(privateUsers)) {
|
||||
const privateUser = privateUsers[publicKey];
|
||||
const accessLevel = this.getAccessLevel(publicKey, hostname);
|
||||
for (const name of Object.keys(privateAccounts)) {
|
||||
const privateAccount = privateAccounts[name];
|
||||
const accessLevel = this.getAccessLevel(name, hostname);
|
||||
if (accessLevel === AccessLevel.None) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const encryptedSeedHex = this.cryptoService.encryptSeedHex(privateUser.seedHex, hostname);
|
||||
const accessLevelHmac = this.cryptoService.accessLevelHmac(accessLevel, privateUser.seedHex);
|
||||
// TODO
|
||||
throw 'Implement the hmac properly'
|
||||
|
||||
publicUsers[publicKey] = {
|
||||
hasExtraText: privateUser.extraText?.length > 0,
|
||||
encryptedSeedHex,
|
||||
network: privateUser.network,
|
||||
// TODO - why do we even have hmac if everything's in local storage anyway?
|
||||
const accessLevelHmac = this.cryptoService.accessLevelHmac(accessLevel, privateAccount.seed);
|
||||
|
||||
publicAccounts[name] = {
|
||||
name,
|
||||
network: privateAccount.ledger,
|
||||
accessLevel,
|
||||
accessLevelHmac,
|
||||
};
|
||||
}
|
||||
|
||||
return publicUsers;
|
||||
return publicAccounts;
|
||||
}
|
||||
|
||||
getAccessLevel(publicKey: string, hostname: string): AccessLevel {
|
||||
// TODO - Need to confirm that this works I think
|
||||
public getWalletAccounts(): {[key: string]: PrivateAccountInfo} {
|
||||
const wallet = this.cryptoService.getWallet(this.globalVars.hostname)
|
||||
if (wallet === null) {
|
||||
return {}
|
||||
}
|
||||
const filteredAccounts: {[key: string]: PrivateAccountInfo} = {};
|
||||
|
||||
for (const account of wallet.accounts) {
|
||||
// Only include accounts from the current network
|
||||
if (account.ledger !== this.globalVars.network) {
|
||||
continue;
|
||||
}
|
||||
|
||||
filteredAccounts[account.name] = account;
|
||||
}
|
||||
return filteredAccounts
|
||||
}
|
||||
|
||||
getAccessLevel(accountName: string, hostname: string): AccessLevel {
|
||||
const levels = JSON.parse(localStorage.getItem(AccountService.levelsStorageKey) || '{}');
|
||||
const hostMapping = levels[hostname] || {};
|
||||
const accessLevel = hostMapping[publicKey];
|
||||
const accessLevel = hostMapping[accountName];
|
||||
|
||||
if (Object.values(AccessLevel).includes(accessLevel)) {
|
||||
return accessLevel;
|
||||
|
@ -65,78 +90,25 @@ export class AccountService {
|
|||
|
||||
// Public Modifiers
|
||||
|
||||
addUser(keychain: HDKey, mnemonic: string, extraText: string, network: Network): string {
|
||||
const seedHex = this.cryptoService.keychainToSeedHex(keychain);
|
||||
|
||||
return this.addPrivateUser({
|
||||
seedHex,
|
||||
mnemonic,
|
||||
extraText,
|
||||
network,
|
||||
});
|
||||
}
|
||||
|
||||
deleteUser(publicKey: string): void {
|
||||
const privateUsers = this.getPrivateUsersRaw();
|
||||
|
||||
delete privateUsers[publicKey];
|
||||
|
||||
this.setPrivateUsersRaw(privateUsers);
|
||||
}
|
||||
|
||||
setAccessLevel(publicKey: string, hostname: string, accessLevel: AccessLevel): void {
|
||||
setAccessLevel(accountName: string, hostname: string, accessLevel: AccessLevel): void {
|
||||
const levels = JSON.parse(localStorage.getItem(AccountService.levelsStorageKey) || '{}');
|
||||
|
||||
levels[hostname] ||= {};
|
||||
levels[hostname][publicKey] = accessLevel;
|
||||
levels[hostname][accountName] = accessLevel;
|
||||
|
||||
localStorage.setItem(AccountService.levelsStorageKey, JSON.stringify(levels));
|
||||
}
|
||||
|
||||
// Private Getters and Modifiers
|
||||
// log out of hostname entirely by setting accesslevel to None
|
||||
resetAccessLevels(hostname: string): void {
|
||||
const levels = JSON.parse(localStorage.getItem(AccountService.levelsStorageKey) || '{}');
|
||||
|
||||
// TEMP: public for import flow
|
||||
public addPrivateUser(userInfo: PrivateUserInfo): string {
|
||||
const privateUsers = this.getPrivateUsersRaw();
|
||||
const privateKey = this.cryptoService.seedHexToPrivateKey(userInfo.seedHex);
|
||||
const publicKey = this.cryptoService.privateKeyToDeSoPublicKey(privateKey, userInfo.network);
|
||||
|
||||
privateUsers[publicKey] = userInfo;
|
||||
|
||||
this.setPrivateUsersRaw(privateUsers);
|
||||
|
||||
return publicKey;
|
||||
levels[hostname] ||= {};
|
||||
for (const accountName in levels[hostname]) {
|
||||
levels[hostname][accountName] = AccessLevel.None;
|
||||
}
|
||||
|
||||
private getPrivateUsers(): {[key: string]: PrivateUserInfo} {
|
||||
const privateUsers = this.getPrivateUsersRaw();
|
||||
const filteredPrivateUsers: {[key: string]: PrivateUserInfo} = {};
|
||||
|
||||
for (const publicKey of Object.keys(privateUsers)) {
|
||||
const privateUser = privateUsers[publicKey];
|
||||
|
||||
// Only include users from the current network
|
||||
if (privateUser.network !== this.globalVars.network) {
|
||||
continue;
|
||||
localStorage.setItem(AccountService.levelsStorageKey, JSON.stringify(levels));
|
||||
}
|
||||
|
||||
// Get rid of some users who have invalid public keys
|
||||
if (!publicKey.match(AccountService.publicKeyRegex)) {
|
||||
this.deleteUser(publicKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
filteredPrivateUsers[publicKey] = privateUser;
|
||||
}
|
||||
|
||||
return filteredPrivateUsers;
|
||||
}
|
||||
|
||||
private getPrivateUsersRaw(): {[key: string]: PrivateUserInfo} {
|
||||
return JSON.parse(localStorage.getItem(AccountService.usersStorageKey) || '{}');
|
||||
}
|
||||
|
||||
private setPrivateUsersRaw(privateUsers: {[key: string]: PrivateUserInfo}): void {
|
||||
localStorage.setItem(AccountService.usersStorageKey, JSON.stringify(privateUsers));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ export class AppComponent implements OnInit {
|
|||
}
|
||||
|
||||
if (params.get('testnet')) {
|
||||
this.globalVars.network = Network.testnet;
|
||||
this.globalVars.network = Network.TestNet;
|
||||
}
|
||||
|
||||
// Callback should only be used in mobile applications, where payload is passed through URL parameters.
|
||||
|
|
|
@ -84,7 +84,7 @@ export class ApproveComponent implements OnInit {
|
|||
|
||||
finishFlow(signedTransactionHex?: string): void {
|
||||
this.identityService.login({
|
||||
users: this.accountService.getEncryptedUsers(),
|
||||
accounts: this.accountService.getPublicAccounts(),
|
||||
signedTransactionHex,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import {environment} from '../environments/environment';
|
|||
providedIn: 'root'
|
||||
})
|
||||
export class GlobalVarsService {
|
||||
network = Network.mainnet;
|
||||
network : Network = Network.MainNet;
|
||||
hostname = '';
|
||||
accessLevelRequest = AccessLevel.ApproveAll;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {Injectable} from '@angular/core';
|
||||
import {Observable, Subject} from 'rxjs';
|
||||
import {v4 as uuid} from 'uuid';
|
||||
import {AccessLevel, PublicUserInfo} from '../types/identity';
|
||||
import {AccessLevel, PublicAccountInfo} from '../types/identity';
|
||||
import {CryptoService} from './crypto.service';
|
||||
import {GlobalVarsService} from './global-vars.service';
|
||||
import {CookieService} from 'ngx-cookie';
|
||||
|
@ -65,8 +65,8 @@ export class IdentityService {
|
|||
}
|
||||
|
||||
login(payload: {
|
||||
users: {[key: string]: PublicUserInfo},
|
||||
publicKeyAdded?: string,
|
||||
accounts: {[key: string]: PublicAccountInfo}, // Channel ids
|
||||
accountNameAdded?: string, // Which channel id was just authorized. the app may allow the user to switch between them, but this determines which is on now.
|
||||
signedUp?: boolean
|
||||
signedTransactionHex?: string,
|
||||
addresses?: string[],
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {CryptoService} from '../crypto.service';
|
||||
import {IdentityService} from '../identity.service';
|
||||
import {AccountService} from '../account.service';
|
||||
import {AccessLevel} from '../../types/identity';
|
||||
import {GlobalVarsService} from '../global-vars.service';
|
||||
|
||||
@Component({
|
||||
|
@ -20,7 +18,6 @@ export class LogoutComponent implements OnInit {
|
|||
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private cryptoService: CryptoService,
|
||||
private identityService: IdentityService,
|
||||
private accountService: AccountService,
|
||||
|
@ -28,9 +25,6 @@ export class LogoutComponent implements OnInit {
|
|||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.activatedRoute.queryParams.subscribe(params => {
|
||||
this.publicKey = params.publicKey || '';
|
||||
});
|
||||
}
|
||||
|
||||
onCancel(): void {
|
||||
|
@ -39,7 +33,7 @@ export class LogoutComponent implements OnInit {
|
|||
|
||||
onSubmit(): void {
|
||||
// We set the accessLevel for the logged out user to None.
|
||||
this.accountService.setAccessLevel(this.publicKey, this.globalVars.hostname, AccessLevel.None);
|
||||
this.accountService.resetAccessLevels(this.globalVars.hostname);
|
||||
// We reset the seed encryption key so that all existing accounts, except
|
||||
// the logged out user, will regenerate their encryptedSeedHex. Without this,
|
||||
// someone could have reused the encryptedSeedHex of an already logged out user.
|
||||
|
@ -49,7 +43,7 @@ export class LogoutComponent implements OnInit {
|
|||
|
||||
finishFlow(): void {
|
||||
this.identityService.login({
|
||||
users: this.accountService.getEncryptedUsers(),
|
||||
accounts: this.accountService.getPublicAccounts(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ export class TestLbryLogInComponent implements OnInit {
|
|||
|
||||
finishFlow(addresses: string[]): void {
|
||||
this.identityService.login({
|
||||
users: {}, // TODO sigh
|
||||
accounts: this.accountService.getPublicAccounts(),
|
||||
addresses,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import {AccountService} from '../account.service';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {SigningService} from '../signing.service';
|
||||
import {IdentityService} from '../identity.service';
|
||||
|
@ -14,6 +15,7 @@ export class TestSignTransactionComponent implements OnInit {
|
|||
signedTransactionHex?: string
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private signingService: SigningService,
|
||||
private identityService: IdentityService,
|
||||
private cryptoService: CryptoService,
|
||||
|
@ -39,7 +41,7 @@ export class TestSignTransactionComponent implements OnInit {
|
|||
|
||||
finishFlow(signedTransactionHex?: string): void {
|
||||
this.identityService.login({
|
||||
users: {}, // TODO sigh
|
||||
accounts: this.accountService.getPublicAccounts(),
|
||||
signedTransactionHex,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import {AccountService} from '../account.service';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {SigningService} from '../signing.service';
|
||||
import {IdentityService} from '../identity.service';
|
||||
|
@ -18,6 +19,7 @@ export class TestSignComponent implements OnInit {
|
|||
privateKeyString: string = "thhUUVXQtyxonMaezCKwihLw9tZUrGJgWBMxDNfxoWub8dLGA"
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private signingService: SigningService,
|
||||
private identityService: IdentityService,
|
||||
) { }
|
||||
|
@ -34,7 +36,7 @@ export class TestSignComponent implements OnInit {
|
|||
|
||||
finishFlow(signatureHex?: string): void {
|
||||
this.identityService.login({
|
||||
users: {}, // TODO sigh
|
||||
accounts: this.accountService.getPublicAccounts(),
|
||||
signatureHex,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,21 +1,47 @@
|
|||
export interface PrivateUserInfo {
|
||||
seedHex: string;
|
||||
mnemonic: string;
|
||||
extraText: string;
|
||||
network: Network;
|
||||
}
|
||||
// TODO: Use our wallet json-schema. Otherwise, bitcoinjs-lib probably already has a networks enum.
|
||||
// TODO: what about encrypted wallets?
|
||||
|
||||
export interface PublicUserInfo {
|
||||
hasExtraText: boolean;
|
||||
encryptedSeedHex: string;
|
||||
network: Network;
|
||||
accessLevel: AccessLevel;
|
||||
accessLevelHmac: string;
|
||||
export enum AddressType {
|
||||
DeterministicChain = "deterministic-chain",
|
||||
SingleAddress = "single-address",
|
||||
}
|
||||
|
||||
export enum Network {
|
||||
mainnet = 'mainnet',
|
||||
testnet = 'testnet',
|
||||
MainNet = "lbc_mainnet",
|
||||
TestNet = "lbc_testnet",
|
||||
RegTest = "lbc_regtest",
|
||||
}
|
||||
|
||||
// Only safe for web wallet, not sent to the app
|
||||
export interface PrivateAccountInfo {
|
||||
address_generator: {
|
||||
change?: {
|
||||
gap: number,
|
||||
maximum_uses_per_address: number,
|
||||
},
|
||||
name: AddressType,
|
||||
receiving?: {
|
||||
gap: number,
|
||||
maximum_uses_per_address: number,
|
||||
}
|
||||
},
|
||||
certificates: {[key: string]: string},
|
||||
encrypted: boolean,
|
||||
ledger: Network,
|
||||
modified_on: number,
|
||||
name: string,
|
||||
private_key: string,
|
||||
public_key: string,
|
||||
seed: string,
|
||||
}
|
||||
|
||||
// can be sent to the app
|
||||
export interface PublicAccountInfo {
|
||||
// TODO - add more useful stuff
|
||||
name: string;
|
||||
network: Network;
|
||||
accessLevel: AccessLevel;
|
||||
accessLevelHmac: string;
|
||||
}
|
||||
|
||||
export enum AccessLevel {
|
||||
|
|
Loading…
Reference in a new issue