eth-janus
Version:
Indirection service
208 lines (181 loc) • 9.13 kB
text/typescript
import { ISigner } from "../interfaces/isigner";
import { IDirectoryProvider } from "../interfaces/idirectory-provider";
import { IMessageProvider } from "../interfaces/imessage-provider";
import { IStorageProvider } from "../interfaces/istorage-provider";
import { ISmartContractService } from "../interfaces/ismart-contract-service";
import { SmartContractService } from "../services/smart-contract-service";
import { OnetimeKeyGeneratorService } from "../services/onetimekey-generator-service";
import { Utils } from "../services/utils";
import { Hdwallet } from "./hdwallet";
import { OnetimeKey, Message, MessageType, OnetimeKeyRequest, OnetimeKeyResponse } from "../common/models";
var Tx = require('ethereumjs-tx');
var Web3 = require("web3");
const utils = new Utils();
export class Janus {
private directoryProvider: IDirectoryProvider;
private messageProvider: IMessageProvider
private storageProvider: IStorageProvider;
private signer: ISigner;
private companyName: string;
private mnemonics: string;
private nodeUrl: string;
private smartContractService: ISmartContractService;
private web3;
private callbackMap: Map<string, (response) => void>;
private fundingAccount: string;
private hdwallet: Hdwallet;
constructor(companyName: string, mnemonics: string, nodeUrl: string, directoryProvider: IDirectoryProvider,
messageProvider: IMessageProvider, storageProvider: IStorageProvider, signer: ISigner) {
this.companyName = companyName;
this.mnemonics = mnemonics;
this.nodeUrl = nodeUrl;
this.directoryProvider = directoryProvider;
this.messageProvider = messageProvider;
this.storageProvider = storageProvider;
this.signer = signer;
//console.log(this.directoryProvider);
if(this.nodeUrl)
this.web3 = new Web3(new Web3.providers.HttpProvider(this.nodeUrl));
if(!this.web3)
throw "Web3 instance is null";
this.smartContractService = new SmartContractService(this.web3);
this.callbackMap = new Map<string, (message) => void>();
this.messageProvider.watch(async (err, message) => { await this.onMessage(err, message); });
this.fundingAccount = this.web3.eth.accounts[0];
}
public startHdwallet(messageProvider: IMessageProvider) {
if(!this.hdwallet)
this.hdwallet = new Hdwallet(this.companyName, this.mnemonics, this.directoryProvider, messageProvider, this.storageProvider, this.signer, this.web3);
}
public async requestOnetimeKeys(txnRef: string, networkId: string, parties: Array<string>, callback: (response) => void) {
let partiesKeyMap = new Array<{partyName:string,onetimeKey:OnetimeKey}>();
//parties.forEach(function (value, index, array) {
for(let i = 0; i<parties.length;i++) {
let value = parties[i];
if(value) {
partiesKeyMap.push({partyName:value, onetimeKey: null});
}
//});
}
await this.storageProvider.storeOnetimeKeyMap(txnRef, networkId, partiesKeyMap);
this.registerCallback(txnRef, callback);
//parties.forEach(function (value, index, array) {
for(let i = 0; i<parties.length;i++) {
let value = parties[i];
if(value) {
//console.log("In requestOnetimeKeys");
//Post OTA request for party
let fromPublicKey = await this.directoryProvider.getCompanyKey(this.companyName, "shhKey");
let toPublicKey = await this.directoryProvider.getCompanyKey(value, "shhKey");
if(toPublicKey) {
//console.log("toPublicKey", toPublicKey);
let requestMsg = new OnetimeKeyRequest({transactionId:txnRef, networkId:networkId, sender:this.companyName, recepient:value});
let msgString = JSON.stringify(requestMsg);
let signature = await this.signer.sign(msgString);
let message = new Message({type: MessageType.OnetimeKeyRequest, payload: msgString, signature: signature});
//console.log("sending message", message);
await this.messageProvider.postMessage(fromPublicKey,toPublicKey,JSON.stringify(message));
}
}
//});
}
}
public async getOnetimeKeys(txnRef: string, networkId) {
//Reading from storage provider
console.log(this.storageProvider);
return await this.storageProvider.readOnetimeKeyMap(txnRef, networkId);
}
public async signTransaction(txnRef: string, networkId: string, txn: any ): Promise<{signedTx: string, signedTxObj: any, rawTx: any}> {
let signedObj;
if(this.hdwallet && txn) {
signedObj = await this.hdwallet.signTransaction(txnRef, networkId, txn);
}
return signedObj;
}
public async signMessage(txnRef: string, networkId: string, message: string): Promise<string> {
let signature;
if(this.hdwallet) {
signature = await this.hdwallet.signMessage(txnRef, networkId, message);
}
return signature;
}
public async postTransaction(txnRef: string, networkId: string, txn: any ): Promise<any> {
let response;
if(this.hdwallet) {
if(!txn["gasPrice"])
txn["gasPrice"] = 0;
if(!txn["gas"] && !txn["gasLimit"]) {
var block = this.web3.eth.getBlock("latest");
txn["gasLimit"] = block.gasLimit;
}
let signedObj = await this.hdwallet.signTransaction(txnRef, networkId, txn);
//console.log("SignedTx",signedObj.signedTx);
await this.fundAccountIfNeeded(signedObj.rawTx["from"]);
response = await this.web3.eth.sendRawTransaction(signedObj.signedTx);
}
return response;
}
public async getTransactionReceipt(txnHash: string, interval: number) {
return await this.smartContractService.getTransactionReceipt(txnHash, interval);
}
private async fundAccountIfNeeded(address: string) {
let balance = await this.web3.eth.getBalance(address);
//console.log("account", address);
//console.log("balance", balance);
if(!balance || balance.lt(this.web3.toWei(0.05,"ether"))) {
console.log("funding...");
let amount = this.web3.toWei(0.1,"ether");
let tx = await this.web3.eth.sendTransaction({from:this.fundingAccount, to:address, value:amount});
console.log("tx hash:", tx);
//let confirmedTxn = await this.web3.currentProvider.waitForTransaction(tx.hash, 20000);
//console.log("confirmedTxn", confirmedTxn);
}
}
private async onMessage(err, message) {
if(err || !message) {
console.log("Message:",message);
console.log("Error:",err);
return;
}
let msgType: MessageType = message["type"];
let payload = message["payload"];
let result = await utils.verifySignature(payload, message["signature"]);
if(!result.isValid) {
console.log("Invalid signature on message of type", msgType);
return;
}
if(msgType == MessageType.OnetimeKeyResponse) {
await this.processOnetimeKeyResponse(payload, result.signerAddress);
}
}
private async processOnetimeKeyResponse(payload: string, fromAddress: string) {
let response = new OnetimeKeyResponse(JSON.parse(payload));
//TODO: verify responder "fromAddress"
console.log("Onetime key response received for txnRef:", response.transactionId, " from", response.sender);
//console.log("Response received:", response);
await this.storageProvider.storeOnetimeKeyMap(response.transactionId, response.networkId, [{partyName:response.sender, onetimeKey:response.onetimeKey}]);
let result = await this.storageProvider.readOnetimeKeyMap(response.transactionId, response.networkId);
//console.log("Result:",result);
//console.log("Result:",JSON.stringify(result));
console.log(this.storageProvider);
if(utils.checkIfKeyMapHasAllKeys(result.partyKeyMap)) {
this.invokeCallback(response.transactionId, result);
this.unregisterCallback(response.transactionId);
}
}
private registerCallback(transactionId: string, callback: (response: any) => void) {
if (transactionId && callback)
this.callbackMap.set(transactionId, callback);
}
private unregisterCallback(transactionId: string) {
if (transactionId)
this.callbackMap.delete(transactionId);
}
private invokeCallback(transactionId: string, response: any) {
if (transactionId && this.callbackMap.has(transactionId)){
let callback = this.callbackMap.get(transactionId);
if(callback)
callback(response);
}
}
}