• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

handshake-org / hsd / 5590039400

pending completion
5590039400

push

github

nodech
Merge PR #829 from '5.x-proposal'

7400 of 12662 branches covered (58.44%)

Branch coverage included in aggregate %.

15 of 15 new or added lines in 5 files covered. (100.0%)

23568 of 32588 relevant lines covered (72.32%)

31601.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

34.15
/lib/node/rpc.js
1
/*!
2
 * rpc.js - bitcoind-compatible json rpc for hsd.
3
 * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License).
4
 * https://github.com/handshake-org/hsd
5
 */
6

7
'use strict';
8

9
const assert = require('bsert');
1✔
10
const bweb = require('bweb');
1✔
11
const {Lock} = require('bmutex');
1✔
12
const IP = require('binet');
1✔
13
const Validator = require('bval');
1✔
14
const {BufferMap, BufferSet} = require('buffer-map');
1✔
15
const blake2b = require('bcrypto/lib/blake2b');
1✔
16
const {safeEqual} = require('bcrypto/lib/safe');
1✔
17
const secp256k1 = require('bcrypto/lib/secp256k1');
1✔
18
const util = require('../utils/util');
1✔
19
const common = require('../blockchain/common');
1✔
20
const Amount = require('../ui/amount');
1✔
21
const NetAddress = require('../net/netaddress');
1✔
22
const Script = require('../script/script');
1✔
23
const Address = require('../primitives/address');
1✔
24
const Block = require('../primitives/block');
1✔
25
const Input = require('../primitives/input');
1✔
26
const KeyRing = require('../primitives/keyring');
1✔
27
const MerkleBlock = require('../primitives/merkleblock');
1✔
28
const Headers = require('../primitives/headers');
1✔
29
const MTX = require('../primitives/mtx');
1✔
30
const Network = require('../protocol/network');
1✔
31
const Outpoint = require('../primitives/outpoint');
1✔
32
const Output = require('../primitives/output');
1✔
33
const TX = require('../primitives/tx');
1✔
34
const Claim = require('../primitives/claim');
1✔
35
const consensus = require('../protocol/consensus');
1✔
36
const pkg = require('../pkg');
1✔
37
const rules = require('../covenants/rules');
1✔
38
const {Resource} = require('../dns/resource');
1✔
39
const NameState = require('../covenants/namestate');
1✔
40
const ownership = require('../covenants/ownership');
1✔
41
const AirdropProof = require('../primitives/airdropproof');
1✔
42
const {EXP} = consensus;
1✔
43
const RPCBase = bweb.RPC;
1✔
44
const RPCError = bweb.RPCError;
1✔
45

46
/*
47
 * Constants
48
 */
49

50
const errs = {
1✔
51
  // Standard JSON-RPC 2.0 errors
52
  INVALID_REQUEST: bweb.errors.INVALID_REQUEST,
53
  METHOD_NOT_FOUND: bweb.errors.METHOD_NOT_FOUND,
54
  INVALID_PARAMS: bweb.errors.INVALID_PARAMS,
55
  INTERNAL_ERROR: bweb.errors.INTERNAL_ERROR,
56
  PARSE_ERROR: bweb.errors.PARSE_ERROR,
57

58
  // General application defined errors
59
  MISC_ERROR: -1,
60
  FORBIDDEN_BY_SAFE_MODE: -2,
61
  TYPE_ERROR: -3,
62
  INVALID_ADDRESS_OR_KEY: -5,
63
  OUT_OF_MEMORY: -7,
64
  INVALID_PARAMETER: -8,
65
  DATABASE_ERROR: -20,
66
  DESERIALIZATION_ERROR: -22,
67
  VERIFY_ERROR: -25,
68
  VERIFY_REJECTED: -26,
69
  VERIFY_ALREADY_IN_CHAIN: -27,
70
  IN_WARMUP: -28,
71

72
  // P2P client errors
73
  CLIENT_NOT_CONNECTED: -9,
74
  CLIENT_IN_INITIAL_DOWNLOAD: -10,
75
  CLIENT_NODE_ALREADY_ADDED: -23,
76
  CLIENT_NODE_NOT_ADDED: -24,
77
  CLIENT_NODE_NOT_CONNECTED: -29,
78
  CLIENT_INVALID_IP_OR_SUBNET: -30,
79
  CLIENT_P2P_DISABLED: -31
80
};
81

82
const MAGIC_STRING = `${pkg.currency} signed message:\n`;
1✔
83

84
/**
85
 * Handshake RPC
86
 * @alias module:http.RPC
87
 * @extends bweb.RPC
88
 */
89

