Skip to content

Commit

Permalink
feat: add lookahead
Browse files Browse the repository at this point in the history
  • Loading branch information
emjshrx committed Feb 18, 2024
1 parent 490be82 commit 1554033
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 8 deletions.
3 changes: 2 additions & 1 deletion src/wallet/db/db.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export type DbInterface = {
encryptedChainCode: string,
): Promise<void>;
saveAddress(address: string, path: string): Promise<void>;
getAddress(address: string): Promise<string>;
getPathFromAddress(address: string): Promise<string>;
getAddressFromPath(path: string): Promise<string>;
hasAddress(address: string): Promise<boolean>;
getReceiveDepth(): Promise<number>;
setReceiveDepth(depth: number): Promise<void>;
Expand Down
11 changes: 9 additions & 2 deletions src/wallet/db/level/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,20 @@ export class WalletDB implements DbInterface {
}

public async saveAddress(address: string, path: string): Promise<void> {
await this.db.sublevel(wdb.A).put(address, path);
await Promise.all([
this.db.sublevel(wdb.A).put(address, path),
this.db.sublevel(wdb.P).put(path, address),
]);
}

public async getAddress(address: string): Promise<string> {
public async getPathFromAddress(address: string): Promise<string> {
return await this.db.sublevel(wdb.A).get(address);
}

public async getAddressFromPath(path: string): Promise<string> {
return await this.db.sublevel(wdb.P).get(path);
}

async hasAddress(address: string): Promise<boolean> {
return (await this.db.sublevel(wdb.A).get(address)) !== undefined;
}
Expand Down
3 changes: 2 additions & 1 deletion src/wallet/db/level/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
export const wdb = {
V: 'V', // Version
M: 'M', // Master key
A: 'A', // Address
A: 'A', // Address:Path
P: 'P', // Path:Address
D: 'D', // Address depth
C: 'C', // Coins
SP: 'SP', // Silent payment address
Expand Down
20 changes: 16 additions & 4 deletions src/wallet/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,24 @@ const bip32 = BIP32Factory(ecc);
export type WalletConfigOptions = {
db: DbInterface;
networkClient: NetworkInterface;
lookahead?: number;
};

const DEFAULT_ENCRYPTION_PASSWORD = '12345678';
const DEFAULT_LOOKAHEAD = 10;

export class Wallet {
private readonly db: DbInterface;
private readonly network: NetworkInterface;
private masterKey: BIP32Interface;
private receiveDepth: number = 0;
private changeDepth: number = 0;
private lookahead: number;

constructor(config: WalletConfigOptions) {
this.db = config.db;
this.network = config.networkClient;
this.lookahead = config.lookahead ?? DEFAULT_LOOKAHEAD;
}

async init(params?: { mnemonic?: string; password?: string }) {
Expand All @@ -44,6 +48,9 @@ export class Wallet {
const seed = mnemonicToSeedSync(mnemonic).toString('hex');
this.masterKey = bip32.fromSeed(Buffer.from(seed, 'hex'));
this.setPassword(password ?? DEFAULT_ENCRYPTION_PASSWORD);
for (let i = 0; i < this.lookahead; i++) {
await this.deriveAddress(`m/84'/0'/0'/0/${i}`);
}
} else {
const { encryptedPrivateKey, encryptedChainCode } =
await this.db.getMasterKey();
Expand Down Expand Up @@ -102,8 +109,11 @@ export class Wallet {
}

async deriveReceiveAddress(): Promise<string> {
const path = `m/84'/0'/0'/0/${this.receiveDepth}`;
const address = await this.deriveAddress(path);
const nextPath = `m/84'/0'/0'/0/${this.receiveDepth + this.lookahead}`;
await this.deriveAddress(nextPath);
const address = await this.db.getAddressFromPath(
`m/84'/0'/0'/0/${this.receiveDepth}`,
);
this.receiveDepth++;
return address;
}
Expand Down Expand Up @@ -133,7 +143,7 @@ export class Wallet {

private async signTransaction(psbt: Psbt, coins: Coin[]): Promise<void> {
for (let index = 0; index < coins.length; index++) {
const path = await this.db.getAddress(coins[index].address);
const path = await this.db.getPathFromAddress(coins[index].address);
const privateKey = this.masterKey.derivePath(path);
psbt.signInput(index, privateKey);
}
Expand Down Expand Up @@ -237,7 +247,9 @@ export class Wallet {

const privateKeys = (
await Promise.all(
selectedCoins.map((coin) => this.db.getAddress(coin.address)),
selectedCoins.map((coin) =>
this.db.getPathFromAddress(coin.address),
),
)
).map((path) => this.masterKey.derivePath(path));
const outpoints = selectedCoins.map((coin) => ({
Expand Down

0 comments on commit 1554033

Please sign in to comment.