bip45 wallet
This commit is contained in:
parent
28a2cab787
commit
eacd1e2ece
@ -42,18 +42,6 @@ function Address(options) {
|
|||||||
compressed: options.compressed
|
compressed: options.compressed
|
||||||
});
|
});
|
||||||
|
|
||||||
// Compatability
|
|
||||||
if (options.multisig) {
|
|
||||||
if (options.multisig.type)
|
|
||||||
options.type = options.multisig.type;
|
|
||||||
if (options.multisig.keys)
|
|
||||||
options.keys = options.multisig.keys;
|
|
||||||
if (options.multisig.m)
|
|
||||||
options.m = options.multisig.m;
|
|
||||||
if (options.multisig.n)
|
|
||||||
options.n = options.multisig.n;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.type = options.type || 'pubkeyhash';
|
this.type = options.type || 'pubkeyhash';
|
||||||
this.subtype = options.subtype;
|
this.subtype = options.subtype;
|
||||||
this.keys = [];
|
this.keys = [];
|
||||||
@ -141,8 +129,8 @@ Address.prototype.removeKey = function removeKey(key) {
|
|||||||
|
|
||||||
key = utils.toBuffer(key);
|
key = utils.toBuffer(key);
|
||||||
|
|
||||||
var index = this.keys.map(function(key, i) {
|
var index = this.keys.map(function(pub, i) {
|
||||||
return utils.isEqual(key, pub) ? i : null;
|
return utils.isEqual(pub, key) ? i : null;
|
||||||
}).filter(function(i) {
|
}).filter(function(i) {
|
||||||
return i !== null;
|
return i !== null;
|
||||||
})[0];
|
})[0];
|
||||||
@ -226,9 +214,6 @@ Address.prototype.getScriptAddress = function getScriptAddress() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Address.prototype.getPublicKey = function getPublicKey(enc) {
|
Address.prototype.getPublicKey = function getPublicKey(enc) {
|
||||||
if (!this.key.priv)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!enc) {
|
if (!enc) {
|
||||||
if (this._pub)
|
if (this._pub)
|
||||||
return this._pub;
|
return this._pub;
|
||||||
@ -241,6 +226,32 @@ Address.prototype.getPublicKey = function getPublicKey(enc) {
|
|||||||
return this.key.getPublic(enc);
|
return this.key.getPublic(enc);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Address.prototype.getHDPublicKey = function getPublicKey(enc) {
|
||||||
|
if (!this.key.hd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (enc === 'base58')
|
||||||
|
return this.key.hd.xpubkey;
|
||||||
|
|
||||||
|
if (this.key.hd.isPublic)
|
||||||
|
return this.key.hd;
|
||||||
|
|
||||||
|
return this.key.hd.hdpub;
|
||||||
|
};
|
||||||
|
|
||||||
|
Address.prototype.getHDPrivateKey = function getPublicKey(enc) {
|
||||||
|
if (!this.key.hd)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.key.hd.isPrivate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (enc === 'base58')
|
||||||
|
return this.key.hd.xprivkey;
|
||||||
|
|
||||||
|
return this.key.hd;
|
||||||
|
};
|
||||||
|
|
||||||
Address.prototype.getKeyHash = function getKeyHash() {
|
Address.prototype.getKeyHash = function getKeyHash() {
|
||||||
if (this._hash)
|
if (this._hash)
|
||||||
return this._hash;
|
return this._hash;
|
||||||
@ -521,6 +532,20 @@ Address.prototype.__defineGetter__('address', function() {
|
|||||||
return this.getAddress();
|
return this.getAddress();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Address.prototype.__defineGetter__('realType', function() {
|
||||||
|
if (this.type === 'scripthash')
|
||||||
|
return this.subtype;
|
||||||
|
return this.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
Address.prototype.__defineGetter__('hdPrivateKey', function() {
|
||||||
|
return this.getHDPrivateKey();
|
||||||
|
});
|
||||||
|
|
||||||
|
Address.prototype.__defineGetter__('hdPublicKey', function() {
|
||||||
|
return this.getHDPublicKey();
|
||||||
|
});
|
||||||
|
|
||||||
Address.prototype.toJSON = function toJSON(encrypt) {
|
Address.prototype.toJSON = function toJSON(encrypt) {
|
||||||
return {
|
return {
|
||||||
v: 1,
|
v: 1,
|
||||||
|
|||||||
@ -252,12 +252,19 @@ HDPrivateKey.prototype.scan44 = function scan44(options, txByAddress, callback)
|
|||||||
})(0);
|
})(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.deriveRoot44 = function deriveRoot44(options) {
|
HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(options) {
|
||||||
var coinType = options.coinType;
|
var coinType, accountIndex, child;
|
||||||
var accountIndex = options.accountIndex;
|
|
||||||
|
|
||||||
if (this instanceof HDPublicKey)
|
if (typeof options === 'number')
|
||||||
|
options = { accountIndex: options };
|
||||||
|
|
||||||
|
coinType = options.coinType;
|
||||||
|
accountIndex = options.accountIndex;
|
||||||
|
|
||||||
|
if (this instanceof HDPublicKey) {
|
||||||
|
assert(this.isAccount44());
|
||||||
return this;
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
if (coinType == null)
|
if (coinType == null)
|
||||||
coinType = network.type === 'main' ? 0 : 1;
|
coinType = network.type === 'main' ? 0 : 1;
|
||||||
@ -265,13 +272,17 @@ HDPrivateKey.prototype.deriveRoot44 = function deriveRoot44(options) {
|
|||||||
assert(utils.isFinite(coinType));
|
assert(utils.isFinite(coinType));
|
||||||
assert(utils.isFinite(accountIndex));
|
assert(utils.isFinite(accountIndex));
|
||||||
|
|
||||||
return this
|
child = this
|
||||||
.derive(44, true)
|
.derive(44, true)
|
||||||
.derive(coinType, true)
|
.derive(coinType, true)
|
||||||
.derive(accountIndex, true);
|
.derive(accountIndex, true);
|
||||||
|
|
||||||
|
assert(child.isAccount44());
|
||||||
|
|
||||||
|
return child;
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.deriveBIP44 = function deriveBIP44(options, isPublic) {
|
HDPrivateKey.prototype.deriveBIP44 = function deriveBIP44(options) {
|
||||||
var chain = options.chain;
|
var chain = options.chain;
|
||||||
var addressIndex = options.addressIndex;
|
var addressIndex = options.addressIndex;
|
||||||
|
|
||||||
@ -282,7 +293,7 @@ HDPrivateKey.prototype.deriveBIP44 = function deriveBIP44(options, isPublic) {
|
|||||||
assert(utils.isFinite(addressIndex));
|
assert(utils.isFinite(addressIndex));
|
||||||
|
|
||||||
return this
|
return this
|
||||||
.deriveRoot44(options)
|
.deriveAccount44(options)
|
||||||
.derive(chain)
|
.derive(chain)
|
||||||
.derive(addressIndex);
|
.derive(addressIndex);
|
||||||
};
|
};
|
||||||
@ -317,7 +328,7 @@ HDPrivateKey.prototype.scan45 = function scan45(options, txByAddress, callback)
|
|||||||
var keys = [];
|
var keys = [];
|
||||||
var root;
|
var root;
|
||||||
|
|
||||||
root = this.deriveRoot45(options);
|
root = this.derivePurpose45(options);
|
||||||
|
|
||||||
return (function chainCheck(chainConstant) {
|
return (function chainCheck(chainConstant) {
|
||||||
return (function scanner(cosignerIndex) {
|
return (function scanner(cosignerIndex) {
|
||||||
@ -372,10 +383,19 @@ HDPrivateKey.prototype.scan45 = function scan45(options, txByAddress, callback)
|
|||||||
})(0);
|
})(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.deriveRoot45 = function deriveRoot45(options) {
|
HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45(options) {
|
||||||
if (this instanceof HDPublicKey)
|
var child;
|
||||||
|
|
||||||
|
if (this instanceof HDPublicKey) {
|
||||||
|
assert(this.isPurpose45());
|
||||||
return this;
|
return this;
|
||||||
return this.derive(45, true);
|
}
|
||||||
|
|
||||||
|
child = this.derive(45, true);
|
||||||
|
|
||||||
|
assert(child.isPurpose45());
|
||||||
|
|
||||||
|
return child;
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.deriveBIP45 = function deriveBIP45(options) {
|
HDPrivateKey.prototype.deriveBIP45 = function deriveBIP45(options) {
|
||||||
@ -391,7 +411,7 @@ HDPrivateKey.prototype.deriveBIP45 = function deriveBIP45(options) {
|
|||||||
assert(utils.isFinite(addressIndex));
|
assert(utils.isFinite(addressIndex));
|
||||||
|
|
||||||
return this
|
return this
|
||||||
.deriveRoot45(options)
|
.derivePurpose45(options)
|
||||||
.derive(cosignerIndex)
|
.derive(cosignerIndex)
|
||||||
.derive(chain)
|
.derive(chain)
|
||||||
.derive(addressIndex);
|
.derive(addressIndex);
|
||||||
@ -413,6 +433,14 @@ HDPrivateKey.prototype.deriveCosignerAddress = function deriveCosignerAddress(co
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HDPrivateKey.prototype.isPurpose45 = function isPurpose45(options) {
|
||||||
|
return new bn(this.childIndex).toNumber() === constants.hd.hardened + 45;
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPrivateKey.prototype.isAccount44 = function isAccount44(options) {
|
||||||
|
return new bn(this.depth).toNumber() === 3;
|
||||||
|
};
|
||||||
|
|
||||||
HDPrivateKey.getPath = function getPath(options) {
|
HDPrivateKey.getPath = function getPath(options) {
|
||||||
var purpose, coinType, accountIndex, chain, addressIndex;
|
var purpose, coinType, accountIndex, chain, addressIndex;
|
||||||
|
|
||||||
@ -765,17 +793,20 @@ function HDPublicKey(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HDPublicKey.prototype.scan44 = HDPrivateKey.prototype.scan44;
|
HDPublicKey.prototype.scan44 = HDPrivateKey.prototype.scan44;
|
||||||
HDPublicKey.prototype.deriveRoot44 = HDPrivateKey.prototype.deriveRoot44;
|
HDPublicKey.prototype.deriveAccount44 = HDPrivateKey.prototype.deriveAccount44;
|
||||||
HDPublicKey.prototype.deriveBIP44 = HDPrivateKey.prototype.deriveBIP44;
|
HDPublicKey.prototype.deriveBIP44 = HDPrivateKey.prototype.deriveBIP44;
|
||||||
HDPublicKey.prototype.deriveChange = HDPrivateKey.prototype.deriveChange;
|
HDPublicKey.prototype.deriveChange = HDPrivateKey.prototype.deriveChange;
|
||||||
HDPublicKey.prototype.deriveAddress = HDPrivateKey.prototype.deriveAddress;
|
HDPublicKey.prototype.deriveAddress = HDPrivateKey.prototype.deriveAddress;
|
||||||
|
|
||||||
HDPublicKey.prototype.scan45 = HDPrivateKey.prototype.scan45;
|
HDPublicKey.prototype.scan45 = HDPrivateKey.prototype.scan45;
|
||||||
HDPublicKey.prototype.deriveRoot45 = HDPrivateKey.prototype.deriveRoot45;
|
HDPublicKey.prototype.derivePurpose45 = HDPrivateKey.prototype.derivePurpose45;
|
||||||
HDPublicKey.prototype.deriveBIP45 = HDPrivateKey.prototype.deriveBIP45;
|
HDPublicKey.prototype.deriveBIP45 = HDPrivateKey.prototype.deriveBIP45;
|
||||||
HDPublicKey.prototype.deriveCosignerChange = HDPrivateKey.prototype.deriveCosignerChange;
|
HDPublicKey.prototype.deriveCosignerChange = HDPrivateKey.prototype.deriveCosignerChange;
|
||||||
HDPublicKey.prototype.deriveCosignerAddress = HDPrivateKey.prototype.deriveCosignerAddress;
|
HDPublicKey.prototype.deriveCosignerAddress = HDPrivateKey.prototype.deriveCosignerAddress;
|
||||||
|
|
||||||
|
HDPublicKey.prototype.isPurpose45 = HDPrivateKey.prototype.isPurpose45;
|
||||||
|
HDPublicKey.prototype.isAccount44 = HDPrivateKey.prototype.isAccount44;
|
||||||
|
|
||||||
HDPublicKey.isExtended = function isExtended(data) {
|
HDPublicKey.isExtended = function isExtended(data) {
|
||||||
if (typeof data !== 'string')
|
if (typeof data !== 'string')
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -33,6 +33,12 @@ function KeyPair(options) {
|
|||||||
this.hd = options.hd || null;
|
this.hd = options.hd || null;
|
||||||
this.compressed = options.compressed !== false;
|
this.compressed = options.compressed !== false;
|
||||||
|
|
||||||
|
if (options.privateKey)
|
||||||
|
options.priv = options.privateKey;
|
||||||
|
|
||||||
|
if (options.publicKey)
|
||||||
|
options.pub = options.publicKey;
|
||||||
|
|
||||||
if (options.priv instanceof bcoin.hd.priv) {
|
if (options.priv instanceof bcoin.hd.priv) {
|
||||||
this.hd = options.priv;
|
this.hd = options.priv;
|
||||||
this._key = options.priv.pair;
|
this._key = options.priv.pair;
|
||||||
|
|||||||
@ -763,6 +763,12 @@ utils.sortKeys = function sortKeys(keys) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
utils.sortHDKeys = function sortHDKeys(keys) {
|
||||||
|
return keys.slice().sort(function(a, b) {
|
||||||
|
return new bn(a.publicKey).cmp(new bn(b.publicKey)) > 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
utils.uniq = function(obj) {
|
utils.uniq = function(obj) {
|
||||||
var out = [];
|
var out = [];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|||||||
@ -31,67 +31,323 @@ function Wallet(options) {
|
|||||||
|
|
||||||
options = utils.merge({}, options);
|
options = utils.merge({}, options);
|
||||||
|
|
||||||
|
if (options.hd) {
|
||||||
|
options.master = options.hd !== true
|
||||||
|
? bcoin.hd.priv(options.hd)
|
||||||
|
: bcoin.hd.priv();
|
||||||
|
delete options.hd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.priv
|
||||||
|
|| options.pub
|
||||||
|
|| options.key
|
||||||
|
|| options.personalization
|
||||||
|
|| options.entropy
|
||||||
|
|| options.compressed) {
|
||||||
|
if ((options.key instanceof bcoin.hd.priv)
|
||||||
|
|| options.key instanceof bcoin.hd.pub) {
|
||||||
|
options.master = options.key;
|
||||||
|
delete options.key;
|
||||||
|
} else if (options.priv instanceof bcoin.hd.priv) {
|
||||||
|
options.master = options.priv;
|
||||||
|
delete options.priv;
|
||||||
|
} else if (options.pub instanceof bcoin.hd.pub) {
|
||||||
|
options.master = options.pub;
|
||||||
|
delete options.pub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.addresses = [];
|
this.addresses = [];
|
||||||
this.master = options.master || null;
|
this.master = options.master || null;
|
||||||
|
|
||||||
|
if (this.master && !(this.master instanceof bcoin.keypair))
|
||||||
|
this.master = bcoin.keypair({ hd: this.master });
|
||||||
|
|
||||||
this._addressTable = {};
|
this._addressTable = {};
|
||||||
this._labelMap = {};
|
this._labelMap = {};
|
||||||
|
|
||||||
this.accountIndex = options.accountIndex || 0;
|
this.accountIndex = options.accountIndex || 0;
|
||||||
this.addressDepth = options.addressDepth || 0;
|
this.addressDepth = options.addressDepth || 0;
|
||||||
this.changeDepth = options.changeDepth || 0;
|
this.changeDepth = options.changeDepth || 0;
|
||||||
|
this.cosignerIndex = 0;
|
||||||
|
this.purposeKeys = options.purposeKeys || [];
|
||||||
|
this.keys = options.keys || [];
|
||||||
|
|
||||||
|
this.hd = false;
|
||||||
|
this.hdpm = false;
|
||||||
|
this.multisig = false;
|
||||||
|
|
||||||
|
this.type = options.type || 'pubkeyhash';
|
||||||
|
this.subtype = options.subtype;
|
||||||
|
this.keys = [];
|
||||||
|
this.m = options.m || 1;
|
||||||
|
this.n = options.n || 1;
|
||||||
|
this.nmax = this.type === 'scripthash'
|
||||||
|
? (this.compressed !== false ? 15 : 7)
|
||||||
|
: 3;
|
||||||
|
|
||||||
|
if (this.n > 1) {
|
||||||
|
if (this.type !== 'multisig')
|
||||||
|
this.type = 'scripthash';
|
||||||
|
if (this.type === 'scripthash')
|
||||||
|
this.subtype = 'multisig';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.master)
|
||||||
|
this.hd = true;
|
||||||
|
|
||||||
|
if (this.master && this.type === 'scripthash' && this.subtype === 'multisig')
|
||||||
|
this.hdpm = true;
|
||||||
|
|
||||||
|
if (this.type === 'multisig' || this.subtype === 'multisig')
|
||||||
|
this.multisig = true;
|
||||||
|
|
||||||
|
if (network.prefixes[this.type] == null)
|
||||||
|
throw new Error('Unknown prefix: ' + this.type);
|
||||||
|
|
||||||
|
if (this.m < 1 || this.m > this.n)
|
||||||
|
throw new Error('m ranges between 1 and n');
|
||||||
|
|
||||||
|
if (this.n < 1 || this.n > this.nmax)
|
||||||
|
throw new Error('n ranges between 1 and ' + this.nmax);
|
||||||
|
|
||||||
|
if (this.hdpm) {
|
||||||
|
this.purposeKey = this.master.hd.isPublic
|
||||||
|
? this.master.hd
|
||||||
|
: this.master.hd.derivePurpose45();
|
||||||
|
} else if (this.hd) {
|
||||||
|
this.accountKey = this.master.hd.isPublic
|
||||||
|
? this.master.hd
|
||||||
|
: this.master.hd.deriveAccount44(this.accountIndex);
|
||||||
|
}
|
||||||
|
|
||||||
if (!options.addresses)
|
if (!options.addresses)
|
||||||
options.addresses = [];
|
options.addresses = [];
|
||||||
|
|
||||||
if (!options.addresses.length)
|
if (options.priv
|
||||||
options.addresses.push(utils.merge({}, options));
|
|| options.pub
|
||||||
|
|| options.key
|
||||||
options.addresses.forEach(function(address) {
|
|| options.personalization
|
||||||
this.addAddress(address);
|
|| options.entropy
|
||||||
}, this);
|
|| options.compressed) {
|
||||||
|
options.addresses.push({
|
||||||
// Create a non-master account address if we don't have one.
|
priv: options.priv,
|
||||||
if (this.master) {
|
pub: options.pub,
|
||||||
for (i = 0; i < this.addresses.length; i++) {
|
key: options.key,
|
||||||
if (this.addresses[i].key.hd && !this.addresses[i].change)
|
personalization: options.personalization,
|
||||||
break;
|
entropy: options.entropy,
|
||||||
}
|
compressed: options.compressed,
|
||||||
if (i === this.addresses.length)
|
type: this.type,
|
||||||
this.createNewAddress(this._cleanOptions(options.addresses[0]));
|
subtype: this.subtype,
|
||||||
|
m: this.m,
|
||||||
|
n: this.n,
|
||||||
|
keys: [],
|
||||||
|
change: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the last change address if there is one.
|
|
||||||
for (i = this.addresses.length - 1; i >= 0; i--) {
|
|
||||||
if (this.addresses[i].change)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i === -1)
|
|
||||||
this.changeAddress = this.createChangeAddress();
|
|
||||||
else
|
|
||||||
this.changeAddress = this.addresses[i];
|
|
||||||
|
|
||||||
this.storage = options.storage;
|
this.storage = options.storage;
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.lastTs = 0;
|
this.lastTs = 0;
|
||||||
|
|
||||||
|
if (!this.hdpm) {
|
||||||
|
if (options.addresses.length) {
|
||||||
|
this.current = bcoin.address(options.addresses[options.addresses.length - 1]);
|
||||||
|
this._firstKey = {
|
||||||
|
priv: this.current.key._key.getPrivate().toArray(),
|
||||||
|
pub: this.current.key._key.getPublic(true, 'array')
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
this._firstKey = this.createKey(false, Math.max(0, this.addressDepth - 1));
|
||||||
|
this.current = bcoin.address({ priv: this._firstKey.priv });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hdpm)
|
||||||
|
this.addKey(this.purposeKey);
|
||||||
|
else
|
||||||
|
this.addKey(this.current.publicKey);
|
||||||
|
|
||||||
|
(options.keys || []).forEach(function(key) {
|
||||||
|
this.addKey(key);
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(Wallet, EventEmitter);
|
||||||
|
|
||||||
|
Wallet.prototype._initAddresses = function() {
|
||||||
|
var options = this.options;
|
||||||
|
|
||||||
|
options.addresses.forEach(function(address) {
|
||||||
|
address = this.addAddress(address);
|
||||||
|
if (!address.change)
|
||||||
|
this.current = address;
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
if (this.hd) {
|
||||||
|
for (i = 0; i < this.addressDepth; i++)
|
||||||
|
this.createAddress(false, i);
|
||||||
|
|
||||||
|
for (i = 0; i < this.changeDepth; i++)
|
||||||
|
this.createAddress(true, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a non-master account address if we don't have one.
|
||||||
|
if (this.addresses.length === 0)
|
||||||
|
this.createAddress();
|
||||||
|
|
||||||
|
// Find the last change address if there is one.
|
||||||
|
if (this.hd) {
|
||||||
|
if (this.changeDepth === 0)
|
||||||
|
this.changeAddress = this.createAddress(true);
|
||||||
|
else
|
||||||
|
this.changeAddress = this.addresses[this.addresses.length - 1];
|
||||||
|
} else {
|
||||||
|
for (i = this.addresses.length - 1; i >= 0; i--) {
|
||||||
|
if (this.addresses[i].change)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i === -1)
|
||||||
|
this.changeAddress = this.createAddress(true);
|
||||||
|
else
|
||||||
|
this.changeAddress = this.addresses[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(this.current);
|
||||||
|
assert(!this.current.change);
|
||||||
|
assert(this.changeAddress.change);
|
||||||
|
|
||||||
this.prefix = 'bt/wallet/' + this.getKeyAddress() + '/';
|
this.prefix = 'bt/wallet/' + this.getKeyAddress() + '/';
|
||||||
|
|
||||||
this.tx = new bcoin.txPool(this);
|
this.tx = new bcoin.txPool(this);
|
||||||
|
|
||||||
this._init();
|
this._init();
|
||||||
}
|
};
|
||||||
|
|
||||||
inherits(Wallet, EventEmitter);
|
Wallet.prototype.addKey = function addKey(key) {
|
||||||
|
var hdKey, has, i;
|
||||||
|
|
||||||
Wallet.prototype._cleanOptions = function _cleanOptions(options) {
|
if (bcoin.hd.priv.isExtended(key))
|
||||||
return utils.merge(options, {
|
key = bcoin.hd.priv(key);
|
||||||
key: null,
|
else if (bcoin.hd.pub.isExtended(key))
|
||||||
priv: null,
|
key = bcoin.hd.pub(key);
|
||||||
pub: null,
|
|
||||||
hd: null
|
if (key instanceof bcoin.keypair)
|
||||||
|
key = key.hd;
|
||||||
|
|
||||||
|
if (key instanceof bcoin.hd.priv)
|
||||||
|
key = key.hdpub;
|
||||||
|
|
||||||
|
if (key instanceof bcoin.hd.pub) {
|
||||||
|
hdKey = key;
|
||||||
|
key = hdKey.publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hdpm) {
|
||||||
|
if (!hdKey || !hdKey.isPurpose45())
|
||||||
|
throw new Error('Must add HD purpose keys to HD wallet.');
|
||||||
|
|
||||||
|
has = this.purposeKeys.some(function(pub) {
|
||||||
|
return pub.xpubkey === hdKey.xpubkey;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (has)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.purposeKeys.push(hdKey);
|
||||||
|
|
||||||
|
if (this.purposeKeys.length === this.n)
|
||||||
|
this.finalizeKeys();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = utils.toBuffer(key);
|
||||||
|
|
||||||
|
has = this.keys.some(function(k) {
|
||||||
|
return utils.isEqual(k, key);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (has)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.keys.push(key);
|
||||||
|
|
||||||
|
if (this.keys.length === this.n)
|
||||||
|
this.finalizeKeys();
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.finalizeKeys = function finalizeKeys(key) {
|
||||||
|
if (this.hdpm) {
|
||||||
|
this.purposeKeys = utils.sortHDKeys(this.purposeKeys);
|
||||||
|
|
||||||
|
for (i = 0; i < this.purposeKeys.length; i++) {
|
||||||
|
if (utils.isEqual(this.purposeKeys[i].publicKey, this.purposeKey.publicKey)) {
|
||||||
|
this.cosignerIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._initAddresses();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.keys = utils.sortKeys(this.keys);
|
||||||
|
|
||||||
|
this._initAddresses();
|
||||||
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.removeKey = function removeKey(key) {
|
||||||
|
var hdKey, index;
|
||||||
|
|
||||||
|
if (bcoin.hd.priv.isExtended(key))
|
||||||
|
key = bcoin.hd.priv(key);
|
||||||
|
else if (bcoin.hd.pub.isExtended(key))
|
||||||
|
key = bcoin.hd.pub(key);
|
||||||
|
|
||||||
|
if (key instanceof bcoin.keypair)
|
||||||
|
key = key.hd;
|
||||||
|
|
||||||
|
if (key instanceof bcoin.hd.priv)
|
||||||
|
key = key.hdpub;
|
||||||
|
|
||||||
|
if (key instanceof bcoin.hd.pub) {
|
||||||
|
hdKey = key;
|
||||||
|
key = hd.publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hdpm) {
|
||||||
|
if (!hdKey || !hdKey.isPurpose45())
|
||||||
|
throw new Error('Must add HD purpose keys to HD wallet.');
|
||||||
|
|
||||||
|
index = this.purposeKeys.map(function(pub, i) {
|
||||||
|
return pub.xpubkey === hdKey.xpubkey ? i : null;
|
||||||
|
}).filter(function(i) {
|
||||||
|
return i !== null;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if (index == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.purposeKeys.splice(index, 1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = utils.toBuffer(key);
|
||||||
|
|
||||||
|
index = this.keys.map(function(pub, i) {
|
||||||
|
return utils.isEqual(pub, key) ? i : null;
|
||||||
|
}).filter(function(i) {
|
||||||
|
return i !== null;
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
if (index == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.keys.splice(index, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype._init = function init() {
|
Wallet.prototype._init = function init() {
|
||||||
@ -134,7 +390,7 @@ Wallet.prototype._init = function init() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.__defineGetter__('primary', function() {
|
Wallet.prototype.__defineGetter__('primary', function() {
|
||||||
return this.addresses[0];
|
return this.current;
|
||||||
});
|
});
|
||||||
|
|
||||||
Wallet.prototype._getAddressTable = function() {
|
Wallet.prototype._getAddressTable = function() {
|
||||||
@ -171,30 +427,52 @@ Wallet.prototype._addressIndex = function _addressIndex(address) {
|
|||||||
return -1;
|
return -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.createChangeAddress = function createChangeAddress(options) {
|
Wallet.prototype.createAddress = function createAddress(change, index) {
|
||||||
if (!options)
|
var self = this;
|
||||||
options = {};
|
var key = this.createKey(change, index);
|
||||||
|
var address;
|
||||||
|
|
||||||
options.change = true;
|
var options = {
|
||||||
|
priv: key.priv,
|
||||||
|
pub: key.pub,
|
||||||
|
type: this.type,
|
||||||
|
subtype: this.subtype,
|
||||||
|
m: this.m,
|
||||||
|
n: this.n,
|
||||||
|
keys: [],
|
||||||
|
change: change
|
||||||
|
};
|
||||||
|
|
||||||
if (this.master) {
|
if (this.hdpm) {
|
||||||
options.priv =
|
this.purposeKeys.forEach(function(key, cosignerIndex) {
|
||||||
this.master.key.hd.deriveChange(this.accountIndex, this.changeDepth++);
|
key = key
|
||||||
|
.derive(cosignerIndex)
|
||||||
|
.derive(change ? 1 : 0)
|
||||||
|
.derive(change ? self.changeDepth : self.addressDepth);
|
||||||
|
options.keys.push(key.publicKey);
|
||||||
|
});
|
||||||
|
this.keys = utils.sortKeys(options.keys);
|
||||||
|
} else {
|
||||||
|
this.keys.forEach(function(key, i) {
|
||||||
|
options.keys.push(key);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.addAddress(options);
|
if (index == null) {
|
||||||
};
|
if (this.hd) {
|
||||||
|
if (change)
|
||||||
Wallet.prototype.createNewAddress = function createNewAddress(options) {
|
this.changeDepth++;
|
||||||
if (!options)
|
else
|
||||||
options = {};
|
this.addressDepth++;
|
||||||
|
}
|
||||||
if (this.master) {
|
|
||||||
options.priv =
|
|
||||||
this.master.key.hd.deriveAddress(this.accountIndex, this.addressDepth++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.addAddress(options);
|
address = this.addAddress(options);
|
||||||
|
|
||||||
|
if (!change)
|
||||||
|
this.current = address;
|
||||||
|
|
||||||
|
return address;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.hasAddress = function hasAddress(address) {
|
Wallet.prototype.hasAddress = function hasAddress(address) {
|
||||||
@ -220,12 +498,6 @@ Wallet.prototype.addAddress = function addAddress(address) {
|
|||||||
if (this._addressIndex(address) !== -1)
|
if (this._addressIndex(address) !== -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (address.key.hd && address.key.hd.isMaster) {
|
|
||||||
assert(!this.master);
|
|
||||||
this.master = address;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (address._wallet)
|
if (address._wallet)
|
||||||
address._wallet.removeAddress(address);
|
address._wallet.removeAddress(address);
|
||||||
|
|
||||||
@ -282,14 +554,6 @@ Wallet.prototype.removeAddress = function removeAddress(address) {
|
|||||||
return address;
|
return address;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.addKey = function addKey(key, i) {
|
|
||||||
return this.primary.addKey(key);
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.removeKey = function removeKey(key) {
|
|
||||||
return this.primary.removeKey(key);
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
|
Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
|
||||||
return this.primary.getPrivateKey(enc);
|
return this.primary.getPrivateKey(enc);
|
||||||
};
|
};
|
||||||
@ -305,13 +569,49 @@ Wallet.prototype.getScripthash = function getScripthash() {
|
|||||||
|
|
||||||
Wallet.prototype.getScriptAddress =
|
Wallet.prototype.getScriptAddress =
|
||||||
Wallet.prototype.getScriptaddress = function getScriptaddress() {
|
Wallet.prototype.getScriptaddress = function getScriptaddress() {
|
||||||
return this.primary.getScriptAddress();
|
return this.current.getScriptAddress();
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getPublicKey = function getPublicKey(enc) {
|
Wallet.prototype.getPublicKey = function getPublicKey(enc) {
|
||||||
return this.primary.getPublicKey(enc);
|
return this.primary.getPublicKey(enc);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.createKey = function createKey(change, index) {
|
||||||
|
var key, pub, priv;
|
||||||
|
|
||||||
|
if (!this.hd) {
|
||||||
|
if (this._firstKey) {
|
||||||
|
key = this._firstKey;
|
||||||
|
delete this._firstKey;
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
key = bcoin.ecdsa.genKeyPair();
|
||||||
|
return {
|
||||||
|
priv: key.getPrivate().toArray(),
|
||||||
|
pub: key.getPublic(true, 'array')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == null)
|
||||||
|
index = change ? this.changeDepth : this.addressDepth;
|
||||||
|
|
||||||
|
if (this.hdpm) {
|
||||||
|
key = this.purposeKey
|
||||||
|
.derive(this.cosignerIndex)
|
||||||
|
.derive(change ? 1 : 0)
|
||||||
|
.derive(index);
|
||||||
|
} else {
|
||||||
|
key = this.accountKey
|
||||||
|
.derive(change ? 1 : 0)
|
||||||
|
.derive(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
priv: key.privateKey,
|
||||||
|
pub: key.publicKey
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.getKeyHash =
|
Wallet.prototype.getKeyHash =
|
||||||
Wallet.prototype.getKeyhash = function getKeyhash() {
|
Wallet.prototype.getKeyhash = function getKeyhash() {
|
||||||
return this.primary.getKeyHash();
|
return this.primary.getKeyHash();
|
||||||
@ -327,7 +627,7 @@ Wallet.prototype.getHash = function getHash() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.getAddress = function getAddress() {
|
Wallet.prototype.getAddress = function getAddress() {
|
||||||
return this.primary.getAddress();
|
return this.current.getAddress();
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.ownInput = function ownInput(tx, index) {
|
Wallet.prototype.ownInput = function ownInput(tx, index) {
|
||||||
|
|||||||
@ -74,11 +74,9 @@ describe('Wallet', function() {
|
|||||||
|
|
||||||
it('should multisign/verify TX', function() {
|
it('should multisign/verify TX', function() {
|
||||||
var w = bcoin.wallet({
|
var w = bcoin.wallet({
|
||||||
multisig: {
|
type: 'multisig',
|
||||||
type: 'multisig',
|
m: 1,
|
||||||
m: 1,
|
n: 2
|
||||||
n: 2
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// var k2 = w.getPublicKey().concat(1);
|
// var k2 = w.getPublicKey().concat(1);
|
||||||
var k2 = bcoin.ecdsa.genKeyPair().getPublic(true, 'array');
|
var k2 = bcoin.ecdsa.genKeyPair().getPublic(true, 'array');
|
||||||
@ -265,37 +263,38 @@ describe('Wallet', function() {
|
|||||||
it('should verify 2-of-3 p2sh tx', function(cb) {
|
it('should verify 2-of-3 p2sh tx', function(cb) {
|
||||||
// Create 3 2-of-3 wallets with our pubkeys as "shared keys"
|
// Create 3 2-of-3 wallets with our pubkeys as "shared keys"
|
||||||
var w1 = bcoin.wallet({
|
var w1 = bcoin.wallet({
|
||||||
multisig: {
|
hd: true,
|
||||||
type: 'scripthash',
|
type: 'scripthash',
|
||||||
m: 2,
|
subtype: 'multisig',
|
||||||
n: 3
|
m: 2,
|
||||||
}
|
n: 3
|
||||||
});
|
});
|
||||||
|
|
||||||
var w2 = bcoin.wallet({
|
var w2 = bcoin.wallet({
|
||||||
multisig: {
|
hd: true,
|
||||||
type: 'scripthash',
|
type: 'scripthash',
|
||||||
m: 2,
|
subtype: 'multisig',
|
||||||
n: 3
|
m: 2,
|
||||||
}
|
n: 3
|
||||||
});
|
});
|
||||||
|
|
||||||
var w3 = bcoin.wallet({
|
var w3 = bcoin.wallet({
|
||||||
hd: true,
|
hd: true,
|
||||||
multisig: {
|
type: 'scripthash',
|
||||||
type: 'scripthash',
|
subtype: 'multisig',
|
||||||
m: 2,
|
m: 2,
|
||||||
n: 3
|
n: 3
|
||||||
}
|
|
||||||
});
|
});
|
||||||
w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
// w3 = bcoin.wallet.fromJSON(w3.toJSON());
|
||||||
|
|
||||||
var receive = bcoin.wallet();
|
var receive = bcoin.wallet();
|
||||||
|
|
||||||
w1.addKey(w2.getPublicKey());
|
w1.addKey(w2.purposeKey);
|
||||||
w1.addKey(w3.getPublicKey());
|
w1.addKey(w3.purposeKey);
|
||||||
w2.addKey(w1.getPublicKey());
|
w2.addKey(w1.purposeKey);
|
||||||
w2.addKey(w3.getPublicKey());
|
w2.addKey(w3.purposeKey);
|
||||||
w3.addKey(w1.getPublicKey());
|
w3.addKey(w1.purposeKey);
|
||||||
w3.addKey(w2.getPublicKey());
|
w3.addKey(w2.purposeKey);
|
||||||
|
|
||||||
// Our p2sh address
|
// Our p2sh address
|
||||||
var addr = w1.getAddress();
|
var addr = w1.getAddress();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user