aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkaotisk <kaotisk@arching-kaos.org>2023-03-30 01:09:30 +0300
committerkaotisk <kaotisk@arching-kaos.org>2023-03-30 01:09:30 +0300
commite2da6d2db20093ebd2a65aad35c9991ab1a02176 (patch)
treee5c77a8f4a6fd106b13b659e248cab5768d07cdc
parenta4901ad47d2945e9a6c6616661840c97ebbf03e7 (diff)
downloadarching-kaos-tools-e2da6d2db20093ebd2a65aad35c9991ab1a02176.tar.gz
arching-kaos-tools-e2da6d2db20093ebd2a65aad35c9991ab1a02176.tar.bz2
arching-kaos-tools-e2da6d2db20093ebd2a65aad35c9991ab1a02176.zip
Introducing an HTTP JSON API
-rw-r--r--api/config.js24
-rwxr-xr-xapi/index.js226
-rw-r--r--api/package.json20
-rw-r--r--api/routes/default/index.js22
-rw-r--r--api/routes/getSBlock/index.js43
-rw-r--r--api/routes/getSLatest/index.js29
-rw-r--r--api/routes/getZChain/index.js29
-rw-r--r--api/routes/getZLatest/index.js28
-rw-r--r--api/routes/index.js43
-rw-r--r--api/routes/receiveZBlock/index.js28
-rw-r--r--api/routes/receiveZChain/index.js27
-rw-r--r--api/routes/showEntriesFile/index.js14
-rw-r--r--api/routes/showNSEntriesFile/index.js15
-rw-r--r--api/settings/index.js6
-rw-r--r--api/tests/default_route.sh2
-rwxr-xr-xapi/tests/gathered_zblocks.sh23
-rwxr-xr-xapi/tests/gathered_zchain_zlatest_pairs.sh10
-rwxr-xr-xapi/tests/latest_known_mined_block.sh13
-rwxr-xr-xapi/tests/node_local_chain.sh12
-rwxr-xr-xapi/tests/node_local_zlatest.sh12
-rwxr-xr-xapi/tests/send_me_a_zblock.sh3
-rwxr-xr-xapi/tests/send_me_a_zchain_link.sh3
-rwxr-xr-xapi/tests/show_mined_block.sh3
-rwxr-xr-xapi/tests/test_all_routes.sh9
-rw-r--r--api/validators/ZblockValidator/index.js29
25 files changed, 673 insertions, 0 deletions
diff --git a/api/config.js b/api/config.js
new file mode 100644
index 0000000..b4d40cb
--- /dev/null
+++ b/api/config.js
@@ -0,0 +1,24 @@
+const process = require("process");
+const env = process.env;
+
+module.exports = {
+ port : 8610,
+ session : {
+ secret:'setabigsecrethere'
+ },
+ zblockDir : env.AK_ZBLOCKDIR,
+ zchain:env.AK_ZCHAIN,
+ zchainasc:env.AK_ZCHAINASC,
+ zgenesis:env.AK_ZGENESIS,
+ zgenesisasc:env.AK_ZGENESISASC,
+ zlatest:env.AK_ZLATEST,
+ zlist:env.AK_ZLIST,
+ zzchain:env.AK_ZZCHAIN,
+ binDir:env.AK_BINDIR,
+ workDir:env.AK_WORKDIR,
+ blocksFile : this.workDir+"/zBlocksFile",
+ pairsFile : this.workDir+"/pairsFile",
+ minedBlocksDir: env.AK_MINEDBLOCKSDIR
+
+}
+
diff --git a/api/index.js b/api/index.js
new file mode 100755
index 0000000..486cff8
--- /dev/null
+++ b/api/index.js
@@ -0,0 +1,226 @@
+#!/usr/bin/env node
+/*
+ * Quick API
+ * Author: Kaotisk Hund <kaotisk@arching-kaos.com>
+ * Description: Provides a quick API implementation.
+ * License: MIT
+ */
+
+/*
+ * Loading configuration
+ *
+ */
+const config = require('./config');
+
+/*
+ * PORT we run the service on.
+ *
+ * Default: 8610
+ *
+ */
+const DEFAULT_PORT = 8610;
+const PORT = config.port || DEFAULT_PORT;
+
+/*
+ * Split the prefix of each API call in segments for better management
+ *
+ * To add a new route, use URL_PREFIX and start your route with '/'
+ *
+ * LOCAL_IP and DEF_PROTO as well as PORT and URL_PREFIX are used to generate
+ * visitable links.
+ *
+ */
+const API_VERSION = "v0";
+const URL_PREFIX = "/"+API_VERSION;
+const DEF_PROTO="http://";
+const LOCAL_IP="127.0.0.1";
+
+/* Requiring packages */
+const { spawn } = require('child_process');
+const logger = require('morgan');
+const express = require('express');
+const cors = require('cors');
+const fs = require('fs');
+const path = require('path');
+const bodyParser = require('body-parser');
+const multer = require('multer');
+const upload = multer();
+const session = require('express-session');
+// Delete this (maybe)
+const cookieParser = require('cookie-parser');
+
+// We start our server ...
+const app = express();
+
+// Set logger format output
+app.use(logger('combined'));
+
+// Port number to listen to
+const server = app.listen(PORT);
+
+// Use CORS
+app.use(cors());
+
+// Whitelist of IPs
+// var iplist = fs.readFileSync(config.ipList)
+
+// Parsers
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({extended:true}));
+
+// for parsing multipart/form-data
+app.use(upload.array());
+
+// Cookie!
+app.use(cookieParser());
+
+app.use(session({
+ secret: config.session.secret,
+ resave: false,
+ saveUninitialized: true
+}));
+
+// Function for adding stuff...
+function genericaddtest(req,res){
+ console.table(req.body)
+ var myobj = req.body;
+ res.send(myobj);
+}
+// POST data
+app.post('/test', cors(corsOptions), genericaddtest);
+
+const routes = require('./routes');
+
+//Routes.provideTheAppHere(app);
+app.use('/', routes);
+
+
+
+
+
+/*
+ * After NS validation went through we examine the return code
+ * of the application that run the test.
+ *
+ * Returns:
+ * - errno on failure
+ * - on success we process with addNSEntriesToFile()
+ *
+ */
+function continuethingsNS(validitycode,sh,res,gotit){
+ if (validitycode === 0){
+ var entry = {
+ zchain: sh,
+ latest: JSON.parse(gotit).Path.replace('/ipfs/','')
+ };
+ addNSEntriesToFile(entry,res);
+ } else {
+ res.send({errno:"Invalid data"});
+ }
+}
+
+/*
+ * After validation went through we examine the return code
+ * of the application that run the test.
+ *
+ * Returns :
+ * - errno on failure
+ * - on success we process with addEntriesToFile()
+ *
+ */
+function continuethings(validitycode,sh,res){
+ if (validitycode === 0){
+ var entry = {zblock:sh};
+ addEntriesToFile(entry,res);
+ } else {
+ res.send({errno:"Invalid data"});
+ }
+}
+
+
+
+/*
+ * Adds a latest resolved IPFS path for a given IPNS link
+ * to a file.
+ *
+ * Returns:
+ * - error if error
+ * - success:
+ * - adds the entry to the file
+ * - returns the whole file to the client
+ *
+ */
+function addNSEntriesToFile(entry,res){
+ var data = JSON.parse(fs.readFileSync(config.pairsFile));
+ var duplicate_entry = 0;
+ data.forEach(a=>{
+ if ( a.zchain === entry.zchain && a.latest=== entry.latest ){
+ duplicate_entry = 1;
+ res.send({errno:"already there"});
+ }
+ });
+
+ if ( duplicate_entry === 0 ) {
+ // Store it as the first array element of our new list
+ var all = [entry];
+
+ // Append the previous entries
+ for (var i = 0; i < data.length; i++){
+ all[i+1] = data[i];
+ }
+
+ // Turn additional back into text
+ var json = JSON.stringify(all);
+
+ // Write out the file
+ fs.writeFile(config.pairsFile, json, 'utf8', finished);
+
+ // Callback for when file is finished
+ function finished(err) {
+ console.log('finished writing file');
+ }
+ res.send(json);
+ }
+}
+
+/*
+ * Adds a latest given zblock to a file.
+ *
+ * Returns:
+ * - error if error
+ * - success:
+ * - adds the entry to the file
+ * - returns the whole file to the client
+ *
+ */
+function addEntriesToFile(entry,res){
+ var data = JSON.parse(fs.readFileSync(config.blocksFile));
+
+ var duplicate_entry = 0;
+ data.forEach(a=>{
+ if ( a.zblock === entry.zblock ){
+ duplicate_entry = 1;
+ res.send({errno:"already there"});
+ }
+ });
+
+ if ( duplicate_entry === 0 ) {
+ var all = [entry];
+ for (var i = 0; i < data.length; i++){
+ all[i+1] = data[i];
+ }
+ var json = JSON.stringify(all);
+ fs.writeFile(config.blocksFile, json, 'utf8', finished);
+ function finished(err) {
+ console.log('finished writing file');
+ }
+ res.send(json);
+ }
+}
+
+
+app.use(cors);
+var corsOptions = {
+ origin: '*',
+ optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
+}
diff --git a/api/package.json b/api/package.json
new file mode 100644
index 0000000..23833e9
--- /dev/null
+++ b/api/package.json
@@ -0,0 +1,20 @@
+{
+ "name": "qapi",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "cookie-parser": "^1.4.6",
+ "cors": "^2.8.5",
+ "express": "^4.18.1",
+ "express-cors": "^0.0.3",
+ "express-session": "^1.17.3",
+ "morgan": "^1.10.0",
+ "multer": "^1.4.5-lts.1"
+ }
+}
diff --git a/api/routes/default/index.js b/api/routes/default/index.js
new file mode 100644
index 0000000..e9be5cb
--- /dev/null
+++ b/api/routes/default/index.js
@@ -0,0 +1,22 @@
+const settings = require('../../settings');
+module.exports = (req, res) => {
+ res.send({
+ message:"Hello! Welcome to Arching Kaos API! See available routes below!",
+ routes:{
+ GET:[
+ {welcome:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+"/"},
+ {node_local_chain:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+settings.URL_PREFIX+"/chain"},
+ {node_local_zlatest:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+settings.URL_PREFIX+"/zlatest"},
+ {gathered_zblocks:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+settings.URL_PREFIX+"/see"},
+ {gathered_zchain_zlatest_pairs:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+settings.URL_PREFIX+"/seens"},
+ {latest_known_mined_block:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+settings.URL_PREFIX+"/slatest"},
+ {show_mined_block:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+settings.URL_PREFIX+"/sblock"}
+ ],
+ POST:[
+ {send_me_a_zchain_link:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+settings.URL_PREFIX+"/szch"},
+ {send_me_a_zblock:settings.DEF_PROTO+settings.LOCAL_IP+":"+settings.PORT+settings.URL_PREFIX+"/sblk"},
+ ]
+ }
+ });
+}
+
diff --git a/api/routes/getSBlock/index.js b/api/routes/getSBlock/index.js
new file mode 100644
index 0000000..4ad9127
--- /dev/null
+++ b/api/routes/getSBlock/index.js
@@ -0,0 +1,43 @@
+const { spawn } = require('child_process');
+const config = require("../../config.js");
+
+
+/*
+ * Gets a SBLOCK from superchain
+ * LOL
+ *
+ */
+module.exports = (req, res) => {
+ regex= /[a-f0-9]{128}/
+ if (regex.test(req.query.sblock)){
+ genesisreg = /0{128}/
+ if (!genesisreg.test(req.query.sblock)){
+ const command = spawn("cat",[config.minedBlocksDir+req.query.sblock]);
+ response_string = "";
+ command.stdout.on("data", data => {
+ response_string = response_string+data;
+ });
+
+ command.stderr.on("data", data => {
+ console.log(`stderr: ${data}`);
+ });
+
+ command.on('error', (error) => {
+ console.log(`error: ${error.message}`);
+ response_string={err:"error.message"};
+ });
+
+ command.on("close", code => {
+ smt = JSON.stringify(response_string);
+ res.send({hrefPrevious:"http://127.0.0.1:8610/v0/sblock?sblock="+smt.previous,sblock:smt});
+ // res.send(JSON.parse(response_string));
+ console.log(`child process exited with code ${code}`);
+ });
+ } else {
+ res.send({sblock:"Genesis Block - Arching Kaos Net"});
+ }
+ } else {
+ res.send({"error":"No hash"})
+ }
+}
+
diff --git a/api/routes/getSLatest/index.js b/api/routes/getSLatest/index.js
new file mode 100644
index 0000000..ea6f30c
--- /dev/null
+++ b/api/routes/getSLatest/index.js
@@ -0,0 +1,29 @@
+const { spawn } = require('child_process');
+const os = require("os");
+const HomeDir = os.userInfo().homedir;
+
+/*
+ * Gets the latest SBLOCK from superchain
+ * LOL
+ * sorry I was laughing at the term.. superchain
+ */
+module.exports = (req, res) => {
+ const command = spawn("ak-find-latest-mined-block.sh");
+ response_string = "";
+ command.stdout.on("data", data => {
+ response_string = response_string + data;
+ });
+
+ command.stderr.on("data", data => {
+ console.log(`stderr: ${data}`);
+ });
+
+ command.on('error', (error) => {
+ console.log(`error: ${error.message}`);
+ });
+
+ command.on("close", code => {
+ res.send(JSON.parse(response_string));
+ console.log(`child process exited with code ${code}`);
+ });
+}
diff --git a/api/routes/getZChain/index.js b/api/routes/getZChain/index.js
new file mode 100644
index 0000000..6aaa5d9
--- /dev/null
+++ b/api/routes/getZChain/index.js
@@ -0,0 +1,29 @@
+const { spawn } = require('child_process');
+
+/*
+ * Gets the local chain as minified version
+ *
+ * Returns:
+ * - A JSON array representing the nodes' arching-kaos-zchain
+ *
+ */
+module.exports = (req, res) => {
+ const command = spawn("ak-get-chain-minified");
+ response_string = "";
+ command.stdout.on("data", data => {
+ response_string = response_string + data;
+ });
+
+ command.stderr.on("data", data => {
+ console.log(`stderr: ${data}`);
+ });
+
+ command.on('error', (error) => {
+ console.log(`error: ${error.message}`);
+ });
+
+ command.on("close", code => {
+ res.send(JSON.parse(response_string)/*.reverse()*/);
+ console.log(`child process exited with code ${code}`);
+ });
+};
diff --git a/api/routes/getZLatest/index.js b/api/routes/getZLatest/index.js
new file mode 100644
index 0000000..3eeca81
--- /dev/null
+++ b/api/routes/getZLatest/index.js
@@ -0,0 +1,28 @@
+const { spawn } = require('child_process');
+
+/*
+ * Gets the local latest zblock
+ *
+ * Returns:
+ * - JSON object
+ * { zlatest: "Qm..." }
+ *
+ */
+module.exports = (req, res) => {
+ const command = spawn("ak-get-latest");
+ command.stdout.on("data", data => {
+ res.send({zlatest:`${data}`});
+ });
+
+ command.stderr.on("data", data => {
+ console.log(`stderr: ${data}`);
+ });
+
+ command.on('error', (error) => {
+ console.log(`error: ${error.message}`);
+ });
+
+ command.on("close", code => {
+ console.log(`child process exited with code ${code}`);
+ });
+}; \ No newline at end of file
diff --git a/api/routes/index.js b/api/routes/index.js
new file mode 100644
index 0000000..b771758
--- /dev/null
+++ b/api/routes/index.js
@@ -0,0 +1,43 @@
+const settings = require('../settings');
+const {Router} = require('express');
+const cors = require('cors');
+const hi = require('./default');
+const seeNSEntriesFile = require('./showNSEntriesFile');
+const seeEntriesFile = require('./showEntriesFile');
+const getSLatest = require('./getSLatest');
+const getZLatest = require('./getZLatest');
+const getSBlock = require('./getSBlock');
+const getZChain = require('./getZChain');
+const receiveZBlock = require('./receiveZBlock');
+const receiveZChain = require('./receiveZChain');
+const corsOptions = {
+ origin: '*',
+ optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
+};
+const router = new Router();
+// Basic route, welcomes and provides the available routes to the visitor
+router.route('/').get(hi);
+
+/*
+ * Replies with contents of files
+ *
+ */
+// Gathered zchain zlatest pairs
+router.route(settings.URL_PREFIX+'/seens').get(seeNSEntriesFile);
+// Gathered zblocks
+router.route(settings.URL_PREFIX+'/see').get(seeEntriesFile);
+// Latest known mined block
+router.route(settings.URL_PREFIX+'/slatest').get(getSLatest);
+// Shows a mined block (provided that /sblock?sblock=SHA512 hash)
+router.route(settings.URL_PREFIX+'/sblock').get(getSBlock);
+// Outputs node's local chain
+router.route(settings.URL_PREFIX+'/zchain').get(getZChain);
+// Returns latest zblock from node's local chain
+router.route(settings.URL_PREFIX+'/zlatest').get(getZLatest);
+// Send a block to the node (zchain block)
+router.route(settings.URL_PREFIX+'/sblk').post(receiveZBlock);
+// Send a zchain link to the node (refering to a valid zchain out there)
+router.route(settings.URL_PREFIX+'/szch').post(receiveZChain);
+
+router.route('/*').get((req,res)=>{console.log(req.url);res.send({errno:"404"})});
+module.exports = router;
diff --git a/api/routes/receiveZBlock/index.js b/api/routes/receiveZBlock/index.js
new file mode 100644
index 0000000..3ad6100
--- /dev/null
+++ b/api/routes/receiveZBlock/index.js
@@ -0,0 +1,28 @@
+/*
+ * Accepts a ZBLOCK!
+ *
+ * Checks:
+ * 1. Exists,
+ * 2. Length is 46 bytes,
+ * 3. Matches regular expression /Qm[A-Za-z0-9]{44}/
+ *
+ * Returns:
+ * - errno on failure
+ * - on success the string is processed for further
+ * validation to the function getvalidity()
+ *
+ */
+const getvalidity = require('../../validators/ZblockValidator')
+module.exports = (req, res) => {
+ console.log("okay we got called")
+ if ( (req.body.zblock) && req.body.zblock.length === 46 ){
+ regex= /Qm[A-Za-z0-9]{44}/;
+ if (regex.test(req.body.zblock)){
+ getvalidity(req.body.zblock,res);
+ } else {
+ res.send({errno:"Invalid data"});
+ }
+ } else {
+ res.send({errno:"Invalid data"});
+ }
+}
diff --git a/api/routes/receiveZChain/index.js b/api/routes/receiveZChain/index.js
new file mode 100644
index 0000000..85d1854
--- /dev/null
+++ b/api/routes/receiveZChain/index.js
@@ -0,0 +1,27 @@
+/*
+ * Accepts a zchain
+ *
+ * Checks:
+ * 1. Exists,
+ * 2. Length is 62 bytes,
+ * 3. Matches regular expression /k51qzi5uqu5d[A-Za-z0-9]{50}/
+ *
+ * Returns:
+ * - errno on failure
+ * - on success the string is processed for further validation to the
+ * function getNSvalidity()
+ *
+ */
+
+module.exports = (req, res) => {
+ if ( (req.body.zchain) && req.body.zchain.length === 62 ){//&& req.body.block_signature.length === 46){
+ regex= /k51qzi5uqu5d[A-Za-z0-9]{50}/
+ if (regex.test(req.body.zchain)){ // && regex.test(req.body.block_signature)){
+ getNSvalidity(req.body.zchain,res);
+ } else {
+ res.send({errno:"Invalid data"});
+ }
+ } else {
+ res.send({errno:"Invalid data"});
+ }
+}
diff --git a/api/routes/showEntriesFile/index.js b/api/routes/showEntriesFile/index.js
new file mode 100644
index 0000000..3dd1c40
--- /dev/null
+++ b/api/routes/showEntriesFile/index.js
@@ -0,0 +1,14 @@
+const fs = require('fs');
+const config = require('../../config');
+
+/*
+ * Reads ./lol file where the zblocks are saved
+ *
+ * Returns:
+ * - the file, it's already in JSON format.
+ *
+ */
+module.exports = (req, res) => {
+ var data = JSON.parse(fs.readFileSync(config.blocksFile));
+ res.send(data);
+}; \ No newline at end of file
diff --git a/api/routes/showNSEntriesFile/index.js b/api/routes/showNSEntriesFile/index.js
new file mode 100644
index 0000000..745ddee
--- /dev/null
+++ b/api/routes/showNSEntriesFile/index.js
@@ -0,0 +1,15 @@
+const fs = require('fs');
+const config = require('../../config');
+
+/*
+ * Reads ./szch file where the zchains with their
+ * zlatest's are saved.
+ *
+ * Returns:
+ * - the file, it's already in JSON format.
+ *
+ */
+module.exports = (req, res) => {
+ var data = JSON.parse(fs.readFileSync(config.pairsFile));
+ res.send(data);
+}; \ No newline at end of file
diff --git a/api/settings/index.js b/api/settings/index.js
new file mode 100644
index 0000000..f71a66c
--- /dev/null
+++ b/api/settings/index.js
@@ -0,0 +1,6 @@
+module.exports = {
+ PORT: 8610,
+ URL_PREFIX : "/v0",
+ DEF_PROTO : "http://",
+ LOCAL_IP : "127.0.0.1"
+}
diff --git a/api/tests/default_route.sh b/api/tests/default_route.sh
new file mode 100644
index 0000000..8e73ae6
--- /dev/null
+++ b/api/tests/default_route.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+curl http://127.0.0.1:8610
diff --git a/api/tests/gathered_zblocks.sh b/api/tests/gathered_zblocks.sh
new file mode 100755
index 0000000..cbe2c9a
--- /dev/null
+++ b/api/tests/gathered_zblocks.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+
+printf "TEST /see\n"
+printf "\t01:\tOutput is JSON"
+curl http://127.0.0.1:8610/v0/see 2>/dev/null | jq > /dev/null
+if [ "$?" == "0" ]
+then
+ printf "\n\t\t\033[0;32mPASSED\033[0;0m"
+else
+ printf "\t\033[0;31mFAILED\033[0;0m"
+fi
+printf "\n"
+
+printf "\t02:\tFound blocks inside response"
+curl http://127.0.0.1:8610/v0/see 2>/dev/null | grep block > /dev/null
+if [ "$?" == "0" ]
+then
+ printf "\n\t\t\033[0;32mPASSED\033[0;0m"
+else
+ printf "\n\t\t\033[0;31mFAILED\033[0;0m"
+fi
+printf "\n"
diff --git a/api/tests/gathered_zchain_zlatest_pairs.sh b/api/tests/gathered_zchain_zlatest_pairs.sh
new file mode 100755
index 0000000..466c669
--- /dev/null
+++ b/api/tests/gathered_zchain_zlatest_pairs.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+printf "TEST\t/seens\n"
+printf "\t01:\tendpoint returns JSON...\n"
+curl http://127.0.0.1:8610/v0/seens 2>/dev/null | jq > /dev/null
+if [ "$?" == "0" ]
+then
+ printf '\t\t\033[0;32mPASSED\033[0;0m\n'
+else
+ printf '\t\t\033[0;31mFAILED\033[0;0m\n'
+fi
diff --git a/api/tests/latest_known_mined_block.sh b/api/tests/latest_known_mined_block.sh
new file mode 100755
index 0000000..b26e6f3
--- /dev/null
+++ b/api/tests/latest_known_mined_block.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+#
+printf "TEST\t/slatest\n"
+printf "\t01:\tendpoint\n"
+A="$(curl http://127.0.0.1:8610/v0/slatest 2>/dev/null)"
+B="$(bash /home/$USER/projects/arching-kaos-tools/find-latest-mined-block.sh)"
+if [ "$A" == "$B" ]
+then
+ printf '\t\t\033[0;32mPASSED\033[0;0m'
+else
+ printf '\t\t\033[0;31mFAILED\033[0;0m'
+fi
+printf "\n"
diff --git a/api/tests/node_local_chain.sh b/api/tests/node_local_chain.sh
new file mode 100755
index 0000000..06b08cc
--- /dev/null
+++ b/api/tests/node_local_chain.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+printf "\nTEST /zchain\n"
+printf "\t01:\tComparing API with CLI response..."
+API_RESPONSE="$(curl http://127.0.0.1:8610/v0/zchain 2>/dev/null | sha512sum - | awk '{print $1 }')"
+CMD_RESPONSE="$(get-chain-min | sha512sum - | awk '{ print $1 }')"
+printf "api: %s\nenter: %s\n" $API_RESPONSE $CMD_RESPONSE
+if [ "$API_RESPONSE" == "$CMD_RESPONSE" ]
+then
+ printf "\t\t\033[0;32mPASSED\033[0;0m\n"
+else
+ printf "\t\t\033[0;31mFAILED\033[0;0m\n"
+fi
diff --git a/api/tests/node_local_zlatest.sh b/api/tests/node_local_zlatest.sh
new file mode 100755
index 0000000..4f37280
--- /dev/null
+++ b/api/tests/node_local_zlatest.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+API_RES="$(curl http://127.0.0.1:8610/v0/zlatest 2>/dev/null | sha512sum - | awk '{ printf $1 }')"
+CMD_RES="$(get-latest | sed -e 's/^/{"zlatest":"/; s/$/"}/' | sha512sum - | awk '{ printf $1 }')"
+printf "TEST /zlatest\n"
+printf "\t01:\tLatest is the same between API response and CLI..."
+if [ "$API_RES" == "$CMD_RES" ]
+then
+ printf "\n\t\t\033[0;32mPASSED\033[0;0m"
+else
+ printf "\n\t\t\033[0;31mFAILED\033[0;0m"
+fi
+printf "\n"
diff --git a/api/tests/send_me_a_zblock.sh b/api/tests/send_me_a_zblock.sh
new file mode 100755
index 0000000..7fd814e
--- /dev/null
+++ b/api/tests/send_me_a_zblock.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+printf "\n#TODO Send me a block /sblk endpoint\n"
+# curl http://127.0.0.1:8610/v0/sblk
diff --git a/api/tests/send_me_a_zchain_link.sh b/api/tests/send_me_a_zchain_link.sh
new file mode 100755
index 0000000..9820c7b
--- /dev/null
+++ b/api/tests/send_me_a_zchain_link.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+printf "\n#TODO /szch endpoint\n"
+# curl http://127.0.0.1:8610/v0/szch
diff --git a/api/tests/show_mined_block.sh b/api/tests/show_mined_block.sh
new file mode 100755
index 0000000..99d126c
--- /dev/null
+++ b/api/tests/show_mined_block.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+printf "\n#TODO /sblock endpoint\n"
+# curl http://127.0.0.1:8610/v0/sblock
diff --git a/api/tests/test_all_routes.sh b/api/tests/test_all_routes.sh
new file mode 100755
index 0000000..4903ca9
--- /dev/null
+++ b/api/tests/test_all_routes.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+bash node_local_chain.sh
+bash node_local_zlatest.sh
+bash gathered_zblocks.sh
+bash gathered_zchain_zlatest_pairs.sh
+bash latest_known_mined_block.sh
+bash show_mined_block.sh
+bash send_me_a_zchain_link.sh
+bash send_me_a_zblock.sh
diff --git a/api/validators/ZblockValidator/index.js b/api/validators/ZblockValidator/index.js
new file mode 100644
index 0000000..2cd2a7b
--- /dev/null
+++ b/api/validators/ZblockValidator/index.js
@@ -0,0 +1,29 @@
+/*
+ * To verify a block we simply put it on `enter`. `enter` will crawl
+ * the zchain that is connected to the zblock we got. If it fails for
+ * any reason we can check `logfollow` for that.
+ *
+ * We send the data tested and the exit code to continuethings()
+ *
+ */
+module.exports = (ch, res) => {
+ const command = spawn("ak-enter",[ch]);
+ response_string = "";
+ command.stdout.on("data", data => {
+ response_string = response_string + data;
+ console.log(`${data}`);
+ });
+
+ command.stderr.on("data", data => {
+ console.log(`stderr: ${data}`);
+ });
+
+ command.on('error', (error) => {
+ console.log(`error: ${error.message}`);
+ });
+
+ command.on("close", code => {
+ console.log(`child process exited with code ${code}`);
+ continuethings(code,ch,res);
+ });
+};