Skip to content

Commit

Permalink
Merge pull request #148 from CityOfZion/CU-86a6xp22g
Browse files Browse the repository at this point in the history
CU-86a6xp22g - BSLib - Ledger/Mnemonic - Add functionality to generat…
  • Loading branch information
thiagocbalducci authored Feb 24, 2025
2 parents f84829d + bc51622 commit b3f88df
Show file tree
Hide file tree
Showing 13 changed files with 197 additions and 31 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/blockchain-service",
"comment": "Add functionality to generate address until a specific index",
"type": "minor"
}
],
"packageName": "@cityofzion/blockchain-service"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/bs-ethereum",
"comment": "Add functionality to generate address until a specific index",
"type": "minor"
}
],
"packageName": "@cityofzion/bs-ethereum"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/bs-neo-legacy",
"comment": "Add functionality to generate address until a specific index",
"type": "minor"
}
],
"packageName": "@cityofzion/bs-neo-legacy"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@cityofzion/bs-neo3",
"comment": "Add functionality to generate address until a specific index",
"type": "minor"
}
],
"packageName": "@cityofzion/bs-neo3"
}
16 changes: 13 additions & 3 deletions packages/blockchain-service/src/BSAggregator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchAccountsForBlockchainServices } from './functions'
import { fetchAccountsForBlockchainServices, generateAccountUntilIndexForBlockchainService } from './functions'
import { Account, BlockchainService } from './interfaces'

