Moved static Wallet calls to utils and refactors exposed types.

This commit is contained in:
Richard Moore 2018-07-17 01:44:04 -04:00
parent 922de67a8b
commit b61b84dfc8
No known key found for this signature in database
GPG Key ID: 525F70A6FCABC295
9 changed files with 145 additions and 110 deletions

@ -10,11 +10,17 @@ import { id } from '../utils/hash';
import { keccak256 } from '../utils/keccak256';
import { defineReadOnly, defineFrozen } from '../utils/properties';
import { BigNumber, BigNumberish, DeployDescription, EventDescription, EventFragment, FunctionDescription, FunctionFragment, Indexed, ParamType } from '../utils/types';
import {
BigNumber, BigNumberish,
DeployDescription as _DeployDescription, EventDescription as _EventDescription, FunctionDescription as _FunctionDescription, LogDescription as _LogDescription, TransactionDescription as _TransactionDescription,
EventFragment, FunctionFragment,
Indexed as _Indexed,
ParamType
} from '../utils/types';
import * as errors from '../utils/errors';
class _Indexed extends Indexed {
class Indexed extends _Indexed {
constructor(hash: string) {
super();
defineReadOnly(this, 'hash', hash);
@ -22,7 +28,6 @@ class _Indexed extends Indexed {
}
class Description {
readonly type: string;
constructor(info: any) {
for (var key in info) {
let value = info[key];
@ -35,8 +40,7 @@ class Description {
}
}
class _DeployDescription extends Description implements DeployDescription {
readonly type: "deploy";
class DeployDescription extends Description implements _DeployDescription {
readonly inputs: Array<ParamType>;
readonly payable: boolean;
@ -64,7 +68,7 @@ class _DeployDescription extends Description implements DeployDescription {
}
}
class _FunctionDescription extends Description implements FunctionDescription {
class FunctionDescription extends Description implements _FunctionDescription {
readonly type: "call" | "transaction";
readonly name: string;
readonly signature: string;
@ -110,8 +114,7 @@ class Result extends Description {
[key: number]: any;
}
class _EventDescription extends Description implements EventDescription {
readonly type: "event";
class EventDescription extends Description implements _EventDescription {
readonly name: string;
readonly signature: string;
@ -198,10 +201,10 @@ class _EventDescription extends Description implements EventDescription {
this.inputs.forEach(function(input, index) {
if (input.indexed) {
if (topics == null) {
result[index] = new _Indexed(null);
result[index] = new Indexed(null);
} else if (inputDynamic[index]) {
result[index] = new _Indexed(resultIndexed[indexedIndex++]);
result[index] = new Indexed(resultIndexed[indexedIndex++]);
} else {
result[index] = resultIndexed[indexedIndex++];
@ -218,7 +221,7 @@ class _EventDescription extends Description implements EventDescription {
}
}
class TransactionDescription extends Description {
class TransactionDescription extends Description implements _TransactionDescription{
readonly name: string;
readonly args: Array<any>;
readonly signature: string;
@ -227,7 +230,7 @@ class TransactionDescription extends Description {
readonly value: BigNumber;
}
class LogDescription extends Description {
class LogDescription extends Description implements _LogDescription {
readonly name: string;
readonly signature: string;
readonly topic: string;
@ -238,10 +241,9 @@ class LogDescription extends Description {
function addMethod(method: any): void {
switch (method.type) {
case 'constructor': {
let description = new _DeployDescription({
let description = new DeployDescription({
inputs: method.inputs,
payable: (method.payable == null || !!method.payable),
type: "deploy"
payable: (method.payable == null || !!method.payable)
});
if (!this.deployFunction) { this.deployFunction = description; }
@ -253,7 +255,7 @@ function addMethod(method: any): void {
let signature = formatSignature(method).replace(/tuple/g, '');
let sighash = id(signature).substring(0, 10);
let description = new _FunctionDescription({
let description = new FunctionDescription({
inputs: method.inputs,
outputs: method.outputs,
@ -280,15 +282,13 @@ function addMethod(method: any): void {
case 'event': {
let signature = formatSignature(method).replace(/tuple/g, '');
let description = new _EventDescription({
let description = new EventDescription({
name: method.name,
signature: signature,
inputs: method.inputs,
topic: id(signature),
anonymous: (!!method.anonymous),
type: 'event'
anonymous: (!!method.anonymous)
});
// Expose the first (and hopefully unique) event name
@ -304,7 +304,6 @@ function addMethod(method: any): void {
break;
}
case 'fallback':
// Nothing to do for fallback
break;
@ -317,9 +316,9 @@ function addMethod(method: any): void {
export class Interface {
readonly abi: Array<EventFragment | FunctionFragment>;
readonly functions: { [ name: string ]: FunctionDescription };
readonly events: { [ name: string ]: EventDescription };
readonly deployFunction: DeployDescription;
readonly functions: { [ name: string ]: _FunctionDescription };
readonly events: { [ name: string ]: _EventDescription };
readonly deployFunction: _DeployDescription;
constructor(abi: Array<string | ParamType> | string) {
errors.checkNew(this, Interface);
@ -364,7 +363,7 @@ export class Interface {
}
}
parseTransaction(tx: { data: string, value?: BigNumberish }): TransactionDescription {
parseTransaction(tx: { data: string, value?: BigNumberish }): _TransactionDescription {
var sighash = tx.data.substring(0, 10).toLowerCase();
for (var name in this.functions) {
if (name.indexOf('(') === -1) { continue; }
@ -377,7 +376,6 @@ export class Interface {
name: name,
signature: func.signature,
sighash: func.sighash,
type: 'transaction',
value: bigNumberify(tx.value || 0),
});
}
@ -386,7 +384,7 @@ export class Interface {
return null;
}
parseLog(log: { topics: Array<string>, data: string}): LogDescription {
parseLog(log: { topics: Array<string>, data: string}): _LogDescription {
for (var name in this.events) {
if (name.indexOf('(') === -1) { continue; }
var event = this.events[name];
@ -399,7 +397,6 @@ export class Interface {
name: event.name,
signature: event.signature,
topic: event.topic,
type: 'log',
values: event.decode(log.data, log.topics)
});
}

@ -11,7 +11,7 @@ import { Web3Provider } from './web3-provider';
import { Network } from '../utils/types';
function getDefaultProvider(network?: Network | string): FallbackProvider {
function getDefaultProvider(network?: Network | string): Provider {
return new FallbackProvider([
new InfuraProvider(network),
new EtherscanProvider(network),

@ -9,6 +9,7 @@ import * as base64 from './base64';
import { bigNumberify, ConstantNegativeOne, ConstantZero, ConstantOne, ConstantTwo, ConstantWeiPerEther } from './bignumber';
import { AddressZero, arrayify, concat, HashZero, hexDataSlice, hexDataLength, hexlify, hexStripZeros, hexZeroPad, joinSignature, padZeros, splitSignature, stripZeros } from './bytes';
import { hashMessage, id, namehash } from './hash';
import { getJsonWalletAddress } from './json-wallet';
import { keccak256 } from './keccak256';
import { sha256 } from './sha2';
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from './solidity';
@ -16,6 +17,7 @@ import { randomBytes } from './random-bytes';
import { getNetwork } from './networks';
import { defineFrozen, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import * as RLP from './rlp';
import { verifyMessage } from './secp256k1';
import { parse as parseTransaction, serialize as serializeTransaction } from './transaction';
import { toUtf8Bytes, toUtf8String } from './utf8';
import { formatEther, parseEther, formatUnits, parseUnits } from './units';
@ -112,6 +114,10 @@ export {
parseTransaction,
serializeTransaction,
getJsonWalletAddress,
verifyMessage,
errors
}

@ -0,0 +1,44 @@
import { getAddress } from './address';
export function isCrowdsaleWallet(json: string): boolean {
try {
var data = JSON.parse(json);
} catch (error) { return false; }
return (data.encseed && data.ethaddr);
}
export function isSecretStorageWallet(json: string): boolean {
try {
var data = JSON.parse(json);
} catch (error) { return false; }
if (!data.version || parseInt(data.version) !== data.version || parseInt(data.version) !== 3) {
return false;
}
// @TODO: Put more checks to make sure it has kdf, iv and all that good stuff
return true;
}
//export function isJsonWallet(json: string): boolean {
// return (isSecretStorageWallet(json) || isCrowdsaleWallet(json));
//}
export function getJsonWalletAddress(json: string): string {
if (isCrowdsaleWallet(json)) {
try {
return getAddress(JSON.parse(json).ethaddr);
} catch (error) { return null; }
}
if (isSecretStorageWallet(json)) {
try {
return getAddress(JSON.parse(json).address);
} catch (error) { return null; }
}
return null;
}

@ -1,11 +1,11 @@
'use strict';
import { ec as EC } from 'elliptic';
const curve = new EC('secp256k1');
import { getAddress } from './address';
import { arrayify, hexlify, hexZeroPad } from './bytes';
import { arrayify, hexlify, hexZeroPad, splitSignature } from './bytes';
import { hashMessage } from './hash';
import { keccak256 } from './keccak256';
import { defineReadOnly } from './properties';
@ -13,7 +13,6 @@ import { Arrayish, Signature } from './types';
import * as errors from './errors';
export const N = '0x' + curve.n.toString(16);
export class KeyPair {
@ -87,3 +86,28 @@ export function computeAddress(key: string): string {
let publicKey = '0x' + computePublicKey(key).slice(4);
return getAddress('0x' + keccak256(publicKey).substring(26));
}
export function verifyMessage(message: Arrayish | string, signature: Signature | string): string {
let sig = splitSignature(signature);
let digest = hashMessage(message);
return recoverAddress(
digest,
{
r: sig.r,
s: sig.s,
recoveryParam: sig.recoveryParam
}
);
}
// !!!!!! IMPORTANT !!!!!!!!
//
// This import MUST be at the bottom, otehrwise browserify executes several imports
// BEFORE they are exported, resulting in undefined
import { ec as EC } from 'elliptic';
const curve = new EC('secp256k1');
export const N = '0x' + curve.n.toString(16);

@ -250,40 +250,55 @@ export interface TransactionResponse extends Transaction {
///////////////////////////////
// Interface
export class Indexed {
hash: string;
export abstract class Indexed {
readonly hash: string;
}
export interface DeployDescription {
type: "deploy";
inputs: Array<ParamType>;
payable: boolean;
readonly inputs: Array<ParamType>;
readonly payable: boolean;
encode(bytecode: string, params: Array<any>): string;
}
export interface FunctionDescription {
type: "call" | "transaction";
name: string;
signature: string;
sighash: string;
inputs: Array<ParamType>;
outputs: Array<ParamType>;
payable: boolean;
readonly type: "call" | "transaction";
readonly name: string;
readonly signature: string;
readonly sighash: string;
readonly inputs: Array<ParamType>;
readonly outputs: Array<ParamType>;
readonly payable: boolean;
encode(params: Array<any>): string;
decode(data: string): any;
}
export interface EventDescription {
type: "event";
name: string;
signature: string;
inputs: Array<ParamType>;
anonymous: boolean;
topic: string;
readonly name: string;
readonly signature: string;
readonly inputs: Array<ParamType>;
readonly anonymous: boolean;
readonly topic: string;
encodeTopics(params: Array<any>): Array<string>;
decode(data: string, topics?: Array<string>): any;
}
export interface LogDescription {
readonly name: string;
readonly signature: string;
readonly topic: string;
readonly values: Array<any>
}
export interface TransactionDescription {
readonly name: string;
readonly args: Array<any>;
readonly signature: string;
readonly sighash: string;
readonly decode: (data: string) => any;
readonly value: BigNumber;
}
///////////////////////////////
// Contract
@ -322,7 +337,7 @@ export type Listener = (...args: Array<any>) => void;
* Note: We use an abstract class so we can use instanceof to determine if an
* object is a Provider.
*/
export abstract class MinimalProvider {
export abstract class MinimalProvider implements OnceBlockable {
abstract getNetwork(): Promise<Network>;
abstract getBlockNumber(): Promise<number>;
@ -358,7 +373,7 @@ export abstract class MinimalProvider {
}
export type AsyncProvider = {
isMetaMask: boolean;
isMetaMask?: boolean;
host?: string;
path?: string;
sendAsync: (request: any, callback: (error: any, response: any) => void) => void

@ -157,14 +157,14 @@ function _fromSeed(seed: Arrayish, mnemonic: string): HDNode {
return new HDNode(I.slice(0, 32), I.slice(32), 0, 0, mnemonic, 'm');
}
export function fromMnemonic(mnemonic: string, wordlist?: Wordlist): HDNode {
export function fromMnemonic(mnemonic: string, wordlist?: Wordlist): _HDNode {
// Check that the checksum s valid (will throw an error)
mnemonicToEntropy(mnemonic, wordlist);
return _fromSeed(mnemonicToSeed(mnemonic), mnemonic);
}
export function fromSeed(seed: Arrayish): HDNode {
export function fromSeed(seed: Arrayish): _HDNode {
return _fromSeed(seed, null);
}

@ -64,26 +64,6 @@ function searchPath(object: any, path: string): string {
return currentChild;
}
export function isCrowdsaleWallet(json: string): boolean {
try {
var data = JSON.parse(json);
} catch (error) { return false; }
return (data.encseed && data.ethaddr);
}
export function isValidWallet(json: string): boolean {
try {
var data = JSON.parse(json);
} catch (error) { return false; }
if (!data.version || parseInt(data.version) !== data.version || parseInt(data.version) !== 3) {
return false;
}
// @TODO: Put more checks to make sure it has kdf, iv and all that good stuff
return true;
}
// @TODO: Make a type for string or arrayish
// See: https://github.com/ethereum/pyethsaletool

@ -4,12 +4,12 @@ import { defaultPath, entropyToMnemonic, fromMnemonic } from './hdnode';
import * as secretStorage from './secret-storage';
import { SigningKey } from './signing-key';
import { arrayify, concat, hexlify, joinSignature } from '../utils/bytes';
import { arrayify, concat, joinSignature } from '../utils/bytes';
import { hashMessage } from '../utils/hash';
import { isCrowdsaleWallet, isSecretStorageWallet } from '../utils/json-wallet';
import { keccak256 } from '../utils/keccak256';
import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { randomBytes } from '../utils/random-bytes';
import { recoverAddress } from '../utils/secp256k1';
import { serialize as serializeTransaction } from '../utils/transaction';
import { Arrayish, BigNumber, BlockTag, HDNode, MinimalProvider, ProgressCallback, Signer, TransactionRequest, TransactionResponse, Wordlist } from '../utils/types';
@ -160,7 +160,7 @@ export class Wallet extends Signer {
}
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet> {
if (secretStorage.isCrowdsaleWallet(json)) {
if (isCrowdsaleWallet(json)) {
try {
if (progressCallback) { progressCallback(0); }
let privateKey = secretStorage.decryptCrowdsale(json, password);
@ -170,7 +170,7 @@ export class Wallet extends Signer {
return Promise.reject(error);
}
} else if (secretStorage.isValidWallet(json)) {
} else if (isSecretStorageWallet(json)) {
return secretStorage.decrypt(json, password, progressCallback).then(function(signingKey) {
return new Wallet(signingKey);
@ -184,35 +184,4 @@ export class Wallet extends Signer {
if (!path) { path = defaultPath; }
return new Wallet(fromMnemonic(mnemonic, wordlist).derivePath(path));
}
/**
* Determine if this is an encryped JSON wallet.
*/
static isEncryptedWallet(json: string): boolean {
return (secretStorage.isValidWallet(json) || secretStorage.isCrowdsaleWallet(json));
}
/**
* Verify a signed message, returning the address of the signer.
*/
static verifyMessage(message: Arrayish | string, signature: string): string {
signature = hexlify(signature);
if (signature.length != 132) { throw new Error('invalid signature'); }
var digest = hashMessage(message);
var recoveryParam = parseInt(signature.substring(130), 16);
if (recoveryParam >= 27) { recoveryParam -= 27; }
if (recoveryParam < 0) { throw new Error('invalid signature'); }
return recoverAddress(
digest,
{
r: signature.substring(0, 66),
s: '0x' + signature.substring(66, 130),
recoveryParam: recoveryParam
}
);
}
}