-
Notifications
You must be signed in to change notification settings - Fork 219
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Description --- Tari scanner app. Start by running `npm start` in `applications/tari_scanner`. Don't forget to run `npm install` first. You have to have a base node running.
- Loading branch information
Showing
27 changed files
with
4,492 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright 2022 The Tari Project | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
|
||
var createError = require("http-errors"); | ||
var express = require("express"); | ||
var path = require("path"); | ||
var cookieParser = require("cookie-parser"); | ||
var logger = require("morgan"); | ||
|
||
var indexRouter = require("./routes/index"); | ||
var contractRouter = require("./routes/contract"); | ||
var validatorRouter = require("./routes/validatorNode"); | ||
var dataRouter = require("./routes/data"); | ||
var livereload = require("livereload"); | ||
var connectLiveReload = require("connect-livereload"); | ||
const liveReloadServer = livereload.createServer(); | ||
liveReloadServer.server.once("connection", () => { | ||
setTimeout(() => { | ||
liveReloadServer.refresh("/"); | ||
}, 100); | ||
}); | ||
|
||
var hbs = require("hbs"); | ||
const { title } = require("process"); | ||
hbs.registerHelper("hex", function (buffer) { | ||
return buffer ? Buffer.from(buffer).toString("hex") : ""; | ||
}); | ||
hbs.registerHelper("concat", function (string1, string2) { | ||
return string1 + string2; | ||
}); | ||
hbs.registerHelper("times", function (n, block) { | ||
var accum = ""; | ||
for (var i = 0; i < n; ++i) accum += block.fn({ index: i, render: `render-${i}` }); | ||
return accum; | ||
}); | ||
hbs.registerHelper("table", function (block) { | ||
let { id, endpoint, rows } = block.hash; | ||
let table_navigation = `<div class="navigation"><span id="${id}Prev" class="prev clickable">< Prev</span><span id="${id}Pages" class="pages"></span><span id="${id}Next" class="next clickable">Next ></span></div>`; | ||
let header = block.fn(block.hash); | ||
let cols = (header.match(/<\/th>/g) || []).length; | ||
let row_data = ""; | ||
for (let i = 0; i < rows; ++i) { | ||
row_data += "<tr>"; | ||
for (let j = 0; j < cols; ++j) { | ||
row_data += `<td id="${id}-td-${i}-${j}"></td>`; | ||
} | ||
row_data += "</tr>"; | ||
} | ||
return `<table id="${id}-table" endpoint="${endpoint}" cols=${cols} rows=${rows}><thead><tr>${header}</tr></thead><tbody id="${id}Body">${row_data}</tbody></table>${table_navigation}`; | ||
}); | ||
hbs.registerPartials(path.join(__dirname, "components")); | ||
// hbs.registerHelper("sort", function (list, )) | ||
|
||
var app = express(); | ||
app.use(connectLiveReload()); | ||
|
||
// view engine setup | ||
app.set("views", path.join(__dirname, "views")); | ||
app.set("view engine", "hbs"); | ||
|
||
app.use(logger("dev")); | ||
app.use(express.json()); | ||
app.use(express.urlencoded({ extended: false })); | ||
app.use(cookieParser()); | ||
app.use(express.static(path.join(__dirname, "public"))); | ||
|
||
app.use("/", indexRouter); | ||
app.use("/validator_node", validatorRouter); | ||
app.use("/contract", contractRouter); | ||
app.use("/data", dataRouter); | ||
|
||
// catch 404 and forward to error handler | ||
app.use(function (req, res, next) { | ||
next(createError(404)); | ||
}); | ||
|
||
// error handler | ||
app.use(function (err, req, res, next) { | ||
// set locals, only providing error in development | ||
res.locals.message = err.message; | ||
res.locals.error = req.app.get("env") === "development" ? err : {}; | ||
|
||
// render the error page | ||
res.status(err.status || 500); | ||
res.render("error"); | ||
}); | ||
|
||
module.exports = app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
// Copyright 2022 The Tari Project | ||
// SPDX-License-Identifier: BSD-3-Clause | ||
|
||
var { Client } = require("@tari/base-node-grpc-client"); | ||
const { contracts } = require("./helpers/contracts"); | ||
const { validator_nodes } = require("./helpers/validatorNodes"); | ||
var { range } = require("./utils"); | ||
|
||
class ExpressClient { | ||
constructor() { | ||
this.client = Client.connect("localhost:18142"); | ||
this.blocks = {}; | ||
this.validator_nodes = validator_nodes; | ||
this.contracts = contracts; | ||
} | ||
|
||
getTip() { | ||
return this.client.getTipInfo().then((tip_info) => parseInt(tip_info.metadata.height_of_longest_chain)); | ||
} | ||
|
||
#getBlocksArray(from, to) { | ||
let result = []; | ||
for (var i = from; i < to; ++i) { | ||
if (i in this.blocks) { | ||
result.push(this.blocks[i]); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
getBlocks(from, to, refresh = false) { | ||
let heights = range(from, to); | ||
if (!refresh) { | ||
heights = heights.filter((height) => !(height in this.blocks)); | ||
} | ||
if (heights.length) { | ||
return this.client.getBlocks({ heights: heights }).then((blocks) => { | ||
for (const block of blocks) { | ||
const height = parseInt(block.block.header.height); | ||
this.blocks[height] = block.block; | ||
} | ||
return this.#getBlocksArray(from, to); | ||
}); | ||
} else { | ||
return this.#getBlocksArray(from, to); | ||
} | ||
} | ||
|
||
#updateFromContractDefinition(sidechain_features, height) { | ||
let contract_id = sidechain_features.contract_id.toString("hex"); | ||
let contract = contracts.addContract(contract_id, height); | ||
contract.name = sidechain_features.definition.contract_name.toString(); | ||
contract.issuer = sidechain_features.definition.contract_issuer.toString("hex"); | ||
} | ||
|
||
#updateFromContractConstitution(sidechain_features) { | ||
let contract_id = sidechain_features.contract_id.toString("hex"); | ||
for (let member of sidechain_features.constitution.validator_committee.members) { | ||
member = member.toString("hex"); | ||
validator_nodes.addValidatorNode(member).addContract(contract_id); | ||
contracts.addContract(contract_id).addToCommittee(member); | ||
} | ||
} | ||
|
||
#updateFromContractValidatorAcceptance(sidechain_features, height) { | ||
let contract_id = sidechain_features.contract_id.toString("hex"); | ||
let member = sidechain_features.acceptance.validator_node_public_key.toString("hex"); | ||
validator_nodes.addValidatorNode(member).addContract(contract_id, height); | ||
contracts.addContract(contract_id).addToCommittee(member); | ||
} | ||
|
||
#updateFromContractCheckpoint(sidechain_features, height) { | ||
let contract_id = sidechain_features.contract_id.toString("hex"); | ||
let signatures = sidechain_features?.checkpoint?.signatures?.signatures; | ||
if (contract_id != "b5ad8929ca1026d7411633f7cde8955c1150f5747660d635c85a3f684ebd47c5") | ||
signatures = [{ public_nonce: "5c50fcb6b0966e595c3c9b121443989ec78c6d3ac45ba98edd1dcc5d39c2f665" }]; | ||
if (signatures) { | ||
let validators = signatures.map((signature) => signature.public_nonce); | ||
for (const validator of validators) { | ||
validator_nodes.addValidatorNode(validator).addCheckpoint(contract_id, height); | ||
} | ||
contracts.addContract(contract_id).addCheckpoint(height, validators); | ||
} | ||
} | ||
|
||
#updateFromContractConstitutionProposal(sidechain_features) { | ||
let contract_id = sidechain_features.contract_id.toString("hex"); | ||
for (let member of sidechain_features.update_proposal.updated_constitution.validator_committee.members) { | ||
member = member.toString("hex"); | ||
validator_nodes.addValidatorNode(member).addContract(contract_id); | ||
contracts.addContract(contract_id).addToCommittee(member); | ||
} | ||
} | ||
|
||
#updateFromContractConstitutionChangeAcceptance(sidechain_features) { | ||
console.log("updateFromContractConstitutionChangeAcceptance"); | ||
} | ||
|
||
async updateAllValidatorNodes() { | ||
let tip = await this.getTip(); | ||
let blocks = await this.getBlocks(1, tip + 1); | ||
for (const block of blocks) { | ||
let height = block.header.height; | ||
for (const output of block.body.outputs) { | ||
switch (output.features.output_type) { | ||
case 2: // CONTRACT_DEFINITION | ||
this.#updateFromContractDefinition(output.features.sidechain_features, height); | ||
break; | ||
case 3: // CONTRACT_CONSTITUTION | ||
this.#updateFromContractConstitution(output.features.sidechain_features); | ||
break; | ||
case 4: // CONTRACT_VALIDATOR_ACCEPTANCE | ||
this.#updateFromContractValidatorAcceptance(output.features.sidechain_features, height); | ||
break; | ||
case 5: // CONTRACT_CHECKPOINT | ||
this.#updateFromContractCheckpoint(output.features.sidechain_features, height); | ||
break; | ||
case 6: // CONTRACT_CONSTITUTION_PROPOSAL | ||
this.#updateFromContractConstitutionProposal(output.features.sidechain_features); | ||
break; | ||
case 7: // CONTRACT_CONSTITUTION_CHANGE_ACCEPTANCE | ||
this.#updateFromContractConstitutionChangeAcceptance(output.features.sidechain_features); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
|
||
async getAllContracts() { | ||
await this.updateAllValidatorNodes(); | ||
return this.contracts.getAllIDs(); | ||
} | ||
|
||
async getAllValidatorNodes() { | ||
await this.updateAllValidatorNodes(); | ||
return this.validator_nodes.getAllIDs(); | ||
} | ||
|
||
async getValidatorNode(id) { | ||
await this.updateAllValidatorNodes(); | ||
return this.validator_nodes.getValidatorNode(id); | ||
} | ||
|
||
async getContract(id) { | ||
await this.updateAllValidatorNodes(); | ||
return this.contracts.getContract(id); | ||
} | ||
} | ||
|
||
client = new ExpressClient(); | ||
|
||
module.exports = { | ||
client, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#!/usr/bin/env node | ||
|
||
/** | ||
* Module dependencies. | ||
*/ | ||
|
||
var app = require('../app'); | ||
var debug = require('debug')('tari-scanner:server'); | ||
var http = require('http'); | ||
|
||
/** | ||
* Get port from environment and store in Express. | ||
*/ | ||
|
||
var port = normalizePort(process.env.PORT || '3000'); | ||
app.set('port', port); | ||
|
||
/** | ||
* Create HTTP server. | ||
*/ | ||
|
||
var server = http.createServer(app); | ||
|
||
/** | ||
* Listen on provided port, on all network interfaces. | ||
*/ | ||
|
||
server.listen(port); | ||
server.on('error', onError); | ||
server.on('listening', onListening); | ||
|
||
/** | ||
* Normalize a port into a number, string, or false. | ||
*/ | ||
|
||
function normalizePort(val) { | ||
var port = parseInt(val, 10); | ||
|
||
if (isNaN(port)) { | ||
// named pipe | ||
return val; | ||
} | ||
|
||
if (port >= 0) { | ||
// port number | ||
return port; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Event listener for HTTP server "error" event. | ||
*/ | ||
|
||
function onError(error) { | ||
if (error.syscall !== 'listen') { | ||
throw error; | ||
} | ||
|
||
var bind = typeof port === 'string' | ||
? 'Pipe ' + port | ||
: 'Port ' + port; | ||
|
||
// handle specific listen errors with friendly messages | ||
switch (error.code) { | ||
case 'EACCES': | ||
console.error(bind + ' requires elevated privileges'); | ||
process.exit(1); | ||
break; | ||
case 'EADDRINUSE': | ||
console.error(bind + ' is already in use'); | ||
process.exit(1); | ||
break; | ||
default: | ||
throw error; | ||
} | ||
} | ||
|
||
/** | ||
* Event listener for HTTP server "listening" event. | ||
*/ | ||
|
||
function onListening() { | ||
var addr = server.address(); | ||
var bind = typeof addr === 'string' | ||
? 'pipe ' + addr | ||
: 'port ' + addr.port; | ||
debug('Listening on ' + bind); | ||
} |
Oops, something went wrong.