Browse Source

More work on #8 - showing balances and transaction history for addresses

- in tx history, request/display all inputs to get correct gain/loss values
- cross referencing for txid history from electrum
- include genesis coinbase values for genesis coinbase output address (electrum ignores the genesis coinbase TX and +50 value, but for consistency with the rest of this tool they're included)
- banner describing the electrum trust model
- ui tweaks including showing gain/loss for each address tx history item
fix-133-memory-crash
Dan Janosik 6 years ago
parent
commit
8bd07537b4
  1. 12
      app.js
  2. 10
      app/api/coreApi.js
  3. 40
      app/api/electrumApi.js
  4. 59
      app/coins/btc.js
  5. 58
      app/coins/ltc.js
  6. 4
      app/config.js
  7. 43
      routes/baseActionsRouter.js
  8. 160
      views/address.pug

12
app.js

@ -245,6 +245,18 @@ app.use(function(req, res, next) {
} }
} }
// electrum trust warnings on address pages
if (!req.session.hideElectrumTrustWarnings) {
var cookieValue = req.cookies['user-setting-hideElectrumTrustWarnings'];
if (cookieValue) {
req.session.hideElectrumTrustWarnings = cookieValue;
} else {
req.session.hideElectrumTrustWarnings = "false";
}
}
res.locals.currencyFormatType = req.session.currencyFormatType; res.locals.currencyFormatType = req.session.currencyFormatType;

10
app/api/coreApi.js

