From d6e4978baf42d140b636c17cf9b0850da324394a Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Fri, 19 Aug 2016 18:09:49 -0700 Subject: [PATCH] wallet/buffer/rpc: fix serialization and buffer writes. import keys. --- lib/bcoin/http/rpc.js | 81 +++++++++++++++++++++++++++++++++++++++---- lib/bcoin/keyring.js | 66 +++++------------------------------ lib/bcoin/wallet.js | 9 +++-- lib/bcoin/walletdb.js | 46 ++++++++++++++---------- lib/bcoin/writer.js | 9 +++++ 5 files changed, 126 insertions(+), 85 deletions(-) diff --git a/lib/bcoin/http/rpc.js b/lib/bcoin/http/rpc.js index 0169390f..a5230701 100644 --- a/lib/bcoin/http/rpc.js +++ b/lib/bcoin/http/rpc.js @@ -3073,19 +3073,55 @@ RPC.prototype.getwalletinfo = function getwalletinfo(args, callback) { }; RPC.prototype.importprivkey = function importprivkey(args, callback) { + var self = this; + var secret, label, rescan, key; + if (args.help || args.length < 1 || args.length > 3) { return callback(new RPCError('importprivkey' + ' "bitcoinprivkey" ( "label" rescan )')); } - // Impossible to implement in bcoin. - callback(new Error('Not implemented.')); + + secret = toString(args[0]); + + if (args.length > 1) + label = toString(args[1]); + + if (args.length > 2) + rescan = toBool(args[2]); + + if (rescan && this.chain.db.options.prune) + return callback(new RPCError('Cannot rescan when pruned.')); + + key = bcoin.keyring.fromSecret(secret); + + this.wallet.importKey(0, key, null, function(err) { + if (err) + return callback(err); + + if (!rescan) + return callback(null, null); + + self.walletdb.rescan(self.chain.db, 0, function(err) { + if (err) + return callback(err); + callback(null, null); + }); + }); }; RPC.prototype.importwallet = function importwallet(args, callback) { + var file, data; + if (args.help || args.length !== 1) return callback(new RPCError('importwallet "filename"')); - // Impossible to implement in bcoin. - callback(new Error('Not implemented.')); + + file = toString(args[0]), + + fs.readFile(file, 'utf8', function(err, data) { + if (err) + return callback(err); + + }); }; RPC.prototype.importaddress = function importaddress(args, callback) { @@ -3098,10 +3134,43 @@ RPC.prototype.importaddress = function importaddress(args, callback) { }; RPC.prototype.importpubkey = function importpubkey(args, callback) { + var self = this; + var pubkey, label, rescan, key; + if (args.help || args.length < 1 || args.length > 4) return callback(new RPCError('importpubkey "pubkey" ( "label" rescan )')); - // Impossible to implement in bcoin. - callback(new Error('Not implemented.')); + + pubkey = toString(args[0]); + + if (!utils.isHex(pubkey)) + return callback(new RPCError('Invalid paremeter.')); + + if (args.length > 1) + label = toString(args[1]); + + if (args.length > 2) + rescan = toBool(args[2]); + + if (rescan && this.chain.db.options.prune) + return callback(new RPCError('Cannot rescan when pruned.')); + + pubkey = new Buffer(pubkey, 'hex'); + + key = bcoin.keyring.fromPublic(pubkey, this.network); + + this.wallet.importKey(0, key, null, function(err) { + if (err) + return callback(err); + + if (!rescan) + return callback(null, null); + + self.walletdb.rescan(self.chain.db, 0, function(err) { + if (err) + return callback(err); + callback(null, null); + }); + }); }; RPC.prototype.keypoolrefill = function keypoolrefill(args, callback) { diff --git a/lib/bcoin/keyring.js b/lib/bcoin/keyring.js index 0f7aa6a9..d6378769 100644 --- a/lib/bcoin/keyring.js +++ b/lib/bcoin/keyring.js @@ -11,6 +11,7 @@ var bcoin = require('./env'); var constants = bcoin.protocol.constants; var utils = bcoin.utils; var assert = utils.assert; +var network = bcoin.protocol.network; var BufferReader = require('./reader'); var BufferWriter = require('./writer'); var scriptTypes = constants.scriptTypes; @@ -79,9 +80,6 @@ KeyRing.prototype.fromOptions = function fromOptions(options, network) { this.witness = options.witness; } - if (options.keys) - return this.fromKeys(key, options.m, options.n, options.keys, this.network); - if (options.script) return this.fromScript(key, options.script, this.network); @@ -148,12 +146,11 @@ KeyRing.prototype.fromPublic = function fromPublic(publicKey, network) { * @returns {KeyRing} */ -KeyRing.generate = function(witness, network) { +KeyRing.generate = function(network) { var key = new KeyRing(); key.network = bcoin.network.get(network); key.privateKey = bcoin.ec.generatePrivateKey(); key.publicKey = bcoin.ec.publicKeyCreate(key.privateKey, true); - key.witness = !!witness; return key; }; @@ -196,36 +193,6 @@ KeyRing.fromKey = function fromKey(key, network) { return new KeyRing().fromKey(key, network); }; -/** - * Inject data from public key. - * @private - * @param {Buffer} key - * @param {Number} m - * @param {Number} n - * @param {Buffer[]} keys - * @param {(NetworkType|Network}) network - */ - -KeyRing.prototype.fromKeys = function fromKeys(key, m, n, keys, network) { - var script = bcoin.script.fromMultisig(m, n, keys); - this.fromScript(key, script, network); - return this; -}; - -/** - * Instantiate keyring from keys. - * @param {Buffer} key - * @param {Number} m - * @param {Number} n - * @param {Buffer[]} keys - * @param {(NetworkType|Network}) network - * @returns {KeyRing} - */ - -KeyRing.fromKeys = function fromKeys(key, m, n, keys, network) { - return new KeyRing().fromKeys(key, m, n, keys, network); -}; - /** * Inject data from script. * @private @@ -259,17 +226,12 @@ KeyRing.fromScript = function fromScript(key, script, network) { * @returns {Base58String} */ -KeyRing.prototype.toSecret = function toSecret(network) { +KeyRing.prototype.toSecret = function toSecret() { var p = new BufferWriter(); assert(this.privateKey, 'Cannot serialize without private key.'); - if (!network) - network = this.network; - - network = bcoin.network.get(network); - - p.writeU8(network.keyPrefix.privkey); + p.writeU8(this.network.keyPrefix.privkey); p.writeBytes(this.privateKey); p.writeU8(1); @@ -691,8 +653,10 @@ KeyRing.prototype.verify = function verify(msg, sig) { KeyRing.prototype.getType = function getType() { if (this.program) return this.program.getType(); + if (this.script) return this.script.getType(); + return scriptTypes.PUBKEYHASH; }; @@ -766,7 +730,7 @@ KeyRing.prototype.toJSON = function toJSON() { return { network: this.network.type, witness: this.witness, - key: this.publicKey.toString('hex'), + publicKey: this.publicKey.toString('hex'), script: this.script ? this.script.toRaw().toString('hex') : null, type: constants.scriptTypesByVal[this.type].toLowerCase(), wid: this.path ? this.path.wid : undefined, @@ -793,13 +757,6 @@ KeyRing.prototype.fromJSON = function fromJSON(json) { assert(typeof json.publicKey === 'string'); assert(!json.script || typeof json.script === 'string'); - assert(!json.wid || utils.isNumber(json.wid)); - assert(!json.id || utils.isName(json.id)); - assert(!json.name || utils.isName(json.name)); - assert(utils.isNumber(json.account)); - assert(utils.isNumber(json.change)); - assert(utils.isNumber(json.index)); - this.nework = bcoin.network.get(json.network); this.witness = json.witness; this.publicKey = new Buffer(json.publicKey, 'hex'); @@ -807,12 +764,6 @@ KeyRing.prototype.fromJSON = function fromJSON(json) { if (json.script) this.script = new Buffer(json.script, 'hex'); - this.wid = json.wid; - this.name = json.name; - this.account = json.account; - this.change = json.change; - this.index = json.index; - return this; }; @@ -859,10 +810,11 @@ KeyRing.prototype.toRaw = function toRaw(writer) { * @param {Buffer} data */ -KeyRing.prototype.fromRaw = function fromRaw(data) { +KeyRing.prototype.fromRaw = function fromRaw(data, network) { var p = new BufferReader(data); var i, count, key; + this.network = bcoin.network.get(network); this.witness = p.readU8() === 1; key = p.readVarBytes(); diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 594b8fd2..f0c43a4b 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -1253,7 +1253,7 @@ Wallet.prototype.getOutputPaths = function getOutputPaths(tx, callback) { * This is used for deriving new addresses when * a confirmed transaction is seen. * @param {PathInfo} info - * @param {Function} callback - Returns [Errr, Boolean] + * @param {Function} callback - Returns [Error, Boolean] * (true if new addresses were allocated). */ @@ -1273,6 +1273,9 @@ Wallet.prototype.syncOutputDepth = function syncOutputDepth(info, callback) { for (i = 0; i < info.paths.length; i++) { path = info.paths[i]; + if (path.index === -1) + continue; + if (!accounts[path.account]) accounts[path.account] = []; @@ -2578,7 +2581,7 @@ Account.prototype.derivePath = function derivePath(path, master) { // Imported key. if (path.index === -1) { assert(path.imported); - assert(this.n === 1); + assert(this.type === Account.types.PUBKEYHASH); raw = path.imported; @@ -2588,7 +2591,7 @@ Account.prototype.derivePath = function derivePath(path, master) { if (!raw) return; - ring = bcoin.keyring.fromRaw(raw); + ring = bcoin.keyring.fromRaw(raw, this.network); ring.path = path; return ring; diff --git a/lib/bcoin/walletdb.js b/lib/bcoin/walletdb.js index 4cb9028e..293f8eac 100644 --- a/lib/bcoin/walletdb.js +++ b/lib/bcoin/walletdb.js @@ -92,7 +92,8 @@ var layout = { var len = Buffer.byteLength(id, 'utf8'); var key = new Buffer(1 + len); key[0] = 0x6c; - key.write(id, 1, 'utf8'); + if (len > 0) + key.write(id, 1, 'utf8'); return key; }, ll: function(key) { @@ -110,7 +111,8 @@ var layout = { var key = new Buffer(5 + len); key[0] = 0x69; key.writeUInt32BE(wid, 1, true); - key.write(name, 5, 'utf8'); + if (len > 0) + key.write(name, 5, 'utf8'); return key; }, ii: function ii(key) { @@ -1110,16 +1112,24 @@ WalletDB.prototype.getWallets = function getWallets(callback) { * @param {Function} callback */ -WalletDB.prototype.rescan = function rescan(chaindb, callback) { +WalletDB.prototype.rescan = function rescan(chaindb, height, callback) { var self = this; + if (typeof height === 'function') { + callback = height; + height = null; + } + + if (height == null) + height = self.height; + this.getAddressHashes(function(err, hashes) { if (err) return callback(err); self.logger.info('Scanning for %d addresses.', hashes.length); - chaindb.scan(self.height, hashes, function(block, txs, next) { + chaindb.scan(height, hashes, function(block, txs, next) { self.addBlock(block, txs, next); }, callback); }); @@ -1624,7 +1634,7 @@ Path.prototype.fromRaw = function fromRaw(data) { case 0: this.change = p.readU32(); this.index = p.readU32(); - if (p.left() > 0) + if (p.readU8() === 1) this.script = p.readVarBytes(); break; case 1: @@ -1638,12 +1648,9 @@ Path.prototype.fromRaw = function fromRaw(data) { break; } - this.version = p.readU8(); + this.version = p.read8(); this.type = p.readU8(); - if (this.version === 0xff) - this.version = -1; - return this; }; @@ -1670,11 +1677,16 @@ Path.prototype.toRaw = function toRaw(writer) { p.writeU32(this.account); if (this.index !== -1) { + assert(!this.imported); p.writeU8(0); p.writeU32(this.change); p.writeU32(this.index); - if (this.script) + if (this.script) { + p.writeU8(1); p.writeVarBytes(this.script); + } else { + p.writeU8(0); + } } else { assert(this.imported); p.writeU8(1); @@ -1682,7 +1694,7 @@ Path.prototype.toRaw = function toRaw(writer) { p.writeVarBytes(this.imported); } - p.writeU8(this.version === -1 ? 0xff : this.version); + p.write8(this.version); p.writeU8(this.type); if (!writer) @@ -1718,10 +1730,6 @@ Path.prototype.fromAccount = function fromAccount(account, ring, change, index) return this; }; -Path.fromAccount = function fromAccount(account, ring, change, index) { - return new Path().fromAccount(account, ring, change, index); -}; - /** * Instantiate path from keyring. * @param {WalletID} wid @@ -1729,8 +1737,8 @@ Path.fromAccount = function fromAccount(account, ring, change, index) { * @returns {Path} */ -Path.fromKeyRing = function fromKeyRing(ring) { - return new Path().fromKeyRing(ring); +Path.fromAccount = function fromAccount(account, ring, change, index) { + return new Path().fromAccount(account, ring, change, index); }; /** @@ -1749,8 +1757,8 @@ Path.prototype.toPath = function toPath() { * @returns {Address} */ -Path.prototype.toAddress = function toAddress() { - return bcoin.address.fromHash(this.hash, this.type, this.version); +Path.prototype.toAddress = function toAddress(network) { + return bcoin.address.fromHash(this.hash, this.type, this.version, network); }; /** diff --git a/lib/bcoin/writer.js b/lib/bcoin/writer.js index 58f390bc..93b67182 100644 --- a/lib/bcoin/writer.js +++ b/lib/bcoin/writer.js @@ -393,7 +393,12 @@ BufferWriter.prototype.writeVarBytes = function writeVarBytes(value) { BufferWriter.prototype.writeString = function writeString(value, enc) { if (typeof value !== 'string') return this.writeBytes(value); + this.written += Buffer.byteLength(value, enc); + + if (value.length === 0) + return; + this.data.push([STR, value, enc]); }; @@ -424,6 +429,10 @@ BufferWriter.prototype.writeVarString = function writeVarString(value, enc) { this.written += size; this.data.push([VARINT, size]); + + if (value.length === 0) + return; + this.data.push([STR, value, enc]); };