Bug fixes.
- Change environment variables. - Refactor http getter. - Add custom error state matcher. - Hide create account component.
This commit is contained in:
parent
8dc0eb24d8
commit
0be8bcd51a
2379
package-lock.json
generated
2379
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
7
src/app/_helpers/custom-error-state-matcher.spec.ts
Normal file
7
src/app/_helpers/custom-error-state-matcher.spec.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { CustomErrorStateMatcher } from './custom-error-state-matcher';
|
||||||
|
|
||||||
|
describe('CustomErrorStateMatcher', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new CustomErrorStateMatcher()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
9
src/app/_helpers/custom-error-state-matcher.ts
Normal file
9
src/app/_helpers/custom-error-state-matcher.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import {ErrorStateMatcher} from '@angular/material/core';
|
||||||
|
import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
|
||||||
|
|
||||||
|
export class CustomErrorStateMatcher implements ErrorStateMatcher{
|
||||||
|
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||||
|
const isSubmitted = form && form.submitted;
|
||||||
|
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +1,19 @@
|
|||||||
// function httpGetter(): any {
|
|
||||||
// }
|
|
||||||
//
|
class HttpGetter {
|
||||||
// httpGetter().prototype.get = (filename: string) => new Promise((whohoo, doh) => {
|
async get(filename: string): Promise<any> {
|
||||||
// const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
// xhr.addEventListener('load', (e) => {
|
xhr.addEventListener('load', (e) => {
|
||||||
// if (xhr.status === 200) {
|
if (xhr.status === 200) {
|
||||||
// whohoo(xhr.responseText);
|
console.log(xhr.responseText);
|
||||||
// return;
|
return;
|
||||||
// }
|
}
|
||||||
// doh('failed with status ' + xhr.status + ': ' + xhr.statusText);
|
console.log('failed with status ' + xhr.status + ': ' + xhr.statusText);
|
||||||
// });
|
});
|
||||||
// xhr.open('GET', filename);
|
xhr.open('GET', filename);
|
||||||
// xhr.send();
|
xhr.send();
|
||||||
// });
|
}
|
||||||
//
|
}
|
||||||
// class HttpGetter {
|
export {
|
||||||
// async get(filename: string): Promise<any> {
|
HttpGetter
|
||||||
// const xhr = new XMLHttpRequest();
|
};
|
||||||
// xhr.addEventListener('load', (e) => {
|
|
||||||
// if (xhr.status === 200) {
|
|
||||||
// // whohoo(xhr.responseText);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// // doh('failed with status ' + xhr.status + ': ' + xhr.statusText);
|
|
||||||
// });
|
|
||||||
// xhr.open('GET', filename);
|
|
||||||
// xhr.send();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// export {
|
|
||||||
// httpGetter,
|
|
||||||
// HttpGetter
|
|
||||||
// };
|
|
||||||
|
@ -2,3 +2,4 @@ export * from './mock-backend';
|
|||||||
export * from './array-sum';
|
export * from './array-sum';
|
||||||
export * from './accountIndex';
|
export * from './accountIndex';
|
||||||
export * from './custom-error-state-matcher';
|
export * from './custom-error-state-matcher';
|
||||||
|
export * from './http-getter';
|
||||||
|
@ -3,6 +3,7 @@ import {BehaviorSubject, Observable} from 'rxjs';
|
|||||||
import {HttpClient, HttpParams} from '@angular/common/http';
|
import {HttpClient, HttpParams} from '@angular/common/http';
|
||||||
import {environment} from '../../environments/environment';
|
import {environment} from '../../environments/environment';
|
||||||
import {first, map} from 'rxjs/operators';
|
import {first, map} from 'rxjs/operators';
|
||||||
|
import {} from '../../assets/js/cic-meta/sync';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -108,4 +109,8 @@ export class UserService {
|
|||||||
return response;
|
return response;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeUserCredentials(): void {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export class AppComponent implements OnInit {
|
|||||||
private transactionService: TransactionService,
|
private transactionService: TransactionService,
|
||||||
private blockSyncService: BlockSyncService
|
private blockSyncService: BlockSyncService
|
||||||
) {
|
) {
|
||||||
// this.blockSyncService.blockSync();
|
this.blockSyncService.blockSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
@ -8,7 +8,7 @@ import {AccountDetailsComponent} from './account-details/account-details.compone
|
|||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: '', component: AccountsComponent },
|
{ path: '', component: AccountsComponent },
|
||||||
{ path: 'create', component: CreateAccountComponent },
|
// { path: 'create', component: CreateAccountComponent },
|
||||||
{ path: 'export', component: ExportAccountsComponent },
|
{ path: 'export', component: ExportAccountsComponent },
|
||||||
{ path: ':id', component: AccountDetailsComponent },
|
{ path: ':id', component: AccountDetailsComponent },
|
||||||
{ path: '**', redirectTo: '', pathMatch: 'full' }
|
{ path: '**', redirectTo: '', pathMatch: 'full' }
|
||||||
|
@ -33,10 +33,10 @@
|
|||||||
<mat-option value="group">GROUPACCOUNT</mat-option>
|
<mat-option value="group">GROUPACCOUNT</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button mat-raised-button color="primary" routerLink="/accounts/create" type="button" class="btn btn-outline-primary ml-auto">
|
<!-- <button mat-raised-button color="primary" routerLink="/accounts/create" type="button" class="btn btn-outline-primary ml-auto">-->
|
||||||
<mat-icon>add</mat-icon>
|
<!-- <mat-icon>add</mat-icon>-->
|
||||||
ADD NEW </button>
|
<!-- ADD NEW </button>-->
|
||||||
<button mat-raised-button color="primary" routerLink="/accounts/export" type="button" class="btn btn-outline-primary ml-2"> EXPORT </button>
|
<button mat-raised-button color="primary" routerLink="/accounts/export" type="button" class="btn btn-outline-primary ml-auto"> EXPORT </button>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="selection.selected.length" class="row card-header">
|
<div *ngIf="selection.selected.length" class="row card-header">
|
||||||
<p><strong> {{selection.selected.length}} selected </strong></p>
|
<p><strong> {{selection.selected.length}} selected </strong></p>
|
||||||
|
38
src/assets/js/cic-meta/sync.d.ts
vendored
Normal file
38
src/assets/js/cic-meta/sync.d.ts
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { JSONSerializable } from './format';
|
||||||
|
import { Authoritative, Signer } from './auth';
|
||||||
|
import { VersionedSpec } from './constants';
|
||||||
|
declare class Envelope {
|
||||||
|
o: VersionedSpec;
|
||||||
|
constructor(payload: Object);
|
||||||
|
set(payload: Object): void;
|
||||||
|
get(): string;
|
||||||
|
toJSON(): string;
|
||||||
|
static fromJSON(s: string): Envelope;
|
||||||
|
unwrap(): Syncable;
|
||||||
|
}
|
||||||
|
declare class ArgPair {
|
||||||
|
k: string;
|
||||||
|
v: any;
|
||||||
|
constructor(k: string, v: any);
|
||||||
|
}
|
||||||
|
declare class Syncable implements JSONSerializable, Authoritative {
|
||||||
|
id: string;
|
||||||
|
timestamp: number;
|
||||||
|
m: any;
|
||||||
|
e: Envelope;
|
||||||
|
signer: Signer;
|
||||||
|
onwrap: (string: any) => void;
|
||||||
|
onauthenticate: (boolean: any) => void;
|
||||||
|
constructor(id: string, v: Object);
|
||||||
|
setSigner(signer: Signer): void;
|
||||||
|
digest(): string;
|
||||||
|
private wrap;
|
||||||
|
authenticate(full?: boolean): void;
|
||||||
|
sign(): void;
|
||||||
|
update(changes: Array<ArgPair>, changesDescription: string): void;
|
||||||
|
replace(o: Object, changesDescription: string): void;
|
||||||
|
merge(s: Syncable): void;
|
||||||
|
toJSON(): string;
|
||||||
|
static fromJSON(s: string): Syncable;
|
||||||
|
}
|
||||||
|
export { JSONSerializable, Syncable, ArgPair, Envelope };
|
246
src/assets/js/cic-meta/sync.js
Normal file
246
src/assets/js/cic-meta/sync.js
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
"use strict";
|
||||||
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||||
|
}) : (function(o, m, k, k2) {
|
||||||
|
if (k2 === undefined) k2 = k;
|
||||||
|
o[k2] = m[k];
|
||||||
|
}));
|
||||||
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||||
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||||
|
}) : function(o, v) {
|
||||||
|
o["default"] = v;
|
||||||
|
});
|
||||||
|
var __importStar = (this && this.__importStar) || function (mod) {
|
||||||
|
if (mod && mod.__esModule) return mod;
|
||||||
|
var result = {};
|
||||||
|
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||||
|
__setModuleDefault(result, mod);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.Envelope = exports.ArgPair = exports.Syncable = void 0;
|
||||||
|
var Automerge = __importStar(require("automerge"));
|
||||||
|
var constants_1 = require("./constants");
|
||||||
|
var fullSpec = {
|
||||||
|
name: 'cic',
|
||||||
|
version: '1',
|
||||||
|
ext: {
|
||||||
|
network: constants_1.cryptoSpec,
|
||||||
|
engine: constants_1.engineSpec,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
var Envelope = /** @class */ (function () {
|
||||||
|
function Envelope(payload) {
|
||||||
|
this.o = fullSpec;
|
||||||
|
this.set(payload);
|
||||||
|
}
|
||||||
|
Envelope.prototype.set = function (payload) {
|
||||||
|
this.o['payload'] = payload;
|
||||||
|
};
|
||||||
|
Envelope.prototype.get = function () {
|
||||||
|
return this.o['payload'];
|
||||||
|
};
|
||||||
|
Envelope.prototype.toJSON = function () {
|
||||||
|
return JSON.stringify(this.o);
|
||||||
|
};
|
||||||
|
Envelope.fromJSON = function (s) {
|
||||||
|
var e = new Envelope(undefined);
|
||||||
|
e.o = JSON.parse(s);
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
Envelope.prototype.unwrap = function () {
|
||||||
|
return Syncable.fromJSON(this.o['payload']);
|
||||||
|
};
|
||||||
|
return Envelope;
|
||||||
|
}());
|
||||||
|
exports.Envelope = Envelope;
|
||||||
|
var ArgPair = /** @class */ (function () {
|
||||||
|
function ArgPair(k, v) {
|
||||||
|
this.k = k;
|
||||||
|
this.v = v;
|
||||||
|
}
|
||||||
|
return ArgPair;
|
||||||
|
}());
|
||||||
|
exports.ArgPair = ArgPair;
|
||||||
|
var SignablePart = /** @class */ (function () {
|
||||||
|
function SignablePart(s) {
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
SignablePart.prototype.digest = function () {
|
||||||
|
return this.s;
|
||||||
|
};
|
||||||
|
return SignablePart;
|
||||||
|
}());
|
||||||
|
function orderDict(src) {
|
||||||
|
var dst;
|
||||||
|
if (Array.isArray(src)) {
|
||||||
|
dst = [];
|
||||||
|
src.forEach(function (v) {
|
||||||
|
if (typeof (v) == 'object') {
|
||||||
|
v = orderDict(v);
|
||||||
|
}
|
||||||
|
dst.push(v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dst = {};
|
||||||
|
Object.keys(src).sort().forEach(function (k) {
|
||||||
|
var v = src[k];
|
||||||
|
if (typeof (v) == 'object') {
|
||||||
|
v = orderDict(v);
|
||||||
|
}
|
||||||
|
dst[k] = v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
var Syncable = /** @class */ (function () {
|
||||||
|
// TODO: Move data to sub-object so timestamp, id, signature don't collide
|
||||||
|
function Syncable(id, v) {
|
||||||
|
this.id = id;
|
||||||
|
var o = {
|
||||||
|
'id': id,
|
||||||
|
'timestamp': Math.floor(Date.now() / 1000),
|
||||||
|
'data': v,
|
||||||
|
};
|
||||||
|
//this.m = Automerge.from(v)
|
||||||
|
this.m = Automerge.from(o);
|
||||||
|
}
|
||||||
|
Syncable.prototype.setSigner = function (signer) {
|
||||||
|
var _this = this;
|
||||||
|
this.signer = signer;
|
||||||
|
this.signer.onsign = function (s) {
|
||||||
|
_this.wrap(s);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// TODO: To keep integrity, the non-link key/value pairs for each step also need to be hashed
|
||||||
|
Syncable.prototype.digest = function () {
|
||||||
|
var links = [];
|
||||||
|
Automerge.getAllChanges(this.m).forEach(function (ch) {
|
||||||
|
var op = ch['ops'];
|
||||||
|
ch['ops'].forEach(function (op) {
|
||||||
|
if (op['action'] == 'link') {
|
||||||
|
//console.log('op link', op);
|
||||||
|
links.push([op['obj'], op['value']]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
//return JSON.stringify(links);
|
||||||
|
var j = JSON.stringify(links);
|
||||||
|
return Buffer.from(j).toString('base64');
|
||||||
|
};
|
||||||
|
Syncable.prototype.wrap = function (s) {
|
||||||
|
this.m = Automerge.change(this.m, 'sign', function (doc) {
|
||||||
|
doc['signature'] = s;
|
||||||
|
});
|
||||||
|
this.e = new Envelope(this.toJSON());
|
||||||
|
if (this.onwrap !== undefined) {
|
||||||
|
this.onwrap(this.e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// private _verifyLoop(i:number, history:Array<any>, signable:Signable, result:boolean) {
|
||||||
|
// if (!result) {
|
||||||
|
// this.onauthenticate(false);
|
||||||
|
// return;
|
||||||
|
// } else if (history.length == 0) {
|
||||||
|
// this.onauthenticate(true);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// const h = history.shift()
|
||||||
|
// if (i % 2 == 0) {
|
||||||
|
// i++;
|
||||||
|
// signable = {
|
||||||
|
// digest: () => {
|
||||||
|
// return Automerge.save(h.snapshot)
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// this._verifyLoop(i, history, signable, true);
|
||||||
|
// } else {
|
||||||
|
// i++;
|
||||||
|
// const signature = h.snapshot['signature'];
|
||||||
|
// console.debug('signature', signature, signable.digest());
|
||||||
|
// this.signer.onverify = (v) => {
|
||||||
|
// this._verifyLoop(i, history, signable, v)
|
||||||
|
// }
|
||||||
|
// this.signer.verify(signable, signature);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO: This should replay the graph and check signatures on each step
|
||||||
|
// public _authenticate(full:boolean=false) {
|
||||||
|
// let h = Automerge.getHistory(this.m);
|
||||||
|
// h.forEach((m) => {
|
||||||
|
// //console.debug(m.snapshot);
|
||||||
|
// });
|
||||||
|
// const signable = {
|
||||||
|
// digest: () => { return '' },
|
||||||
|
// }
|
||||||
|
// if (!full) {
|
||||||
|
// h = h.slice(h.length-2);
|
||||||
|
// }
|
||||||
|
// this._verifyLoop(0, h, signable, true);
|
||||||
|
// }
|
||||||
|
Syncable.prototype.authenticate = function (full) {
|
||||||
|
var _this = this;
|
||||||
|
if (full === void 0) { full = false; }
|
||||||
|
if (full) {
|
||||||
|
console.warn('only doing shallow authentication for now, sorry');
|
||||||
|
}
|
||||||
|
//console.log('authenticating', signable.digest());
|
||||||
|
//console.log('signature', this.m.signature);
|
||||||
|
this.signer.onverify = function (v) {
|
||||||
|
//this._verifyLoop(i, history, signable, v)
|
||||||
|
_this.onauthenticate(v);
|
||||||
|
};
|
||||||
|
this.signer.verify(this.m.signature.digest, this.m.signature);
|
||||||
|
};
|
||||||
|
Syncable.prototype.sign = function () {
|
||||||
|
//this.signer.prepare(this);
|
||||||
|
this.signer.sign(this.digest());
|
||||||
|
};
|
||||||
|
Syncable.prototype.update = function (changes, changesDescription) {
|
||||||
|
this.m = Automerge.change(this.m, changesDescription, function (m) {
|
||||||
|
changes.forEach(function (c) {
|
||||||
|
var path = c.k.split('.');
|
||||||
|
var target = m['data'];
|
||||||
|
while (path.length > 1) {
|
||||||
|
var part = path.shift();
|
||||||
|
target = target[part];
|
||||||
|
}
|
||||||
|
target[path[0]] = c.v;
|
||||||
|
});
|
||||||
|
m['timestamp'] = Math.floor(Date.now() / 1000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Syncable.prototype.replace = function (o, changesDescription) {
|
||||||
|
this.m = Automerge.change(this.m, changesDescription, function (m) {
|
||||||
|
Object.keys(o).forEach(function (k) {
|
||||||
|
m['data'][k] = o[k];
|
||||||
|
});
|
||||||
|
Object.keys(m).forEach(function (k) {
|
||||||
|
if (o[k] == undefined) {
|
||||||
|
delete m['data'][k];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m['timestamp'] = Math.floor(Date.now() / 1000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Syncable.prototype.merge = function (s) {
|
||||||
|
this.m = Automerge.merge(s.m, this.m);
|
||||||
|
};
|
||||||
|
Syncable.prototype.toJSON = function () {
|
||||||
|
var s = Automerge.save(this.m);
|
||||||
|
var o = JSON.parse(s);
|
||||||
|
var oo = orderDict(o);
|
||||||
|
return JSON.stringify(oo);
|
||||||
|
};
|
||||||
|
Syncable.fromJSON = function (s) {
|
||||||
|
var doc = Automerge.load(s);
|
||||||
|
var y = new Syncable(doc['id'], {});
|
||||||
|
y.m = doc;
|
||||||
|
return y;
|
||||||
|
};
|
||||||
|
return Syncable;
|
||||||
|
}());
|
||||||
|
exports.Syncable = Syncable;
|
264
src/assets/js/cic-meta/sync.ts
Normal file
264
src/assets/js/cic-meta/sync.ts
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
import * as Automerge from 'automerge';
|
||||||
|
|
||||||
|
import { JSONSerializable } from './format';
|
||||||
|
|
||||||
|
import { Authoritative, Signer, PGPSigner, Signable, Signature } from './auth';
|
||||||
|
|
||||||
|
import { engineSpec, cryptoSpec, networkSpec, VersionedSpec } from './constants';
|
||||||
|
|
||||||
|
const fullSpec: VersionedSpec = {
|
||||||
|
name: 'cic',
|
||||||
|
version: '1',
|
||||||
|
ext: {
|
||||||
|
network: cryptoSpec,
|
||||||
|
engine: engineSpec,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
class Envelope {
|
||||||
|
|
||||||
|
o = fullSpec;
|
||||||
|
|
||||||
|
constructor(payload: Object) {
|
||||||
|
this.set(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set(payload: Object) {
|
||||||
|
this.o['payload'] = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(): string {
|
||||||
|
return this.o['payload'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON() {
|
||||||
|
return JSON.stringify(this.o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromJSON(s: string): Envelope {
|
||||||
|
const e = new Envelope(undefined);
|
||||||
|
e.o = JSON.parse(s);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unwrap(): Syncable {
|
||||||
|
return Syncable.fromJSON(this.o['payload']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArgPair {
|
||||||
|
|
||||||
|
k: string;
|
||||||
|
v: any;
|
||||||
|
|
||||||
|
constructor(k: string, v: any) {
|
||||||
|
this.k = k;
|
||||||
|
this.v = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignablePart implements Signable {
|
||||||
|
|
||||||
|
s: string;
|
||||||
|
|
||||||
|
constructor(s: string) {
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public digest(): string {
|
||||||
|
return this.s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function orderDict(src) {
|
||||||
|
let dst;
|
||||||
|
if (Array.isArray(src)) {
|
||||||
|
dst = [];
|
||||||
|
src.forEach((v) => {
|
||||||
|
if (typeof (v) == 'object') {
|
||||||
|
v = orderDict(v);
|
||||||
|
}
|
||||||
|
dst.push(v);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
dst = {};
|
||||||
|
Object.keys(src).sort().forEach((k) => {
|
||||||
|
let v = src[k];
|
||||||
|
if (typeof (v) == 'object') {
|
||||||
|
v = orderDict(v);
|
||||||
|
}
|
||||||
|
dst[k] = v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Syncable implements JSONSerializable, Authoritative {
|
||||||
|
|
||||||
|
// TODO: Move data to sub-object so timestamp, id, signature don't collide
|
||||||
|
constructor(id: string, v: Object) {
|
||||||
|
this.id = id;
|
||||||
|
const o = {
|
||||||
|
id: id,
|
||||||
|
timestamp: Math.floor(Date.now() / 1000),
|
||||||
|
data: v,
|
||||||
|
};
|
||||||
|
// this.m = Automerge.from(v)
|
||||||
|
this.m = Automerge.from(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
id: string;
|
||||||
|
timestamp: number;
|
||||||
|
m: any; // automerge object
|
||||||
|
e: Envelope;
|
||||||
|
signer: Signer;
|
||||||
|
onwrap: (string) => void;
|
||||||
|
onauthenticate: (boolean) => void;
|
||||||
|
|
||||||
|
public static fromJSON(s: string): Syncable {
|
||||||
|
const doc = Automerge.load(s);
|
||||||
|
let y = new Syncable(doc['id'], {});
|
||||||
|
y.m = doc;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSigner(signer: Signer) {
|
||||||
|
this.signer = signer;
|
||||||
|
this.signer.onsign = (s) => {
|
||||||
|
this.wrap(s);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: To keep integrity, the non-link key/value pairs for each step also need to be hashed
|
||||||
|
public digest(): string {
|
||||||
|
const links = [];
|
||||||
|
Automerge.getAllChanges(this.m).forEach((ch: Object) => {
|
||||||
|
const op: Array<any> = ch['ops'];
|
||||||
|
ch['ops'].forEach((op: Array<Object>) => {
|
||||||
|
if (op['action'] == 'link') {
|
||||||
|
// console.log('op link', op);
|
||||||
|
links.push([op['obj'], op['value']]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
// return JSON.stringify(links);
|
||||||
|
const j = JSON.stringify(links);
|
||||||
|
return Buffer.from(j).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
|
private wrap(s: string) {
|
||||||
|
this.m = Automerge.change(this.m, 'sign', (doc) => {
|
||||||
|
doc['signature'] = s;
|
||||||
|
});
|
||||||
|
this.e = new Envelope(this.toJSON());
|
||||||
|
if (this.onwrap !== undefined) {
|
||||||
|
this.onwrap(this.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// private _verifyLoop(i:number, history:Array<any>, signable:Signable, result:boolean) {
|
||||||
|
// if (!result) {
|
||||||
|
// this.onauthenticate(false);
|
||||||
|
// return;
|
||||||
|
// } else if (history.length == 0) {
|
||||||
|
// this.onauthenticate(true);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// const h = history.shift()
|
||||||
|
// if (i % 2 == 0) {
|
||||||
|
// i++;
|
||||||
|
// signable = {
|
||||||
|
// digest: () => {
|
||||||
|
// return Automerge.save(h.snapshot)
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// this._verifyLoop(i, history, signable, true);
|
||||||
|
// } else {
|
||||||
|
// i++;
|
||||||
|
// const signature = h.snapshot['signature'];
|
||||||
|
// console.debug('signature', signature, signable.digest());
|
||||||
|
// this.signer.onverify = (v) => {
|
||||||
|
// this._verifyLoop(i, history, signable, v)
|
||||||
|
// }
|
||||||
|
// this.signer.verify(signable, signature);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO: This should replay the graph and check signatures on each step
|
||||||
|
// public _authenticate(full:boolean=false) {
|
||||||
|
// let h = Automerge.getHistory(this.m);
|
||||||
|
// h.forEach((m) => {
|
||||||
|
// //console.debug(m.snapshot);
|
||||||
|
// });
|
||||||
|
// const signable = {
|
||||||
|
// digest: () => { return '' },
|
||||||
|
// }
|
||||||
|
// if (!full) {
|
||||||
|
// h = h.slice(h.length-2);
|
||||||
|
// }
|
||||||
|
// this._verifyLoop(0, h, signable, true);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public authenticate(full: boolean = false) {
|
||||||
|
if (full) {
|
||||||
|
console.warn('only doing shallow authentication for now, sorry');
|
||||||
|
}
|
||||||
|
// console.log('authenticating', signable.digest());
|
||||||
|
// console.log('signature', this.m.signature);
|
||||||
|
this.signer.onverify = (v) => {
|
||||||
|
// this._verifyLoop(i, history, signable, v)
|
||||||
|
this.onauthenticate(v);
|
||||||
|
};
|
||||||
|
this.signer.verify(this.m.signature.digest, this.m.signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public sign() {
|
||||||
|
// this.signer.prepare(this);
|
||||||
|
this.signer.sign(this.digest());
|
||||||
|
}
|
||||||
|
|
||||||
|
public update(changes: Array<ArgPair>, changesDescription: string) {
|
||||||
|
this.m = Automerge.change(this.m, changesDescription, (m) => {
|
||||||
|
changes.forEach((c) => {
|
||||||
|
let path = c.k.split('.');
|
||||||
|
let target = m['data'];
|
||||||
|
while (path.length > 1) {
|
||||||
|
const part = path.shift();
|
||||||
|
target = target[part];
|
||||||
|
}
|
||||||
|
target[path[0]] = c.v;
|
||||||
|
});
|
||||||
|
m['timestamp'] = Math.floor(Date.now() / 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public replace(o: Object, changesDescription: string) {
|
||||||
|
this.m = Automerge.change(this.m, changesDescription, (m) => {
|
||||||
|
Object.keys(o).forEach((k) => {
|
||||||
|
m['data'][k] = o[k];
|
||||||
|
});
|
||||||
|
Object.keys(m).forEach((k) => {
|
||||||
|
if (o[k] == undefined) {
|
||||||
|
delete m['data'][k];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m['timestamp'] = Math.floor(Date.now() / 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public merge(s: Syncable) {
|
||||||
|
this.m = Automerge.merge(s.m, this.m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toJSON(): string {
|
||||||
|
const s = Automerge.save(this.m);
|
||||||
|
const o = JSON.parse(s);
|
||||||
|
const oo = orderDict(o);
|
||||||
|
return JSON.stringify(oo);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { JSONSerializable, Syncable, ArgPair, Envelope };
|
@ -1,8 +1,8 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: true,
|
production: true,
|
||||||
cicCacheUrl: 'http://localhost:5555',
|
cicCacheUrl: 'http://localhost:63313',
|
||||||
cicScriptsUrl: 'http://localhost:9999',
|
cicScriptsUrl: 'http://localhost:9999',
|
||||||
web3Provider: 'http://localhost:63545',
|
web3Provider: 'ws://localhost:8546',
|
||||||
cicUssdUrl: 'http://localhost:63315',
|
cicUssdUrl: 'http://localhost:63315',
|
||||||
cicEthUrl: 'http://localhost:63314',
|
cicEthUrl: 'http://localhost:63314',
|
||||||
contractAddress: '0x35Ef60C4624Eaf6AeEBeBec9Ddd3CBA6b24C4b17',
|
contractAddress: '0x35Ef60C4624Eaf6AeEBeBec9Ddd3CBA6b24C4b17',
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
cicCacheUrl: 'http://localhost:5555',
|
cicCacheUrl: 'http://localhost:63313',
|
||||||
cicScriptsUrl: 'http://localhost:9999',
|
cicScriptsUrl: 'http://localhost:9999',
|
||||||
web3Provider: 'http://localhost:63545',
|
web3Provider: 'ws://localhost:8546',
|
||||||
cicUssdUrl: 'http://localhost:63315',
|
cicUssdUrl: 'http://localhost:63315',
|
||||||
cicEthUrl: 'http://localhost:63314',
|
cicEthUrl: 'http://localhost:63314',
|
||||||
contractAddress: '0x35Ef60C4624Eaf6AeEBeBec9Ddd3CBA6b24C4b17',
|
contractAddress: '0x35Ef60C4624Eaf6AeEBeBec9Ddd3CBA6b24C4b17',
|
||||||
|
Loading…
Reference in New Issue
Block a user