Skip to content

Commit

Permalink
feat<node>: added deriveaddresses rpc
Browse files Browse the repository at this point in the history
  • Loading branch information
Vasu-08 committed Jul 18, 2023
1 parent db7b93d commit e69cb9f
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 2 deletions.
85 changes: 83 additions & 2 deletions lib/node/rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ class RPC extends RPCBase {
this.add('getmemoryinfo', this.getMemoryInfo);
this.add('setloglevel', this.setLogLevel);
this.add('getdescriptorinfo', this.getDescriptorInfo);
this.add('deriveaddresses', this.deriveAddresses);
}

/*
Expand Down Expand Up @@ -2346,10 +2347,90 @@ class RPC extends RPCBase {
return result;
}

async deriveAddresses(args, help) {
if (help || args.length > 2 || args.length === 0)
throw new RPCError(errs.MISC_ERROR, 'deriveaddresses "descriptor"');

Check warning on line 2352 in lib/node/rpc.js

View check run for this annotation

Codecov / codecov/patch

lib/node/rpc.js#L2352

Added line #L2352 was not covered by tests

const valid = new Validator(args);

const desc = parseDescriptor(valid.str(0, ''), this.network, true);

if (desc.isRange() && args.length === 1) {
throw new RPCError(
errs.INVALID_PARAMETER,
'Range must be specified for ranged descriptor'
);
}

if (!desc.isRange() && args.length > 1) {
throw new RPCError(
errs.INVALID_PARAMETER,
'Range should not be specified for un-ranged descriptor'
);
}

let low = 0, high = 0;

try {
high = valid.u32(1, 0);
} catch (e) {
try {
const arr = valid.array(1, []);
low = arr[0];
high = arr[1];
} catch (innerErr) {
throw new RPCError(
errs.INVALID_PARAMETER,
'Range must be specified as end or as [begin,end]'
);
}
}

this.isValidRange(low, high);

const addresses = [];

for (let i = low; i <= high; i++) {
const address = desc.getAddresses(i);

for (let j = 0; j < address.length; j++) {
addresses.push(address[j]);
};
}

return addresses;
}

/*
* Helpers
*/

isValidRange(low, high) {
assert(Number.isInteger(low), 'Range begin must be an integer');
assert(Number.isInteger(high), 'Range end must be an integer');

if (low > high) {
throw new RPCError(
errs.INVALID_PARAMETER,
'Range specified as [begin,end] must not have begin after end'
);
}

if (low < 0) {
throw new RPCError(errs.INVALID_PARAMETER, 'Range should be >= 0');
}

if ((high >> 31) !== 0) {
throw new RPCError(errs.INVALID_PARAMETER, 'End of range is too high');

Check warning on line 2424 in lib/node/rpc.js

View check run for this annotation

Codecov / codecov/patch

lib/node/rpc.js#L2424

Added line #L2424 was not covered by tests
}

if (high >= low + 1000000) {
throw new RPCError(errs.INVALID_PARAMETER, 'Range is too large');

Check warning on line 2428 in lib/node/rpc.js

View check run for this annotation

Codecov / codecov/patch

lib/node/rpc.js#L2428

Added line #L2428 was not covered by tests
}

return {low, high};
}

async handleLongpoll(lpid) {
if (lpid.length !== 72)
throw new RPCError(errs.INVALID_PARAMETER, 'Invalid longpoll ID.');
Expand Down Expand Up @@ -2884,9 +2965,9 @@ function parseAddress(raw, network) {
}
}

