bip45 wallet

This commit is contained in:
Christopher Jeffrey 2016-02-03 19:44:14 -08:00
parent 28a2cab787
commit eacd1e2ece
6 changed files with 494 additions and 127 deletions

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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();