Skip to content

Commit

Permalink
Merge branch 'master' into spending_tx
Browse files Browse the repository at this point in the history
  • Loading branch information
Pantamis authored Oct 2, 2021
2 parents bbd4ece + cdcc559 commit c0a2f28
Show file tree
Hide file tree
Showing 35 changed files with 2,451 additions and 1,914 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG-API.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
This changelog specifically tracks changes to the Public API available at `/api` and is maintained separately from the app CHANGELOG such that it can properly adhere to semantic versioning.

#### v1.1.0
###### Unreleased

* Added: /api/mining/next-block
* Added: /api/mining/next-block/txids
* Added: /api/mining/next-block/includes/:txid



#### v1.0.0
###### 2021-08-10

Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
##### Unreleased

* Fix for difficulty adjustment estimate
* New API actions:
* [/api/mining/next-block](./api/mining/next-block)
* [/api/mining/next-block/txids](./api/mining/next-block/txids)
* [/api/mining/next-block/includes/:txid](./api/mining/next-block/includes/yourTxid)
* Updated miners, including identification of "Patoshi"-pattern blocks
* Performance improvements
* Fix for performance degradation over time due to slow "estimatedSupply" function
* Homepage speedup by making "Estimated Next Block" data load asynchonously
* Error handling improvements
* Fix for `/api/quotes/all`
* Performance log admin page at [/admin/perf-log](./admin/perf-log)


##### v3.2.0
###### 2021-08-10

Expand Down
47 changes: 31 additions & 16 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ debug.enable(debugDefaultCategories);
const debugLog = debug("btcexp:app");
const debugErrorLog = debug("btcexp:error");
const debugPerfLog = debug("btcexp:actionPerformace");
const debugAccessLog = debug("btcexp:access");

const configPaths = [
path.join(os.homedir(), ".config", "btc-rpc-explorer.env"),
Expand Down Expand Up @@ -83,7 +84,7 @@ const momentDurationFormat = require("moment-duration-format");
const coreApi = require("./app/api/coreApi.js");
const rpcApi = require("./app/api/rpcApi.js");
const coins = require("./app/coins.js");
const request = require("request");
const axios = require("axios");
const qrcode = require("qrcode");
const addressApi = require("./app/api/addressApi.js");
const electrumAddressApi = require("./app/api/electrumAddressApi.js");
Expand All @@ -93,7 +94,6 @@ const auth = require('./app/auth.js');
const sso = require('./app/sso.js');
const markdown = require("markdown-it")();
const v8 = require("v8");
const axios = require("axios");
var compression = require("compression");

require("./app/currencies.js");
Expand Down Expand Up @@ -247,24 +247,21 @@ function loadMiningPoolConfigs() {
});
}

