diff --git a/.eslintrc.json b/.eslintrc.json index 34f7205..deb4061 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -19,6 +19,8 @@ "linebreak-style": "error", "quotes": ["error", "double", { "avoidEscape": true }], "prefer-destructuring": "error", - "semi": "error" + "semi": "error", + "camelcase": "off", + "@typescript-eslint/camelcase": ["error", { "properties": "never" }] } } diff --git a/README.md b/README.md index fa4af3c..aa86d18 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,195 @@ const response = await client.batch([ ]); ``` +### Blockchain + +- [`getbestblockhash`](https://bitcoin.org/en/developer-reference#getbestblockhash) + +```javascript +const hex = await client.getbestblockhash(); +``` + +- [`getblock`](https://bitcoin.org/en/developer-reference#getblock) + +```javascript +const blockhash = + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c"; +const verbosity = 2; +const block = await client.getblock({ blockhash, verbosity }); +``` + +- [`getblockchaininfo`](https://bitcoin.org/en/developer-reference#getblockchaininfo) + +```javascript +const info = await client.getblockchaininfo(); +``` + +- [`getblockcount`](https://bitcoin.org/en/developer-reference#getblockcount) + +```javascript +const count = await client.getblockcount(); +``` + +- [`getblockhash`](https://bitcoin.org/en/developer-reference#getblockhash) + +```javascript +const height = 1583782; +const hash = await client.getblockhash({ height }); +``` + +- [`getblockheader`](https://bitcoin.org/en/developer-reference#getblockheader) + +```javascript +const blockhash = + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c"; +const verbose = false; +const header = await client.getblockheader({ blockhash, verbose }); +``` + +- [`getblockstats`](https://bitcoin.org/en/developer-reference#getblockstats) + +```javascript +const hash_or_height = + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c"; +const stats = ["txs", "time"]; +const info = await client.getblockstats({ hash_or_height, stats }); +``` + +- [`getchaintips`](https://bitcoin.org/en/developer-reference#getchaintips) + +```javascript +const tips = await client.getchaintips(); +``` + +- [`getchaintxstats`](https://bitcoin.org/en/developer-reference#getchaintxstats) + +```javascript +const nblocks = 2016; +const blockhash = + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c"; +const data = await client.getchaintxstats({ nblocks, blockhash }); +``` + +- [`getdifficulty`](https://bitcoin.org/en/developer-reference#getdifficulty) + +```javascript +const difficulty = await client.getdifficulty(); +``` + +- [`getmempoolancestors`](https://bitcoin.org/en/developer-reference#getmempoolancestors) + +```javascript +const verbose = true; +const txid = "3e128c38f35520d4121d582f15998b7f74b44f17aa650b4d60decf975e642b9a"; +const data = await client.getmempoolancestors({ txid, verbose }); +``` + +- [`getmempooldescendants`](https://bitcoin.org/en/developer-reference#getmempooldescendants) + +```javascript +const verbose = true; +const txid = "ff758ffd73729be8afae0d683547f7840bdaee75ad5e5c464fb621b2509c366b"; +const data = await client.getmempooldescendants({ txid, verbose }); +``` + +- [`getmempoolentry`](https://bitcoin.org/en/developer-reference#getmempoolentry) + +```javascript +const txid = "0629e01f05088728b089715f247de82a160428c06d6c85484adab2aa66574ace"; +const data = await client.getmempoolentry({ txid }); +``` + +- [`getmempoolinfo`](https://bitcoin.org/en/developer-reference#getmempoolinfo) + +```javascript +const info = await client.getmempoolinfo(); +``` + +- [`getrawmempool`](https://bitcoin.org/en/developer-reference#getrawmempool) + +```javascript +const verbose = true; +const data = await client.getrawmempool({ verbose }); +``` + +- [`gettxout`](https://bitcoin.org/en/developer-reference#gettxout) + +```javascript +const txid = "d2f6b1d1844e483ce350a4a22fbaef36c31ebe88730415b7408c1f34b834fab5"; +const n = 1; +const include_mempool = true; +const data = await client.gettxout({ txid, n, include_mempool }); +``` + +- [`gettxoutproof`](https://bitcoin.org/en/developer-reference#gettxoutproof) + +```javascript +const txids = [ + "42e75d074cf5b836170d20fc09593245c65a3f07283a497c3350c4d109b38bb6" +]; +const blockhash = + "000000000000000000055bc30b762904ab996430603cafe846cc6adc82c4af1e"; +const data = await client.gettxoutproof({ txids, blockhash }); +``` + +- [`gettxoutsetinfo`](https://bitcoin.org/en/developer-reference#gettxoutsetinfo) + +```javascript +const info = await client.gettxoutsetinfo(); +``` + +- [`preciousblock`](https://bitcoin.org/en/developer-reference#preciousblock) + +```javascript +const blockhash = + "00000000000000261a35cf378bf8fa1bf6ac87800d798ce2a11f581f562e92ba"; +const data = await client.preciousblock({ blockhash }); +``` + +- [`pruneblockchain`](https://bitcoin.org/en/developer-reference#pruneblockchain) + +```javascript +const height = 1000; +const result = await client.pruneblockchain({ height }); +``` + +- [`savemempool`](https://bitcoin.org/en/developer-reference#savemempool) + +```javascript +const result = await client.savemempool(); +``` + +- [`scantxoutset`](https://bitcoin.org/en/developer-reference#scantxoutset) + +```javascript +const action = "start"; +const scanobjects = [ + "addr(mxosQ4CvQR8ipfWdRktyB3u16tauEdamGc)", + { + desc: + "wpkh([d34db33f/84'/0'/0']tpubD6NzVbkrYhZ4YTN7usjEzYmfu4JKqnfp9RCbDmdKH78vTyuwgQat8vRw5cX1YaZZvFfQrkHrM2XsyfA8cZE1thA3guTBfTkKqbhCDpcKFLG/0/*)#8gfuh6ex", + range: [1, 3] + } +]; +const result = await client.scantxoutset({ action, scanobjects }); +``` + +- [`verifychain`](https://bitcoin.org/en/developer-reference#verifychain) + +```javascript +const checklevel = 1; +const nblocks = 10; +const result = await client.verifychain({ checklevel, nblocks }); +``` + +- [`verifytxoutproof`](https://bitcoin.org/en/developer-reference#verifytxoutproof) + +```javascript +const proof = + "00000020ed07b12f0398e45fd403db11dbe894dc3301ce1a7725424e0b5e460c0000000066fca14db436f305aea37b3ae0f8b188cbf112dff3854c3d419f3ff3ebbc821f6c0c975dffff001d8fd5a7d2eb00000009e7acd3f605d1b957d684d9eeca9c472d803d90c0d17e29e5606f9b080b177a4abcd854622ad3900b5bc1ae71e99699a05eb972d46bd439c08eb7fbd20bba6494542222b2d1388f52c6d23ac12b32245ca47b02fc2f0a283a88aabca1f4db43ca8a4da8ffd7d9ae403b0c34ccbf14d2318c34fabb713c48f6d6490c6095250b6f08f26f020275d448dfb9967c62bedefaf29260021671a191f620f7783252788549b1e033dc815e2cd36ff204b398046f834643859f881a4d93b3fc5b91413a009c5069be274e1dcc675183ea2a989ef598422c0ed02e407aade8eaa6ef7ec1120ca4ffdef21b5fd26c4525a27c78cc38026b257f9d23f0d796603b1d3cbf539bdf87ccf9e81954f58e072d67eff2891339f203cbdec68bbbabbbbc0c070cceea03bf0a00"; +const result = await client.verifytxoutproof({ proof }); +``` + ## [HTTP REST](https://bitcoin.org/en/developer-reference#http-rest) ```javascript diff --git a/package-lock.json b/package-lock.json index 80ae57b..e864916 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "rpc-bitcoin", - "version": "1.0.0", + "version": "1.2.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b68ca86..643eada 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rpc-bitcoin", - "version": "1.0.0", + "version": "1.2.0", "description": "A TypeScript library to make RPC and HTTP REST requests to Bitcoin Core", "main": "build/index.js", "type": "module", diff --git a/src/rpc.ts b/src/rpc.ts index 8d0688c..a60c625 100644 --- a/src/rpc.ts +++ b/src/rpc.ts @@ -14,6 +14,51 @@ export type JSONRPC = { params?: any; }; +export type Verbosity = { verbosity?: 0 | 1 | 2 }; + +export type Verbose = { verbose?: boolean }; + +export type Height = { height: number }; + +export type Blockhash = { blockhash: string }; + +export type TxId = { txid: string }; + +export type GetBlockParams = Verbosity & Blockhash; + +export type GetBlockHeaderParams = Blockhash & Verbose; + +export type GetBlockStatsParams = { + hash_or_height: string | number; + stats?: string[]; +}; + +export type GetChainTxStatsParams = { + nblocks?: number; + blockhash?: string; +}; + +export type GetMemPoolParams = TxId & Verbose; + +export type GetTxOutParams = TxId & { + n: number; + include_mempool?: boolean; +}; + +export type GetTxOutProofParams = { txids: string[]; blockhash?: string }; + +export type Descriptor = + | string + | { + desc: string; + range: number | [number, number]; + }; + +export type ScanTxOutSetParams = { + action: "start" | "abort" | "status"; + scanobjects: Descriptor[]; +}; + export class RPCClient extends RESTClient { wallet?: string; fullResponse?: boolean; @@ -42,4 +87,172 @@ export class RPCClient extends RESTClient { const response = await this.batch(body, uri); return this.fullResponse ? response : response.result; } + + /** + * @description Returns the hash of the best (tip) block in the longest blockchain. + */ + async getbestblockhash() { + return this.rpc("getbestblockhash"); + } + + /** + * @description If verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbosity is 1, returns an Object with information about block . If verbosity is 2, returns an Object with information about block and information about each transaction. + */ + async getblock({ blockhash, verbosity = 1 }: GetBlockParams) { + return this.rpc("getblock", { blockhash, verbosity }); + } + + /** + * @description Returns an object containing various state info regarding blockchain processing. + */ + async getblockchaininfo() { + return this.rpc("getblockchaininfo"); + } + + /** + * @description Returns the number of blocks in the longest blockchain. + */ + async getblockcount() { + return this.rpc("getblockcount"); + } + + /** + * @description Returns hash of block in best-block-chain at height provided. + */ + async getblockhash({ height }: Height) { + return this.rpc("getblockhash", { height }); + } + + /** + * @description If verbose is `false`, returns a string that is serialized, hex-encoded data for blockheader 'hash'. If verbose is `true`, returns an Object with information about blockheader . + */ + async getblockheader({ blockhash, verbose = true }: GetBlockHeaderParams) { + return this.rpc("getblockheader", { blockhash, verbose }); + } + + /** + * @description Compute per block statistics for a given window. + */ + async getblockstats({ hash_or_height, stats = [] }: GetBlockStatsParams) { + return this.rpc("getblockstats", { hash_or_height, stats }); + } + + /** + * @description Return information about all known tips in the block tree, including the main chain as well as orphaned branches. + */ + async getchaintips() { + return this.rpc("getchaintips"); + } + + /** + * @description Compute statistics about the total number and rate of transactions in the chain. + */ + async getchaintxstats({ nblocks, blockhash }: GetChainTxStatsParams) { + return this.rpc("getchaintxstats", { nblocks, blockhash }); + } + + /** + * @description Returns the proof-of-work difficulty as a multiple of the minimum difficulty. + */ + async getdifficulty() { + return this.rpc("getdifficulty"); + } + + /** + * @description If txid is in the mempool, returns all in-mempool ancestors. + */ + async getmempoolancestors({ txid, verbose = false }: GetMemPoolParams) { + return this.rpc("getmempoolancestors", { txid, verbose }); + } + + /** + * @description If txid is in the mempool, returns all in-mempool descendants. + */ + async getmempooldescendants({ txid, verbose = false }: GetMemPoolParams) { + return this.rpc("getmempooldescendants", { txid, verbose }); + } + + /** + * @description Returns mempool data for given transaction + */ + async getmempoolentry({ txid }: TxId) { + return this.rpc("getmempoolentry", { txid }); + } + + /** + * @description Returns details on the active state of the TX memory pool. + */ + async getmempoolinfo() { + return this.rpc("getmempoolinfo"); + } + + /** + * @description Returns all transaction ids in memory pool as a json array of string transaction ids. + */ + async getrawmempool({ verbose = false }: Verbose = {}) { + return this.rpc("getrawmempool", { verbose }); + } + + /** + * @description Returns details about an unspent transaction output. + */ + async gettxout({ txid, n, include_mempool = true }: GetTxOutParams) { + return this.rpc("gettxout", { txid, n, include_mempool }); + } + + /** + * @description Returns a hex-encoded proof that "txid" was included in a block. + */ + async gettxoutproof({ txids, blockhash }: GetTxOutProofParams) { + return this.rpc("gettxoutproof", { txids, blockhash }); + } + + /** + * @description Returns statistics about the unspent transaction output set. + */ + async gettxoutsetinfo() { + return this.rpc("gettxoutsetinfo"); + } + + /** + * @description Treats a block as if it were received before others with the same work. + */ + async preciousblock({ blockhash }: Blockhash) { + return this.rpc("preciousblock", { blockhash }); + } + + /** + * @description Prune the blockchain. + */ + async pruneblockchain({ height }: Height) { + return this.rpc("pruneblockchain", { height }); + } + + /** + * @description Dumps the mempool to disk. It will fail until the previous dump is fully loaded. + */ + async savemempool() { + return this.rpc("savemempool"); + } + + /** + * @description Scans the unspent transaction output set for entries that match certain output descriptors. + */ + async scantxoutset({ action, scanobjects }: ScanTxOutSetParams) { + return this.rpc("scantxoutset", { action, scanobjects }); + } + + /** + * @description Verifies blockchain database. + */ + async verifychain({ checklevel = 3, nblocks = 6 } = {}) { + return this.rpc("verifychain", { checklevel, nblocks }); + } + + /** + * @description Verifies that a proof points to a transaction in a block, returning the transaction it commits to and throwing an RPC error if the block is not in our best chain. + */ + async verifytxoutproof({ proof }: { proof: string }) { + return this.rpc("verifytxoutproof", { proof }); + } } diff --git a/test/rpc.spec.ts b/test/rpc.spec.ts index 5cd3bb7..60a5ede 100644 --- a/test/rpc.spec.ts +++ b/test/rpc.spec.ts @@ -1,4 +1,9 @@ -import { RPCClient } from "../."; +import { + RPCClient, + GetBlockParams, + ScanTxOutSetParams, + Descriptor +} from "../."; import * as nock from "nock"; import * as assert from "assert"; @@ -112,4 +117,777 @@ suite("RPCClient", () => { const data = await client.rpc(method, params, wallet); assert.deepStrictEqual(data, result); }); + + suite("Blockchain", () => { + test(".getbestblockhash()", async () => { + const request = { params: {}, method: "getbestblockhash", id, jsonrpc }; + const result = + "000000006e60e2ae7b464e4e38e061cb4aea9dafa605cc1d38d34601fdf77064"; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getbestblockhash(); + assert.deepStrictEqual(data, result); + }); + + test(".getblock()", async () => { + const blockhash = + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c"; + const verbosity = 2; + const params: GetBlockParams = { blockhash, verbosity }; + const request = { params, method: "getblock", id, jsonrpc }; + const result = { + hash: + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c", + confirmations: 3383, + strippedsize: 123950, + size: 178661, + weight: 550511, + height: 1580399, + version: 536870912, + versionHex: "20000000", + merkleroot: + "d3f3bb31c88119d341f9a5598a213655bc0168a83a9d9cb80ac4386485b3c0f5", + tx: [ + { + txid: + "6adb52bcfbc0f88378eed06783d5dbb3edd649f485c3fdc2f38e779e724f612a", + hash: + "2b1826bdd156e6f86248404da4f51febd58eaa8abdb85572302bb27af89a4089", + version: 1, + size: 197, + vsize: 170, + weight: 680, + locktime: 0, + vin: [ + { + coinbase: + "036f1d180459fe965d08f80000014b0700000c2f746573746e65747465722f", + sequence: 0 + } + ], + vout: [ + { + value: 0.53670882, + n: 0, + scriptPubKey: { + asm: + "OP_HASH160 b4316d69836fe185d3d4ca234e90a7a5ce6491ab OP_EQUAL", + hex: "a914b4316d69836fe185d3d4ca234e90a7a5ce6491ab87", + reqSigs: 1, + type: "scripthash", + addresses: ["2N9fzq66uZYQXp7uqrPBH6jKBhjrgTzpGCy"] + } + }, + { + value: 0, + n: 1, + scriptPubKey: { + asm: + "OP_RETURN aa21a9ed3116a1d2c6dc9cf601112589ce7d2334acd8a196fb02ceacddf6eed2bf4b72b5", + hex: + "6a24aa21a9ed3116a1d2c6dc9cf601112589ce7d2334acd8a196fb02ceacddf6eed2bf4b72b5", + type: "nulldata" + } + } + ], + hex: + "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff1f036f1d180459fe965d08f80000014b0700000c2f746573746e65747465722f0000000002e2f332030000000017a914b4316d69836fe185d3d4ca234e90a7a5ce6491ab870000000000000000266a24aa21a9ed3116a1d2c6dc9cf601112589ce7d2334acd8a196fb02ceacddf6eed2bf4b72b50120000000000000000000000000000000000000000000000000000000000000000000000000" + }, + { + txid: + "5416f86c8d41840ff9c09d997372a7f140e78b765252583fb23f74e0992c6e71", + hash: + "5416f86c8d41840ff9c09d997372a7f140e78b765252583fb23f74e0992c6e71", + version: 1, + size: 226, + vsize: 226, + weight: 904, + locktime: 0, + vin: [ + { + txid: + "623145a17de90d190f3b2379672b3bb11f19f73b3be4c412cea1f60f9bbe7344", + vout: 1, + scriptSig: { + asm: + "304502210098a6a7a2329a7373ff838b3e816e8b48f94238dfbe08add7896ff95127ebfc310220541302168ae986ec6abbb0c5eea95f069621166fe841ba965ed6ea3bb351735c[ALL] 0396cfa148d2fc150d225262836aaf4ed98da771a9c2f6bc54da03d402d3f1a384", + hex: + "48304502210098a6a7a2329a7373ff838b3e816e8b48f94238dfbe08add7896ff95127ebfc310220541302168ae986ec6abbb0c5eea95f069621166fe841ba965ed6ea3bb351735c01210396cfa148d2fc150d225262836aaf4ed98da771a9c2f6bc54da03d402d3f1a384" + }, + sequence: 4294967295 + } + ], + vout: [ + { + value: 5e-8, + n: 0, + scriptPubKey: { + asm: + "OP_DUP OP_HASH160 ef6639af5e3f5beb577f327f09ec3b0708cb03f1 OP_EQUALVERIFY OP_CHECKSIG", + hex: "76a914ef6639af5e3f5beb577f327f09ec3b0708cb03f188ac", + reqSigs: 1, + type: "pubkeyhash", + addresses: ["n3LnFeCEuo6zcRS7kGuKx9URwnLXHaKonL"] + } + }, + { + value: 0.40164686, + n: 1, + scriptPubKey: { + asm: + "OP_DUP OP_HASH160 60ade08bbf58068ccbed250d4ee64c0a827745d3 OP_EQUALVERIFY OP_CHECKSIG", + hex: "76a91460ade08bbf58068ccbed250d4ee64c0a827745d388ac", + reqSigs: 1, + type: "pubkeyhash", + addresses: ["mpL9TfWHudvTkWPrtzwkcuD4mKoo7eXJo5"] + } + } + ], + hex: + "01000000014473be9b0ff6a1ce12c4e43b3bf7191fb13b2b6779233b0f190de97da1453162010000006b48304502210098a6a7a2329a7373ff838b3e816e8b48f94238dfbe08add7896ff95127ebfc310220541302168ae986ec6abbb0c5eea95f069621166fe841ba965ed6ea3bb351735c01210396cfa148d2fc150d225262836aaf4ed98da771a9c2f6bc54da03d402d3f1a384ffffffff0205000000000000001976a914ef6639af5e3f5beb577f327f09ec3b0708cb03f188ac4edd6402000000001976a91460ade08bbf58068ccbed250d4ee64c0a827745d388ac00000000" + } + ], + time: 1570176600, + mediantime: 1570170591, + nonce: 534459415, + bits: "1d00ffff", + difficulty: 1, + chainwork: + "00000000000000000000000000000000000000000000012b3c804ee0bb6e8fdf", + nTx: 469, + previousblockhash: + "000000006d7da16a58a4e56c790d1aeb4e08bce6dab68e03995f585b187551be", + nextblockhash: + "000000005104d2692a021f9b58932c3fa32ea15b97cbff2147e11ad24f9d49af" + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getblock(params); + assert.deepStrictEqual(data, result); + }); + + test(".getblockchaininfo()", async () => { + const request = { params: {}, method: "getblockchaininfo", id, jsonrpc }; + const result = { + chain: "test", + blocks: 1583781, + headers: 1583781, + bestblockhash: + "000000003572a2f04d0b30c644ae4281b37e9becde9fc8a17fb54253a1a07397", + difficulty: 1, + mediantime: 1571834687, + verificationprogress: 0.9999861133153368, + initialblockdownload: false, + chainwork: + "00000000000000000000000000000000000000000000012e44a73ad9f48c5bbb", + size_on_disk: 1004297599, + pruned: true, + pruneheight: 1566856, + automatic_pruning: true, + prune_target_size: 1073741824, + softforks: [ + { id: "bip34", version: 2, reject: { status: true } }, + { id: "bip66", version: 3, reject: { status: true } }, + { id: "bip65", version: 4, reject: { status: true } } + ], + bip9_softforks: { + csv: { + status: "active", + startTime: 1456790400, + timeout: 1493596800, + since: 770112 + }, + segwit: { + status: "active", + startTime: 1462060800, + timeout: 1493596800, + since: 834624 + } + }, + warnings: "Warning: unknown new rules activated (versionbit 28)" + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getblockchaininfo(); + assert.deepStrictEqual(data, result); + }); + + test(".getblockcount()", async () => { + const request = { params: {}, method: "getblockcount", id, jsonrpc }; + const result = 1583782; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getblockcount(); + assert.deepStrictEqual(data, result); + }); + + test(".getblockhash()", async () => { + const height = 1583782; + const params = { height }; + const request = { params, method: "getblockhash", id, jsonrpc }; + const result = + "00000000a4991fe43933f0a0bde13b6b22b4308442453845903151004e9cc0a5"; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getblockhash(params); + assert.deepStrictEqual(data, result); + }); + + test(".getblockheader()", async () => { + const blockhash = + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c"; + const verbose = false; + const params = { blockhash, verbose }; + const request = { params, method: "getblockheader", id, jsonrpc }; + const result = + "00000020be5175185b585f99038eb6dae6bc084eeb1a0d796ce5a4586aa17d6d00000000f5c0b3856438c40ab89c9d3aa86801bc5536218a59a5f941d31981c831bbf3d358fe965dffff001d1734db1f"; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getblockheader(params); + assert.deepStrictEqual(data, result); + }); + + test(".getblockstats()", async () => { + const hash_or_height = + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c"; + const stats = ["txs", "time"]; + const params = { hash_or_height, stats }; + const request = { params, method: "getblockstats", id, jsonrpc }; + const result = { time: 1570176600, txs: 469 }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getblockstats(params); + assert.deepStrictEqual(data, result); + }); + + test(".getchaintips()", async () => { + const request = { params: {}, method: "getchaintips", id, jsonrpc }; + const result = [ + { + height: 1583784, + hash: + "0000000071434abcdf7b2a82fb3005a67fe9f458e542a586313f5a7dc671a0c9", + branchlen: 0, + status: "active" + }, + { + height: 1580960, + hash: + "000000006999656106c726515ccfc34d160a5fa299ddb6bb278598b2feefaa7e", + branchlen: 1, + status: "valid-fork" + }, + { + height: 1580787, + hash: + "0000000029515fe9800761af4c19a087525ad9f3a1e41c4d1b136993711c3f83", + branchlen: 1, + status: "valid-fork" + }, + { + height: 1414433, + hash: + "00000000210004840364b52bc5e455d888f164e4264a4fec06a514b67e9d5722", + branchlen: 23, + status: "headers-only" + } + ]; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getchaintips(); + assert.deepStrictEqual(data, result); + }); + + test(".getchaintxstats()", async () => { + const nblocks = 2016; + const blockhash = + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c"; + const params = { nblocks, blockhash }; + const request = { params, method: "getchaintxstats", id, jsonrpc }; + const result = { + time: 1570176600, + txcount: 52393515, + window_final_block_hash: + "000000004182034f427d463b92162d35d0accef9ea0c5354a87e870ca1815b4c", + window_block_count: 2016, + window_tx_count: 274713, + window_interval: 1731648, + txrate: 0.1586425185719038 + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getchaintxstats(params); + assert.deepStrictEqual(data, result); + }); + + test(".getdifficulty()", async () => { + const request = { params: {}, method: "getdifficulty", id, jsonrpc }; + const result = 1; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getdifficulty(); + assert.deepStrictEqual(data, result); + }); + + test(".getmempoolancestors()", async () => { + const verbose = true; + const txid = + "3e128c38f35520d4121d582f15998b7f74b44f17aa650b4d60decf975e642b9a"; + const params = { verbose, txid }; + const request = { params, method: "getmempoolancestors", id, jsonrpc }; + const result = { + ff758ffd73729be8afae0d683547f7840bdaee75ad5e5c464fb621b2509c366b: { + fees: { + base: 0.00000208, + modified: 0.00000208, + ancestor: 0.00000208, + descendant: 0.00000349 + }, + size: 208, + fee: 0.00000208, + modifiedfee: 0.00000208, + time: 1571845913, + height: 1583786, + descendantcount: 2, + descendantsize: 349, + descendantfees: 349, + ancestorcount: 1, + ancestorsize: 208, + ancestorfees: 208, + wtxid: + "4ec7101b17a19ad11c6b738330303f9baa30c0aabc3e56ce8735d019fcff13e7", + depends: [], + spentby: [ + "3e128c38f35520d4121d582f15998b7f74b44f17aa650b4d60decf975e642b9a" + ], + "bip125-replaceable": true + } + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getmempoolancestors(params); + assert.deepStrictEqual(data, result); + }); + + test(".getmempooldescendants()", async () => { + const verbose = true; + const txid = + "ff758ffd73729be8afae0d683547f7840bdaee75ad5e5c464fb621b2509c366b"; + const params = { verbose, txid }; + const request = { params, method: "getmempooldescendants", id, jsonrpc }; + const result = { + "3e128c38f35520d4121d582f15998b7f74b44f17aa650b4d60decf975e642b9a": { + fees: { + base: 0.00000141, + modified: 0.00000141, + ancestor: 0.00000349, + descendant: 0.00000141 + }, + size: 141, + fee: 0.00000141, + modifiedfee: 0.00000141, + time: 1571845933, + height: 1583786, + descendantcount: 1, + descendantsize: 141, + descendantfees: 141, + ancestorcount: 2, + ancestorsize: 349, + ancestorfees: 349, + wtxid: + "6a2704699b5935b96a9c008b21518e99990cf099a6977760d56aec350b2d9d66", + depends: [ + "ff758ffd73729be8afae0d683547f7840bdaee75ad5e5c464fb621b2509c366b" + ], + spentby: [], + "bip125-replaceable": true + } + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getmempooldescendants(params); + assert.deepStrictEqual(data, result); + }); + + test(".getmempoolentry()", async () => { + const txid = + "0629e01f05088728b089715f247de82a160428c06d6c85484adab2aa66574ace"; + const params = { txid }; + const request = { params, method: "getmempoolentry", id, jsonrpc }; + const result = { + fees: { + base: 0.00000148, + modified: 0.00000148, + ancestor: 0.00000391, + descendant: 0.00000148 + }, + size: 146, + fee: 0.00000148, + modifiedfee: 0.00000148, + time: 1571846114, + height: 1583786, + descendantcount: 1, + descendantsize: 146, + descendantfees: 148, + ancestorcount: 2, + ancestorsize: 389, + ancestorfees: 391, + wtxid: + "cf5208ea2196816473a315504b5476fb4e75a16fb4584dda92093b72901bde08", + depends: [ + "f594f57099cd6e1c4d0697ad92795196a1774ea752d1bec481019abd3eef30ee" + ], + spentby: [], + "bip125-replaceable": false + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getmempoolentry(params); + assert.deepStrictEqual(data, result); + }); + + test(".getmempoolinfo()", async () => { + const request = { params: {}, method: "getmempoolinfo", id, jsonrpc }; + const result = { + size: 208, + bytes: 73712, + usage: 362416, + maxmempool: 300000000, + mempoolminfee: 0.00001, + minrelaytxfee: 0.00001 + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getmempoolinfo(); + assert.deepStrictEqual(data, result); + }); + + test(".getrawmempool()", async () => { + const verbose = true; + const params = { verbose }; + const request = { params, method: "getrawmempool", id, jsonrpc }; + const result = { + "9d4e511e5138b90cf530e1b33e391bf2f04f68a6d5bec3218b03d353655e3769": { + fees: { + base: 0.00000543, + modified: 0.00000543, + ancestor: 0.00000543, + descendant: 0.00000543 + }, + size: 149, + fee: 0.00000543, + modifiedfee: 0.00000543, + time: 1571846917, + height: 1583787, + descendantcount: 1, + descendantsize: 149, + descendantfees: 543, + ancestorcount: 1, + ancestorsize: 149, + ancestorfees: 543, + wtxid: + "957c764cede26364705436977e087039f4a8fb9d68c51c9db9e2347dbd12a120", + depends: [], + spentby: [], + "bip125-replaceable": true + }, + "2275109000640d8e45ec8e23cf74ba8a82850bb5c01993972f1a40dd20fa9484": { + fees: { + base: 0.00016797, + modified: 0.00016797, + ancestor: 0.00016797, + descendant: 0.00016797 + }, + size: 166, + fee: 0.00016797, + modifiedfee: 0.00016797, + time: 1571846920, + height: 1583787, + descendantcount: 1, + descendantsize: 166, + descendantfees: 16797, + ancestorcount: 1, + ancestorsize: 166, + ancestorfees: 16797, + wtxid: + "7b05abb26d64d6c39d1f483a2cac992c3ebad77f9240fadeeee90649ffbc9092", + depends: [], + spentby: [], + "bip125-replaceable": false + } + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.getrawmempool(params); + assert.deepStrictEqual(data, result); + }); + + test(".gettxout()", async () => { + const txid = + "d2f6b1d1844e483ce350a4a22fbaef36c31ebe88730415b7408c1f34b834fab5"; + const n = 1; + const include_mempool = true; + const params = { txid, n, include_mempool }; + const request = { params, method: "gettxout", id, jsonrpc }; + const result = { + bestblock: + "000000000000006b3157f2f7d8cda21be0204863c93521e9afa05615436deb71", + confirmations: 0, + value: 76.67255676, + scriptPubKey: { + asm: "OP_HASH160 f2420e17b443ea418ec4c6ac97affaafd48eca70 OP_EQUAL", + hex: "a914f2420e17b443ea418ec4c6ac97affaafd48eca7087", + reqSigs: 1, + type: "scripthash", + addresses: ["2NFLAeHuBzrMfqQHfCtnKErNJSF3fqysUhF"] + }, + coinbase: false + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.gettxout(params); + assert.deepStrictEqual(data, result); + }); + + test(".gettxoutproof()", async () => { + const txids = [ + "e50c4bf07ca16e5089bf8c4f4b4d12b4e6b2bb47b09d533ebaa395342c756b95" + ]; + const blockhash = + "000000005a9b7656024e9d1a2a0e559b91dcc5756048ce4904ba877686f0eecc"; + const params = { txids, blockhash }; + const request = { params, method: "gettxoutproof", id, jsonrpc }; + const result = + "00000020e798ee174759ba2eb4f57c8055eaadb903aeef74a407878265361d00000000005c70c7f197058b8ff6f06d9f144497d1801e057e06735a56b658ac78ff915516fa7ab05dffff001d32384563dd00000009eeeb7a022e70291fe3d8d5186615358d45107c10d71a212459b30fe73174494f956b752c3495a3ba3e539db047bbb2e6b4124d4b4f8cbf89506ea17cf04b0ce5864f91edb21f1918fb1031d5545c7b835fb82d8cdc87f1df76808c599bbcb4e372e0788a9ce2c9b2f1a07305b7bea5e1fca0b19f77d919e51e2e72e438c19df5a953ce7bad42e1c78372ad2df199a08c26153f7846f6cc95c4615572bc997b45803b803c49e6298dec1a9029c32addac44f2abbc8c496def1f47a6ff55d8f90901b66efccdea0dcb52c26d610c4f2ac0b1e699ee4606918ff9901c317919038a5a3f3cedf7d664a2891fe1e116dc52e9ca9166e4e8a6398b10072412ef3893693508204127c87c8c025dded104f34ceeb44958095571907e6787615929cbc8cd03ff0200"; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.gettxoutproof(params); + assert.deepStrictEqual(data, result); + }); + + test(".gettxoutsetinfo()", async () => { + const request = { params: {}, method: "gettxoutsetinfo", id, jsonrpc }; + const result = { + height: 1583793, + bestblock: + "00000000000000b332c067ccb2cb52aafac5b7de4bec01470b6c634449e6ebbc", + transactions: 8345507, + txouts: 22946232, + bogosize: 1722061178, + hash_serialized_2: + "9b4021d53da6689371aa734ef32fe502ed38a47870db2802c48c8cde0a5c191e", + disk_size: 1256916738, + total_amount: 20879513.30609612 + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.gettxoutsetinfo(); + assert.deepStrictEqual(data, result); + }); + + test(".preciousblock()", async () => { + const blockhash = + "00000000000000261a35cf378bf8fa1bf6ac87800d798ce2a11f581f562e92ba"; + const params = { blockhash }; + const request = { params, method: "preciousblock", id, jsonrpc }; + const result = null; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.preciousblock(params); + assert.deepStrictEqual(data, result); + }); + + test(".pruneblockchain()", async () => { + const height = 1000; + const params = { height }; + const request = { params, method: "pruneblockchain", id, jsonrpc }; + const result = 1566856; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.pruneblockchain(params); + assert.deepStrictEqual(data, result); + }); + + test(".savemempool()", async () => { + const request = { params: {}, method: "savemempool", id, jsonrpc }; + const result = null; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.savemempool(); + assert.deepStrictEqual(data, result); + }); + + test(".scantxoutset()", async () => { + const action = "start"; + const desc_1 = "addr(mxosQ4CvQR8ipfWdRktyB3u16tauEdamGc)"; + const desc_2: Descriptor = { + desc: + "wpkh([d34db33f/84'/0'/0']tpubD6NzVbkrYhZ4YTN7usjEzYmfu4JKqnfp9RCbDmdKH78vTyuwgQat8vRw5cX1YaZZvFfQrkHrM2XsyfA8cZE1thA3guTBfTkKqbhCDpcKFLG/0/*)#8gfuh6ex", + range: [1, 20] + }; + const scanobjects = [desc_1, desc_2]; + const params: ScanTxOutSetParams = { action, scanobjects }; + const request = { params, method: "scantxoutset", id, jsonrpc }; + const result = { + success: true, + searched_items: 22946468, + unspents: [ + { + txid: + "ab78587c07c039d1e55dc0efc959ba872693f98dce9e749a53582125e692f408", + vout: 1, + scriptPubKey: "76a914bdad1f4d02035b61fb1d237410e85d8402a1187d88ac", + desc: "addr(mxosQ4CvQR8ipfWdRktyB3u16tauEdamGc)#7ca3vlzt", + amount: 0.08745533, + height: 1583799 + }, + { + txid: + "ed6f71276d0624989e8d572c98386e35c646cdce062c73ae0a1f554887d41aa5", + vout: 1, + scriptPubKey: "76a914bdad1f4d02035b61fb1d237410e85d8402a1187d88ac", + desc: "addr(mxosQ4CvQR8ipfWdRktyB3u16tauEdamGc)#7ca3vlzt", + amount: 0, + height: 1352790 + }, + { + txid: + "801d5821586dd0dc10123b17d284983d6c835b8aa616e0ee828721c9073ba7ea", + vout: 1, + scriptPubKey: "76a914bdad1f4d02035b61fb1d237410e85d8402a1187d88ac", + desc: "addr(mxosQ4CvQR8ipfWdRktyB3u16tauEdamGc)#7ca3vlzt", + amount: 0, + height: 1326382 + } + ], + total_amount: 0.08745533 + }; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.scantxoutset(params); + assert.deepStrictEqual(data, result); + }); + + test(".verifychain()", async () => { + const checklevel = 1; + const nblocks = 10; + const params = { checklevel, nblocks }; + const request = { params, method: "verifychain", id, jsonrpc }; + const result = true; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.verifychain(params); + assert.deepStrictEqual(data, result); + }); + + test(".verifytxoutproof()", async () => { + const proof = + "00000020ed07b12f0398e45fd403db11dbe894dc3301ce1a7725424e0b5e460c0000000066fca14db436f305aea37b3ae0f8b188cbf112dff3854c3d419f3ff3ebbc821f6c0c975dffff001d8fd5a7d2eb00000009e7acd3f605d1b957d684d9eeca9c472d803d90c0d17e29e5606f9b080b177a4abcd854622ad3900b5bc1ae71e99699a05eb972d46bd439c08eb7fbd20bba6494542222b2d1388f52c6d23ac12b32245ca47b02fc2f0a283a88aabca1f4db43ca8a4da8ffd7d9ae403b0c34ccbf14d2318c34fabb713c48f6d6490c6095250b6f08f26f020275d448dfb9967c62bedefaf29260021671a191f620f7783252788549b1e033dc815e2cd36ff204b398046f834643859f881a4d93b3fc5b91413a009c5069be274e1dcc675183ea2a989ef598422c0ed02e407aade8eaa6ef7ec1120ca4ffdef21b5fd26c4525a27c78cc38026b257f9d23f0d796603b1d3cbf539bdf87ccf9e81954f58e072d67eff2891339f203cbdec68bbbabbbbc0c070cceea03bf0a00"; + const params = { proof }; + const request = { params, method: "verifytxoutproof", id, jsonrpc }; + const result = [ + "6f0b2595600c49d6f6483c71bbfa348c31d214bfcc340c3b40aed9d7ffa84d8a" + ]; + const response = { result, error, id }; + nock(uri) + .post("/", request) + .times(1) + .basicAuth(auth) + .reply(200, response); + const data = await client.verifytxoutproof(params); + assert.deepStrictEqual(data, result); + }); + }); });