basic tests

This commit is contained in:
Pedro Gomes 2020-05-19 20:07:23 +02:00
parent 0e32a1f136
commit 28ad834b94
12 changed files with 202 additions and 119 deletions

View File

@ -60,7 +60,9 @@
"trailingComma": "es5"
},
"dependencies": {
"axios": "^0.19.2",
"basic-provider": "^1.1.0",
"eccrypto-js": "^5.2.0"
"eccrypto-js": "^5.2.0",
"localStorage": "^1.0.4"
}
}

View File

@ -27,6 +27,11 @@ class Waku implements IWakuClient {
this.signer = new WakuSigner(this.store);
}
public async init(): Promise<any> {
await this.signer.init();
return this.provider.init();
}
public async request(payload: JsonRpcRequest): Promise<any> {
if (isSignerMethod(payload.method)) {
return this.signer.request(payload);

View File

@ -1,3 +1,4 @@
import axios from "axios";
import { EventEmitter } from "events";
import { IRpcConnection } from "basic-provider";
@ -7,10 +8,7 @@ export class HttpConnection extends EventEmitter implements IRpcConnection {
constructor(url: string) {
super();
if (url) {
this.url = url;
this.connected = true;
}
this.url = url;
}
public async send(payload: any): Promise<any> {
@ -20,22 +18,26 @@ export class HttpConnection extends EventEmitter implements IRpcConnection {
if (!this.connected) {
throw new Error("HttpConnection is closed");
}
const response = await fetch(this.url, {
method: "post",
const { data } = await axios.post(this.url, payload, {
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
const result = await response.json();
return result;
if (data.error) {
throw new Error(data.error.message);
}
return data.result;
}
public async open(): Promise<void> {
this.connected = true;
this.emit("connect");
return;
}
public async close(): Promise<void> {
this.connected = false;
this.emit("close");
return;
}
}

View File

@ -13,7 +13,7 @@ export class WakuProvider extends BasicProvider implements IWakuProvider {
if (!this.connected) {
await this.open();
}
const result = await this.send(RPC_METHODS.NETWORK.waku_info);
const result = await this.send(RPC_METHODS.NETWORK.waku_info, []);
this.emit("enable");
return result;
} catch (err) {
@ -22,6 +22,10 @@ export class WakuProvider extends BasicProvider implements IWakuProvider {
}
}
public async init(): Promise<any> {
return this.enable();
}
public async request(payload: JsonRpcRequest): Promise<any> {
return this.send(payload.method, payload.params);
}

View File

@ -22,21 +22,26 @@ import { isKeyPair, isSymKey } from "../helpers/validators";
export class WakuSigner implements IWakuSigner {
private keyMap: KeyMap = {};
constructor(private store: IWakuStore) {
this.loadKeys();
}
constructor(private store: IWakuStore) {}
// -- public ----------------------------------------------- //
public async init(): Promise<any> {
return await this.loadKeys();
}
public async request(payload: JsonRpcRequest): Promise<any> {
const method = payload.method.replace(WAKU_PREFIX + "_", "");
return this[method](...payload.params);
}
public async newKeyPair(): Promise<string> {
await this.loadKeys();
const key = this.genKeyPair();
await this.addKey(key);
return key.id;
}
public async addPrivateKey(prvKey: string): Promise<string> {
await this.loadKeys();
let key = this.getMatchingKey("prvKey", prvKey);
if (!key) {
key = this.genKeyPair(prvKey);
@ -46,19 +51,16 @@ export class WakuSigner implements IWakuSigner {
}
public async deleteKeyPair(id: string): Promise<boolean> {
await this.loadKeys();
await this.removeKey(id);
return true;
}
public async hasKeyPair(id: string): Promise<boolean> {
await this.loadKeys();
let key = this.getMatchingKey("id", id);
return isKeyPair(key);
}
public async getPublicKey(id: string): Promise<string> {
await this.loadKeys();
let key = this.getMatchingKey("id", id);
if (!key) {
throw new Error(`No matching pubKey for id: ${id}`);
@ -67,7 +69,6 @@ export class WakuSigner implements IWakuSigner {
}
public async getPrivateKey(id: string): Promise<string> {
await this.loadKeys();
let key = this.getMatchingKey("id", id);
if (!key) {
throw new Error(`No matching privKey for id: ${id}`);
@ -76,14 +77,12 @@ export class WakuSigner implements IWakuSigner {
}
public async newSymKey(): Promise<string> {
await this.loadKeys();
const key = this.genSymKey();
await this.addKey(key);
return key.id;
}
public async addSymKey(symKey: string): Promise<string> {
await this.loadKeys();
let key = this.getMatchingKey("symKey", symKey);
if (!key) {
key = {
@ -96,7 +95,6 @@ export class WakuSigner implements IWakuSigner {
}
public async generateSymKeyFromPassword(): Promise<string> {
await this.loadKeys();
// TODO: needs to accept optional "password" argument
const key = this.genSymKey();
await this.addKey(key);
@ -104,13 +102,11 @@ export class WakuSigner implements IWakuSigner {
}
public async hasSymKey(id: string): Promise<boolean> {
await this.loadKeys();
let key = this.getMatchingKey("id", id);
return isSymKey(key);
}
public async getSymKey(id: string): Promise<string> {
await this.loadKeys();
let key = this.getMatchingKey("id", id);
if (!key) {
throw new Error(`No matching symKey for id: ${id}`);
@ -119,16 +115,10 @@ export class WakuSigner implements IWakuSigner {
}
public async deleteSymKey(id: string): Promise<boolean> {
await this.loadKeys();
await this.removeKey(id);
return true;
}
public async request(payload: JsonRpcRequest): Promise<any> {
const method = payload.method.replace(WAKU_PREFIX + "_", "");
return this[method](...payload.params);
}
// -- private ----------------------------------------------- //
private getMatchingKey(param: string, value: string): Key | undefined {
@ -160,21 +150,21 @@ export class WakuSigner implements IWakuSigner {
};
}
private async loadKeys() {
this.keyMap = await this.store.get(STORE_KEYS_ID);
private async loadKeys(): Promise<void> {
this.keyMap = (await this.store.get(STORE_KEYS_ID)) || {};
}
private async addKey(key: Key) {
private async addKey(key: Key): Promise<void> {
this.keyMap[key.id] = key;
await this.persistKeys();
}
private async removeKey(id: string) {
private async removeKey(id: string): Promise<void> {
delete this.keyMap[id];
await this.persistKeys();
}
private async persistKeys() {
private async persistKeys(): Promise<void> {
await this.store.set(STORE_KEYS_ID, this.keyMap);
}
}

View File

@ -1,28 +1,24 @@
import storage from "localStorage";
import { safeJsonParse, safeJsonStringify } from "./json";
import { getOrUndefined } from "./misc";
export function getLocalStorage(): Storage {
return getOrUndefined<Storage>("localStorage", window) || storage;
}
export function setLocal(key: string, data: any): void {
const raw = safeJsonStringify(data);
const local = getOrUndefined<Storage>("localStorage", window);
if (local) {
local.setItem(key, raw);
}
const local = getLocalStorage();
local.setItem(key, safeJsonStringify(data));
}
export function getLocal(key: string): any {
let data: any = null;
let raw: string | null = null;
const local = getOrUndefined<Storage>("localStorage", window);
if (local) {
raw = local.getItem(key);
}
data = safeJsonParse(raw);
const local = getLocalStorage();
const data = safeJsonParse(local.getItem(key));
return data;
}
export function removeLocal(key: string): void {
const local = getOrUndefined<Storage>("localStorage", window);
if (local) {
local.removeItem(key);
}
const local = getLocalStorage();
local.removeItem(key);
}

View File

@ -0,0 +1,39 @@
import BasicProvider from "basic-provider";
import { JsonRpcRequest } from "./rpc";
export interface IWakuController {
init(): Promise<any>;
request(payload: JsonRpcRequest): Promise<any>;
}
export interface IWakuProvider extends BasicProvider, IWakuController {
isWakuProvider: boolean;
enable(): Promise<any>;
}
export interface IWakuSigner extends IWakuController {
newKeyPair(): Promise<string>;
addPrivateKey(prvKey: string): Promise<string>;
deleteKeyPair(id: string): Promise<boolean>;
hasKeyPair(id: string): Promise<boolean>;
getPublicKey(id: string): Promise<string>;
getPrivateKey(id: string): Promise<string>;
newSymKey(): Promise<string>;
addSymKey(symKey: string): Promise<string>;
generateSymKeyFromPassword(): Promise<string>;
hasSymKey(id: string): Promise<boolean>;
getSymKey(id: string): Promise<string>;
deleteSymKey(id: string): Promise<boolean>;
}
export interface IWakuStore {
set(key: string, data: any): Promise<void>;
get(key: string): Promise<any>;
remove(key: string): Promise<void>;
}
export interface IWakuClient extends IWakuController {
provider: IWakuProvider;
store: IWakuStore;
signer: IWakuSigner;
}

View File

@ -1,64 +1,3 @@
import BasicProvider from "basic-provider";
// -- interfaces ----------------------------------------------- //
export interface IWakuController {
request(payload: JsonRpcRequest): Promise<any>;
}
export interface IWakuProvider extends BasicProvider, IWakuController {
isWakuProvider: boolean;
enable(): Promise<any>;
}
export interface IWakuSigner extends IWakuController {
newKeyPair(): Promise<string>;
addPrivateKey(prvKey: string): Promise<string>;
deleteKeyPair(id: string): Promise<boolean>;
hasKeyPair(id: string): Promise<boolean>;
getPublicKey(id: string): Promise<string>;
getPrivateKey(id: string): Promise<string>;
newSymKey(): Promise<string>;
addSymKey(symKey: string): Promise<string>;
generateSymKeyFromPassword(): Promise<string>;
hasSymKey(id: string): Promise<boolean>;
getSymKey(id: string): Promise<string>;
deleteSymKey(id: string): Promise<boolean>;
}
export interface IWakuStore {
set(key: string, data: any): Promise<void>;
get(key: string): Promise<any>;
remove(key: string): Promise<void>;
}
export interface IWakuClient extends IWakuController {
provider: IWakuProvider;
store: IWakuStore;
signer: IWakuSigner;
}
// -- types ----------------------------------------------- //
export type SymKey = {
id: string;
symKey: string;
};
export type KeyPair = {
id: string;
pubKey: string;
prvKey: string;
};
export type Key = SymKey | KeyPair;
export type KeyMap = {
[id: string]: Key;
};
export type JsonRpcRequest = {
id: number;
jsonrpc: "2.0";
method: string;
params: any;
};
export * from "./controllers";
export * from "./keys";
export * from "./rpc";

16
src/typings/keys.ts Normal file
View File

@ -0,0 +1,16 @@
export type SymKey = {
id: string;
symKey: string;
};
export type KeyPair = {
id: string;
pubKey: string;
prvKey: string;
};
export type Key = SymKey | KeyPair;
export type KeyMap = {
[id: string]: Key;
};

12
src/typings/rpc.ts Normal file
View File

@ -0,0 +1,12 @@
export interface IWakuInfoResponse {
messages: number;
minPow: number;
maxMessageSize: number;
}
export type JsonRpcRequest = {
id: number;
jsonrpc: "2.0";
method: string;
params: any;
};

View File

@ -1,8 +1,60 @@
import Waku from "../src";
import Waku, { IWakuInfoResponse } from "../src";
describe("Waku", () => {
it("needs tests", async () => {
const waku = new Waku("http://localhost:8545");
let waku: Waku;
let info: IWakuInfoResponse;
let keyPairId: string;
beforeEach(async () => {
waku = new Waku("https://waku.walletconnect.org");
info = await waku.init();
});
it("should instantiate", async () => {
expect(waku).toBeTruthy();
});
it("should init controllers", async () => {
expect(info).toBeTruthy();
});
it("should create new key pair", async () => {
keyPairId = await waku.request({
id: 1,
jsonrpc: "2.0",
method: "waku_newKeyPair",
params: [],
});
expect(keyPairId).toBeTruthy();
});
it("should check key pair", async () => {
const check = await waku.request({
id: 1,
jsonrpc: "2.0",
method: "waku_hasKeyPair",
params: [keyPairId],
});
expect(check).toBeTruthy();
});
it("should get public key", async () => {
const pubKey = await waku.request({
id: 1,
jsonrpc: "2.0",
method: "waku_getPublicKey",
params: [keyPairId],
});
expect(pubKey).toBeTruthy();
});
it("should get private key", async () => {
const prvKey = await waku.request({
id: 1,
jsonrpc: "2.0",
method: "waku_getPrivateKey",
params: [keyPairId],
});
expect(prvKey).toBeTruthy();
});
});

View File

@ -1638,6 +1638,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
axios@^0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
dependencies:
follow-redirects "1.5.10"
axobject-query@^2.0.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799"
@ -2523,6 +2530,13 @@ data-urls@^1.0.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@ -3351,6 +3365,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
dependencies:
debug "=3.1.0"
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@ -4750,6 +4771,11 @@ loader-utils@^1.2.3:
emojis-list "^3.0.0"
json5 "^1.0.1"
localStorage@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/localStorage/-/localStorage-1.0.4.tgz#57dfa28084385f81431accb8ae24b196398223f7"
integrity sha512-r35zrihcDiX+dqWlJSeIwS9nrF95OQTgqMFm3FB2D/+XgdmZtcutZOb7t0xXkhOEM8a9kpuu7cc28g1g36I5DQ==
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"