Skip to content

Commit 0b14223

Browse files
authored
Merge branch 'janoside:master' into master
2 parents 3493c42 + 6a52f39 commit 0b14223

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+10186
-3642
lines changed

.env-sample

+12-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
#BTCEXP_BASEURL=/explorer/
1313

1414

15+
16+
# Whether your site will be served via HTTPS (impacts session cookies)
17+
# Note: if true, the express app will also have "trust proxy" set to 1, to help anyone running this tool behind a HTTPS reverse proxy
18+
# Default: false
19+
#BTCEXP_SECURE_SITE=false
20+
21+
1522
# The active coin. Only officially supported value is "BTC".
1623
# Default: BTC
1724
#BTCEXP_COIN=BTC
@@ -199,16 +206,19 @@
199206
#########
200207

201208

202-
# Options designed for production use, on public instances like the one at BitcoinExplorer.org. These tend to be cost-reduction-oriented in nature.
209+
# Options designed for production use, on public instances like the one at BitcoinExplorer.org.
203210

204211
# S3 details for uploading assets to be served via CloudFront
205212

206213
# This is the optional profile name that the AWS SDK will use to load credentials. By default this refers to an item in ~/.aws/credentials
207-
#BTCEXP_S3_PROFILE_NAME=xxx
214+
#AWS_PROFILE=xxx
208215

209216
# The S3 bucket where assets will be uploaded on launch
210217
#BTCEXP_S3_BUCKET=xxx
211218

219+
# The region that the above S3 bucket exists
220+
#BTCEXP_S3_BUCKET_REGION=xxx
221+
212222
# The path in the above S3 bucket where assets will be uploaded on launch
213223
#BTCEXP_S3_BUCKET_PATH=xxx/
214224

.eslintrc.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = {
2+
"env": {
3+
"node": true,
4+
"commonjs": true,
5+
"es2021": true
6+
},
7+
"extends": "eslint:recommended",
8+
"overrides": [
9+
],
10+
"parserOptions": {
11+
"ecmaVersion": "latest"
12+
},
13+
"rules": {
14+
}
15+
}

CHANGELOG-API.md

+41-25
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,51 @@
11
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.
22

3-
##### v1.2.0
4-
###### Unreleased
5-
6-
* Added: /api/xyzpub/txids/:xyzpub
7-
* Added: /api/xyzpub/addresses/:xyzpub
8-
* Added: /api/block/header/:height
9-
* Added: /api/block/header/:hash
10-
* Added: /api/holidays/all
11-
* Added: /api/holidays/today
12-
* Added: /api/holidays/:day
13-
* Added: /api/tx/volume/24h
14-
* Changed: /api/tx/:txid
15-
* Added result.vin[i].scriptSig.address
16-
* Added result.vin[i].scriptSig.type
17-
* Added result.fee, including result.fee.amount and result.fee.unit
18-
* Added result.fun, when applicable, which includes special details about the tx
19-
* Changed path: /api/util/xyzpub/:xyzpub -> /api/xyzpub/:xyzpub (auto-redirect included)
20-
* Changed: /api/price[/...]
21-
* Return values exclude thousands separators by default; they can be added with "?format=true"
3+
##### v2.0.0
4+
###### 2023-06-14
5+
6+
* BREAKING: All actions now return JSON content
7+
* Added:
8+
* `/api/blocks/tip` (replaces `/api/blocks/tip/hash` and `/api/blocks/tip/height`)
9+
* `/api/xyzpub/txids/$XPUB`
10+
* `/api/xyzpub/addresses/$XPUB`
11+
* `/api/block/header/$HEIGHT`
12+
* `/api/block/header/$HASH`
13+
* `/api/blockchain/next-halving`
14+
* `/api/holidays/all`
15+
* `/api/holidays/today`
16+
* `/api/holidays/$DAY`
17+
* `/api/tx/volume/24h`
18+
* `/api/price/marketcap` (replaces `/api/price/$CURRENCY/marketcap`)
19+
* `/api/price/sats` (replaces `/api/price/$CURRENCY/sats`)
20+
* Changed output:
21+
* `/api/tx/$TXID`
22+
* Added result.vin[i].scriptSig.address
23+
* Added result.vin[i].scriptSig.type
24+
* Added result.fee, including result.fee.amount and result.fee.unit
25+
* Added result.fun, when applicable, which includes special details about the tx
26+
* `/api/price[/...]`
27+
* Return values exclude thousands separators by default; they can be added with "?format=true"
28+
* Changed path:
29+
* `/api/util/xyzpub/$XPUB` -> `/api/xyzpub/$XPUB` (auto-redirect included)
30+
* Removed:
31+
* `/api/blocks/tip/hash` (see `/api/blocks/tip`)
32+
* `/api/blocks/tip/height` (see `/api/blocks/tip`)
33+
* `/api/mempool/count` (see "size" field in output from `/api/mempool/summary`)
34+
* `/api/price/$CURRENCY/marketcap` (see individual fields in output from `/api/price/marketcap`)
35+
* `/api/price/$CURRENCY/sats` (see individual fields in output from `/api/price/sats`)
36+
2237