@ -558,10 +558,18 @@ function getRawTransactions(txids) {
}); });
} }
function getRawTransactionsWithInputs(txids) { function getRawTransactionsWithInputs(txids, maxInputs=-1) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
getRawTransactions(txids).then(function(transactions) { getRawTransactions(txids).then(function(transactions) {
var maxInputsTracked = config.site.txMaxInput; var maxInputsTracked = config.site.txMaxInput;
if (maxInputs <= 0) {
maxInputsTracked = 1000000;
} else if (maxInputs > 0) {
maxInputsTracked = maxInputs;
}
var vinTxids = []; var vinTxids = [];
for (var i = 0; i < transactions.length; i++) { for (var i = 0; i < transactions.length; i++) {
var transaction = transactions[i]; var transaction = transactions[i];

40
app/api/electrumApi.js

@ -1,4 +1,8 @@
var config = require("./../config.js"); var config = require("./../config.js");
var coins = require("../coins.js");
var utils = require("../utils.js");
var coinConfig = coins[config.coin];
const ElectrumClient = require('electrum-client'); const ElectrumClient = require('electrum-client');
@ -17,16 +21,16 @@ function connectToServer(host, port) {
electrumClient.connect().then(function() { electrumClient.connect().then(function() {
electrumClient.server_version("btc-rpc-explorer-1.1", "1.2").then(function(res) { electrumClient.server_version("btc-rpc-explorer-1.1", "1.2").then(function(res) {
console.log("Connected to ElectrumX Server: " + host + ":" + port + ", versions: " + res); console.log("Connected to ElectrumX Server: " + host + ":" + port + ", versions: " + res);
electrumClients.push(electrumClient);
}); });
}); });
electrumClients.push(electrumClient);
} }
function runOnServer(electrumClient, f) { function runOnServer(electrumClient, f) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
f(electrumClient).then(function(result) { f(electrumClient).then(function(result) {
resolve(result); resolve({result:result, server:electrumClient.host});
}).catch(function(err) { }).catch(function(err) {
console.log("Error dif0e21qdh: " + JSON.stringify(err) + ", host=" + electrumClient.host + ", port=" + electrumClient.port); console.log("Error dif0e21qdh: " + JSON.stringify(err) + ", host=" + electrumClient.host + ", port=" + electrumClient.port);
@ -57,8 +61,26 @@ function getAddressTxids(addrScripthash) {
return electrumClient.blockchainScripthash_getHistory(addrScripthash); return electrumClient.blockchainScripthash_getHistory(addrScripthash);
}).then(function(results) { }).then(function(results) {
resolve(results[0]); if (addrScripthash == coinConfig.genesisCoinbaseOutputAddressScripthash) {
for (var i = 0; i < results.length; i++) {
results[i].result.unshift({tx_hash:coinConfig.genesisCoinbaseTransactionId, height:0});
}
}
var first = results[0];
var done = false;
for (var i = 1; i < results.length; i++) {
if (results[i].length != first.length) {
resolve({conflictedResults:results});
done = true;
}
}
if (!done) {
resolve(results[0]);
}
}).catch(function(err) { }).catch(function(err) {
reject(err); reject(err);
}); });
@ -71,12 +93,21 @@ function getAddressBalance(addrScripthash) {
return electrumClient.blockchainScripthash_getBalance(addrScripthash); return electrumClient.blockchainScripthash_getBalance(addrScripthash);
}).then(function(results) { }).then(function(results) {
if (addrScripthash == coinConfig.genesisCoinbaseOutputAddressScripthash) {
for (var i = 0; i < results.length; i++) {
var coinbaseBlockReward = coinConfig.blockRewardFunction(0);
results[i].result.confirmed += (coinbaseBlockReward * coinConfig.baseCurrencyUnit.multiplier);
}
}
var first = results[0]; var first = results[0];
var done = false; var done = false;
for (var i = 1; i < results.length; i++) { for (var i = 1; i < results.length; i++) {
if (results[i].confirmed != first.confirmed) { if (results[i].confirmed != first.confirmed) {
resolve({conflictedResults:results}); resolve({conflictedResults:results});
done = true; done = true;
} }
} }
@ -84,7 +115,6 @@ function getAddressBalance(addrScripthash) {
if (!done) { if (!done) {
resolve(results[0]); resolve(results[0]);
} }
}).catch(function(err) { }).catch(function(err) {
reject(err); reject(err);
}); });

59
app/coins/btc.js

@ -1,6 +1,34 @@
var Decimal = require("decimal.js"); var Decimal = require("decimal.js");
Decimal8 = Decimal.clone({ precision:8, rounding:8 }); Decimal8 = Decimal.clone({ precision:8, rounding:8 });
var btcCurrencyUnits = [
{
name:"BTC",
multiplier:1,
default:true,
values:["", "btc", "BTC"],
decimalPlaces:8
},
{
name:"mBTC",
multiplier:1000,
values:["mbtc"],
decimalPlaces:5
},
{
name:"bits",
multiplier:1000000,
values:["bits"],
decimalPlaces:2
},
{
name:"sat",
multiplier:100000000,
values:["sat", "satoshi"],
decimalPlaces:0
}
];
module.exports = { module.exports = {
name:"Bitcoin", name:"Bitcoin",
logoUrl:"/img/logo/btc.svg", logoUrl:"/img/logo/btc.svg",
@ -14,33 +42,9 @@ module.exports = {
"https://raw.githubusercontent.com/btccom/Blockchain-Known-Pools/master/pools.json" "https://raw.githubusercontent.com/btccom/Blockchain-Known-Pools/master/pools.json"
], ],
maxBlockWeight: 4000000, maxBlockWeight: 4000000,
currencyUnits:[ currencyUnits:btcCurrencyUnits,
{ currencyUnitsByName:{"BTC":btcCurrencyUnits[0], "mBTC":btcCurrencyUnits[1], "bits":btcCurrencyUnits[2], "sat":btcCurrencyUnits[3]},
name:"BTC", baseCurrencyUnit:btcCurrencyUnits[3],
multiplier:1,
default:true,
values:["", "btc", "BTC"],
decimalPlaces:8
},
{
name:"mBTC",
multiplier:1000,
values:["mbtc"],
decimalPlaces:5
},
{
name:"bits",
multiplier:1000000,
values:["bits"],
decimalPlaces:2
},
{
name:"sat",
multiplier:100000000,
values:["sat", "satoshi"],
decimalPlaces:0
}
],
feeSatoshiPerByteBucketMaxima: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 50, 75, 100, 150], feeSatoshiPerByteBucketMaxima: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 50, 75, 100, 150],
genesisBlockHash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f", genesisBlockHash: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
genesisCoinbaseTransactionId: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b", genesisCoinbaseTransactionId: "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b",
@ -77,6 +81,7 @@ module.exports = {
"time": 1230988505, "time": 1230988505,
"blocktime": 1230988505 "blocktime": 1230988505
}, },
genesisCoinbaseOutputAddressScripthash:"8b01df4e368ea28f8dc0423bcf7a4923e3a12d307c875e47a0cfbf90b5c39161",
historicalData: [ historicalData: [
{ {
type: "blockheight", type: "blockheight",

58
app/coins/ltc.js

@ -1,6 +1,34 @@
var Decimal = require("decimal.js"); var Decimal = require("decimal.js");
Decimal8 = Decimal.clone({ precision:8, rounding:8 }); Decimal8 = Decimal.clone({ precision:8, rounding:8 });
var ltcCurrencyUnits = [
{
name:"LTC",
multiplier:1,
default:true,
values:["", "ltc", "LTC"],
decimalPlaces:8
},
{
name:"lite",
multiplier:1000,
values:["lite"],
decimalPlaces:5
},
{
name:"photon",
multiplier:1000000,
values:["photon"],
decimalPlaces:2
},
{
name:"litoshi",
multiplier:100000000,
values:["litoshi", "lit"],
decimalPlaces:0
}
];
module.exports = { module.exports = {
name:"Litecoin", name:"Litecoin",
logoUrl:"/img/logo/ltc.svg", logoUrl:"/img/logo/ltc.svg",
@ -12,33 +40,9 @@ module.exports = {
"https://raw.githubusercontent.com/hashstream/pools/master/pools.json", "https://raw.githubusercontent.com/hashstream/pools/master/pools.json",
], ],
maxBlockWeight: 4000000, maxBlockWeight: 4000000,
currencyUnits:[ currencyUnits:ltcCurrencyUnits,
{ currencyUnitsByName:{"LTC":ltcCurrencyUnits[0], "lite":ltcCurrencyUnits[1], "photon":ltcCurrencyUnits[2], "litoshi":ltcCurrencyUnits[3]},
name:"LTC", baseCurrencyUnit:ltcCurrencyUnits[3],
multiplier:1,
default:true,
values:["", "ltc", "LTC"],
decimalPlaces:8
},
{
name:"lite",
multiplier:1000,
values:["lite"],
decimalPlaces:5
},
{
name:"photon",
multiplier:1000000,
values:["photon"],
decimalPlaces:2
},
{
name:"litoshi",
multiplier:100000000,
values:["litoshi", "lit"],
decimalPlaces:0
}
],
feeSatoshiPerByteBucketMaxima: [5, 10, 25, 50, 100, 150, 200, 250], feeSatoshiPerByteBucketMaxima: [5, 10, 25, 50, 100, 150, 200, 250],
genesisBlockHash: "12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2", genesisBlockHash: "12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2",
genesisCoinbaseTransactionId: "97ddfbbae6be97fd6cdf3e7ca13232a3afff2353e29badfab7f73011edd4ced9", genesisCoinbaseTransactionId: "97ddfbbae6be97fd6cdf3e7ca13232a3afff2353e29badfab7f73011edd4ced9",

4
app/config.js

@ -32,6 +32,10 @@ module.exports = {
"pruneblockchain" "pruneblockchain"
], ],
// https://uasf.saltylemon.org/electrum
electrumXServers:[
],
site: { site: {
blockTxPageSize:20, blockTxPageSize:20,
addressTxPageSize:20, addressTxPageSize:20,

43
routes/baseActionsRouter.js

@ -525,7 +525,8 @@ router.get("/address/:address", function(req, res) {
res.locals.address = address; res.locals.address = address;
res.locals.limit = limit; res.locals.limit = limit;
res.locals.offset = offset; res.locals.offset = offset;
res.locals.paginationBaseUrl = ("/address/" + address); res.locals.sort = sort;
res.locals.paginationBaseUrl = ("/address/" + address + "?sort=" + sort);
res.locals.result = {}; res.locals.result = {};
@ -557,6 +558,8 @@ router.get("/address/:address", function(req, res) {
var addrScripthash = hexEnc.stringify(sha256(hexEnc.parse(validateaddressResult.scriptPubKey))); var addrScripthash = hexEnc.stringify(sha256(hexEnc.parse(validateaddressResult.scriptPubKey)));
addrScripthash = addrScripthash.match(/.{2}/g).reverse().join(""); addrScripthash = addrScripthash.match(/.{2}/g).reverse().join("");
res.locals.electrumScripthash = addrScripthash;
promises.push(new Promise(function(resolve, reject) { promises.push(new Promise(function(resolve, reject) {
electrumApi.getAddressBalance(addrScripthash).then(function(result) { electrumApi.getAddressBalance(addrScripthash).then(function(result) {
res.locals.balance = result; res.locals.balance = result;
@ -572,14 +575,25 @@ router.get("/address/:address", function(req, res) {
promises.push(new Promise(function(resolve, reject) { promises.push(new Promise(function(resolve, reject) {
electrumApi.getAddressTxids(addrScripthash).then(function(result) { electrumApi.getAddressTxids(addrScripthash).then(function(result) {
res.locals.electrumHistory = result; var txidResult = null;
if (result.conflictedResults) {
res.locals.conflictedTxidResults = true;
txidResult = result.conflictedResults[0];
} else {
txidResult = result;
}
res.locals.electrumHistory = txidResult;
var txids = []; var txids = [];
var blockHeightsByTxid = {}; var blockHeightsByTxid = {};
for (var i = 0; i < result.length; i++) { for (var i = 0; i < txidResult.result.length; i++) {
txids.push(result[i].tx_hash); txids.push(txidResult.result[i].tx_hash);
blockHeightsByTxid[result[i].tx_hash] = result[i].height; blockHeightsByTxid[txidResult.result[i].tx_hash] = txidResult.result[i].height;
} }
if (sort == "desc") { if (sort == "desc") {
@ -606,7 +620,7 @@ router.get("/address/:address", function(req, res) {
var txInputs = rawTxResult.txInputsByTransaction[tx.txid]; var txInputs = rawTxResult.txInputsByTransaction[tx.txid];
for (var j = 0; j < tx.vout.length; j++) { for (var j = 0; j < tx.vout.length; j++) {
if (tx.vout[j].scriptPubKey.addresses.includes(address)) { if (tx.vout[j].value > 0 && tx.vout[j].scriptPubKey && tx.vout[j].scriptPubKey.addresses && tx.vout[j].scriptPubKey.addresses.includes(address)) {
if (addrGainsByTx[tx.txid] == null) { if (addrGainsByTx[tx.txid] == null) {
addrGainsByTx[tx.txid] = new Decimal(0); addrGainsByTx[tx.txid] = new Decimal(0);
} }
@ -618,16 +632,17 @@ router.get("/address/:address", function(req, res) {
for (var j = 0; j < tx.vin.length; j++) { for (var j = 0; j < tx.vin.length; j++) {
var txInput = txInputs[j]; var txInput = txInputs[j];
for (var k = 0; k < txInput.vout.length; k++) { if (txInput != null) {
if (txInput.vout[k].scriptPubKey.addresses.includes(address)) { for (var k = 0; k < txInput.vout.length; k++) {
if (addrLossesByTx[tx.txid] == null) { if (txInput.vout[k].scriptPubKey.addresses.includes(address)) {
addrLossesByTx[tx.txid] = new Decimal(0); if (addrLossesByTx[tx.txid] == null) {
} addrLossesByTx[tx.txid] = new Decimal(0);
}
addrLossesByTx[tx.txid] = addrLossesByTx[tx.txid].plus(new Decimal(txInput.vout[k].value)); addrLossesByTx[tx.txid] = addrLossesByTx[tx.txid].plus(new Decimal(txInput.vout[k].value));
}
} }
} }
} }
//console.log("tx: " + JSON.stringify(tx)); //console.log("tx: " + JSON.stringify(tx));
@ -658,6 +673,8 @@ router.get("/address/:address", function(req, res) {
}); });
}).catch(function(err) { }).catch(function(err) {
console.log(err); console.log(err);
res.render("address");
}); });
}).catch(function(err) { }).catch(function(err) {

160
views/address.pug

@ -52,6 +52,22 @@ block content
div(class="tab-content") div(class="tab-content")
div(id="tab-details", class="tab-pane active", role="tabpanel") div(id="tab-details", class="tab-pane active", role="tabpanel")
if (config.electrumXServers && config.electrumXServers.length > 0)
if (session.hideElectrumTrustWarnings != "true")
div(class="alert alert-primary alert-dismissible clearfix", role="alert")
h4(class="alert-heading h6 font-weight-bold") Note
p Since this explorer is database-free, it doesn't natively support address balances and transaction histories. In order to provide this functionality, address balances and transaction history can be requested from a configurable set of ElectrumX servers. If multiple ElectrumX servers are configured, the results are cross-referenced and conflicts noted. For the transaction history displayed below, only the transaction identifiers from ElectrumX are used; the transaction details are requested via RPC from this app's primary node, as usual.
span(class="font-weight-bold") Configured ElectrumX Servers
ul
each server in config.electrumXServers
li
span #{server.host}
span : #{server.port}
a(href="/changeSetting?name=hideElectrumTrustWarnings&value=true", class="close", aria-label="Close", style="text-decoration: none;")
span(aria-hidden="true") &times;
div(class="card mb-3") div(class="card mb-3")
div(class="card-header") div(class="card-header")
span(class="h6") Summary span(class="h6") Summary
@ -90,10 +106,9 @@ block content
div(class="summary-split-table-label") Balance div(class="summary-split-table-label") Balance
div(class="summary-split-table-content monospace") div(class="summary-split-table-content monospace")
span(class="text-danger") Conflicted ElectrumX Results span(class="text-danger") Conflicted ElectrumX Results
include includes/electrum-trust-note.pug
each item in balance.conflictedResults each item in balance.conflictedResults
- var currencyValue = item.confirmed / 100000000; - var currencyValue = item.confirmed / coinConfig.baseCurrencyUnit.multiplier;
include includes/value-display.pug include includes/value-display.pug
@ -101,23 +116,20 @@ block content
div(class="row") div(class="row")
div(class="summary-split-table-label") Balance div(class="summary-split-table-label") Balance
div(class="summary-split-table-content monospace") div(class="summary-split-table-content monospace")
- var currencyValue = balance.confirmed / 100000000; - var currencyValue = balance.result.confirmed / coinConfig.baseCurrencyUnit.multiplier;
include includes/value-display.pug include includes/value-display.pug
include includes/electrum-trust-note.pug
if (balance.unconfirmed) if (balance.unconfirmed)
div(class="row") div(class="row")
div(class="summary-split-table-label") Unconfirmed div(class="summary-split-table-label") Unconfirmed
div(class="summary-split-table-content monospace") div(class="summary-split-table-content monospace")
- var currencyValue = balance.unconfirmed / 100000000; - var currencyValue = balance.unconfirmed / coinConfig.baseCurrencyUnit.multiplier;
include includes/value-display.pug include includes/value-display.pug
include includes/electrum-trust-note.pug
if (electrumHistory) if (electrumHistory)
div(class="row") div(class="row")
div(class="summary-split-table-label") Transactions div(class="summary-split-table-label") Transactions
div(class="summary-split-table-content monospace") #{electrumHistory.length.toLocaleString()} div(class="summary-split-table-content monospace") #{electrumHistory.result.length.toLocaleString()}
include includes/electrum-trust-note.pug
div(class="row") div(class="row")
div(class="summary-split-table-label") QR Code div(class="summary-split-table-label") QR Code
@ -125,6 +137,11 @@ block content
img(src=addressQrCodeUrl, alt=address, style="border: solid 1px #ccc;") img(src=addressQrCodeUrl, alt=address, style="border: solid 1px #ccc;")
div(class="col-md-6") div(class="col-md-6")
if (electrumScripthash)
div(class="row")
div(class="summary-split-table-label") Scripthash
div(class="summary-split-table-content monospace") #{electrumScripthash}
- var x = result.validateaddress; - var x = result.validateaddress;
- var flagNames = ["Is Valid?", "Is Script?", "Is Witness?", "Is Mine?", "Is Watch-Only?"]; - var flagNames = ["Is Valid?", "Is Script?", "Is Witness?", "Is Mine?", "Is Watch-Only?"];
- var flags = [x.isvalid, x.isscript, x.iswitness, x.ismine, x.iswatchonly]; - var flags = [x.isvalid, x.isscript, x.iswitness, x.ismine, x.iswatchonly];
@ -164,34 +181,79 @@ block content
i(class="fas fa-times text-danger") i(class="fas fa-times text-danger")
div(class="card") div(class="card")
div(class="card-header") div(class="card-header clearfix")
span(class="h6") Transactions div(class="float-left")
if (transactions) span(class="h6")
include includes/electrum-trust-note.pug if (txids)
if (txids.length == 1)
span 1 Transaction
else
span #{txids.length.toLocaleString()} Transactions
else
span Transactions
if (!crawlerBot && txids && txids.length > 0)
div(class="float-right")
a(href="#", class="pull-right dropdown-toggle", data-toggle="dropdown", aria-haspopup="true", aria-expanded="false")
if (sort == "desc")
span Newest First
else
span Oldest First
div(class="dropdown-menu dropdown-menu-right")
a(href=("/address/" + address), class="dropdown-item")
if (sort == "desc")
i(class="fa fa-check")
span Newest First
a(href=("/address/" + address + "?sort=asc"), class="dropdown-item")
if (sort != "desc")
i(class="fa fa-check")
span Oldest First
div(class="card-body") div(class="card-body")
if (conflictedTxidResults)
div(class="alert alert-warning", style="padding-bottom: 0;")
div(class="float-left", style="width: 55px; height: 50px; font-size: 18px;")
i(class="fas fa-exclamation-triangle fa-2x", style="margin-top: 10px;")
h4(class="alert-heading h6 font-weight-bold") Trust Warning
p
span The transaction history for this address was requested from mulitple ElectrumX servers and the results did not match. The results below were obtained only from
span(class="font-weight-bold") #{electrumHistory.server}
if (transactions) if (transactions)
each tx, txIndex in transactions each tx, txIndex in transactions
//pre //pre
// code #{JSON.stringify(tx, null, 4)} // code #{JSON.stringify(tx, null, 4)}
div(class="xcard mb-3") div(class="xcard mb-3")
div(class="card-header monospace clearfix") div(class="card-header monospace clearfix")
div(class="float-left", style="margin-right: 10px;") div(class="float-left", style="margin-right: 0px;")
span ##{(offset + txIndex + 1).toLocaleString()} if (sort == "desc")
span &ndash; span ##{(txids.length - offset - txIndex).toLocaleString()}
div(class="float-left") else
if (tx && tx.txid) span ##{(offset + txIndex + 1).toLocaleString()}
if (tx.time) span &ndash;
span #{moment.utc(new Date(tx["time"] * 1000)).format("Y-MM-DD HH:mm:ss")} utc
- var timeAgoTime = tx.time;
include includes/time-ago.pug
else
span(class="text-danger") Unconfirmed
br
a(href=("/tx/" + tx.txid)) #{tx.txid}
br
div(class="row")
div(class="col-md-6")
if (tx && tx.txid)
a(href=("/tx/" + tx.txid)) #{tx.txid}
if (global.specialTransactions && global.specialTransactions[tx.txid])
span
a(data-toggle="tooltip", title=(coinConfig.name + " Fun! See transaction for details"))
i(class="fas fa-certificate text-primary")
div(class="col-md-6")
div(class="text-md-right")
if (tx.time)
span #{moment.utc(new Date(tx["time"] * 1000)).format("Y-MM-DD HH:mm:ss")} utc
- var timeAgoTime = tx.time;
include includes/time-ago.pug
else
span(class="text-danger") Unconfirmed
div(class="col")
if (addrGainsByTx[tx.txid]) if (addrGainsByTx[tx.txid])
- var currencyValue = addrGainsByTx[tx.txid]; - var currencyValue = addrGainsByTx[tx.txid];
span(class="text-success") + span(class="text-success") +
@ -204,11 +266,6 @@ block content
- var currencyValue = addrLossesByTx[tx.txid]; - var currencyValue = addrLossesByTx[tx.txid];
span(class="text-danger") - span(class="text-danger") -
include includes/value-display.pug include includes/value-display.pug
if (global.specialTransactions && global.specialTransactions[tx.txid])
span
a(data-toggle="tooltip", title=(coinConfig.name + " Fun! See transaction for details"))
i(class="fas fa-certificate text-primary")
div(class="card-body") div(class="card-body")
if (true) if (true)
@ -216,6 +273,7 @@ block content
- var blockHeight = blockHeightsByTxid[tx.txid]; - var blockHeight = blockHeightsByTxid[tx.txid];
include includes/transaction-io-details.pug include includes/transaction-io-details.pug
else else
p Since this explorer is database-free, it doesn't natively support address transaction history. However, you can configure it to communicate with one or more ElectrumX servers to build and display this data. In doing so, you should be aware that you'll be trusting those ElectrumX servers. If you configure multiple servers the results obtained from each will be cross-referenced against the others. Communicating with ElectrumX servers will also impact your privacy since the servers will know what addresses you're interested in. If these tradeoffs are acceptable, you can see a list of public ElectrumX servers here: p Since this explorer is database-free, it doesn't natively support address transaction history. However, you can configure it to communicate with one or more ElectrumX servers to build and display this data. In doing so, you should be aware that you'll be trusting those ElectrumX servers. If you configure multiple servers the results obtained from each will be cross-referenced against the others. Communicating with ElectrumX servers will also impact your privacy since the servers will know what addresses you're interested in. If these tradeoffs are acceptable, you can see a list of public ElectrumX servers here:
a(href="https://uasf.saltylemon.org/electrum") https://uasf.saltylemon.org/electrum a(href="https://uasf.saltylemon.org/electrum") https://uasf.saltylemon.org/electrum
@ -224,18 +282,19 @@ block content
pre pre
code #{JSON.stringify(transactions, null, 4)} code #{JSON.stringify(transactions, null, 4)}
- var pageNumber = offset / limit + 1; if (!crawlerBot && txids && txids.length > limit)
- var pageCount = Math.floor(txids.length / limit); - var pageNumber = offset / limit + 1;
- if (pageCount * limit < txids.length) { - var pageCount = Math.floor(txids.length / limit);
- pageCount++; - if (pageCount * limit < txids.length) {
- } - pageCount++;
- var paginationUrlFunction = function(x) { - }
- return paginationBaseUrl + "?limit=" + limit + "&offset=" + ((x - 1) * limit); - var paginationUrlFunction = function(x) {
- } - return paginationBaseUrl + "&limit=" + limit + "&offset=" + ((x - 1) * limit);
- }
hr
hr
include includes/pagination.pug include includes/pagination.pug
@ -246,12 +305,13 @@ block content
pre pre
code(class="language-json", data-lang="json") #{JSON.stringify(result.validateaddress, null, 4)} code(class="language-json", data-lang="json") #{JSON.stringify(result.validateaddress, null, 4)}
h4 Electrum.Balance if (config.electrumXServers && config.electrumXServers.length > 0)
pre h4 Electrum.Balance
code(class="language-json", data-lang="json") #{JSON.stringify(electrumBalance, null, 4)} pre
code(class="language-json", data-lang="json") #{JSON.stringify(electrumBalance, null, 4)}
h4 Electrum.History h4 Electrum.History
pre pre
code(class="language-json", data-lang="json") #{JSON.stringify(electrumHistory, null, 4)} code(class="language-json", data-lang="json") #{JSON.stringify(electrumHistory, null, 4)}

Loading…
Cancel
Save