From 08cdf6f9230f6bd65fba7c8a7283869295389795 Mon Sep 17 00:00:00 2001 From: Laura Abro Date: Wed, 3 Jul 2024 14:41:12 -0300 Subject: [PATCH 1/5] update package.json --- Lesson 1/EZ-testing-task/package.json | 2 ++ Lesson 2/file-sharing/after/package.json | 1 + Lesson 2/file-sharing/before/package.json | 1 + Lesson 2/upnp-basics/after/package.json | 2 ++ Lesson 2/upnp-basics/before/package.json | 2 ++ Lesson 3/simple-crawler/after/package.json | 1 + Lesson 3/simple-crawler/before/package.json | 2 ++ Lesson 4/caesar-task/after/package.json | 2 ++ Lesson 4/caesar-task/before/package.json | 2 ++ 9 files changed, 15 insertions(+) diff --git a/Lesson 1/EZ-testing-task/package.json b/Lesson 1/EZ-testing-task/package.json index 71ddb4e..4622ac5 100644 --- a/Lesson 1/EZ-testing-task/package.json +++ b/Lesson 1/EZ-testing-task/package.json @@ -16,6 +16,8 @@ "license": "ISC", "dependencies": { "@_koi/web3.js": "^0.0.6", + "@_koii/namespace-wrapper": "^1.0.1", + "@_koii/storage-task-sdk": "^1.2.4", "axios": "^0.27.2", "body-parser": "^1.20.2", "cross-spawn": "^7.0.3", diff --git a/Lesson 2/file-sharing/after/package.json b/Lesson 2/file-sharing/after/package.json index 55858d7..28d8f6f 100644 --- a/Lesson 2/file-sharing/after/package.json +++ b/Lesson 2/file-sharing/after/package.json @@ -17,6 +17,7 @@ "dependencies": { "@_koi/web3.js": "^0.0.6", "@_koii/storage-task-sdk": "^1.2.4", + "@_koii/namespace-wrapper": "^1.0.1", "axios": "^0.27.2", "body-parser": "^1.20.2", "cross-spawn": "^7.0.3", diff --git a/Lesson 2/file-sharing/before/package.json b/Lesson 2/file-sharing/before/package.json index 55858d7..e1532ee 100644 --- a/Lesson 2/file-sharing/before/package.json +++ b/Lesson 2/file-sharing/before/package.json @@ -16,6 +16,7 @@ "license": "ISC", "dependencies": { "@_koi/web3.js": "^0.0.6", + "@_koii/namespace-wrapper": "^1.0.1", "@_koii/storage-task-sdk": "^1.2.4", "axios": "^0.27.2", "body-parser": "^1.20.2", diff --git a/Lesson 2/upnp-basics/after/package.json b/Lesson 2/upnp-basics/after/package.json index 0406a57..e1532ee 100644 --- a/Lesson 2/upnp-basics/after/package.json +++ b/Lesson 2/upnp-basics/after/package.json @@ -16,6 +16,8 @@ "license": "ISC", "dependencies": { "@_koi/web3.js": "^0.0.6", + "@_koii/namespace-wrapper": "^1.0.1", + "@_koii/storage-task-sdk": "^1.2.4", "axios": "^0.27.2", "body-parser": "^1.20.2", "cross-spawn": "^7.0.3", diff --git a/Lesson 2/upnp-basics/before/package.json b/Lesson 2/upnp-basics/before/package.json index 0406a57..e1532ee 100644 --- a/Lesson 2/upnp-basics/before/package.json +++ b/Lesson 2/upnp-basics/before/package.json @@ -16,6 +16,8 @@ "license": "ISC", "dependencies": { "@_koi/web3.js": "^0.0.6", + "@_koii/namespace-wrapper": "^1.0.1", + "@_koii/storage-task-sdk": "^1.2.4", "axios": "^0.27.2", "body-parser": "^1.20.2", "cross-spawn": "^7.0.3", diff --git a/Lesson 3/simple-crawler/after/package.json b/Lesson 3/simple-crawler/after/package.json index f6bc56c..a4e1ed2 100644 --- a/Lesson 3/simple-crawler/after/package.json +++ b/Lesson 3/simple-crawler/after/package.json @@ -17,6 +17,7 @@ "dependencies": { "@_koi/web3.js": "^0.0.6", "@_koii/storage-task-sdk": "^1.2.4", + "@_koii/namespace-wrapper": "^1.0.1", "axios": "^0.27.2", "body-parser": "^1.20.2", "cross-spawn": "^7.0.3", diff --git a/Lesson 3/simple-crawler/before/package.json b/Lesson 3/simple-crawler/before/package.json index 0406a57..e1532ee 100644 --- a/Lesson 3/simple-crawler/before/package.json +++ b/Lesson 3/simple-crawler/before/package.json @@ -16,6 +16,8 @@ "license": "ISC", "dependencies": { "@_koi/web3.js": "^0.0.6", + "@_koii/namespace-wrapper": "^1.0.1", + "@_koii/storage-task-sdk": "^1.2.4", "axios": "^0.27.2", "body-parser": "^1.20.2", "cross-spawn": "^7.0.3", diff --git a/Lesson 4/caesar-task/after/package.json b/Lesson 4/caesar-task/after/package.json index 0406a57..e1532ee 100644 --- a/Lesson 4/caesar-task/after/package.json +++ b/Lesson 4/caesar-task/after/package.json @@ -16,6 +16,8 @@ "license": "ISC", "dependencies": { "@_koi/web3.js": "^0.0.6", + "@_koii/namespace-wrapper": "^1.0.1", + "@_koii/storage-task-sdk": "^1.2.4", "axios": "^0.27.2", "body-parser": "^1.20.2", "cross-spawn": "^7.0.3", diff --git a/Lesson 4/caesar-task/before/package.json b/Lesson 4/caesar-task/before/package.json index cbc9f5d..cbbe08b 100644 --- a/Lesson 4/caesar-task/before/package.json +++ b/Lesson 4/caesar-task/before/package.json @@ -15,6 +15,8 @@ "license": "ISC", "dependencies": { "@_koi/web3.js": "^0.0.6", + "@_koii/namespace-wrapper": "^1.0.1", + "@_koii/storage-task-sdk": "^1.2.4", "axios": "^0.27.2", "body-parser": "^1.20.2", "cross-spawn": "^7.0.3", From 801056f1c88547e086db76642cacc69c108f0761 Mon Sep 17 00:00:00 2001 From: Laura Abro Date: Wed, 3 Jul 2024 14:41:32 -0300 Subject: [PATCH 2/5] ignore lock files and node_modules --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 496ee2c..01198e3 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -.DS_Store \ No newline at end of file +.DS_Store +node_modules +package-lock.json +yarn.lock From 67cb2d622aa7b42e4e5668d7b40316f07fa3eb7f Mon Sep 17 00:00:00 2001 From: Laura Abro Date: Wed, 3 Jul 2024 14:42:24 -0300 Subject: [PATCH 3/5] change imports to npm package --- Lesson 1/EZ-testing-task/coreLogic.js | 2 +- Lesson 1/EZ-testing-task/index.js | 8 ++++---- Lesson 1/EZ-testing-task/task/audit.js | 2 +- Lesson 1/EZ-testing-task/task/distribution.js | 2 +- Lesson 1/EZ-testing-task/task/submission.js | 2 +- Lesson 1/EZ-testing-task/tests/main.test.js | 2 +- Lesson 2/file-sharing/after/coreLogic.js | 2 +- Lesson 2/file-sharing/after/index.js | 2 +- Lesson 2/file-sharing/after/task/audit.js | 2 +- Lesson 2/file-sharing/after/task/distribution.js | 2 +- Lesson 2/file-sharing/after/task/submission.js | 2 +- Lesson 2/file-sharing/after/tests/main.test.js | 2 +- Lesson 2/file-sharing/before/coreLogic.js | 2 +- Lesson 2/file-sharing/before/index.js | 2 +- Lesson 2/file-sharing/before/task/audit.js | 2 +- Lesson 2/file-sharing/before/task/distribution.js | 2 +- Lesson 2/file-sharing/before/task/submission.js | 2 +- Lesson 2/file-sharing/before/tests/main.test.js | 2 +- Lesson 2/upnp-basics/after/coreLogic.js | 2 +- Lesson 2/upnp-basics/after/index.js | 8 ++++---- Lesson 2/upnp-basics/after/task/audit.js | 4 ++-- Lesson 2/upnp-basics/after/task/distribution.js | 2 +- Lesson 2/upnp-basics/after/task/submission.js | 2 +- Lesson 2/upnp-basics/after/tests/main.test.js | 2 +- Lesson 2/upnp-basics/before/coreLogic.js | 2 +- Lesson 2/upnp-basics/before/index.js | 2 +- Lesson 2/upnp-basics/before/task/audit.js | 2 +- Lesson 2/upnp-basics/before/task/distribution.js | 2 +- Lesson 2/upnp-basics/before/task/submission.js | 2 +- Lesson 2/upnp-basics/before/tests/main.test.js | 2 +- Lesson 3/simple-crawler/after/coreLogic.js | 2 +- Lesson 3/simple-crawler/after/index.js | 10 +++++----- Lesson 3/simple-crawler/after/task/audit.js | 2 +- Lesson 3/simple-crawler/after/task/distribution.js | 2 +- Lesson 3/simple-crawler/after/task/submission.js | 2 +- Lesson 3/simple-crawler/after/tests/main.test.js | 2 +- Lesson 3/simple-crawler/before/coreLogic.js | 2 +- Lesson 3/simple-crawler/before/index.js | 8 ++++---- Lesson 3/simple-crawler/before/task/audit.js | 2 +- Lesson 3/simple-crawler/before/task/distribution.js | 2 +- Lesson 3/simple-crawler/before/task/submission.js | 2 +- Lesson 3/simple-crawler/before/tests/main.test.js | 2 +- Lesson 4/caesar-task/after/coreLogic.js | 2 +- Lesson 4/caesar-task/after/index.js | 10 +++++----- Lesson 4/caesar-task/after/task/audit.js | 2 +- Lesson 4/caesar-task/after/task/distribution.js | 2 +- Lesson 4/caesar-task/after/task/submission.js | 2 +- Lesson 4/caesar-task/after/tests/main.test.js | 2 +- Lesson 4/caesar-task/before/coreLogic.js | 2 +- Lesson 4/caesar-task/before/index.js | 10 +++++----- Lesson 4/caesar-task/before/task/audit.js | 2 +- Lesson 4/caesar-task/before/task/distribution.js | 2 +- Lesson 4/caesar-task/before/task/submission.js | 2 +- Lesson 4/caesar-task/before/tests/main.test.js | 2 +- 54 files changed, 76 insertions(+), 76 deletions(-) diff --git a/Lesson 1/EZ-testing-task/coreLogic.js b/Lesson 1/EZ-testing-task/coreLogic.js index a7d7165..d044a53 100644 --- a/Lesson 1/EZ-testing-task/coreLogic.js +++ b/Lesson 1/EZ-testing-task/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 1/EZ-testing-task/index.js b/Lesson 1/EZ-testing-task/index.js index 8f18299..6e75289 100644 --- a/Lesson 1/EZ-testing-task/index.js +++ b/Lesson 1/EZ-testing-task/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); if (app) { // Write your Express Endpoints here. @@ -59,13 +59,13 @@ async function setup() { /* GUIDE TO CALLS K2 FUNCTIONS MANUALLY - If you wish to do the development by avoiding the timers then you can do the intended calls to K2 - directly using these function calls. + If you wish to do the development by avoiding the timers then you can do the intended calls to K2 + directly using these function calls. To disable timers please set the TIMERS flag in task-node ENV to disable NOTE : K2 will still have the windows to accept the submission value, audit, so you are expected - to make calls in the intended slots of your round time. + to make calls in the intended slots of your round time. */ diff --git a/Lesson 1/EZ-testing-task/task/audit.js b/Lesson 1/EZ-testing-task/task/audit.js index 9f20d12..d3ba817 100644 --- a/Lesson 1/EZ-testing-task/task/audit.js +++ b/Lesson 1/EZ-testing-task/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Audit { /** diff --git a/Lesson 1/EZ-testing-task/task/distribution.js b/Lesson 1/EZ-testing-task/task/distribution.js index e5b6426..6dab59d 100644 --- a/Lesson 1/EZ-testing-task/task/distribution.js +++ b/Lesson 1/EZ-testing-task/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 1/EZ-testing-task/task/submission.js b/Lesson 1/EZ-testing-task/task/submission.js index 586ac90..a96bd5d 100644 --- a/Lesson 1/EZ-testing-task/task/submission.js +++ b/Lesson 1/EZ-testing-task/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Submission { /** * Executes your task, optionally storing the result. diff --git a/Lesson 1/EZ-testing-task/tests/main.test.js b/Lesson 1/EZ-testing-task/tests/main.test.js index ba05a7f..491318b 100644 --- a/Lesson 1/EZ-testing-task/tests/main.test.js +++ b/Lesson 1/EZ-testing-task/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 2/file-sharing/after/coreLogic.js b/Lesson 2/file-sharing/after/coreLogic.js index 7b09d08..77a9ec7 100644 --- a/Lesson 2/file-sharing/after/coreLogic.js +++ b/Lesson 2/file-sharing/after/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 2/file-sharing/after/index.js b/Lesson 2/file-sharing/after/index.js index c7d556a..01f4d91 100644 --- a/Lesson 2/file-sharing/after/index.js +++ b/Lesson 2/file-sharing/after/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); const setupRoutes = require('./routes'); diff --git a/Lesson 2/file-sharing/after/task/audit.js b/Lesson 2/file-sharing/after/task/audit.js index 88b0594..db638df 100644 --- a/Lesson 2/file-sharing/after/task/audit.js +++ b/Lesson 2/file-sharing/after/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const isValidFile = require('./fileUtils/isValidFile'); class Audit { diff --git a/Lesson 2/file-sharing/after/task/distribution.js b/Lesson 2/file-sharing/after/task/distribution.js index 6b7c83b..882ec2a 100644 --- a/Lesson 2/file-sharing/after/task/distribution.js +++ b/Lesson 2/file-sharing/after/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 2/file-sharing/after/task/submission.js b/Lesson 2/file-sharing/after/task/submission.js index a2ec3fa..b159d7a 100644 --- a/Lesson 2/file-sharing/after/task/submission.js +++ b/Lesson 2/file-sharing/after/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const storeFile = require('./fileUtils/storeFile'); class Submission { diff --git a/Lesson 2/file-sharing/after/tests/main.test.js b/Lesson 2/file-sharing/after/tests/main.test.js index 1eb8a52..5f970ed 100644 --- a/Lesson 2/file-sharing/after/tests/main.test.js +++ b/Lesson 2/file-sharing/after/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 2/file-sharing/before/coreLogic.js b/Lesson 2/file-sharing/before/coreLogic.js index 7b09d08..77a9ec7 100644 --- a/Lesson 2/file-sharing/before/coreLogic.js +++ b/Lesson 2/file-sharing/before/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 2/file-sharing/before/index.js b/Lesson 2/file-sharing/before/index.js index c7d556a..01f4d91 100644 --- a/Lesson 2/file-sharing/before/index.js +++ b/Lesson 2/file-sharing/before/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); const setupRoutes = require('./routes'); diff --git a/Lesson 2/file-sharing/before/task/audit.js b/Lesson 2/file-sharing/before/task/audit.js index 709a13d..fbe73e3 100644 --- a/Lesson 2/file-sharing/before/task/audit.js +++ b/Lesson 2/file-sharing/before/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Audit { /** diff --git a/Lesson 2/file-sharing/before/task/distribution.js b/Lesson 2/file-sharing/before/task/distribution.js index 6b7c83b..882ec2a 100644 --- a/Lesson 2/file-sharing/before/task/distribution.js +++ b/Lesson 2/file-sharing/before/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 2/file-sharing/before/task/submission.js b/Lesson 2/file-sharing/before/task/submission.js index 3768a3d..a285a2d 100644 --- a/Lesson 2/file-sharing/before/task/submission.js +++ b/Lesson 2/file-sharing/before/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const storeFile = require('./fileUtils/storeFile'); diff --git a/Lesson 2/file-sharing/before/tests/main.test.js b/Lesson 2/file-sharing/before/tests/main.test.js index 1eb8a52..5f970ed 100644 --- a/Lesson 2/file-sharing/before/tests/main.test.js +++ b/Lesson 2/file-sharing/before/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 2/upnp-basics/after/coreLogic.js b/Lesson 2/upnp-basics/after/coreLogic.js index 7b09d08..77a9ec7 100644 --- a/Lesson 2/upnp-basics/after/coreLogic.js +++ b/Lesson 2/upnp-basics/after/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 2/upnp-basics/after/index.js b/Lesson 2/upnp-basics/after/index.js index b14447e..dd2ff64 100644 --- a/Lesson 2/upnp-basics/after/index.js +++ b/Lesson 2/upnp-basics/after/index.js @@ -4,7 +4,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); const setupRoutes = require('./routes'); if (app) { @@ -44,13 +44,13 @@ async function setup() { /* GUIDE TO CALLS K2 FUNCTIONS MANUALLY - If you wish to do the development by avoiding the timers then you can do the intended calls to K2 - directly using these function calls. + If you wish to do the development by avoiding the timers then you can do the intended calls to K2 + directly using these function calls. To disable timers please set the TIMERS flag in task-node ENV to disable NOTE : K2 will still have the windows to accept the submission value, audit, so you are expected - to make calls in the intended slots of your round time. + to make calls in the intended slots of your round time. */ diff --git a/Lesson 2/upnp-basics/after/task/audit.js b/Lesson 2/upnp-basics/after/task/audit.js index b7ae5a8..31b09a8 100644 --- a/Lesson 2/upnp-basics/after/task/audit.js +++ b/Lesson 2/upnp-basics/after/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Audit { /** @@ -12,7 +12,7 @@ class Audit { let vote; console.log('SUBMISSION VALUE', submission_value, round); try { - // Verify the type of value is string + // Verify the type of value is string if (typeof submission_value === 'string' && submission_value.length > 0) { vote = true; } else { diff --git a/Lesson 2/upnp-basics/after/task/distribution.js b/Lesson 2/upnp-basics/after/task/distribution.js index 6b7c83b..882ec2a 100644 --- a/Lesson 2/upnp-basics/after/task/distribution.js +++ b/Lesson 2/upnp-basics/after/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 2/upnp-basics/after/task/submission.js b/Lesson 2/upnp-basics/after/task/submission.js index 9bef0c3..5d6ba47 100644 --- a/Lesson 2/upnp-basics/after/task/submission.js +++ b/Lesson 2/upnp-basics/after/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper, TASK_ID } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, TASK_ID } = require('koii-task-node'); const { default: axios } = require('axios'); const getData = require('./getData'); diff --git a/Lesson 2/upnp-basics/after/tests/main.test.js b/Lesson 2/upnp-basics/after/tests/main.test.js index 1eb8a52..5f970ed 100644 --- a/Lesson 2/upnp-basics/after/tests/main.test.js +++ b/Lesson 2/upnp-basics/after/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 2/upnp-basics/before/coreLogic.js b/Lesson 2/upnp-basics/before/coreLogic.js index 7b09d08..77a9ec7 100644 --- a/Lesson 2/upnp-basics/before/coreLogic.js +++ b/Lesson 2/upnp-basics/before/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 2/upnp-basics/before/index.js b/Lesson 2/upnp-basics/before/index.js index 99607ec..aa99cba 100644 --- a/Lesson 2/upnp-basics/before/index.js +++ b/Lesson 2/upnp-basics/before/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); const setupRoutes = require('./routes'); diff --git a/Lesson 2/upnp-basics/before/task/audit.js b/Lesson 2/upnp-basics/before/task/audit.js index 709a13d..fbe73e3 100644 --- a/Lesson 2/upnp-basics/before/task/audit.js +++ b/Lesson 2/upnp-basics/before/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Audit { /** diff --git a/Lesson 2/upnp-basics/before/task/distribution.js b/Lesson 2/upnp-basics/before/task/distribution.js index 6b7c83b..882ec2a 100644 --- a/Lesson 2/upnp-basics/before/task/distribution.js +++ b/Lesson 2/upnp-basics/before/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 2/upnp-basics/before/task/submission.js b/Lesson 2/upnp-basics/before/task/submission.js index b9d0fc2..1570f05 100644 --- a/Lesson 2/upnp-basics/before/task/submission.js +++ b/Lesson 2/upnp-basics/before/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Submission { /** * Executes your task, optionally storing the result. diff --git a/Lesson 2/upnp-basics/before/tests/main.test.js b/Lesson 2/upnp-basics/before/tests/main.test.js index 1eb8a52..5f970ed 100644 --- a/Lesson 2/upnp-basics/before/tests/main.test.js +++ b/Lesson 2/upnp-basics/before/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 3/simple-crawler/after/coreLogic.js b/Lesson 3/simple-crawler/after/coreLogic.js index 7b09d08..77a9ec7 100644 --- a/Lesson 3/simple-crawler/after/coreLogic.js +++ b/Lesson 3/simple-crawler/after/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 3/simple-crawler/after/index.js b/Lesson 3/simple-crawler/after/index.js index 75723ec..13d7bec 100644 --- a/Lesson 3/simple-crawler/after/index.js +++ b/Lesson 3/simple-crawler/after/index.js @@ -3,12 +3,12 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); if (app) { // Write your Express Endpoints here. // Ex. app.post('/accept-cid', async (req, res) => {}) - + // Sample API that return your task state app.get('/taskState', async (req, res) => { const state = await namespaceWrapper.getTaskState(); @@ -54,13 +54,13 @@ async function setup() { /* GUIDE TO CALLS K2 FUNCTIONS MANUALLY - If you wish to do the development by avoiding the timers then you can do the intended calls to K2 - directly using these function calls. + If you wish to do the development by avoiding the timers then you can do the intended calls to K2 + directly using these function calls. To disable timers please set the TIMERS flag in task-node ENV to disable NOTE : K2 will still have the windows to accept the submission value, audit, so you are expected - to make calls in the intended slots of your round time. + to make calls in the intended slots of your round time. */ diff --git a/Lesson 3/simple-crawler/after/task/audit.js b/Lesson 3/simple-crawler/after/task/audit.js index b7bfac4..bd8e7bf 100644 --- a/Lesson 3/simple-crawler/after/task/audit.js +++ b/Lesson 3/simple-crawler/after/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const SimpleCrawlerTask = require('../crawler/SimpleCrawlerTask'); class Audit { diff --git a/Lesson 3/simple-crawler/after/task/distribution.js b/Lesson 3/simple-crawler/after/task/distribution.js index 6b7c83b..882ec2a 100644 --- a/Lesson 3/simple-crawler/after/task/distribution.js +++ b/Lesson 3/simple-crawler/after/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 3/simple-crawler/after/task/submission.js b/Lesson 3/simple-crawler/after/task/submission.js index 8c13686..8083674 100644 --- a/Lesson 3/simple-crawler/after/task/submission.js +++ b/Lesson 3/simple-crawler/after/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const { KoiiStorageClient } = require('@_koii/storage-task-sdk'); const fs = require('fs'); diff --git a/Lesson 3/simple-crawler/after/tests/main.test.js b/Lesson 3/simple-crawler/after/tests/main.test.js index 1eb8a52..5f970ed 100644 --- a/Lesson 3/simple-crawler/after/tests/main.test.js +++ b/Lesson 3/simple-crawler/after/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 3/simple-crawler/before/coreLogic.js b/Lesson 3/simple-crawler/before/coreLogic.js index 7b09d08..77a9ec7 100644 --- a/Lesson 3/simple-crawler/before/coreLogic.js +++ b/Lesson 3/simple-crawler/before/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 3/simple-crawler/before/index.js b/Lesson 3/simple-crawler/before/index.js index e8166ae..ec042fc 100644 --- a/Lesson 3/simple-crawler/before/index.js +++ b/Lesson 3/simple-crawler/before/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); if (app) { // Write your Express Endpoints here. @@ -59,13 +59,13 @@ async function setup() { /* GUIDE TO CALLS K2 FUNCTIONS MANUALLY - If you wish to do the development by avoiding the timers then you can do the intended calls to K2 - directly using these function calls. + If you wish to do the development by avoiding the timers then you can do the intended calls to K2 + directly using these function calls. To disable timers please set the TIMERS flag in task-node ENV to disable NOTE : K2 will still have the windows to accept the submission value, audit, so you are expected - to make calls in the intended slots of your round time. + to make calls in the intended slots of your round time. */ diff --git a/Lesson 3/simple-crawler/before/task/audit.js b/Lesson 3/simple-crawler/before/task/audit.js index 8e580c0..65e66d5 100644 --- a/Lesson 3/simple-crawler/before/task/audit.js +++ b/Lesson 3/simple-crawler/before/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Audit { /** diff --git a/Lesson 3/simple-crawler/before/task/distribution.js b/Lesson 3/simple-crawler/before/task/distribution.js index 6b7c83b..882ec2a 100644 --- a/Lesson 3/simple-crawler/before/task/distribution.js +++ b/Lesson 3/simple-crawler/before/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 3/simple-crawler/before/task/submission.js b/Lesson 3/simple-crawler/before/task/submission.js index 342663f..ad08048 100644 --- a/Lesson 3/simple-crawler/before/task/submission.js +++ b/Lesson 3/simple-crawler/before/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const { KoiiStorageClient } = require('@_koii/storage-task-sdk'); const fs = require('fs'); diff --git a/Lesson 3/simple-crawler/before/tests/main.test.js b/Lesson 3/simple-crawler/before/tests/main.test.js index 1eb8a52..5f970ed 100644 --- a/Lesson 3/simple-crawler/before/tests/main.test.js +++ b/Lesson 3/simple-crawler/before/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 4/caesar-task/after/coreLogic.js b/Lesson 4/caesar-task/after/coreLogic.js index 7b09d08..77a9ec7 100644 --- a/Lesson 4/caesar-task/after/coreLogic.js +++ b/Lesson 4/caesar-task/after/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 4/caesar-task/after/index.js b/Lesson 4/caesar-task/after/index.js index 75723ec..13d7bec 100644 --- a/Lesson 4/caesar-task/after/index.js +++ b/Lesson 4/caesar-task/after/index.js @@ -3,12 +3,12 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); if (app) { // Write your Express Endpoints here. // Ex. app.post('/accept-cid', async (req, res) => {}) - + // Sample API that return your task state app.get('/taskState', async (req, res) => { const state = await namespaceWrapper.getTaskState(); @@ -54,13 +54,13 @@ async function setup() { /* GUIDE TO CALLS K2 FUNCTIONS MANUALLY - If you wish to do the development by avoiding the timers then you can do the intended calls to K2 - directly using these function calls. + If you wish to do the development by avoiding the timers then you can do the intended calls to K2 + directly using these function calls. To disable timers please set the TIMERS flag in task-node ENV to disable NOTE : K2 will still have the windows to accept the submission value, audit, so you are expected - to make calls in the intended slots of your round time. + to make calls in the intended slots of your round time. */ diff --git a/Lesson 4/caesar-task/after/task/audit.js b/Lesson 4/caesar-task/after/task/audit.js index e0b3f52..45f13c2 100644 --- a/Lesson 4/caesar-task/after/task/audit.js +++ b/Lesson 4/caesar-task/after/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const CaesarCipher = require('../caesar-cipher/caesar-cipher'); class Audit { diff --git a/Lesson 4/caesar-task/after/task/distribution.js b/Lesson 4/caesar-task/after/task/distribution.js index c86be30..df4dc2a 100644 --- a/Lesson 4/caesar-task/after/task/distribution.js +++ b/Lesson 4/caesar-task/after/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 4/caesar-task/after/task/submission.js b/Lesson 4/caesar-task/after/task/submission.js index c740e78..f53261c 100644 --- a/Lesson 4/caesar-task/after/task/submission.js +++ b/Lesson 4/caesar-task/after/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const CaesarCipher = require('../caesar-cipher/caesar-cipher'); class Submission { diff --git a/Lesson 4/caesar-task/after/tests/main.test.js b/Lesson 4/caesar-task/after/tests/main.test.js index 1eb8a52..5f970ed 100644 --- a/Lesson 4/caesar-task/after/tests/main.test.js +++ b/Lesson 4/caesar-task/after/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 4/caesar-task/before/coreLogic.js b/Lesson 4/caesar-task/before/coreLogic.js index 7b09d08..77a9ec7 100644 --- a/Lesson 4/caesar-task/before/coreLogic.js +++ b/Lesson 4/caesar-task/before/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('./_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 4/caesar-task/before/index.js b/Lesson 4/caesar-task/before/index.js index 75723ec..13d7bec 100644 --- a/Lesson 4/caesar-task/before/index.js +++ b/Lesson 4/caesar-task/before/index.js @@ -3,12 +3,12 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('./_koiiNode/koiiNode'); +} = require('koii-task-node'); if (app) { // Write your Express Endpoints here. // Ex. app.post('/accept-cid', async (req, res) => {}) - + // Sample API that return your task state app.get('/taskState', async (req, res) => { const state = await namespaceWrapper.getTaskState(); @@ -54,13 +54,13 @@ async function setup() { /* GUIDE TO CALLS K2 FUNCTIONS MANUALLY - If you wish to do the development by avoiding the timers then you can do the intended calls to K2 - directly using these function calls. + If you wish to do the development by avoiding the timers then you can do the intended calls to K2 + directly using these function calls. To disable timers please set the TIMERS flag in task-node ENV to disable NOTE : K2 will still have the windows to accept the submission value, audit, so you are expected - to make calls in the intended slots of your round time. + to make calls in the intended slots of your round time. */ diff --git a/Lesson 4/caesar-task/before/task/audit.js b/Lesson 4/caesar-task/before/task/audit.js index 93001c4..d24ab31 100644 --- a/Lesson 4/caesar-task/before/task/audit.js +++ b/Lesson 4/caesar-task/before/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const CaesarCipher = require('../caesar-cipher/caesar-cipher'); class Audit { diff --git a/Lesson 4/caesar-task/before/task/distribution.js b/Lesson 4/caesar-task/before/task/distribution.js index 11fddc7..826a8df 100644 --- a/Lesson 4/caesar-task/before/task/distribution.js +++ b/Lesson 4/caesar-task/before/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); class Distribution { /** diff --git a/Lesson 4/caesar-task/before/task/submission.js b/Lesson 4/caesar-task/before/task/submission.js index c740e78..f53261c 100644 --- a/Lesson 4/caesar-task/before/task/submission.js +++ b/Lesson 4/caesar-task/before/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper } = require('koii-task-node'); const CaesarCipher = require('../caesar-cipher/caesar-cipher'); class Submission { diff --git a/Lesson 4/caesar-task/before/tests/main.test.js b/Lesson 4/caesar-task/before/tests/main.test.js index 1eb8a52..5f970ed 100644 --- a/Lesson 4/caesar-task/before/tests/main.test.js +++ b/Lesson 4/caesar-task/before/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('../_koiiNode/koiiNode'); +const { namespaceWrapper, _server } = require('koii-task-node'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { From 8cef32642b9f3e19261ca2ef6675f227bde6ecdc Mon Sep 17 00:00:00 2001 From: Laura Abro Date: Wed, 3 Jul 2024 14:42:37 -0300 Subject: [PATCH 4/5] remove old namespacewrapper file --- .../EZ-testing-task/_koiiNode/koiiNode.js | 1133 ---------------- .../file-sharing/after/_koiiNode/koiiNode.js | 1156 ----------------- .../file-sharing/before/_koiiNode/koiiNode.js | 1156 ----------------- .../upnp-basics/after/_koiiNode/koiiNode.js | 1156 ----------------- .../upnp-basics/before/_koiiNode/koiiNode.js | 1156 ----------------- .../after/_koiiNode/koiiNode.js | 1156 ----------------- .../before/_koiiNode/koiiNode.js | 1156 ----------------- .../caesar-task/after/_koiiNode/koiiNode.js | 1155 ---------------- .../caesar-task/before/_koiiNode/koiiNode.js | 1155 ---------------- Lesson 5/koiiNode.js | 1156 ----------------- 10 files changed, 11535 deletions(-) delete mode 100644 Lesson 1/EZ-testing-task/_koiiNode/koiiNode.js delete mode 100644 Lesson 2/file-sharing/after/_koiiNode/koiiNode.js delete mode 100644 Lesson 2/file-sharing/before/_koiiNode/koiiNode.js delete mode 100644 Lesson 2/upnp-basics/after/_koiiNode/koiiNode.js delete mode 100644 Lesson 2/upnp-basics/before/_koiiNode/koiiNode.js delete mode 100644 Lesson 3/simple-crawler/after/_koiiNode/koiiNode.js delete mode 100644 Lesson 3/simple-crawler/before/_koiiNode/koiiNode.js delete mode 100644 Lesson 4/caesar-task/after/_koiiNode/koiiNode.js delete mode 100644 Lesson 4/caesar-task/before/_koiiNode/koiiNode.js delete mode 100644 Lesson 5/koiiNode.js diff --git a/Lesson 1/EZ-testing-task/_koiiNode/koiiNode.js b/Lesson 1/EZ-testing-task/_koiiNode/koiiNode.js deleted file mode 100644 index 70edc11..0000000 --- a/Lesson 1/EZ-testing-task/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1133 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); -const semver = require('semver'); - -/** **************************************** init.js ********************************** */ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || "https://testnet.koii.network"; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - { return res.send(204); } - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/** **************************************** NamespaceWrapper.js ********************************** */ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - - #testingMainSystemAccount; - - #testingStakingSystemAccount; - - #testingTaskState; - - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key }); - if (resp) { - return resp[key]; - } - return null; - } catch (e) { - console.error(e); - return null; - } - } - - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } - return fsPromises[method](`${path}`, ...args); - } - - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } - return fsPromises[method](`${path}`, ...args); - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } - const writer = fsPromises.createWriteStream(imagepath); - return writer; - } - - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } - const file = fsPromises.readFileSync(imagepath); - return file; - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } - return 100; - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } - return this.#testingStakingSystemAccount; - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const { blockhash } = await connection.getRecentBlockhash('finalized'); - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } - return this.#testingTaskState; - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } - return await this.logMessage(level, message, action); - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } - if ( - this.#testingTaskState.submissions_audit_trigger[round] - && this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } - if ( - this.#testingTaskState.distributions_audit_trigger[round] - && this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } - return 1; - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - const round = 1; - const submissionValAcc = this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } - if (!this.#testingDistributionList[round]) { this.#testingDistributionList[round] = {}; } - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } - if (!this.#testingTaskState.distribution_rewards_submission[round]) { this.#testingTaskState.distribution_rewards_submission[round] = {}; } - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } - if (!this.#testingTaskState.submissions[round]) { this.#testingTaskState.submissions[round] = {}; } - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round, - }; - } - - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } - console.log('Cannot call getProgramAccounts in testing mode'); - } - - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } - console.log('Cannot call getNodes in testing mode'); - } - - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } - console.log('Cannot call getNodes in testing mode'); - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } - const submissionValAcc = this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - - async getTaskSubmissionInfo(round, forcefetch = false) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - forcefetch - ); - if (!taskSubmissionInfo || taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } - // console.log(this.#testingTaskState) - return this.#testingTaskState; - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - const candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - const candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey && taskNodeAdministered) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger - && submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (!taskDistributionInfo || taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } - return this.#testingTaskState; - } - async validateAndVoteOnDistributionList( - validateDistribution, - round, - isPreviousRoundFailed = false - ) { - console.log("******/ IN VOTING OF DISTRIBUTION LIST /******"); - let tasknodeVersionSatisfied = false; - const taskNodeVersion = await this.getTaskNodeVersion(); - if (semver.gte(taskNodeVersion, "1.11.19")) { - tasknodeVersionSatisfied = true; - } - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error("Error in getting distributions for the round", error); - } - if (taskAccountDataJSON == null) { - console.log("No distribution submissions found for the round", round); - return; - } - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed - ); - console.log("SELECTED NODE FOR AUDIT", selectedNode); - if (selectedNode == submitterPubkey) { - console.log("YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS"); - return; - } - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - try { - console.log("VOTING ON DISTRIBUTION LIST"); - isValid = await validateDistribution( - values[i].submission_value, - round - ); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log("VOTING TRUE ON DISTRIBUTION AUDIT"); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round - ); - console.log( - "RESPONSE FROM DISTRIBUTION AUDIT FUNCTION", - response - ); - } - } else if (isValid == false && tasknodeVersionSatisfied) { - // Call auditSubmission function and isValid is passed as false - console.log("RAISING AUDIT / VOTING FALSE ON DISTRIBUTION"); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round - ); - console.log("RESPONSE FROM DISTRIBUTION AUDIT FUNCTION", response); - } - } catch (err) { - console.log("ERROR IN ELSE CONDITION FOR DISTRIBUTION", err); - } - } - } - } - - async getTaskNodeVersion() { - if (taskNodeAdministered) { - try { - return await genericHandler("getTaskNodeVersion"); - } catch (error) { - console.error("Error getting task node version", error); - return; - } - } else { - return "1.11.19"; - } - } - - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } - return './KOIIDB'; - } - - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } - return './'; - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round,true); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter((r) => r >= 0); - - const promises = latestRounds.map(async (r) => { - if (r == round) { - return new Set(Object.keys(submissions)); - } - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r, true); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = keySets.length > 0 - ? [...keySets[0]].filter((key) => keySets.every((set) => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map((key) => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => str.split('').reduce((acc, val) => acc + val.charCodeAt(0), 0); - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined - || selectedNode == '' - || submitPubKey == undefined - ) { return; } - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } - return this.#testingMainSystemAccount.publicKey.toBase58(); - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - const responseData = err?.response?.data?.message; - if ((args[0] === 'getTaskSubmissionInfo' || args[0] === 'getTaskDistributionInfo') && - responseData && typeof responseData === 'string' && responseData.includes('Task does not have any')) { - console.log(`Error in genericHandler: "${args[0]}"`, err.message); - console.log(err?.response?.data); - }else{ - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then((rpcUrl) => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 2/file-sharing/after/_koiiNode/koiiNode.js b/Lesson 2/file-sharing/after/_koiiNode/koiiNode.js deleted file mode 100644 index 852bdb6..0000000 --- a/Lesson 2/file-sharing/after/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1156 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - // console.log(this.#testingTaskState) - return this.#testingTaskState; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey && taskNodeAdministered) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 2/file-sharing/before/_koiiNode/koiiNode.js b/Lesson 2/file-sharing/before/_koiiNode/koiiNode.js deleted file mode 100644 index 852bdb6..0000000 --- a/Lesson 2/file-sharing/before/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1156 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - // console.log(this.#testingTaskState) - return this.#testingTaskState; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey && taskNodeAdministered) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 2/upnp-basics/after/_koiiNode/koiiNode.js b/Lesson 2/upnp-basics/after/_koiiNode/koiiNode.js deleted file mode 100644 index 852bdb6..0000000 --- a/Lesson 2/upnp-basics/after/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1156 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - // console.log(this.#testingTaskState) - return this.#testingTaskState; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey && taskNodeAdministered) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 2/upnp-basics/before/_koiiNode/koiiNode.js b/Lesson 2/upnp-basics/before/_koiiNode/koiiNode.js deleted file mode 100644 index 852bdb6..0000000 --- a/Lesson 2/upnp-basics/before/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1156 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - // console.log(this.#testingTaskState) - return this.#testingTaskState; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey && taskNodeAdministered) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 3/simple-crawler/after/_koiiNode/koiiNode.js b/Lesson 3/simple-crawler/after/_koiiNode/koiiNode.js deleted file mode 100644 index 852bdb6..0000000 --- a/Lesson 3/simple-crawler/after/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1156 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - // console.log(this.#testingTaskState) - return this.#testingTaskState; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey && taskNodeAdministered) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 3/simple-crawler/before/_koiiNode/koiiNode.js b/Lesson 3/simple-crawler/before/_koiiNode/koiiNode.js deleted file mode 100644 index 852bdb6..0000000 --- a/Lesson 3/simple-crawler/before/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1156 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - // console.log(this.#testingTaskState) - return this.#testingTaskState; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey && taskNodeAdministered) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 4/caesar-task/after/_koiiNode/koiiNode.js b/Lesson 4/caesar-task/after/_koiiNode/koiiNode.js deleted file mode 100644 index 6f17a8d..0000000 --- a/Lesson 4/caesar-task/after/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1155 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round: 1, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - return this.#testingTaskState.submissions[round]; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState.distribution_rewards_submission[round]; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 4/caesar-task/before/_koiiNode/koiiNode.js b/Lesson 4/caesar-task/before/_koiiNode/koiiNode.js deleted file mode 100644 index 6f17a8d..0000000 --- a/Lesson 4/caesar-task/before/_koiiNode/koiiNode.js +++ /dev/null @@ -1,1155 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round: 1, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - return this.#testingTaskState.submissions[round]; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState.distribution_rewards_submission[round]; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; diff --git a/Lesson 5/koiiNode.js b/Lesson 5/koiiNode.js deleted file mode 100644 index 852bdb6..0000000 --- a/Lesson 5/koiiNode.js +++ /dev/null @@ -1,1156 +0,0 @@ -const { default: axios } = require('axios'); -const { createHash } = require('crypto'); - -const { Connection, PublicKey, Keypair } = require('@_koi/web3.js'); - -const Datastore = require('nedb-promises'); -const fsPromises = require('fs/promises'); -const bs58 = require('bs58'); -const nacl = require('tweetnacl'); - -/****************************************** init.js ***********************************/ - -const express = require('express'); -// Only used for testing purposes, in production the env will be injected by tasknode -require('dotenv').config(); -const bodyParser = require('body-parser'); -/** - * This will be the name of the current task as coming from the task node running this task. - */ -const TASK_NAME = process.argv[2] || 'Local'; -/** - * This will be the id of the current task as coming from the task node running this task. - */ -const TASK_ID = process.argv[3]; -/** - * This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. - * As all communication via the task node and this task will be done on this port. - */ -const EXPRESS_PORT = process.argv[4] || 10000; - -const LogLevel = { - Log: 'log', - Warn: 'warn', - Error: 'error', -}; - -// Not used anymore -// const NODE_MODE = process.argv[5]; - -/** - * This will be the main account public key in string format of the task node running this task. - */ -const MAIN_ACCOUNT_PUBKEY = process.argv[6]; -/** - * This will be the secret used by the task to authenticate with task node running this task. - */ -const SECRET_KEY = process.argv[7]; -/** - * This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - */ -const K2_NODE_URL = process.argv[8] || 'https://k2-testnet.koii.live'; -/** - * This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - */ -const SERVICE_URL = process.argv[9]; -/** - * This will be stake of the task node running this task, can be double checked with the task state and staking public key. - */ -const STAKE = Number(process.argv[10]); -/** - * This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - */ -const TASK_NODE_PORT = Number(process.argv[11]); - -const app = express(); - -console.log('SETTING UP EXPRESS'); - -app.use(bodyParser.urlencoded({ extended: false })); - -app.use(bodyParser.json()); - -app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader( - 'Access-Control-Allow-Methods', - 'GET, POST, PUT, PATCH, DELETE', - ); - res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); - res.setHeader('Access-Control-Allow-Credentials', false); - if (req.method === 'OPTIONS') - // if is preflight(OPTIONS) then response status 204(NO CONTENT) - return res.send(204); - next(); -}); - -app.get('/', (req, res) => { - res.send('Hello World!'); -}); - -const _server = app.listen(EXPRESS_PORT, () => { - console.log(`${TASK_NAME} listening on port ${EXPRESS_PORT}`); -}); - -/****************************************** NamespaceWrapper.js ***********************************/ - -const taskNodeAdministered = !!TASK_ID; -const BASE_ROOT_URL = `http://localhost:${TASK_NODE_PORT}/namespace-wrapper`; -let connection; - -class NamespaceWrapper { - #db; - #testingMainSystemAccount; - #testingStakingSystemAccount; - #testingTaskState; - #testingDistributionList; - - constructor() { - if (taskNodeAdministered) { - this.initializeDB(); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - this.defaultTaskSetup(); - } - } - - async initializeDB() { - if (this.#db) return; - try { - if (taskNodeAdministered) { - const path = await this.getTaskLevelDBPath(); - this.#db = Datastore.create(path); - } else { - this.#db = Datastore.create('./localKOIIDB.db'); - } - } catch (e) { - this.#db = Datastore.create(`../namespace/${TASK_ID}/KOIILevelDB.db`); - } - } - - async getDb() { - if (this.#db) return this.#db; - await this.initializeDB(); - return this.#db; - } - /** - * Namespace wrapper of storeGetAsync - * @param {string} key // Path to get - */ - async storeGet(key) { - try { - await this.initializeDB(); - const resp = await this.#db.findOne({ key: key }); - if (resp) { - return resp[key]; - } else { - return null; - } - } catch (e) { - console.error(e); - return null; - } - } - /** - * Namespace wrapper over storeSetAsync - * @param {string} key Path to set - * @param {*} value Data to set - */ - async storeSet(key, value) { - try { - await this.initializeDB(); - await this.#db.update( - { key: key }, - { [key]: value, key }, - { upsert: true }, - ); - } catch (e) { - console.error(e); - return undefined; - } - } - - /** - * Namespace wrapper over fsPromises methods - * @param {*} method The fsPromise method to call - * @param {*} path Path for the express call - * @param {...any} args Remaining parameters for the FS call - */ - async fs(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fs', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - async fsStaking(method, path, ...args) { - if (taskNodeAdministered) { - return await genericHandler('fsStaking', method, path, ...args); - } else { - return fsPromises[method](`${path}`, ...args); - } - } - - async fsWriteStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsWriteStream', imagepath); - } else { - const writer = createWriteStream(imagepath); - return writer; - } - } - async fsReadStream(imagepath) { - if (taskNodeAdministered) { - return await genericHandler('fsReadStream', imagepath); - } else { - const file = readFileSync(imagepath); - return file; - } - } - - /** - * Namespace wrapper for getting current slots - */ - async getSlot() { - if (taskNodeAdministered) { - return await genericHandler('getCurrentSlot'); - } else { - return 100; - } - } - - async payloadSigning(body) { - if (taskNodeAdministered) { - return await genericHandler('signData', body); - } else { - const msg = new TextEncoder().encode(JSON.stringify(body)); - const signedMessage = nacl.sign( - msg, - this.#testingMainSystemAccount.secretKey, - ); - return await this.bs58Encode(signedMessage); - } - } - - async bs58Encode(data) { - return bs58.encode( - Buffer.from(data.buffer, data.byteOffset, data.byteLength), - ); - } - - async bs58Decode(data) { - return new Uint8Array(bs58.decode(data)); - } - - decodePayload(payload) { - return new TextDecoder().decode(payload); - } - - /** - * Namespace wrapper of storeGetAsync - * @param {string} signedMessage r // Path to get - */ - - async verifySignature(signedMessage, pubKey) { - if (taskNodeAdministered) { - return await genericHandler('verifySignedData', signedMessage, pubKey); - } else { - try { - const payload = nacl.sign.open( - await this.bs58Decode(signedMessage), - await this.bs58Decode(pubKey), - ); - if (!payload) return { error: 'Invalid signature' }; - return { data: this.decodePayload(payload) }; - } catch (e) { - console.error(e); - return { error: `Verification failed: ${e}` }; - } - } - } - - // async submissionOnChain(submitterKeypair, submission) { - // return await genericHandler( - // 'submissionOnChain', - // submitterKeypair, - // submission, - // ); - // } - - async stakeOnChain( - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'stakeOnChain', - taskStateInfoPublicKey, - stakingAccKeypair, - stakePotAccount, - stakeAmount, - ); - } else { - this.#testingTaskState.stake_list[ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = stakeAmount; - } - } - async claimReward(stakePotAccount, beneficiaryAccount, claimerKeypair) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'claimReward', - stakePotAccount, - beneficiaryAccount, - claimerKeypair, - ); - } - async sendTransaction(serviceNodeAccount, beneficiaryAccount, amount) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - return await genericHandler( - 'sendTransaction', - serviceNodeAccount, - beneficiaryAccount, - amount, - ); - } - - async getSubmitterAccount() { - if (taskNodeAdministered) { - const submitterAccountResp = await genericHandler('getSubmitterAccount'); - return Keypair.fromSecretKey( - Uint8Array.from(Object.values(submitterAccountResp._keypair.secretKey)), - ); - } else { - return this.#testingStakingSystemAccount; - } - } - - /** - * sendAndConfirmTransaction wrapper that injects mainSystemWallet as the first signer for paying the tx fees - * @param {connection} method // Receive method ["get", "post", "put", "delete"] - * @param {transaction} path // Endpoint path appended to namespace - * @param {Function} callback // Callback function on traffic receive - */ - async sendAndConfirmTransactionWrapper(transaction, signers) { - if (!taskNodeAdministered) { - console.log('Cannot call sendTransaction in testing mode'); - return; - } - const blockhash = (await connection.getRecentBlockhash('finalized')) - .blockhash; - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(MAIN_ACCOUNT_PUBKEY); - return await genericHandler( - 'sendAndConfirmTransactionWrapper', - transaction.serialize({ - requireAllSignatures: false, - verifySignatures: false, - }), - signers, - ); - } - - // async signArweave(transaction) { - // let tx = await genericHandler('signArweave', transaction.toJSON()); - // return arweave.transactions.fromRaw(tx); - // } - // async signEth(transaction) { - // return await genericHandler('signEth', transaction); - // } - async getTaskState(options) { - if (taskNodeAdministered) { - const response = await genericHandler('getTaskState', options); - if (response.error) { - console.log('Error in getting task state', response.error); - return null; - } - return response; - } else { - return this.#testingTaskState; - } - } - - async logMessage(level, message, action) { - switch (level) { - case LogLevel.Log: - console.log(message, action); - break; - case LogLevel.Warn: - console.warn(message, action); - break; - case LogLevel.Error: - console.error(message, action); - break; - default: - console.log( - `Invalid log level: ${level}. The log levels can be log, warn or error`, - ); - return false; - } - return true; - } - - /** - * This logger function is used to log the task erros , warnings and logs on desktop-node - * @param {level} enum // Receive method ["Log", "Warn", "Error"] - enum LogLevel { - Log = 'log', - Warn = 'warn', - Error = 'error', - } - * @param {message} string // log, error or warning message - * @returns {boolean} // true if the message is logged successfully otherwise false - */ - - async logger(level, message, action) { - if (taskNodeAdministered) { - return await genericHandler('logger', level, message, action); - } else { - return await this.logMessage(level, message, action); - } - } - - async auditSubmission(candidatePubkey, isValid, voterKeypair, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'auditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.submissions_audit_trigger[round] && - this.#testingTaskState.submissions_audit_trigger[round][candidatePubkey] - ) { - this.#testingTaskState.submissions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.submissions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async distributionListAuditSubmission( - candidatePubkey, - isValid, - voterKeypair, - round, - ) { - if (taskNodeAdministered) { - return await genericHandler( - 'distributionListAuditSubmission', - candidatePubkey, - isValid, - round, - ); - } else { - if ( - this.#testingTaskState.distributions_audit_trigger[round] && - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ] - ) { - this.#testingTaskState.distributions_audit_trigger[round][ - candidatePubkey - ].votes.push({ - is_valid: isValid, - voter: voterKeypair.pubKey.toBase58(), - slot: 100, - }); - } else { - this.#testingTaskState.distributions_audit_trigger[round] = { - [candidatePubkey]: { - trigger_by: this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 100, - votes: [], - }, - }; - } - } - } - - async getRound() { - if (taskNodeAdministered) { - return await genericHandler('getRound'); - } else { - return 1; - } - } - - async payoutTrigger(round) { - if (taskNodeAdministered) { - return await genericHandler('payloadTrigger', round); - } else { - console.log( - 'Payout Trigger only handles possitive flows (Without audits)', - ); - let round = 1; - const submissionValAcc = - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.toBase58() - ].submission_value; - this.#testingTaskState.available_balances = - this.#testingDistributionList[round][submissionValAcc]; - } - } - - async uploadDistributionList(distributionList, round) { - if (taskNodeAdministered) { - return await genericHandler( - 'uploadDistributionList', - distributionList, - round, - ); - } else { - if (!this.#testingDistributionList[round]) - this.#testingDistributionList[round] = {}; - - this.#testingDistributionList[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = Buffer.from(JSON.stringify(distributionList)); - return true; - } - } - - async distributionListSubmissionOnChain(round) { - if (taskNodeAdministered) { - return await genericHandler('distributionListSubmissionOnChain', round); - } else { - if (!this.#testingTaskState.distribution_rewards_submission[round]) - this.#testingTaskState.distribution_rewards_submission[round] = {}; - - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: - this.#testingStakingSystemAccount.publicKey.toBase58(), - slot: 200, - round: 1, - }; - } - } - - async checkSubmissionAndUpdateRound(submissionValue = 'default', round) { - if (taskNodeAdministered) { - return await genericHandler( - 'checkSubmissionAndUpdateRound', - submissionValue, - round, - ); - } else { - if (!this.#testingTaskState.submissions[round]) - this.#testingTaskState.submissions[round] = {}; - this.#testingTaskState.submissions[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ] = { - submission_value: submissionValue, - slot: 100, - round, - }; - } - } - async getProgramAccounts() { - if (taskNodeAdministered) { - return await genericHandler('getProgramAccounts'); - } else { - console.log('Cannot call getProgramAccounts in testing mode'); - } - } - async defaultTaskSetup() { - if (taskNodeAdministered) { - return await genericHandler('defaultTaskSetup'); - } else { - if (this.#testingTaskState) return; - this.#testingMainSystemAccount = new Keypair(); - this.#testingStakingSystemAccount = new Keypair(); - this.#testingDistributionList = {}; - this.#testingTaskState = { - task_name: 'DummyTestState', - task_description: 'Dummy Task state for testing flow', - submissions: {}, - submissions_audit_trigger: {}, - total_bounty_amount: 10000000000, - bounty_amount_per_round: 1000000000, - total_stake_amount: 50000000000, - minimum_stake_amount: 5000000000, - available_balances: {}, - stake_list: {}, - round_time: 600, - starting_slot: 0, - audit_window: 200, - submission_window: 200, - distribution_rewards_submission: {}, - distributions_audit_trigger: {}, - }; - } - } - async getRpcUrl() { - if (taskNodeAdministered) { - return await genericHandler('getRpcUrl'); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - async getNodes(url) { - if (taskNodeAdministered) { - return await genericHandler('getNodes', url); - } else { - console.log('Cannot call getNodes in testing mode'); - } - } - - async getDistributionList(publicKey, round) { - if (taskNodeAdministered) { - const response = await genericHandler( - 'getDistributionList', - publicKey, - round, - ); - if (response.error) { - return null; - } - return response; - } else { - const submissionValAcc = - this.#testingTaskState.distribution_rewards_submission[round][ - this.#testingStakingSystemAccount.publicKey.toBase58() - ].submission_value; - return this.#testingDistributionList[round][submissionValAcc]; - } - } - - async getTaskSubmissionInfo(round) { - if (taskNodeAdministered) { - const taskSubmissionInfo = await genericHandler( - 'getTaskSubmissionInfo', - round, - ); - if (taskSubmissionInfo.error) { - return null; - } - return taskSubmissionInfo; - } else { - // console.log(this.#testingTaskState) - return this.#testingTaskState; - } - } - - async validateAndVoteOnNodes(validate, round) { - console.log('******/ IN VOTING /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No submissions found for the round', round); - return; - } - console.log( - `Fetching the submissions of round ${round}`, - taskAccountDataJSON.submissions[round], - ); - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log('Submissions from last round: ', keys, values, size); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey && taskNodeAdministered) { - console.log('YOU CANNOT VOTE ON YOUR OWN SUBMISSIONS'); - } else { - try { - console.log( - 'SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validate(values[i].submission_value, round); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const submissions_audit_trigger = - taskAccountDataJSON.submissions_audit_trigger[round]; - console.log('SUBMIT AUDIT TRIGGER', submissions_audit_trigger); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // submissions_audit_trigger[candidatePublicKey] - // ); - if ( - submissions_audit_trigger && - submissions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON AUDIT'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE'); - const response = await this.auditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log('RESPONSE FROM AUDIT FUNCTION', response); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION', err); - } - } - } - } - } - - async getTaskDistributionInfo(round) { - if (taskNodeAdministered) { - const taskDistributionInfo = await genericHandler( - 'getTaskDistributionInfo', - round, - ); - if (taskDistributionInfo.error) { - return null; - } - return taskDistributionInfo; - } else { - return this.#testingTaskState; - } - } - - async validateAndVoteOnDistributionList(validateDistribution, round) { - // await this.checkVoteStatus(); - console.log('******/ IN VOTING OF DISTRIBUTION LIST /******'); - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskDistributionInfo(round); - } catch (error) { - console.error('Error in getting distributions for the round', error); - } - if (taskAccountDataJSON == null) { - console.log('No distribution submissions found for the round', round); - return; - } - console.log( - `Fetching the Distribution submissions of round ${round}`, - taskAccountDataJSON.distribution_rewards_submission[round], - ); - const submissions = - taskAccountDataJSON?.distribution_rewards_submission[round]; - if ( - submissions == null || - submissions == undefined || - submissions.length == 0 - ) { - console.log(`No submisssions found in round ${round}`); - return `No submisssions found in round ${round}`; - } else { - const keys = Object.keys(submissions); - const values = Object.values(submissions); - const size = values.length; - console.log( - 'Distribution Submissions from last round: ', - keys, - values, - size, - ); - let isValid; - const submitterAccountKeyPair = await this.getSubmitterAccount(); - const submitterPubkey = submitterAccountKeyPair.publicKey.toBase58(); - - for (let i = 0; i < size; i++) { - let candidatePublicKey = keys[i]; - console.log('FOR CANDIDATE KEY', candidatePublicKey); - let candidateKeyPairPublicKey = new PublicKey(keys[i]); - if (candidatePublicKey == submitterPubkey) { - console.log('YOU CANNOT VOTE ON YOUR OWN DISTRIBUTION SUBMISSIONS'); - } else { - try { - console.log( - 'DISTRIBUTION SUBMISSION VALUE TO CHECK', - values[i].submission_value, - ); - isValid = await validateDistribution( - values[i].submission_value, - round, - ); - console.log(`Voting ${isValid} to ${candidatePublicKey}`); - - if (isValid) { - // check for the submissions_audit_trigger , if it exists then vote true on that otherwise do nothing - const distributions_audit_trigger = - taskAccountDataJSON.distributions_audit_trigger[round]; - console.log( - 'SUBMIT DISTRIBUTION AUDIT TRIGGER', - distributions_audit_trigger, - ); - // console.log( - // "CANDIDATE PUBKEY CHECK IN AUDIT TRIGGER", - // distributions_audit_trigger[candidatePublicKey] - // ); - if ( - distributions_audit_trigger && - distributions_audit_trigger[candidatePublicKey] - ) { - console.log('VOTING TRUE ON DISTRIBUTION AUDIT'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } else if (isValid == false) { - // Call auditSubmission function and isValid is passed as false - console.log('RAISING AUDIT / VOTING FALSE ON DISTRIBUTION'); - const response = await this.distributionListAuditSubmission( - candidateKeyPairPublicKey, - isValid, - submitterAccountKeyPair, - round, - ); - console.log( - 'RESPONSE FROM DISTRIBUTION AUDIT FUNCTION', - response, - ); - } - } catch (err) { - console.log('ERROR IN ELSE CONDITION FOR DISTRIBUTION', err); - } - } - } - } - } - async getTaskLevelDBPath() { - if (taskNodeAdministered) { - return await genericHandler('getTaskLevelDBPath'); - } else { - return './KOIIDB'; - } - } - async getBasePath() { - if (taskNodeAdministered) { - const basePath = (await namespaceWrapper.getTaskLevelDBPath()).replace( - '/KOIIDB', - '', - ); - return basePath; - } else { - return './'; - } - } - - async getAverageSlotTime() { - if (taskNodeAdministered) { - try { - return await genericHandler('getAverageSlotTime'); - } catch (error) { - console.error('Error getting average slot time', error); - return 400; - } - } else { - return 400; - } - } - - async nodeSelectionDistributionList(round, isPreviousFailed) { - let taskAccountDataJSON = null; - try { - taskAccountDataJSON = await this.getTaskSubmissionInfo(round); - } catch (error) { - console.error('Task submission not found', error); - return; - } - - if (taskAccountDataJSON == null) { - console.error('Task state not found'); - return; - } - console.log('EXPECTED ROUND', round); - - const submissions = taskAccountDataJSON.submissions[round]; - if (submissions == null) { - console.log('No submisssions found in N-1 round'); - return 'No submisssions found in N-1 round'; - } else { - // getting last 3 submissions for the rounds - let keys; - const latestRounds = [round, round - 1, round - 2].filter(r => r >= 0); - - const promises = latestRounds.map(async r => { - if (r == round) { - return new Set(Object.keys(submissions)); - } else { - let roundSubmissions = null; - try { - roundSubmissions = await this.getTaskSubmissionInfo(r); - if (roundSubmissions && roundSubmissions.submissions[r]) { - return new Set(Object.keys(roundSubmissions.submissions[r])); - } - } catch (error) { - console.error('Error in getting submissions for the round', error); - } - return new Set(); - } - }); - - const keySets = await Promise.all(promises); - - // Find the keys present in all the rounds - keys = - keySets.length > 0 - ? [...keySets[0]].filter(key => keySets.every(set => set.has(key))) - : []; - if (keys.length == 0) { - console.log('No common keys found in last 3 rounds'); - keys = Object.keys(submissions); - } - console.log('KEYS', keys.length); - const values = keys.map(key => submissions[key]); - - let size = keys.length; - console.log('Submissions from N-2 round: ', size); - - // Check the keys i.e if the submitter shall be excluded or not - try { - const distributionData = await this.getTaskDistributionInfo(round); - const audit_record = distributionData?.distributions_audit_record; - if (audit_record && audit_record[round] == 'PayoutFailed') { - console.log('ROUND DATA', audit_record[round]); - console.log( - 'SUBMITTER LIST', - distributionData.distribution_rewards_submission[round], - ); - const submitterList = - distributionData.distribution_rewards_submission[round]; - const submitterKeys = Object.keys(submitterList); - console.log('SUBMITTER KEYS', submitterKeys); - const submitterSize = submitterKeys.length; - console.log('SUBMITTER SIZE', submitterSize); - - for (let j = 0; j < submitterSize; j++) { - console.log('SUBMITTER KEY CANDIDATE', submitterKeys[j]); - const id = keys.indexOf(submitterKeys[j]); - console.log('ID', id); - if (id != -1) { - keys.splice(id, 1); - values.splice(id, 1); - size--; - } - } - - console.log('KEYS FOR HASH CALC', keys.length); - } - } catch (error) { - console.log('Error in getting distribution data', error); - } - - // calculating the digest - - const ValuesString = JSON.stringify(values); - - const hashDigest = createHash('sha256') - .update(ValuesString) - .digest('hex'); - - console.log('HASH DIGEST', hashDigest); - - // function to calculate the score - const calculateScore = (str = '') => { - return str.split('').reduce((acc, val) => { - return acc + val.charCodeAt(0); - }, 0); - }; - - // function to compare the ASCII values - - const compareASCII = (str1, str2) => { - const firstScore = calculateScore(str1); - const secondScore = calculateScore(str2); - return Math.abs(firstScore - secondScore); - }; - - // loop through the keys and select the one with higest score - - const selectedNode = { - score: 0, - pubkey: '', - }; - let score = 0; - if (isPreviousFailed) { - let leastScore = -Infinity; - let secondLeastScore = -Infinity; - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - if (candidateScore > leastScore) { - secondLeastScore = leastScore; - leastScore = candidateScore; - } else if (candidateScore > secondLeastScore) { - secondLeastScore = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } else { - for (let i = 0; i < size; i++) { - const candidateSubmissionJson = {}; - candidateSubmissionJson[keys[i]] = values[i]; - const candidateSubmissionString = JSON.stringify( - candidateSubmissionJson, - ); - const candidateSubmissionHash = createHash('sha256') - .update(candidateSubmissionString) - .digest('hex'); - const candidateScore = compareASCII( - hashDigest, - candidateSubmissionHash, - ); - // console.log('CANDIDATE SCORE', candidateScore); - if (candidateScore > score) { - score = candidateScore; - selectedNode.score = candidateScore; - selectedNode.pubkey = keys[i]; - } - } - } - - console.log('SELECTED NODE OBJECT', selectedNode); - return selectedNode.pubkey; - } - } - - async selectAndGenerateDistributionList( - submitDistributionList, - round, - isPreviousRoundFailed, - ) { - console.log('SelectAndGenerateDistributionList called'); - const selectedNode = await this.nodeSelectionDistributionList( - round, - isPreviousRoundFailed, - ); - console.log('Selected Node', selectedNode); - const submitPubKey = await this.getSubmitterAccount(); - if ( - selectedNode == undefined || - selectedNode == '' || - submitPubKey == undefined - ) - return; - if (selectedNode == submitPubKey?.publicKey.toBase58()) { - await submitDistributionList(round); - const taskState = await this.getTaskState({}); - if (taskState == null) { - console.error('Task state not found'); - return; - } - const avgSlotTime = await this.getAverageSlotTime(); - if (avgSlotTime == null) { - console.error('Avg slot time not found'); - return; - } - setTimeout(async () => { - await this.payoutTrigger(round); - }, (taskState.audit_window + taskState.submission_window) * avgSlotTime); - } - } - - getMainAccountPubkey() { - if (taskNodeAdministered) { - return MAIN_ACCOUNT_PUBKEY; - } else { - return this.#testingMainSystemAccount.publicKey.toBase58(); - } - } -} - -async function genericHandler(...args) { - try { - let response = await axios.post(BASE_ROOT_URL, { - args, - taskId: TASK_ID, - secret: SECRET_KEY, - }); - if (response.status == 200) return response.data.response; - else { - console.error(response.status, response.data); - return null; - } - } catch (err) { - console.error(`Error in genericHandler: "${args[0]}"`, err.message); - console.error(err?.response?.data); - return { error: err }; - } -} - -const namespaceWrapper = new NamespaceWrapper(); -if (taskNodeAdministered) { - namespaceWrapper.getRpcUrl().then(rpcUrl => { - console.log(rpcUrl, 'RPC URL'); - connection = new Connection(rpcUrl, 'confirmed'); - }); -} -module.exports = { - namespaceWrapper, - taskNodeAdministered, // Boolean flag indicating that the task is being ran in active mode (Task node supervised), or development (testing) mode - app, // The initialized express app to be used to register endpoints - TASK_ID, // This will be the PORT on which the this task is expected to run the express server coming from the task node running this task. As all communication via the task node and this task will be done on this port. - MAIN_ACCOUNT_PUBKEY, // This will be the secret used to authenticate with task node running this task. - SECRET_KEY, // This will be the secret used by the task to authenticate with task node running this task. - K2_NODE_URL, // This will be K2 url being used by the task node, possible values are 'https://k2-testnet.koii.live' | 'https://k2-devnet.koii.live' | 'http://localhost:8899' - SERVICE_URL, // This will be public task node endpoint (Or local if it doesn't have any) of the task node running this task. - STAKE, // This will be stake of the task node running this task, can be double checked with the task state and staking public key. - TASK_NODE_PORT, // This will be the port used by task node as the express server port, so it can be used by the task for the communication with the task node - _server, // Express server object -}; From 9d4f0fa1f31f711fae3d9812b6ba988ceb5fe408 Mon Sep 17 00:00:00 2001 From: Laura Abro Date: Thu, 4 Jul 2024 08:32:15 -0300 Subject: [PATCH 5/5] correct namespacewrapper import --- Lesson 1/EZ-testing-task/coreLogic.js | 2 +- Lesson 1/EZ-testing-task/index.js | 2 +- Lesson 1/EZ-testing-task/task/audit.js | 2 +- Lesson 1/EZ-testing-task/task/distribution.js | 2 +- Lesson 1/EZ-testing-task/task/submission.js | 2 +- Lesson 1/EZ-testing-task/tests/main.test.js | 2 +- Lesson 2/file-sharing/after/coreLogic.js | 2 +- Lesson 2/file-sharing/after/index.js | 2 +- Lesson 2/file-sharing/after/task/audit.js | 2 +- Lesson 2/file-sharing/after/task/distribution.js | 2 +- Lesson 2/file-sharing/after/task/submission.js | 2 +- Lesson 2/file-sharing/after/tests/main.test.js | 2 +- Lesson 2/file-sharing/before/coreLogic.js | 2 +- Lesson 2/file-sharing/before/index.js | 2 +- Lesson 2/file-sharing/before/task/audit.js | 2 +- Lesson 2/file-sharing/before/task/distribution.js | 2 +- Lesson 2/file-sharing/before/task/submission.js | 2 +- Lesson 2/file-sharing/before/tests/main.test.js | 2 +- Lesson 2/upnp-basics/after/coreLogic.js | 2 +- Lesson 2/upnp-basics/after/index.js | 2 +- Lesson 2/upnp-basics/after/task/audit.js | 2 +- Lesson 2/upnp-basics/after/task/distribution.js | 2 +- Lesson 2/upnp-basics/after/task/submission.js | 2 +- Lesson 2/upnp-basics/after/tests/main.test.js | 2 +- Lesson 2/upnp-basics/before/coreLogic.js | 2 +- Lesson 2/upnp-basics/before/index.js | 2 +- Lesson 2/upnp-basics/before/task/audit.js | 2 +- Lesson 2/upnp-basics/before/task/distribution.js | 2 +- Lesson 2/upnp-basics/before/task/submission.js | 2 +- Lesson 2/upnp-basics/before/tests/main.test.js | 2 +- Lesson 3/simple-crawler/after/coreLogic.js | 2 +- Lesson 3/simple-crawler/after/index.js | 2 +- Lesson 3/simple-crawler/after/task/audit.js | 2 +- Lesson 3/simple-crawler/after/task/distribution.js | 2 +- Lesson 3/simple-crawler/after/task/submission.js | 2 +- Lesson 3/simple-crawler/after/tests/main.test.js | 2 +- Lesson 3/simple-crawler/before/coreLogic.js | 2 +- Lesson 3/simple-crawler/before/index.js | 2 +- Lesson 3/simple-crawler/before/task/audit.js | 2 +- Lesson 3/simple-crawler/before/task/distribution.js | 2 +- Lesson 3/simple-crawler/before/task/submission.js | 2 +- Lesson 3/simple-crawler/before/tests/main.test.js | 2 +- Lesson 4/caesar-task/after/coreLogic.js | 2 +- Lesson 4/caesar-task/after/index.js | 2 +- Lesson 4/caesar-task/after/task/audit.js | 2 +- Lesson 4/caesar-task/after/task/distribution.js | 2 +- Lesson 4/caesar-task/after/task/submission.js | 2 +- Lesson 4/caesar-task/after/tests/main.test.js | 2 +- Lesson 4/caesar-task/before/coreLogic.js | 2 +- Lesson 4/caesar-task/before/index.js | 2 +- Lesson 4/caesar-task/before/task/audit.js | 2 +- Lesson 4/caesar-task/before/task/distribution.js | 2 +- Lesson 4/caesar-task/before/task/submission.js | 2 +- Lesson 4/caesar-task/before/tests/main.test.js | 2 +- 54 files changed, 54 insertions(+), 54 deletions(-) diff --git a/Lesson 1/EZ-testing-task/coreLogic.js b/Lesson 1/EZ-testing-task/coreLogic.js index d044a53..64b3e7f 100644 --- a/Lesson 1/EZ-testing-task/coreLogic.js +++ b/Lesson 1/EZ-testing-task/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 1/EZ-testing-task/index.js b/Lesson 1/EZ-testing-task/index.js index 6e75289..caf6e98 100644 --- a/Lesson 1/EZ-testing-task/index.js +++ b/Lesson 1/EZ-testing-task/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); if (app) { // Write your Express Endpoints here. diff --git a/Lesson 1/EZ-testing-task/task/audit.js b/Lesson 1/EZ-testing-task/task/audit.js index d3ba817..809cbfb 100644 --- a/Lesson 1/EZ-testing-task/task/audit.js +++ b/Lesson 1/EZ-testing-task/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Audit { /** diff --git a/Lesson 1/EZ-testing-task/task/distribution.js b/Lesson 1/EZ-testing-task/task/distribution.js index 6dab59d..ebb40de 100644 --- a/Lesson 1/EZ-testing-task/task/distribution.js +++ b/Lesson 1/EZ-testing-task/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 1/EZ-testing-task/task/submission.js b/Lesson 1/EZ-testing-task/task/submission.js index a96bd5d..88ac8f4 100644 --- a/Lesson 1/EZ-testing-task/task/submission.js +++ b/Lesson 1/EZ-testing-task/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Submission { /** * Executes your task, optionally storing the result. diff --git a/Lesson 1/EZ-testing-task/tests/main.test.js b/Lesson 1/EZ-testing-task/tests/main.test.js index 491318b..1756f01 100644 --- a/Lesson 1/EZ-testing-task/tests/main.test.js +++ b/Lesson 1/EZ-testing-task/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 2/file-sharing/after/coreLogic.js b/Lesson 2/file-sharing/after/coreLogic.js index 77a9ec7..b210551 100644 --- a/Lesson 2/file-sharing/after/coreLogic.js +++ b/Lesson 2/file-sharing/after/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 2/file-sharing/after/index.js b/Lesson 2/file-sharing/after/index.js index 01f4d91..8d05e4a 100644 --- a/Lesson 2/file-sharing/after/index.js +++ b/Lesson 2/file-sharing/after/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); const setupRoutes = require('./routes'); diff --git a/Lesson 2/file-sharing/after/task/audit.js b/Lesson 2/file-sharing/after/task/audit.js index db638df..21688dd 100644 --- a/Lesson 2/file-sharing/after/task/audit.js +++ b/Lesson 2/file-sharing/after/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const isValidFile = require('./fileUtils/isValidFile'); class Audit { diff --git a/Lesson 2/file-sharing/after/task/distribution.js b/Lesson 2/file-sharing/after/task/distribution.js index 882ec2a..f07071c 100644 --- a/Lesson 2/file-sharing/after/task/distribution.js +++ b/Lesson 2/file-sharing/after/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 2/file-sharing/after/task/submission.js b/Lesson 2/file-sharing/after/task/submission.js index b159d7a..a72964c 100644 --- a/Lesson 2/file-sharing/after/task/submission.js +++ b/Lesson 2/file-sharing/after/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const storeFile = require('./fileUtils/storeFile'); class Submission { diff --git a/Lesson 2/file-sharing/after/tests/main.test.js b/Lesson 2/file-sharing/after/tests/main.test.js index 5f970ed..05f5352 100644 --- a/Lesson 2/file-sharing/after/tests/main.test.js +++ b/Lesson 2/file-sharing/after/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 2/file-sharing/before/coreLogic.js b/Lesson 2/file-sharing/before/coreLogic.js index 77a9ec7..b210551 100644 --- a/Lesson 2/file-sharing/before/coreLogic.js +++ b/Lesson 2/file-sharing/before/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 2/file-sharing/before/index.js b/Lesson 2/file-sharing/before/index.js index 01f4d91..8d05e4a 100644 --- a/Lesson 2/file-sharing/before/index.js +++ b/Lesson 2/file-sharing/before/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); const setupRoutes = require('./routes'); diff --git a/Lesson 2/file-sharing/before/task/audit.js b/Lesson 2/file-sharing/before/task/audit.js index fbe73e3..e0a1017 100644 --- a/Lesson 2/file-sharing/before/task/audit.js +++ b/Lesson 2/file-sharing/before/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Audit { /** diff --git a/Lesson 2/file-sharing/before/task/distribution.js b/Lesson 2/file-sharing/before/task/distribution.js index 882ec2a..f07071c 100644 --- a/Lesson 2/file-sharing/before/task/distribution.js +++ b/Lesson 2/file-sharing/before/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 2/file-sharing/before/task/submission.js b/Lesson 2/file-sharing/before/task/submission.js index a285a2d..ae27343 100644 --- a/Lesson 2/file-sharing/before/task/submission.js +++ b/Lesson 2/file-sharing/before/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const storeFile = require('./fileUtils/storeFile'); diff --git a/Lesson 2/file-sharing/before/tests/main.test.js b/Lesson 2/file-sharing/before/tests/main.test.js index 5f970ed..05f5352 100644 --- a/Lesson 2/file-sharing/before/tests/main.test.js +++ b/Lesson 2/file-sharing/before/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 2/upnp-basics/after/coreLogic.js b/Lesson 2/upnp-basics/after/coreLogic.js index 77a9ec7..b210551 100644 --- a/Lesson 2/upnp-basics/after/coreLogic.js +++ b/Lesson 2/upnp-basics/after/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 2/upnp-basics/after/index.js b/Lesson 2/upnp-basics/after/index.js index dd2ff64..8e983a1 100644 --- a/Lesson 2/upnp-basics/after/index.js +++ b/Lesson 2/upnp-basics/after/index.js @@ -4,7 +4,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); const setupRoutes = require('./routes'); if (app) { diff --git a/Lesson 2/upnp-basics/after/task/audit.js b/Lesson 2/upnp-basics/after/task/audit.js index 31b09a8..2e91f41 100644 --- a/Lesson 2/upnp-basics/after/task/audit.js +++ b/Lesson 2/upnp-basics/after/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Audit { /** diff --git a/Lesson 2/upnp-basics/after/task/distribution.js b/Lesson 2/upnp-basics/after/task/distribution.js index 882ec2a..f07071c 100644 --- a/Lesson 2/upnp-basics/after/task/distribution.js +++ b/Lesson 2/upnp-basics/after/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 2/upnp-basics/after/task/submission.js b/Lesson 2/upnp-basics/after/task/submission.js index 5d6ba47..78eb3d1 100644 --- a/Lesson 2/upnp-basics/after/task/submission.js +++ b/Lesson 2/upnp-basics/after/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper, TASK_ID } = require('koii-task-node'); +const { namespaceWrapper, TASK_ID } = require('@_koii/namespace-wrapper'); const { default: axios } = require('axios'); const getData = require('./getData'); diff --git a/Lesson 2/upnp-basics/after/tests/main.test.js b/Lesson 2/upnp-basics/after/tests/main.test.js index 5f970ed..05f5352 100644 --- a/Lesson 2/upnp-basics/after/tests/main.test.js +++ b/Lesson 2/upnp-basics/after/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 2/upnp-basics/before/coreLogic.js b/Lesson 2/upnp-basics/before/coreLogic.js index 77a9ec7..b210551 100644 --- a/Lesson 2/upnp-basics/before/coreLogic.js +++ b/Lesson 2/upnp-basics/before/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 2/upnp-basics/before/index.js b/Lesson 2/upnp-basics/before/index.js index aa99cba..5cc9df9 100644 --- a/Lesson 2/upnp-basics/before/index.js +++ b/Lesson 2/upnp-basics/before/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); const setupRoutes = require('./routes'); diff --git a/Lesson 2/upnp-basics/before/task/audit.js b/Lesson 2/upnp-basics/before/task/audit.js index fbe73e3..e0a1017 100644 --- a/Lesson 2/upnp-basics/before/task/audit.js +++ b/Lesson 2/upnp-basics/before/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Audit { /** diff --git a/Lesson 2/upnp-basics/before/task/distribution.js b/Lesson 2/upnp-basics/before/task/distribution.js index 882ec2a..f07071c 100644 --- a/Lesson 2/upnp-basics/before/task/distribution.js +++ b/Lesson 2/upnp-basics/before/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 2/upnp-basics/before/task/submission.js b/Lesson 2/upnp-basics/before/task/submission.js index 1570f05..8789cdc 100644 --- a/Lesson 2/upnp-basics/before/task/submission.js +++ b/Lesson 2/upnp-basics/before/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Submission { /** * Executes your task, optionally storing the result. diff --git a/Lesson 2/upnp-basics/before/tests/main.test.js b/Lesson 2/upnp-basics/before/tests/main.test.js index 5f970ed..05f5352 100644 --- a/Lesson 2/upnp-basics/before/tests/main.test.js +++ b/Lesson 2/upnp-basics/before/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 3/simple-crawler/after/coreLogic.js b/Lesson 3/simple-crawler/after/coreLogic.js index 77a9ec7..b210551 100644 --- a/Lesson 3/simple-crawler/after/coreLogic.js +++ b/Lesson 3/simple-crawler/after/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 3/simple-crawler/after/index.js b/Lesson 3/simple-crawler/after/index.js index 13d7bec..17ac502 100644 --- a/Lesson 3/simple-crawler/after/index.js +++ b/Lesson 3/simple-crawler/after/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); if (app) { // Write your Express Endpoints here. diff --git a/Lesson 3/simple-crawler/after/task/audit.js b/Lesson 3/simple-crawler/after/task/audit.js index bd8e7bf..fa5a061 100644 --- a/Lesson 3/simple-crawler/after/task/audit.js +++ b/Lesson 3/simple-crawler/after/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const SimpleCrawlerTask = require('../crawler/SimpleCrawlerTask'); class Audit { diff --git a/Lesson 3/simple-crawler/after/task/distribution.js b/Lesson 3/simple-crawler/after/task/distribution.js index 882ec2a..f07071c 100644 --- a/Lesson 3/simple-crawler/after/task/distribution.js +++ b/Lesson 3/simple-crawler/after/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 3/simple-crawler/after/task/submission.js b/Lesson 3/simple-crawler/after/task/submission.js index 8083674..62cf3d8 100644 --- a/Lesson 3/simple-crawler/after/task/submission.js +++ b/Lesson 3/simple-crawler/after/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const { KoiiStorageClient } = require('@_koii/storage-task-sdk'); const fs = require('fs'); diff --git a/Lesson 3/simple-crawler/after/tests/main.test.js b/Lesson 3/simple-crawler/after/tests/main.test.js index 5f970ed..05f5352 100644 --- a/Lesson 3/simple-crawler/after/tests/main.test.js +++ b/Lesson 3/simple-crawler/after/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 3/simple-crawler/before/coreLogic.js b/Lesson 3/simple-crawler/before/coreLogic.js index 77a9ec7..b210551 100644 --- a/Lesson 3/simple-crawler/before/coreLogic.js +++ b/Lesson 3/simple-crawler/before/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 3/simple-crawler/before/index.js b/Lesson 3/simple-crawler/before/index.js index ec042fc..803068c 100644 --- a/Lesson 3/simple-crawler/before/index.js +++ b/Lesson 3/simple-crawler/before/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); if (app) { // Write your Express Endpoints here. diff --git a/Lesson 3/simple-crawler/before/task/audit.js b/Lesson 3/simple-crawler/before/task/audit.js index 65e66d5..ba99e54 100644 --- a/Lesson 3/simple-crawler/before/task/audit.js +++ b/Lesson 3/simple-crawler/before/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Audit { /** diff --git a/Lesson 3/simple-crawler/before/task/distribution.js b/Lesson 3/simple-crawler/before/task/distribution.js index 882ec2a..f07071c 100644 --- a/Lesson 3/simple-crawler/before/task/distribution.js +++ b/Lesson 3/simple-crawler/before/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 3/simple-crawler/before/task/submission.js b/Lesson 3/simple-crawler/before/task/submission.js index ad08048..46849cf 100644 --- a/Lesson 3/simple-crawler/before/task/submission.js +++ b/Lesson 3/simple-crawler/before/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const { KoiiStorageClient } = require('@_koii/storage-task-sdk'); const fs = require('fs'); diff --git a/Lesson 3/simple-crawler/before/tests/main.test.js b/Lesson 3/simple-crawler/before/tests/main.test.js index 5f970ed..05f5352 100644 --- a/Lesson 3/simple-crawler/before/tests/main.test.js +++ b/Lesson 3/simple-crawler/before/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 4/caesar-task/after/coreLogic.js b/Lesson 4/caesar-task/after/coreLogic.js index 77a9ec7..b210551 100644 --- a/Lesson 4/caesar-task/after/coreLogic.js +++ b/Lesson 4/caesar-task/after/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 4/caesar-task/after/index.js b/Lesson 4/caesar-task/after/index.js index 13d7bec..17ac502 100644 --- a/Lesson 4/caesar-task/after/index.js +++ b/Lesson 4/caesar-task/after/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); if (app) { // Write your Express Endpoints here. diff --git a/Lesson 4/caesar-task/after/task/audit.js b/Lesson 4/caesar-task/after/task/audit.js index 45f13c2..f1795fc 100644 --- a/Lesson 4/caesar-task/after/task/audit.js +++ b/Lesson 4/caesar-task/after/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const CaesarCipher = require('../caesar-cipher/caesar-cipher'); class Audit { diff --git a/Lesson 4/caesar-task/after/task/distribution.js b/Lesson 4/caesar-task/after/task/distribution.js index df4dc2a..2192646 100644 --- a/Lesson 4/caesar-task/after/task/distribution.js +++ b/Lesson 4/caesar-task/after/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 4/caesar-task/after/task/submission.js b/Lesson 4/caesar-task/after/task/submission.js index f53261c..7710561 100644 --- a/Lesson 4/caesar-task/after/task/submission.js +++ b/Lesson 4/caesar-task/after/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const CaesarCipher = require('../caesar-cipher/caesar-cipher'); class Submission { diff --git a/Lesson 4/caesar-task/after/tests/main.test.js b/Lesson 4/caesar-task/after/tests/main.test.js index 5f970ed..05f5352 100644 --- a/Lesson 4/caesar-task/after/tests/main.test.js +++ b/Lesson 4/caesar-task/after/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => { diff --git a/Lesson 4/caesar-task/before/coreLogic.js b/Lesson 4/caesar-task/before/coreLogic.js index 77a9ec7..b210551 100644 --- a/Lesson 4/caesar-task/before/coreLogic.js +++ b/Lesson 4/caesar-task/before/coreLogic.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const task = require('./task'); class CoreLogic { diff --git a/Lesson 4/caesar-task/before/index.js b/Lesson 4/caesar-task/before/index.js index 13d7bec..17ac502 100644 --- a/Lesson 4/caesar-task/before/index.js +++ b/Lesson 4/caesar-task/before/index.js @@ -3,7 +3,7 @@ const { namespaceWrapper, taskNodeAdministered, app, -} = require('koii-task-node'); +} = require('@_koii/namespace-wrapper'); if (app) { // Write your Express Endpoints here. diff --git a/Lesson 4/caesar-task/before/task/audit.js b/Lesson 4/caesar-task/before/task/audit.js index d24ab31..67dff86 100644 --- a/Lesson 4/caesar-task/before/task/audit.js +++ b/Lesson 4/caesar-task/before/task/audit.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const CaesarCipher = require('../caesar-cipher/caesar-cipher'); class Audit { diff --git a/Lesson 4/caesar-task/before/task/distribution.js b/Lesson 4/caesar-task/before/task/distribution.js index 826a8df..bf6cef4 100644 --- a/Lesson 4/caesar-task/before/task/distribution.js +++ b/Lesson 4/caesar-task/before/task/distribution.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); class Distribution { /** diff --git a/Lesson 4/caesar-task/before/task/submission.js b/Lesson 4/caesar-task/before/task/submission.js index f53261c..7710561 100644 --- a/Lesson 4/caesar-task/before/task/submission.js +++ b/Lesson 4/caesar-task/before/task/submission.js @@ -1,4 +1,4 @@ -const { namespaceWrapper } = require('koii-task-node'); +const { namespaceWrapper } = require('@_koii/namespace-wrapper'); const CaesarCipher = require('../caesar-cipher/caesar-cipher'); class Submission { diff --git a/Lesson 4/caesar-task/before/tests/main.test.js b/Lesson 4/caesar-task/before/tests/main.test.js index 5f970ed..05f5352 100644 --- a/Lesson 4/caesar-task/before/tests/main.test.js +++ b/Lesson 4/caesar-task/before/tests/main.test.js @@ -1,5 +1,5 @@ const { coreLogic } = require('../coreLogic'); -const { namespaceWrapper, _server } = require('koii-task-node'); +const { namespaceWrapper, _server } = require('@_koii/namespace-wrapper'); const Joi = require('joi'); const axios = require('axios'); beforeAll(async () => {