90
class RPC extends RPCBase {
91
  /**
92
   * Create RPC.
93
   * @param {Node} node
94
   */
95

96
  constructor(node) {
97
    super();
86✔
98

99
    assert(node, 'RPC requires a Node.');
86✔
100

101
    this.node = node;
86✔
102
    this.network = node.network;
86✔
103
    this.workers = node.workers;
86✔
104
    this.chain = node.chain;
86✔
105
    this.mempool = node.mempool;
86✔
106
    this.pool = node.pool;
86✔
107
    this.fees = node.fees;
86✔
108
    this.miner = node.miner;
86✔
109
    this.logger = node.logger.context('node-rpc');
86✔
110
    this.locker = new Lock();
86✔
111

112
    this.mining = false;
86✔
113
    this.procLimit = 0;
86✔
114
    this.attempt = null;
86✔
115
    this.lastActivity = 0;
86✔
116
    this.boundChain = false;
86✔
117
    this.mask = Buffer.alloc(32, 0x00);
86✔
118
    this.merkleMap = new BufferMap();
86✔
119
    this.merkleList = [];
86✔
120
    this.pollers = [];
86✔
121

122
    this.init();
86✔
123
  }
124

125
  getCode(err) {
126
    switch (err.type) {
40!
127
      case 'RPCError':
128
        return err.code;
37✔
129
      case 'ValidationError':
130
        return errs.TYPE_ERROR;
×
131
      case 'EncodingError':
132
        return errs.DESERIALIZATION_ERROR;
×
133
      default:
134
        return errs.INTERNAL_ERROR;
3✔
135
    }
136
  }
137

138
  handleCall(cmd, query) {
139
    if (cmd.method !== 'getwork'
688✔
140
        && cmd.method !== 'getblocktemplate'
141
        && cmd.method !== 'getbestblockhash') {
142
      this.logger.debug('Handling RPC call: %s.', cmd.method);
684✔
143

144
      if (cmd.method !== 'submitblock')
684✔
145
        this.logger.debug(cmd.params);
683✔
146
    }
147

148
    if (cmd.method === 'getwork') {
688!
149
      if (query.longpoll)
×
150
        cmd.method = 'getworklp';
×
151
    }
152
  }
153

154
  handleError(err) {
155
    this.logger.error('RPC internal error.');
3✔
156
    this.logger.error(err);
3✔
157
  }
158

159
  init() {
160
    this.add('stop', this.stop);
86✔
161
    this.add('help', this.help);
86✔
162

163
    this.add('getblockchaininfo', this.getBlockchainInfo);
86✔
164
    this.add('getbestblockhash', this.getBestBlockHash);
86✔
165
    this.add('getblockcount', this.getBlockCount);
86✔
166
    this.add('getblock', this.getBlock);
86✔
167
    this.add('getblockbyheight', this.getBlockByHeight);
86✔
168
    this.add('getblockhash', this.getBlockHash);
86✔
169
    this.add('getblockheader', this.getBlockHeader);
86✔
170
    this.add('getchaintips', this.getChainTips);
86✔
171
    this.add('getdifficulty', this.getDifficulty);
86✔
172
    this.add('getmempoolancestors', this.getMempoolAncestors);
86✔
173
    this.add('getmempooldescendants', this.getMempoolDescendants);
86✔
174
    this.add('getmempoolentry', this.getMempoolEntry);
86✔
175
    this.add('getmempoolinfo', this.getMempoolInfo);
86✔
176
    this.add('getrawmempool', this.getRawMempool);
86✔
177
    this.add('gettxout', this.getTXOut);
86✔
178
    this.add('gettxoutsetinfo', this.getTXOutSetInfo);
86✔
179
    this.add('pruneblockchain', this.pruneBlockchain);
86✔
180
    this.add('compacttree', this.compactTree);
86✔
181
    this.add('reconstructtree', this.reconstructTree);
86✔
182
    this.add('verifychain', this.verifyChain);
86✔
183

184
    this.add('invalidateblock', this.invalidateBlock);
86✔
185
    this.add('reconsiderblock', this.reconsiderBlock);
86✔
186

187
    this.add('getnetworkhashps', this.getNetworkHashPS);
86✔
188
    this.add('getmininginfo', this.getMiningInfo);
86✔
189
    this.add('prioritisetransaction', this.prioritiseTransaction);
86✔
190
    this.add('getwork', this.getWork);
86✔
191
    this.add('getworklp', this.getWorkLongpoll);
86✔
192
    this.add('submitwork', this.submitWork);
86✔
193
    this.add('getblocktemplate', this.getBlockTemplate);
86✔
194
    this.add('submitblock', this.submitBlock);
86✔
195
    this.add('verifyblock', this.verifyBlock);
86✔
196

197
    this.add('setgenerate', this.setGenerate);
86✔
198
    this.add('getgenerate', this.getGenerate);
86✔
199
    this.add('generate', this.generate);
86✔
200
    this.add('generatetoaddress', this.generateToAddress);
86✔
201

202
    this.add('estimatefee', this.estimateFee);
86✔
203
    this.add('estimatepriority', this.estimatePriority);
86✔
204
    this.add('estimatesmartfee', this.estimateSmartFee);
86✔
205
    this.add('estimatesmartpriority', this.estimateSmartPriority);
86✔
206

207
    this.add('getinfo', this.getInfo);
86✔
208
    this.add('validateaddress', this.validateAddress);
86✔
209
    this.add('createmultisig', this.createMultisig);
86✔
210
    this.add('verifymessage', this.verifyMessage);
86✔
211
    this.add('verifymessagewithname', this.verifyMessageWithName);
86✔
212
    this.add('signmessagewithprivkey', this.signMessageWithPrivkey);
86✔
213

214
    this.add('setmocktime', this.setMockTime);
86✔
215

216
    this.add('getconnectioncount', this.getConnectionCount);
86✔
217
    this.add('ping', this.ping);
86✔
218
    this.add('getpeerinfo', this.getPeerInfo);
86✔
219
    this.add('addnode', this.addNode);
86✔
220
    this.add('disconnectnode', this.disconnectNode);
86✔
221
    this.add('getaddednodeinfo', this.getAddedNodeInfo);
86✔
222
    this.add('getnettotals', this.getNetTotals);
86✔
223
    this.add('getnetworkinfo', this.getNetworkInfo);
86✔
224
    this.add('setban', this.setBan);
86✔
225
    this.add('listbanned', this.listBanned);
86✔
226
    this.add('clearbanned', this.clearBanned);
86✔
227

228
    this.add('getrawtransaction', this.getRawTransaction);
86✔
229
    this.add('createrawtransaction', this.createRawTransaction);
86✔
230
    this.add('decoderawtransaction', this.decodeRawTransaction);
86✔
231
    this.add('decodescript', this.decodeScript);
86✔
232
    this.add('decoderesource', this.decodeResource);
86✔
233
    this.add('sendrawtransaction', this.sendRawTransaction);
86✔
234
    this.add('signrawtransaction', this.signRawTransaction);
86✔
235

236
    this.add('gettxoutproof', this.getTXOutProof);
86✔
237
    this.add('verifytxoutproof', this.verifyTXOutProof);
86✔
238

239
    this.add('getmemoryinfo', this.getMemoryInfo);
86✔
240
    this.add('setloglevel', this.setLogLevel);
86✔
241
    this.add('getnames', this.getNames);
86✔
242
    this.add('getnameinfo', this.getNameInfo);
86✔
243
    this.add('getnameresource', this.getNameResource);
86✔
244
    this.add('getnameproof', this.getNameProof);
86✔
245
    this.add('getdnssecproof', this.getDNSSECProof);
86✔
246
    this.add('getnamebyhash', this.getNameByHash);
86✔
247
    this.add('grindname', this.grindName);
86✔
248
    this.add('sendrawclaim', this.sendRawClaim);
86✔
249
    this.add('sendrawairdrop', this.sendRawAirdrop);
86✔
250
    this.add('validateresource', this.validateResource);
86✔
251

252
    this.add('resetrootcache', this.resetRootCache);
86✔
253

254
    // Compat
255
    // this.add('getnameinfo', this.getNameInfo);
256
    // this.add('getnameresource', this.getNameResource);
257
    // this.add('getnameproof', this.getNameProof);
258
  }
259

260
  /*
261
   * Overall control/query calls
262
   */
263

264
  async getInfo(args, help) {
265
    if (help || args.length !== 0)
×
266
      throw new RPCError(errs.MISC_ERROR, 'getinfo');
×
267

268
    return {
×
269
      version: pkg.version,
270
      protocolversion: this.pool.options.version,
271
      walletversion: 0,
272
      balance: 0,
273
      blocks: this.chain.height,
274
      timeoffset: this.network.time.offset,
275
      connections: this.pool.peers.size(),
276
      proxy: '',
277
      difficulty: toDifficulty(this.chain.tip.bits),
278
      testnet: this.network !== Network.main,
279
      keypoololdest: 0,
280
      keypoolsize: 0,
281
      unlocked_until: 0,
282
      paytxfee: Amount.coin(this.network.feeRate, true),
283
      relayfee: Amount.coin(this.network.minRelay, true),
284
      errors: ''
285
    };
286
  }
287

288
  async help(args, _help) {
289
    if (args.length === 0)
×
290
      return 'Select a command.';
×
291

292
    const json = {
×
293
      method: args[0],
294
      params: []
295
    };
296

297
    return this.execute(json, true);
×
298
  }
299

300
  async stop(args, help) {
301
    if (help || args.length !== 0)
×
302
      throw new RPCError(errs.MISC_ERROR, 'stop');
×
303

304
    this.node.close().catch((err) => {
×
305
      setImmediate(() => {
×
306
        throw err;
×
307
      });
308
    });
309

310
    return 'Stopping.';
×
311
  }
312

313
  /*
314
   * P2P networking
315
   */
316

317
  async getNetworkInfo(args, help) {
318
    if (help || args.length !== 0)
1!
319
      throw new RPCError(errs.MISC_ERROR, 'getnetworkinfo');
×
320

321
    const hosts = this.pool.hosts;
1✔
322
    const locals = [];
1✔
323

324
    for (const local of hosts.local.values()) {
1✔
325
      locals.push({
×
326
        address: local.addr.host,
327
        port: local.addr.port,
328
        score: local.score
329
      });
330
    }
331

332
    return {
1✔
333
      version: pkg.version,
334
      subversion: this.pool.options.agent,
335
      protocolversion: this.pool.options.version,
336
      identitykey: this.pool.hosts.brontide.getKey('base32'),
337
      localservices: util.hex32(this.pool.options.services),
338
      localservicenames: this.pool.getServiceNames(),
339
      localrelay: !this.pool.options.noRelay,
340
      timeoffset: this.network.time.offset,
341
      networkactive: this.pool.connected,
342
      connections: this.pool.peers.size(),
343
      networks: [],
344
      relayfee: Amount.coin(this.network.minRelay, true),
345
      incrementalfee: 0,
346
      localaddresses: locals,
347
      warnings: ''
348
    };
349
  }
350

351
  async addNode(args, help) {
352
    if (help || args.length !== 2)
×
353
      throw new RPCError(errs.MISC_ERROR, 'addnode "node" "add|remove|onetry"');
×
354

355
    const valid = new Validator(args);
×
356
    const node = valid.str(0, '');
×
357
    const cmd = valid.str(1, '');
×
358

359
    switch (cmd) {
×
360
      case 'add': {
361
        this.pool.hosts.addNode(node);
×
362
        ; // fall through
363
      }
364
      case 'onetry': {
365
        const addr = parseNetAddress(node, this.network);
×
366

367
        if (!this.pool.peers.get(addr.hostname)) {
×
368
          const peer = this.pool.createOutbound(addr);
×
369
          this.pool.peers.add(peer);
×
370
        }
371

372
        break;
×
373
      }
374
      case 'remove': {
375
        this.pool.hosts.removeNode(node);
×
376
        break;
×
377
      }
378
    }
379

380
    return null;
×
381
  }
382

383
  async disconnectNode(args, help) {
384
    if (help || args.length !== 1)
×
385
      throw new RPCError(errs.MISC_ERROR, 'disconnectnode "node"');
×
386

387
    const valid = new Validator(args);
×
388
    const str = valid.str(0, '');
×
389

390
    const addr = parseIP(str, this.network);
×
391
    const peer = this.pool.peers.get(addr.hostname);
×
392

393
    if (peer)
×
394
      peer.destroy();
×
395

396
    return null;
×
397
  }
398

399
  async getAddedNodeInfo(args, help) {
400
    if (help || args.length > 1)
×
401
      throw new RPCError(errs.MISC_ERROR, 'getaddednodeinfo ( "node" )');
×
402

403
    const hosts = this.pool.hosts;
×
404
    const valid = new Validator(args);
×
405
    const addr = valid.str(0, '');
×
406

407
    let target;
408
    if (args.length === 1)
×
409
      target = parseIP(addr, this.network);
×
410

411
    const result = [];
×
412

413
    for (const node of hosts.nodes) {
×
414
      if (target) {
×
415
        if (node.host !== target.host)
×
416
          continue;
×
417

418
        if (node.port !== target.port)
×
419
          continue;
×
420
      }
421

422
      const peer = this.pool.peers.get(node.hostname);
×
423

424
      if (!peer || !peer.connected) {
×
425
        result.push({
×
426
          addednode: node.hostname,
427
          connected: false,
428
          addresses: []
429
        });
430
        continue;
×
431
      }
432

433
      result.push({
×
434
        addednode: node.hostname,
435
        connected: peer.connected,
436
        addresses: [
437
          {
438
            address: peer.hostname(),
439
            connected: peer.outbound
×
440
              ? 'outbound'
441
              : 'inbound'
442
          }
443
        ]
444
      });
445
    }
446

447
    if (target && result.length === 0) {
×
448
      throw new RPCError(errs.CLIENT_NODE_NOT_ADDED,
×
449
        'Node has not been added.');
450
    }
451

452
    return result;
×
453
  }
454

455
  async getConnectionCount(args, help) {
456
    if (help || args.length !== 0)
×
457
      throw new RPCError(errs.MISC_ERROR, 'getconnectioncount');
×
458

459
    return this.pool.peers.size();
×
460
  }
461

462
  async getNetTotals(args, help) {
463
    let sent = 0;
×
464
    let recv = 0;
×
465

466
    if (help || args.length > 0)
×
467
      throw new RPCError(errs.MISC_ERROR, 'getnettotals');
×
468

469
    for (let peer = this.pool.peers.head(); peer; peer = peer.next) {
×
470
      sent += peer.socket.bytesWritten;
×
471
      recv += peer.socket.bytesRead;
×
472
    }
473

474
    return {
×
475
      totalbytesrecv: recv,
476
      totalbytessent: sent,
477
      timemillis: Date.now()
478
    };
479
  }
480

481
  async getPeerInfo(args, help) {
482
    if (help || args.length > 1)
×
483
      throw new RPCError(errs.MISC_ERROR, 'getpeerinfo ( "type" )');
×
484

485
    const valid = new Validator(args);
×
486
    const type = valid.str(0);
×
487
    const peers = [];
×
488

489
    for (let peer = this.pool.peers.head(); peer; peer = peer.next) {
×
490
      if (!peer.connected)
×
491
        continue;
×
492

493
      if (type && peer.outbound !== (type === 'outbound'))
×
494
        continue;
×
495

496
      const offset = this.network.time.known.get(peer.hostname()) || 0;
×
497
      const hashes = [];
×
498

499
      for (const hash in peer.blockMap.keys())
×
500
        hashes.push(hash.toString('hex'));
×
501

502
      peer.getName();
×
503

504
      peers.push({
×
505
        id: peer.id,
506
        addr: peer.hostname(),
507
        addrlocal: !peer.local.isNull()
×
508
          ? peer.local.hostname
509
          : undefined,
510
        name: peer.name || undefined,
×
511
        services: util.hex32(peer.services),
512
        servicenames: peer.getServiceNames(),
513
        relaytxes: !peer.noRelay,
514
        lastsend: peer.lastSend / 1000 | 0,
515
        lastrecv: peer.lastRecv / 1000 | 0,
516
        bytessent: peer.socket.bytesWritten,
517
        bytesrecv: peer.socket.bytesRead,
518
        conntime: peer.time !== 0 ? (Date.now() - peer.time) / 1000 | 0 : 0,
×
519
        timeoffset: offset,
520
        pingtime: peer.lastPong !== -1
×
521
          ? (peer.lastPong - peer.lastPing) / 1000
522
          : -1,
523
        minping: peer.minPing !== -1 ? peer.minPing / 1000 : -1,
×
524
        version: peer.version,
525
        subver: peer.agent,
526
        inbound: !peer.outbound,
527
        startingheight: peer.height,
528
        besthash: peer.bestHash.toString('hex'),
529
        bestheight: peer.bestHeight,
530
        banscore: peer.banScore,
531
        inflight: hashes,
532
        whitelisted: false
533
      });
534
    }
535

536
    return peers;
×
537
  }
538

539
  async ping(args, help) {
540
    if (help || args.length !== 0)
×
541
      throw new RPCError(errs.MISC_ERROR, 'ping');
×
542

543
    for (let peer = this.pool.peers.head(); peer; peer = peer.next)
×
544
      peer.sendPing();
×
545

546
    return null;
×
547
  }
548

549
  async setBan(args, help) {
550
    const valid = new Validator(args);
×
551
    const str = valid.str(0, '');
×
552
    const action = valid.str(1, '');
×
553

554
    if (help
×
555
        || args.length < 2
556
        || (action !== 'add' && action !== 'remove')) {
557
      throw new RPCError(errs.MISC_ERROR,
×
558
        'setban "ip(/netmask)" "add|remove" (bantime) (absolute)');
559
    }
560

561
    const addr = parseNetAddress(str, this.network);
×
562

563
    switch (action) {
×
564
      case 'add':
565
        this.pool.ban(addr);
×
566
        break;
×
567
      case 'remove':
568
        this.pool.unban(addr);
×
569
        break;
×
570
    }
571

572
    return null;
×
573
  }
574

575
  async listBanned(args, help) {
576
    if (help || args.length !== 0)
×
577
      throw new RPCError(errs.MISC_ERROR, 'listbanned');
×
578

579
    const banned = [];
×
580

581
    for (const [host, time] of this.pool.hosts.banned) {
×
582
      banned.push({
×
583
        address: host,
584
        banned_until: time + this.pool.options.banTime,
585
        ban_created: time,
586
        ban_reason: ''
587
      });
588
    }
589

590
    return banned;
×
591
  }
592

593
  async clearBanned(args, help) {
594
    if (help || args.length !== 0)
×
595
      throw new RPCError(errs.MISC_ERROR, 'clearbanned');
×
596

597
    this.pool.hosts.clearBanned();
×
598

599
    return null;
×
600
  }
601

602
  /* Block chain and UTXO */
603
  async getBlockchainInfo(args, help) {
604
    if (help || args.length !== 0)
1!
605
      throw new RPCError(errs.MISC_ERROR, 'getblockchaininfo');
×
606

607
    return {
1✔
608
      chain: this.network.type !== 'testnet'
1!
609
        ? this.network.type
610
        : 'test',
611
      blocks: this.chain.height,
612
      headers: this.chain.height,
613
      bestblockhash: this.chain.tip.hash.toString('hex'),
614
      treeroot: this.chain.tip.treeRoot.toString('hex'),
615
      difficulty: toDifficulty(this.chain.tip.bits),
616
      mediantime: await this.chain.getMedianTime(this.chain.tip),
617
      verificationprogress: this.chain.getProgress(),
618
      chainwork: this.chain.tip.chainwork.toString('hex', 64),
619
      pruned: this.chain.options.prune,
620
      softforks: await this.getSoftforks(),
621
      deflationary: this.chain.height >= this.network.deflationHeight,
622
      pruneheight: this.chain.options.prune
1!
623
        ? Math.max(0, this.chain.height - this.network.block.keepBlocks)
624
        : null
625
    };
626
  }
627

628
  async getBestBlockHash(args, help) {
629
    if (help || args.length !== 0)
×
630
      throw new RPCError(errs.MISC_ERROR, 'getbestblockhash');
×
631

632
    return this.chain.tip.hash.toString('hex');
×
633
  }
634

635
  async getBlockCount(args, help) {
636
    if (help || args.length !== 0)
1!
637
      throw new RPCError(errs.MISC_ERROR, 'getblockcount');
×
638

639
    return this.chain.tip.height;
1✔
640
  }
641

642
  async getBlock(args, help) {
643
    if (help || args.length < 1 || args.length > 3)
65!
644
      throw new RPCError(errs.MISC_ERROR, 'getblock "hash" ( verbose )');
×
645

646
    const valid = new Validator(args);
65✔
647
    const hash = valid.bhash(0);
65✔
648
    const verbose = valid.bool(1, true);
65✔
649
    const details = valid.bool(2, false);
65✔
650

651
    if (!hash)
65!
652
      throw new RPCError(errs.TYPE_ERROR, 'Invalid block hash.');
×
653

654
    const entry = await this.chain.getEntry(hash);
65✔
655

656
    if (!entry)
65!
657
      throw new RPCError(errs.MISC_ERROR, 'Block not found');
×
658

659
    const block = await this.chain.getBlock(entry.hash);
65✔
660

661
    if (!block) {
65✔
662
      if (this.chain.options.spv)
10!
663
        throw new RPCError(errs.MISC_ERROR, 'Block not available (spv mode)');
×
664

665
      if (this.chain.options.prune) {
10!
666
        throw new RPCError(errs.MISC_ERROR,
10✔
667
          'Block not available (pruned data)');
668
      }
669

670
      throw new RPCError(errs.MISC_ERROR, 'Can\'t read block from disk');
×
671
    }
672

673
    if (!verbose)
55!
674
      return block.toHex();
×
675

676
    return this.blockToJSON(entry, block, details);
55✔
677
  }
678

679
  async getBlockByHeight(args, help) {
680
    if (help || args.length < 1 || args.length > 3) {
20!
681
      throw new RPCError(errs.MISC_ERROR,
×
682
        'getblockbyheight "height" ( verbose )');
683
    }
684

685
    const valid = new Validator(args);
20✔
686
    const height = valid.u32(0, -1);
20✔
687
    const verbose = valid.bool(1, true);
20✔
688
    const details = valid.bool(2, false);
20✔
689

690
    if (height === -1)
20!
691
      throw new RPCError(errs.TYPE_ERROR, 'Invalid block height.');
×
692

693
    const entry = await this.chain.getEntry(height);
20✔
694

695
    if (!entry)
20!
696
      throw new RPCError(errs.MISC_ERROR, 'Block not found');
×
697

698
    const block = await this.chain.getBlock(entry.hash);
20✔
699

700
    if (!block) {
20!
701
      if (this.chain.options.spv)
×
702
        throw new RPCError(errs.MISC_ERROR, 'Block not available (spv mode)');
×
703

704
      if (this.chain.options.prune) {
×
705
        throw new RPCError(errs.MISC_ERROR,
×
706
          'Block not available (pruned data)');
707
      }
708

709
      throw new RPCError(errs.DATABASE_ERROR, 'Can\'t read block from disk');
×
710
    }
711

712
    if (!verbose)
20!
713
      return block.toHex();
×
714

715
    return this.blockToJSON(entry, block, details);
20✔
716
  }
717

718
  async getBlockHash(args, help) {
719
    if (help || args.length !== 1)
×
720
      throw new RPCError(errs.MISC_ERROR, 'getblockhash index');
×
721

722
    const valid = new Validator(args);
×
723
    const height = valid.u32(0);
×
724

725
    if (height == null || height > this.chain.height)
×
726
      throw new RPCError(errs.INVALID_PARAMETER, 'Block height out of range.');
×
727

728
    const hash = await this.chain.getHash(height);
×
729

730
    if (!hash)
×
731
      throw new RPCError(errs.MISC_ERROR, 'Not found.');
×
732

733
    return hash.toString('hex');
×
734
  }
735

736
  async getBlockHeader(args, help) {
737
    if (help || args.length < 1 || args.length > 2)
×
738
      throw new RPCError(errs.MISC_ERROR, 'getblockheader "hash" ( verbose )');
×
739

740
    const valid = new Validator(args);
×
741
    const hash = valid.bhash(0);
×
742
    const verbose = valid.bool(1, true);
×
743

744
    if (!hash)
×
745
      throw new RPCError(errs.MISC_ERROR, 'Invalid block hash.');
×
746

747
    const entry = await this.chain.getEntry(hash);
×
748

749
    if (!entry)
×
750
      throw new RPCError(errs.MISC_ERROR, 'Block not found');
×
751

752
    if (!verbose)
×
753
      return entry.encode().toString('hex', 36, 36 + consensus.HEADER_SIZE);
×
754

755
    return this.headerToJSON(entry);
×
756
  }
757

758
  async getChainTips(args, help) {
759
    if (help || args.length !== 0)
×
760
      throw new RPCError(errs.MISC_ERROR, 'getchaintips');
×
761

762
    const tips = await this.chain.getTips();
×
763
    const result = [];
×
764

765
    for (const hash of tips) {
×
766
      const entry = await this.chain.getEntry(hash);
×
767

768
      assert(entry);
×
769

770
      const fork = await this.findFork(entry);
×
771
      const main = await this.chain.isMainChain(entry);
×
772

773
      result.push({
×
774
        height: entry.height,
775
        hash: entry.hash.toString('hex'),
776
        branchlen: entry.height - fork.height,
777
        status: main ? 'active' : 'valid-headers'
×
778
      });
779
    }
780

781
    return result;
×
782
  }
783

784
  async getDifficulty(args, help) {
785
    if (help || args.length !== 0)
×
786
      throw new RPCError(errs.MISC_ERROR, 'getdifficulty');
×
787

788
    return toDifficulty(this.chain.tip.bits);
×
789
  }
790

791
  async getMempoolInfo(args, help) {
792
    if (help || args.length !== 0)
×
793
      throw new RPCError(errs.MISC_ERROR, 'getmempoolinfo');
×
794

795
    if (!this.mempool)
×
796
      throw new RPCError(errs.MISC_ERROR, 'No mempool available.');
×
797

798
    return {
×
799
      size: this.mempool.map.size,
800
      bytes: this.mempool.getSize(),
801
      usage: this.mempool.getSize(),
802
      maxmempool: this.mempool.options.maxSize,
803
      mempoolminfee: Amount.coin(this.mempool.options.minRelay, true)
804
    };
805
  }
806

807
  async getMempoolAncestors(args, help) {
808
    if (help || args.length < 1 || args.length > 2)
×
809
      throw new RPCError(errs.MISC_ERROR, 'getmempoolancestors txid (verbose)');
×
810

811
    const valid = new Validator(args);
×
812
    const hash = valid.bhash(0);
×
813
    const verbose = valid.bool(1, false);
×
814

815
    if (!this.mempool)
×
816
      throw new RPCError(errs.MISC_ERROR, 'No mempool available.');
×
817

818
    if (!hash)
×
819
      throw new RPCError(errs.TYPE_ERROR, 'Invalid TXID.');
×
820

821
    const entry = this.mempool.getEntry(hash);
×
822

823
    if (!entry)
×
824
      throw new RPCError(errs.MISC_ERROR, 'Transaction not in mempool.');
×
825

826
    const entries = this.mempool.getAncestors(entry);
×
827
    const out = [];
×
828

829
    if (verbose) {
×
830
      for (const entry of entries)
×
831
        out.push(this.entryToJSON(entry));
×
832
    } else {
833
      for (const entry of entries)
×
834
        out.push(entry.txid());
×
835
    }
836

837
    return out;
×
838
  }
839

840
  async getMempoolDescendants(args, help) {
841
    if (help || args.length < 1 || args.length > 2) {
×
842
      throw new RPCError(errs.MISC_ERROR,
×
843
        'getmempooldescendants txid (verbose)');
844
    }
845

846
    const valid = new Validator(args);
×
847
    const hash = valid.bhash(0);
×
848
    const verbose = valid.bool(1, false);
×
849

850
    if (!this.mempool)
×
851
      throw new RPCError(errs.MISC_ERROR, 'No mempool available.');
×
852

853
    if (!hash)
×
854
      throw new RPCError(errs.TYPE_ERROR, 'Invalid TXID.');
×
855

856
    const entry = this.mempool.getEntry(hash);
×
857

858
    if (!entry)
×
859
      throw new RPCError(errs.MISC_ERROR, 'Transaction not in mempool.');
×
860

861
    const entries = this.mempool.getDescendants(entry);
×
862
    const out = [];
×
863

864
    if (verbose) {
×
865
      for (const entry of entries)
×
866
        out.push(this.entryToJSON(entry));
×
867
    } else {
868
      for (const entry of entries)
×
869
        out.push(entry.txid());
×
870
    }
871

872
    return out;
×
873
  }
874

875
  async getMempoolEntry(args, help) {
876
    if (help || args.length !== 1)
×
877
      throw new RPCError(errs.MISC_ERROR, 'getmempoolentry txid');
×
878

879
    const valid = new Validator(args);
×
880
    const hash = valid.bhash(0);
×
881

882
    if (!this.mempool)
×
883
      throw new RPCError(errs.MISC_ERROR, 'No mempool available.');
×
884

885
    if (!hash)
×
886
      throw new RPCError(errs.TYPE_ERROR, 'Invalid TXID.');
×
887

888
    const entry = this.mempool.getEntry(hash);
×
889

890
    if (!entry)
×
891
      throw new RPCError(errs.MISC_ERROR, 'Transaction not in mempool.');
×
892

893
    return this.entryToJSON(entry);
×
894
  }
895

896
  async getRawMempool(args, help) {
897
    if (help || args.length > 1)
1!
898
      throw new RPCError(errs.MISC_ERROR, 'getrawmempool ( verbose )');
×
899

900
    const valid = new Validator(args);
1✔
901
    const verbose = valid.bool(0, false);
1✔
902

903
    if (!this.mempool)
1!
904
      throw new RPCError(errs.MISC_ERROR, 'No mempool available.');
×
905

906
    if (verbose) {
1!
907
      const out = Object.create(null);
1✔
908

909
      for (const entry of this.mempool.map.values())
1✔
910
        out[entry.txid()] = this.entryToJSON(entry);
×
911

912
      return out;
1✔
913
    }
914

915
    const hashes = this.mempool.getSnapshot();
×
916

917
    return hashes.map(hash => hash.toString('hex'));
×
918
  }
919

920
  async getTXOut(args, help) {
921
    if (help || args.length < 2 || args.length > 3) {
×
922
      throw new RPCError(errs.MISC_ERROR,
×
923
        'gettxout "txid" n ( includemempool )');
924
    }
925

926
    const valid = new Validator(args);
×
927
    const hash = valid.bhash(0);
×
928
    const index = valid.u32(1);
×
929
    const mempool = valid.bool(2, true);
×
930

931
    if (this.chain.options.spv)
×
932
      throw new RPCError(errs.MISC_ERROR, 'Cannot get coins in SPV mode.');
×
933

934
    if (this.chain.options.prune)
×
935
      throw new RPCError(errs.MISC_ERROR, 'Cannot get coins when pruned.');
×
936

937
    if (!hash || index == null)
×
938
      throw new RPCError(errs.TYPE_ERROR, 'Invalid outpoint.');
×
939

940
    let coin;
941
    if (mempool) {
×
942
      if (!this.mempool)
×
943
        throw new RPCError(errs.MISC_ERROR, 'No mempool available.');
×
944
      coin = this.mempool.getCoin(hash, index);
×
945
    }
946

947
    if (!coin)
×
948
      coin = await this.chain.getCoin(hash, index);
×
949

950
    if (!coin)
×
951
      return null;
×
952

953
    return {
×
954
      bestblock: this.chain.tip.hash.toString('hex'),
955
      confirmations: coin.getDepth(this.chain.height),
956
      value: Amount.coin(coin.value, true),
957
      address: this.addrToJSON(coin.address),
958
      version: coin.version,
959
      coinbase: coin.coinbase
960
    };
961
  }
962

963
  async getTXOutProof(args, help) {
964
    if (help || (args.length !== 1 && args.length !== 2)) {
×
965
      throw new RPCError(errs.MISC_ERROR,
×
966
        'gettxoutproof ["txid",...] ( blockhash )');
967
    }
968

969
    const valid = new Validator(args);
×
970
    const txids = valid.array(0);
×
971
    const hash = valid.bhash(1);
×
972

973
    if (this.chain.options.spv)
×
974
      throw new RPCError(errs.MISC_ERROR, 'Cannot get coins in SPV mode.');
×
975

976
    if (this.chain.options.prune)
×
977
      throw new RPCError(errs.MISC_ERROR, 'Cannot get coins when pruned.');
×
978

979
    if (!txids || txids.length === 0)
×
980
      throw new RPCError(errs.INVALID_PARAMETER, 'Invalid TXIDs.');
×
981

982
    const items = new Validator(txids);
×
983
    const set = new BufferSet();
×
984
    const hashes = [];
×
985

986
    let last = null;
×
987

988
    for (let i = 0; i < txids.length; i++) {
×
989
      const hash = items.bhash(i);
×
990

991
      if (!hash)
×
992
        throw new RPCError(errs.TYPE_ERROR, 'Invalid TXID.');
×
993

994
      if (set.has(hash))
×
995
        throw new RPCError(errs.INVALID_PARAMETER, 'Duplicate txid.');
×
996

997
      set.add(hash);
×
998
      hashes.push(hash);
×
999

1000
      last = hash;
×
1001
    }
1002

1003
    let block = null;
×
1004

1005
    if (hash) {
×
1006
      block = await this.chain.getBlock(hash);
×
1007
    } else if (this.chain.options.indexTX) {
×
1008
      const tx = await this.chain.getMeta(last);
×
1009
      if (tx)
×
1010
        block = await this.chain.getBlock(tx.block);
×
1011
    } else {
1012
      const coin = await this.chain.getCoin(last, 0);
×
1013
      if (coin)
×
1014
        block = await this.chain.getBlock(coin.height);
×
1015
    }
1016

1017
    if (!block)
×
1018
      throw new RPCError(errs.MISC_ERROR, 'Block not found.');
×
1019

1020
    const whashes = [];
×
1021

1022
    for (const hash of hashes) {
×
1023
      const index = block.indexOf(hash);
×
1024

1025
      if (index === -1) {
×
1026
        throw new RPCError(errs.VERIFY_ERROR,
×
1027
          'Block does not contain all txids.');
1028
      }
1029

1030
      const tx = block.txs[index];
×
1031

1032
      whashes.push(tx.hash());
×
1033
    }
1034

1035
    const mblock = MerkleBlock.fromHashes(block, whashes);
×
1036

1037
    return mblock.toHex();
×
1038
  }
1039

1040
  async verifyTXOutProof(args, help) {
1041
    if (help || args.length !== 1)
×
1042
      throw new RPCError(errs.MISC_ERROR, 'verifytxoutproof "proof"');
×
1043

1044
    const valid = new Validator(args);
×
1045
    const data = valid.buf(0);
×
1046

1047
    if (!data)
×
1048
      throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.');
×
1049

1050
    const block = MerkleBlock.decode(data);
×
1051

1052
    if (!block.verify())
×
1053
      return [];
×
1054

1055
    const entry = await this.chain.getEntry(block.hash());
×
1056

1057
    if (!entry)
×
1058
      throw new RPCError(errs.MISC_ERROR, 'Block not found in chain.');
×
1059

1060
    const tree = block.getTree();
×
1061
    const out = [];
×
1062

1063
    for (const hash of tree.matches)
×
1064
      out.push(hash.toString('hex'));
×
1065

1066
    return out;
×
1067
  }
1068

1069
  async getTXOutSetInfo(args, help) {
1070
    if (help || args.length !== 0)
×
1071
      throw new RPCError(errs.MISC_ERROR, 'gettxoutsetinfo');
×
1072

1073
    if (this.chain.options.spv) {
×
1074
      throw new RPCError(errs.MISC_ERROR,
×
1075
        'Chainstate not available (SPV mode).');
1076
    }
1077

1078
    return {
×
1079
      height: this.chain.height,
1080
      bestblock: this.chain.tip.hash.toString('hex'),
1081
      transactions: this.chain.db.state.tx,
1082
      txouts: this.chain.db.state.coin,
1083
      bytes_serialized: 0,
1084
      hash_serialized: 0,
1085
      total_amount: Amount.coin(this.chain.db.state.value, true),
1086
      total_burned: Amount.coin(this.chain.db.state.burned, true)
1087
    };
1088
  }
1089

1090
  async pruneBlockchain(args, help) {
1091
    if (help || args.length !== 0)
5✔
1092
      throw new RPCError(errs.MISC_ERROR, 'pruneblockchain');
1✔
1093

1094
    if (this.chain.options.spv)
4✔
1095
      throw new RPCError(errs.MISC_ERROR, 'Cannot prune chain in SPV mode.');
1✔
1096

1097
    if (this.chain.options.prune)
3✔
1098
      throw new RPCError(errs.MISC_ERROR, 'Chain is already pruned.');
1✔
1099

1100
    if (this.chain.height < this.network.block.pruneAfterHeight)
2✔
1101
      throw new RPCError(errs.MISC_ERROR, 'Chain is too short for pruning.');
1✔
1102

1103
    try {
1✔
1104
      await this.chain.prune();
1✔
1105
    } catch (e) {
1106
      throw new RPCError(errs.DATABASE_ERROR, e.message);
×
1107
    }
1108
  }
1109

1110
    async compactTree(args, help) {
1111
    if (help || args.length !== 0)
2!
1112
      throw new RPCError(errs.MISC_ERROR, 'compacttree');
×
1113

1114
    if (this.chain.options.spv)
2✔
1115
      throw new RPCError(errs.MISC_ERROR, 'Cannot compact tree in SPV mode.');
1✔
1116

1117
    try {
1✔
1118
      await this.chain.compactTree();
1✔
1119
    } catch (e) {
1120
      throw new RPCError(errs.DATABASE_ERROR, e.message);
×
1121
    }
1122
  }
1123

1124
  async reconstructTree(args, help) {
1125
    if (help || args.length !== 0)
2!
1126
      throw new RPCError(errs.MISC_ERROR, 'reconstructtree');
×
1127

1128
    if (this.chain.options.spv) {
2✔
1129
      throw new RPCError(errs.MISC_ERROR,
1✔
1130
        'Cannot reconstruct tree in SPV mode.');
1131
    }
1132

1133
    if (this.chain.options.prune) {
1!
1134
      throw new RPCError(errs.MISC_ERROR,
×
1135
        'Cannot reconstruct tree in pruned node.');
1136
    }
1137

1138
    try {
1✔
1139
      await this.chain.reconstructTree();
1✔
1140
    } catch (e) {
1141
      throw new RPCError(errs.DATABASE_ERROR, e.message);
×
1142
    }
1143
  }
1144

1145
  async verifyChain(args, help) {
1146
    if (help || args.length > 2) {
×
1147
      throw new RPCError(errs.MISC_ERROR,
×
1148
        'verifychain ( checklevel numblocks )');
1149
    }
1150

1151
    const valid = new Validator(args);
×
1152
    const level = valid.u32(0);
×
1153
    const blocks = valid.u32(1);
×
1154

1155
    if (level == null || blocks == null)
×
1156
      throw new RPCError(errs.TYPE_ERROR, 'Missing parameters.');
×
1157

1158
    if (this.chain.options.spv)
×
1159
      throw new RPCError(errs.MISC_ERROR, 'Cannot verify chain in SPV mode.');
×
1160

1161
    if (this.chain.options.prune)
×
1162
      throw new RPCError(errs.MISC_ERROR, 'Cannot verify chain when pruned.');
×
1163

1164
    return null;
×
1165
  }
1166

1167
  /*
1168
   * Mining
1169
   */
1170

1171
  async handleWork(data, mask) {
1172
    const unlock = await this.locker.lock();
7✔
1173
    try {
7✔
1174
      return await this._handleWork(data, mask);
7✔
1175
    } finally {
1176
      unlock();
7✔
1177
    }
1178
  }
1179

1180
  async _handleWork(data, mask) {
1181
    if (data.length !== 256)
7!
1182
      return [false, 'invalid-data-length'];
×
1183

1184
    const hdr = Headers.fromMiner(data);
7✔
1185
    const maskHash = blake2b.multi(hdr.prevBlock, mask);
7✔
1186

1187
    if (!hdr.maskHash().equals(maskHash))
7!
1188
      return [false, 'bad-maskhash'];
×
1189

1190
    const attempt = this.merkleMap.get(hdr.witnessRoot);
7✔
1191

1192
    if (!attempt)
7✔
1193
      return [false, 'stale'];
3✔
1194

1195
    if (!hdr.prevBlock.equals(attempt.prevBlock)
4!
1196
        || hdr.bits !== attempt.bits) {
1197
      return [false, 'stale'];
×
1198
    }
1199

1200
    const {nonce, time, extraNonce} = hdr;
4✔
1201
    const proof = attempt.getProof(nonce, time, extraNonce, mask);
4✔
1202

1203
    if (!proof.verify(attempt.target, this.network))
4!
1204
      return [false, 'bad-diffbits'];
×
1205

1206
    const block = attempt.commit(proof);
4✔
1207

1208
    let entry;
1209
    try {
4✔
1210
      entry = await this.chain.add(block);
4✔
1211
    } catch (err) {
1212
      if (err.type === 'VerifyError') {
×
1213
        this.logger.warning('RPC block rejected: %x (%s).',
×
1214
          block.hash(), err.reason);
1215
        return [false, err.reason];
×
1216
      }
1217
      throw err;
×
1218
    }
1219

1220
    if (!entry) {
4!
1221
      this.logger.warning('RPC block rejected: %x (bad-prevblk).',
×
1222
        block.hash());
1223
      return [false, 'bad-prevblk'];
×
1224
    }
1225

1226
    return [true, 'valid'];
4✔
1227
  }
1228

1229
  async createWork() {
1230
    const unlock = await this.locker.lock();
10✔
1231
    try {
10✔
1232
      return await this._createWork();
10✔
1233
    } finally {
1234
      unlock();
10✔
1235
    }
1236
  }
1237

1238
  async _createWork() {
1239
    const attempt = await this.updateWork();
10✔
1240
    const time = attempt.time;
10✔
1241
    const nonce = consensus.ZERO_NONCE;
10✔
1242
    const mask = consensus.ZERO_HASH;
10✔
1243
    const data = attempt.getHeader(0, time, nonce, mask);
10✔
1244

1245
    return {
10✔
1246
      network: this.network.type,
1247
      data: data.toString('hex'),
1248
      target: attempt.target.toString('hex'),
1249
      height: attempt.height,
1250
      time: this.network.now(),
1251
      fee: attempt.fees
1252
    };
1253
  }
1254

1255
  async getWorkLongpoll(args, help) {
1256
    await this.longpoll();
×
1257
    return this.createWork();
×
1258
  }
1259

1260
  async getWork(args, help) {
1261
    if (help || args.length !== 0)
10!
1262
      throw new RPCError(errs.MISC_ERROR, 'getwork');
×
1263

1264
    return this.createWork();
10✔
1265
  }
1266

1267
  async submitWork(args, help) {
1268
    if (help || args.length < 1 || args.length > 2)
7!
1269
      throw new RPCError(errs.MISC_ERROR, 'submitwork ( "data" "mask" )');
×
1270

1271
    const valid = new Validator(args);
7✔
1272
    const data = valid.buf(0);
7✔
1273

1274
    if (!data)
7!
1275
      throw new RPCError(errs.TYPE_ERROR, 'Invalid work data.');
×
1276

1277
    let mask = consensus.ZERO_HASH;
7✔
1278

1279
    if (args.length === 2) {
7!
1280
      mask = valid.bhash(1);
×
1281

1282
      if (!mask)
×
1283
        throw new RPCError(errs.TYPE_ERROR, 'Invalid mask.');
×
1284
    }
1285

1286
    return this.handleWork(data, mask);
7✔
1287
  }
1288

1289
  async submitBlock(args, help) {
1290
    if (help || args.length < 1 || args.length > 2) {
1!
1291
      throw new RPCError(errs.MISC_ERROR,
×
1292
        'submitblock "hexdata" ( "jsonparametersobject" )');
1293
    }
1294

1295
    const valid = new Validator(args);
1✔
1296
    const data = valid.buf(0);
1✔
1297

1298
    const block = Block.decode(data);
1✔
1299

1300
    return this.addBlock(block);
1✔
1301
  }
1302

1303
  async getBlockTemplate(args, help) {
1304
    if (help || args.length > 1) {
4!
1305
      throw new RPCError(errs.MISC_ERROR,
×
1306
        'getblocktemplate ( "jsonrequestobject" )');
1307
    }
1308

1309
    const validator = new Validator(args);
4✔
1310
    const options = validator.obj(0, {});
4✔
1311
    const valid = new Validator(options);
4✔
1312
    const mode = valid.str('mode', 'template');
4✔
1313

1314
    if (mode !== 'template' && mode !== 'proposal')
4!
1315
      throw new RPCError(errs.INVALID_PARAMETER, 'Invalid mode.');
×
1316

1317
    if (mode === 'proposal') {
4✔
1318
      const data = valid.buf('data');
1✔
1319

1320
      if (!data)
1!
1321
        throw new RPCError(errs.TYPE_ERROR, 'Missing data parameter.');
×
1322

1323
      const block = Block.decode(data);
1✔
1324

1325
      if (!block.prevBlock.equals(this.chain.tip.hash))
1!
1326
        return 'inconclusive-not-best-prevblk';
×
1327

1328
      try {
1✔
1329
        await this.chain.verifyBlock(block);
1✔
1330
      } catch (e) {
1331
        if (e.type === 'VerifyError')
×
1332
          return e.reason;
×
1333
        throw e;
×
1334
      }
1335

1336
      return null;
1✔
1337
    }
1338

1339
    let maxVersion = valid.u32('maxversion', -1);
3✔
1340
    let rules = valid.array('rules');
3✔
1341

1342
    if (rules)
3✔
1343
      maxVersion = -1;
2✔
1344

1345
    const capabilities = valid.array('capabilities');
3✔
1346
    let coinbase = false;
3✔
1347

1348
    if (capabilities) {
3!
1349
      let txnCap = false;
×
1350
      let valueCap = false;
×
1351

1352
      for (const capability of capabilities) {
×
1353
        if (typeof capability !== 'string')
×
1354
          throw new RPCError(errs.TYPE_ERROR, 'Invalid capability.');
×
1355

1356
        switch (capability) {
×
1357
          case 'coinbasetxn':
1358
            txnCap = true;
×
1359
            break;
×
1360
          case 'coinbasevalue':
1361
            // Prefer value if they support it.
1362
            valueCap = true;
×
1363
            break;
×
1364
        }
1365
      }
1366

1367
      if (txnCap && !valueCap) {
×
1368
        if (this.miner.addresses.length === 0) {
×
1369
          throw new RPCError(errs.MISC_ERROR,
×
1370
            'No addresses available for coinbase.');
1371
        }
1372
        coinbase = true;
×
1373
      }
1374
    }
1375

1376
    if (!this.network.selfConnect) {
3!
1377
      if (this.pool.peers.size() === 0) {
×
1378
        throw new RPCError(errs.CLIENT_NOT_CONNECTED,
×
1379
          'Node is not connected!');
1380
      }
1381

1382
      if (!this.chain.synced) {
×
1383
        throw new RPCError(errs.CLIENT_IN_INITIAL_DOWNLOAD,
×
1384
          'Node is downloading blocks...');
1385
      }
1386
    }
1387

1388
    const lpid = valid.str('longpollid');
3✔
1389

1390
    if (lpid)
3!
1391
      await this.handleLongpoll(lpid);
×
1392

1393
    if (!rules)
3✔
1394
      rules = [];
1✔
1395

1396
    return this.createTemplate(maxVersion, coinbase, rules);
3✔
1397
  }
1398

1399
  async createTemplate(maxVersion, coinbase, rules) {
1400
    const unlock = await this.locker.lock();
3✔
1401
    try {
3✔
1402
      return await this._createTemplate(maxVersion, coinbase, rules);
3✔
1403
    } finally {
1404
      unlock();
3✔
1405
    }
1406
  }
1407

1408
  async _createTemplate(maxVersion, coinbase, rules) {
1409
    const attempt = await this.getTemplate();
3✔
1410
    const scale = attempt.witness ? 1 : consensus.WITNESS_SCALE_FACTOR;
3!
1411

1412
    // Default mutable fields.
1413
    const mutable = ['time', 'transactions', 'prevblock'];
3✔
1414

1415
    // The miner doesn't support
1416
    // versionbits. Force them to
1417
    // encode our version.
1418
    if (maxVersion >= 2)
3!
1419
      mutable.push('version/force');
×
1420

1421
    // Allow the miner to change
1422
    // our provided coinbase.
1423
    // Note that these are implied
1424
    // without `coinbasetxn`.
1425
    if (coinbase) {
3!
1426
      mutable.push('coinbase');
×
1427
      mutable.push('coinbase/append');
×
1428
      mutable.push('generation');
×
1429
    }
1430

1431
    // Build an index of every transaction.
1432
    const index = new BufferMap();
3✔
1433
    for (let i = 0; i < attempt.items.length; i++) {
3✔
1434
      const entry = attempt.items[i];
4✔
1435
      index.set(entry.hash, i + 1);
4✔
1436
    }
1437

1438
    // Calculate dependencies for each transaction.
1439
    const txs = [];
3✔
1440
    for (let i = 0; i < attempt.items.length; i++) {
3✔
1441
      const entry = attempt.items[i];
4✔
1442
      const tx = entry.tx;
4✔
1443
      const deps = [];
4✔
1444

1445
      for (let j = 0; j < tx.inputs.length; j++) {
4✔
1446
        const input = tx.inputs[j];
4✔
1447
        const dep = index.get(input.prevout.hash);
4✔
1448

1449
        if (dep == null)
4!
1450
          continue;
4✔
1451

1452
        if (deps.indexOf(dep) === -1) {
×
1453
          assert(dep < i + 1);
×
1454
          deps.push(dep);
×
1455
        }
1456
      }
1457

1458
      txs.push({
4✔
1459
        data: tx.toHex(),
1460
        txid: tx.txid(),
1461
        hash: tx.wtxid(),
1462
        depends: deps,
1463
        fee: entry.fee,
1464
        sigops: entry.sigops / scale | 0,
1465
        weight: tx.getWeight()
1466
      });
1467
    }
1468

1469
    // Calculate version based on given rules.
1470
    let version = attempt.version;
3✔
1471

1472
    const vbavailable = {};
3✔
1473
    const vbrules = [];
3✔
1474

1475
    for (const deploy of this.network.deploys) {
3✔
1476
      const state = await this.chain.getState(this.chain.tip, deploy);
6✔
1477

1478
      let name = deploy.name;
6✔
1479

1480
      switch (state) {
6!
1481
        case common.thresholdStates.DEFINED:
1482
        case common.thresholdStates.FAILED:
1483
          break;
6✔
1484
        case common.thresholdStates.LOCKED_IN:
1485
          version |= 1 << deploy.bit;
×
1486
        case common.thresholdStates.STARTED:
1487
          if (!deploy.force) {
×
1488
            if (rules.indexOf(name) === -1)
×
1489
              version &= ~(1 << deploy.bit);
×
1490
            if (deploy.required)
×
1491
              name = '!' + name;
×
1492
          }
1493
          vbavailable[name] = deploy.bit;
×
1494
          break;
×
1495
        case common.thresholdStates.ACTIVE:
1496
          if (!deploy.force && deploy.required) {
×
1497
            if (rules.indexOf(name) === -1) {
×
1498
              throw new RPCError(errs.INVALID_PARAMETER,
×
1499
                `Client must support ${name}.`);
1500
            }
1501
            name = '!' + name;
×
1502
          }
1503
          vbrules.push(name);
×
1504
          break;
×
1505
        default:
1506
          assert(false, 'Bad state.');
×
1507
          break;
×
1508
      }
1509
    }
1510

1511
    version >>>= 0;
3✔
1512

1513
    const json = {
3✔
1514
      capabilities: ['proposal'],
1515
      mutable: mutable,
1516
      version: version,
1517
      rules: vbrules,
1518
      vbavailable: vbavailable,
1519
      vbrequired: 0,
1520
      height: attempt.height,
1521
      previousblockhash: attempt.prevBlock.toString('hex'),
1522
      merkleroot: undefined,
1523
      witnessroot: undefined,
1524
      treeroot: attempt.treeRoot.toString('hex'),
1525
      reservedroot: attempt.reservedRoot.toString('hex'),
1526
      mask: consensus.ZERO_HASH.toString('hex'), // Compat.
1527
      target: attempt.target.toString('hex'),
1528
      bits: util.hex32(attempt.bits),
1529
      noncerange:
1530
        Array(consensus.NONCE_SIZE + 1).join('00')
1531
        + Array(consensus.NONCE_SIZE + 1).join('ff'),
1532
      curtime: attempt.time,
1533
      mintime: attempt.mtp + 1,
1534
      maxtime: attempt.time + 7200,
1535
      expires: attempt.time + 7200,
1536
      sigoplimit: consensus.MAX_BLOCK_SIGOPS,
1537
      // sizelimit: consensus.MAX_RAW_BLOCK_SIZE,
1538
      sizelimit: consensus.MAX_BLOCK_SIZE,
1539
      weightlimit: consensus.MAX_BLOCK_WEIGHT,
1540
      longpollid: this.chain.tip.hash.toString('hex')
1541
                  + util.hex32(this.totalTX()),
1542
      submitold: false,
1543
      coinbaseaux: {
1544
        flags: attempt.coinbaseFlags.toString('hex')
1545
      },
1546
      coinbasevalue: attempt.getReward(),
1547
      coinbasetxn: undefined,
1548
      claims: attempt.claims.map((claim) => {
1549
        let value = claim.value;
×
1550
        let fee = claim.fee;
×
1551

1552
        // Account for mining software which creates its own
1553
        // coinbase with something other than `coinbasevalue`.
1554
        if (attempt.height >= this.network.deflationHeight) {
×
1555
          if (claim.commitHeight !== 1) {
×
1556
            value = claim.value - claim.fee;
×
1557
            fee = 0;
×
1558
          }
1559
        }
1560

1561
        return {
×
1562
          data: claim.blob.toString('hex'),
1563
          name: claim.name.toString('binary'),
1564
          namehash: claim.nameHash.toString('hex'),
1565
          version: claim.address.version,
1566
          hash: claim.address.hash.toString('hex'),
1567
          value: value,
1568
          fee: fee,
1569
          weak: claim.weak,
1570
          commitHash: claim.commitHash.toString('hex'),
1571
          commitHeight: claim.commitHeight,
1572
          weight: claim.getWeight()
1573
        };
1574
      }),
1575
      airdrops: attempt.airdrops.map((airdrop) => {
1576
        return {
×
1577
          data: airdrop.blob.toString('hex'),
1578
          position: airdrop.position,
1579
          version: airdrop.address.version,
1580
          address: airdrop.address.hash.toString('hex'),
1581
          value: airdrop.value,
1582
          fee: airdrop.fee,
1583
          rate: airdrop.rate,
1584
          weak: airdrop.weak
1585
        };
1586
      }),
1587
      transactions: txs
1588
    };
1589

1590
    // The client wants a coinbasetxn
1591
    // instead of a coinbasevalue.
1592
    if (coinbase) {
3!
1593
      const tx = attempt.coinbase;
×
1594

1595
      json.merkleroot = attempt.merkleRoot.toString('hex');
×
1596
      json.witnessroot = attempt.witnessRoot.toString('hex');
×
1597

1598
      json.coinbasetxn = {
×
1599
        data: tx.toHex(),
1600
        txid: tx.txid(),
1601
        hash: tx.wtxid(),
1602
        depends: [],
1603
        fee: 0,
1604
        sigops: tx.getSigops() / scale | 0,
1605
        weight: tx.getWeight()
1606
      };
1607
    }
1608

1609
    return json;
3✔
1610
  }
1611

1612
  async getMiningInfo(args, help) {
1613
    if (help || args.length !== 0)
×
1614
      throw new RPCError(errs.MISC_ERROR, 'getmininginfo');
×
1615

1616
    const attempt = this.attempt;
×
1617

1618
    let size = 0;
×
1619
    let weight = 0;
×
1620
    let txs = 0;
×
1621
    let diff = 0;
×
1622

1623
    if (attempt) {
×
1624
      weight = attempt.weight;
×
1625
      txs = attempt.items.length + 1;
×
1626
      diff = attempt.getDifficulty();
×
1627
      size = 1000;
×
1628
      for (const item of attempt.items)
×
1629
        size += item.tx.getBaseSize();
×
1630
    }
1631

1632
    return {
×
1633
      blocks: this.chain.height,
1634
      currentblocksize: size,
1635
      currentblockweight: weight,
1636
      currentblocktx: txs,
1637
      difficulty: diff,
1638
      errors: '',
1639
      genproclimit: this.procLimit,
1640
      networkhashps: await this.getHashRate(120),
1641
      pooledtx: this.totalTX(),
1642
      testnet: this.network !== Network.main,
1643
      chain: this.network.type !== 'testnet'
×
1644
        ? this.network.type
1645
        : 'test',
1646
      generate: this.mining
1647
    };
1648
  }
1649

1650
  async getNetworkHashPS(args, help) {
1651
    if (help || args.length > 2)
×
1652
      throw new RPCError(errs.MISC_ERROR, 'getnetworkhashps ( blocks height )');
×
1653

1654
    const valid = new Validator(args);
×
1655
    const lookup = valid.u32(0, 120);
×
1656
    const height = valid.u32(1);
×
1657

1658
    return this.getHashRate(lookup, height);
×
1659
  }
1660

1661
  async prioritiseTransaction(args, help) {
1662
    if (help || args.length !== 3) {
1!
1663
      throw new RPCError(errs.MISC_ERROR,
×
1664
        'prioritisetransaction <txid> <priority delta> <fee delta>');
1665
    }
1666

1667
    const valid = new Validator(args);
1✔
1668
    const hash = valid.bhash(0);
1✔
1669
    const pri = valid.i64(1);
1✔
1670
    const fee = valid.i64(2);
1✔
1671

1672
    if (!this.mempool)
1!
1673
      throw new RPCError(errs.MISC_ERROR, 'No mempool available.');
×
1674

1675
    if (!hash)
1!
1676
      throw new RPCError(errs.TYPE_ERROR, 'Invalid TXID');
×
1677

1678
    if (pri == null || fee == null)
1!
1679
      throw new RPCError(errs.TYPE_ERROR, 'Invalid fee or priority.');
×
1680

1681
    const entry = this.mempool.getEntry(hash);
1✔
1682

1683
    if (!entry)
1!
1684
      throw new RPCError(errs.MISC_ERROR, 'Transaction not in mempool.');
×
1685

1686
    this.mempool.prioritise(entry, pri, fee);
1✔
1687

1688
    return true;
1✔
1689
  }
1690

1691
  async verifyBlock(args, help) {
1692
    if (help || args.length !== 1)
×
1693
      throw new RPCError(errs.MISC_ERROR, 'verifyblock "block-hex"');
×
1694

1695
    const valid = new Validator(args);
×
1696
    const data = valid.buf(0);
×
1697

1698
    if (!data)
×
1699
      throw new RPCError(errs.TYPE_ERROR, 'Invalid block hex.');
×
1700

1701
    if (this.chain.options.spv)
×
1702
      throw new RPCError(errs.MISC_ERROR, 'Cannot verify block in SPV mode.');
×
1703

1704
    const block = Block.decode(data);
×
1705

1706
    try {
×
1707
      await this.chain.verifyBlock(block);
×
1708
    } catch (e) {
1709
      if (e.type === 'VerifyError')
×
1710
        return e.reason;
×
1711
      throw e;
×
1712
    }
1713

1714
    return null;
×
1715
  }
1716

1717
  /*
1718
   * Coin generation
1719
   */
1720

1721
  async getGenerate(args, help) {
1722
    if (help || args.length !== 0)
×
1723
      throw new RPCError(errs.MISC_ERROR, 'getgenerate');
×
1724
    return this.mining;
×
1725
  }
1726

1727
  async setGenerate(args, help) {
1728
    if (help || args.length < 1 || args.length > 2)
×
1729
      throw new RPCError(errs.MISC_ERROR, 'setgenerate mine ( proclimit )');
×
1730

1731
    const valid = new Validator(args);
×
1732
    const mine = valid.bool(0, false);
×
1733
    const limit = valid.u32(1, 0);
×
1734

1735
    if (mine && this.miner.addresses.length === 0) {
×
1736
      throw new RPCError(errs.MISC_ERROR,
×
1737
        'No addresses available for coinbase.');
1738
    }
1739

1740
    this.mining = mine;
×
1741
    this.procLimit = limit;
×
1742

1743
    if (mine) {
×
1744
      this.miner.cpu.start();
×
1745
      return true;
×
1746
    }
1747

1748
    await this.miner.cpu.stop();
×
1749

1750
    return false;
×
1751
  }
1752

1753
  async generate(args, help) {
1754
    if (help || args.length < 1 || args.length > 2)
20!
1755
      throw new RPCError(errs.MISC_ERROR, 'generate numblocks ( maxtries )');
×
1756

1757
    const valid = new Validator(args);
20✔
1758
    const blocks = valid.u32(0, 1);
20✔
1759
    const tries = valid.u32(1);
20✔
1760

1761
    if (this.miner.addresses.length === 0) {
20!
1762
      throw new RPCError(errs.MISC_ERROR,
×
1763
        'No addresses available for coinbase.');
1764
    }
1765

1766
    return this.mineBlocks(blocks, null, tries);
20✔
1767
  }
1768

1769
  async generateToAddress(args, help) {
1770
    if (help || args.length < 2 || args.length > 3) {
476!
1771
      throw new RPCError(errs.MISC_ERROR,
×
1772
        'generatetoaddress numblocks address ( maxtries )');
1773
    }
1774

1775
    const valid = new Validator(args);
476✔
1776
    const blocks = valid.u32(0, 1);
476✔
1777
    const str = valid.str(1, '');
476✔
1778
    const tries = valid.u32(2);
476✔
1779

1780
    const addr = parseAddress(str, this.network);
476✔
1781

1782
    return this.mineBlocks(blocks, addr, tries);
476✔
1783
  }
1784

1785
  /*
1786
   * Raw transactions
1787
   */
1788

1789
  async createRawTransaction(args, help) {
1790
    if (help || args.length < 2 || args.length > 3) {
×
1791
      throw new RPCError(errs.MISC_ERROR,
×
1792
        'createrawtransaction'
1793
        + ' [{"txid":"id","vout":n},...]'
1794
        + ' {"address":amount,"data":"hex",...}'
1795
        + ' ( locktime )');
1796
    }
1797

1798
    const valid = new Validator(args);
×
1799
    const inputs = valid.array(0);
×
1800
    const sendTo = valid.obj(1);
×
1801
    const locktime = valid.u32(2);
×
1802

1803
    if (!inputs || !sendTo) {
×
1804
      throw new RPCError(errs.TYPE_ERROR,
×
1805
        'Invalid parameters (inputs and sendTo).');
1806
    }
1807

1808
    const tx = new MTX();
×
1809

1810
    if (locktime != null)
×
1811
      tx.locktime = locktime;
×
1812

1813
    for (const obj of inputs) {
×
1814
      const valid = new Validator(obj);
×
1815
      const hash = valid.bhash('txid');
×
1816
      const index = valid.u32('vout');
×
1817

1818
      let sequence = valid.u32('sequence', 0xffffffff);
×
1819

1820
      if (tx.locktime)
×
1821
        sequence -= 1;
×
1822

1823
      if (!hash || index == null)
×
1824
        throw new RPCError(errs.TYPE_ERROR, 'Invalid outpoint.');
×
1825

1826
      const input = new Input();
×
1827
      input.prevout.hash = hash;
×
1828
      input.prevout.index = index;
×
1829
      input.sequence = sequence;
×
1830

1831
      tx.inputs.push(input);
×
1832
    }
1833

1834
    const sends = new Validator(sendTo);
×
1835
    const uniq = new Set();
×
1836

1837
    for (const key of Object.keys(sendTo)) {
×
1838
      if (key === 'data') {
×
1839
        const value = sends.buf(key);
×
1840

1841
        if (!value)
×
1842
          throw new RPCError(errs.TYPE_ERROR, 'Invalid nulldata..');
×
1843

1844
        const output = new Output();
×
1845
        output.value = 0;
×
1846
        output.address.fromNulldata(value);
×
1847
        tx.outputs.push(output);
×
1848

1849
        continue;
×
1850
      }
1851

1852
      const addr = parseAddress(key, this.network);
×
1853
      const b58 = addr.toString(this.network);
×
1854

1855
      if (uniq.has(b58))
×
1856
        throw new RPCError(errs.INVALID_PARAMETER, 'Duplicate address');
×
1857

1858
      uniq.add(b58);
×
1859

1860
      const value = sends.ufixed(key, EXP);
×
1861

1862
      if (value == null)
×
1863
        throw new RPCError(errs.TYPE_ERROR, 'Invalid output value.');
×
1864

1865
      const output = new Output();
×
1866
      output.value = value;
×
1867
      output.address = addr;
×
1868

1869
      tx.outputs.push(output);
×
1870
    }
1871

1872
    return tx.toHex();
×
1873
  }
1874

1875
  async decodeRawTransaction(args, help) {
1876
    if (help || args.length !== 1)
×
1877
      throw new RPCError(errs.MISC_ERROR, 'decoderawtransaction "hexstring"');
×
1878

1879
    const valid = new Validator(args);
×
1880
    const data = valid.buf(0);
×
1881

1882
    if (!data)
×
1883
      throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.');
×
1884

1885
    const tx = TX.decode(data);
×
1886

1887
    return this.txToJSON(tx);
×
1888
  }
1889

1890
  async decodeScript(args, help) {
1891
    if (help || args.length !== 1)
×
1892
      throw new RPCError(errs.MISC_ERROR, 'decodescript "hex"');
×
1893

1894
    const valid = new Validator(args);
×
1895
    const data = valid.buf(0);
×
1896
    const script = new Script();
×
1897

1898
    if (data)
×
1899
      script.decode(data);
×
1900

1901
    const addr = Address.fromScripthash(script.sha3());
×
1902

1903
    const json = this.scriptToJSON(script);
×
1904
    json.p2sh = addr.toString(this.network);
×
1905

1906
    return json;
×
1907
  }
1908

1909
  async decodeResource(args, help) {
1910
    if (help || args.length !== 1)
1!
1911
      throw new RPCError(errs.MISC_ERROR, 'decoderesource "hex"');
×
1912

1913
    const valid = new Validator(args);
1✔
1914
    const data = valid.buf(0);
1✔
1915

1916
    const res = Resource.decode(data);
1✔
1917
    return res.toJSON();
1✔
1918
  }
1919

1920
  async getRawTransaction(args, help) {
1921
    if (help || args.length < 1 || args.length > 2) {
2!
1922
      throw new RPCError(errs.MISC_ERROR,
×
1923
        'getrawtransaction "txid" ( verbose )');
1924
    }
1925

1926
    const valid = new Validator(args);
2✔
1927
    const hash = valid.bhash(0);
2✔
1928
    const verbose = valid.bool(1, false);
2✔
1929

1930
    if (!hash)
2!
1931
      throw new RPCError(errs.TYPE_ERROR, 'Invalid TXID.');
×
1932

1933
    const meta = await this.node.getMeta(hash);
2✔
1934

1935
    if (!meta)
2!
1936
      throw new RPCError(errs.MISC_ERROR, 'Transaction not found.');
×
1937

1938
    const tx = meta.tx;
2✔
1939

1940
    if (!verbose)
2✔
1941
      return tx.toHex();
1✔
1942

1943
    let entry;
1944
    if (meta.block)
1!
1945
      entry = await this.chain.getEntry(meta.block);
1✔
1946

1947
    const json = this.txToJSON(tx, entry);
1✔
1948
    json.time = meta.mtime;
1✔
1949
    json.hex = tx.toHex();
1✔
1950

1951
    return json;
1✔
1952
  }
1953

1954
  async sendRawTransaction(args, help) {
1955
    if (help || args.length < 1 || args.length > 2) {
14!
1956
      throw new RPCError(errs.MISC_ERROR,
×
1957
        'sendrawtransaction "hexstring" ( allowhighfees )');
1958
    }
1959

1960
    const valid = new Validator(args);
14✔
1961
    const data = valid.buf(0);
14✔
1962

1963
    if (!data)
14!
1964
      throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.');
×
1965

1966
    const tx = TX.decode(data);
14✔
1967

1968
    this.node.relay(tx);
14✔
1969

1970
    return tx.txid();
14✔
1971
  }
1972

1973
  async signRawTransaction(args, help) {
1974
    if (help || args.length < 1 || args.length > 4) {
×
1975
      throw new RPCError(errs.MISC_ERROR,
×
1976
        'signrawtransaction'
1977
        + ' "hexstring" ('
1978
        + ' [{"txid":"id","vout":n,"address":"bech32",'
1979
        + 'redeemScript":"hex"},...] ["privatekey1",...]'
1980
        + ' sighashtype )');
1981
    }
1982

1983
    const valid = new Validator(args);
×
1984
    const data = valid.buf(0);
×
1985
    const prevout = valid.array(1);
×
1986
    const secrets = valid.array(2);
×
1987
    const sighash = valid.str(3);
×
1988

1989
    if (!data)
×
1990
      throw new RPCError(errs.TYPE_ERROR, 'Invalid hex string.');
×
1991

1992
    if (!this.mempool)
×
1993
      throw new RPCError(errs.MISC_ERROR, 'No mempool available.');
×
1994

1995
    const tx = MTX.decode(data);
×
1996
    tx.view = await this.mempool.getSpentView(tx);
×
1997

1998
    const map = new BufferMap();
×
1999
    const keys = [];
×
2000

2001
    if (secrets) {
×
2002
      const valid = new Validator(secrets);
×
2003
      for (let i = 0; i < secrets.length; i++) {
×
2004
        const secret = valid.str(i, '');
×
2005
        const key = parseSecret(secret, this.network);
×
2006
        map.set(key.getPublicKey(), key);
×
2007
        keys.push(key);
×
2008
      }
2009
    }
2010

2011
    if (prevout) {
×
2012
      for (const prev of prevout) {
×
2013
        const valid = new Validator(prev);
×
2014
        const hash = valid.bhash('txid');
×
2015
        const index = valid.u32('vout');
×
2016
        const addrRaw = valid.str('address');
×
2017
        const value = valid.ufixed('amount', EXP);
×
2018
        const redeemRaw = valid.buf('redeemScript');
×
2019

2020
        if (!hash || index == null || !addrRaw || value == null)
×
2021
          throw new RPCError(errs.INVALID_PARAMETER, 'Invalid UTXO.');
×
2022

2023
        const outpoint = new Outpoint(hash, index);
×
2024

2025
        const addr = parseAddress(addrRaw, this.network);
×
2026
        const coin = Output.fromScript(addr, value);
×
2027

2028
        tx.view.addOutput(outpoint, coin);
×
2029

2030
        if (keys.length === 0 || !redeemRaw)
×
2031
          continue;
×
2032

2033
        if (!addr.isScripthash())
×
2034
          continue;
×
2035

2036
        if (!redeemRaw) {
×
2037
          throw new RPCError(errs.INVALID_PARAMETER,
×
2038
            'P2SH requires redeem script.');
2039
        }
2040

2041
        const redeem = Script.decode(redeemRaw);
×
2042

2043
        for (const op of redeem.code) {
×
2044
          if (!op.data)
×
2045
            continue;
×
2046

2047
          const key = map.get(op.data);
×
2048

2049
          if (key) {
×
2050
            key.script = redeem;
×
2051
            key.refresh();
×
2052
            break;
×
2053
          }
2054
        }
2055
      }
2056
    }
2057

2058
    let type = Script.hashType.ALL;
×
2059
    if (sighash) {
×
2060
      const parts = sighash.split('|');
×
2061

2062
      if (parts.length < 1 || parts.length > 2)
×
2063
        throw new RPCError(errs.INVALID_PARAMETER, 'Invalid sighash type.');
×
2064

2065
      type = Script.hashType[parts[0]];
×
2066

2067
      if (type == null)
×
2068
        throw new RPCError(errs.INVALID_PARAMETER, 'Invalid sighash type.');
×
2069

2070
      if (parts.length === 2) {
×
2071
        if (parts[1] !== 'NOINPUT' && parts[1] !== 'ANYONECANPAY')
×
2072
          throw new RPCError(errs.INVALID_PARAMETER, 'Invalid sighash type.');
×
2073

2074
        if (parts[1] === 'NOINPUT')
×
2075
          type |= Script.hashType.NOINPUT;
×
2076

2077
        if (parts[1] === 'ANYONECANPAY')
×
2078
          type |= Script.hashType.ANYONECANPAY;
×
2079
      }
2080
    }
2081

2082
    await tx.signAsync(keys, type, this.workers);
×
2083

2084
    return {
×
2085
      hex: tx.toHex(),
2086
      complete: tx.isSigned()
2087
    };
2088
  }
2089

2090
  /*
2091
   * Utility Functions
2092
   */
2093

2094
  async createMultisig(args, help) {
2095
    if (help || args.length < 2 || args.length > 2) {
×
2096
      throw new RPCError(errs.MISC_ERROR,
×
2097
        'createmultisig nrequired ["key",...]');
2098
    }
2099

2100
    const valid = new Validator(args);
×
2101
    const keys = valid.array(1, []);
×
2102
    const m = valid.u32(0, 0);
×
2103
    const n = keys.length;
×
2104

2105
    if (m < 1 || n < m || n > 16)
×
2106
      throw new RPCError(errs.INVALID_PARAMETER, 'Invalid m and n values.');
×
2107

2108
    const items = new Validator(keys);
×
2109

2110
    for (let i = 0; i < keys.length; i++) {
×
2111
      const key = items.buf(i);
×
2112

2113
      if (!key)
×
2114
        throw new RPCError(errs.TYPE_ERROR, 'Invalid key.');
×
2115

2116
      if (!secp256k1.publicKeyVerify(key) || key.length !== 33)
×
2117
        throw new RPCError(errs.INVALID_ADDRESS_OR_KEY, 'Invalid key.');
×
2118

2119
      keys[i] = key;
×
2120
    }
2121

2122
    const script = Script.fromMultisig(m, n, keys);
×
2123

2124
    if (script.getSize() > consensus.MAX_SCRIPT_PUSH) {
×
2125
      throw new RPCError(errs.VERIFY_ERROR,
×
2126
        'Redeem script exceeds size limit.');
2127
    }
2128

2129
    const addr = Address.fromScripthash(script.sha3());
×
2130

2131
    return {
×
2132
      address: addr.toString(this.network),
2133
      redeemScript: script.toJSON()
2134
    };
2135
  }
2136

2137
  async validateAddress(args, help) {
2138
    if (help || args.length !== 1)
4!
2139
      throw new RPCError(errs.MISC_ERROR, 'validateaddress "address"');
×
2140

2141
    const valid = new Validator(args);
4✔
2142
    const str = valid.str(0, '');
4✔
2143

2144
    let addr;
2145
    try {
4✔
2146
      addr = Address.fromString(str, this.network);
4✔
2147
    } catch (e) {
2148
      return {
1✔
2149
        isvalid: false
2150
      };
2151
    }
2152

2153
    return {
3✔
2154
      isvalid: true,
2155
      address: addr.toString(this.network),
2156
      isscript: addr.isScripthash(),
2157
      isspendable: !addr.isUnspendable(),
2158
      witness_version: addr.version,
2159
      witness_program: addr.hash.toString('hex')
2160
    };
2161
  }
2162

2163
  async verifyMessage(args, help) {
2164
    if (help || args.length !== 3) {
6!
2165
      throw new RPCError(errs.MISC_ERROR,
×
2166
        'verifymessage "address" "signature" "message"');
2167
    }
2168

2169
    const valid = new Validator(args);
6✔
2170
    const b58 = valid.str(0, '');
6✔
2171
    const sig = valid.buf(1, null, 'base64');
6✔
2172
    const str = valid.str(2);
6✔
2173

2174
    if (!sig || !str)
6!
2175
      throw new RPCError(errs.TYPE_ERROR, 'Invalid parameters.');
×
2176

2177
    const addr = parseAddress(b58, this.network);
6✔
2178

2179
    if (addr.version !== 0 || addr.hash.length !== 20)
6!
2180
      return false;
×
2181

2182
    const msg = Buffer.from(MAGIC_STRING + str, 'utf8');
6✔
2183
    const hash = blake2b.digest(msg);
6✔
2184

2185
    for (let i = 0; i < 4; i++) {
6✔
2186
      const key = secp256k1.recover(hash, sig, i, true);
7✔
2187

2188
      if (!key)
7!
2189
        continue;
×
2190

2191
      if (safeEqual(blake2b.digest(key, 20), addr.hash))
7✔
2192
        return true;
6✔
2193
    }
2194

2195
    return false;
×
2196
  }
2197

2198
  async verifyMessageWithName(args, help) {
2199
    if (help || args.length < 3 || args.length > 4 ) {
15!
2200
      throw new RPCError(errs.MISC_ERROR,
×
2201
        'verifymessagewithname "name" "signature" "message" (safe)');
2202
    }
2203

2204
    const valid = new Validator(args);
15✔
2205
    const name = valid.str(0, '');
15✔
2206
    const sig = valid.buf(1, null, 'base64');
15✔
2207
    const str = valid.str(2);
15✔
2208
    const safe = valid.bool(3, false);
15✔
2209
    const network = this.network;
15✔
2210
    const height = this.chain.height;
15✔
2211

2212
    if (!name || !rules.verifyName(name))
15✔
2213
      throw new RPCError(errs.TYPE_ERROR, 'Invalid name.');
4✔
2214

2215
    const nameHash = rules.hashName(name);
11✔
2216

2217
    const ns = await this.getNameState(nameHash, safe);
11✔
2218

2219
    if (!ns || !ns.owner)
11✔
2220
      throw new RPCError(errs.MISC_ERROR, 'Cannot find the name owner.');
1✔
2221

2222
    if (!ns.isClosed(height, network))
10✔
2223
      throw new Error('Invalid name state.');
3✔
2224

2225
    const coin = await this.chain.getCoin(ns.owner.hash, ns.owner.index);
7✔
2226

2227
    if (!coin) {
7✔
2228
      throw new RPCError(
3✔
2229
        errs.DATABASE_ERROR,
2230
        'Cannot find the owner\'s address.'
2231
      );
2232
    }
2233

2234
    const address = coin.address.toString(this.network);
4✔
2235
    return this.verifyMessage([address, sig, str]);
4✔
2236
  }
2237

2238
  async signMessageWithPrivkey(args, help) {
2239
    if (help || args.length !== 2) {
×
2240
      throw new RPCError(errs.MISC_ERROR,
×
2241
        'signmessagewithprivkey "privkey" "message"');
2242
    }
2243

2244
    const valid = new Validator(args);
×
2245
    const wif = valid.str(0, '');
×
2246
    const str = valid.str(1, '');
×
2247

2248
    const key = parseSecret(wif, this.network);
×
2249
    const msg = Buffer.from(MAGIC_STRING + str, 'utf8');
×
2250
    const hash = blake2b.digest(msg);
×
2251
    const sig = key.sign(hash);
×
2252

2253
    return sig.toString('base64');
×
2254
  }
2255

2256
  async estimateFee(args, help) {
2257
    if (help || args.length !== 1)
×
2258
      throw new RPCError(errs.MISC_ERROR, 'estimatefee nblocks');
×
2259

2260
    const valid = new Validator(args);
×
2261
    const blocks = valid.u32(0, 1);
×
2262

2263
    if (!this.fees)
×
2264
      throw new RPCError(errs.MISC_ERROR, 'Fee estimation not available.');
×
2265

2266
    const fee = this.fees.estimateFee(blocks, false);
×
2267

2268
    if (fee === 0)
×
2269
      return -1;
×
2270

2271
    return Amount.coin(fee, true);
×
2272
  }
2273

2274
  async estimatePriority(args, help) {
2275
    if (help || args.length !== 1)
×
2276
      throw new RPCError(errs.MISC_ERROR, 'estimatepriority nblocks');
×
2277

2278
    const valid = new Validator(args);
×
2279
    const blocks = valid.u32(0, 1);
×
2280

2281
    if (!this.fees)
×
2282
      throw new RPCError(errs.MISC_ERROR, 'Priority estimation not available.');
×
2283

2284
    return this.fees.estimatePriority(blocks, false);
×
2285
  }
2286

2287
  async estimateSmartFee(args, help) {
2288
    if (help || args.length !== 1)
×
2289
      throw new RPCError(errs.MISC_ERROR, 'estimatesmartfee nblocks');
×
2290

2291
    const valid = new Validator(args);
×
2292
    const blocks = valid.u32(0, 1);
×
2293

2294
    if (!this.fees)
×
2295
      throw new RPCError(errs.MISC_ERROR, 'Fee estimation not available.');
×
2296

2297
    let fee = this.fees.estimateFee(blocks, true);
×
2298

2299
    if (fee === 0)
×
2300
      fee = -1;
×
2301
    else
2302
      fee = Amount.coin(fee, true);
×
2303

2304
    return {
×
2305
      fee: fee,
2306
      blocks: blocks
2307
    };
2308
  }
2309

2310
  async estimateSmartPriority(args, help) {
2311
    if (help || args.length !== 1)
×
2312
      throw new RPCError(errs.MISC_ERROR, 'estimatesmartpriority nblocks');
×
2313

2314
    const valid = new Validator(args);
×
2315
    const blocks = valid.u32(0, 1);
×
2316

2317
    if (!this.fees)
×
2318
      throw new RPCError(errs.MISC_ERROR, 'Priority estimation not available.');
×
2319

2320
    const pri = this.fees.estimatePriority(blocks, true);
×
2321

2322
    return {
×
2323
      priority: pri,
2324
      blocks: blocks
2325
    };
2326
  }
2327

2328
  async invalidateBlock(args, help) {
2329
    if (help || args.length !== 1)
×
2330
      throw new RPCError(errs.MISC_ERROR, 'invalidateblock "hash"');
×
2331

2332
    const valid = new Validator(args);
×
2333
    const hash = valid.bhash(0);
×
2334

2335
    if (!hash)
×
2336
      throw new RPCError(errs.TYPE_ERROR, 'Invalid block hash.');
×
2337

2338
    await this.chain.invalidate(hash);
×
2339

2340
    return null;
×
2341
  }
2342

2343
  async reconsiderBlock(args, help) {
2344
    if (help || args.length !== 1)
×
2345
      throw new RPCError(errs.MISC_ERROR, 'reconsiderblock "hash"');
×
2346

2347
    const valid = new Validator(args);
×
2348
    const hash = valid.bhash(0);
×
2349

2350
    if (!hash)
×
2351
      throw new RPCError(errs.TYPE_ERROR, 'Invalid block hash.');
×
2352

2353
    this.chain.removeInvalid(hash);
×
2354

2355
    return null;
×
2356
  }
2357

2358
  async setMockTime(args, help) {
2359
    if (help || args.length !== 1)
×
2360
      throw new RPCError(errs.MISC_ERROR, 'setmocktime timestamp');
×
2361

2362
    const valid = new Validator(args);
×
2363
    const time = valid.u32(0);
×
2364

2365
    if (time == null)
×
2366
      throw new RPCError(errs.TYPE_ERROR, 'Invalid timestamp.');
×
2367

2368
    this.network.time.offset = 0;
×
2369

2370
    const delta = this.network.now() - time;
×
2371

2372
    this.network.time.offset = -delta;
×
2373

2374
    return null;
×
2375
  }
2376

2377
  async getMemoryInfo(args, help) {
2378
    if (help || args.length !== 0)
×
2379
      throw new RPCError(errs.MISC_ERROR, 'getmemoryinfo');
×
2380

2381
    return this.logger.memoryUsage();
×
2382
  }
2383

2384
  async setLogLevel(args, help) {
2385
    if (help || args.length !== 1)
×
2386
      throw new RPCError(errs.MISC_ERROR, 'setloglevel "level"');
×
2387

2388
    const valid = new Validator(args);
×
2389
    const level = valid.str(0, '');
×
2390

2391
    this.logger.setLevel(level);
×
2392

2393
    return null;
×
2394
  }
2395

2396
  async getNames(args, help) {
2397
    if (help || args.length !== 0)
×
2398
      throw new RPCError(errs.MISC_ERROR, 'getnames');
×
2399

2400
    const network = this.network;
×
2401
    const height = this.chain.height;
×
2402
    const txn = this.chain.db.txn;
×
2403
    const items = [];
×
2404

2405
    const iter = txn.iterator();
×
2406

2407
    while (await iter.next()) {
×
2408
      const {key, value} = iter;
×
2409
      const ns = NameState.decode(value);
×
2410
      ns.nameHash = key;
×
2411

2412
      const info = ns.getJSON(height, network);
×
2413
      items.push(info);
×
2414
    }
2415

2416
    return items;
×
2417
  }
2418

2419
  async getNameInfo(args, help) {
2420
    if (help || args.length < 1 || args.length > 2)
10!
2421
      throw new RPCError(errs.MISC_ERROR, 'getnameinfo "name" (safe)');
×
2422

2423
    const valid = new Validator(args);
10✔
2424
    const name = valid.str(0);
10✔
2425
    const safe = valid.bool(1, false);
10✔
2426

2427
    if (!name || !rules.verifyName(name))
10!
2428
      throw new RPCError(errs.TYPE_ERROR, 'Invalid name.');
×
2429

2430
    const network = this.network;
10✔
2431
    const height = this.chain.height;
10✔
2432
    const nameHash = rules.hashName(name);
10✔
2433
    const reserved = rules.isReserved(nameHash, height + 1, network);
10✔
2434
    const [start, week] = rules.getRollout(nameHash, network);
10✔
2435

2436
    const ns = await this.getNameState(nameHash, safe);
10✔
2437

2438
    let info = null;
10✔
2439

2440
    if (ns) {
10✔
2441
      if (!ns.isExpired(height, network))
9!
2442
        info = ns.getJSON(height, network);
9✔
2443
    }
2444

2445
    return {
10✔
2446
      start: {
2447
        reserved: reserved,
2448
        week: week,
2449
        start: start
2450
      },
2451
      info
2452
    };
2453
  }
2454

2455
  async getNameResource(args, help) {
2456
    if (help || args.length < 1 || args.length > 2)
12!
2457
      throw new RPCError(errs.MISC_ERROR, 'getnameresource "name" (safe)');
×
2458

2459
    const valid = new Validator(args);
12✔
2460
    const name = valid.str(0);
12✔
2461
    const safe = valid.bool(1, false);
12✔
2462

2463
    if (!name || !rules.verifyName(name))
12!
2464
      throw new RPCError(errs.TYPE_ERROR, 'Invalid name.');
×
2465

2466
    const nameHash = rules.hashName(name);
12✔
2467

2468
    const ns = await this.getNameState(nameHash, safe);
12✔
2469

2470
    if (!ns || ns.data.length === 0)
12✔
2471
      return null;
1✔
2472

2473
    try {
11✔
2474
      const res = Resource.decode(ns.data);
11✔
2475
      return res.getJSON(name);
11✔
2476
    } catch (e) {
2477
      return {};
×
2478
    }
2479
  }
2480

2481
  async getNameProof(args, help) {
2482
    if (help || args.length < 1 || args.length > 2)
×
2483
      throw new RPCError(errs.MISC_ERROR, 'getnameproof "name" ("root")');
×
2484

2485
    const valid = new Validator(args);
×
2486
    const name = valid.str(0);
×
2487
    const treeRoot = valid.bhash(1);
×
2488

2489
    if (!name || !rules.verifyName(name))
×
2490
      throw new RPCError(errs.TYPE_ERROR, 'Invalid name.');
×
2491

2492
    const hash = this.chain.tip.hash;
×
2493
    const height = this.chain.tip.height;
×
2494
    const root = treeRoot || this.chain.tip.treeRoot;
×
2495
    const key = rules.hashName(name);
×
2496
    const proof = await this.chain.db.prove(root, key);
×
2497

2498
    return {
×
2499
      hash: hash.toString('hex'),
2500
      height: height,
2501
      root: root.toString('hex'),
2502
      name: name,
2503
      key: key.toString('hex'),
2504
      proof: proof.toJSON()
2505
    };
2506
  }
2507

2508
  async getDNSSECProof(args, help) {
2509
    if (help || args.length < 1 || args.length > 3)
×
2510
      throw new RPCError(errs.MISC_ERROR,
×
2511
        'getdnssecproof "name" ( estimate ) ( verbose )');
2512

2513
    const valid = new Validator(args);
×
2514
    const name = valid.str(0);
×
2515
    const estimate = valid.bool(1, false);
×
2516
    const verbose = valid.bool(2, true);
×
2517

2518
    const proof = await ownership.prove(name, estimate);
×
2519

2520
    if (!verbose)
×
2521
      return proof.toHex();
×
2522

2523
    return proof.toJSON();
×
2524
  }
2525

2526
  async getNameByHash(args, help) {
2527
    if (help || args.length < 1 || args.length > 2)
×
2528
      throw new RPCError(errs.MISC_ERROR, 'getnamebyhash "hash" (safe)');
×
2529

2530
    const valid = new Validator(args);
×
2531
    const hash = valid.bhash(0);
×
2532
    const safe = valid.bool(1, false);
×
2533

2534
    if (!hash)
×
2535
      throw new RPCError(errs.TYPE_ERROR, 'Invalid name hash.');
×
2536

2537
    const ns = await this.getNameState(hash, safe);
×
2538

2539
    if (!ns)
×
2540
      return null;
×
2541

2542
    return ns.name.toString('binary');
×
2543
  }
2544

2545
  async grindName(args, help) {
2546
    if (help || args.length > 1)
55!
2547
      throw new RPCError(errs.MISC_ERROR, 'grindname size');
×
2548

2549
    const valid = new Validator(args);
55✔
2550
    const size = valid.u32(0, 10);
55✔
2551

2552
    if (size < 1 || size > 63)
55!
2553
      throw new RPCError(errs.TYPE_ERROR, 'Invalid length.');
×
2554

2555
    const network = this.network;
55✔
2556
    const height = this.chain.height;
55✔
2557

2558
    return rules.grindName(size, height + 1, network);
55✔
2559
  }
2560

2561
  async sendRawClaim(args, help) {
2562
    if (help || args.length < 1 || args.length > 2)
×
2563
      throw new RPCError(errs.MISC_ERROR, 'sendrawclaim "base64-string"');
×
2564

2565
    const valid = new Validator(args);
×
2566
    const data = valid.buf(0, null, 'base64');
×
2567

2568
    if (!data)
×
2569
      throw new RPCError(errs.TYPE_ERROR, 'Invalid base64 string.');
×
2570

2571
    const claim = Claim.fromBlob(data);
×
2572

2573
    this.node.relayClaim(claim);
×
2574

2575
    return claim.hash().toString('hex');
×
2576
  }
2577

2578
  async sendRawAirdrop(args, help) {
2579
    if (help || args.length < 1 || args.length > 2)
×
2580
      throw new RPCError(errs.MISC_ERROR, 'sendrawairdrop "base64-string"');
×
2581

2582
    if (this.network.type !== 'main')
×
2583
      throw new RPCError(errs.MISC_ERROR, 'Currently disabled.');
×
2584

2585
    const valid = new Validator(args);
×
2586
    const data = valid.buf(0, null, 'base64');
×
2587

2588
    if (!data)
×
2589
      throw new RPCError(errs.TYPE_ERROR, 'Invalid base64 string.');
×
2590

2591
    const proof = AirdropProof.decode(data);
×
2592

2593
    this.node.relayAirdrop(proof);
×
2594

2595
    return proof.hash().toString('hex');
×
2596
  }
2597

2598
  async validateResource(args, help) {
2599
    if (help || args.length < 1 || args.length > 2)
22!
2600
      throw new RPCError(errs.MISC_ERROR, 'validateresource'
×
2601
        + ' \'{"records": [{...}]}\'');
2602

2603
    const valid = new Validator(args);
22✔
2604
    const data = valid.obj(0);
22✔
2605

2606
    if (!data)
22!
2607
      throw new RPCError(errs.TYPE_ERROR, 'Invalid resource object.');
×
2608

2609
    let resource;
2610
    try {
22✔
2611
      resource = Resource.fromJSON(data);
22✔
2612
    } catch (e) {
2613
      throw new RPCError(errs.PARSE_ERROR, e.message);
15✔
2614
    }
2615

2616
    return resource.toJSON();
7✔
2617
  }
2618

2619
  async resetRootCache(args, help) {
2620
    if (help || args.length !== 0)
×
2621
      throw new RPCError(errs.MISC_ERROR, 'resetrootcache');
×
2622

2623
    if (!this.node.ns)
×
2624
      return null;
×
2625

2626
    this.node.ns.resetCache();
×
2627

2628
    return null;
×
2629
  }
2630

2631
  /*
2632
   * Helpers
2633
   */
2634

2635
  async handleLongpoll(lpid) {
2636
    if (lpid.length !== 72)
×
2637
      throw new RPCError(errs.INVALID_PARAMETER, 'Invalid longpoll ID.');
×
2638

2639
    const watched = lpid.slice(0, 64);
×
2640
    const lastTX = parseInt(lpid.slice(64, 72), 16);
×
2641

2642
    if ((lastTX >>> 0) !== lastTX)
×
2643
      throw new RPCError(errs.INVALID_PARAMETER, 'Invalid longpoll ID.');
×
2644

2645
    const hash = util.parseHex(watched, 32);
×
2646

2647
    if (!this.chain.tip.hash.equals(hash))
×
2648
      return;
×
2649

2650
    await this.longpoll();
×
2651
  }
2652

2653
  longpoll() {
2654
    return new Promise((resolve, reject) => {
×
2655
      this.pollers.push({ resolve, reject });
×
2656
    });
2657
  }
2658

2659
  refreshBlock() {
2660
    const pollers = this.pollers;
12✔
2661

2662
    this.attempt = null;
12✔
2663
    this.lastActivity = 0;
12✔
2664
    this.pollers = [];
12✔
2665

2666
    for (const job of pollers)
12✔
2667
      job.resolve();
×
2668
  }
2669

2670
  bindChain() {
2671
    if (this.boundChain)
13✔
2672
      return;
10✔
2673

2674
    this.boundChain = true;
3✔
2675

2676
    const refresh = () => {
3✔
2677
      if (!this.attempt)
15✔
2678
        return;
9✔
2679

2680
      this.refreshBlock();
6✔
2681
      this.merkleMap.clear();
6✔
2682
      this.merkleList.length = 0;
6✔
2683
    };
2684

2685
    this.node.on('connect', refresh);
3✔
2686
    this.node.on('reset', refresh);
3✔
2687

2688
    if (!this.mempool)
3!
2689
      return;
×
2690

2691
    const tryRefresh = () => {
3✔
2692
      if (!this.attempt)
4!
2693
        return;
×
2694

2695
      if (util.now() - this.lastActivity > 10)
4!
2696
        this.refreshBlock();
4✔
2697
    };
2698

2699
    this.node.on('tx', tryRefresh);
3✔
2700
    this.node.on('claim', tryRefresh);
3✔
2701
    this.node.on('airdrop', tryRefresh);
3✔
2702
  }
2703

2704
  async getTemplate() {
2705
    this.bindChain();
3✔
2706

2707
    let attempt = this.attempt;
3✔
2708

2709
    if (attempt) {
3!
2710
      this.miner.updateTime(attempt);
×
2711
    } else {
2712
      attempt = await this.miner.createBlock();
3✔
2713
      this.attempt = attempt;
3✔
2714
      this.lastActivity = util.now();
3✔
2715
    }
2716

2717
    return attempt;
3✔
2718
  }
2719

2720
  async updateWork() {
2721
    this.bindChain();
10✔
2722

2723
    let attempt = this.attempt;
10✔
2724

2725
    if (attempt) {
10✔
2726
      if (attempt.address.isNull()) {
1!
2727
        throw new RPCError(errs.MISC_ERROR,
×
2728
          'No addresses available for coinbase.');
2729
      }
2730

2731
      this.miner.updateTime(attempt);
1✔
2732

2733
      return attempt;
1✔
2734
    }
2735

2736
    if (this.miner.addresses.length === 0) {
9!
2737
      throw new RPCError(errs.MISC_ERROR,
×
2738
        'No addresses available for coinbase.');
2739
    }
2740

2741
    attempt = await this.miner.createBlock();
9✔
2742

2743
    if (this.merkleMap.size >= 10)
9!
2744
      this.merkleMap.delete(this.merkleList.shift());
×
2745

2746
    this.attempt = attempt;
9✔
2747
    this.lastActivity = util.now();
9✔
2748
    this.merkleMap.set(attempt.witnessRoot, attempt);
9✔
2749
    this.merkleList.push(attempt.witnessRoot);
9✔
2750

2751
    return attempt;
9✔
2752
  }
2753

2754
  async addBlock(block) {
2755
    const unlock1 = await this.locker.lock();
1✔
2756
    const unlock2 = await this.chain.locker.lock();
1✔
2757
    try {
1✔
2758
      return await this._addBlock(block);
1✔
2759
    } finally {
2760
      unlock2();
1✔
2761
      unlock1();
1✔
2762
    }
2763
  }
2764

2765
  async _addBlock(block) {
2766
    this.logger.info('Handling submitted block: %x.', block.hash());
1✔
2767

2768
    let entry;
2769
    try {
1✔
2770
      entry = await this.chain._add(block);
1✔
2771
    } catch (err) {
2772
      if (err.type === 'VerifyError') {
×
2773
        this.logger.warning('RPC block rejected: %x (%s).',
×
2774
          block.hash(), err.reason);
2775
        return `rejected: ${err.reason}`;
×
2776
      }
2777
      throw err;
×
2778
    }
2779

2780
    if (!entry) {
1!
2781
      this.logger.warning('RPC block rejected: %x (bad-prevblk).',
×
2782
        block.hash());
2783
      return 'rejected: bad-prevblk';
×
2784
    }
2785

2786
    return null;
1✔
2787
  }
2788

2789
  totalTX() {
2790
    return this.mempool ? this.mempool.map.size : 0;
3!
2791
  }
2792

2793
  async getSoftforks() {
2794
    const tip = this.chain.tip;
1✔
2795
    const forks = {};
1✔
2796

2797
    for (const deployment of this.network.deploys) {
1✔
2798
      const state = await this.chain.getState(tip, deployment);
2✔
2799
      let status;
2800

2801
      switch (state) {
2!
2802
        case common.thresholdStates.DEFINED:
2803
          status = 'defined';
2✔
2804
          break;
2✔
2805
        case common.thresholdStates.STARTED:
2806
          status = 'started';
×
2807
          break;
×
2808
        case common.thresholdStates.LOCKED_IN:
2809
          status = 'locked_in';
×
2810
          break;
×
2811
        case common.thresholdStates.ACTIVE:
2812
          status = 'active';
×
2813
          break;
×
2814
        case common.thresholdStates.FAILED:
2815
          status = 'failed';
×
2816
          break;
×
2817
        default:
2818
          assert(false, 'Bad state.');
×
2819
          break;
×
2820
      }
2821

2822
      forks[deployment.name] = {
2✔
2823
        status: status,
2824
        bit: deployment.bit,
2825
        startTime: deployment.startTime,
2826
        timeout: deployment.timeout
2827
      };
2828

2829
      if (status === 'started') {
2!
2830
        forks[deployment.name].statistics =
×
2831
          await this.chain.getBIP9Stats(tip, deployment);
2832
      }
2833
    }
2834

2835
    return forks;
1✔
2836
  }
2837

2838
  async getHashRate(lookup, height) {
2839
    let tip = this.chain.tip;
×
2840

2841
    if (height != null)
×
2842
      tip = await this.chain.getEntry(height);
×
2843

2844
    if (!tip)
×
2845
      return 0;
×
2846

2847
    assert(typeof lookup === 'number');
×
2848
    assert(lookup >= 0);
×
2849

2850
    if (lookup === 0)
×
2851
      lookup = tip.height % this.network.pow.targetWindow + 1;
×
2852

2853
    if (lookup > tip.height)
×
2854
      lookup = tip.height;
×
2855

2856
    let min = tip.time;
×
2857
    let max = min;
×
2858
    let entry = tip;
×
2859

2860
    for (let i = 0; i < lookup; i++) {
×
2861
      entry = await this.chain.getPrevious(entry);
×
2862

2863
      if (!entry)
×
2864
        throw new RPCError(errs.DATABASE_ERROR, 'Not found.');
×
2865

2866
      min = Math.min(entry.time, min);
×
2867
      max = Math.max(entry.time, max);
×
2868
    }
2869

2870
    const diff = max - min;
×
2871

2872
    if (diff === 0)
×
2873
      return 0;
×
2874

2875
    const work = tip.chainwork.sub(entry.chainwork);
×
2876

2877
    return Number(work.toString()) / diff;
×
2878
  }
2879

2880
  async mineBlocks(blocks, addr, tries) {
2881
    const unlock = await this.locker.lock();
496✔
2882
    try {
496✔
2883
      return await this._mineBlocks(blocks, addr, tries);
496✔
2884
    } finally {
2885
      unlock();
496✔
2886
    }
2887
  }
2888

2889
  async _mineBlocks(blocks, addr, tries) {
2890
    const hashes = [];
496✔
2891

2892
    for (let i = 0; i < blocks; i++) {
496✔
2893
      const block = await this.miner.mineBlock(null, addr);
1,696✔
2894
      const entry = await this.chain.add(block);
1,696✔
2895
      assert(entry);
1,696✔
2896
      hashes.push(entry.hash.toString('hex'));
1,696✔
2897
    }
2898

2899
    return hashes;
496✔
2900
  }
2901

2902
  async findFork(entry) {
2903
    while (entry) {
×
2904
      if (await this.chain.isMainChain(entry))
×
2905
        return entry;
×
2906
      entry = await this.chain.getPrevious(entry);
×
2907
    }
2908
    throw new Error('Fork not found.');
×
2909
  }
2910

2911
  txToJSON(tx, entry) {
2912
    let height = -1;
1✔
2913
    let time = 0;
1✔
2914
    let hash = null;
1✔
2915
    let conf = 0;
1✔
2916

2917
    if (entry) {
1!
2918
      height = entry.height;
1✔
2919
      time = entry.time;
1✔
2920
      hash = entry.hash;
1✔
2921
      conf = this.chain.height - height + 1;
1✔
2922
    }
2923

2924
    const vin = [];
1✔
2925

2926
    for (const input of tx.inputs) {
1✔
2927
      const json = {
1✔
2928
        coinbase: undefined,
2929
        txid: undefined,
2930
        vout: undefined,
2931
        txinwitness: undefined,
2932
        sequence: input.sequence,
2933
        link: input.link
2934
      };
2935

2936
      json.coinbase = tx.isCoinbase();
1✔
2937
      json.txid = input.prevout.txid();
1✔
2938
      json.vout = input.prevout.index;
1✔
2939
      json.txinwitness = input.witness.toJSON();
1✔
2940

2941
      vin.push(json);
1✔
2942
    }
2943

2944
    const vout = [];
1✔
2945

2946
    for (let i = 0; i < tx.outputs.length; i++) {
1✔
2947
      const output = tx.outputs[i];
2✔
2948
      vout.push({
2✔
2949
        value: Amount.coin(output.value, true),
2950
        n: i,
2951
        address: this.addrToJSON(output.address),
2952
        covenant: output.covenant.toJSON()
2953
      });
2954
    }
2955

2956
    return {
1✔
2957
      txid: tx.txid(),
2958
      hash: tx.wtxid(),
2959
      size: tx.getSize(),
2960
      vsize: tx.getVirtualSize(),
2961
      version: tx.version,
2962
      locktime: tx.locktime,
2963
      vin: vin,
2964
      vout: vout,
2965
      blockhash: hash ? hash.toString('hex') : null,
1!
2966
      confirmations: conf,
2967
      time: time,
2968
      blocktime: time,
2969
      hex: undefined
2970
    };
2971
  }
2972

2973
  scriptToJSON(script, hex) {
2974
    const type = script.getType();
×
2975

2976
    const json = {
×
2977
      asm: script.toASM(),
2978
      hex: undefined,
2979
      type: Script.typesByVal[type],
2980
      reqSigs: 1,
2981
      totalSigs: 1,
2982
      p2sh: undefined
2983
    };
2984

2985
    if (hex)
×
2986
      json.hex = script.toJSON();
×
2987

2988
    const [m, n] = script.getMultisig();
×
2989

2990
    if (m !== -1)
×
2991
      json.reqSigs = m;
×
2992

2993
    if (n !== -1)
×
2994
      json.totalSigs = n;
×
2995

2996
    return json;
×
2997
  }
2998

2999
  addrToJSON(addr) {
3000
    return {
2✔
3001
      version: addr.version,
3002
      hash: addr.hash.toString('hex'),
3003
      string: addr.toString(this.network)
3004
    };
3005
  }
3006

3007
  async headerToJSON(entry) {
3008
    const mtp = await this.chain.getMedianTime(entry);
×
3009
    const next = await this.chain.getNextHash(entry.hash);
×
3010

3011
    let confirmations = -1;
×
3012
    if (await this.chain.isMainChain(entry))
×
3013
      confirmations = this.chain.height - entry.height + 1;
×
3014

3015
    return {
×
3016
      hash: entry.hash.toString('hex'),
3017
      confirmations: confirmations,
3018
      height: entry.height,
3019
      version: entry.version,
3020
      versionHex: util.hex32(entry.version),
3021
      merkleroot: entry.merkleRoot.toString('hex'),
3022
      witnessroot: entry.witnessRoot.toString('hex'),
3023
      treeroot: entry.treeRoot.toString('hex'),
3024
      reservedroot: entry.reservedRoot.toString('hex'),
3025
      mask: entry.mask.toString('hex'),
3026
      time: entry.time,
3027
      mediantime: mtp,
3028
      nonce: entry.nonce,
3029
      extranonce: entry.extraNonce.toString('hex'),
3030
      bits: util.hex32(entry.bits),
3031
      difficulty: toDifficulty(entry.bits),
3032
      chainwork: entry.chainwork.toString('hex', 64),
3033
      previousblockhash: !entry.prevBlock.equals(consensus.ZERO_HASH)
×
3034
        ? entry.prevBlock.toString('hex')
3035
        : null,
3036
      nextblockhash: next ? next.toString('hex') : null
×
3037
    };
3038
  }
3039

3040
  async blockToJSON(entry, block, details) {
3041
    const mtp = await this.chain.getMedianTime(entry);
75✔
3042
    const next = await this.chain.getNextHash(entry.hash);
75✔
3043

3044
    let confirmations = -1;
75✔
3045
    if (await this.chain.isMainChain(entry))
75✔
3046
      confirmations = this.chain.height - entry.height + 1;
73✔
3047

3048
    const txs = [];
75✔
3049

3050
    for (const tx of block.txs) {
75✔
3051
      if (details) {
75!
3052
        const json = this.txToJSON(tx, entry);
×
3053
        txs.push(json);
×
3054
        continue;
×
3055
      }
3056
      txs.push(tx.txid());
75✔
3057
    }
3058

3059
    return {
75✔
3060
      hash: entry.hash.toString('hex'),
3061
      confirmations: confirmations,
3062
      strippedsize: block.getBaseSize(),
3063
      size: block.getSize(),
3064
      weight: block.getWeight(),
3065
      height: entry.height,
3066
      version: entry.version,
3067
      versionHex: util.hex32(entry.version),
3068
      merkleroot: entry.merkleRoot.toString('hex'),
3069
      witnessroot: entry.witnessRoot.toString('hex'),
3070
      treeroot: entry.treeRoot.toString('hex'),
3071
      reservedroot: entry.reservedRoot.toString('hex'),
3072
      mask: entry.mask.toString('hex'),
3073
      coinbase: !details
75!
3074
        ? block.txs[0].inputs[0].witness.toJSON()
3075
        : undefined,
3076
      tx: txs,
3077
      time: entry.time,
3078
      mediantime: mtp,
3079
      nonce: entry.nonce,
3080
      extranonce: entry.extraNonce.toString('hex'),
3081
      bits: util.hex32(entry.bits),
3082
      difficulty: toDifficulty(entry.bits),
3083
      chainwork: entry.chainwork.toString('hex', 64),
3084
      nTx: txs.length,
3085
      previousblockhash: !entry.prevBlock.equals(consensus.ZERO_HASH)
75✔
3086
        ? entry.prevBlock.toString('hex')
3087
        : null,
3088
      nextblockhash: next ? next.toString('hex') : null
75✔
3089
    };
3090
  }
3091

3092
  entryToJSON(entry) {
3093
    return {
×
3094
      size: entry.size,
3095
      fee: Amount.coin(entry.deltaFee, true),
3096
      modifiedfee: 0,
3097
      time: entry.time,
3098
      height: entry.height,
3099
      startingpriority: entry.priority,
3100
      currentpriority: entry.getPriority(this.chain.height),
3101
      descendantcount: this.mempool.countDescendants(entry),
3102
      descendantsize: entry.descSize,
3103
      descendantfees: entry.descFee,
3104
      ancestorcount: this.mempool.countAncestors(entry),
3105
      ancestorsize: 0,
3106
      ancestorfees: 0,
3107
      depends: this.mempool.getDepends(entry.tx)
3108
    };
3109
  }
3110

3111
  async getNameState(nameHash, safe) {
3112
    if (!safe) {
33✔
3113
      // Will always return null in SPV mode
3114
      return this.chain.db.getNameState(nameHash);
27✔
3115
    }
3116

3117
    // Safe roots are the last Urkel tree commitment
3118
    // with more than 12 confirmations.
3119
    const root = await this.chain.getSafeRoot();
6✔
3120
    let data;
3121
    if (this.chain.options.spv)
6✔
3122
      data = await this.pool.resolveAtRoot(nameHash, root);
3✔
3123
    else
3124
      data = await this.chain.db.lookup(root, nameHash);
3✔
3125

3126
    if (!data)
6!
3127
      return null;
×
3128

3129
    const ns = NameState.decode(data);
6✔
3130
    ns.nameHash = nameHash;
6✔
3131
    return ns;
6✔
3132
  }
3133
}
3134