2338

2439
##### v1.1.0
2540
###### 2021-12-07
2641

27-
* Added: /api/blockchain/utxo-set
28-
* Added: /api/address/:address
29-
* Added: /api/mining/next-block
30-
* Added: /api/mining/next-block/txids
31-
* Added: /api/mining/next-block/includes/:txid
32-
* Added: /api/mining/miner-summary
42+
* Added:
43+
* `/api/blockchain/utxo-set`
44+
* `/api/address/$ADDRESS`
45+
* `/api/mining/next-block`
46+
* `/api/mining/next-block/txids`
47+
* `/api/mining/next-block/includes/$TXID`
48+
* `/api/mining/miner-summary`
3349

3450

3551

CHANGELOG.md

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
##### v3.4.0
2-
###### Unreleased (beta)
2+
###### 2023-06-14
33

4-
* Include median fee rate for next-block estimates, on homepage and at [/next-block](./next-block)
5-
* Minor fixes for running against Bitcoin Core v23
6-
* URL change: /mining-template -> /next-block (redirect is included for compatibility)
7-
* Homepage additions
4+
* Breaking changes to the API (see [./api/changelog](/api/changelog))
5+
* Homepage
6+
* New "Next Halving" widget in Network Summary
87
* Show difficulty ATH comparison
98
* Show "Next Block" fullness
9+
* Progress bar for difficulty adjustment estimate
10+
* Include median fee rate for next-block estimates (also on [/next-block](./next-block))
1011
* Show a banner if 'today' is a Bitcoin 'Holiday' (see more below)
12+
* Minor fixes for running against Bitcoin Core v23
13+
* Block Analysis: include top "days destroyed" transactions
14+
* URL change: /mining-template -> /next-block (redirect is included for compatibility)
15+
* On Extended PubKey pages, include balance data for various address (if Electrum server is configured)
16+
* New [/next-halving](./next-halving) tool
1117
* Several new API actions/changes; see [/api/changelog](./api/changelog)
1218
* New [/holidays](./holidays), a curated list of Bitcoin 'Holidays'
19+
* Support for different view options on [/fun](./fun)
20+
* On [/difficulty-history](./difficulty-history), make delta graph honor timespan filtering
21+
* Proper use of production-ready MemoryStore for session data
1322
* Support for serving static assets via a configurable CDN
1423
* Misc fixes for erroneous data display on non-mainnet nodes
1524
* Switch from fontawesome to bootstrap-icons v1.8.0
1625
* Refreshed miner-identification database
1726
* Refreshed "Dark" theme with blues toned down (legacy dark theme still available)
1827
* UI/UX tweaks
28+
* Misc minor fixes
1929
* Updated dependencies
2030

2131

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ See [CHANGELOG.md](/CHANGELOG.md).
4545

