feat(Provider): added a Provider to get Http and WS Provider easier

dev
neeboo 5 years ago
parent c727071ae6
commit 5db3a5004d
  1. 9
      .prettierrc
  2. 31
      packages/harmony-core/src/harmony.ts
  3. 29
      packages/harmony-crypto/src/keyTool.ts
  4. 3
      packages/harmony-network/src/index.ts
  5. 55
      packages/harmony-network/src/providers/provider.ts
  6. 92
      packages/harmony-transaction/src/transaction.ts

@ -1,7 +1,8 @@
{
"parser": "typescript",
"printWidth": 80,
"printWidth": 100,
"singleQuote": true,
"bracketSpacing": true,
"trailingComma": "all",
"arrowParens": "always",
"overrides": [
@ -10,6 +11,12 @@
"options": {
"parser": "json"
}
},
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
}
]
}

@ -1,12 +1,12 @@
import * as crypto from '@harmony-js/crypto';
import * as utils from '@harmony-js/utils';
import {HttpProvider, Messenger, WSProvider} from '@harmony-js/network';
import {TransactionFactory, Transaction} from '@harmony-js/transaction';
import {ContractFactory, Contract} from '@harmony-js/contract';
import {Wallet, Account} from '@harmony-js/account';
import {Blockchain} from './blockchain';
import {HarmonyConfig} from './util';
import { Provider, HttpProvider, Messenger, WSProvider } from '@harmony-js/network';
import { TransactionFactory, Transaction } from '@harmony-js/transaction';
import { ContractFactory, Contract } from '@harmony-js/contract';
import { Wallet, Account } from '@harmony-js/account';
import { Blockchain } from './blockchain';
import { HarmonyConfig } from './util';
export class Harmony extends utils.HarmonyCore {
Modules = {
@ -40,7 +40,7 @@ export class Harmony extends utils.HarmonyCore {
const providerUrl = config.chainUrl || url;
this.provider = this.onInitSetProvider(providerUrl);
this.provider = new Provider(providerUrl).provider;
this.messenger = new Messenger(this.provider, this.chainType, this.chainId);
this.blockchain = new Blockchain(this.messenger);
this.transactions = new TransactionFactory(this.messenger);
@ -50,15 +50,7 @@ export class Harmony extends utils.HarmonyCore {
this.utils = utils;
}
public setProvider(provider: string | HttpProvider | WSProvider): void {
if (utils.isHttp(provider) && typeof provider === 'string') {
this.provider = new HttpProvider(provider);
} else if (provider instanceof HttpProvider) {
this.provider = provider;
} else if (utils.isWs(provider) && typeof provider === 'string') {
this.provider = new WSProvider(provider);
} else if (provider instanceof WSProvider) {
this.provider = provider;
}
this.provider = new Provider(provider).provider;
this.messenger.setProvider(this.provider);
this.blockchain.setMessenger(this.messenger);
this.wallet.setMessenger(this.messenger);
@ -79,11 +71,4 @@ export class Harmony extends utils.HarmonyCore {
this.wallet.setMessenger(this.messenger);
this.transactions.setMessenger(this.messenger);
}
private onInitSetProvider(providerUrl: string): HttpProvider | WSProvider {
return utils.isHttp(providerUrl)
? new HttpProvider(providerUrl)
: utils.isWs(providerUrl)
? new WSProvider(providerUrl)
: new HttpProvider(utils.defaultConfig.Default.Chain_URL);
}
}

@ -50,10 +50,11 @@ export const getAddressFromPrivateKey = (privateKey: string): string => {
};
export const getPublic = (privateKey: string, compress?: boolean): string => {
if (!isPrivateKey(privateKey)) {
if (!isPrivateKey(privateKey) || !validatePrivateKey(privateKey)) {
throw new Error(`${privateKey} is not PrivateKey`);
}
const ecKey = secp256k1.keyFromPrivate(strip0x(privateKey), 'hex');
return ecKey.getPublic(compress || false, 'hex');
};
@ -104,10 +105,7 @@ export const toChecksumAddress = (address: string): string => {
return '0x' + chars.join('');
};
export const sign = (
digest: bytes.Arrayish | string,
privateKey: string,
): bytes.Signature => {
export const sign = (digest: bytes.Arrayish | string, privateKey: string): bytes.Signature => {
if (!isPrivateKey(privateKey)) {
throw new Error(`${privateKey} is not PrivateKey`);
}
@ -134,9 +132,7 @@ export function getContractAddress(from: string, nonce: number): string {
throw new Error('missing from address');
}
const addr = keccak256(
encode([from, bytes.stripZeros(bytes.hexlify(nonce))]),
);
const addr = keccak256(encode([from, bytes.stripZeros(bytes.hexlify(nonce))]));
return '0x' + addr.substring(26);
}
@ -156,11 +152,7 @@ export function recoverPublicKey(
const rs = { r: bytes.arrayify(sig.r), s: bytes.arrayify(sig.s) };
////
const recovered = secp256k1.recoverPubKey(
bytes.arrayify(digest),
rs,
sig.recoveryParam,
);
const recovered = secp256k1.recoverPubKey(bytes.arrayify(digest), rs, sig.recoveryParam);
const key = recovered.encode('hex', false);
const ecKey = secp256k1.keyFromPublic(key, 'hex');
@ -189,8 +181,11 @@ export function recoverAddress(
* @returns {boolean}
*/
export const isValidChecksumAddress = (address: string): boolean => {
return (
isAddress(address.replace('0x', '')) &&
toChecksumAddress(address) === address
);
return isAddress(address.replace('0x', '')) && toChecksumAddress(address) === address;
};
export const validatePrivateKey = (privateKey: string): boolean => {
const ecKey = secp256k1.keyFromPrivate(strip0x(privateKey), 'hex');
const { result } = ecKey.validate();
return result;
};

@ -1,5 +1,5 @@
import mitt from 'mitt';
export {mitt};
export { mitt };
// provider related
export * from './providers/baseProvider';
export * from './providers/baseSocket';
@ -7,6 +7,7 @@ export * from './providers/defaultFetcher';
export * from './providers/http';
export * from './providers/ws';
export * from './providers/emitter';
export * from './providers/provider';
// messenger and middlewares
export * from './messenger/messenger';
export * from './messenger/responseMiddleware';

@ -0,0 +1,55 @@
import { HttpProvider } from './http';
import { WSProvider } from './ws';
import { defaultConfig, isHttp, isWs } from '@harmony-js/utils';
export enum ProviderType {
http = 'http',
ws = 'ws',
}
export class Provider {
static getProvider(provider: string | HttpProvider | WSProvider) {
try {
this.getProvider(provider);
return new Provider(provider);
} catch (error) {
throw error;
}
}
public provider: WSProvider | HttpProvider;
public providerType: ProviderType;
constructor(url: string | WSProvider | HttpProvider) {
this.provider = this.onInitSetProvider(url);
this.providerType = this.getType(this.provider);
}
private onInitSetProvider(
providerUrl: string | HttpProvider | WSProvider,
): HttpProvider | WSProvider {
if (typeof providerUrl === 'string') {
return isHttp(providerUrl)
? new HttpProvider(providerUrl)
: isWs(providerUrl)
? new WSProvider(providerUrl)
: new HttpProvider(defaultConfig.Default.Chain_URL);
}
try {
const providerType = this.getType(providerUrl);
if (providerType === ProviderType.http || providerType === ProviderType.ws) {
return providerUrl;
} else {
throw new Error('cannot get provider type');
}
} catch (error) {
throw error;
}
}
private getType(provider: HttpProvider | WSProvider) {
if (provider instanceof HttpProvider) {
return ProviderType.http;
}
if (provider instanceof WSProvider) {
return ProviderType.ws;
}
throw new Error('provider is not correct');
}
}

@ -9,7 +9,7 @@ import {
getAddress,
HarmonyAddress,
} from '@harmony-js/crypto';
import {add0xToString, numberToHex, ChainType, Unit} from '@harmony-js/utils';
import { add0xToString, numberToHex, ChainType, Unit } from '@harmony-js/utils';
import {
Messenger,
RPCMethod,
@ -19,7 +19,7 @@ import {
// SubscribeReturns,
NewHeaders,
} from '@harmony-js/network';
import {TxParams, TxStatus, TransasctionReceipt} from './types';
import { TxParams, TxStatus, TransasctionReceipt } from './types';
import {
recover,
transactionFields,
@ -80,19 +80,13 @@ class Transaction {
this.to = params && params.to ? this.normalizeAddress(params.to) : '0x';
this.value =
params && params.value
? new Unit(params.value).asWei().toWei()
: new Unit(0).asWei().toWei();
params && params.value ? new Unit(params.value).asWei().toWei() : new Unit(0).asWei().toWei();
this.data = params && params.data ? params.data : '0x';
// chainid should change with different network settings
this.chainId =
params && params.chainId ? params.chainId : this.messenger.chainId;
this.rawTransaction =
params && params.rawTransaction ? params.rawTransaction : '0x';
this.chainId = params && params.chainId ? params.chainId : this.messenger.chainId;
this.rawTransaction = params && params.rawTransaction ? params.rawTransaction : '0x';
this.unsignedRawTransaction =
params && params.unsignedRawTransaction
? params.unsignedRawTransaction
: '0x';
params && params.unsignedRawTransaction ? params.unsignedRawTransaction : '0x';
this.signature =
params && params.signature
? params.signature
@ -114,24 +108,15 @@ class Transaction {
// temp setting to be compatible with eth
const fields =
this.messenger.chainType === ChainType.Harmony
? transactionFields
: transactionFieldsETH;
this.messenger.chainType === ChainType.Harmony ? transactionFields : transactionFieldsETH;
fields.forEach((field: any) => {
let value = (<any>this.txParams)[field.name] || [];
value = arrayify(
hexlify(
field.transform === 'hex' ? add0xToString(value.toString(16)) : value,
),
hexlify(field.transform === 'hex' ? add0xToString(value.toString(16)) : value),
);
// Fixed-width field
if (
field.fix === true &&
field.length &&
value.length !== field.length &&
value.length > 0
) {
if (field.fix === true && field.length && value.length !== field.length && value.length > 0) {
throw new Error(`invalid length for ${field.name}`);
}
@ -196,9 +181,7 @@ class Transaction {
shardID: this.txParams.shardID ? numberToHex(this.shardID) : '0x',
toShardID: this.txParams.toShardID ? numberToHex(this.toShardID) : '0x',
gas: this.txParams.gasLimit ? numberToHex(this.txParams.gasLimit) : '0x',
gasPrice: this.txParams.gasPrice
? numberToHex(this.txParams.gasPrice)
: '0x',
gasPrice: this.txParams.gasPrice ? numberToHex(this.txParams.gasPrice) : '0x',
value: this.txParams.value ? numberToHex(this.txParams.value) : '0x',
data: this.txParams.data || '0x',
nonce: this.txParams.nonce ? numberToHex(this.nonce) : '0x',
@ -239,17 +222,12 @@ class Transaction {
this.toShardID = params && params.toShardID ? params.toShardID : 0;
this.to = params && params.to ? this.normalizeAddress(params.to) : '0x';
this.value =
params && params.value
? new Unit(params.value).asWei().toWei()
: new Unit(0).asWei().toWei();
params && params.value ? new Unit(params.value).asWei().toWei() : new Unit(0).asWei().toWei();
this.data = params && params.data ? params.data : '0x';
this.chainId = params && params.chainId ? params.chainId : 0;
this.rawTransaction =
params && params.rawTransaction ? params.rawTransaction : '0x';
this.rawTransaction = params && params.rawTransaction ? params.rawTransaction : '0x';
this.unsignedRawTransaction =
params && params.unsignedRawTransaction
? params.unsignedRawTransaction
: '0x';
params && params.unsignedRawTransaction ? params.unsignedRawTransaction : '0x';
this.signature =
params && params.signature
? params.signature
@ -310,10 +288,7 @@ class Transaction {
if (!this.messenger) {
throw new Error('Messenger not found');
}
const res = await this.messenger.send(
RPCMethod.SendRawTransaction,
this.rawTransaction,
);
const res = await this.messenger.send(RPCMethod.SendRawTransaction, this.rawTransaction);
// temporarilly hard coded
if (res.isResult()) {
@ -337,10 +312,7 @@ class Transaction {
throw new Error('Messenger not found');
}
// TODO: regex validation for txHash so we don't get garbage
const res = await this.messenger.send(
RPCMethod.GetTransactionReceipt,
txHash,
);
const res = await this.messenger.send(RPCMethod.GetTransactionReceipt, txHash);
if (res.isResult() && res.result !== null) {
this.receipt = res.result;
@ -363,20 +335,22 @@ class Transaction {
} else {
this.txStatus = TxStatus.PENDING;
const currentBlock = await this.getBlockNumber();
this.blockNumbers.push('0x' + currentBlock.toString('hex'));
this.confirmationCheck += 1;
return false;
}
} else {
this.txStatus = TxStatus.PENDING;
const currentBlock = await this.getBlockNumber();
this.blockNumbers.push('0x' + currentBlock.toString('hex'));
this.confirmationCheck += 1;
return false;
}
}
async confirm(
txHash: string,
maxAttempts: number = 20,
interval: number = 1000,
) {
async confirm(txHash: string, maxAttempts: number = 20, interval: number = 1000) {
if (this.messenger.provider instanceof HttpProvider) {
this.txStatus = TxStatus.PENDING;
const oldBlock = await this.getBlockNumber();
@ -390,6 +364,7 @@ class Transaction {
if (newBlock.gte(nextBlock)) {
checkBlock = newBlock;
if (await this.trackTx(txHash)) {
this.emitConfirm(this.txStatus);
return this;
@ -410,9 +385,7 @@ class Transaction {
}
this.txStatus = TxStatus.REJECTED;
this.emitConfirm(this.txStatus);
throw new Error(
`The transaction is still not confirmed after ${maxAttempts} attempts.`,
);
throw new Error(`The transaction is still not confirmed after ${maxAttempts} attempts.`);
} else {
try {
if (await this.trackTx(txHash)) {
@ -426,22 +399,18 @@ class Transaction {
this.txStatus = TxStatus.REJECTED;
this.emitConfirm(this.txStatus);
throw new Error(
`The transaction is still not confirmed after ${maxAttempts *
interval} mil seconds.`,
`The transaction is still not confirmed after ${maxAttempts * interval} mil seconds.`,
);
}
}
}
socketConfirm(
txHash: string,
maxAttempts: number = 20,
): Promise<Transaction> {
socketConfirm(txHash: string, maxAttempts: number = 20): Promise<Transaction> {
return new Promise((resolve, reject) => {
const newHeads = Promise.resolve(new NewHeaders(this.messenger));
newHeads.then((p) => {
p.onData(async (data: any) => {
if (!this.blockNumbers.includes(data.number)) {
if (!this.blockNumbers.includes(data.params.result.number)) {
if (await this.trackTx(txHash)) {
this.emitConfirm(this.txStatus);
await p.unsubscribe();
@ -492,10 +461,7 @@ class Transaction {
}
async getBlockByNumber(blockNumber: string) {
try {
const block = await this.messenger.send(RPCMethod.GetBlockByNumber, [
blockNumber,
true,
]);
const block = await this.messenger.send(RPCMethod.GetBlockByNumber, [blockNumber, true]);
if (block.isError()) {
throw block.message;
}
@ -519,4 +485,4 @@ class Transaction {
}
}
}
export {Transaction};
export { Transaction };

Loading…
Cancel
Save