Browse Source

Lots of error handling improvements:

- RPC api now throws errors up to callers on RpcError results
- More consistent handling of errors from baseActionRouter
- Add some missing rejection handlers
- Tweak UI for display of errors
- Handle missing block data on /block pages (in UI) so that error info can be displayed cleanly (rather than the template error showing because data is missing)
master
Dan Janosik 5 years ago
parent
commit
36e8015b64
No known key found for this signature in database GPG Key ID: C6F8CE9FFDB2CED2
  1. 4
      app/api/coreApi.js
  2. 86
      app/api/rpcApi.js
  3. 25
      routes/baseActionsRouter.js
  4. 2
      views/address.pug
  5. 18
      views/block.pug
  6. 47
      views/includes/page-errors-modal.pug

4
app/api/coreApi.js

@ -802,6 +802,8 @@ function getRawTransactionsWithInputs(txids, maxInputs=-1) {
resolve({ transactions:transactions, txInputsByTransaction:txInputsByTransaction }); resolve({ transactions:transactions, txInputsByTransaction:txInputsByTransaction });
}); });
}).catch(function(err) {
reject(err);
}); });
}); });
} }
@ -833,6 +835,8 @@ function getBlockByHashWithTransactions(blockHash, txLimit, txOffset) {
resolve({ getblock:block, transactions:txsResult.transactions, txInputsByTransaction:txsResult.txInputsByTransaction }); resolve({ getblock:block, transactions:txsResult.transactions, txInputsByTransaction:txsResult.txInputsByTransaction });
}); });
}).catch(function(err) {
reject(err);
}); });
}); });
} }

86
app/api/rpcApi.js