4646
1. Install `Bitcoin Core` - [instructions](https://bitcoin.org/en/full-node). Ensure that `Bitcoin Core`'s' RPC server is enabled (`server=1`).
4747
2. Allow `Bitcoin Core` to synchronize with the Bitcoin network (you *can* use this tool while sychronizing, but some pages may fail).
48-
3. Install Node.js (12.9+ required, 16+ recommended).
48+
3. Install Node.js (16+ required, 18+ recommended).
4949

5050
### Note about pruning and indexing configurations
5151

app.js

+79-17
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ const logger = require('morgan');
7272
const cookieParser = require('cookie-parser');
7373
const bodyParser = require('body-parser');
7474
const session = require("express-session");
75-
const csurf = require("csurf");
75+
const MemoryStore = require('memorystore')(session);
76+
const csrfApi = require("csurf");
7677
const config = require("./app/config.js");
7778
const simpleGit = require('simple-git');
7879
const utils = require("./app/utils.js");
7980
const moment = require("moment");
8081
const Decimal = require('decimal.js');
81-
const bitcoinCore = require("btc-rpc-client");
8282
const pug = require("pug");
8383
const momentDurationFormat = require("moment-duration-format");
8484
const coreApi = require("./app/api/coreApi.js");
@@ -95,14 +95,15 @@ const auth = require('./app/auth.js');
9595
const sso = require('./app/sso.js');
9696
const markdown = require("markdown-it")();
9797
const v8 = require("v8");
98-
var compression = require("compression");
98+
const compression = require("compression");
99+
const jayson = require('jayson/promise');
99100

100101
const appUtils = require("@janoside/app-utils");
101102
const s3Utils = appUtils.s3Utils;
102103

103104
let cdnS3Bucket = null;
104105
if (config.cdn.active) {
105-
cdnS3Bucket = s3Utils.createBucket(config.cdn.s3Bucket, config.cdn.s3BucketPath);
106+
cdnS3Bucket = s3Utils.createBucket(config.cdn.s3Bucket, config.cdn.s3BucketRegion, config.cdn.s3BucketPath);
106107
}
107108

108109
require("./app/currencies.js");
@@ -203,11 +204,31 @@ if (process.env.BTCEXP_BASIC_AUTH_PASSWORD) {
203204
//expressApp.use(logger('dev'));
204205
expressApp.use(bodyParser.json());
205206
expressApp.use(bodyParser.urlencoded({ extended: false }));
206-
expressApp.use(session({
207+
208+
209+
const sessionConfig = {
207210
secret: config.cookieSecret,
208211
resave: false,
209-
saveUninitialized: false
210-
}));
212+
saveUninitialized: true,
213+
cookie: {
214+
secure: config.secureSite
215+
}
216+
};
217+
218+
if (config.secureSite) {
219+
expressApp.set('trust proxy', 1);
220+
}
221+
222+
// Helpful reference for production: nginx HTTPS proxy:
223+
// https://gist.github.com/nikmartin/5902176
224+
debugLog(`Session config: ${JSON.stringify(utils.obfuscateProperties(sessionConfig, ["secret"]))}`);
225+
226+
sessionConfig.store = new MemoryStore({
227+
checkPeriod: 86400000 // prune expired entries every 24h
228+
});
229+
230+
231+
expressApp.use(session(sessionConfig));
211232

212233
expressApp.use(compression());
213234

@@ -729,6 +750,8 @@ expressApp.onStartup = async () => {
729750
global.coinConfig = coins[config.coin];
730751
global.coinConfigs = coins;
731752

753+
global.SATS_PER_BTC = global.coinConfig.baseCurrencyUnit.multiplier;
754+
732755
global.specialTransactions = {};
733756
global.specialBlocks = {};
734757
global.specialAddresses = {};
@@ -852,29 +875,67 @@ expressApp.onStartup = async () => {
852875
}
853876
}
854877

855-
expressApp.continueStartup = function() {
856-
var rpcCred = config.credentials.rpc;
857-
debugLog(`Connecting to RPC node at ${rpcCred.host}:${rpcCred.port}`);
878+
function connectToRpcServer() {
879+
// reload credentials, the main "config.credentials.rpc" can be stale
880+
// since the username/password can be sourced from the auth cookie
881+
// which changes each startup of bitcoind
882+
let credentialsForRpcConnect = config.credentials.loadFreshRpcCredentials();
883+
884+
debugLog(`RPC Credentials: ${JSON.stringify(utils.obfuscateProperties(credentialsForRpcConnect, ["password"]), null, 4)}`);
885+
886+
let rpcCred = credentialsForRpcConnect;
887+
debugLog(`Connecting to RPC node at [${rpcCred.host}]:${rpcCred.port}`);
858888

859-
var rpcClientProperties = {
889+
let usernamePassword = `${rpcCred.username}:${rpcCred.password}`;
890+
let authorizationHeader = `Basic ${btoa(usernamePassword)}`; // basic auth header format (base64 of "username:password")
891+
892+
let rpcClientProperties = {
860893
host: rpcCred.host,
861894
port: rpcCred.port,
862895
username: rpcCred.username,
863896
password: rpcCred.password,
864897
timeout: rpcCred.timeout
865898
};
866899

867-
global.rpcClient = new bitcoinCore(rpcClientProperties);
900+
debugLog(`RPC Connection properties: ${JSON.stringify(utils.obfuscateProperties(rpcClientProperties, ["password"]), null, 4)}`);
901+
902+
// add after logging to avoid logging base64'd credentials
903+
rpcClientProperties.headers = {
904+
"Authorization": authorizationHeader
905+
};
906+
907+
// main RPC client
908+
global.rpcClient = jayson.Client.http(rpcClientProperties);
868909

869-
var rpcClientNoTimeoutProperties = {
910+
let rpcClientNoTimeoutProperties = {
870911
host: rpcCred.host,
871912
port: rpcCred.port,
872913
username: rpcCred.username,
873914
password: rpcCred.password,
874-
timeout: 0
915+
timeout: 0,
916+
headers: {
917+
"Authorization": authorizationHeader
918+
}
875919
};
876920

877-
global.rpcClientNoTimeout = new bitcoinCore(rpcClientNoTimeoutProperties);
921+
// no timeout RPC client, for long-running commands
922+
global.rpcClientNoTimeout = jayson.Client.http(rpcClientNoTimeoutProperties);
923+
}
924+
925+
expressApp.continueStartup = function() {
926+
connectToRpcServer();
927+
928+
// if using cookie auth, watch for changes to the file and reconnect
929+
if (config.credentials.rpc.authType == "cookie") {
930+
debugLog(`RPC authentication is cookie based; watching for changes to the auth cookie file...`);
931+
932+
fs.watchFile(config.credentials.rpc.authCookieFilepath, (curr, prev) => {
933+
debugLog(`RPC auth cookie change detected; attempting reconnect...`);
934+
935+
connectToRpcServer();
936+
});
937+
}
938+
878939

879940
// default values - after we connect via RPC, we update these
880941
global.txindexAvailable = false;
@@ -889,7 +950,7 @@ expressApp.continueStartup = function() {
889950

890951

891952
if (config.addressApi) {
892-
var supportedAddressApis = addressApi.getSupportedAddressApis();
953+
let supportedAddressApis = addressApi.getSupportedAddressApis();
893954
if (!supportedAddressApis.includes(config.addressApi)) {
894955
utils.logError("32907ghsd0ge", `Unrecognized value for BTCEXP_ADDRESS_API: '${config.addressApi}'. Valid options are: ${supportedAddressApis}`);
895956
}
@@ -1033,7 +1094,8 @@ expressApp.use(function(req, res, next) {
10331094
next();
10341095
});
10351096

1036-
expressApp.use(csurf(), (req, res, next) => {
1097+
const csrfProtection = csrfApi();
1098+
expressApp.use(csrfProtection, (req, res, next) => {
10371099
res.locals.csrfToken = req.csrfToken();
10381100

10391101
next();

app/actionPerformanceMonitor.js

-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
const fs = require('fs');
2-
const path = require('path');
31
const onHeaders = require('on-headers');
4-
const os = require('os');
5-
const v8 = require('v8');
62
const debug = require("debug");
73
const debugLog = debug("monitor");
84
const utils = require("./utils.js");

app/api/addressApi.js

-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ const config = require("./../config.js");
44
const coins = require("../coins.js");
55
const utils = require("../utils.js");
66

7-
const coinConfig = coins[config.coin];
8-
97
const electrumAddressApi = require("./electrumAddressApi.js");
108
const blockchainAddressApi = require("./blockchainAddressApi.js");
119
const blockchairAddressApi = require("./blockchairAddressApi.js");

0 commit comments

Comments
 (0)