diff --git a/lib/bcoin/http/rpc.js b/lib/bcoin/http/rpc.js index 6ab17e2c..24c08370 100644 --- a/lib/bcoin/http/rpc.js +++ b/lib/bcoin/http/rpc.js @@ -2062,7 +2062,7 @@ RPC.prototype._signrawtransaction = function signrawtransaction(merged, txs, arg op = redeem.get(j); key = keyMap[op.toString('hex')]; if (key) { - key.addr.type = 'multisig'; + key.addr.type = bcoin.keyring.types.MULTISIG; key.addr.m = redeem.getSmall(0); key.addr.n = redeem.getSmall(redeem.length - 1); key.addr.keys = redeem.slice(1, -2); diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index cba41a7b..2ef42ab1 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -388,31 +388,6 @@ Input.prototype.isCoinbase = function isCoinbase() { return this.prevout.isNull(); }; -/** - * Test the input against an address, an - * array of addresses, or a map of hashes. - * @param {Hash|Hash[]|AddressHashMap} addressMap - * @returns {Boolean} Whether the input matched. - */ - -Input.prototype.test = function test(addressMap) { - var hash = this.getHash('hex'); - - if (!hash) - return false; - - if (typeof addressMap === 'string') - return hash === addressMap; - - if (Array.isArray(addressMap)) - return addressMap.indexOf(hash) !== -1; - - if (addressMap[hash] != null) - return true; - - return false; -}; - /** * Convert the input to a more user-friendly object. * @returns {Object} diff --git a/lib/bcoin/keyring.js b/lib/bcoin/keyring.js index 135f987e..74b9eec4 100644 --- a/lib/bcoin/keyring.js +++ b/lib/bcoin/keyring.js @@ -37,7 +37,7 @@ function KeyRing(options) { return new KeyRing(options); this.network = bcoin.network.get(); - this.type = 'pubkeyhash'; + this.type = KeyRing.types.PUBKEYHASH; this.m = 1; this.n = 1; this.witness = false; @@ -65,6 +65,27 @@ function KeyRing(options) { this.fromOptions(options); } +/** + * KeyRing types. + * @enum {Number} + * @default + */ + +KeyRing.types = { + PUBKEYHASH: 0, + MULTISIG: 1 +}; + +/** + * KeyRing types by value. + * @const {RevMap} + */ + +KeyRing.typesByVal = { + 0: 'pubkeyhash', + 1: 'multisig' +}; + /** * Inject properties from options object. * @private @@ -79,9 +100,15 @@ KeyRing.prototype.fromOptions = function fromOptions(options) { if (options.network) this.network = bcoin.network.get(options.network); - if (options.type) { - assert(options.type === 'pubkeyhash' || options.type === 'multisig'); - this.type = options.type; + if (options.type != null) { + if (typeof options.type === 'string') { + this.type = KeyRing.types[options.type.toUpperCase()]; + assert(this.type != null); + } else { + assert(typeof options.type === 'number'); + this.type = options.type; + assert(KeyRing.typesByVal[this.type]); + } } if (options.m != null) { @@ -137,7 +164,7 @@ KeyRing.prototype.fromOptions = function fromOptions(options) { assert(Buffer.isBuffer(this.key)); if (this.n > 1) - this.type = 'multisig'; + this.type = KeyRing.types.MULTISIG; if (this.m < 1 || this.m > this.n) throw new Error('m ranges between 1 and n'); @@ -163,6 +190,54 @@ KeyRing.fromOptions = function fromOptions(options) { return new KeyRing().fromOptions(options); }; +/** + * Inject properties from account object. + * @private + * @param {Account} account + * @param {Buffer} key + * @param {Buffer[]} keys + * @param {Number} change + * @param {Number} index + */ + +KeyRing.prototype.fromAccount = function fromAccount(account, key, keys, change, index) { + var i; + + this.network = account.network; + this.key = key.publicKey; + this.wid = account.wid; + this.id = account.id; + this.name = account.name; + this.account = account.accountIndex; + this.change = change; + this.index = index; + this.type = account.type; + this.witness = account.witness; + this.m = account.m; + this.n = account.n; + + this.addKey(this.key); + + for (i = 0; i < keys.length; i++) + this.addKey(keys[i]); + + return this; +}; + +/** + * Instantiate key ring from an account. + * @param {Account} account + * @param {Buffer} key + * @param {Buffer[]} keys + * @param {Number} change + * @param {Number} index + * @returns {KeyRing} + */ + +KeyRing.fromAccount = function fromAccount(account, key, keys, change, index) { + return new KeyRing().fromAccount(account, key, keys, change, index); +}; + /** * Add a key to shared keys. * @param {Buffer} key @@ -218,7 +293,7 @@ KeyRing.prototype.getPublicKey = function getPublicKey(enc) { KeyRing.prototype.getScript = function getScript() { var redeem; - if (this.type !== 'multisig') + if (this.type !== KeyRing.types.MULTISIG) return; if (!this._script) { @@ -247,10 +322,10 @@ KeyRing.prototype.getProgram = function getProgram() { return; if (!this._program) { - if (this.type === 'pubkeyhash') { + if (this.type === KeyRing.types.PUBKEYHASH) { hash = utils.hash160(this.getPublicKey()); program = bcoin.script.fromProgram(0, hash); - } else if (this.type === 'multisig') { + } else if (this.type === KeyRing.types.MULTISIG) { hash = utils.sha256(this.getScript().toRaw()); program = bcoin.script.fromProgram(0, hash); } else { @@ -324,7 +399,7 @@ KeyRing.prototype.getScriptHash = function getScriptHash(enc) { */ KeyRing.prototype.getScriptHash160 = function getScriptHash256(enc) { - if (this.type !== 'multisig') + if (this.type !== KeyRing.types.MULTISIG) return; if (!this._scriptHash160) @@ -342,7 +417,7 @@ KeyRing.prototype.getScriptHash160 = function getScriptHash256(enc) { */ KeyRing.prototype.getScriptHash256 = function getScriptHash256(enc) { - if (this.type !== 'multisig') + if (this.type !== KeyRing.types.MULTISIG) return; if (!this._scriptHash256) @@ -362,7 +437,7 @@ KeyRing.prototype.getScriptHash256 = function getScriptHash256(enc) { KeyRing.prototype.getScriptAddress = function getScriptAddress(enc) { var hash, address; - if (this.type !== 'multisig') + if (this.type !== KeyRing.types.MULTISIG) return; if (!this._scriptAddress) { @@ -442,7 +517,7 @@ KeyRing.prototype.compile = function compile(hash, type, version) { */ KeyRing.prototype.getHash = function getHash(enc) { - if (this.type === 'multisig') + if (this.type === KeyRing.types.MULTISIG) return this.getScriptHash(enc); return this.getKeyHash(enc); }; @@ -454,7 +529,7 @@ KeyRing.prototype.getHash = function getHash(enc) { */ KeyRing.prototype.getAddress = function getAddress(enc) { - if (this.type === 'multisig') + if (this.type === KeyRing.types.MULTISIG) return this.getScriptAddress(enc); return this.getKeyAddress(enc); }; @@ -470,7 +545,7 @@ KeyRing.prototype.getAddressMap = function getAddressMap() { this._addressMap[this.getKeyHash('hex')] = true; - if (this.type === 'multisig') + if (this.type === KeyRing.types.MULTISIG) this._addressMap[this.getScriptHash('hex')] = true; if (this.witness) @@ -489,11 +564,21 @@ KeyRing.prototype.getAddressMap = function getAddressMap() { KeyRing.prototype.ownInput = function ownInput(tx, index) { var addressMap = this.getAddressMap(); + var input, hash; - if (tx instanceof bcoin.input) - return tx.test(addressMap); + if (tx instanceof bcoin.input) { + input = tx; + } else { + input = tx.inputs[index]; + assert(input, 'Input does not exist.'); + } - return tx.testInputs(addressMap, index); + hash = input.getHash('hex'); + + if (!hash) + return false; + + return addressMap[hash] === true; }; /** @@ -505,11 +590,21 @@ KeyRing.prototype.ownInput = function ownInput(tx, index) { KeyRing.prototype.ownOutput = function ownOutput(tx, index) { var addressMap = this.getAddressMap(); + var output, hash; - if (tx instanceof bcoin.output) - return tx.test(addressMap); + if (tx instanceof bcoin.output) { + output = tx; + } else { + output = tx.outputs[index]; + assert(output, 'Output does not exist.'); + } - return tx.testOutputs(addressMap, index); + hash = output.getHash('hex'); + + if (!hash) + return false; + + return addressMap[hash] === true; }; /** @@ -657,7 +752,7 @@ KeyRing.prototype.__defineGetter__('address', function() { KeyRing.prototype.toJSON = function toJSON() { return { network: this.network.type, - type: this.type, + type: KeyRing.typesByVal[this.type].toLowerCase(), m: this.m, n: this.n, witness: this.witness, @@ -667,8 +762,10 @@ KeyRing.prototype.toJSON = function toJSON() { account: this.account, change: this.change, index: this.index, - key: utils.toBase58(this.key), - keys: this.keys.map(utils.toBase58), + key: this.key.toString('hex'), + keys: this.keys.map(function(key) { + return key.toString('hex'); + }), keyAddress: this.getKeyAddress('base58'), scriptAddress: this.getScriptAddress('base58'), programAddress: this.getProgramAddress('base58') @@ -686,7 +783,7 @@ KeyRing.prototype.fromJSON = function fromJSON(json) { assert(json); assert(typeof json.network === 'string'); - assert(json.type === 'pubkeyhash' || json.type === 'multisig'); + assert(typeof json.type === 'string'); assert(utils.isNumber(json.m)); assert(utils.isNumber(json.n)); assert(typeof json.witness === 'boolean'); @@ -700,7 +797,7 @@ KeyRing.prototype.fromJSON = function fromJSON(json) { assert(Array.isArray(json.keys)); this.nework = bcoin.network.get(json.network); - this.type = json.type; + this.type = KeyRing.types[json.type.toUpperCase()]; this.m = json.m; this.n = json.n; this.witness = json.witness; @@ -709,10 +806,12 @@ KeyRing.prototype.fromJSON = function fromJSON(json) { this.account = json.account; this.change = json.change; this.index = json.index; - this.key = utils.fromBase58(json.key); + this.key = new Buffer(json.key, 'hex'); + + assert(this.type != null); for (i = 0; i < json.keys.length; i++) - this.keys.push(utils.fromBase58(json.keys[i])); + this.keys.push(new Buffer(json.keys[i], 'hex')); return this; }; @@ -737,7 +836,7 @@ KeyRing.prototype.toRaw = function toRaw(writer) { var i; p.writeU32(this.network.magic); - p.writeU8(this.type === 'pubkeyhash' ? 0 : 1); + p.writeU8(this.type); p.writeU8(this.m); p.writeU8(this.n); p.writeU8(this.witness ? 1 : 0); @@ -770,7 +869,7 @@ KeyRing.prototype.fromRaw = function fromRaw(data) { var i, count; this.network = bcoin.network.fromMagic(p.readU32()); - this.type = p.readU8() === 0 ? 'pubkeyhash' : 'multisig'; + this.type = p.readU8(); this.m = p.readU8(); this.n = p.readU8(); this.witness = p.readU8() === 1; @@ -782,6 +881,8 @@ KeyRing.prototype.fromRaw = function fromRaw(data) { this.index = p.readU32(); this.key = p.readVarBytes(); + assert(KeyRing.typesByVal[this.type]); + count = p.readU8(); for (i = 0; i < count; i++) diff --git a/lib/bcoin/mtx.js b/lib/bcoin/mtx.js index 1842d8ba..bc24c8c3 100644 --- a/lib/bcoin/mtx.js +++ b/lib/bcoin/mtx.js @@ -27,7 +27,6 @@ var TX = bcoin.tx; * @param {Number?} options.changeIndex * @param {Input[]?} options.inputs * @param {Output[]?} options.outputs - * @property {String} type - "tx" (inv type). * @property {Number} version - Transaction version. Note that BCoin reads * versions as unsigned even though they are signed at the protocol level. * This value will never be negative. diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index 5e2ca1ed..6e829a16 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -106,31 +106,6 @@ Output.prototype.getHash = function getHash(enc) { return address.getHash(enc); }; -/** - * Test the output against an address, an - * array of addresses, or a map of addresses. - * @param {Hash|Hash[]|AddressHashMap} addressMap - * @returns {Boolean} Whether the output matched. - */ - -Output.prototype.test = function test(addressMap) { - var hash = this.getHash('hex'); - - if (!hash) - return false; - - if (typeof addressMap === 'string') - return hash === addressMap; - - if (Array.isArray(addressMap)) - return addressMap.indexOf(hash) !== -1; - - if (addressMap[hash] != null) - return true; - - return false; -}; - /** * Convert the input to a more user-friendly object. * @returns {Object} diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 92186fa1..9b863591 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -928,68 +928,6 @@ TX.prototype.getHashes = function getHashes(enc) { return hashes; }; -/** - * Test the inputs against an address, an - * array of address hashes, or a map of address hashes. - * @param {Hash|Hash[]|AddressHashMap} addressMap - * @param {Number?} index - * @returns {Boolean} Whether the transaction matched. - */ - -TX.prototype.testInputs = function testInputs(addressMap, index) { - var i; - - if (typeof addressMap === 'string') - addressMap = [addressMap]; - - if (Array.isArray(addressMap)) - addressMap = utils.toMap(addressMap); - - if (index && typeof index === 'object') - index = this.inputs.indexOf(index); - - if (index != null) - return this.inputs[index].test(addressMap); - - for (i = 0; i < this.inputs.length; i++) { - if (this.inputs[i].test(addressMap)) - return true; - } - - return false; -}; - -/** - * Test the outputs against an address, an - * array of address hashes, or a map of address hashes. - * @param {Hash|Hash[]|AddressHashMap} addressMap - * @param {Number?} index - * @returns {Boolean} Whether the transaction matched. - */ - -TX.prototype.testOutputs = function testOutputs(addressMap, index) { - var i; - - if (typeof addressMap === 'string') - addressMap = [addressMap]; - - if (Array.isArray(addressMap)) - addressMap = utils.toMap(addressMap); - - if (index && typeof index === 'object') - index = this.outputs.indexOf(index); - - if (index != null) - return this.outputs[index].test(addressMap); - - for (i = 0; i < this.outputs.length; i++) { - if (this.outputs[i].test(addressMap)) - return true; - } - - return false; -}; - /** * Test whether the transaction has * all coins available/filled. diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index e82997e6..5704fcee 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -15,6 +15,8 @@ var assert = utils.assert; var BufferReader = require('./reader'); var BufferWriter = require('./writer'); var TXDB = require('./txdb'); +var keyTypes = bcoin.keyring.types; +var keyTypesByVal = bcoin.keyring.typesByVal; /** * BIP44 Wallet @@ -2070,7 +2072,7 @@ function Account(db, options) { this.accountIndex = 0; this.receiveDepth = 0; this.changeDepth = 0; - this.type = 'pubkeyhash'; + this.type = keyTypes.PUBKEYHASH; this.m = 1; this.n = 1; this.keys = []; @@ -2127,9 +2129,15 @@ Account.prototype.fromOptions = function fromOptions(options) { this.changeDepth = options.changeDepth; } - if (options.type) { - assert(options.type === 'pubkeyhash' || options.type === 'multisig'); - this.type = options.type; + if (options.type != null) { + if (typeof options.type === 'string') { + this.type = keyTypes[options.type.toUpperCase()]; + assert(this.type != null); + } else { + assert(typeof options.type === 'number'); + this.type = options.type; + assert(keyTypesByVal[this.type]); + } } if (options.m != null) { @@ -2148,7 +2156,7 @@ Account.prototype.fromOptions = function fromOptions(options) { } if (this.n > 1) - this.type = 'multisig'; + this.type = keyTypes.MULTISIG; if (this.m < 1 || this.m > this.n) throw new Error('m ranges between 1 and n'); @@ -2363,7 +2371,7 @@ Account.prototype._checkKeys = function _checkKeys(callback) { var self = this; var address; - if (this.initialized || this.type !== 'multisig') + if (this.initialized || this.type !== keyTypes.MULTISIG) return callback(null, false); if (this.keys.length !== this.n) @@ -2499,21 +2507,7 @@ Account.prototype.deriveAddress = function deriveAddress(change, index) { keys.push(shared.publicKey); } - return new bcoin.keyring({ - network: this.network, - key: key.publicKey, - wid: this.wid, - id: this.id, - name: this.name, - account: this.accountIndex, - change: change, - index: index, - type: this.type, - witness: this.witness, - m: this.m, - n: this.n, - keys: keys - }); + return bcoin.keyring.fromAccount(this, key, keys, change, index); }; /** @@ -2599,7 +2593,7 @@ Account.prototype.inspect = function inspect() { name: this.name, network: this.network, initialized: this.initialized, - type: this.type, + type: keyTypesByVal[this.type].toLowerCase(), m: this.m, n: this.n, keyAddress: this.initialized @@ -2634,7 +2628,7 @@ Account.prototype.toJSON = function toJSON() { wid: this.wid, name: this.name, initialized: this.initialized, - type: this.type, + type: keyTypesByVal[this.type].toLowerCase(), m: this.m, n: this.n, witness: this.witness, @@ -2671,7 +2665,7 @@ Account.prototype.fromJSON = function fromJSON(json) { assert(utils.isAlpha(json.id), 'Account name must be alphanumeric.'); assert(utils.isAlpha(json.name), 'Account name must be alphanumeric.'); assert(typeof json.initialized === 'boolean'); - assert(json.type === 'pubkeyhash' || json.type === 'multisig'); + assert(typeof json.type === 'string'); assert(utils.isNumber(json.m)); assert(utils.isNumber(json.n)); assert(typeof json.witness === 'boolean'); @@ -2683,7 +2677,7 @@ Account.prototype.fromJSON = function fromJSON(json) { this.wid = json.wid; this.name = json.name; this.initialized = json.initialized; - this.type = json.type; + this.type = keyTypes[json.type.toUpperCase()]; this.m = json.m; this.n = json.n; this.witness = json.witness; @@ -2692,6 +2686,8 @@ Account.prototype.fromJSON = function fromJSON(json) { this.changeDepth = json.changeDepth; this.accountKey = bcoin.hd.fromBase58(json.accountKey); + assert(this.type != null); + for (i = 0; i < json.keys.length; i++) this.keys.push(bcoin.hd.fromBase58(json.keys[i])); @@ -2710,7 +2706,7 @@ Account.prototype.toRaw = function toRaw(writer) { p.writeU32(this.network.magic); p.writeVarString(this.name, 'utf8'); p.writeU8(this.initialized ? 1 : 0); - p.writeU8(this.type === 'pubkeyhash' ? 0 : 1); + p.writeU8(this.type); p.writeU8(this.m); p.writeU8(this.n); p.writeU8(this.witness ? 1 : 0); @@ -2743,7 +2739,7 @@ Account.prototype.fromRaw = function fromRaw(data) { this.network = bcoin.network.fromMagic(p.readU32()); this.name = p.readVarString('utf8'); this.initialized = p.readU8() === 1; - this.type = p.readU8() === 0 ? 'pubkeyhash' : 'multisig'; + this.type = p.readU8(); this.m = p.readU8(); this.n = p.readU8(); this.witness = p.readU8() === 1; @@ -2752,6 +2748,8 @@ Account.prototype.fromRaw = function fromRaw(data) { this.changeDepth = p.readU32(); this.accountKey = bcoin.hd.fromRaw(p.readBytes(82)); + assert(keyTypesByVal[this.type]); + count = p.readU8(); for (i = 0; i < count; i++) diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index 0c081401..311578c1 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -29,6 +29,7 @@ var constants = bcoin.protocol.constants; var BufferReader = require('./reader'); var BufferWriter = require('./writer'); var TXDB = require('./txdb'); +var keyTypes = bcoin.keyring.types; /** * WalletDB @@ -828,7 +829,7 @@ WalletDB.prototype.saveAddress = function saveAddress(wid, addresses, callback) items.push([address.getKeyAddress(), path]); - if (address.type === 'multisig') + if (address.type === keyTypes.MULTISIG) items.push([address.getScriptAddress(), path]); if (address.witness) diff --git a/test/wallet-test.js b/test/wallet-test.js index 27ecb610..8edd7004 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -150,7 +150,7 @@ describe('Wallet', function() { p2pkh(true, true, cb); }); - it('should multisign/verify TX', function() { + it('should multisign/verify TX', function(cb) { walletdb.create({ type: 'multisig', m: 1, @@ -185,6 +185,7 @@ describe('Wallet', function() { assert.ifError(err); assert(tx.toRaw().length <= maxSize); assert(tx.verify()); + cb(); }); }); });