-
-
Notifications
You must be signed in to change notification settings - Fork 97
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add mobile keyring bridge (#225)
* feat: add mobile keyring bridge * chore: update version number * feat: upgrade @ethereumjs/tx library to match the same version used in TransactionController 8.0.1 to fix all transaction broken issue. Fix the signPersonalMessage in ledger always return 0x0 in v value in test-dapp * feat: upgrade ledgerHQ library to latest to resolve vulnerablity report from socket-security * Feat/add mobile keyring bridge flat version (#3) * feat: this is a backup of flat directory version of eth-ledger-bridge-keyring to successfully solve the metamask-mobile not able to resovle all the module under the subdirectory during build process. * feat: improve the unit tests coverage for some API. * feat: run `yarn lint:fix` and update the correct import in index.ts file. * feat: add unit tests coverage * feat: fix lint error in test file. * feat: Remove unit tests which failed. * feat: Add extra test to cover more lines in ledger-keyring.ts * feat: Add yarn lint:fix * feat: Increase the threshold coverage to pass the pipeline. * feat: revert the version of package.. * Update src/ledger-transport-middleware.ts Co-authored-by: Gustavo Antunes <[email protected]> * Update src/ledger-mobile-bridge.test.ts Co-authored-by: Gustavo Antunes <[email protected]> * Update src/ledger-mobile-bridge.test.ts Co-authored-by: Gustavo Antunes <[email protected]> * Update src/ledger-mobile-bridge.test.ts Co-authored-by: Gustavo Antunes <[email protected]> * Apply suggestions from code review Co-authored-by: Gustavo Antunes <[email protected]> * feat: remove the comment code in `prepack.sh` * feat: change the jsDoc for deviceSignMessage method. * feat: fix the lint issue. * Apply suggestions from code review Co-authored-by: Gustavo Antunes <[email protected]> * Apply suggestions from code review Co-authored-by: Gustavo Antunes <[email protected]> * feat: change setAccountToUnlock method signature to enforce index is number type. * feat: change setAccountToUnlock method signature to enforce index is number type. * feat: commit the new coverageThreshold to pass the test. --------- Co-authored-by: Xiaoming Wang <[email protected]> Co-authored-by: Gustavo Antunes <[email protected]>
- Loading branch information
1 parent
127eb74
commit 8979b63
Showing
14 changed files
with
2,678 additions
and
1,841 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
export * from './ledger-keyring'; | ||
export * from './ledger-iframe-bridge'; | ||
export * from './ledger-mobile-bridge'; | ||
export * from './ledger-bridge'; | ||
export * from './ledger-transport-middleware'; | ||
export * from './type'; | ||
export * from './ledger-hw-app'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import Transport from '@ledgerhq/hw-transport'; | ||
|
||
import { MetaMaskLedgerHwAppEth } from './ledger-hw-app'; | ||
|
||
const DEVICE_ID = 'DEVICE_ID'; | ||
|
||
const mockTransport = { | ||
deviceModel: { | ||
id: DEVICE_ID, | ||
}, | ||
send: jest.fn(), | ||
close: jest.fn(), | ||
decorateAppAPIMethods: jest.fn(), | ||
}; | ||
|
||
describe('MetaMaskLedgerHwAppEth', function () { | ||
afterEach(function () { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe('openEthApp', function () { | ||
it('sends "open ETH app" command correctly', async function () { | ||
const ethApp = new MetaMaskLedgerHwAppEth( | ||
mockTransport as unknown as Transport, | ||
); | ||
|
||
const transportSpy = jest | ||
.spyOn(mockTransport, 'send') | ||
.mockImplementation(async () => Promise.resolve(Buffer.alloc(1))); | ||
|
||
await ethApp.openEthApp(); | ||
expect(transportSpy).toHaveBeenCalledTimes(1); | ||
expect(transportSpy).toHaveBeenCalledWith( | ||
0xe0, | ||
0xd8, | ||
0x00, | ||
0x00, | ||
Buffer.from('Ethereum', 'ascii'), | ||
); | ||
}); | ||
}); | ||
|
||
describe('closeApps', function () { | ||
it('sends "closeApp" command correctly', async function () { | ||
const ethApp = new MetaMaskLedgerHwAppEth( | ||
mockTransport as unknown as Transport, | ||
); | ||
|
||
const transportSpy = jest | ||
.spyOn(mockTransport, 'send') | ||
.mockImplementation(async () => Promise.resolve(Buffer.alloc(1))); | ||
|
||
await ethApp.closeApps(); | ||
expect(transportSpy).toHaveBeenCalledTimes(1); | ||
expect(transportSpy).toHaveBeenCalledWith(0xb0, 0xa7, 0x00, 0x00); | ||
}); | ||
}); | ||
|
||
describe('getAppNameAndVersion', function () { | ||
it('gets appName and appVersion correctly', async function () { | ||
const appNameBuf = Buffer.alloc(7, 'appName', 'ascii'); | ||
const verionBuf = Buffer.alloc(10, 'appVersion', 'ascii'); | ||
const buffer = Buffer.alloc(20); | ||
buffer[0] = 1; | ||
buffer[1] = appNameBuf.length; | ||
let j = 2; | ||
for (let i = 0; i < appNameBuf.length; i++, j++) { | ||
buffer[j] = appNameBuf[i] ?? 0; | ||
} | ||
buffer[j] = verionBuf.length; | ||
j += 1; | ||
for (let i = 0; i < verionBuf.length; i++, j++) { | ||
buffer[j] = verionBuf[i] ?? 0; | ||
} | ||
|
||
const ethApp = new MetaMaskLedgerHwAppEth( | ||
mockTransport as unknown as Transport, | ||
); | ||
|
||
const transportSpy = jest | ||
.spyOn(mockTransport, 'send') | ||
.mockImplementation(async () => Promise.resolve(buffer)); | ||
|
||
const result = await ethApp.getAppNameAndVersion(); | ||
expect(transportSpy).toHaveBeenCalledTimes(1); | ||
expect(transportSpy).toHaveBeenCalledWith(0xb0, 0x01, 0x00, 0x00); | ||
expect(result).toStrictEqual({ | ||
appName: 'appName', | ||
version: 'appVersion', | ||
}); | ||
}); | ||
|
||
it('does not throw an error when the result length is less than expected', async function () { | ||
const buffer = Buffer.alloc(1); | ||
buffer[0] = 1; | ||
const ethApp = new MetaMaskLedgerHwAppEth( | ||
mockTransport as unknown as Transport, | ||
); | ||
const transportSpy = jest | ||
.spyOn(mockTransport, 'send') | ||
.mockImplementation(async () => Promise.resolve(buffer)); | ||
|
||
const result = await ethApp.getAppNameAndVersion(); | ||
expect(transportSpy).toHaveBeenCalledTimes(1); | ||
expect(transportSpy).toHaveBeenCalledWith(0xb0, 0x01, 0x00, 0x00); | ||
expect(result).toStrictEqual({ | ||
appName: '', | ||
version: '', | ||
}); | ||
}); | ||
|
||
it('throws an error when first byte is not 1', async function () { | ||
const ethApp = new MetaMaskLedgerHwAppEth( | ||
mockTransport as unknown as Transport, | ||
); | ||
|
||
const transportSpy = jest | ||
.spyOn(mockTransport, 'send') | ||
.mockImplementation(async () => Promise.resolve(Buffer.alloc(1))); | ||
|
||
await expect(ethApp.getAppNameAndVersion()).rejects.toThrow( | ||
'Incorrect format return from getAppNameAndVersion.', | ||
); | ||
expect(transportSpy).toHaveBeenCalledTimes(1); | ||
expect(transportSpy).toHaveBeenCalledWith(0xb0, 0x01, 0x00, 0x00); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import LedgerHwAppEth from '@ledgerhq/hw-app-eth'; | ||
// eslint-disable-next-line import/no-nodejs-modules | ||
import { Buffer } from 'buffer'; | ||
|
||
import { GetAppNameAndVersionResponse } from './type'; | ||
|
||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions | ||
export interface MetaMaskLedgerHwAppEth extends LedgerHwAppEth { | ||
openEthApp(): void; | ||
closeApps(): void; | ||
getAppNameAndVersion(): Promise<GetAppNameAndVersionResponse>; | ||
} | ||
|
||
export class MetaMaskLedgerHwAppEth | ||
extends LedgerHwAppEth | ||
implements MetaMaskLedgerHwAppEth | ||
{ | ||
readonly mainAppName = 'BOLOS'; | ||
|
||
readonly ethAppName = 'Ethereum'; | ||
|
||
readonly transportEncoding = 'ascii'; | ||
|
||
/** | ||
* Method to open ethereum application on ledger device. | ||
* | ||
*/ | ||
async openEthApp(): Promise<void> { | ||
await this.transport.send( | ||
0xe0, | ||
0xd8, | ||
0x00, | ||
0x00, | ||
Buffer.from(this.ethAppName, this.transportEncoding), | ||
); | ||
} | ||
|
||
/** | ||
* Method to close all running application on ledger device. | ||
* | ||
*/ | ||
async closeApps(): Promise<void> { | ||
await this.transport.send(0xb0, 0xa7, 0x00, 0x00); | ||
} | ||
|
||
/** | ||
* Method to retrieve the name and version of the running application in ledger device. | ||
* | ||
* @returns An object contains appName and version. | ||
*/ | ||
async getAppNameAndVersion(): Promise<GetAppNameAndVersionResponse> { | ||
const response = await this.transport.send(0xb0, 0x01, 0x00, 0x00); | ||
if (response[0] !== 1) { | ||
throw new Error('Incorrect format return from getAppNameAndVersion.'); | ||
} | ||
|
||
let i = 1; | ||
const nameLength = response[i] ?? 0; | ||
i += 1; | ||
|
||
const appName = response | ||
.slice(i, (i += nameLength)) | ||
.toString(this.transportEncoding); | ||
|
||
const versionLength = response[i] ?? 0; | ||
i += 1; | ||
|
||
const version = response | ||
.slice(i, (i += versionLength)) | ||
.toString(this.transportEncoding); | ||
|
||
return { | ||
appName, | ||
version, | ||
}; | ||
} | ||
} |
Oops, something went wrong.