rewrite wallet. allow multiple addresses. utilize hd.
This commit is contained in:
parent
f9d960ca23
commit
42e17bc0f2
@ -27,6 +27,8 @@ bcoin.tx = require('./bcoin/tx');
|
||||
bcoin.txPool = require('./bcoin/tx-pool');
|
||||
bcoin.block = require('./bcoin/block');
|
||||
bcoin.chain = require('./bcoin/chain');
|
||||
bcoin.keypair = require('./bcoin/keypair');
|
||||
bcoin.address = require('./bcoin/address');
|
||||
bcoin.wallet = require('./bcoin/wallet');
|
||||
bcoin.peer = require('./bcoin/peer');
|
||||
bcoin.pool = require('./bcoin/pool');
|
||||
|
||||
431
lib/bcoin/address.js
Normal file
431
lib/bcoin/address.js
Normal file
@ -0,0 +1,431 @@
|
||||
/**
|
||||
* address.js - address object for bcoin
|
||||
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
||||
* https://github.com/indutny/bcoin
|
||||
*/
|
||||
|
||||
var bcoin = require('../bcoin');
|
||||
var hash = require('hash.js');
|
||||
var bn = require('bn.js');
|
||||
var inherits = require('inherits');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var utils = bcoin.utils;
|
||||
var assert = utils.assert;
|
||||
var constants = bcoin.protocol.constants;
|
||||
var network = bcoin.protocol.network;
|
||||
|
||||
/**
|
||||
* Address
|
||||
*/
|
||||
|
||||
function Address(options) {
|
||||
if (!(this instanceof Address))
|
||||
return new Address(options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
this.options = options;
|
||||
this.storage = options.storage;
|
||||
this.label = options.label || '';
|
||||
this.change = !!options.change;
|
||||
|
||||
this.key = bcoin.keypair({
|
||||
priv: options.priv,
|
||||
pub: options.pub,
|
||||
hd: options.hd,
|
||||
key: options.key,
|
||||
personalization: options.personalization,
|
||||
entropy: options.entropy,
|
||||
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.subtype = options.subtype;
|
||||
this.keys = [];
|
||||
this.m = options.m || 1;
|
||||
this.n = options.n || 1;
|
||||
this.redeem = null;
|
||||
|
||||
if (this.n > 1) {
|
||||
if (this.type !== 'multisig')
|
||||
this.type = 'scripthash';
|
||||
if (this.type === 'scripthash')
|
||||
this.subtype = 'multisig';
|
||||
}
|
||||
|
||||
if (network.prefixes[this.type] == null)
|
||||
throw new Error('Unknown prefix: ' + this.type);
|
||||
|
||||
this.nmax = this.type === 'scripthash'
|
||||
? (this.key.compressed ? 15 : 7)
|
||||
: 3;
|
||||
|
||||
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);
|
||||
|
||||
this.addKey(this.getPublicKey());
|
||||
|
||||
(options.keys || []).forEach(function(key) {
|
||||
this.addKey(key);
|
||||
}, this);
|
||||
|
||||
if (options.redeem)
|
||||
this.setRedeem(options.redeem);
|
||||
|
||||
this.prefix = 'bt/address/' + this.getKeyAddress() + '/';
|
||||
}
|
||||
|
||||
inherits(Address, EventEmitter);
|
||||
|
||||
Address.prototype.setRedeem = function setRedeem(redeem) {
|
||||
var old = this.getScriptAddress();
|
||||
|
||||
if (!utils.isBytes(redeem))
|
||||
redeem = bcoin.script.encode(redeem);
|
||||
|
||||
this.type = 'scripthash';
|
||||
this.subtype = null;
|
||||
this.redeem = redeem;
|
||||
this.emit('scriptaddress', old, this.getScriptAddress());
|
||||
};
|
||||
|
||||
Address.prototype.addKey = function addKey(key) {
|
||||
var old = this.getScriptAddress();
|
||||
|
||||
key = utils.toBuffer(key);
|
||||
|
||||
var has = this.keys.some(function(k) {
|
||||
return utils.isEqual(k, key);
|
||||
});
|
||||
|
||||
if (has)
|
||||
return;
|
||||
|
||||
this.keys.push(key);
|
||||
|
||||
this.keys = utils.sortKeys(this.keys);
|
||||
this.emit('scriptaddress', old, this.getScriptAddress());
|
||||
};
|
||||
|
||||
Address.prototype.removeKey = function removeKey(key) {
|
||||
var old = this.getScriptAddress();
|
||||
|
||||
key = utils.toBuffer(key);
|
||||
|
||||
var index = this.keys.map(function(key, i) {
|
||||
return utils.isEqual(key, pub) ? i : null;
|
||||
}).filter(function(i) {
|
||||
return i !== null;
|
||||
})[0];
|
||||
|
||||
if (index == null)
|
||||
return;
|
||||
|
||||
this.keys.splice(index, 1);
|
||||
|
||||
this.keys = utils.sortKeys(this.keys);
|
||||
this.emit('scriptaddress', old, this.getScriptAddress());
|
||||
};
|
||||
|
||||
Address.prototype.getPrivateKey = function getPrivateKey(enc) {
|
||||
return this.key.getPrivate(enc);
|
||||
};
|
||||
|
||||
Address.toSecret = function toSecret(priv, compressed) {
|
||||
return bcoin.keypair.toSecret(priv, compressed);
|
||||
};
|
||||
|
||||
Address.fromSecret = function fromSecret(priv) {
|
||||
return bcoin.keypair.fromSecret(priv);
|
||||
};
|
||||
|
||||
Address.prototype.getScript = function getScript() {
|
||||
if (this.type !== 'scripthash')
|
||||
return;
|
||||
|
||||
if (this.redeem)
|
||||
return this.redeem.slice();
|
||||
|
||||
if (this.subtype === 'pubkey')
|
||||
return bcoin.script.encode([this.getPublicKey(), 'checksig']);
|
||||
|
||||
if (this.subtype === 'pubkeyhash' || this.keys.length < this.n) {
|
||||
return bcoin.script.encode([
|
||||
'dup',
|
||||
'hash160',
|
||||
this.getKeyHash(),
|
||||
'equalverify',
|
||||
'checksig'
|
||||
]);
|
||||
}
|
||||
|
||||
return bcoin.script.encode(
|
||||
bcoin.script.createMultisig(this.keys, this.m, this.n)
|
||||
);
|
||||
};
|
||||
|
||||
Address.prototype.getScriptHash = function getScriptHash() {
|
||||
if (this.type !== 'scripthash')
|
||||
return;
|
||||
|
||||
return utils.ripesha(this.getScript());
|
||||
};
|
||||
|
||||
Address.prototype.getScriptAddress = function getScriptAddress() {
|
||||
if (this.type !== 'scripthash')
|
||||
return;
|
||||
|
||||
return Address.hash2addr(this.getScriptHash(), this.type);
|
||||
};
|
||||
|
||||
Address.prototype.getPublicKey = function getPublicKey(enc) {
|
||||
return this.key.getPublic(enc);
|
||||
};
|
||||
|
||||
Address.prototype.getKeyHash = function getKeyHash() {
|
||||
return Address.key2hash(this.getPublicKey());
|
||||
};
|
||||
|
||||
Address.prototype.getKeyAddress = function getKeyAddress() {
|
||||
return Address.hash2addr(this.getKeyHash(), 'pubkeyhash');
|
||||
};
|
||||
|
||||
Address.prototype.getHash = function getHash() {
|
||||
if (this.type === 'scripthash')
|
||||
return this.getScriptHash();
|
||||
return this.getKeyHash();
|
||||
};
|
||||
|
||||
Address.prototype.getAddress = function getAddress() {
|
||||
if (this.type === 'scripthash')
|
||||
return this.getScriptAddress();
|
||||
return this.getKeyAddress();
|
||||
};
|
||||
|
||||
Address.key2hash = function key2hash(key) {
|
||||
key = utils.toBuffer(key);
|
||||
return utils.ripesha(key);
|
||||
};
|
||||
|
||||
Address.hash2addr = function hash2addr(hash, prefix) {
|
||||
var addr;
|
||||
|
||||
hash = utils.toArray(hash, 'hex');
|
||||
|
||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||
hash = [ prefix ].concat(hash);
|
||||
|
||||
addr = hash.concat(utils.checksum(hash));
|
||||
|
||||
return utils.toBase58(addr);
|
||||
};
|
||||
|
||||
Address.__defineGetter__('prefixes', function() {
|
||||
if (Address._prefixes)
|
||||
return Address._prefixes;
|
||||
|
||||
Address._prefixes = ['pubkeyhash', 'scripthash'].reduce(function(out, prefix) {
|
||||
var ch = Address.hash2addr(Address.key2hash([]), prefix)[0];
|
||||
out[ch] = prefix;
|
||||
return out;
|
||||
}, {});
|
||||
|
||||
return Address._prefixes;
|
||||
});
|
||||
|
||||
Address.addr2hash = function addr2hash(addr, prefix) {
|
||||
var chk;
|
||||
|
||||
if (prefix == null && typeof addr === 'string')
|
||||
prefix = Address.prefixes[addr[0]];
|
||||
|
||||
if (!utils.isBuffer(addr))
|
||||
addr = utils.fromBase58(addr);
|
||||
|
||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||
|
||||
if (addr.length !== 25)
|
||||
return [];
|
||||
|
||||
if (addr[0] !== prefix)
|
||||
return [];
|
||||
|
||||
chk = utils.checksum(addr.slice(0, -4));
|
||||
|
||||
if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
|
||||
return [];
|
||||
|
||||
return addr.slice(1, -4);
|
||||
};
|
||||
|
||||
Address.validate = function validateAddress(addr, prefix) {
|
||||
if (!addr || typeof addr !== 'string')
|
||||
return false;
|
||||
|
||||
var p = Address.addr2hash(addr, prefix);
|
||||
|
||||
return p.length !== 0;
|
||||
};
|
||||
|
||||
Address.validateAddress = Address.validate;
|
||||
|
||||
Address.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
var scripthash = this.getScriptHash();
|
||||
var hash = this.getKeyHash();
|
||||
var key = this.getPublicKey();
|
||||
var keys = this.keys;
|
||||
|
||||
var outputs = tx.outputs.filter(function(output, i) {
|
||||
var s = output.script;
|
||||
|
||||
if (index != null && index !== i)
|
||||
return false;
|
||||
|
||||
if (bcoin.script.isPubkey(s, key))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isPubkeyhash(s, hash))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isMultisig(s, keys))
|
||||
return true;
|
||||
|
||||
if (scripthash) {
|
||||
if (bcoin.script.isScripthash(s, scripthash))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}, this);
|
||||
|
||||
if (outputs.length === 0)
|
||||
return false;
|
||||
|
||||
return outputs;
|
||||
};
|
||||
|
||||
Address.prototype.ownInput = function ownInput(tx, index) {
|
||||
var scripthash = this.getScriptHash();
|
||||
var hash = this.getKeyHash();
|
||||
var key = this.getPublicKey();
|
||||
var redeem = this.getScript();
|
||||
var keys = this.keys;
|
||||
|
||||
var inputs = tx.inputs.filter(function(input, i) {
|
||||
var s;
|
||||
|
||||
if (!input.prevout.tx && this.tx._all[input.prevout.hash])
|
||||
input.prevout.tx = this.tx._all[input.prevout.hash];
|
||||
|
||||
if (index != null && index !== i)
|
||||
return false;
|
||||
|
||||
// if (bcoin.script.isPubkeyInput(input.script, key, tx, i))
|
||||
// return true;
|
||||
|
||||
if (bcoin.script.isPubkeyhashInput(input.script, key))
|
||||
return true;
|
||||
|
||||
// if (bcoin.script.isMultisigInput(input.script, keys, tx, i))
|
||||
// return true;
|
||||
|
||||
if (redeem) {
|
||||
if (bcoin.script.isScripthashInput(input.script, redeem))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!input.prevout.tx)
|
||||
return false;
|
||||
|
||||
s = input.prevout.tx.getSubscript(input.prevout.index);
|
||||
|
||||
if (bcoin.script.isPubkey(s, key))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isPubkeyhash(s, hash))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isMultisig(s, keys))
|
||||
return true;
|
||||
|
||||
if (scripthash) {
|
||||
if (bcoin.script.isScripthash(s, scripthash))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}, this);
|
||||
|
||||
if (inputs.length === 0)
|
||||
return false;
|
||||
|
||||
return inputs;
|
||||
};
|
||||
|
||||
Address.prototype.toJSON = function toJSON(encrypt) {
|
||||
return {
|
||||
v: 1,
|
||||
name: 'address',
|
||||
network: network.type,
|
||||
label: this.label,
|
||||
change: this.change,
|
||||
address: this.getKeyAddress(),
|
||||
scriptaddress: this.getScriptAddress(),
|
||||
key: this.key.toJSON(encrypt),
|
||||
type: this.type,
|
||||
subtype: this.subtype,
|
||||
redeem: this.redeem ? utils.toHex(this.redeem) : null,
|
||||
keys: this.keys.map(utils.toBase58),
|
||||
m: this.m,
|
||||
n: this.n
|
||||
};
|
||||
};
|
||||
|
||||
Address.fromJSON = function fromJSON(json, decrypt) {
|
||||
var priv, pub, xprivkey, multisig, compressed, key, w;
|
||||
|
||||
assert.equal(json.v, 1);
|
||||
assert.equal(json.name, 'address');
|
||||
|
||||
if (json.network)
|
||||
assert.equal(json.network, network.type);
|
||||
|
||||
w = new Address({
|
||||
label: json.label,
|
||||
change: json.change,
|
||||
key: bcoin.keypair.fromJSON(json.key, decrypt),
|
||||
multisig: multisig,
|
||||
type: json.type,
|
||||
subtype: json.subtype,
|
||||
redeem: json.redeem ? utils.toArray(json.redeem, 'hex') : null,
|
||||
keys: json.keys.map(utils.fromBase58),
|
||||
m: json.m,
|
||||
n: json.n
|
||||
});
|
||||
|
||||
return w;
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = Address;
|
||||
258
lib/bcoin/hd.js
258
lib/bcoin/hd.js
@ -126,9 +126,12 @@ function HDPrivateKey(options) {
|
||||
if (!options)
|
||||
options = { seed: bcoin.hd.seed() };
|
||||
|
||||
if (typeof options === 'string' && options.indexOf('xprv') === 0)
|
||||
if (HDPrivateKey.isExtended(options))
|
||||
options = { xkey: options };
|
||||
|
||||
if (HDPublicKey.isExtended(options))
|
||||
return new HDPublicKey(options);
|
||||
|
||||
if (options.passphrase !== undefined
|
||||
|| options.bits
|
||||
|| options.entropy
|
||||
@ -157,8 +160,171 @@ function HDPrivateKey(options) {
|
||||
this.data = data;
|
||||
|
||||
this._build(data);
|
||||
|
||||
if (new bn(data.parentFingerPrint).cmpn(0) === 0) {
|
||||
this.isMaster = true;
|
||||
this.master = this;
|
||||
} else {
|
||||
this.master = options.master;
|
||||
}
|
||||
|
||||
this.purpose = options.purpose != null
|
||||
? options.purpose
|
||||
: 44;
|
||||
|
||||
this.coinType = options.coinType != null
|
||||
? options.coinType
|
||||
: (network.type === 'main' ? 0 : 1);
|
||||
|
||||
this.accountIndex = options.accountIndex != null
|
||||
? options.accountIndex
|
||||
: 0;
|
||||
|
||||
this.isChange = options.isChange != null
|
||||
? options.isChange
|
||||
: false;
|
||||
|
||||
// this.addressIndex = options.addressIndex != null
|
||||
// ? options.addressIndex
|
||||
// : 0;
|
||||
|
||||
this.isPrivate = true;
|
||||
}
|
||||
|
||||
HDPrivateKey.prototype.scan = function scan(txs) {
|
||||
var keys = [];
|
||||
|
||||
assert(this.isMaster);
|
||||
|
||||
// 0. get the root node
|
||||
var root = master
|
||||
.derive(44, true)
|
||||
.derive(network.type === 'main' ? 0 : 1, true);
|
||||
|
||||
return (function scanner(accountIndex) {
|
||||
var account, chain, addressIndex, address, addr, i;
|
||||
var gap = 0;
|
||||
|
||||
// 1. derive the first account's node (index = 0)
|
||||
account = root.derive(accountIndex, true);
|
||||
|
||||
// 2. derive the external chain node of this account
|
||||
chain = account.derive(0);
|
||||
|
||||
// 3. scan addresses of the external chain;
|
||||
// respect the gap limit described below
|
||||
addressIndex = 0;
|
||||
main:
|
||||
for (;;) {
|
||||
address = chain.derive(addressIndex++);
|
||||
addr = bcoin.address.hash2addr(
|
||||
bcoin.address.key2hash(address.publicKey),
|
||||
'pubkey');
|
||||
|
||||
for (i = 0; i < txs.length; i++) {
|
||||
txs[i].fillPrevout(txs);
|
||||
if (txs[i].testInputs(addr) || txs[i].testOutputs(addr)) {
|
||||
keys.push(address);
|
||||
gap = 0;
|
||||
} else {
|
||||
// 4. if no transactions are found on the
|
||||
// external chain, stop discovery
|
||||
if (++gap >= 20)
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. if there are some transactions, increase
|
||||
// the account index and go to step 1
|
||||
return scanner(accountIndex + 1);
|
||||
})(0);
|
||||
};
|
||||
|
||||
HDPrivateKey.prototype.__defineGetter__('addressIndex', function() {
|
||||
var index = this.childIndex;
|
||||
assert(index < contants.hd.hardened);
|
||||
return index;
|
||||
});
|
||||
|
||||
HDPrivateKey.prototype._deriveBIP44 = function _deriveBIP44() {
|
||||
var child;
|
||||
|
||||
assert(this.isMaster);
|
||||
|
||||
if (options.purpose == null)
|
||||
options.purpose = 44;
|
||||
|
||||
if (options.coinType == null)
|
||||
options.coinType = network.type === 'main' ? 0 : 1;
|
||||
|
||||
child = this
|
||||
.derive(options.purpose, true)
|
||||
.derive(options.coinType, true)
|
||||
.derive(options.accountIndex, true)
|
||||
.derive(options.isChange ? 1 : 0)
|
||||
.derive(options.addressIndex);
|
||||
|
||||
child.purpose = options.purpose;
|
||||
child.coinType = options.coinType;
|
||||
child.accountIndex = options.accountIndex;
|
||||
child.isChange = options.isChange;
|
||||
child.addressIndex = options.addressIndex;
|
||||
|
||||
return child;
|
||||
};
|
||||
|
||||
HDPrivateKey.prototype.deriveAccount = function deriveAccount(accountIndex) {
|
||||
return this._deriveBIP44({
|
||||
purpose: this.purpose,
|
||||
coinType: this.coinType,
|
||||
accountIndex: accountIndex != null ? accountIndex : this.accountIndex + 1,
|
||||
isChange: false,
|
||||
addressIndex: 0
|
||||
});
|
||||
};
|
||||
|
||||
HDPrivateKey.prototype.deriveChange = function deriveChange(addressIndex, accountIndex) {
|
||||
return this._deriveBIP44({
|
||||
purpose: this.purpose,
|
||||
coinType: this.coinType,
|
||||
accountIndex: accountIndex != null ? accountIndex : this.accountIndex,
|
||||
isChange: true,
|
||||
addressIndex: addressIndex != null ? addressIndex : this.addressIndex + 1
|
||||
});
|
||||
};
|
||||
|
||||
HDPrivateKey.prototype.deriveAddress = function deriveAddress(addressIndex, accountIndex) {
|
||||
return this._deriveBIP44({
|
||||
purpose: this.purpose,
|
||||
coinType: this.coinType,
|
||||
accountIndex: accountIndex != null ? accountIndex : this.accountIndex,
|
||||
isChange: false,
|
||||
addressIndex: addressIndex != null ? addressIndex : this.addressIndex + 1
|
||||
});
|
||||
};
|
||||
|
||||
HDPrivateKey.prototype.toPath = function toPath(data) {
|
||||
assert(!this.isMaster);
|
||||
return HDPrivateKey.getPath(
|
||||
this.accountIndex, this.addressIndex, this.isChange);
|
||||
};
|
||||
|
||||
HDPrivateKey.getPath = function toPath(accountIndex, addressIndex, isChange) {
|
||||
return 'm/44\'/'
|
||||
+ (network.type === 'main' ? '0' : '1') + '\'' + '/'
|
||||
+ accountIndex + '\'/'
|
||||
+ (isChange ? '1' : '0') + '/'
|
||||
+ addressIndex;
|
||||
};
|
||||
|
||||
HDPrivateKey.isExtended = function isExtended(data) {
|
||||
if (typeof data !== 'string')
|
||||
return false;
|
||||
|
||||
return data.indexOf('xprv') === 0 || data.indexOf('tprv') === 0;
|
||||
};
|
||||
|
||||
HDPrivateKey.prototype._normalize = function _normalize(data, version) {
|
||||
data.version = version || network.prefixes.xprivkey;
|
||||
data.privateKey = data.privateKey || data.priv;
|
||||
@ -350,7 +516,8 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
|
||||
childIndex: index,
|
||||
chainCode: chainCode,
|
||||
privateKey: privateKey,
|
||||
checksum: null
|
||||
checksum: null,
|
||||
master: this.master
|
||||
});
|
||||
};
|
||||
|
||||
@ -378,6 +545,19 @@ HDPrivateKey._getIndexes = function _getIndexes(path) {
|
||||
return null;
|
||||
|
||||
index = +step;
|
||||
|
||||
if (i === 0) {
|
||||
indexes.purpose = index;
|
||||
} else if (i === 1) {
|
||||
indexes.coinType = index;
|
||||
} else if (i === 2) {
|
||||
indexes.accountIndex = index;
|
||||
} else if (i === 3) {
|
||||
indexes.isChange = index === 1;
|
||||
} else if (i === 4) {
|
||||
indexes.addressIndex = index;
|
||||
}
|
||||
|
||||
if (hardened)
|
||||
index += constants.hd.hardened;
|
||||
|
||||
@ -388,15 +568,16 @@ HDPrivateKey._getIndexes = function _getIndexes(path) {
|
||||
};
|
||||
|
||||
HDPrivateKey.isValidPath = function isValidPath(path, hardened) {
|
||||
var indexes;
|
||||
|
||||
if (typeof path === 'string') {
|
||||
var indexes = HDPrivateKey._getIndexes(path);
|
||||
indexes = HDPrivateKey._getIndexes(path);
|
||||
return indexes !== null && indexes.every(HDPrivateKey.isValidPath);
|
||||
}
|
||||
|
||||
if (typeof path === 'number') {
|
||||
if (path < constants.hd.hardened && hardened) {
|
||||
if (path < constants.hd.hardened && hardened)
|
||||
path += constants.hd.hardened;
|
||||
}
|
||||
return path >= 0 && path < constants.hd.maxIndex;
|
||||
}
|
||||
|
||||
@ -404,14 +585,24 @@ HDPrivateKey.isValidPath = function isValidPath(path, hardened) {
|
||||
};
|
||||
|
||||
HDPrivateKey.prototype.deriveString = function deriveString(path) {
|
||||
var indexes, child;
|
||||
|
||||
if (!HDPrivateKey.isValidPath(path))
|
||||
throw new Error('invalid path');
|
||||
|
||||
var indexes = HDPrivateKey._getIndexes(path);
|
||||
indexes = HDPrivateKey._getIndexes(path);
|
||||
|
||||
return indexes.reduce(function(prev, index) {
|
||||
child = indexes.reduce(function(prev, index, i) {
|
||||
return prev.derive(index);
|
||||
}, this);
|
||||
|
||||
child.purpose = indexes.purpose;
|
||||
child.coinType = indexes.coinType;
|
||||
child.accountIndex = indexes.accountIndex;
|
||||
child.isChange = indexes.isChange;
|
||||
child.addressIndex = indexes.addressIndex;
|
||||
|
||||
return child;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -427,7 +618,7 @@ function HDPublicKey(options) {
|
||||
if (!options)
|
||||
throw new Error('No options for HDPublicKey');
|
||||
|
||||
if (typeof options === 'string' && options.indexOf('xpub') === 0)
|
||||
if (HDPublicKey.isExtended(data))
|
||||
options = { xkey: options };
|
||||
|
||||
data = options.xkey
|
||||
@ -439,8 +630,56 @@ function HDPublicKey(options) {
|
||||
this.data = data;
|
||||
|
||||
this._build(data);
|
||||
|
||||
if (new bn(data.parentFingerPrint).cmpn(0) === 0) {
|
||||
this.isMaster = true;
|
||||
this.master = this;
|
||||
} else {
|
||||
this.master = options.master;
|
||||
}
|
||||
|
||||
this.purpose = options.purpose != null
|
||||
? options.purpose
|
||||
: 44;
|
||||
|
||||
this.coinType = options.coinType != null
|
||||
? options.coinType
|
||||
: (network.type === 'main' ? 0 : 1);
|
||||
|
||||
this.accountIndex = options.accountIndex != null
|
||||
? options.accountIndex
|
||||
: 0;
|
||||
|
||||
this.isChange = options.isChange != null
|
||||
? options.isChange
|
||||
: false;
|
||||
|
||||
// this.addressIndex = options.addressIndex != null
|
||||
// ? options.addressIndex
|
||||
// : 0;
|
||||
|
||||
this.isPublic = true;
|
||||
}
|
||||
|
||||
HDPublicKey.prototype.__defineGetter__('addressIndex', function() {
|
||||
var index = this.childIndex;
|
||||
assert(index < contants.hd.hardened);
|
||||
return index;
|
||||
});
|
||||
|
||||
HDPublicKey.prototype._deriveBIP44 = HDPrivateKey.prototype._deriveBIP44;
|
||||
HDPublicKey.prototype.deriveAccount = HDPrivateKey.prototype.deriveAccount;
|
||||
HDPublicKey.prototype.deriveChange = HDPrivateKey.prototype.deriveChange;
|
||||
HDPublicKey.prototype.deriveAddress = HDPrivateKey.prototype.deriveAddress;
|
||||
HDPublicKey.prototype.toPath = HDPrivateKey.prototype.toPath;
|
||||
|
||||
HDPublicKey.isExtended = function isExtended(data) {
|
||||
if (typeof data !== 'string')
|
||||
return false;
|
||||
|
||||
return data.indexOf('xpub') === 0 || data.indexOf('tpub') === 0;
|
||||
};
|
||||
|
||||
HDPublicKey.prototype._normalize = HDPrivateKey.prototype._normalize;
|
||||
|
||||
HDPublicKey.prototype._unbuild = function _unbuild(xkey) {
|
||||
@ -546,7 +785,8 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
|
||||
childIndex: index,
|
||||
chainCode: chainCode,
|
||||
publicKey: publicKey,
|
||||
checksum: null
|
||||
checksum: null,
|
||||
master: this.master
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
270
lib/bcoin/keypair.js
Normal file
270
lib/bcoin/keypair.js
Normal file
@ -0,0 +1,270 @@
|
||||
/**
|
||||
* keypair.js - keypair object for bcoin
|
||||
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
||||
* https://github.com/indutny/bcoin
|
||||
*/
|
||||
|
||||
var bcoin = require('../bcoin');
|
||||
var hash = require('hash.js');
|
||||
var bn = require('bn.js');
|
||||
var inherits = require('inherits');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var utils = bcoin.utils;
|
||||
var assert = utils.assert;
|
||||
var constants = bcoin.protocol.constants;
|
||||
var network = bcoin.protocol.network;
|
||||
|
||||
/**
|
||||
* KeyPair
|
||||
*/
|
||||
|
||||
function KeyPair(options) {
|
||||
if (!(this instanceof KeyPair))
|
||||
return new KeyPair(options);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
if (options instanceof KeyPair)
|
||||
return options;
|
||||
|
||||
if (options.key instanceof KeyPair)
|
||||
return options.key;
|
||||
|
||||
this.options = options;
|
||||
this.key = options.key || null;
|
||||
this.hd = options.hd || null;
|
||||
this.compressed = options.compressed !== false;
|
||||
|
||||
if (options.priv instanceof bcoin.hd.priv) {
|
||||
this.hd = options.priv;
|
||||
this.key = options.priv.pair;
|
||||
} else if (options.pub instanceof bcoin.hd.pub) {
|
||||
this.hd = options.pub;
|
||||
this.key = options.pub.pair;
|
||||
} else if (options.hd) {
|
||||
this.hd = typeof options.hd === 'object'
|
||||
? bcoin.hd.priv(options.hd)
|
||||
: bcoin.hd.priv();
|
||||
this.key = this.hd.pair;
|
||||
} else if (options.key) {
|
||||
if ((options.key instanceof bcoin.hd.priv)
|
||||
|| (options.key instanceof bcoin.hd.pub)) {
|
||||
this.hd = options.key;
|
||||
this.key = options.key.pair;
|
||||
} else {
|
||||
this.key = options.key;
|
||||
}
|
||||
} else if (options.priv || options.pub) {
|
||||
this.key = bcoin.ecdsa.keyPair({
|
||||
priv: options.priv,
|
||||
pub: options.pub
|
||||
});
|
||||
} else {
|
||||
this.key = bcoin.ecdsa.genKeyPair({
|
||||
pers: options.personalization,
|
||||
entropy: options.entropy
|
||||
|| (options.passphrase ? utils.sha256(options.passphrase) : null)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
KeyPair.prototype.__defineGetter__('priv', function() {
|
||||
return this.key.getPrivate();
|
||||
});
|
||||
|
||||
KeyPair.prototype.__defineGetter__('pub', function() {
|
||||
return this.key.getPublic();
|
||||
});
|
||||
|
||||
KeyPair.prototype.getPrivate = function getPrivate(enc) {
|
||||
var priv = this.key.getPrivate();
|
||||
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
priv = priv.toArray();
|
||||
|
||||
if (enc === 'base58')
|
||||
return KeyPair.toSecret(priv, this.compressed);
|
||||
|
||||
if (enc === 'hex')
|
||||
return utils.toHex(priv);
|
||||
|
||||
return priv;
|
||||
};
|
||||
|
||||
KeyPair.prototype.getPublic = function getPublic(enc) {
|
||||
var pub = this.key.getPublic(this.compressed, 'array');
|
||||
|
||||
if (enc === 'base58')
|
||||
return utils.toBase58(pub);
|
||||
|
||||
if (enc === 'hex')
|
||||
return utils.toHex(pub);
|
||||
|
||||
return pub;
|
||||
};
|
||||
|
||||
KeyPair.prototype.toSecret = function toSecret() {
|
||||
return KeyPair.toSecret(this.getPrivate(), this.compressed);
|
||||
};
|
||||
|
||||
KeyPair.toSecret = function toSecret(priv, compressed) {
|
||||
var arr, chk;
|
||||
|
||||
// We'll be using ncompressed public key as an address
|
||||
arr = [network.prefixes.privkey];
|
||||
|
||||
// 0-pad key
|
||||
while (arr.length + priv.length < 33)
|
||||
arr.push(0);
|
||||
|
||||
arr = arr.concat(priv);
|
||||
|
||||
if (compressed)
|
||||
arr.push(1);
|
||||
|
||||
chk = utils.checksum(arr);
|
||||
|
||||
return utils.toBase58(arr.concat(chk));
|
||||
};
|
||||
|
||||
KeyPair.fromSecret = function fromSecret(priv) {
|
||||
var key, compressed;
|
||||
|
||||
key = utils.fromBase58(priv);
|
||||
assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
|
||||
assert.equal(key[0], network.prefixes.privkey);
|
||||
|
||||
key = key.slice(0, -4);
|
||||
if (key.length === 34) {
|
||||
assert.equal(key[33], 1);
|
||||
priv = key.slice(1, -1);
|
||||
compressed = true;
|
||||
} else {
|
||||
priv = key.slice(1);
|
||||
compressed = false;
|
||||
}
|
||||
|
||||
return new KeyPair({
|
||||
priv: priv,
|
||||
compressed: compressed
|
||||
});
|
||||
};
|
||||
|
||||
KeyPair.prototype.toJSON = function toJSON(encrypt) {
|
||||
var json = {
|
||||
v: 1,
|
||||
name: 'keypair',
|
||||
encrypted: encrypt ? true : false
|
||||
};
|
||||
|
||||
if (this.hd) {
|
||||
if (this.hd.xprivkey) {
|
||||
if (this.hd.seed) {
|
||||
json.mnemonic = encrypt
|
||||
? encrypt(this.hd.seed.mnemonic)
|
||||
: this.hd.seed.mnemonic;
|
||||
json.passphrase = encrypt
|
||||
? encrypt(this.hd.seed.passphrase)
|
||||
: this.hd.seed.passphrase;
|
||||
return json;
|
||||
}
|
||||
json.xpriv = encrypt
|
||||
? encrypt(this.hd.xprivkey)
|
||||
: this.hd.xprivkey;
|
||||
return json;
|
||||
}
|
||||
|
||||
json.xpub = this.hd.xpubkey;
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
if (this.key.priv) {
|
||||
json.priv = encrypt
|
||||
? encrypt(this.getPrivate('base58'))
|
||||
: this.getPrivate('base58');
|
||||
return json;
|
||||
}
|
||||
|
||||
json.pub = this.getPublic('hex');
|
||||
return json;
|
||||
};
|
||||
|
||||
KeyPair.fromJSON = function fromJSON(json, decrypt) {
|
||||
var key, priv, pub, compressed, xprivkey;
|
||||
var path = {};
|
||||
|
||||
assert.equal(json.v, 1);
|
||||
assert.equal(json.name, 'keypair');
|
||||
|
||||
if (json.encrypted && !decrypt)
|
||||
throw new Error('Cannot decrypt address');
|
||||
|
||||
if (json.mnemonic) {
|
||||
return new KeyPair({
|
||||
key: bcoin.hd.priv({
|
||||
seed: bcoin.hd.seed({
|
||||
mnemonic: json.encrypted
|
||||
? decrypt(json.mnemonic)
|
||||
: json.mnemonic,
|
||||
passphrase: json.encrypted
|
||||
? decrypt(json.passphrase)
|
||||
: json.passphrase
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (json.xpriv) {
|
||||
xprivkey = json.xpriv;
|
||||
if (json.encrypted)
|
||||
xprivkey = decrypt(xprivkey);
|
||||
return new KeyPair({
|
||||
key: bcoin.hd.priv({
|
||||
xkey: xprivkey
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (json.xpub) {
|
||||
return new KeyPair({
|
||||
key: bcoin.hd.pub({
|
||||
xkey: json.xpub
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (json.priv) {
|
||||
priv = json.priv;
|
||||
if (json.encrypted)
|
||||
priv = decrypt(priv);
|
||||
|
||||
key = KeyPair.fromSecret(json.priv);
|
||||
priv = key.priv;
|
||||
compressed = key.compressed;
|
||||
return new KeyPair({
|
||||
priv: priv,
|
||||
compressed: compressed
|
||||
});
|
||||
}
|
||||
|
||||
if (json.pub) {
|
||||
pub = bcoin.utils.toArray(json.pub, 'hex');
|
||||
compressed = pub[0] !== 0x04;
|
||||
return new KeyPair({
|
||||
pub: pub,
|
||||
compressed: compressed
|
||||
});
|
||||
}
|
||||
|
||||
assert(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = KeyPair;
|
||||
@ -921,6 +921,7 @@ Pool.prototype._removePeer = function _removePeer(peer) {
|
||||
};
|
||||
|
||||
Pool.prototype.watch = function watch(id) {
|
||||
var self = this;
|
||||
var hid, i;
|
||||
|
||||
if (id instanceof bcoin.wallet) {
|
||||
@ -941,14 +942,25 @@ Pool.prototype.watch = function watch(id) {
|
||||
this.bloom.add(id, 'hex');
|
||||
}
|
||||
|
||||
if (this.peers.load)
|
||||
this.peers.load.updateWatch();
|
||||
// Send it to peers
|
||||
if (this._pendingWatch)
|
||||
return;
|
||||
|
||||
for (i = 0; i < this.peers.block.length; i++)
|
||||
this.peers.block[i].updateWatch();
|
||||
this._pendingWatch = true;
|
||||
|
||||
utils.nextTick(function() {
|
||||
self._pendingWatch = false;
|
||||
|
||||
if (self.peers.load)
|
||||
self.peers.load.updateWatch();
|
||||
|
||||
for (i = 0; i < self.peers.block.length; i++)
|
||||
self.peers.block[i].updateWatch();
|
||||
});
|
||||
};
|
||||
|
||||
Pool.prototype.unwatch = function unwatch(id) {
|
||||
var self = this;
|
||||
var i;
|
||||
|
||||
id = utils.toHex(id);
|
||||
@ -968,11 +980,20 @@ Pool.prototype.unwatch = function unwatch(id) {
|
||||
}, this);
|
||||
|
||||
// Resend it to peers
|
||||
if (this.peers.load)
|
||||
this.peers.load.updateWatch();
|
||||
if (this._pendingWatch)
|
||||
return;
|
||||
|
||||
for (i = 0; i < this.peers.block.length; i++)
|
||||
this.peers.block[i].updateWatch();
|
||||
this._pendingWatch = true;
|
||||
|
||||
utils.nextTick(function() {
|
||||
self._pendingWatch = false;
|
||||
|
||||
if (self.peers.load)
|
||||
self.peers.load.updateWatch();
|
||||
|
||||
for (i = 0; i < self.peers.block.length; i++)
|
||||
self.peers.block[i].updateWatch();
|
||||
});
|
||||
};
|
||||
|
||||
// See "Filter matching algorithm":
|
||||
@ -1084,32 +1105,58 @@ Pool.prototype.removeWallet = function removeWallet(w) {
|
||||
this.unwatchWallet(w);
|
||||
};
|
||||
|
||||
Pool.prototype.watchWallet = function watchWallet(w) {
|
||||
if (w.type === 'scripthash') {
|
||||
Pool.prototype.watchAddress = function watchAddress(address) {
|
||||
if (address.type === 'scripthash') {
|
||||
// For the redeem script hash in outputs:
|
||||
this.watch(w.getScriptHash());
|
||||
this.watch(address.getScriptHash());
|
||||
// For the redeem script in inputs:
|
||||
this.watch(w.getScript());
|
||||
this.watch(address.getScript());
|
||||
}
|
||||
|
||||
// For the pubkey hash in outputs:
|
||||
this.watch(w.getKeyHash());
|
||||
this.watch(address.getKeyHash());
|
||||
// For the pubkey in inputs:
|
||||
this.watch(w.getPublicKey());
|
||||
this.watch(address.getPublicKey());
|
||||
};
|
||||
|
||||
Pool.prototype.unwatchWallet = function unwatchWallet(w) {
|
||||
if (w.type === 'scripthash') {
|
||||
Pool.prototype.unwatchAddress = function unwatchAddress(address) {
|
||||
if (address.type === 'scripthash') {
|
||||
// For the redeem script hash in p2sh outputs:
|
||||
this.unwatch(w.getScriptHash());
|
||||
this.unwatch(address.getScriptHash());
|
||||
// For the redeem script in p2sh inputs:
|
||||
this.unwatch(w.getScript());
|
||||
this.unwatch(address.getScript());
|
||||
}
|
||||
|
||||
// For the pubkey hash in p2pk/multisig outputs:
|
||||
this.unwatch(w.getKeyHash());
|
||||
this.unwatch(address.getKeyHash());
|
||||
// For the pubkey in p2pkh inputs:
|
||||
this.unwatch(w.getPublicKey());
|
||||
this.unwatch(address.getPublicKey());
|
||||
};
|
||||
|
||||
Pool.prototype.watchWallet = function watchWallet(w) {
|
||||
var self = this;
|
||||
|
||||
w.addresses.forEach(function(address) {
|
||||
this.watchAddress(address);
|
||||
}, this);
|
||||
|
||||
w.on('add address', w._poolOnAdd = function(address) {
|
||||
self.watchAddress(address);
|
||||
});
|
||||
|
||||
w.on('remove address', w._poolOnRemove = function(address) {
|
||||
self.unwatchAddress(address);
|
||||
});
|
||||
};
|
||||
|
||||
Pool.prototype.unwatchWallet = function unwatchWallet(w) {
|
||||
w.addresses.forEach(function(address) {
|
||||
this.unwatchAddress(address);
|
||||
}, this);
|
||||
w.removeListener('add address', w._poolOnAdd);
|
||||
w.removeListener('remove address', w._poolOnRemove);
|
||||
delete w._poolOnAdd;
|
||||
delete w._poolOnRemove;
|
||||
};
|
||||
|
||||
Pool.prototype.searchWallet = function(w) {
|
||||
|
||||
158
lib/bcoin/tx.js
158
lib/bcoin/tx.js
@ -197,19 +197,20 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
|
||||
input = this.inputs[index];
|
||||
assert(input);
|
||||
|
||||
// Already has a script template (at least)
|
||||
// if (input.script.length)
|
||||
// return;
|
||||
|
||||
// We should have previous outputs by now.
|
||||
assert(input.prevout.tx);
|
||||
|
||||
// Already has a script template (at least)
|
||||
if (input.script.length)
|
||||
return;
|
||||
|
||||
// Get the previous output's subscript
|
||||
s = input.prevout.tx.getSubscript(input.prevout.index);
|
||||
|
||||
// P2SH
|
||||
if (bcoin.script.isScripthash(s)) {
|
||||
assert(redeem);
|
||||
if (!redeem)
|
||||
return false;
|
||||
s = bcoin.script.getSubscript(bcoin.script.decode(redeem));
|
||||
} else {
|
||||
redeem = null;
|
||||
@ -217,12 +218,34 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
|
||||
|
||||
if (bcoin.script.isPubkey(s)) {
|
||||
// P2PK
|
||||
if (!utils.isEqual(s[0], pub))
|
||||
return false;
|
||||
// Already has a script template (at least)
|
||||
if (input.script.length)
|
||||
return true;
|
||||
input.script = [[]];
|
||||
} else if (bcoin.script.isPubkeyhash(s)) {
|
||||
// P2PKH
|
||||
if (!utils.isEqual(s[2], bcoin.wallet.key2hash(pub)))
|
||||
return false;
|
||||
// Already has a script template (at least)
|
||||
if (input.script.length)
|
||||
return true;
|
||||
input.script = [[], pub];
|
||||
} else if (bcoin.script.isMultisig(s)) {
|
||||
// Multisig
|
||||
for (i = 0; i < s.length; i++) {
|
||||
if (utils.isEqual(s[i], pub))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i === s.length)
|
||||
return false;
|
||||
|
||||
// Already has a script template (at least)
|
||||
if (input.script.length)
|
||||
return true;
|
||||
|
||||
// Technically we should create m signature slots,
|
||||
// but we create n signature slots so we can order
|
||||
// the signatures properly.
|
||||
@ -235,6 +258,18 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
|
||||
for (i = 0; i < n; i++)
|
||||
input.script[i + 1] = [];
|
||||
} else {
|
||||
for (i = 0; i < s.length; i++) {
|
||||
if (utils.isEqual(s[i], pub))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i === s.length)
|
||||
return false;
|
||||
|
||||
// Already has a script template (at least)
|
||||
if (input.script.length)
|
||||
return true;
|
||||
|
||||
// Likely a non-standard scripthash multisig
|
||||
// input. Determine n value by counting keys.
|
||||
// Also, only allow nonstandard types for
|
||||
@ -256,6 +291,8 @@ TX.prototype.scriptInput = function scriptInput(index, pub, redeem) {
|
||||
// now that the redeem script is available.
|
||||
this._recalculateFee();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
TX.prototype.createSignature = function createSignature(index, key, type) {
|
||||
@ -501,10 +538,12 @@ TX.prototype.scriptSig = function scriptSig(index, key, pub, redeem, type) {
|
||||
assert(input);
|
||||
|
||||
// Build script for input
|
||||
this.scriptInput(index, pub, redeem);
|
||||
if (!this.scriptInput(index, pub, redeem))
|
||||
return false;
|
||||
|
||||
// Sign input
|
||||
this.signInput(index, key, type);
|
||||
if (!this.signInput(index, key, type))
|
||||
return false;
|
||||
|
||||
return input.script;
|
||||
};
|
||||
@ -1131,6 +1170,111 @@ TX.prototype.getFunds = function getFunds(side) {
|
||||
// Legacy
|
||||
TX.prototype.funds = TX.prototype.getFunds;
|
||||
|
||||
TX.prototype.testInputs = function testInputs(addressTable, index, collect) {
|
||||
var inputs = [];
|
||||
var i, input, prev, data, j;
|
||||
|
||||
if (typeof addressTable === 'string')
|
||||
addressTable = [addressTable];
|
||||
|
||||
if (Array.isArray(addressTable)) {
|
||||
addressTable = addressTable.reduce(function(address) {
|
||||
out[address] = true;
|
||||
return out;
|
||||
}, {});
|
||||
}
|
||||
|
||||
for (i = 0; i < this.inputs.length; i++) {
|
||||
if (index != null && i !== index)
|
||||
continue;
|
||||
|
||||
input = this.inputs[i];
|
||||
|
||||
if (input.prevout.tx)
|
||||
prev = input.prevout.tx.outputs[input.prevout.index].script;
|
||||
else
|
||||
prev = null;
|
||||
|
||||
data = bcoin.script.getInputData(input.script, prev);
|
||||
|
||||
if (data.addresses) {
|
||||
for (j = 0; j < data.addresses.length; j++) {
|
||||
if (addressTable[data.addresses[j]] != null) {
|
||||
if (!collect)
|
||||
return true;
|
||||
inputs.push(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.scriptaddress) {
|
||||
if (addressTable[data.scriptaddress] != null) {
|
||||
if (!collect)
|
||||
return true;
|
||||
inputs.push(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!collect)
|
||||
return false;
|
||||
|
||||
if (inputs.length === 0)
|
||||
return false;
|
||||
|
||||
return inputs;
|
||||
};
|
||||
|
||||
TX.prototype.testOutputs = function testOutputs(addressTable, index, collect) {
|
||||
var outputs = [];
|
||||
var i, output, data, j;
|
||||
|
||||
if (typeof addressTable === 'string')
|
||||
addressTable = [addressTable];
|
||||
|
||||
if (Array.isArray(addressTable)) {
|
||||
addressTable = addressTable.reduce(function(address) {
|
||||
out[address] = true;
|
||||
return out;
|
||||
}, {});
|
||||
}
|
||||
|
||||
for (i = 0; i < this.outputs.length; i++) {
|
||||
if (index != null && i !== index)
|
||||
continue;
|
||||
|
||||
output = this.outputs[i];
|
||||
|
||||
data = bcoin.script.getOutputData(output.script);
|
||||
|
||||
if (data.addresses) {
|
||||
for (j = 0; j < data.addresses.length; j++) {
|
||||
if (addressTable[data.addresses[j]] != null) {
|
||||
if (!collect)
|
||||
return true;
|
||||
outputs.push(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.scriptaddress) {
|
||||
if (addressTable[data.scriptaddress] != null) {
|
||||
if (!collect)
|
||||
return true;
|
||||
outputs.push(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!collect)
|
||||
return false;
|
||||
|
||||
if (outputs.length === 0)
|
||||
return false;
|
||||
|
||||
return outputs;
|
||||
};
|
||||
|
||||
TX.prototype.avoidFeeSnipping = function avoidFeeSnipping() {
|
||||
if (!this.chain)
|
||||
return;
|
||||
|
||||
@ -18,115 +18,36 @@ var network = bcoin.protocol.network;
|
||||
* Wallet
|
||||
*/
|
||||
|
||||
function Wallet(options, passphrase) {
|
||||
function Wallet(options) {
|
||||
if (!(this instanceof Wallet))
|
||||
return new Wallet(options, passphrase);
|
||||
return new Wallet(options);
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
if (typeof options === 'string' && typeof passphrase === 'string') {
|
||||
options = {
|
||||
scope: options,
|
||||
passphrase: passphrase
|
||||
};
|
||||
}
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
this.options = options;
|
||||
this.compressed = options.compressed !== false;
|
||||
this.addresses = [];
|
||||
this.master = null;
|
||||
this._addressTable = {};
|
||||
|
||||
this.accountIndex = options.accountIndex || 0;
|
||||
this.addressIndex = options.addressIndex || 0;
|
||||
this.changeIndex = options.changeIndex || 0;
|
||||
|
||||
if (options.addresses) {
|
||||
options.addresses.forEach(function(address) {
|
||||
this.addAddress(address);
|
||||
}, this);
|
||||
} else {
|
||||
this.addAddress(options);
|
||||
}
|
||||
|
||||
this.storage = options.storage;
|
||||
this.label = options.label || '';
|
||||
this.key = null;
|
||||
this.loaded = false;
|
||||
this.lastTs = 0;
|
||||
this.changeAddress = options.changeAddress || null;
|
||||
this.redeem = options.redeem || options.script;
|
||||
|
||||
if (options.priv instanceof bcoin.hd.priv) {
|
||||
this.hd = options.priv;
|
||||
this.key = this.hd;
|
||||
} else if (options.pub instanceof bcoin.hd.pub) {
|
||||
this.hd = options.pub;
|
||||
this.key = this.hd;
|
||||
} else if (options.hd) {
|
||||
this.hd = typeof options.hd === 'object'
|
||||
? bcoin.hd.priv(options.hd)
|
||||
: bcoin.hd.priv();
|
||||
this.key = this.hd;
|
||||
} else if (options.key) {
|
||||
if ((options.key instanceof bcoin.hd.priv)
|
||||
|| (options.key instanceof bcoin.hd.pub)) {
|
||||
this.hd = options.key;
|
||||
this.key = options.key;
|
||||
} else {
|
||||
this.key = options.key;
|
||||
}
|
||||
} else if (options.passphrase) {
|
||||
this.key = bcoin.ecdsa.genKeyPair({
|
||||
pers: options.scope,
|
||||
entropy: hash.sha256().update(options.passphrase).digest()
|
||||
});
|
||||
} else if (options.priv || options.pub) {
|
||||
this.key = bcoin.ecdsa.keyPair({
|
||||
priv: options.priv,
|
||||
pub: options.pub
|
||||
});
|
||||
} else {
|
||||
this.key = bcoin.ecdsa.genKeyPair();
|
||||
}
|
||||
|
||||
// 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.subtype = options.subtype;
|
||||
this.keys = [];
|
||||
this.m = options.m || 1;
|
||||
this.n = options.n || 1;
|
||||
|
||||
if (this.n > 1) {
|
||||
if (this.type !== 'multisig')
|
||||
this.type = 'scripthash';
|
||||
if (this.type === 'scripthash')
|
||||
this.subtype = 'multisig';
|
||||
}
|
||||
|
||||
if (network.prefixes[this.type] == null)
|
||||
throw new Error('Unknown prefix: ' + this.type);
|
||||
|
||||
this.nmax = this.type === 'scripthash'
|
||||
? (this.compressed ? 15 : 7)
|
||||
: 3;
|
||||
|
||||
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);
|
||||
|
||||
this.addKey(this.getPublicKey());
|
||||
|
||||
(options.keys || []).forEach(function(key) {
|
||||
this.addKey(key);
|
||||
}, this);
|
||||
|
||||
if (this.redeem) {
|
||||
if (!utils.isBytes(this.redeem))
|
||||
this.redeem = bcoin.script.encode(this.redeem);
|
||||
this.type = 'scripthash';
|
||||
this.subtype = null;
|
||||
}
|
||||
|
||||
this.prefix = 'bt/wallet/' + this.getKeyAddress() + '/';
|
||||
|
||||
@ -170,342 +91,205 @@ Wallet.prototype._init = function init() {
|
||||
});
|
||||
};
|
||||
|
||||
Wallet.prototype.addKey = function addKey(key) {
|
||||
key = utils.toBuffer(key);
|
||||
Wallet.prototype.__defineGetter__('address', function() {
|
||||
return this.addresses[0];
|
||||
});
|
||||
|
||||
var has = this.keys.some(function(k) {
|
||||
return utils.isEqual(k, key);
|
||||
});
|
||||
Wallet.prototype._getAddressTable = function() {
|
||||
var addresses = {};
|
||||
var i, address;
|
||||
|
||||
if (has)
|
||||
for (i = 0; i < this.addresses.length; i++) {
|
||||
address = this.addresses[i];
|
||||
if (address.type === 'scripthash')
|
||||
addresses[address.getScriptAddress()] = i;
|
||||
addresses[address.getKeyAddress()] = i;
|
||||
}
|
||||
|
||||
return addresses;
|
||||
};
|
||||
|
||||
// Faster than indexOf if we have tons of addresses
|
||||
Wallet.prototype._addressIndex = function _addressIndex(address) {
|
||||
var addr;
|
||||
|
||||
if (!(address instanceof bcoin.address))
|
||||
address = bcoin.address(address);
|
||||
|
||||
if (address.type === 'scripthash') {
|
||||
addr = address.getScriptAddress();
|
||||
if (this._addressTable[addr] != null)
|
||||
return this._addressTable[addr];
|
||||
}
|
||||
|
||||
addr = address.getKeyAddress();
|
||||
if (this._addressTable[addr] != null)
|
||||
return this._addressTable[addr];
|
||||
|
||||
return -1;
|
||||
};
|
||||
|
||||
Wallet.prototype.createChangeAddress = function createChangeAddress(address) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
if (this.master) {
|
||||
options.change = true;
|
||||
options.priv = this.master.deriveAddress(this.addressIndex++, this.accountIndex);
|
||||
}
|
||||
|
||||
return this.addAddress({ change: true });
|
||||
};
|
||||
|
||||
Wallet.prototype.createNewAddress = function createNewAddress(address, options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
if (this.master)
|
||||
options.priv = this.master.deriveAddress(this.addressIndex++, this.accountIndex);
|
||||
|
||||
return this.addAddress(options);
|
||||
};
|
||||
|
||||
Wallet.prototype.hasAddress = function hasAddress(address) {
|
||||
return this._addressIndex(address) != -1;
|
||||
};
|
||||
|
||||
Wallet.prototype.findAddress = function findAddress(address) {
|
||||
var i = this._addressIndex(address);
|
||||
|
||||
if (i === -1)
|
||||
return;
|
||||
|
||||
this.keys.push(key);
|
||||
return this.addresses[i];
|
||||
};
|
||||
|
||||
this.keys = utils.sortKeys(this.keys);
|
||||
Wallet.prototype.addAddress = function addAddress(address) {
|
||||
var self = this;
|
||||
var index;
|
||||
|
||||
if (!(address instanceof bcoin.address))
|
||||
address = bcoin.address(address);
|
||||
|
||||
if (this._addressIndex(address) !== -1)
|
||||
return;
|
||||
|
||||
if (address._wallet)
|
||||
address._wallet.removeAddress(address);
|
||||
|
||||
address._wallet = this;
|
||||
|
||||
index = this.addresses.push(address) - 1;
|
||||
|
||||
if (address.hd && address.hd.isMaster && adress.hd.isPrivate) {
|
||||
assert(!this.master);
|
||||
this.master = address;
|
||||
}
|
||||
|
||||
address.on('scriptaddress', address._onUpdate = function(old, cur) {
|
||||
self._addressTable[cur] = self._addressTable[old];
|
||||
delete self._addressTable[old];
|
||||
});
|
||||
|
||||
if (address.type === 'scripthash')
|
||||
this._addressTable[address.getScriptAddress()] = index;
|
||||
|
||||
this._addressTable[address.getKeyAddress()] = index;
|
||||
|
||||
this.emit('add address', address);
|
||||
|
||||
return address;
|
||||
};
|
||||
|
||||
Wallet.prototype.removeAddress = function removeAddress(address) {
|
||||
var i;
|
||||
|
||||
assert(address instanceof bcoin.address);
|
||||
|
||||
i = this._addressIndex(address);
|
||||
|
||||
if (i === -1)
|
||||
return;
|
||||
|
||||
assert(address._wallet === this);
|
||||
assert(address._onUpdate);
|
||||
|
||||
this.addresses.splice(i, 1);
|
||||
|
||||
address.removeListener('scriptaddress', address._onUpdate);
|
||||
|
||||
this._addressTable = this._getAddressTable();
|
||||
|
||||
delete address._onUpdate;
|
||||
delete address._wallet;
|
||||
|
||||
this.emit('remove address', address);
|
||||
|
||||
return address;
|
||||
};
|
||||
|
||||
Wallet.prototype.addKey = function addKey(key, i) {
|
||||
return this.address.addKey(key);
|
||||
};
|
||||
|
||||
Wallet.prototype.removeKey = function removeKey(key) {
|
||||
key = utils.toBuffer(key);
|
||||
|
||||
var index = this.keys.map(function(key, i) {
|
||||
return utils.isEqual(key, pub) ? i : null;
|
||||
}).filter(function(i) {
|
||||
return i !== null;
|
||||
})[0];
|
||||
|
||||
if (index == null)
|
||||
return;
|
||||
|
||||
this.keys.splice(index, 1);
|
||||
|
||||
this.keys = utils.sortKeys(this.keys);
|
||||
return this.address.removeKey(key);
|
||||
};
|
||||
|
||||
Wallet.prototype.derive = function derive() {
|
||||
var options = this.options;
|
||||
|
||||
if (!this.hd)
|
||||
throw new Error('Wallet is not HD');
|
||||
|
||||
options.priv = this.hd.derive.apply(this.hd, arguments);
|
||||
|
||||
return new Wallet(options);
|
||||
this.addAddress(this.address.derive.apply(this.address, arguments));
|
||||
};
|
||||
|
||||
Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
|
||||
var priv = this.key.getPrivate();
|
||||
var arr, chk;
|
||||
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
priv = priv.toArray();
|
||||
|
||||
if (!enc)
|
||||
return priv;
|
||||
|
||||
if (enc === 'base58')
|
||||
return Wallet.toSecret(priv, this.compressed);
|
||||
else if (enc === 'hex')
|
||||
return utils.toHex(priv);
|
||||
else
|
||||
return priv;
|
||||
};
|
||||
|
||||
Wallet.toSecret = function toSecret(priv, compressed) {
|
||||
var arr, chk;
|
||||
|
||||
// We'll be using ncompressed public key as an address
|
||||
arr = [network.prefixes.privkey];
|
||||
|
||||
// 0-pad key
|
||||
while (arr.length + priv.length < 33)
|
||||
arr.push(0);
|
||||
|
||||
arr = arr.concat(priv);
|
||||
|
||||
if (compressed)
|
||||
arr.push(1);
|
||||
|
||||
chk = utils.checksum(arr);
|
||||
|
||||
return utils.toBase58(arr.concat(chk));
|
||||
};
|
||||
|
||||
Wallet.fromSecret = function fromSecret(priv) {
|
||||
var key, compressed;
|
||||
|
||||
key = bcoin.utils.fromBase58(priv);
|
||||
assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
|
||||
assert.equal(key[0], network.prefixes.privkey);
|
||||
|
||||
key = key.slice(0, -4);
|
||||
if (key.length === 34) {
|
||||
assert.equal(key[33], 1);
|
||||
priv = key.slice(1, -1);
|
||||
compressed = true;
|
||||
} else {
|
||||
priv = key.slice(1);
|
||||
compressed = false;
|
||||
}
|
||||
|
||||
return {
|
||||
priv: priv,
|
||||
compressed: compressed
|
||||
};
|
||||
return this.address.getPrivateKey(enc);
|
||||
};
|
||||
|
||||
Wallet.prototype.getScript = function getScript() {
|
||||
if (this.type !== 'scripthash')
|
||||
return;
|
||||
|
||||
if (this.redeem)
|
||||
return this.redeem.slice();
|
||||
|
||||
if (this.subtype === 'pubkey')
|
||||
return bcoin.script.encode([this.getPublicKey(), 'checksig']);
|
||||
|
||||
if (this.subtype === 'pubkeyhash') {
|
||||
return bcoin.script.encode([
|
||||
'dup',
|
||||
'hash160',
|
||||
this.getKeyHash(),
|
||||
'equalverify',
|
||||
'checksig'
|
||||
]);
|
||||
}
|
||||
|
||||
return bcoin.script.encode(
|
||||
bcoin.script.createMultisig(this.keys, this.m, this.n)
|
||||
);
|
||||
return this.address.getScript();
|
||||
};
|
||||
|
||||
Wallet.prototype.getScriptHash = function getScriptHash() {
|
||||
if (this.type !== 'scripthash')
|
||||
return;
|
||||
|
||||
return utils.ripesha(this.getScript());
|
||||
return this.address.getScriptHash();
|
||||
};
|
||||
|
||||
Wallet.prototype.getScriptAddress = function getScriptAddress() {
|
||||
if (this.type !== 'scripthash')
|
||||
return;
|
||||
|
||||
return Wallet.hash2addr(this.getScriptHash(), this.type);
|
||||
return this.address.getScriptAddress();
|
||||
};
|
||||
|
||||
Wallet.prototype.getPublicKey = function getPublicKey(enc) {
|
||||
var pub = this.key.getPublic(this.compressed, 'array');
|
||||
|
||||
if (enc === 'base58')
|
||||
return utils.toBase58(pub);
|
||||
else if (enc === 'hex')
|
||||
return utils.toHex(pub);
|
||||
else
|
||||
return pub;
|
||||
return this.address.getPublicKey(enc);
|
||||
};
|
||||
|
||||
Wallet.prototype.getKeyHash = function getKeyHash() {
|
||||
return Wallet.key2hash(this.getPublicKey());
|
||||
return this.address.getKeyHash();
|
||||
};
|
||||
|
||||
Wallet.prototype.getKeyAddress = function getKeyAddress() {
|
||||
return Wallet.hash2addr(this.getKeyHash(), 'pubkeyhash');
|
||||
return this.address.getKeyAddress();
|
||||
};
|
||||
|
||||
Wallet.prototype.getHash = function getHash() {
|
||||
if (this.type === 'scripthash')
|
||||
return this.getScriptHash();
|
||||
return this.getKeyHash();
|
||||
return this.address.getHash();
|
||||
};
|
||||
|
||||
Wallet.prototype.getAddress = function getAddress() {
|
||||
if (this.type === 'scripthash')
|
||||
return this.getScriptAddress();
|
||||
return this.getKeyAddress();
|
||||
};
|
||||
|
||||
Wallet.key2hash = function key2hash(key) {
|
||||
key = utils.toBuffer(key);
|
||||
return utils.ripesha(key);
|
||||
};
|
||||
|
||||
Wallet.hash2addr = function hash2addr(hash, prefix) {
|
||||
var addr;
|
||||
|
||||
hash = utils.toArray(hash, 'hex');
|
||||
|
||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||
hash = [ prefix ].concat(hash);
|
||||
|
||||
addr = hash.concat(utils.checksum(hash));
|
||||
|
||||
return utils.toBase58(addr);
|
||||
};
|
||||
|
||||
Wallet.__defineGetter__('prefixes', function() {
|
||||
if (Wallet._prefixes) return Wallet._prefixes;
|
||||
Wallet._prefixes = ['pubkeyhash', 'scripthash'].reduce(function(out, prefix) {
|
||||
var ch = Wallet.hash2addr(Wallet.key2hash([]), prefix)[0];
|
||||
out[ch] = prefix;
|
||||
return out;
|
||||
}, {});
|
||||
return Wallet._prefixes;
|
||||
});
|
||||
|
||||
Wallet.addr2hash = function addr2hash(addr, prefix) {
|
||||
var chk;
|
||||
|
||||
if (prefix == null && typeof addr === 'string')
|
||||
prefix = Wallet.prefixes[addr[0]];
|
||||
|
||||
if (!utils.isBuffer(addr))
|
||||
addr = utils.fromBase58(addr);
|
||||
|
||||
prefix = network.prefixes[prefix || 'pubkeyhash'];
|
||||
|
||||
if (addr.length !== 25)
|
||||
return [];
|
||||
|
||||
if (addr[0] !== prefix)
|
||||
return [];
|
||||
|
||||
chk = utils.checksum(addr.slice(0, -4));
|
||||
|
||||
if (utils.readU32(chk, 0) !== utils.readU32(addr, 21))
|
||||
return [];
|
||||
|
||||
return addr.slice(1, -4);
|
||||
};
|
||||
|
||||
Wallet.validateAddress = function validateAddress(addr, prefix) {
|
||||
if (!addr)
|
||||
return false;
|
||||
|
||||
var p = Wallet.addr2hash(addr, prefix);
|
||||
|
||||
return p.length !== 0;
|
||||
};
|
||||
|
||||
Wallet.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
var scripthash = this.getScriptHash();
|
||||
var hash = this.getKeyHash();
|
||||
var key = this.getPublicKey();
|
||||
var keys = this.keys;
|
||||
|
||||
var outputs = tx.outputs.filter(function(output, i) {
|
||||
var s = output.script;
|
||||
|
||||
if (index != null && index !== i)
|
||||
return false;
|
||||
|
||||
if (bcoin.script.isPubkey(s, key))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isPubkeyhash(s, hash))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isMultisig(s, keys))
|
||||
return true;
|
||||
|
||||
if (scripthash) {
|
||||
if (bcoin.script.isScripthash(s, scripthash))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}, this);
|
||||
|
||||
if (outputs.length === 0)
|
||||
return false;
|
||||
|
||||
return outputs;
|
||||
return this.address.getAddress();
|
||||
};
|
||||
|
||||
Wallet.prototype.ownInput = function ownInput(tx, index) {
|
||||
var scripthash = this.getScriptHash();
|
||||
var hash = this.getKeyHash();
|
||||
var key = this.getPublicKey();
|
||||
var redeem = this.getScript();
|
||||
var keys = this.keys;
|
||||
this.fillPrevout(tx);
|
||||
return tx.testInputs(this._addressTable, index, true);
|
||||
};
|
||||
|
||||
var inputs = tx.inputs.filter(function(input, i) {
|
||||
var s;
|
||||
|
||||
if (!input.prevout.tx && this.tx._all[input.prevout.hash])
|
||||
input.prevout.tx = this.tx._all[input.prevout.hash];
|
||||
|
||||
if (index != null && index !== i)
|
||||
return false;
|
||||
|
||||
// if (bcoin.script.isPubkeyInput(input.script, key, tx, i))
|
||||
// return true;
|
||||
|
||||
if (bcoin.script.isPubkeyhashInput(input.script, key))
|
||||
return true;
|
||||
|
||||
// if (bcoin.script.isMultisigInput(input.script, keys, tx, i))
|
||||
// return true;
|
||||
|
||||
if (redeem) {
|
||||
if (bcoin.script.isScripthashInput(input.script, redeem))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!input.prevout.tx)
|
||||
return false;
|
||||
|
||||
s = input.prevout.tx.getSubscript(input.prevout.index);
|
||||
|
||||
if (bcoin.script.isPubkey(s, key))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isPubkeyhash(s, hash))
|
||||
return true;
|
||||
|
||||
if (bcoin.script.isMultisig(s, keys))
|
||||
return true;
|
||||
|
||||
if (scripthash) {
|
||||
if (bcoin.script.isScripthash(s, scripthash))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}, this);
|
||||
|
||||
if (inputs.length === 0)
|
||||
return false;
|
||||
|
||||
return inputs;
|
||||
Wallet.prototype.ownOutput = function ownOutput(tx, index) {
|
||||
return tx.testOutputs(this._addressTable, index, true);
|
||||
};
|
||||
|
||||
Wallet.prototype.fill = function fill(tx, address, fee) {
|
||||
var result;
|
||||
|
||||
if (!address)
|
||||
address = this.changeAddress || this.getAddress();
|
||||
address = this.createChangeAddress();
|
||||
|
||||
result = tx.fill(this.getUnspent(), address, fee);
|
||||
|
||||
@ -527,65 +311,72 @@ Wallet.prototype.fillPrevout = function fillPrevout(tx) {
|
||||
Wallet.prototype.fillTX = Wallet.prototype.fillPrevout;
|
||||
|
||||
Wallet.prototype.scriptInputs = function scriptInputs(tx) {
|
||||
var pub = this.getPublicKey();
|
||||
var redeem = this.getScript();
|
||||
var inputs = tx.inputs;
|
||||
return this.addresses.reduce(function(total, address) {
|
||||
var pub = address.getPublicKey();
|
||||
var redeem = address.getScript();
|
||||
tx.inputs.forEach(function(input, i) {
|
||||
if (!input.prevout.tx && this.tx._all[input.prevout.hash])
|
||||
input.prevout.tx = this.tx._all[input.prevout.hash];
|
||||
|
||||
inputs = inputs.filter(function(input, i) {
|
||||
if (!input.prevout.tx && this.tx._all[input.prevout.hash])
|
||||
input.prevout.tx = this.tx._all[input.prevout.hash];
|
||||
if (!input.prevout.tx || !this.ownOutput(input.prevout.tx))
|
||||
return;
|
||||
|
||||
if (!input.prevout.tx || !this.ownOutput(input.prevout.tx))
|
||||
return false;
|
||||
if (tx.scriptInput(i, pub, redeem))
|
||||
total++;
|
||||
}, this);
|
||||
|
||||
tx.scriptInput(i, pub, redeem);
|
||||
|
||||
return true;
|
||||
}, this);
|
||||
|
||||
return inputs.length;
|
||||
return total;
|
||||
}, 0, this);
|
||||
};
|
||||
|
||||
Wallet.prototype.signInputs = function signInputs(tx, type) {
|
||||
var key = this.key;
|
||||
var inputs = tx.inputs;
|
||||
var self = this;
|
||||
|
||||
inputs = inputs.filter(function(input, i) {
|
||||
if (!input.prevout.tx && this.tx._all[input.prevout.hash])
|
||||
input.prevout.tx = this.tx._all[input.prevout.hash];
|
||||
return this.addresses.reduce(function(total, address) {
|
||||
if (!address.key.priv)
|
||||
return total;
|
||||
|
||||
if (!input.prevout.tx || !this.ownOutput(input.prevout.tx))
|
||||
return false;
|
||||
tx.inputs.forEach(function(input, i) {
|
||||
if (!input.prevout.tx && self.tx._all[input.prevout.hash])
|
||||
input.prevout.tx = self.tx._all[input.prevout.hash];
|
||||
|
||||
tx.signInput(i, key, type);
|
||||
if (!input.prevout.tx || !self.ownOutput(input.prevout.tx))
|
||||
return;
|
||||
|
||||
return true;
|
||||
}, this);
|
||||
if (tx.signInput(i, address.key, type))
|
||||
total++;
|
||||
});
|
||||
|
||||
return inputs.length;
|
||||
return total;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
Wallet.prototype.sign = function sign(tx, type) {
|
||||
var pub = this.getPublicKey();
|
||||
var redeem = this.getScript();
|
||||
var key = this.key;
|
||||
var inputs = tx.inputs;
|
||||
var self = this;
|
||||
|
||||
// Add signature script to each input
|
||||
inputs = inputs.filter(function(input, i) {
|
||||
if (!input.prevout.tx && this.tx._all[input.prevout.hash])
|
||||
input.prevout.tx = this.tx._all[input.prevout.hash];
|
||||
return this.addresses.reduce(function(total, address) {
|
||||
var pub = address.getPublicKey();
|
||||
var redeem = address.getScript();
|
||||
var key = address.key;
|
||||
|
||||
// Filter inputs that this wallet own
|
||||
if (!input.prevout.tx || !this.ownOutput(input.prevout.tx))
|
||||
return false;
|
||||
if (!key.priv)
|
||||
return total;
|
||||
|
||||
tx.scriptSig(i, key, pub, redeem, type);
|
||||
// Add signature script to each input
|
||||
tx.inputs.forEach(function(input, i) {
|
||||
if (!input.prevout.tx && self.tx._all[input.prevout.hash])
|
||||
input.prevout.tx = self.tx._all[input.prevout.hash];
|
||||
|
||||
return true;
|
||||
}, this);
|
||||
// Filter inputs that this wallet own
|
||||
if (!input.prevout.tx || !self.ownOutput(input.prevout.tx))
|
||||
return;
|
||||
|
||||
return inputs.length;
|
||||
if (tx.scriptSig(i, key, pub, redeem, type))
|
||||
total++;
|
||||
});
|
||||
|
||||
return total;
|
||||
}, 0);
|
||||
};
|
||||
|
||||
Wallet.prototype.addTX = function addTX(tx, block) {
|
||||
@ -647,82 +438,87 @@ Wallet.prototype.toAddress = function toAddress() {
|
||||
|
||||
Wallet.prototype.toJSON = function toJSON(encrypt) {
|
||||
return {
|
||||
v: 2,
|
||||
v: 3,
|
||||
name: 'wallet',
|
||||
network: network.type,
|
||||
encrypted: encrypt ? true : false,
|
||||
label: this.label,
|
||||
address: this.getKeyAddress(),
|
||||
scriptaddress: this.getScriptAddress(),
|
||||
accountIndex: this.accountIndex,
|
||||
addressIndex: this.addressIndex,
|
||||
changeIndex: this.changeIndex,
|
||||
addresses: this.addresses.filter(function(address) {
|
||||
if (!address.hd)
|
||||
return true;
|
||||
|
||||
if (address.change)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}).map(function(address) {
|
||||
return address.toJSON(encrypt);
|
||||
}),
|
||||
balance: utils.toBTC(this.getBalance()),
|
||||
pub: this.getPublicKey('hex'),
|
||||
priv: encrypt
|
||||
? encrypt(this.getPrivateKey('base58'))
|
||||
: this.getPrivateKey('base58'),
|
||||
xprivkey: this.hd
|
||||
? (encrypt ? encrypt(this.hd.xprivkey) : this.hd.xprivkey)
|
||||
: null,
|
||||
type: this.type,
|
||||
subtype: this.subtype,
|
||||
redeem: this.redeem ? utils.toHex(this.redeem) : null,
|
||||
keys: this.keys.map(utils.toBase58),
|
||||
m: this.m,
|
||||
n: this.n,
|
||||
tx: this.tx.toJSON()
|
||||
};
|
||||
};
|
||||
|
||||
Wallet.fromJSON = function fromJSON(json, decrypt) {
|
||||
var priv, pub, xprivkey, multisig, compressed, key, w;
|
||||
var priv, pub, xprivkey, multisig, compressed, key, w, i;
|
||||
|
||||
assert.equal(json.v, 2);
|
||||
assert.equal(json.v, 3);
|
||||
assert.equal(json.name, 'wallet');
|
||||
|
||||
if (json.network)
|
||||
assert.equal(json.network, network.type);
|
||||
|
||||
if (json.encrypted && !decrypt)
|
||||
throw new Error('Cannot decrypt wallet');
|
||||
|
||||
if (json.priv) {
|
||||
priv = json.priv;
|
||||
if (json.encrypted)
|
||||
priv = decrypt(priv);
|
||||
|
||||
key = Wallet.fromSecret(json.priv);
|
||||
priv = key.priv;
|
||||
compressed = key.compressed;
|
||||
} else {
|
||||
pub = bcoin.utils.toArray(json.pub, 'hex');
|
||||
compressed = pub[0] !== 0x04;
|
||||
}
|
||||
|
||||
if (json.xprivkey) {
|
||||
xprivkey = json.xprivkey;
|
||||
if (json.encrypted)
|
||||
xprivkey = decrypt(xprivkey);
|
||||
priv = bcoin.hd.priv(xprivkey);
|
||||
}
|
||||
|
||||
w = new Wallet({
|
||||
label: json.label,
|
||||
priv: priv,
|
||||
pub: pub,
|
||||
compressed: compressed,
|
||||
multisig: multisig,
|
||||
type: json.type,
|
||||
subtype: json.subtype,
|
||||
redeem: json.redeem ? utils.toArray(json.redeem, 'hex') : null,
|
||||
keys: json.keys.map(utils.fromBase58),
|
||||
m: json.m,
|
||||
n: json.n
|
||||
accountIndex: json.accountIndex,
|
||||
addressIndex: json.addressIndex,
|
||||
changeIndex: json.changeIndex,
|
||||
addresses: json.addresses.map(function(address) {
|
||||
return bcoin.address.fromJSON(address, decrypt);
|
||||
})
|
||||
});
|
||||
|
||||
w.tx.fromJSON(json.tx);
|
||||
|
||||
// Make sure we have all the change
|
||||
// addresses (we don't save them)
|
||||
for (i = 0; i < w.changeIndex; i++) {
|
||||
w.addKey({
|
||||
change: true,
|
||||
key: w.master.deriveChange(i, w.accountIndex)
|
||||
});
|
||||
}
|
||||
|
||||
return w;
|
||||
};
|
||||
|
||||
// Compat - Legacy
|
||||
Wallet.toSecret = function toSecret(priv, compressed) {
|
||||
return bcoin.keypair.toSecret(priv, compressed);
|
||||
};
|
||||
|
||||
Wallet.fromSecret = function fromSecret(priv) {
|
||||
return bcoin.keypair.fromSecret(priv);
|
||||
};
|
||||
|
||||
Wallet.key2hash = function key2hash(key) {
|
||||
return bcoin.address.key2hash(key);
|
||||
};
|
||||
|
||||
Wallet.hash2addr = function hash2addr(hash, prefix) {
|
||||
return bcoin.address.hash2addr(hash, prefix);
|
||||
};
|
||||
|
||||
Wallet.addr2hash = function addr2hash(addr, prefix) {
|
||||
return bcoin.address.addr2hash(addr, prefix);
|
||||
};
|
||||
|
||||
Wallet.validateAddress = function validateAddress(addr, prefix) {
|
||||
return bcoin.address.validateAddress(addr, prefix);
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user