Add isChildKey to derived key
This commit is contained in:
@@ -22,7 +22,6 @@ import { walletUtils } from '../utils/wallet_utils';
|
|||||||
import { BaseWalletSubprovider } from './base_wallet_subprovider';
|
import { BaseWalletSubprovider } from './base_wallet_subprovider';
|
||||||
|
|
||||||
const DEFAULT_DERIVATION_PATH = `44'/60'/0'`;
|
const DEFAULT_DERIVATION_PATH = `44'/60'/0'`;
|
||||||
const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10;
|
|
||||||
const ASK_FOR_ON_DEVICE_CONFIRMATION = false;
|
const ASK_FOR_ON_DEVICE_CONFIRMATION = false;
|
||||||
const SHOULD_GET_CHAIN_CODE = true;
|
const SHOULD_GET_CHAIN_CODE = true;
|
||||||
const IS_CHILD_KEY = true;
|
const IS_CHILD_KEY = true;
|
||||||
@@ -59,9 +58,9 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
|
|||||||
: ASK_FOR_ON_DEVICE_CONFIRMATION;
|
: ASK_FOR_ON_DEVICE_CONFIRMATION;
|
||||||
this._addressSearchLimit =
|
this._addressSearchLimit =
|
||||||
!_.isUndefined(config.accountFetchingConfigs) &&
|
!_.isUndefined(config.accountFetchingConfigs) &&
|
||||||
!_.isUndefined(config.accountFetchingConfigs.numAddressesToReturn)
|
!_.isUndefined(config.accountFetchingConfigs.addressSearchLimit)
|
||||||
? config.accountFetchingConfigs.numAddressesToReturn
|
? config.accountFetchingConfigs.addressSearchLimit
|
||||||
: DEFAULT_NUM_ADDRESSES_TO_FETCH;
|
: walletUtils.DEFAULT_ADDRESS_SEARCH_LIMIT;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Retrieve the set derivation path
|
* Retrieve the set derivation path
|
||||||
@@ -86,15 +85,12 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
|
|||||||
* @param numberOfAccounts Number of accounts to retrieve (default: 10)
|
* @param numberOfAccounts Number of accounts to retrieve (default: 10)
|
||||||
* @return An array of accounts
|
* @return An array of accounts
|
||||||
*/
|
*/
|
||||||
public async getAccountsAsync(numberOfAccounts: number = DEFAULT_NUM_ADDRESSES_TO_FETCH): Promise<string[]> {
|
public async getAccountsAsync(
|
||||||
const initialHDKey = await this._initialHDKeyAsync();
|
numberOfAccounts: number = walletUtils.DEFAULT_NUM_ADDRESSES_TO_FETCH,
|
||||||
const derivedKeys = walletUtils._calculateDerivedHDKeys(
|
): Promise<string[]> {
|
||||||
initialHDKey,
|
const offset = 0;
|
||||||
this._derivationPath,
|
const initialHDerivedKey = await this._initialDerivedKeyAsync();
|
||||||
numberOfAccounts,
|
const derivedKeys = walletUtils.calculateDerivedHDKeys(initialHDerivedKey, numberOfAccounts, offset);
|
||||||
0,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
const accounts = _.map(derivedKeys, 'address');
|
const accounts = _.map(derivedKeys, 'address');
|
||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
@@ -108,10 +104,10 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
|
|||||||
*/
|
*/
|
||||||
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
|
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
|
||||||
LedgerSubprovider._validateTxParams(txParams);
|
LedgerSubprovider._validateTxParams(txParams);
|
||||||
const initialHDKey = await this._initialHDKeyAsync();
|
const initialDerivedKey = await this._initialDerivedKeyAsync();
|
||||||
const derivedKey = _.isUndefined(txParams.from)
|
const derivedKey = _.isUndefined(txParams.from)
|
||||||
? walletUtils._firstDerivedKey(initialHDKey, this._derivationPath, IS_CHILD_KEY)
|
? walletUtils._firstDerivedKey(initialDerivedKey)
|
||||||
: this._findDerivedKeyByPublicAddress(initialHDKey, txParams.from);
|
: this._findDerivedKeyByPublicAddress(initialDerivedKey, txParams.from);
|
||||||
|
|
||||||
this._ledgerClientIfExists = await this._createLedgerClientAsync();
|
this._ledgerClientIfExists = await this._createLedgerClientAsync();
|
||||||
|
|
||||||
@@ -163,10 +159,10 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
|
|||||||
throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
|
throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
|
||||||
}
|
}
|
||||||
assert.isHexString('data', data);
|
assert.isHexString('data', data);
|
||||||
const initialHDKey = await this._initialHDKeyAsync();
|
const initialDerivedKey = await this._initialDerivedKeyAsync();
|
||||||
const derivedKey = _.isUndefined(address)
|
const derivedKey = _.isUndefined(address)
|
||||||
? walletUtils._firstDerivedKey(initialHDKey, this._derivationPath, IS_CHILD_KEY)
|
? walletUtils._firstDerivedKey(initialDerivedKey)
|
||||||
: this._findDerivedKeyByPublicAddress(initialHDKey, address);
|
: this._findDerivedKeyByPublicAddress(initialDerivedKey, address);
|
||||||
|
|
||||||
this._ledgerClientIfExists = await this._createLedgerClientAsync();
|
this._ledgerClientIfExists = await this._createLedgerClientAsync();
|
||||||
try {
|
try {
|
||||||
@@ -208,7 +204,7 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
|
|||||||
this._ledgerClientIfExists = undefined;
|
this._ledgerClientIfExists = undefined;
|
||||||
this._connectionLock.release();
|
this._connectionLock.release();
|
||||||
}
|
}
|
||||||
private async _initialHDKeyAsync(): Promise<HDNode> {
|
private async _initialDerivedKeyAsync(): Promise<DerivedHDKey> {
|
||||||
this._ledgerClientIfExists = await this._createLedgerClientAsync();
|
this._ledgerClientIfExists = await this._createLedgerClientAsync();
|
||||||
|
|
||||||
let ledgerResponse;
|
let ledgerResponse;
|
||||||
@@ -224,16 +220,16 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
|
|||||||
const hdKey = new HDNode();
|
const hdKey = new HDNode();
|
||||||
hdKey.publicKey = new Buffer(ledgerResponse.publicKey, 'hex');
|
hdKey.publicKey = new Buffer(ledgerResponse.publicKey, 'hex');
|
||||||
hdKey.chainCode = new Buffer(ledgerResponse.chainCode, 'hex');
|
hdKey.chainCode = new Buffer(ledgerResponse.chainCode, 'hex');
|
||||||
return hdKey;
|
return {
|
||||||
|
hdKey,
|
||||||
|
address: ledgerResponse.address,
|
||||||
|
isChildKey: true,
|
||||||
|
derivationPath: this._derivationPath,
|
||||||
|
derivationIndex: 0,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
private _findDerivedKeyByPublicAddress(initalHDKey: HDNode, address: string): DerivedHDKey {
|
private _findDerivedKeyByPublicAddress(initalHDKey: DerivedHDKey, address: string): DerivedHDKey {
|
||||||
const matchedDerivedKey = walletUtils._findDerivedKeyByAddress(
|
const matchedDerivedKey = walletUtils.findDerivedKeyByAddress(address, initalHDKey, this._addressSearchLimit);
|
||||||
address,
|
|
||||||
initalHDKey,
|
|
||||||
this._derivationPath,
|
|
||||||
this._addressSearchLimit,
|
|
||||||
IS_CHILD_KEY,
|
|
||||||
);
|
|
||||||
if (_.isUndefined(matchedDerivedKey)) {
|
if (_.isUndefined(matchedDerivedKey)) {
|
||||||
throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${address}`);
|
throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${address}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import { BaseWalletSubprovider } from './base_wallet_subprovider';
|
|||||||
import { PrivateKeyWalletSubprovider } from './private_key_wallet_subprovider';
|
import { PrivateKeyWalletSubprovider } from './private_key_wallet_subprovider';
|
||||||
|
|
||||||
const DEFAULT_DERIVATION_PATH = `44'/60'/0'/0`;
|
const DEFAULT_DERIVATION_PATH = `44'/60'/0'/0`;
|
||||||
const DEFAULT_NUM_ADDRESSES_TO_FETCH = 10;
|
|
||||||
const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
|
* This class implements the [web3-provider-engine](https://github.com/MetaMask/provider-engine) subprovider interface.
|
||||||
@@ -22,7 +20,7 @@ const DEFAULT_ADDRESS_SEARCH_LIMIT = 1000;
|
|||||||
export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
|
export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
|
||||||
private _addressSearchLimit: number;
|
private _addressSearchLimit: number;
|
||||||
private _derivationPath: string;
|
private _derivationPath: string;
|
||||||
private _hdKey: HDNode;
|
private _derivedKey: DerivedHDKey;
|
||||||
/**
|
/**
|
||||||
* Instantiates a MnemonicWalletSubprovider. Defaults to derivationPath set to `44'/60'/0'/0`.
|
* Instantiates a MnemonicWalletSubprovider. Defaults to derivationPath set to `44'/60'/0'/0`.
|
||||||
* This is the default in TestRPC/Ganache, this can be overridden if desired.
|
* This is the default in TestRPC/Ganache, this can be overridden if desired.
|
||||||
@@ -34,15 +32,22 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
|
|||||||
constructor(
|
constructor(
|
||||||
mnemonic: string,
|
mnemonic: string,
|
||||||
derivationPath: string = DEFAULT_DERIVATION_PATH,
|
derivationPath: string = DEFAULT_DERIVATION_PATH,
|
||||||
addressSearchLimit: number = DEFAULT_ADDRESS_SEARCH_LIMIT,
|
addressSearchLimit: number = walletUtils.DEFAULT_ADDRESS_SEARCH_LIMIT,
|
||||||
) {
|
) {
|
||||||
assert.isString('mnemonic', mnemonic);
|
assert.isString('mnemonic', mnemonic);
|
||||||
assert.isString('derivationPath', derivationPath);
|
assert.isString('derivationPath', derivationPath);
|
||||||
assert.isNumber('addressSearchLimit', addressSearchLimit);
|
assert.isNumber('addressSearchLimit', addressSearchLimit);
|
||||||
super();
|
super();
|
||||||
const seed = bip39.mnemonicToSeed(mnemonic);
|
const seed = bip39.mnemonicToSeed(mnemonic);
|
||||||
this._hdKey = HDNode.fromMasterSeed(seed);
|
const hdKey = HDNode.fromMasterSeed(seed);
|
||||||
this._derivationPath = derivationPath;
|
this._derivationPath = derivationPath;
|
||||||
|
this._derivedKey = {
|
||||||
|
address: walletUtils.addressOfHDKey(hdKey),
|
||||||
|
derivationPath: this._derivationPath,
|
||||||
|
derivationIndex: 0,
|
||||||
|
hdKey,
|
||||||
|
isChildKey: false,
|
||||||
|
};
|
||||||
this._addressSearchLimit = addressSearchLimit;
|
this._addressSearchLimit = addressSearchLimit;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -66,8 +71,10 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
|
|||||||
* @param numberOfAccounts Number of accounts to retrieve (default: 10)
|
* @param numberOfAccounts Number of accounts to retrieve (default: 10)
|
||||||
* @return An array of accounts
|
* @return An array of accounts
|
||||||
*/
|
*/
|
||||||
public async getAccountsAsync(numberOfAccounts: number = DEFAULT_NUM_ADDRESSES_TO_FETCH): Promise<string[]> {
|
public async getAccountsAsync(
|
||||||
const derivedKeys = walletUtils._calculateDerivedHDKeys(this._hdKey, this._derivationPath, numberOfAccounts);
|
numberOfAccounts: number = walletUtils.DEFAULT_NUM_ADDRESSES_TO_FETCH,
|
||||||
|
): Promise<string[]> {
|
||||||
|
const derivedKeys = walletUtils.calculateDerivedHDKeys(this._derivedKey, numberOfAccounts);
|
||||||
const accounts = _.map(derivedKeys, 'address');
|
const accounts = _.map(derivedKeys, 'address');
|
||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
@@ -82,7 +89,7 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
|
|||||||
*/
|
*/
|
||||||
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
|
public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
|
||||||
const derivedKey = _.isUndefined(txParams.from)
|
const derivedKey = _.isUndefined(txParams.from)
|
||||||
? walletUtils._firstDerivedKey(this._hdKey, this._derivationPath)
|
? walletUtils._firstDerivedKey(this._derivedKey)
|
||||||
: this._findDerivedKeyByPublicAddress(txParams.from);
|
: this._findDerivedKeyByPublicAddress(txParams.from);
|
||||||
const privateKeyWallet = new PrivateKeyWalletSubprovider(derivedKey.hdKey.privateKey.toString('hex'));
|
const privateKeyWallet = new PrivateKeyWalletSubprovider(derivedKey.hdKey.privateKey.toString('hex'));
|
||||||
const signedTx = privateKeyWallet.signTransactionAsync(txParams);
|
const signedTx = privateKeyWallet.signTransactionAsync(txParams);
|
||||||
@@ -100,17 +107,16 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
|
|||||||
*/
|
*/
|
||||||
public async signPersonalMessageAsync(data: string, address?: string): Promise<string> {
|
public async signPersonalMessageAsync(data: string, address?: string): Promise<string> {
|
||||||
const derivedKey = _.isUndefined(address)
|
const derivedKey = _.isUndefined(address)
|
||||||
? walletUtils._firstDerivedKey(this._hdKey, this._derivationPath)
|
? walletUtils._firstDerivedKey(this._derivedKey)
|
||||||
: this._findDerivedKeyByPublicAddress(address);
|
: this._findDerivedKeyByPublicAddress(address);
|
||||||
const privateKeyWallet = new PrivateKeyWalletSubprovider(derivedKey.hdKey.privateKey.toString('hex'));
|
const privateKeyWallet = new PrivateKeyWalletSubprovider(derivedKey.hdKey.privateKey.toString('hex'));
|
||||||
const sig = await privateKeyWallet.signPersonalMessageAsync(data, derivedKey.address);
|
const sig = await privateKeyWallet.signPersonalMessageAsync(data, derivedKey.address);
|
||||||
return sig;
|
return sig;
|
||||||
}
|
}
|
||||||
private _findDerivedKeyByPublicAddress(address: string): DerivedHDKey {
|
private _findDerivedKeyByPublicAddress(address: string): DerivedHDKey {
|
||||||
const matchedDerivedKey = walletUtils._findDerivedKeyByAddress(
|
const matchedDerivedKey = walletUtils.findDerivedKeyByAddress(
|
||||||
address,
|
address,
|
||||||
this._hdKey,
|
this._derivedKey,
|
||||||
this._derivationPath,
|
|
||||||
this._addressSearchLimit,
|
this._addressSearchLimit,
|
||||||
);
|
);
|
||||||
if (_.isUndefined(matchedDerivedKey)) {
|
if (_.isUndefined(matchedDerivedKey)) {
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export interface LedgerSubproviderConfigs {
|
|||||||
* before fetching their addresses
|
* before fetching their addresses
|
||||||
*/
|
*/
|
||||||
export interface AccountFetchingConfigs {
|
export interface AccountFetchingConfigs {
|
||||||
|
addressSearchLimit?: number;
|
||||||
numAddressesToReturn?: number;
|
numAddressesToReturn?: number;
|
||||||
shouldAskForOnDeviceConfirmation?: boolean;
|
shouldAskForOnDeviceConfirmation?: boolean;
|
||||||
}
|
}
|
||||||
@@ -116,6 +117,7 @@ export interface DerivedHDKey {
|
|||||||
derivationPath: string;
|
derivationPath: string;
|
||||||
derivationIndex: number;
|
derivationIndex: number;
|
||||||
hdKey: HDNode;
|
hdKey: HDNode;
|
||||||
|
isChildKey: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ErrorCallback = (err: Error | null, data?: any) => void;
|
export type ErrorCallback = (err: Error | null, data?: any) => void;
|
||||||
|
|||||||
@@ -8,54 +8,51 @@ const DEFAULT_ADDRESS_SEARCH_OFFSET = 0;
|
|||||||
const BATCH_SIZE = 10;
|
const BATCH_SIZE = 10;
|
||||||
|
|
||||||
export const walletUtils = {
|
export const walletUtils = {
|
||||||
_calculateDerivedHDKeys(
|
DEFAULT_NUM_ADDRESSES_TO_FETCH: 10,
|
||||||
initialHDKey: HDNode,
|
DEFAULT_ADDRESS_SEARCH_LIMIT: 1000,
|
||||||
derivationPath: string,
|
calculateDerivedHDKeys(
|
||||||
|
initialDerivedKey: DerivedHDKey,
|
||||||
searchLimit: number,
|
searchLimit: number,
|
||||||
offset: number = DEFAULT_ADDRESS_SEARCH_OFFSET,
|
offset: number = DEFAULT_ADDRESS_SEARCH_OFFSET,
|
||||||
isChildKey: boolean = false,
|
|
||||||
): DerivedHDKey[] {
|
): DerivedHDKey[] {
|
||||||
const derivedKeys: DerivedHDKey[] = [];
|
const derivedKeys: DerivedHDKey[] = [];
|
||||||
_.times(searchLimit, i => {
|
_.times(searchLimit, i => {
|
||||||
|
const derivationPath = initialDerivedKey.derivationPath;
|
||||||
const derivationIndex = offset + i;
|
const derivationIndex = offset + i;
|
||||||
// Normally we need to set the full derivation path to walk the tree from the root
|
// If the DerivedHDKey is a child then we walk relative, if not we walk the full derivation path
|
||||||
// as the initial key is at the root.
|
const path = initialDerivedKey.isChildKey
|
||||||
// But with ledger the initial key is a child so we walk the tree relative to that child
|
? `m/${derivationIndex}`
|
||||||
const path = isChildKey ? `m/${derivationIndex}` : `m/${derivationPath}/${derivationIndex}`;
|
: `m/${derivationPath}/${derivationIndex}`;
|
||||||
const hdKey = initialHDKey.derive(path);
|
const hdKey = initialDerivedKey.hdKey.derive(path);
|
||||||
const derivedPublicKey = hdKey.publicKey;
|
const address = walletUtils.addressOfHDKey(hdKey);
|
||||||
const shouldSanitizePublicKey = true;
|
|
||||||
const ethereumAddressUnprefixed = ethUtil
|
|
||||||
.publicToAddress(derivedPublicKey, shouldSanitizePublicKey)
|
|
||||||
.toString('hex');
|
|
||||||
const address = ethUtil.addHexPrefix(ethereumAddressUnprefixed);
|
|
||||||
const derivedKey: DerivedHDKey = {
|
const derivedKey: DerivedHDKey = {
|
||||||
derivationPath,
|
|
||||||
hdKey,
|
|
||||||
address,
|
address,
|
||||||
|
hdKey,
|
||||||
|
derivationPath,
|
||||||
derivationIndex,
|
derivationIndex,
|
||||||
|
isChildKey: initialDerivedKey.isChildKey,
|
||||||
};
|
};
|
||||||
derivedKeys.push(derivedKey);
|
derivedKeys.push(derivedKey);
|
||||||
});
|
});
|
||||||
return derivedKeys;
|
return derivedKeys;
|
||||||
},
|
},
|
||||||
|
addressOfHDKey(hdKey: HDNode): string {
|
||||||
_findDerivedKeyByAddress(
|
const shouldSanitizePublicKey = true;
|
||||||
|
const derivedPublicKey = hdKey.publicKey;
|
||||||
|
const ethereumAddressUnprefixed = ethUtil
|
||||||
|
.publicToAddress(derivedPublicKey, shouldSanitizePublicKey)
|
||||||
|
.toString('hex');
|
||||||
|
const address = ethUtil.addHexPrefix(ethereumAddressUnprefixed);
|
||||||
|
return address;
|
||||||
|
},
|
||||||
|
findDerivedKeyByAddress(
|
||||||
address: string,
|
address: string,
|
||||||
initialHDKey: HDNode,
|
initialDerivedKey: DerivedHDKey,
|
||||||
derivationPath: string,
|
|
||||||
searchLimit: number,
|
searchLimit: number,
|
||||||
isChild: boolean = false,
|
|
||||||
): DerivedHDKey | undefined {
|
): DerivedHDKey | undefined {
|
||||||
let matchedKey: DerivedHDKey | undefined;
|
let matchedKey: DerivedHDKey | undefined;
|
||||||
for (let index = 0; index < searchLimit; index = index + BATCH_SIZE) {
|
for (let index = 0; index < searchLimit; index = index + BATCH_SIZE) {
|
||||||
const derivedKeys = walletUtils._calculateDerivedHDKeys(
|
const derivedKeys = walletUtils.calculateDerivedHDKeys(initialDerivedKey, BATCH_SIZE, index);
|
||||||
initialHDKey,
|
|
||||||
derivationPath,
|
|
||||||
BATCH_SIZE,
|
|
||||||
index,
|
|
||||||
isChild,
|
|
||||||
);
|
|
||||||
matchedKey = _.find(derivedKeys, derivedKey => derivedKey.address === address);
|
matchedKey = _.find(derivedKeys, derivedKey => derivedKey.address === address);
|
||||||
if (matchedKey) {
|
if (matchedKey) {
|
||||||
break;
|
break;
|
||||||
@@ -64,8 +61,8 @@ export const walletUtils = {
|
|||||||
return matchedKey;
|
return matchedKey;
|
||||||
},
|
},
|
||||||
|
|
||||||
_firstDerivedKey(initialHDKey: HDNode, derivationPath: string, isChild: boolean = false): DerivedHDKey {
|
_firstDerivedKey(initialDerivedKey: DerivedHDKey): DerivedHDKey {
|
||||||
const derivedKeys = walletUtils._calculateDerivedHDKeys(initialHDKey, derivationPath, 1, 0, isChild);
|
const derivedKeys = walletUtils.calculateDerivedHDKeys(initialDerivedKey, 1, 0);
|
||||||
const firstDerivedKey = derivedKeys[0];
|
const firstDerivedKey = derivedKeys[0];
|
||||||
return firstDerivedKey;
|
return firstDerivedKey;
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user