Throw errors when the address is not specified or invalid
This commit is contained in:
		@@ -21,7 +21,7 @@ export abstract class BaseWalletSubprovider extends Subprovider {
 | 
			
		||||
 | 
			
		||||
    public abstract async getAccountsAsync(): Promise<string[]>;
 | 
			
		||||
    public abstract async signTransactionAsync(txParams: PartialTxParams): Promise<string>;
 | 
			
		||||
    public abstract async signPersonalMessageAsync(data: string, address?: string): Promise<string>;
 | 
			
		||||
    public abstract async signPersonalMessageAsync(data: string, address: string): Promise<string>;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This method conforms to the web3-provider-engine interface.
 | 
			
		||||
 
 | 
			
		||||
@@ -105,11 +105,9 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
    public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
 | 
			
		||||
        LedgerSubprovider._validateTxParams(txParams);
 | 
			
		||||
        const initialDerivedKey = await this._initialDerivedKeyAsync();
 | 
			
		||||
        const derivedKey = _.isUndefined(txParams.from)
 | 
			
		||||
            ? walletUtils._firstDerivedKey(initialDerivedKey)
 | 
			
		||||
            : this._findDerivedKeyByPublicAddress(initialDerivedKey, txParams.from);
 | 
			
		||||
        const derivedKey = this._findDerivedKeyByPublicAddress(initialDerivedKey, txParams.from);
 | 
			
		||||
 | 
			
		||||
        this._ledgerClientIfExists = await this._createLedgerClientAsync();
 | 
			
		||||
        const ledgerClient = await this._createLedgerClientAsync();
 | 
			
		||||
 | 
			
		||||
        const tx = new EthereumTx(txParams);
 | 
			
		||||
 | 
			
		||||
@@ -121,7 +119,7 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
        const txHex = tx.serialize().toString('hex');
 | 
			
		||||
        try {
 | 
			
		||||
            const derivationPath = `${derivedKey.derivationPath}/${derivedKey.derivationIndex}`;
 | 
			
		||||
            const result = await this._ledgerClientIfExists.signTransaction(derivationPath, txHex);
 | 
			
		||||
            const result = await ledgerClient.signTransaction(derivationPath, txHex);
 | 
			
		||||
            // Store signature in transaction
 | 
			
		||||
            tx.r = Buffer.from(result.r, 'hex');
 | 
			
		||||
            tx.s = Buffer.from(result.s, 'hex');
 | 
			
		||||
@@ -145,7 +143,7 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Sign a personal Ethereum signed message. The signing address will be the one
 | 
			
		||||
     * either the provided address or the first address on the ledger device.
 | 
			
		||||
     * the provided address.
 | 
			
		||||
     * The Ledger adds the Ethereum signed message prefix on-device.  If you've added
 | 
			
		||||
     * the LedgerSubprovider to your app's provider, you can simply send an `eth_sign`
 | 
			
		||||
     * or `personal_sign` JSON RPC request, and this method will be called auto-magically.
 | 
			
		||||
@@ -154,23 +152,18 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
     * @param address Address to sign with
 | 
			
		||||
     * @return Signature hex string (order: rsv)
 | 
			
		||||
     */
 | 
			
		||||
    public async signPersonalMessageAsync(data: string, address?: string): Promise<string> {
 | 
			
		||||
    public async signPersonalMessageAsync(data: string, address: string): Promise<string> {
 | 
			
		||||
        if (_.isUndefined(data)) {
 | 
			
		||||
            throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
 | 
			
		||||
        }
 | 
			
		||||
        assert.isHexString('data', data);
 | 
			
		||||
        const initialDerivedKey = await this._initialDerivedKeyAsync();
 | 
			
		||||
        const derivedKey = _.isUndefined(address)
 | 
			
		||||
            ? walletUtils._firstDerivedKey(initialDerivedKey)
 | 
			
		||||
            : this._findDerivedKeyByPublicAddress(initialDerivedKey, address);
 | 
			
		||||
        const derivedKey = this._findDerivedKeyByPublicAddress(initialDerivedKey, address);
 | 
			
		||||
 | 
			
		||||
        this._ledgerClientIfExists = await this._createLedgerClientAsync();
 | 
			
		||||
        const ledgerClient = await this._createLedgerClientAsync();
 | 
			
		||||
        try {
 | 
			
		||||
            const derivationPath = `${derivedKey.derivationPath}/${derivedKey.derivationIndex}`;
 | 
			
		||||
            const result = await this._ledgerClientIfExists.signPersonalMessage(
 | 
			
		||||
                derivationPath,
 | 
			
		||||
                ethUtil.stripHexPrefix(data),
 | 
			
		||||
            );
 | 
			
		||||
            const result = await ledgerClient.signPersonalMessage(derivationPath, ethUtil.stripHexPrefix(data));
 | 
			
		||||
            const v = result.v - 27;
 | 
			
		||||
            let vHex = v.toString(16);
 | 
			
		||||
            if (vHex.length < 2) {
 | 
			
		||||
@@ -192,6 +185,7 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
        }
 | 
			
		||||
        const ledgerEthereumClient = await this._ledgerEthereumClientFactoryAsync();
 | 
			
		||||
        this._connectionLock.release();
 | 
			
		||||
        this._ledgerClientIfExists = ledgerEthereumClient;
 | 
			
		||||
        return ledgerEthereumClient;
 | 
			
		||||
    }
 | 
			
		||||
    private async _destroyLedgerClientAsync() {
 | 
			
		||||
@@ -205,11 +199,11 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
        this._connectionLock.release();
 | 
			
		||||
    }
 | 
			
		||||
    private async _initialDerivedKeyAsync(): Promise<DerivedHDKey> {
 | 
			
		||||
        this._ledgerClientIfExists = await this._createLedgerClientAsync();
 | 
			
		||||
        const ledgerClient = await this._createLedgerClientAsync();
 | 
			
		||||
 | 
			
		||||
        let ledgerResponse;
 | 
			
		||||
        try {
 | 
			
		||||
            ledgerResponse = await this._ledgerClientIfExists.getAddress(
 | 
			
		||||
            ledgerResponse = await ledgerClient.getAddress(
 | 
			
		||||
                this._derivationPath,
 | 
			
		||||
                this._shouldAlwaysAskForConfirmation,
 | 
			
		||||
                SHOULD_GET_CHAIN_CODE,
 | 
			
		||||
@@ -229,6 +223,9 @@ export class LedgerSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    private _findDerivedKeyByPublicAddress(initalHDKey: DerivedHDKey, address: string): DerivedHDKey {
 | 
			
		||||
        if (_.isUndefined(address)) {
 | 
			
		||||
            throw new Error(WalletSubproviderErrors.FromAddressMissingOrInvalid);
 | 
			
		||||
        }
 | 
			
		||||
        const matchedDerivedKey = walletUtils.findDerivedKeyByAddress(address, initalHDKey, this._addressSearchLimit);
 | 
			
		||||
        if (_.isUndefined(matchedDerivedKey)) {
 | 
			
		||||
            throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${address}`);
 | 
			
		||||
 
 | 
			
		||||
@@ -63,6 +63,10 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
     */
 | 
			
		||||
    public setPath(derivationPath: string) {
 | 
			
		||||
        this._derivationPath = derivationPath;
 | 
			
		||||
        this._derivedKey = {
 | 
			
		||||
            ...this._derivedKey,
 | 
			
		||||
            derivationPath: this._derivationPath,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieve the accounts associated with the mnemonic.
 | 
			
		||||
@@ -88,10 +92,7 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
     * @return Signed transaction hex string
 | 
			
		||||
     */
 | 
			
		||||
    public async signTransactionAsync(txParams: PartialTxParams): Promise<string> {
 | 
			
		||||
        const derivedKey = _.isUndefined(txParams.from)
 | 
			
		||||
            ? walletUtils._firstDerivedKey(this._derivedKey)
 | 
			
		||||
            : this._findDerivedKeyByPublicAddress(txParams.from);
 | 
			
		||||
        const privateKeyWallet = new PrivateKeyWalletSubprovider(derivedKey.hdKey.privateKey.toString('hex'));
 | 
			
		||||
        const privateKeyWallet = this._privateKeyWalletFromAddress(txParams.from);
 | 
			
		||||
        const signedTx = privateKeyWallet.signTransactionAsync(txParams);
 | 
			
		||||
        return signedTx;
 | 
			
		||||
    }
 | 
			
		||||
@@ -105,15 +106,21 @@ export class MnemonicWalletSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
     * @param address Address to sign with
 | 
			
		||||
     * @return Signature hex string (order: rsv)
 | 
			
		||||
     */
 | 
			
		||||
    public async signPersonalMessageAsync(data: string, address?: string): Promise<string> {
 | 
			
		||||
        const derivedKey = _.isUndefined(address)
 | 
			
		||||
            ? walletUtils._firstDerivedKey(this._derivedKey)
 | 
			
		||||
            : this._findDerivedKeyByPublicAddress(address);
 | 
			
		||||
        const privateKeyWallet = new PrivateKeyWalletSubprovider(derivedKey.hdKey.privateKey.toString('hex'));
 | 
			
		||||
        const sig = await privateKeyWallet.signPersonalMessageAsync(data, derivedKey.address);
 | 
			
		||||
    public async signPersonalMessageAsync(data: string, address: string): Promise<string> {
 | 
			
		||||
        const privateKeyWallet = this._privateKeyWalletFromAddress(address);
 | 
			
		||||
        const sig = await privateKeyWallet.signPersonalMessageAsync(data, address);
 | 
			
		||||
        return sig;
 | 
			
		||||
    }
 | 
			
		||||
    private _privateKeyWalletFromAddress(address: string): PrivateKeyWalletSubprovider {
 | 
			
		||||
        const derivedKey = this._findDerivedKeyByPublicAddress(address);
 | 
			
		||||
        const privateKeyHex = derivedKey.hdKey.privateKey.toString('hex');
 | 
			
		||||
        const privateKeyWallet = new PrivateKeyWalletSubprovider(privateKeyHex);
 | 
			
		||||
        return privateKeyWallet;
 | 
			
		||||
    }
 | 
			
		||||
    private _findDerivedKeyByPublicAddress(address: string): DerivedHDKey {
 | 
			
		||||
        if (_.isUndefined(address)) {
 | 
			
		||||
            throw new Error(WalletSubproviderErrors.FromAddressMissingOrInvalid);
 | 
			
		||||
        }
 | 
			
		||||
        const matchedDerivedKey = walletUtils.findDerivedKeyByAddress(
 | 
			
		||||
            address,
 | 
			
		||||
            this._derivedKey,
 | 
			
		||||
 
 | 
			
		||||
@@ -63,15 +63,15 @@ export class PrivateKeyWalletSubprovider extends BaseWalletSubprovider {
 | 
			
		||||
     * @param address Address to sign with
 | 
			
		||||
     * @return Signature hex string (order: rsv)
 | 
			
		||||
     */
 | 
			
		||||
    public async signPersonalMessageAsync(dataIfExists: string, address?: string): Promise<string> {
 | 
			
		||||
        if (_.isUndefined(dataIfExists)) {
 | 
			
		||||
    public async signPersonalMessageAsync(data: string, address: string): Promise<string> {
 | 
			
		||||
        if (_.isUndefined(data)) {
 | 
			
		||||
            throw new Error(WalletSubproviderErrors.DataMissingForSignPersonalMessage);
 | 
			
		||||
        }
 | 
			
		||||
        if (!_.isUndefined(address) && address !== this._address) {
 | 
			
		||||
            throw new Error(`${WalletSubproviderErrors.AddressNotFound}: ${address}`);
 | 
			
		||||
        if (_.isUndefined(address) || address !== this._address) {
 | 
			
		||||
            throw new Error(`${WalletSubproviderErrors.FromAddressMissingOrInvalid}: ${address}`);
 | 
			
		||||
        }
 | 
			
		||||
        assert.isHexString('data', dataIfExists);
 | 
			
		||||
        const dataBuff = ethUtil.toBuffer(dataIfExists);
 | 
			
		||||
        assert.isHexString('data', data);
 | 
			
		||||
        const dataBuff = ethUtil.toBuffer(data);
 | 
			
		||||
        const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
 | 
			
		||||
        const sig = ethUtil.ecsign(msgHashBuff, this._privateKeyBuffer);
 | 
			
		||||
        const rpcSig = ethUtil.toRpcSig(sig.v, sig.r, sig.s);
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ export interface PartialTxParams {
 | 
			
		||||
    gasPrice?: string;
 | 
			
		||||
    gas: string;
 | 
			
		||||
    to: string;
 | 
			
		||||
    from?: string;
 | 
			
		||||
    from: string;
 | 
			
		||||
    value?: string;
 | 
			
		||||
    data?: string;
 | 
			
		||||
    chainId: number; // EIP 155 chainId - mainnet: 1, ropsten: 3
 | 
			
		||||
@@ -101,10 +101,10 @@ export enum WalletSubproviderErrors {
 | 
			
		||||
    AddressNotFound = 'ADDRESS_NOT_FOUND',
 | 
			
		||||
    DataMissingForSignPersonalMessage = 'DATA_MISSING_FOR_SIGN_PERSONAL_MESSAGE',
 | 
			
		||||
    SenderInvalidOrNotSupplied = 'SENDER_INVALID_OR_NOT_SUPPLIED',
 | 
			
		||||
    FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID',
 | 
			
		||||
}
 | 
			
		||||
export enum LedgerSubproviderErrors {
 | 
			
		||||
    TooOldLedgerFirmware = 'TOO_OLD_LEDGER_FIRMWARE',
 | 
			
		||||
    FromAddressMissingOrInvalid = 'FROM_ADDRESS_MISSING_OR_INVALID',
 | 
			
		||||
    MultipleOpenConnectionsDisallowed = 'MULTIPLE_OPEN_CONNECTIONS_DISALLOWED',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,10 @@ describe('LedgerSubprovider', () => {
 | 
			
		||||
        });
 | 
			
		||||
        it('signs a personal message', async () => {
 | 
			
		||||
            const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
 | 
			
		||||
            const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
 | 
			
		||||
            const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(
 | 
			
		||||
                data,
 | 
			
		||||
                fixtureData.TEST_RPC_ACCOUNT_0,
 | 
			
		||||
            );
 | 
			
		||||
            expect(ecSignatureHex.length).to.be.equal(132);
 | 
			
		||||
            expect(ecSignatureHex).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT);
 | 
			
		||||
        });
 | 
			
		||||
 
 | 
			
		||||
@@ -82,7 +82,7 @@ describe('LedgerSubprovider', () => {
 | 
			
		||||
            });
 | 
			
		||||
            it('signs a personal message', async () => {
 | 
			
		||||
                const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
 | 
			
		||||
                const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data);
 | 
			
		||||
                const ecSignatureHex = await ledgerSubprovider.signPersonalMessageAsync(data, FAKE_ADDRESS);
 | 
			
		||||
                expect(ecSignatureHex).to.be.equal(
 | 
			
		||||
                    '0xa6cc284bff14b42bdf5e9286730c152be91719d478605ec46b3bebcd0ae491480652a1a7b742ceb0213d1e744316e285f41f878d8af0b8e632cbca4c279132d001',
 | 
			
		||||
                );
 | 
			
		||||
@@ -94,7 +94,7 @@ describe('LedgerSubprovider', () => {
 | 
			
		||||
                return expect(
 | 
			
		||||
                    Promise.all([
 | 
			
		||||
                        ledgerSubprovider.getAccountsAsync(),
 | 
			
		||||
                        ledgerSubprovider.signPersonalMessageAsync(data),
 | 
			
		||||
                        ledgerSubprovider.signPersonalMessageAsync(data, FAKE_ADDRESS),
 | 
			
		||||
                    ]),
 | 
			
		||||
                ).to.be.rejectedWith(LedgerSubproviderErrors.MultipleOpenConnectionsDisallowed);
 | 
			
		||||
            });
 | 
			
		||||
@@ -168,6 +168,7 @@ describe('LedgerSubprovider', () => {
 | 
			
		||||
                    gasPrice: '0x00',
 | 
			
		||||
                    nonce: '0x00',
 | 
			
		||||
                    gas: '0x00',
 | 
			
		||||
                    from: FAKE_ADDRESS,
 | 
			
		||||
                };
 | 
			
		||||
                const payload = {
 | 
			
		||||
                    jsonrpc: '2.0',
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ describe('MnemonicWalletSubprovider', () => {
 | 
			
		||||
            });
 | 
			
		||||
            it('signs a personal message', async () => {
 | 
			
		||||
                const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
 | 
			
		||||
                const ecSignatureHex = await subprovider.signPersonalMessageAsync(data);
 | 
			
		||||
                const ecSignatureHex = await subprovider.signPersonalMessageAsync(data, fixtureData.TEST_RPC_ACCOUNT_0);
 | 
			
		||||
                expect(ecSignatureHex).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT);
 | 
			
		||||
            });
 | 
			
		||||
            it('signs a transaction', async () => {
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ describe('PrivateKeyWalletSubprovider', () => {
 | 
			
		||||
            });
 | 
			
		||||
            it('signs a personal message', async () => {
 | 
			
		||||
                const data = ethUtils.bufferToHex(ethUtils.toBuffer(fixtureData.PERSONAL_MESSAGE_STRING));
 | 
			
		||||
                const ecSignatureHex = await subprovider.signPersonalMessageAsync(data);
 | 
			
		||||
                const ecSignatureHex = await subprovider.signPersonalMessageAsync(data, fixtureData.TEST_RPC_ACCOUNT_0);
 | 
			
		||||
                expect(ecSignatureHex).to.be.equal(fixtureData.PERSONAL_MESSAGE_SIGNED_RESULT);
 | 
			
		||||
            });
 | 
			
		||||
            it('signs a transaction', async () => {
 | 
			
		||||
@@ -128,6 +128,23 @@ describe('PrivateKeyWalletSubprovider', () => {
 | 
			
		||||
                });
 | 
			
		||||
                provider.sendAsync(payload, callback);
 | 
			
		||||
            });
 | 
			
		||||
            it('should throw if `address` param is not an address from private key when calling personal_sign', (done: DoneCallback) => {
 | 
			
		||||
                const nonHexMessage = 'hello world';
 | 
			
		||||
                const payload = {
 | 
			
		||||
                    jsonrpc: '2.0',
 | 
			
		||||
                    method: 'personal_sign',
 | 
			
		||||
                    params: [nonHexMessage, fixtureData.TEST_RPC_ACCOUNT_1],
 | 
			
		||||
                    id: 1,
 | 
			
		||||
                };
 | 
			
		||||
                const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
 | 
			
		||||
                    expect(err).to.not.be.a('null');
 | 
			
		||||
                    expect(err.message).to.be.equal(
 | 
			
		||||
                        `${WalletSubproviderErrors.FromAddressMissingOrInvalid}: ${fixtureData.TEST_RPC_ACCOUNT_1}`,
 | 
			
		||||
                    );
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
                provider.sendAsync(payload, callback);
 | 
			
		||||
            });
 | 
			
		||||
            it('should throw if `from` param missing when calling eth_sendTransaction', (done: DoneCallback) => {
 | 
			
		||||
                const tx = {
 | 
			
		||||
                    to: '0xafa3f8684e54059998bc3a7b0d2b0da075154d66',
 | 
			
		||||
@@ -175,7 +192,7 @@ describe('PrivateKeyWalletSubprovider', () => {
 | 
			
		||||
                };
 | 
			
		||||
                const callback = reportCallbackErrors(done)((err: Error, response: JSONRPCResponsePayload) => {
 | 
			
		||||
                    expect(err).to.not.be.a('null');
 | 
			
		||||
                    expect(err.message).to.be.equal(`${WalletSubproviderErrors.AddressNotFound}: 0x0`);
 | 
			
		||||
                    expect(err.message).to.be.equal(`${WalletSubproviderErrors.FromAddressMissingOrInvalid}: 0x0`);
 | 
			
		||||
                    done();
 | 
			
		||||
                });
 | 
			
		||||
                provider.sendAsync(payload, callback);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user