From e506070702520de621a7a9b31f1b1b4ab011037d Mon Sep 17 00:00:00 2001 From: Dan Janosik Date: Wed, 18 May 2022 00:12:22 -0400 Subject: [PATCH] Cleanup related to merge of PR #436 -move /api/xyzpub/:xyzpub to /api/xyzpub/txids/:xyzpub -move /api/util/xyzpub/:xyzpub -> /api/xyzpub/:xyzpub -rename xyzpubAddressApi -> xyzpubApi and move more functionality from apiRouter there -searchXpubTxids: return more details about the search, then in the apiRouter just use what's needed -fix in searchXpubTxids: reset gapCount value when tx is found -update api docs -update changelogs --- CHANGELOG-API.md | 3 + app/api/xyzpubAddressApi.js | 108 -------------- app/api/xyzpubApi.js | 232 ++++++++++++++++++++++++++++++ docs/api.js | 69 ++++++--- routes/apiRouter.js | 277 ++++++++++++++++-------------------- 5 files changed, 406 insertions(+), 283 deletions(-) delete mode 100644 app/api/xyzpubAddressApi.js create mode 100644 app/api/xyzpubApi.js diff --git a/CHANGELOG-API.md b/CHANGELOG-API.md index 10c35d74b..369e131b3 100644 --- a/CHANGELOG-API.md +++ b/CHANGELOG-API.md @@ -3,10 +3,13 @@ This changelog specifically tracks changes to the Public API available at `/api` ##### v1.2.0 ###### Unreleased +* Added /api/xyzpub/txids/:xyzpub +* Added /api/xyzpub/addresses/:xyzpub * /api/tx/:txid * Added result.vin[i].scriptSig.address * Added result.vin[i].scriptSig.type * Added result.fee, including result.fee.amount and result.fee.unit +* Changed path: /api/util/xyzpub/:xyzpub -> /api/xyzpub/:xyzpub (auto-redirect included) diff --git a/app/api/xyzpubAddressApi.js b/app/api/xyzpubAddressApi.js deleted file mode 100644 index ea30f7356..000000000 --- a/app/api/xyzpubAddressApi.js +++ /dev/null @@ -1,108 +0,0 @@ -"use strict"; - -const config = require("./../config.js"); -const coins = require("../coins.js"); -const utils = require("../utils.js"); - -const coinConfig = coins[config.coin]; - -const coreApi = require("./coreApi.js"); -const addressApi = require("./addressApi.js"); - -// 0 is receive -// 1 is change -function generateAddressesFromXyzPub(extendedPubkey, receiveOrChange, limit, offset){ - if(limit == undefined) limit = 20; - if(offset == undefined) offset = 0; - - if(receiveOrChange == undefined) receiveOrChange = 0; - - let addresses = []; - - const xpub_tpub = global.activeBlockchain == "main" ? "xpub" : "tpub"; - const ypub_upub = global.activeBlockchain == "main" ? "ypub" : "upub"; - const zpub_vpub = global.activeBlockchain == "main" ? "zpub" : "vpub"; - - // if xpub/ypub/zpub convert to address under path m/0/0 - if (extendedPubkey.match(/^(xpub|tpub).*$/)) { - let xpub = extendedPubkey; - if (!extendedPubkey.startsWith(xpub_tpub)) { - xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); - } - addresses = utils.bip32Addresses(xpub, "p2pkh", receiveOrChange, limit, offset); - } - else if (extendedPubkey.match(/^(ypub|upub).*$/)) { - let xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); - addresses = utils.bip32Addresses(xpub, "p2sh(p2wpkh)", receiveOrChange, limit, offset); - } - else if (extendedPubkey.match(/^(zpub|vpub).*$/)) { - let xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); - addresses = utils.bip32Addresses(xpub, "p2wpkh", receiveOrChange, limit, offset); - } - - return addresses; -} - -function getXyzPubDetails(extendedPubkey, limit){ - return new Promise(async function(resolve, reject) { - // limit == -1 means we get every address with a transaction and 20 addresses gap at the end. - if(limit == undefined) limit = -1; - var sort = "desc"; - const gapLimit = 20; // as per bip32 - - var txLimit = 20; - var txOffset = 0; - var addressCount = 0; - var gapCount = {"0": 0, "1": 0} - var outJson = {"txids": []}; - while((addressCount < limit) || limit == -1){ - for(var receiveOrChange = 0; receiveOrChange <= 1; receiveOrChange++){ - if(gapCount[receiveOrChange] < gapLimit){ - txOffset = 0; - var address = generateAddressesFromXyzPub(extendedPubkey, receiveOrChange, 1, addressCount); - var result = await coreApi.getAddress(address[0]); - if(result){ - var moreTx = true; - while(moreTx){ - var detailsResult = await addressApi.getAddressDetails(result.address, result.scriptPubKey, sort, txLimit, txOffset); - if(detailsResult && detailsResult.addressDetails && detailsResult.addressDetails.txids ) { - if(detailsResult.addressDetails.txids.length == 0){ - gapCount[receiveOrChange]++; - moreTx = false; - } - else { - outJson.txids = outJson.txids.concat(detailsResult.addressDetails.txids); - if(detailsResult.addressDetails.txids.length < txLimit) moreTx = false; - } - } - txOffset += txLimit; - } - } - } - } - addressCount++; - - // gap of 20 receive and change addresses found - if (gapCount[0] >= gapLimit && gapCount[1] >= gapLimit) break; - } - - // remove duplicate txid eg. receive and change address that have same txid - let uniqueTxids = []; - outJson.txids.forEach((txid) => { - if (!uniqueTxids.includes(txid)) { - uniqueTxids.push(txid); - } - }); - outJson.txids = uniqueTxids; - outJson.txCount = uniqueTxids.length; - resolve(outJson); - - }).catch(function(err) { - reject(err); - }); -} - - -module.exports = { - getXyzPubDetails: getXyzPubDetails -}; \ No newline at end of file diff --git a/app/api/xyzpubApi.js b/app/api/xyzpubApi.js new file mode 100644 index 000000000..651ea1597 --- /dev/null +++ b/app/api/xyzpubApi.js @@ -0,0 +1,232 @@ +"use strict"; + +const config = require("./../config.js"); +const coins = require("../coins.js"); +const utils = require("../utils.js"); + +const coinConfig = coins[config.coin]; + +const coreApi = require("./coreApi.js"); +const addressApi = require("./addressApi.js"); + + + +function getKeyDetails(extendedPubkey) { + let keyDetails = { + keyType: extendedPubkey.substring(0, 4), + relatedKeys: [] + }; + + // if xpub/ypub/zpub convert to address under path m/0/0 + if (extendedPubkey.match(/^(xpub|tpub).*$/)) { + keyDetails.outputType = "P2PKH"; + keyDetails.outputTypeDesc = "Pay to Public Key Hash"; + keyDetails.bip32Path = "m/44'/0'"; + + const xpub_tpub = global.activeBlockchain == "main" ? "xpub" : "tpub"; + const ypub_upub = global.activeBlockchain == "main" ? "ypub" : "upub"; + const zpub_vpub = global.activeBlockchain == "main" ? "zpub" : "vpub"; + + let xpub = extendedPubkey; + if (!extendedPubkey.startsWith(xpub_tpub)) { + xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); + } + + if (!extendedPubkey.startsWith(xpub_tpub)) { + keyDetails.relatedKeys.push({ + keyType: xpub_tpub, + key: utils.xpubChangeVersionBytes(xpub, xpub_tpub), + outputType: "P2PKH", + firstAddress: utils.bip32Addresses(xpub, "p2pkh", 0, 1, 0)[0] + }); + } + + keyDetails.relatedKeys.push({ + keyType: ypub_upub, + key: utils.xpubChangeVersionBytes(xpub, ypub_upub), + outputType: "P2WPKH in P2SH", + firstAddress: utils.bip32Addresses(xpub, "p2sh(p2wpkh)", 0, 1, 0)[0] + }); + + keyDetails.relatedKeys.push({ + keyType: zpub_vpub, + key: utils.xpubChangeVersionBytes(xpub, zpub_vpub), + outputType: "P2WPKH", + firstAddress: utils.bip32Addresses(xpub, "p2wpkh", 0, 1, 0)[0] + }); + + } else if (extendedPubkey.match(/^(ypub|upub).*$/)) { + keyDetails.outputType = "P2WPKH in P2SH"; + keyDetails.outputTypeDesc = "Pay to Witness Public Key Hash (P2WPKH) wrapped inside Pay to Script Hash (P2SH), aka Wrapped Segwit"; + keyDetails.bip32Path = "m/49'/0'"; + + const xpub_tpub = global.activeBlockchain == "main" ? "xpub" : "tpub"; + const zpub_vpub = global.activeBlockchain == "main" ? "zpub" : "vpub"; + + const xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); + + keyDetails.relatedKeys.push({ + keyType: xpub_tpub, + key: xpub, + outputType: "P2PKH", + firstAddress: utils.bip32Addresses(xpub, "p2pkh", 0, 1, 0)[0] + }); + + keyDetails.relatedKeys.push({ + keyType: zpub_vpub, + key: utils.xpubChangeVersionBytes(xpub, zpub_vpub), + outputType: "P2WPKH", + firstAddress: utils.bip32Addresses(xpub, "p2wpkh", 0, 1, 0)[0] + }); + + } else if (extendedPubkey.match(/^(zpub|vpub).*$/)) { + keyDetails.outputType = "P2WPKH"; + keyDetails.outputTypeDesc = "Pay to Witness Public Key Hash, aka Native Segwit"; + keyDetails.bip32Path = "m/84'/0'"; + + const xpub_tpub = global.activeBlockchain == "main" ? "xpub" : "tpub"; + const ypub_upub = global.activeBlockchain == "main" ? "ypub" : "upub"; + + const xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); + + keyDetails.relatedKeys.push({ + keyType: xpub_tpub, + key: xpub, + outputType: "P2PKH", + firstAddress: utils.bip32Addresses(xpub, "p2pkh", 0, 1, 0)[0] + }); + + keyDetails.relatedKeys.push({ + keyType: ypub_upub, + key: utils.xpubChangeVersionBytes(xpub, ypub_upub), + outputType: "P2WPKH in P2SH", + firstAddress: utils.bip32Addresses(xpub, "p2sh(p2wpkh)", 0, 1, 0)[0] + }); + + } else if (extendedPubkey.startsWith("Ypub")) { + keyDetails.outputType = "Multi-Sig P2WSH in P2SH"; + keyDetails.bip32Path = "-"; + + } else if (extendedPubkey.startsWith("Zpub")) { + keyDetails.outputType = "Multi-Sig P2WSH"; + keyDetails.bip32Path = "-"; + } + + return keyDetails; +} + + +// 0 is receive +// 1 is change +function getXpubAddresses(extendedPubkey, receiveOrChange=0, limit=20, offset=0) { + const xpub_tpub = global.activeBlockchain == "main" ? "xpub" : "tpub"; + const ypub_upub = global.activeBlockchain == "main" ? "ypub" : "upub"; + const zpub_vpub = global.activeBlockchain == "main" ? "zpub" : "vpub"; + + // if xpub/ypub/zpub convert to address under path m/0/0 + if (extendedPubkey.match(/^(xpub|tpub).*$/)) { + let xpub = extendedPubkey; + + if (!extendedPubkey.startsWith(xpub_tpub)) { + xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); + } + + return utils.bip32Addresses(xpub, "p2pkh", receiveOrChange, limit, offset); + + } else if (extendedPubkey.match(/^(ypub|upub).*$/)) { + let xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); + + return utils.bip32Addresses(xpub, "p2sh(p2wpkh)", receiveOrChange, limit, offset); + + } else if (extendedPubkey.match(/^(zpub|vpub).*$/)) { + let xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); + + return utils.bip32Addresses(xpub, "p2wpkh", receiveOrChange, limit, offset); + } + + return []; +} + + +// gapLimit=20, default as per bip32 +async function searchXpubTxids(extendedPubkey, gapLimit=20, addressLimit=-1) { + // addressLimit == -1 means we get every address with a transaction and 20 addresses gap at the end. + let sort = "desc"; + + let txLimit = 20; + let txOffset = 0; + let addressCount = 0; + let gapCounts = {"0": 0, "1": 0}; + let result = { + usedAddresses: [], + emptyAddresses: { + receive: [], + change: [] + } + }; + + while ((addressCount < addressLimit) || addressLimit == -1) { + for (let receiveOrChange = 0; receiveOrChange <= 1; receiveOrChange++) { + if (gapCounts[receiveOrChange] < gapLimit) { + txOffset = 0; + + let address = getXpubAddresses(extendedPubkey, receiveOrChange, 1, addressCount)[0]; + let getAddressResult = await coreApi.getAddress(address); + + if (getAddressResult) { + let moreTx = true; + + while (moreTx) { + let detailsResult = await addressApi.getAddressDetails(getAddressResult.address, getAddressResult.scriptPubKey, sort, txLimit, txOffset); + + if (detailsResult && detailsResult.addressDetails && detailsResult.addressDetails.txids) { + if (detailsResult.addressDetails.txids.length == 0) { + result.emptyAddresses[receiveOrChange == 0 ? "receive" : "change"].push(address); + + gapCounts[receiveOrChange]++; + moreTx = false; + + } else { + gapCounts[receiveOrChange] = 0; + + result.usedAddresses.push({ + addressIndex: addressCount, + address: address, + type: receiveOrChange == 0 ? "receive" : "change", + txids: detailsResult.addressDetails.txids, + priorGap: gapCounts[receiveOrChange] + }); + + if (detailsResult.addressDetails.txids.length < txLimit) { + moreTx = false; + } + } + } else { + result.emptyAddresses[receiveOrChange == 0 ? "receive" : "change"].push(address); + } + + txOffset += txLimit; + } + } + } + } + + addressCount++; + + // gap of N (20) receive and change addresses found + if (gapCounts[0] >= gapLimit && gapCounts[1] >= gapLimit) { + break; + } + } + + + return result; +} + + +module.exports = { + getKeyDetails: getKeyDetails, + getXpubAddresses: getXpubAddresses, + searchXpubTxids: searchXpubTxids +}; + diff --git a/docs/api.js b/docs/api.js index 479a21411..61e25d33e 100644 --- a/docs/api.js +++ b/docs/api.js @@ -1,5 +1,6 @@ module.exports = { - "version": "1.1.0", + "version": "1.2.0", + "routes":[ // blocks { @@ -67,10 +68,14 @@ module.exports = { // addresses { - "category":"address", + "category":"addresses", "url":"/api/address/:address", "desc":"Returns a summary of data pertaining to the given address. The output of this call will depend heavily on the configured 'Address API' (see .env-sample file).", - "optionalParams": {"limit":"Number of transactions to return", "offset":"Offset into transactions", "sort":"Sorting direction for transactions ('desc'=new first, 'asc'=old first)"}, + "optionalParams": { + "limit":"Number of transactions to return", + "offset":"Offset into transactions", + "sort":"Sorting direction for transactions ('desc'=new first, 'asc'=old first)" + }, "returnType":"json", "testUrl":"/api/address/34rng4QwB5pHUbGDJw1JxjLwgEU8TQuEqv" }, @@ -78,6 +83,46 @@ module.exports = { + + // xyz-pubs + { + "category":"xpubs", + "url":"/api/xyzpub/:extendedPubkey", + "desc":"Returns details for the specified extended public key, including related keys and addresses.", + "returnType":"json", + "optionalParams": { + "limit":"The number of addresses to return", + "offset":"Offset into the list of addresses" + }, + "testUrl": "/api/util/xyzpub/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd" + }, + { + "category":"xpubs", + "url":"/api/xyzpub/addresses/:xyzpub", + "desc":"Returns a list of addresses derived from the given [xyz]pub.", + "optionalParams": { + "receiveOrChange":"0 for 'receive' addresses (default); 1 for 'change' addresses", + "limit":"Number of addresses to return", + "offset":"Offset into addresses" + }, + "returnType":"json", + "testUrl":"/api/xyzpub/addresses/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd" + }, + { + "category":"xpubs", + "url":"/api/xyzpub/txids/:xyzpub", + "desc":"Returns a list of transaction IDs associated with the given [xyz]pub.", + "optionalParams": { + "gapLimit":"Limit of empty addresses to end searching for transactions (default: 20)", + "addressLimit":"Forced limit on the number of addresses to search through (both 'receive' and 'change' addresses up to this number will be searched)" + }, + "returnType":"json", + "testUrl":"/api/xyzpub/txids/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd" + }, + + + + // mining { "category":"mining", @@ -120,7 +165,10 @@ module.exports = { "url":"/api/mining/miner-summary", "desc":"Returns whether the specified transaction ID is included in the estimated next block to be mined (produced via getblocktemplate).", "returnType":"json", - "optionalParams": {"since":"Use the form 'Nd' to specify the number of day to look back (e.g. 'since=7d' will analyze the last 7 days)", "startHeight+endHeight":"Use these 2 parameters to specify a custom start/end height (e.g. 'startHeight=0&endHeight=24' to analyze the first 25 blocks)"}, + "optionalParams": { + "since":"Use the form 'Nd' to specify the number of day to look back (e.g. 'since=7d' will analyze the last 7 days)", + "startHeight+endHeight":"Use these 2 parameters to specify a custom start/end height (e.g. 'startHeight=0&endHeight=24' to analyze the first 25 blocks)" + }, "testUrl":"/api/mining/miner-summary?since=1d" }, @@ -146,19 +194,6 @@ module.exports = { - - // util - { - "category":"util", - "url":"/api/util/xyzpub/:extendedPubkey", - "desc":"Returns details for the specified extended public key, including related keys and addresses.", - "returnType":"json", - "optionalParams": {"limit":"The number of addresses to return", "offset":"Offset into the list of addresses"}, - "testUrl": "/api/util/xyzpub/xpub6EuV33a2DXxAhoJTRTnr8qnysu81AA4YHpLY6o8NiGkEJ8KADJ35T64eJsStWsmRf1xXkEANVjXFXnaUKbRtFwuSPCLfDdZwYNZToh4LBCd" - }, - - - // price { "category":"price", diff --git a/routes/apiRouter.js b/routes/apiRouter.js index 6c9fe9673..dcd56b2c2 100644 --- a/routes/apiRouter.js +++ b/routes/apiRouter.js @@ -22,7 +22,7 @@ const coins = require("./../app/coins.js"); const config = require("./../app/config.js"); const coreApi = require("./../app/api/coreApi.js"); const addressApi = require("./../app/api/addressApi.js"); -const xyzpubAddressApi = require("./../app/api/xyzpubAddressApi.js"); +const xyzpubApi = require("./../app/api/xyzpubApi.js"); const rpcApi = require("./../app/api/rpcApi.js"); const apiDocs = require("./../docs/api.js"); const btcQuotes = require("./../app/coins/btcQuotes.js"); @@ -359,25 +359,139 @@ router.get("/address/:address", asyncHandler(async (req, res, next) => { })); + + + +/// XYZ PUBS + +// redirect for an old path +router.get("/util/xyzpub/:extendedPubkey", asyncHandler(async (req, res, next) => { + const extendedPubkey = req.params.extendedPubkey; + + res.redirect(`${req.baseUrl}/xyzpub/${extendedPubkey}`); +})); + router.get("/xyzpub/:extendedPubkey", asyncHandler(async (req, res, next) => { try { const extendedPubkey = req.params.extendedPubkey; + res.locals.extendedPubkey = extendedPubkey; + + + let limit = 20; + if (req.query.limit) { + limit = parseInt(req.query.limit); + } + + let offset = 0; + if (req.query.offset) { + offset = parseInt(req.query.offset); + } + + + let relatedKeys = []; + + let outputType = "Unknown"; + let outputTypeDesc = null; + let bip32Path = "Unknown"; + + + const keyDetails = xyzpubApi.getKeyDetails(extendedPubkey); + keyDetails.receiveAddresses = xyzpubApi.getXpubAddresses(extendedPubkey, 0, limit, offset); + keyDetails.changeAddresses = xyzpubApi.getXpubAddresses(extendedPubkey, 1, limit, offset); + + + res.json(keyDetails); + + next(); + + } catch (err) { + res.locals.pageErrors.push(utils.logError("0923tygdusde", err)); + + next(); + } +})); + +router.get("/xyzpub/txids/:extendedPubkey", asyncHandler(async (req, res, next) => { + try { + const extendedPubkey = req.params.extendedPubkey; + + let gapLimit = 20; + if (req.query.gapLimit) { + gapLimit = parseInt(req.query.gapLimit); + } + let limit = -1; if (req.query.limit) { limit = parseInt(req.query.limit); } - const xyzpubResult = await xyzpubAddressApi.getXyzPubDetails(extendedPubkey, limit); + const searchResult = await xyzpubApi.searchXpubTxids(extendedPubkey, gapLimit, limit); + + let result = { + txids: [], + txCount: 0 + }; + + searchResult.usedAddresses.forEach(addrResult => { + addrResult.txids.forEach(txid => { + if (!result.txids.includes(txid)) { + result.txids.push(txid); + result.txCount++; + } + }); + }) - if(xyzpubResult){ - res.json(xyzpubResult); + if (searchResult) { + res.json(result); + + } else { + res.json({success:false}); + } + + next(); + + } catch (e) { + utils.logError("382rdere", e); + + res.json({success:false, error: e.toString()}); + + next(); + } +})); + +router.get("/xyzpub/addresses/:extendedPubkey", asyncHandler(async (req, res, next) => { + try { + const extendedPubkey = req.params.extendedPubkey; + + let receiveOrChange = 0; + if (req.query.receiveOrChange) { + receiveOrChange = parseInt(req.query.receiveOrChange); + } + + let limit = 10; + if (req.query.limit) { + limit = parseInt(req.query.limit); } - else { + + let offset = 0; + if (req.query.offset) { + offset = parseInt(req.query.offset); + } + + const xyzpubResult = await xyzpubApi.getXpubAddresses(extendedPubkey, receiveOrChange, limit, offset); + + if (xyzpubResult){ + res.json(xyzpubResult); + + } else { res.json({success:false}); } next(); + } catch (e) { + utils.logError("3297rwegee", e); + res.json({success:false, error: e.toString()}); next(); @@ -670,159 +784,6 @@ router.get("/mempool/fees", function(req, res, next) { -/// UTIL - -router.get("/util/xyzpub/:extendedPubkey", asyncHandler(async (req, res, next) => { - try { - const extendedPubkey = req.params.extendedPubkey; - res.locals.extendedPubkey = extendedPubkey; - - - let limit = 20; - if (req.query.limit) { - limit = parseInt(req.query.limit); - } - - let offset = 0; - if (req.query.offset) { - offset = parseInt(req.query.offset); - } - - - let receiveAddresses = []; - let changeAddresses = []; - let relatedKeys = []; - - let outputType = "Unknown"; - let outputTypeDesc = null; - let bip32Path = "Unknown"; - - // if xpub/ypub/zpub convert to address under path m/0/0 - if (extendedPubkey.match(/^(xpub|tpub).*$/)) { - outputType = "P2PKH"; - outputTypeDesc = "Pay to Public Key Hash"; - bip32Path = "m/44'/0'"; - - const xpub_tpub = global.activeBlockchain == "main" ? "xpub" : "tpub"; - const ypub_upub = global.activeBlockchain == "main" ? "ypub" : "upub"; - const zpub_vpub = global.activeBlockchain == "main" ? "zpub" : "vpub"; - - let xpub = extendedPubkey; - if (!extendedPubkey.startsWith(xpub_tpub)) { - xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); - } - - receiveAddresses = utils.bip32Addresses(extendedPubkey, "p2pkh", 0, limit, offset); - changeAddresses = utils.bip32Addresses(extendedPubkey, "p2pkh", 1, limit, offset); - - if (!extendedPubkey.startsWith(xpub_tpub)) { - relatedKeys.push({ - keyType: xpub_tpub, - key: utils.xpubChangeVersionBytes(xpub, xpub_tpub), - outputType: "P2PKH", - firstAddress: utils.bip32Addresses(xpub, "p2pkh", 0, 1, 0)[0] - }); - } - - relatedKeys.push({ - keyType: ypub_upub, - key: utils.xpubChangeVersionBytes(xpub, ypub_upub), - outputType: "P2WPKH in P2SH", - firstAddress: utils.bip32Addresses(xpub, "p2sh(p2wpkh)", 0, 1, 0)[0] - }); - - relatedKeys.push({ - keyType: zpub_vpub, - key: utils.xpubChangeVersionBytes(xpub, zpub_vpub), - outputType: "P2WPKH", - firstAddress: utils.bip32Addresses(xpub, "p2wpkh", 0, 1, 0)[0] - }); - - } else if (extendedPubkey.match(/^(ypub|upub).*$/)) { - outputType = "P2WPKH in P2SH"; - outputTypeDesc = "Pay to Witness Public Key Hash (P2WPKH) wrapped inside Pay to Script Hash (P2SH), aka Wrapped Segwit"; - bip32Path = "m/49'/0'"; - - const xpub_tpub = global.activeBlockchain == "main" ? "xpub" : "tpub"; - const zpub_vpub = global.activeBlockchain == "main" ? "zpub" : "vpub"; - - const xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); - - receiveAddresses = utils.bip32Addresses(xpub, "p2sh(p2wpkh)", 0, limit, offset); - changeAddresses = utils.bip32Addresses(xpub, "p2sh(p2wpkh)", 1, limit, offset); - - relatedKeys.push({ - keyType: xpub_tpub, - key: xpub, - outputType: "P2PKH", - firstAddress: utils.bip32Addresses(xpub, "p2pkh", 0, 1, 0)[0] - }); - - relatedKeys.push({ - keyType: zpub_vpub, - key: utils.xpubChangeVersionBytes(xpub, zpub_vpub), - outputType: "P2WPKH", - firstAddress: utils.bip32Addresses(xpub, "p2wpkh", 0, 1, 0)[0] - }); - - } else if (extendedPubkey.match(/^(zpub|vpub).*$/)) { - outputType = "P2WPKH"; - outputTypeDesc = "Pay to Witness Public Key Hash, aka Native Segwit"; - bip32Path = "m/84'/0'"; - - const xpub_tpub = global.activeBlockchain == "main" ? "xpub" : "tpub"; - const ypub_upub = global.activeBlockchain == "main" ? "ypub" : "upub"; - - const xpub = utils.xpubChangeVersionBytes(extendedPubkey, xpub_tpub); - - receiveAddresses = utils.bip32Addresses(xpub, "p2wpkh", 0, limit, offset); - changeAddresses = utils.bip32Addresses(xpub, "p2wpkh", 1, limit, offset); - - relatedKeys.push({ - keyType: xpub_tpub, - key: xpub, - outputType: "P2PKH", - firstAddress: utils.bip32Addresses(xpub, "p2pkh", 0, 1, 0)[0] - }); - - relatedKeys.push({ - keyType: ypub_upub, - key: utils.xpubChangeVersionBytes(xpub, ypub_upub), - outputType: "P2WPKH in P2SH", - firstAddress: utils.bip32Addresses(xpub, "p2sh(p2wpkh)", 0, 1, 0)[0] - }); - - } else if (extendedPubkey.startsWith("Ypub")) { - outputType = "Multi-Sig P2WSH in P2SH"; - bip32Path = "-"; - - } else if (extendedPubkey.startsWith("Zpub")) { - outputType = "Multi-Sig P2WSH"; - bip32Path = "-"; - } - - - res.json({ - keyType: extendedPubkey.substring(0, 4), - outputType: outputType, - outputTypeDesc: outputTypeDesc, - bip32Path: bip32Path, - relatedKeys: relatedKeys, - receiveAddresses: receiveAddresses, - changeAddresses: changeAddresses - }); - - next(); - - } catch (err) { - res.locals.pageErrors.push(utils.logError("0923tygdusde", err)); - - next(); - } -})); - - - /// PRICE