diff --git a/bin/cli b/bin/cli index 0c1a8ef8..8225d825 100755 --- a/bin/cli +++ b/bin/cli @@ -417,18 +417,19 @@ CLI.prototype.backup = co(function* backup() { CLI.prototype.importKey = co(function* importKey() { var key = this.argv[0]; + var account = this.config.account; if (!key) throw new Error('No key for import.'); if (util.isBase58(key)) { - yield this.wallet.importPrivate(key); + yield this.wallet.importPrivate(account, key); this.log('Imported private key.'); return; } if (util.isHex(key)) { - yield this.wallet.importPublic(key); + yield this.wallet.importPublic(account, key); this.log('Imported public key.'); return; } @@ -438,7 +439,8 @@ CLI.prototype.importKey = co(function* importKey() { CLI.prototype.importAddress = co(function* importKey() { var address = this.argv[0]; - yield this.wallet.importAddress(address); + var account = this.config.account; + yield this.wallet.importAddress(account, address); this.log('Imported address.'); }); diff --git a/lib/blockchain/layout.js b/lib/blockchain/layout.js index 6a8925f5..ceb568f7 100644 --- a/lib/blockchain/layout.js +++ b/lib/blockchain/layout.js @@ -19,8 +19,8 @@ * t[hash] -> extended tx * c[hash] -> coins * u[hash] -> undo coins - * s[bit][hash] -> versionbits state * v -> versionbits deployments + * v[bit][hash] -> versionbits state * T[addr-hash][hash] -> dummy (tx by address) * C[addr-hash][hash][index] -> dummy (coin by address) * W+T[witaddr-hash][hash] -> dummy (tx by address) diff --git a/lib/http/client.js b/lib/http/client.js index d0d860fb..230d761c 100644 --- a/lib/http/client.js +++ b/lib/http/client.js @@ -789,7 +789,7 @@ HTTPClient.prototype.removeSharedKey = function removeSharedKey(id, account, key */ HTTPClient.prototype.importPrivate = function importPrivate(id, account, key) { - var options = { privateKey: key }; + var options = { account: account, privateKey: key }; return this._post('/wallet/' + id + '/import', options); }; @@ -802,7 +802,7 @@ HTTPClient.prototype.importPrivate = function importPrivate(id, account, key) { */ HTTPClient.prototype.importPublic = function importPublic(id, account, key) { - var options = { publicKey: key }; + var options = { account: account, publicKey: key }; return this._post('/wallet/' + id + '/import', options); }; @@ -815,7 +815,7 @@ HTTPClient.prototype.importPublic = function importPublic(id, account, key) { */ HTTPClient.prototype.importAddress = function importAddress(id, account, address) { - var options = { address: address }; + var options = { account: account, address: address }; return this._post('/wallet/' + id + '/import', options); }; diff --git a/lib/http/rpc.js b/lib/http/rpc.js index 5383f62c..ffc79c87 100644 --- a/lib/http/rpc.js +++ b/lib/http/rpc.js @@ -299,8 +299,6 @@ RPC.prototype.execute = function execute(json) { return this.selectwallet(json.params); case 'setloglevel': return this.setloglevel(json.params); - case 'restartsync': - return this.restartsync(json.params); default: return Promise.reject(new Error('Method not found: ' + json.method + '.')); @@ -625,14 +623,13 @@ RPC.prototype._getSoftforks = function _getSoftforks() { }; RPC.prototype._getBIP9Softforks = co(function* _getBIP9Softforks() { + var tip = this.chain.tip; var forks = {}; - var keys = Object.keys(this.network.deployments); - var i, id, deployment, state; + var i, deployment, state; - for (i = 0; i < keys.length; i++) { - id = keys[i]; - deployment = this.network.deployments[id]; - state = yield this.chain.getState(this.chain.tip, id); + for (i = 0; i < this.network.deploys.length; i++) { + deployment = this.network.deploys[i]; + state = yield this.chain.getState(tip, deployment); switch (state) { case constants.thresholdStates.DEFINED: @@ -652,7 +649,7 @@ RPC.prototype._getBIP9Softforks = co(function* _getBIP9Softforks() { break; } - forks[id] = { + forks[deployment.name] = { status: state, bit: deployment.bit, startTime: deployment.startTime, @@ -680,7 +677,7 @@ RPC.prototype.getblockchaininfo = co(function* getblockchaininfo(args) { pruned: this.chain.db.options.prune, softforks: this._getSoftforks(), bip9_softforks: yield this._getBIP9Softforks(), - pruneheight: this.chain.db.prune + pruneheight: this.chain.db.options.prune ? Math.max(0, this.chain.height - this.chain.db.keepBlocks) : null }; @@ -939,9 +936,7 @@ RPC.prototype.getchaintips = co(function* getchaintips(args) { for (i = 0; i < tips.length; i++) { hash = tips[i]; entry = yield this.chain.db.get(hash); - - if (!entry) - continue; + assert(entry); fork = yield this._findFork(entry); main = yield entry.isMainChain(); @@ -1125,17 +1120,17 @@ RPC.prototype._entryToJSON = function _entryToJSON(entry) { return { size: entry.size, fee: Amount.btc(entry.fee, true), - modifiedfee: Amount.btc(entry.fees, true), + modifiedfee: Amount.btc(entry.fee, true), time: entry.ts, height: entry.height, startingpriority: entry.priority, currentpriority: entry.getPriority(this.chain.height), descendantcount: this.mempool.countDescendants(tx), descendantsize: entry.sizes, - descendantfees: Amount.btc(entry.fees, true), + descendantfees: Amount.btc(entry.fee, true), ancestorcount: this.mempool.countAncestors(tx), ancestorsize: entry.sizes, - ancestorfees: Amount.btc(entry.fees, true), + ancestorfees: Amount.btc(entry.fee, true), depends: this.mempool.getDepends(tx).map(util.revHex) }; }; @@ -1163,8 +1158,9 @@ RPC.prototype.gettxout = co(function* gettxout(args) { throw new RPCError('Invalid parameter.'); if (mempool) - coin = yield this.node.getCoin(hash, index); - else + coin = this.mempool.getCoin(hash, index); + + if (!coin) coin = yield this.chain.db.getCoin(hash, index); if (!coin) @@ -1738,7 +1734,7 @@ RPC.prototype._bindChain = function _bindChain() { if (!self.attempt) return; - if (util.now() - self.start > 5) + if (util.now() - self.start > 10) self._clearBlock(); }); }; @@ -1770,22 +1766,18 @@ RPC.prototype._totalTX = function _totalTX() { }; RPC.prototype.getmininginfo = co(function* getmininginfo(args) { - var block, hashps; + var attempt = this.attempt; + var hashps; if (args.help || args.length !== 0) throw new RPCError('getmininginfo'); - block = yield this.chain.db.getBlock(this.chain.tip.hash); - - if (!block) - throw new RPCError('Block not found.'); - hashps = yield this._hashps(120, -1); return { blocks: this.chain.height, - currentblocksize: block.getSize(), - currentblocktx: block.txs.length, + currentblocksize: attempt ? attempt.block.getBaseSize() : 0, + currentblocktx: attempt ? attempt.block.txs.length : 0, difficulty: this._getDifficulty(), errors: '', genproclimit: this.proclimit, @@ -1840,22 +1832,22 @@ RPC.prototype.prioritisetransaction = function prioritisetransaction(args) { return Promise.reject(new RPCError('Transaction not in mempool.')); entry.priority += pri; - entry.fees += fee; + entry.fee += fee; if (entry.priority < 0) entry.priority = 0; - if (entry.fees < 0) - entry.fees = 0; + if (entry.fee < 0) + entry.fee = 0; return Promise.resolve(true); }; RPC.prototype._hashps = co(function* _hashps(lookup, height) { - var i, minTime, maxTime, workDiff, timeDiff, ps, tip, entry; + var tip = this.chain.tip; + var i, minTime, maxTime, workDiff, timeDiff, ps, entry; - tip = this.chain.tip; - if (height >= 0 && height < this.chain.tip.height) + if (height !== -1) tip = yield this.chain.db.get(height); if (!tip) @@ -2190,13 +2182,14 @@ RPC.prototype._fillCoins = function _fillCoins(tx) { }; RPC.prototype._signrawtransaction = co(function* signrawtransaction(merged, txs, args) { + var type = constants.hashType.ALL; var keys = []; var keyMap = {}; - var k, i, secret, key; - var coins, prevout, prev; + var coins = []; + var i, j, k, secret, key; + var coin, prevout, prev; var hash, index, script, value; - var redeem, op, j; - var type, parts, tx; + var redeem, op, parts, tx; if (args.length > 2 && Array.isArray(args[2])) { k = args[2]; @@ -2212,7 +2205,6 @@ RPC.prototype._signrawtransaction = co(function* signrawtransaction(merged, txs, } } - coins = []; if (args.length > 1 && Array.isArray(args[1])) { prevout = args[1]; @@ -2235,27 +2227,29 @@ RPC.prototype._signrawtransaction = co(function* signrawtransaction(merged, txs, } script = Script.fromRaw(script, 'hex'); - coins.push(new Coin({ - hash: util.revHex(hash), - index: index, - script: script, - value: value, - coinbase: false, - height: -1 - })); + + coin = new Coin(); + coin.hash = util.revHex(hash); + coin.index = index; + coin.script = script; + coin.value = value; + coin.coinbase = false; + coin.height = -1; + coins.push(coin); if (keys.length === 0 || !util.isHex(prev.redeemScript)) continue; if (script.isScripthash() || script.isWitnessScripthash()) { redeem = Script.fromRaw(prev.redeemScript, 'hex'); - for (j = 0; j < redeem.length; j++) { - op = redeem.get(j); + for (j = 0; j < redeem.code.length; j++) { + op = redeem.code[j]; - if (!Buffer.isBuffer(op)) + if (!op.data) continue; - key = keyMap[op.toString('hex')]; + key = keyMap[op.data.toString('hex')]; + if (key) { key.script = redeem; key.witness = script.isWitnessScripthash(); @@ -2268,14 +2262,16 @@ RPC.prototype._signrawtransaction = co(function* signrawtransaction(merged, txs, tx.fillCoins(coins); } - type = constants.hashType.ALL; - if (args.length > 3 && typeof args[3] === 'string') { - parts = args[3].split('|'); + if (args.length > 3) { + parts = toString(args[3]).split('|'); type = constants.hashType[parts[0]]; + if (type == null) throw new RPCError('Invalid parameter'); + if (parts.length > 2) throw new RPCError('Invalid parameter'); + if (parts.length === 2) { if (parts[1] !== 'ANYONECANPAY') throw new RPCError('Invalid parameter'); @@ -2290,8 +2286,6 @@ RPC.prototype._signrawtransaction = co(function* signrawtransaction(merged, txs, yield this.wallet.sign(merged, { type: type }); - // TODO: Merge with other txs here. - return { hex: merged.toRaw().toString('hex'), complete: merged.isSigned() @@ -2309,8 +2303,8 @@ RPC.prototype.fundrawtransaction = co(function* fundrawtransaction(args) { if (tx.outputs.length === 0) throw new RPCError('TX must have at least one output.'); - if (args.length === 2 && args[1]) { - options = args[1]; + if (args.length > 1) { + options = toObject(args[1]); changeAddress = toString(options.changeAddress); if (changeAddress) @@ -2380,7 +2374,7 @@ RPC.prototype._createRedeem = co(function* _createRedeem(args) { throw new RPCError('Invalid parameters.'); } - if (script.toRaw().length > constants.script.MAX_PUSH) + if (script.getSize() > constants.script.MAX_PUSH) throw new RPCError('Redeem script exceeds size limit.'); return script; @@ -2404,23 +2398,6 @@ RPC.prototype.createmultisig = co(function* createmultisig(args) { }; }); -RPC.prototype._scriptForWitness = function scriptForWitness(script) { - var hash; - - if (script.isPubkey()) { - hash = crypto.hash160(script.get(0)); - return Script.fromProgram(0, hash); - } - - if (script.isPubkeyhash()) { - hash = script.get(2); - return Script.fromProgram(0, hash); - } - - hash = script.sha256(); - return Script.fromProgram(0, hash); -}; - RPC.prototype.createwitnessaddress = function createwitnessaddress(args) { var raw, script, program; @@ -2429,7 +2406,7 @@ RPC.prototype.createwitnessaddress = function createwitnessaddress(args) { raw = toString(args[1]); script = Script.fromRaw(raw, 'hex'); - program = this._scriptForWitness(script); + program = script.forWitness(); return Promise.resolve({ address: program.getAddress().toBase58(this.network), @@ -2712,6 +2689,7 @@ RPC.prototype.backupwallet = co(function* backupwallet(args) { dest = toString(args[0]); yield this.walletdb.backup(dest); + return null; }); @@ -2976,7 +2954,7 @@ RPC.prototype.getreceivedbyaccount = co(function* getreceivedbyaccount(args) { for (j = 0; j < tx.outputs.length; j++) { output = tx.outputs[j]; hash = output.getHash('hex'); - if (filter[hash]) + if (hash && filter[hash]) total += output.value; } } @@ -2985,7 +2963,6 @@ RPC.prototype.getreceivedbyaccount = co(function* getreceivedbyaccount(args) { }); RPC.prototype.getreceivedbyaddress = co(function* getreceivedbyaddress(args) { - var self = this; var minconf = 0; var total = 0; var i, j, hash, tx, output, txs; @@ -3008,7 +2985,7 @@ RPC.prototype.getreceivedbyaddress = co(function* getreceivedbyaddress(args) { if (minconf) { if (tx.height === -1) continue; - if (!(self.chain.height - tx.height + 1 >= minconf)) + if (!(this.chain.height - tx.height + 1 >= minconf)) continue; } for (j = 0; j < tx.outputs.length; j++) { @@ -3191,10 +3168,8 @@ RPC.prototype.importprivkey = co(function* importprivkey(args) { yield this.wallet.importKey(0, key); - if (!rescan) - return null; - - yield this.walletdb.rescan(0); + if (rescan) + yield this.walletdb.rescan(0); return null; }); @@ -3202,16 +3177,22 @@ RPC.prototype.importprivkey = co(function* importprivkey(args) { RPC.prototype.importwallet = co(function* importwallet(args) { var file, keys, lines, line, parts; var i, secret, time, label, addr; - var data, key; + var data, key, rescan; if (args.help || args.length !== 1) - throw new RPCError('importwallet "filename"'); - - file = toString(args[0]); + throw new RPCError('importwallet "filename" ( rescan )'); if (fs.unsupported) throw new RPCError('FS not available.'); + file = toString(args[0]); + + if (args.length > 1) + rescan = toBool(args[1]); + + if (rescan && this.chain.db.options.prune) + throw new RPCError('Cannot rescan when pruned.'); + data = yield readFile(file, 'utf8'); lines = data.split(/\n+/); @@ -3245,19 +3226,43 @@ RPC.prototype.importwallet = co(function* importwallet(args) { yield this.wallet.importKey(0, key); } - yield this.walletdb.rescan(0); + if (rescan) + yield this.walletdb.rescan(0); return null; }); -RPC.prototype.importaddress = function importaddress(args) { +RPC.prototype.importaddress = co(function* importaddress(args) { + var addr, label, rescan, p2sh; + if (args.help || args.length < 1 || args.length > 4) { return Promise.reject(new RPCError( 'importaddress "address" ( "label" rescan p2sh )')); } - // Impossible to implement in bcoin. - return Promise.reject(new Error('Not implemented.')); -}; + + addr = toString(args[0]); + + if (args.length > 1) + label = toString(args[1]); + + if (args.length > 2) + rescan = toBool(args[2]); + + if (args.length > 3) + p2sh = toBool(args[3]); + + if (rescan && this.chain.db.options.prune) + throw new RPCError('Cannot rescan when pruned.'); + + addr = Address.fromBase58(addr); + + yield this.wallet.importAddress(0, addr); + + if (rescan) + yield this.walletdb.rescan(0); + + return null; +}); RPC.prototype.importpubkey = co(function* importpubkey(args) { var pubkey, label, rescan, key; @@ -3285,10 +3290,8 @@ RPC.prototype.importpubkey = co(function* importpubkey(args) { yield this.wallet.importKey(0, key); - if (!rescan) - return null; - - yield this.walletdb.rescan(0); + if (rescan) + yield this.walletdb.rescan(0); return null; }); @@ -3417,10 +3420,13 @@ RPC.prototype._listReceived = co(function* _listReceived(minconf, empty, account for (j = 0; j < tx.outputs.length; j++) { output = tx.outputs[j]; address = output.getAddress(); + if (!address) continue; + hash = address.getHash('hex'); entry = map[hash]; + if (entry) { if (entry.confirmations === -1 || conf < entry.confirmations) entry.confirmations = conf; @@ -3431,6 +3437,7 @@ RPC.prototype._listReceived = co(function* _listReceived(minconf, empty, account } keys = Object.keys(map); + for (i = 0; i < keys.length; i++) { key = keys[i]; entry = map[key]; @@ -3439,6 +3446,7 @@ RPC.prototype._listReceived = co(function* _listReceived(minconf, empty, account if (account) { map = {}; + for (i = 0; i < out.length; i++) { entry = out[i]; item = map[entry.account]; @@ -3449,8 +3457,10 @@ RPC.prototype._listReceived = co(function* _listReceived(minconf, empty, account } item.amount += entry.amount; } + out = []; keys = Object.keys(map); + for (i = 0; i < keys.length; i++) { key = keys[i]; entry = map[key]; @@ -3460,10 +3470,13 @@ RPC.prototype._listReceived = co(function* _listReceived(minconf, empty, account for (i = 0; i < out.length; i++) { entry = out[i]; + if (!empty && entry.amount === 0) continue; + if (entry.confirmations === -1) entry.confirmations = 0; + entry.amount = Amount.btc(entry.amount, true); result.push(entry); } @@ -3771,7 +3784,7 @@ RPC.prototype.lockunspent = function lockunspent(args) { RPC.prototype.move = function move(args) { // Not implementing: stupid and deprecated. - Promise.reject(new Error('Not implemented.')); + return Promise.reject(new Error('Not implemented.')); }; RPC.prototype._send = co(function* _send(account, address, amount, subtractFee) { @@ -4108,17 +4121,6 @@ RPC.prototype.setloglevel = function setloglevel(args) { return Promise.resolve(null); }; -RPC.prototype.restartsync = function restartsync(args) { - if (args.help || args.length !== 0) - return Promise.reject(new RPCError('restartsync')); - - this.pool.stopSync(); - this.chain.synced = false; - this.pool.startSync(); - - return Promise.resolve(null); -}; - /* * Helpers */