export class BSAggregator<BSName extends string = string> {
Expand Down Expand Up @@ -51,9 +51,19 @@ export class BSAggregator<BSName extends string = string> {
return this.#blockchainServices.filter(bs => bs.validateEncrypted(keyOrJson)).map(bs => bs.name)
}

async generateAccountsFromMnemonic(mnemonic: string): Promise<Map<BSName, Account<BSName>[]>> {
return fetchAccountsForBlockchainServices(
async generateAccountsFromMnemonic(mnemonic: string, untilIndex?: number): Promise<Map<BSName, Account<BSName>[]>> {
if (untilIndex === undefined) {
return fetchAccountsForBlockchainServices(
this.#blockchainServices,
async (service: BlockchainService<BSName>, index: number) => {
return service.generateAccountFromMnemonic(mnemonic, index)
}
)
}

return generateAccountUntilIndexForBlockchainService(
this.#blockchainServices,
untilIndex,
async (service: BlockchainService<BSName>, index: number) => {
return service.generateAccountFromMnemonic(mnemonic, index)
}
Expand Down
31 changes: 30 additions & 1 deletion packages/blockchain-service/src/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,36 @@ export async function fetchAccountsForBlockchainServices<BSName extends string =
accountsByBlockchainService.set(service.name, accounts)
})

await Promise.all(promises)
await Promise.allSettled(promises)

return accountsByBlockchainService
}

export async function generateAccountUntilIndexForBlockchainService<BSName extends string = string>(
blockchainServices: BlockchainService<BSName>[],
untilIndex: number,
getAccountCallback: (service: BlockchainService<BSName>, index: number) => Promise<Account<BSName>>
): Promise<Map<BSName, Account<BSName>[]>> {
if (untilIndex < 0) {
throw new Error('Invalid index')
}

const accountsByBlockchainService = new Map<BSName, Account<BSName>[]>()

const promises = blockchainServices.map(async service => {
let index = 0
const accounts: Account<BSName>[] = []

while (index <= untilIndex) {
const generatedAccount = await getAccountCallback(service, index)
accounts.push(generatedAccount)
index++
}

accountsByBlockchainService.set(service.name, accounts)
})

await Promise.allSettled(promises)

return accountsByBlockchainService
}
Expand Down
2 changes: 1 addition & 1 deletion packages/blockchain-service/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export type GetLedgerTransport<BSName extends string = string> = (account: Accou
export interface LedgerService<BSName extends string = string> {
emitter: LedgerServiceEmitter
getLedgerTransport?: GetLedgerTransport<BSName>
getAccounts(transport: Transport): Promise<Account<BSName>[]>
getAccounts(transport: Transport, untilIndex?: number): Promise<Account<BSName>[]>
getAccount(transport: Transport, index: number): Promise<Account<BSName>>
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ describe.skip('EthersLedgerServiceEthereum', () => {
expect(signatureAddress).toEqual(address)
}, 60000)

it('Should be able to get all accounts', async () => {
it('Should be able to get all accounts automatically', async () => {
const accounts = await ledgerService.getAccounts(transport)
expect(accounts.length).toBeGreaterThan(1)

Expand All @@ -135,6 +135,22 @@ describe.skip('EthersLedgerServiceEthereum', () => {
})
}, 60000)

it('Should be able to get all accounts until index', async () => {
const accounts = await ledgerService.getAccounts(transport, 6)

expect(accounts.length).toBe(7)
accounts.forEach((account, index) => {
expect(account).toEqual(
expect.objectContaining({
address: expect.any(String),
key: expect.any(String),
type: 'publicKey',
bip44Path: bsEthereum.bip44DerivationPath.replace('?', index.toString()),
})
)
})
}, 60000)

it('Should be able to get account', async () => {
const account = await ledgerService.getAccount(transport, 0)
expect(account).toEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
LedgerServiceEmitter,
fetchAccountsForBlockchainServices,
GetLedgerTransport,
generateAccountUntilIndexForBlockchainService,
} from '@cityofzion/blockchain-service'
import Transport from '@ledgerhq/hw-transport'
import LedgerEthereumApp, { ledgerService as LedgerEthereumAppService } from '@ledgerhq/hw-app-eth'
Expand Down Expand Up @@ -161,13 +162,25 @@ export class EthersLedgerServiceEthereum<BSName extends string = string> impleme
this.getLedgerTransport = getLedgerTransport
}

async getAccounts(transport: Transport): Promise<Account<BSName>[]> {
const accountsByBlockchainService = await fetchAccountsForBlockchainServices(
[this.#blockchainService],
async (_service, index) => {
return this.getAccount(transport, index)
}
)
async getAccounts(transport: Transport, untilIndex?: number): Promise<Account<BSName>[]> {
let accountsByBlockchainService: Map<string, Account<BSName>[]>

if (untilIndex === undefined) {
accountsByBlockchainService = await fetchAccountsForBlockchainServices(
[this.#blockchainService],
async (_service, index) => {
return this.getAccount(transport, index)
}
)
} else {
accountsByBlockchainService = await generateAccountUntilIndexForBlockchainService(
[this.#blockchainService],
untilIndex,
async (_service, index) => {
return this.getAccount(transport, index)
}
)
}

const accounts = accountsByBlockchainService.get(this.#blockchainService.name)
return accounts ?? []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe.skip('NeonJsLedgerServiceNeoLegacy', () => {
ledgerService = new NeonJsLedgerServiceNeoLegacy(bsNeoLegacy, async () => transport)
}, 60000)

it('Should be able to get all accounts', async () => {
it('Should be able to get all accounts automatically', async () => {
const accounts = await ledgerService.getAccounts(transport)
expect(accounts.length).toBeGreaterThan(1)

Expand All @@ -33,6 +33,22 @@ describe.skip('NeonJsLedgerServiceNeoLegacy', () => {
})
}, 60000)

it('Should be able to get all accounts until index', async () => {
const accounts = await ledgerService.getAccounts(transport, 6)

expect(accounts.length).toBe(7)
accounts.forEach((account, index) => {
expect(account).toEqual(
expect.objectContaining({
address: expect.any(String),
key: expect.any(String),
type: 'publicKey',
bip44Path: bsNeoLegacy.bip44DerivationPath.replace('?', index.toString()),
})
)
})
}, 60000)

it('Should be able to get account', async () => {
const account = await ledgerService.getAccount(transport, 0)
expect(account).toEqual(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Account,
fetchAccountsForBlockchainServices,
generateAccountUntilIndexForBlockchainService,
GetLedgerTransport,
LedgerService,
LedgerServiceEmitter,
Expand Down Expand Up @@ -59,13 +60,25 @@ export class NeonJsLedgerServiceNeoLegacy<BSName extends string = string> implem
}
}

async getAccounts(transport: Transport): Promise<Account<BSName>[]> {
const accountsByBlockchainService = await fetchAccountsForBlockchainServices<BSName>(
[this.#blockchainService],
async (_service, index) => {
return this.getAccount(transport, index)
}
)
async getAccounts(transport: Transport, untilIndex?: number): Promise<Account<BSName>[]> {
let accountsByBlockchainService: Map<string, Account<BSName>[]>

if (untilIndex === undefined) {
accountsByBlockchainService = await fetchAccountsForBlockchainServices(
[this.#blockchainService],
async (_service, index) => {
return this.getAccount(transport, index)
}
)
} else {
accountsByBlockchainService = await generateAccountUntilIndexForBlockchainService(
[this.#blockchainService],
untilIndex,
async (_service, index) => {
return this.getAccount(transport, index)
}
)
}

const accounts = accountsByBlockchainService.get(this.#blockchainService.name)
return accounts ?? []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { BSNeo3 } from '../../../BSNeo3'
import TransportNodeHid from '@ledgerhq/hw-transport-node-hid'
import { BSNeo3Constants } from '../../../constants/BSNeo3Constants'

let ledgerService: NeonDappKitLedgerServiceNeo3
let ledgerService: NeonDappKitLedgerServiceNeo3<'neo3'>
let transport: Transport
let bsNeo3: BSNeo3
let bsNeo3: BSNeo3<'neo3'>

describe.skip('NeonDappKitLedgerServiceNeo3.spec', () => {
beforeAll(async () => {
Expand All @@ -17,7 +17,7 @@ describe.skip('NeonDappKitLedgerServiceNeo3.spec', () => {
ledgerService = new NeonDappKitLedgerServiceNeo3(bsNeo3, async () => transport)
}, 60000)

it('Should be able to get all accounts', async () => {
it('Should be able to get all accounts automatically', async () => {
const accounts = await ledgerService.getAccounts(transport)
expect(accounts.length).toBeGreaterThan(1)

Expand All @@ -33,6 +33,22 @@ describe.skip('NeonDappKitLedgerServiceNeo3.spec', () => {
})
}, 60000)

it('Should be able to get all accounts until index', async () => {
const accounts = await ledgerService.getAccounts(transport, 6)

expect(accounts.length).toBe(7)
accounts.forEach((account, index) => {
expect(account).toEqual(
expect.objectContaining({
address: expect.any(String),
key: expect.any(String),
type: 'publicKey',
bip44Path: bsNeo3.bip44DerivationPath.replace('?', index.toString()),
})
)
})
}, 60000)

it('Should be able to get account', async () => {
const account = await ledgerService.getAccount(transport, 0)
expect(account).toEqual(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Account,
fetchAccountsForBlockchainServices,
generateAccountUntilIndexForBlockchainService,
GetLedgerTransport,
LedgerService,
LedgerServiceEmitter,
Expand Down Expand Up @@ -61,13 +62,25 @@ export class NeonDappKitLedgerServiceNeo3<BSName extends string = string> implem
}
}

async getAccounts(transport: Transport): Promise<Account<BSName>[]> {
const accountsByBlockchainService = await fetchAccountsForBlockchainServices<BSName>(
[this.#blockchainService],
async (_service, index) => {
return this.getAccount(transport, index)
}
)
async getAccounts(transport: Transport, untilIndex?: number): Promise<Account<BSName>[]> {
let accountsByBlockchainService: Map<string, Account<BSName>[]>

if (untilIndex === undefined) {
accountsByBlockchainService = await fetchAccountsForBlockchainServices(
[this.#blockchainService],
async (_service, index) => {
return this.getAccount(transport, index)
}
)
} else {
accountsByBlockchainService = await generateAccountUntilIndexForBlockchainService(
[this.#blockchainService],
untilIndex,
async (_service, index) => {
return this.getAccount(transport, index)
}
)
}

const accounts = accountsByBlockchainService.get(this.#blockchainService.name)
return accounts ?? []
Expand Down

0 comments on commit b3f88df

Please sign in to comment.