3135
/*
3136
 * Helpers
3137
 */
3138

3139
function parseAddress(raw, network) {
3140
  try {
482✔
3141
    return Address.fromString(raw, network);
482✔
3142
  } catch (e) {
3143
    throw new RPCError(errs.INVALID_ADDRESS_OR_KEY, 'Invalid address.');
×
3144
  }
3145
}
3146

3147
function parseSecret(raw, network) {
3148
  try {
×
3149
    return KeyRing.fromSecret(raw, network);
×
3150
  } catch (e) {
3151
    throw new RPCError(errs.INVALID_ADDRESS_OR_KEY, 'Invalid key.');
×
3152
  }
3153
}
3154

3155
function parseIP(addr, network) {
3156
  let ip;
3157

3158
  try {
×
3159
    ip = IP.fromHostname(addr);
×
3160
  } catch (e) {
3161
    throw new RPCError(errs.CLIENT_INVALID_IP_OR_SUBNET,
×
3162
      'Invalid IP address or subnet.');
3163
  }
3164

3165
  if (ip.port === 0)
×
3166
    ip.port = ip.key ? network.brontidePort : network.port;
×
3167

3168
  return ip;
×
3169
}
3170

3171
function parseNetAddress(addr, network) {
3172
  try {
×
3173
    return NetAddress.fromHostname(addr, network);
×
3174
  } catch (e) {
3175
    throw new RPCError(errs.CLIENT_INVALID_IP_OR_SUBNET,
×
3176
      'Invalid IP address or subnet.');
3177
  }
3178
}
3179

3180
function toDifficulty(bits) {
3181
  let shift = (bits >>> 24) & 0xff;
76✔
3182
  let diff = 0x0000ffff / (bits & 0x00ffffff);
76✔
3183

3184
  while (shift < 29) {
76✔
3185
    diff *= 256.0;
×
3186
    shift++;
×
3187
  }
3188

3189
  while (shift > 29) {
76✔
3190
    diff /= 256.0;
228✔
3191
    shift--;
228✔
3192
  }
3193

3194
  return diff;
76✔
3195
}
3196

3197
/*
3198
 * Expose
3199
 */
3200

3201
module.exports = RPC;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc