@abcpros/bitcore-wallet-service
Version:
A service for Mutisig HD Bitcoin Wallets
140 lines (113 loc) • 3.76 kB
text/typescript
import _ from 'lodash';
const $ = require('preconditions').singleton();
const Constants = require('../common/constants');
const Utils = require('../common/utils');
export interface IAddressManager {
version: number;
derivationStrategy: string;
receiveAddressIndex: number;
changeAddressIndex: number;
copayerIndex: number;
skippedPaths: Array<{ path: string; isChange: boolean }>;
}
export class AddressManager {
version: number;
derivationStrategy: string;
receiveAddressIndex: number = 0;
changeAddressIndex: number = 0;
copayerIndex: number;
skippedPaths: Array<{ path: string; isChange: boolean }>;
static create(opts) {
opts = opts || {};
const x = new AddressManager();
x.version = 2;
x.derivationStrategy = opts.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP45;
$.checkState(
Utils.checkValueInCollection(x.derivationStrategy, Constants.DERIVATION_STRATEGIES),
'Failed state: x.derivation Stategy not in DERIVATION_STRATEGIES at <create()>'
);
x.receiveAddressIndex = 0;
x.changeAddressIndex = 0;
x.copayerIndex = _.isNumber(opts.copayerIndex) ? opts.copayerIndex : Constants.BIP45_SHARED_INDEX;
x.skippedPaths = [];
return x;
}
static fromObj(obj) {
const x = new AddressManager();
x.version = obj.version;
x.derivationStrategy = obj.derivationStrategy || Constants.DERIVATION_STRATEGIES.BIP45;
x.receiveAddressIndex = obj.receiveAddressIndex || 0;
x.changeAddressIndex = obj.changeAddressIndex || 0;
x.copayerIndex = obj.copayerIndex;
// this is not stored, only temporary.
x.skippedPaths = [];
return x;
}
static supportsCopayerBranches(derivationStrategy) {
return derivationStrategy == Constants.DERIVATION_STRATEGIES.BIP45;
}
_incrementIndex(isChange) {
if (isChange) {
this.changeAddressIndex++;
} else {
this.receiveAddressIndex++;
}
}
rewindIndex(isChange, step, n) {
step = _.isUndefined(step) ? 1 : step;
n = _.isUndefined(n) ? 1 : n;
if (isChange) {
this.changeAddressIndex = Math.max(0, this.changeAddressIndex - n * step);
} else {
this.receiveAddressIndex = Math.max(0, this.receiveAddressIndex - n * step);
}
// clear skipppedPath, since index is rewinded
// n address were actually derived.
this.skippedPaths = this.skippedPaths.splice(0, this.skippedPaths.length - step * n + n);
}
getCurrentIndex(isChange) {
return isChange ? this.changeAddressIndex : this.receiveAddressIndex;
}
getBaseAddressPath(isChange) {
return (
'm/' +
(this.derivationStrategy == Constants.DERIVATION_STRATEGIES.BIP45 ? this.copayerIndex + '/' : '') +
(isChange ? 1 : 0) +
'/' +
0
);
}
getCurrentAddressPath(isChange) {
return (
'm/' +
(this.derivationStrategy == Constants.DERIVATION_STRATEGIES.BIP45 ? this.copayerIndex + '/' : '') +
(isChange ? 1 : 0) +
'/' +
(isChange ? this.changeAddressIndex : this.receiveAddressIndex)
);
}
getNewAddressPath(isChange, step = 1) {
let ret;
let i = 0;
step = step || 1;
while (i++ < step) {
if (ret) {
this.skippedPaths.push({ path: ret, isChange });
}
ret = this.getCurrentAddressPath(isChange);
this._incrementIndex(isChange);
}
return ret;
}
getNextSkippedPath() {
if (_.isEmpty(this.skippedPaths)) return null;
const ret = this.skippedPaths.pop();
return ret;
}
parseDerivationPath(path) {
const pathIndex = /m\/([0-9]*)\/([0-9]*)/;
const [_input, changeIndex, addressIndex] = path.match(pathIndex);
const isChange = changeIndex > 0;
return { _input, addressIndex, isChange };
}
}