@ -351,35 +351,44 @@ function getRpcData(cmd) {
var client = (cmd == "gettxoutsetinfo" ? global.rpcClientNoTimeout : global.rpcClient); var client = (cmd == "gettxoutsetinfo" ? global.rpcClientNoTimeout : global.rpcClient);
client.command(cmd, function(err, result, resHeaders) { client.command(cmd, function(err, result, resHeaders) {
if (err) { try {
utils.logError("32euofeege", err, {cmd:cmd}); if (err) {
logStats(cmd, false, new Date().getTime() - startTime, false);
reject(err); throw new Error(`RpcError: type=failure-01`);
}
callback(); if (Array.isArray(result) && result.length == 1) {
var result0 = result[0];
if (result0 && result0.name && result0.name == "RpcError") {
logStats(cmd, false, new Date().getTime() - startTime, false);
logStats(cmd, false, new Date().getTime() - startTime, false); throw new Error(`RpcError: type=errorResponse-01`);
}
}
return; if (result.name && result.name == "RpcError") {
} logStats(cmd, false, new Date().getTime() - startTime, false);
if (result.name && result.name == "RpcError") { throw new Error(`RpcError: type=errorResponse-02`);
utils.logError("3084yh4r7ge", result, {cmd:cmd}); }
reject(result); resolve(result);
logStats(cmd, false, new Date().getTime() - startTime, true);
callback(); callback();
logStats(cmd, false, new Date().getTime() - startTime, false); } catch (e) {
e.userData = {error:err, request:cmd, result:result};
return; utils.logError("9u4278t5h7rfhgf", e, {error:err, request:cmd, result:result});
}
resolve(result); reject(e);
logStats(cmd, false, new Date().getTime() - startTime, true); callback();
}
callback();
}); });
}; };
@ -395,35 +404,44 @@ function getRpcDataWithParams(request) {
rpcCall = function(callback) { rpcCall = function(callback) {
global.rpcClient.command([request], function(err, result, resHeaders) { global.rpcClient.command([request], function(err, result, resHeaders) {
if (err != null) { try {
utils.logError("38eh39hdee", err, {result:result, headers:resHeaders}); if (err != null) {
logStats(request.method, true, new Date().getTime() - startTime, false);
reject(err); throw new Error(`RpcError: type=failure-02`);
}
callback(); if (Array.isArray(result) && result.length == 1) {
var result0 = result[0];
logStats(request.method, true, new Date().getTime() - startTime, false); if (result0 && result0.name && result0.name == "RpcError") {
logStats(request.method, true, new Date().getTime() - startTime, false);
return; throw new Error(`RpcError: type=errorResponse-03`);
} }
}
if (result.name && result.name == "RpcError") { if (result.name && result.name == "RpcError") {
utils.logError("23983euewf8d", result, {result:result, headers:resHeaders}); logStats(request.method, true, new Date().getTime() - startTime, false);
reject(result); throw new Error(`RpcError: type=errorResponse-04`);
}
callback(); resolve(result[0]);
logStats(request.method, true, new Date().getTime() - startTime, false); logStats(request.method, true, new Date().getTime() - startTime, true);
return; callback();
}
resolve(result[0]); } catch (e) {
e.userData = {error:err, request:request, result:result};
logStats(request.method, true, new Date().getTime() - startTime, true); utils.logError("283h7ewsede", e, {error:err, request:request, result:result});
callback(); reject(e);
callback();
}
}); });
}; };

25
routes/baseActionsRouter.js

@ -646,6 +646,11 @@ router.get("/block-height/:blockHeight", function(req, res, next) {
res.locals.result.txInputsByTransaction = result.txInputsByTransaction; res.locals.result.txInputsByTransaction = result.txInputsByTransaction;
resolve(); resolve();
}).catch(function(err) {
res.locals.pageErrors.push(utils.logError("98493y4758h55", err));
reject(err);
}); });
})); }));
@ -656,7 +661,7 @@ router.get("/block-height/:blockHeight", function(req, res, next) {
resolve(); resolve();
}).catch(function(err) { }).catch(function(err) {
res.locals.userMessage = "Error getting block stats"; res.locals.pageErrors.push(utils.logError("983yr435r76d", err));
reject(err); reject(err);
}); });
@ -668,13 +673,15 @@ router.get("/block-height/:blockHeight", function(req, res, next) {
next(); next();
}).catch(function(err) { }).catch(function(err) {
res.locals.pageErrors.push(utils.logError("3249y2ewgfee", err)); res.locals.userMessageMarkdown = `Failed loading block: height=**${blockHeight}**`;
res.render("block"); res.render("block");
next(); next();
}); });
}).catch(function(err) { }).catch(function(err) {
res.locals.userMessageMarkdown = `Failed loading block: height=**${blockHeight}**`;
res.locals.pageErrors.push(utils.logError("389wer07eghdd", err)); res.locals.pageErrors.push(utils.logError("389wer07eghdd", err));
res.render("block"); res.render("block");
@ -723,8 +730,8 @@ router.get("/block/:blockHash", function(req, res, next) {
resolve(); resolve();
}).catch(function(err) { }).catch(function(err) {
res.locals.userMessage = "Error getting block data"; res.locals.pageErrors.push(utils.logError("238h38sse", err));
reject(err); reject(err);
}); });
})); }));
@ -736,8 +743,8 @@ router.get("/block/:blockHash", function(req, res, next) {
resolve(); resolve();
}).catch(function(err) { }).catch(function(err) {
res.locals.userMessage = "Error getting block stats"; res.locals.pageErrors.push(utils.logError("21983ue8hye", err));
reject(err); reject(err);
}); });
})); }));
@ -748,7 +755,7 @@ router.get("/block/:blockHash", function(req, res, next) {
next(); next();
}).catch(function(err) { }).catch(function(err) {
res.locals.pageErrors.push(utils.logError("3217wfeghy9sdgs", err)); res.locals.userMessageMarkdown = `Failed to load block: **${blockHash}**`;
res.render("block"); res.render("block");
@ -867,7 +874,7 @@ router.get("/tx/:transactionId", function(req, res, next) {
}); });
}).catch(function(err) { }).catch(function(err) {
res.locals.userMessage = "Failed to load transaction with txid=" + txid + ": " + err; res.locals.userMessageMarkdown = `Failed to load transaction: txid=**${txid}**`;
res.locals.pageErrors.push(utils.logError("1237y4ewssgt", err)); res.locals.pageErrors.push(utils.logError("1237y4ewssgt", err));
@ -1152,7 +1159,7 @@ router.get("/address/:address", function(req, res, next) {
}).catch(function(err) { }).catch(function(err) {
res.locals.pageErrors.push(utils.logError("2108hs0gsdfe", err, {address:address})); res.locals.pageErrors.push(utils.logError("2108hs0gsdfe", err, {address:address}));
res.locals.userMessage = "Failed to load address " + address + " (" + err + ")"; res.locals.userMessageMarkdown = `Failed to load address: **${address}**`;
res.render("address"); res.render("address");

2
views/address.pug

@ -6,7 +6,7 @@ block headContent
block content block content
if (result && result.validateaddress) if (result && result.validateaddress)
if (!result.validateaddress.isvalid) if (!result.validateaddress.isvalid)
div.alert.alert-danger.border-danger.mb-huge div.alert.alert-danger.mb-huge
h1.h6.font-weight-bold Invalid Address h1.h6.font-weight-bold Invalid Address
span.text-monospace #{address} span.text-monospace #{address}

18
views/block.pug

@ -1,13 +1,17 @@
extends layout extends layout
block headContent block headContent
title Block ##{result.getblock.height.toLocaleString()}, #{result.getblock.hash} if (result.getblock)
title Block ##{result.getblock.height.toLocaleString()}, #{result.getblock.hash}
else
title Block: Error
block content block content
h1.h3 Block if (result.getblock)
small.text-monospace(style="width: 100%;") ##{result.getblock.height.toLocaleString()} h1.h3 Block
br small.text-monospace(style="width: 100%;") ##{result.getblock.height.toLocaleString()}
small.text-monospace.word-wrap(style="width: 100%;") #{result.getblock.hash} br
hr small.text-monospace.word-wrap(style="width: 100%;") #{result.getblock.hash}
hr
include includes/block-content.pug include includes/block-content.pug

47
views/includes/page-errors-modal.pug

@ -8,27 +8,38 @@ div.modal.fade(id="pageErrorsModal" role="dialog" aria-hidden="true")
span(aria-hidden="true") × span(aria-hidden="true") ×
div.modal-body div.modal-body
each item, itemIndex in pageErrors if (false)
div.card.shadow-sm(class=(itemIndex < (pageErrors.length - 1) ? "mb-3" : false)) pre
div.card-header code.json #{JSON.stringify(pageErrors, null, 4)}
h6.mb-0 Error ##{(itemIndex + 1).toLocaleString()}
div.card-body
h6 Error Details
pre
code.json.bg-light #{JSON.stringify(item.error, null, 4)}
if (item.error.stack)
hr
h6 Stacktrace
pre
code.json.bg-light #{item.error.stack}
if (item.error.userData) if (true)
each item, itemIndex in pageErrors
div.card.shadow-sm(class=(itemIndex < (pageErrors.length - 1) ? "mb-3" : false))
div.card-body
h6 Error ##{(itemIndex + 1).toLocaleString()}
hr hr
//pre
// code.json.bg-light #{JSON.stringify(item.error, null, 4)}
//pre
// code.json.bg-light #{JSON.stringify(item, null, 4)}
if (item.error.userData)
div.mb-3
h4.h6 Error Data
div.highlight
pre
code.json.bg-light #{JSON.stringify(item.error.userData, null, 4)}
if (item.error.stack)
h6 Stacktrace
- var stackFirstNLines = item.error.stack.split("\n").slice(0, 7).join("\n");
div.highlight
pre
code.json.bg-light #{stackFirstNLines}
h4.h6 User Data
pre
code.json.bg-light #{JSON.stringify(item.error.userData, null, 4)}
div.modal-footer div.modal-footer
button.btn.btn-secondary(type="button" data-dismiss="modal") Close button.btn.btn-secondary(type="button" data-dismiss="modal") Close

Loading…
Cancel
Save