function parseDescriptor(raw, network) {
function parseDescriptor(raw, network, requireChecksum = false) {
try {
return parse(raw, network, false);
return parse(raw, network, requireChecksum);
} catch (e) {
throw new RPCError(errs.INVALID_DESCRIPTOR,
`Invalid descriptor: ${e.message}`
Expand Down
70 changes: 70 additions & 0 deletions test/node-rpc-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,76 @@ describe('RPC', function() {
}
});

it('should rpc deriveaddresses', async () => {
const data = [
{
"input": "sh(wsh(sortedmulti(2,[e7dd1c50/48'/1'/40'/1']tpubDFh3VaUEs71ZMcVBmscSSnP4f4r6TvnLssu8yXvpj3uMfAehciMYTrgbfu4KCxXb7oSaz4kriuWRZtQVhZR2oA9toob6aELnsYLN94fXQLF/*,[e7dd1c50/48'/1'/20'/1']tpubDFPemvLnpMqE1BPuturDUh46KxsR8wGSQrA6HofYE7fqxpMAKCcoYWHGA46B6zKY4xcQAc1vLFTcqQ9BvsbHZ4UhzqqF5nUeeNBjNivHxPT/*,[aedb3d12/48'/1'/0'/1']tpubDEbuxto5Kftus28NyPddiEev2yUhzZGpkpQdCK732KBge5FJDhaMdhG1iVw3rMJ2qvABkaLR9HxobkeFkmQZ4RqQgN1KJadDjPn9ANBLo8V/*)))#zlh5y6z5",
"range": "0,2",
"error": "Range must be specified as end or as [begin,end]"
},
{
"input": "sh(wsh(sortedmulti(2,[e7dd1c50/48'/1'/40'/1']tpubDFh3VaUEs71ZMcVBmscSSnP4f4r6TvnLssu8yXvpj3uMfAehciMYTrgbfu4KCxXb7oSaz4kriuWRZtQVhZR2oA9toob6aELnsYLN94fXQLF/*,[e7dd1c50/48'/1'/20'/1']tpubDFPemvLnpMqE1BPuturDUh46KxsR8wGSQrA6HofYE7fqxpMAKCcoYWHGA46B6zKY4xcQAc1vLFTcqQ9BvsbHZ4UhzqqF5nUeeNBjNivHxPT/*,[aedb3d12/48'/1'/0'/1']tpubDEbuxto5Kftus28NyPddiEev2yUhzZGpkpQdCK732KBge5FJDhaMdhG1iVw3rMJ2qvABkaLR9HxobkeFkmQZ4RqQgN1KJadDjPn9ANBLo8V/*)))#zlh5y6z5",
"range": [0, "10"],
"error": "Range end must be an integer"
},
{
"input": "sh(wsh(sortedmulti(2,[e7dd1c50/48'/1'/40'/1']tpubDFh3VaUEs71ZMcVBmscSSnP4f4r6TvnLssu8yXvpj3uMfAehciMYTrgbfu4KCxXb7oSaz4kriuWRZtQVhZR2oA9toob6aELnsYLN94fXQLF/*,[e7dd1c50/48'/1'/20'/1']tpubDFPemvLnpMqE1BPuturDUh46KxsR8wGSQrA6HofYE7fqxpMAKCcoYWHGA46B6zKY4xcQAc1vLFTcqQ9BvsbHZ4UhzqqF5nUeeNBjNivHxPT/*,[aedb3d12/48'/1'/0'/1']tpubDEbuxto5Kftus28NyPddiEev2yUhzZGpkpQdCK732KBge5FJDhaMdhG1iVw3rMJ2qvABkaLR9HxobkeFkmQZ4RqQgN1KJadDjPn9ANBLo8V/*)))#zlh5y6z5",
"range": ["0", 10],
"error": "Range begin must be an integer"
},
{
"input": "sh(wsh(sortedmulti(2,[e7dd1c50/48'/1'/40'/1']tpubDFh3VaUEs71ZMcVBmscSSnP4f4r6TvnLssu8yXvpj3uMfAehciMYTrgbfu4KCxXb7oSaz4kriuWRZtQVhZR2oA9toob6aELnsYLN94fXQLF/*,[e7dd1c50/48'/1'/20'/1']tpubDFPemvLnpMqE1BPuturDUh46KxsR8wGSQrA6HofYE7fqxpMAKCcoYWHGA46B6zKY4xcQAc1vLFTcqQ9BvsbHZ4UhzqqF5nUeeNBjNivHxPT/*,[aedb3d12/48'/1'/0'/1']tpubDEbuxto5Kftus28NyPddiEev2yUhzZGpkpQdCK732KBge5FJDhaMdhG1iVw3rMJ2qvABkaLR9HxobkeFkmQZ4RqQgN1KJadDjPn9ANBLo8V/*)))#zlh5y6z5",
"range": [10, 0],
"error": "Range specified as [begin,end] must not have begin after end"
},
{
"input": "sh(wsh(sortedmulti(2,[e7dd1c50/48'/1'/40'/1']tpubDFh3VaUEs71ZMcVBmscSSnP4f4r6TvnLssu8yXvpj3uMfAehciMYTrgbfu4KCxXb7oSaz4kriuWRZtQVhZR2oA9toob6aELnsYLN94fXQLF/*,[e7dd1c50/48'/1'/20'/1']tpubDFPemvLnpMqE1BPuturDUh46KxsR8wGSQrA6HofYE7fqxpMAKCcoYWHGA46B6zKY4xcQAc1vLFTcqQ9BvsbHZ4UhzqqF5nUeeNBjNivHxPT/*,[aedb3d12/48'/1'/0'/1']tpubDEbuxto5Kftus28NyPddiEev2yUhzZGpkpQdCK732KBge5FJDhaMdhG1iVw3rMJ2qvABkaLR9HxobkeFkmQZ4RqQgN1KJadDjPn9ANBLo8V/*)))#zlh5y6z5",
"range": [-1, 2],
"error": "Range should be >= 0"
},
{
"input": "sh(wsh(sortedmulti(2,[e7dd1c50/48'/1'/40'/1']tpubDFh3VaUEs71ZMcVBmscSSnP4f4r6TvnLssu8yXvpj3uMfAehciMYTrgbfu4KCxXb7oSaz4kriuWRZtQVhZR2oA9toob6aELnsYLN94fXQLF/*,[e7dd1c50/48'/1'/20'/1']tpubDFPemvLnpMqE1BPuturDUh46KxsR8wGSQrA6HofYE7fqxpMAKCcoYWHGA46B6zKY4xcQAc1vLFTcqQ9BvsbHZ4UhzqqF5nUeeNBjNivHxPT/*,[aedb3d12/48'/1'/0'/1']tpubDEbuxto5Kftus28NyPddiEev2yUhzZGpkpQdCK732KBge5FJDhaMdhG1iVw3rMJ2qvABkaLR9HxobkeFkmQZ4RqQgN1KJadDjPn9ANBLo8V/*)))#zlh5y6z5",
"error": "Range must be specified for ranged descriptor"
},
{
"input": "sh(wsh(sortedmulti(2,[e7dd1c50/48'/1'/40'/1']tpubDFh3VaUEs71ZMcVBmscSSnP4f4r6TvnLssu8yXvpj3uMfAehciMYTrgbfu4KCxXb7oSaz4kriuWRZtQVhZR2oA9toob6aELnsYLN94fXQLF/*,[e7dd1c50/48'/1'/20'/1']tpubDFPemvLnpMqE1BPuturDUh46KxsR8wGSQrA6HofYE7fqxpMAKCcoYWHGA46B6zKY4xcQAc1vLFTcqQ9BvsbHZ4UhzqqF5nUeeNBjNivHxPT/*,[aedb3d12/48'/1'/0'/1']tpubDEbuxto5Kftus28NyPddiEev2yUhzZGpkpQdCK732KBge5FJDhaMdhG1iVw3rMJ2qvABkaLR9HxobkeFkmQZ4RqQgN1KJadDjPn9ANBLo8V/*)))#zlh5y6z5",
"range": 10,
"addresses": [
"2MtWBjxiAi4xYNUdtDe2sHkNw5kdAQqZZNb",
"2MstQfXgUwTR66bhMrbNU3qDqT6RT4hGHnJ",
"2N6QXTyf64KHWddFZ5swjaRmEwk4hEawYuo",
"2NCfXGhiA6EjK6o2JSejtzeP5fYkNxP1TQC",
"2NCU5HcmKUap923abUPEhGNnTFUf3K2hAYr",
"2N6K6jKKeuejPTeiPDbnq1qZsqGhigoApzK",
"2N4jy9MPJee7WvH3tfRVE3LeYQxVNhjt2yH",
"2NCXTtRLCjwWPeUoRz5qGoKSj84Ci4pbcWy",
"2NEwEq98wFu1EcSf5jFexCRZEuGjjeTo265",
"2MzcruaPLyHniY1qjJLXLXEuYRd5PJ1o1EW",
"2MuY8izcxH5KJfhvc432HQVos483krppmrf"
]
},
{
"input": "pkh(04a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd5b8dec5235a0fa8722476c7709c02559e3aa73aa03918ba2d492eea75abea235)#9907vvwz",
"range": [0, 5],
"error": "Range should not be specified for un-ranged descriptor"
},
{
"input":"pkh([d34db33f/44h/0h/0h]tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1h/1h/*h)#u5f4r0y7",
"range": [1, 5],
"error": "Cannot derive script without private keys"
}
];

for (const test of data) {
try {
const result = test.range ? await nclient.execute("deriveaddresses", [test.input, test.range]) : await nclient.execute("deriveaddresses", [test.input]);
assert.deepStrictEqual(result, test.addresses);
} catch (e) {
assert.strictEqual(e.message, test.error);
}
}
});

it('should rpc getblockhash', async () => {
const info = await nclient.execute('getblockhash', [node.chain.tip.height]);
assert.strictEqual(util.revHex(node.chain.tip.hash), info);
Expand Down

0 comments on commit e69cb9f

Please sign in to comment.