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. 21
      packages/harmony-core/src/harmony.ts
  3. 29
      packages/harmony-crypto/src/keyTool.ts
  4. 1
      packages/harmony-network/src/index.ts
  5. 55
      packages/harmony-network/src/providers/provider.ts
  6. 86
      packages/harmony-transaction/src/transaction.ts

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

@ -1,7 +1,7 @@
import * as crypto from '@harmony-js/crypto'; import * as crypto from '@harmony-js/crypto';
import * as utils from '@harmony-js/utils'; import * as utils from '@harmony-js/utils';
import {HttpProvider, Messenger, WSProvider} from '@harmony-js/network'; import { Provider, HttpProvider, Messenger, WSProvider } from '@harmony-js/network';
import { TransactionFactory, Transaction } from '@harmony-js/transaction'; import { TransactionFactory, Transaction } from '@harmony-js/transaction';
import { ContractFactory, Contract } from '@harmony-js/contract'; import { ContractFactory, Contract } from '@harmony-js/contract';
import { Wallet, Account } from '@harmony-js/account'; import { Wallet, Account } from '@harmony-js/account';
@ -40,7 +40,7 @@ export class Harmony extends utils.HarmonyCore {
const providerUrl = config.chainUrl || url; 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.messenger = new Messenger(this.provider, this.chainType, this.chainId);
this.blockchain = new Blockchain(this.messenger); this.blockchain = new Blockchain(this.messenger);
this.transactions = new TransactionFactory(this.messenger); this.transactions = new TransactionFactory(this.messenger);
@ -50,15 +50,7 @@ export class Harmony extends utils.HarmonyCore {
this.utils = utils; this.utils = utils;
} }
public setProvider(provider: string | HttpProvider | WSProvider): void { public setProvider(provider: string | HttpProvider | WSProvider): void {
if (utils.isHttp(provider) && typeof provider === 'string') { this.provider = new Provider(provider).provider;
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.messenger.setProvider(this.provider); this.messenger.setProvider(this.provider);
this.blockchain.setMessenger(this.messenger); this.blockchain.setMessenger(this.messenger);
this.wallet.setMessenger(this.messenger); this.wallet.setMessenger(this.messenger);
@ -79,11 +71,4 @@ export class Harmony extends utils.HarmonyCore {
this.wallet.setMessenger(this.messenger); this.wallet.setMessenger(this.messenger);
this.transactions.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 => { export const getPublic = (privateKey: string, compress?: boolean): string => {
if (!isPrivateKey(privateKey)) { if (!isPrivateKey(privateKey) || !validatePrivateKey(privateKey)) {
throw new Error(`${privateKey} is not PrivateKey`); throw new Error(`${privateKey} is not PrivateKey`);
} }
const ecKey = secp256k1.keyFromPrivate(strip0x(privateKey), 'hex'); const ecKey = secp256k1.keyFromPrivate(strip0x(privateKey), 'hex');
return ecKey.getPublic(compress || false, 'hex'); return ecKey.getPublic(compress || false, 'hex');
}; };
@ -104,10 +105,7 @@ export const toChecksumAddress = (address: string): string => {
return '0x' + chars.join(''); return '0x' + chars.join('');
}; };
export const sign = ( export const sign = (digest: bytes.Arrayish | string, privateKey: string): bytes.Signature => {
digest: bytes.Arrayish | string,
privateKey: string,
): bytes.Signature => {
if (!isPrivateKey(privateKey)) { if (!isPrivateKey(privateKey)) {
throw new Error(`${privateKey} is not 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'); throw new Error('missing from address');
} }
const addr = keccak256( const addr = keccak256(encode([from, bytes.stripZeros(bytes.hexlify(nonce))]));
encode([from, bytes.stripZeros(bytes.hexlify(nonce))]),
);
return '0x' + addr.substring(26); 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 rs = { r: bytes.arrayify(sig.r), s: bytes.arrayify(sig.s) };
//// ////
const recovered = secp256k1.recoverPubKey( const recovered = secp256k1.recoverPubKey(bytes.arrayify(digest), rs, sig.recoveryParam);
bytes.arrayify(digest),
rs,
sig.recoveryParam,
);
const key = recovered.encode('hex', false); const key = recovered.encode('hex', false);
const ecKey = secp256k1.keyFromPublic(key, 'hex'); const ecKey = secp256k1.keyFromPublic(key, 'hex');
@ -189,8 +181,11 @@ export function recoverAddress(
* @returns {boolean} * @returns {boolean}
*/ */
export const isValidChecksumAddress = (address: string): boolean => { export const isValidChecksumAddress = (address: string): boolean => {
return ( return isAddress(address.replace('0x', '')) && toChecksumAddress(address) === address;
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;
}; };

@ -7,6 +7,7 @@ export * from './providers/defaultFetcher';
export * from './providers/http'; export * from './providers/http';
export * from './providers/ws'; export * from './providers/ws';
export * from './providers/emitter'; export * from './providers/emitter';
export * from './providers/provider';
// messenger and middlewares // messenger and middlewares
export * from './messenger/messenger'; export * from './messenger/messenger';
export * from './messenger/responseMiddleware'; 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');
}
}

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

Loading…
Cancel
Save