function getSourcecodeProjectMetadata() {
async function getSourcecodeProjectMetadata() {
var options = {
url: "https://api.github.com/repos/janoside/btc-rpc-explorer",
headers: {
'User-Agent': 'request'
}
};
try {
const response = await axios(options);

request(options, function(error, response, body) {
if (error == null && response && response.statusCode && response.statusCode == 200) {
var responseBody = JSON.parse(body);

global.sourcecodeProjectMetadata = responseBody;
global.sourcecodeProjectMetadata = response.data;

} else {
utils.logError("3208fh3ew7eghfg", {error:error, response:response, body:body});
} catch (err) {
utils.logError("3208fh3ew7eghfg", err);
}
});
}

function loadChangelog() {
Expand Down Expand Up @@ -845,8 +842,15 @@ if (expressApp.get("env") === "local") {

expressApp.use(function(req, res, next) {
var time = Date.now() - req.startTime;
var userAgent = req.headers['user-agent'];
var crawler = utils.getCrawlerFromUserAgentString(userAgent);

debugPerfLog("Finished action '%s' in %d ms", req.path, time);
if (crawler) {
debugAccessLog(`Finished action '${req.path}' (${res.statusCode}) in ${time}ms for crawler '${crawler}' / '${userAgent}'`);

} else {
debugAccessLog(`Finished action '${req.path}' (${res.statusCode}) in ${time}ms for UA '${userAgent}'`);
}

if (!res.headersSent) {
next();
Expand All @@ -863,11 +867,22 @@ expressApp.use(function(req, res, next) {

/// error handlers

const sharedErrorHandler = (err) => {
const sharedErrorHandler = (req, err) => {
if (err && err.message && err.message.includes("Not Found")) {
const path = err.toString().substring(err.toString().lastIndexOf(" ") + 1);
const userAgent = req.headers['user-agent'];
const crawler = utils.getCrawlerFromUserAgentString(userAgent);
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress;

const attributes = { path:path };

if (crawler) {
attributes.crawler = crawler;
}

debugErrorLog(`404 NotFound: path=${path}, ip=${ip}, userAgent=${userAgent} (crawler=${(crawler != null)}${crawler ? crawler : ""})`);

utils.logError(`NotFound`, err, {path: path});
utils.logError(`NotFound`, err, attributes, false);

} else {
utils.logError("ExpressUncaughtError", err);
Expand All @@ -879,7 +894,7 @@ const sharedErrorHandler = (err) => {
if (expressApp.get("env") === "development" || expressApp.get("env") === "local") {
expressApp.use(function(err, req, res, next) {
if (err) {
sharedErrorHandler(err);
sharedErrorHandler(req, err);
}

res.status(err.status || 500);
Expand All @@ -894,7 +909,7 @@ if (expressApp.get("env") === "development" || expressApp.get("env") === "local"
// no stacktraces leaked to user
expressApp.use(function(err, req, res, next) {
if (err) {
sharedErrorHandler(err);
sharedErrorHandler(req, err);
}

res.status(err.status || 500);
Expand Down
122 changes: 54 additions & 68 deletions app/api/blockchainAddressApi.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"use strict";

const request = require("request");
const axios = require("axios");
const utils = require("./../utils.js");


function getAddressDetails(address, scriptPubkey, sort, limit, offset) {
return new Promise(function(resolve, reject) {
return new Promise(async (resolve, reject) => {
if (address.startsWith("bc1")) {
reject({userText:"blockchain.com API does not support bc1 (native Segwit) addresses"});

Expand All @@ -14,49 +14,42 @@ function getAddressDetails(address, scriptPubkey, sort, limit, offset) {

if (sort == "asc") {
// need to query the total number of tx first, then build paging info from that value
var options = {
url: `https://blockchain.info/rawaddr/${address}?limit=1`,
headers: {
'User-Agent': 'request'
try {
const response = await axios.get(
`https://blockchain.info/rawaddr/${address}?limit=1`,
{ headers: { 'User-Agent': 'axios' }});

var blockchainJson = response.data;

var txCount = blockchainJson.n_tx;
var pageCount = parseInt(txCount / limit);
var lastPageSize = limit;
if (pageCount * limit < txCount) {
lastPageSize = txCount - pageCount * limit;
}
};

request(options, function(error, response, body) {
if (error == null && response && response.statusCode && response.statusCode == 200) {
var blockchainJson = JSON.parse(body);

var txCount = blockchainJson.n_tx;
var pageCount = parseInt(txCount / limit);
var lastPageSize = limit;
if (pageCount * limit < txCount) {
lastPageSize = txCount - pageCount * limit;
}

var dynamicOffset = txCount - limit - offset;
if (dynamicOffset < 0) {
limit += dynamicOffset;
dynamicOffset += limit;
}

getAddressDetailsSortDesc(address, limit, dynamicOffset).then(function(result) {
result.txids.reverse();
var dynamicOffset = txCount - limit - offset;
if (dynamicOffset < 0) {
limit += dynamicOffset;
dynamicOffset += limit;
}

resolve({addressDetails:result});
getAddressDetailsSortDesc(address, limit, dynamicOffset).then(function(result) {
result.txids.reverse();

}).catch(function(err) {
utils.logError("2308hsghse", err);
resolve({addressDetails:result});

reject(err);
});
}).catch(function(err) {
utils.logError("2308hsghse", err);

} else {
var fullError = {error:error, response:response, body:body};
reject(err);
});

utils.logError("we0f8hasd0fhas", fullError);
} catch (err) {
utils.logError("we0f8hasd0fhas", err);

reject(fullError);
}
});
reject(fullError);
}
} else {
getAddressDetailsSortDesc(address, limit, offset).then(function(result) {
resolve({addressDetails:result});
Expand All @@ -71,44 +64,37 @@ function getAddressDetails(address, scriptPubkey, sort, limit, offset) {
}

function getAddressDetailsSortDesc(address, limit, offset) {
return new Promise(function(resolve, reject) {
var options = {
url: `https://blockchain.info/rawaddr/${address}?limit=${limit}&offset=${offset}`,
headers: {
'User-Agent': 'request'
}
};
return new Promise(async (resolve, reject) => {
try {
const apiResponse = await axios.get(
`https://blockchain.info/rawaddr/${address}?limit=${limit}&offset=${offset}`,
{ headers: { 'User-Agent': 'axios' }});

request(options, function(error, response, body) {
if (error == null && response && response.statusCode && response.statusCode == 200) {
var blockchainJson = JSON.parse(body);
var blockchainJson = apiResponse.data;

var response = {};

response.txids = [];
response.blockHeightsByTxid = {};
blockchainJson.txs.forEach(function(tx) {
response.txids.push(tx.hash);
response.blockHeightsByTxid[tx.hash] = tx.block_height;
});
var response = {};

response.txCount = blockchainJson.n_tx;
response.hash160 = blockchainJson.hash160;
response.totalReceivedSat = blockchainJson.total_received;
response.totalSentSat = blockchainJson.total_sent;
response.balanceSat = blockchainJson.final_balance;
response.source = "blockchain.com";
response.txids = [];
response.blockHeightsByTxid = {};
blockchainJson.txs.forEach(function(tx) {
response.txids.push(tx.hash);
response.blockHeightsByTxid[tx.hash] = tx.block_height;
});

resolve(response);
response.txCount = blockchainJson.n_tx;
response.hash160 = blockchainJson.hash160;
response.totalReceivedSat = blockchainJson.total_received;
response.totalSentSat = blockchainJson.total_sent;
response.balanceSat = blockchainJson.final_balance;
response.source = "blockchain.com";

} else {
var fullError = {error:error, response:response, body:body};
resolve(response);

utils.logError("32907shsghs", fullError);
} catch (err) {
utils.logError("32907shsghs", err);

reject(fullError);
}
});
reject(err);
}
});
}

Expand Down
50 changes: 25 additions & 25 deletions app/api/blockchairAddressApi.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
"use strict";

const request = require("request");
const axios = require("axios");
const utils = require("./../utils.js");


function getAddressDetails(address, scriptPubkey, sort, limit, offset) {
// Note: blockchair api seems to not respect the limit parameter, always using 100
return new Promise(function(resolve, reject) {
return new Promise(async (resolve, reject) => {
var mainnetUrl = `https://api.blockchair.com/bitcoin/dashboards/address/${address}/?offset=${offset}`;
var testnetUrl = `https://api.blockchair.com/bitcoin/testnet/dashboards/address/${address}/?offset=${offset}`;
var url = (global.activeBlockchain == "main") ? mainnetUrl : ((global.activeBlockchain == "test") ? testnetUrl : mainnetUrl);
Expand All @@ -18,38 +18,38 @@ function getAddressDetails(address, scriptPubkey, sort, limit, offset) {
}
};

request(options, function(error, response, body) {
if (error == null && response && response.statusCode && response.statusCode == 200) {
var responseObj = JSON.parse(body);
responseObj = responseObj.data[address];
try {
const response = await axios.get(
url,
{ headers: { "User-Agent": "axios" }});

var result = {};
var responseObj = response.data;
responseObj = responseObj.data[address];

result.txids = [];
var result = {};

// blockchair doesn't support offset for paging, so simulate up to the hard cap of 2,000
for (var i = 0; i < Math.min(responseObj.transactions.length, limit); i++) {
var txid = responseObj.transactions[i];
result.txids = [];

result.txids.push(txid);
}
// blockchair doesn't support offset for paging, so simulate up to the hard cap of 2,000
for (var i = 0; i < Math.min(responseObj.transactions.length, limit); i++) {
var txid = responseObj.transactions[i];

result.txCount = responseObj.address.transaction_count;
result.totalReceivedSat = responseObj.address.received;
result.totalSentSat = responseObj.address.spent;
result.balanceSat = responseObj.address.balance;
result.source = "blockchair.com";
result.txids.push(txid);
}

resolve({addressDetails:result});
result.txCount = responseObj.address.transaction_count;
result.totalReceivedSat = responseObj.address.received;
result.totalSentSat = responseObj.address.spent;
result.balanceSat = responseObj.address.balance;
result.source = "blockchair.com";

} else {
var fullError = {error:error, response:response, body:body};
resolve({addressDetails:result});

utils.logError("308dhew3w83", fullError);
} catch (err) {
utils.logError("308dhew3w83", err);

reject(fullError);
}
});
reject(err);
}
});
}

Expand Down
Loading

0 comments on commit c0a2f28

Please sign in to comment.