From 68869a5ecabca2595456db0d5a651f71bc70fe1a Mon Sep 17 00:00:00 2001 From: boneyard93501 <4523011+boneyard93501@users.noreply.github.com> Date: Wed, 29 Sep 2021 18:39:32 -0500 Subject: [PATCH] init project --- .gitignore | 47 ++++++++++++++++++++++++++++ data/snapshot.db | 0 package.json | 27 ++++++++++++++++ src/eip_processor.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 54 ++++++++++++++++++++++++++++++++ src/local_db.ts | 52 +++++++++++++++++++++++++++++++ tsconfig.json | 69 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 322 insertions(+) create mode 100644 .gitignore create mode 100644 data/snapshot.db create mode 100644 package.json create mode 100644 src/eip_processor.ts create mode 100644 src/index.ts create mode 100644 src/local_db.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e9f4807 --- /dev/null +++ b/.gitignore @@ -0,0 +1,47 @@ +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz +*.swp + +pids +logs +results +tmp + +# Build +public/css/main.css + +# Coverage reports +coverage + +# API keys and secrets +.env + +# Dependency directory +node_modules +bower_components + +# Editors +.idea +*.iml + +# OS metadata +.DS_Store +Thumbs.db + +# Ignore built ts files +dist/**/* + +# ignore yarn.lock +yarn.lock + +package-lock.json + +.bak +.bk + diff --git a/data/snapshot.db b/data/snapshot.db new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json new file mode 100644 index 0000000..61ede7c --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "snapshot-node-poc", + "version": "0.1.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node -r ts-node/register src/index.ts", + "compile-aqua": "aqua --import . -i ./aqua/ -o ./src/_aqua", + "watch-aqua": "chokidar \"**/*.aqua\" -c \"npm run compile-aqua\"" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@fluencelabs/aqua": "^0.3.1-231", + "chokidar-cli": "^3.0.0", + "ts-node": "^10.2.1", + "typescript": "^4.4.2" + }, + "dependencies": { + "@fluencelabs/fluence": "0.13.0", + "@fluencelabs/fluence-network-environment": "1.0.10", + "@types/sqlite3": "^3.1.7", + "ethers": "^5.4.7", + "ethers-eip712": "^0.2.0", + "sqlite3": "^4.2.0" + } +} diff --git a/src/eip_processor.ts b/src/eip_processor.ts new file mode 100644 index 0000000..3820d51 --- /dev/null +++ b/src/eip_processor.ts @@ -0,0 +1,73 @@ +import { ethers } from "ethers"; +// import { TypedDataUtils } from 'ethers-eip712'; // https://github.com/0xsequence/ethers-eip712 + +// move that +const MAX_TS_DELTA = 180; // 3 minutes -- 60*3 + +export interface Response { + peer_id: string; // may replace with random signing address for now + timestamp: number; + eip_validation: boolean; + ts_validation: any; +} + +// local UTC epoch +function get_local_ts(): number { + return Math.round(Date.now() / 1000); +} + +// validate snapshot timestamp against node +/- delta allowance +function ts_comp(peer_ts: number, snapshot_ts: number, ts_delta: number): boolean { + const ts_diff = Math.abs(peer_ts - snapshot_ts); + if (ts_diff <= ts_delta) { + return true; + } + return false; +} + + +function check_signature(eip_obj: any): boolean { + const signing_addr = ethers.utils.verifyTypedData(eip_obj.data.domain, eip_obj.data.types, eip_obj.data.message, eip_obj.sig); + + // there may be an upper/lowercase hex difference + const sig_assert = (signing_addr == eip_obj.address); + console.log("sig assert: ", sig_assert); + return sig_assert; +} + + +export async function eip_validation(eip_str: string, peer_id: string): Promise { + + const eip_obj = JSON.parse(eip_str); + + // verify eip document integrity -- not working + // const digest = TypedDataUtils.encodeDigest(eip_obj.data); + // const digest_hex = ethers.utils.hexlify(digest); + + // verify signed EIP hash + const sig_check = check_signature(eip_obj); + + var response: Response; + + if (!sig_check) { + response = { peer_id: peer_id, timestamp: get_local_ts(), eip_validation: false, ts_validation: null }; + } + else { + // validate choices -- not implemented + + // validate timestamp + const peer_ts = get_local_ts(); + const ts_check = ts_comp(peer_ts, eip_obj.data.message.timestamp, MAX_TS_DELTA); + + if (!ts_check) { + // no good + response = { peer_id: peer_id, timestamp: peer_ts, eip_validation: true, ts_validation: false }; + } + else { + response = { peer_id: peer_id, timestamp: peer_ts, eip_validation: true, ts_validation: true }; + } + + } + + return response; +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..c2460c7 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,54 @@ +// import { Fluence } from "@fluencelabs/fluence"; +// import { krasnodar } from "@fluencelabs/fluence-network-environment"; +// import { registerCalc, CalcDef } from "./_aqua/calc"; + +import { ethers } from "ethers"; +import { TypedDataUtils } from 'ethers-eip712'; // https://github.com/0xsequence/ethers-eip712 +import { eip_validation, Response } from "./eip_processor"; +import { get_db, crate_table, insert_event } from './local_db'; + + +const DB_PATH = './data/snapshot.db'; + +function create_wallet(): ethers.Wallet { + return ethers.Wallet.createRandom(); +} + +function sign_response(wallet: ethers.Wallet, response: Response): Promise { + const signed_msg = wallet.signMessage(JSON.stringify(response)); + return signed_msg; +} + + +async function main() { + // https://ipfs.fleek.co/ipfs/QmWGzSQFm57ohEq2ATw4UNHWmYU2HkMjtedcNLodYywpmS + // should come from Aqua + // note: I changed \" to \\" in order for JSON.parse to work + const eip712_doc = `{"address":"0xeF8305E140ac520225DAf050e2f71d5fBcC543e7","sig":"0xc0a90a0bf43c0b774570608bf0279143b366b7880798112b678b416a7500576b41e19f7b4eb457d58de29be3a201f700fafab1f02179da0faae653b7e8ecf82b1c","data":{"domain":{"name":"snapshot","version":"0.1.4"},"types":{"Proposal":[{"name":"from","type":"address"},{"name":"space","type":"string"},{"name":"timestamp","type":"uint64"},{"name":"type","type":"string"},{"name":"title","type":"string"},{"name":"body","type":"string"},{"name":"choices","type":"string[]"},{"name":"start","type":"uint64"},{"name":"end","type":"uint64"},{"name":"snapshot","type":"uint64"},{"name":"network","type":"string"},{"name":"strategies","type":"string"},{"name":"plugins","type":"string"},{"name":"metadata","type":"string"}]},"message":{"space":"fabien.eth","type":"single-choice","title":"This is a long title this is a long title this is a long title this is a long title this is a long title this is a long","body":"This is a long title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title title this is a long title this is a long title.","choices":["Approve","Reject"],"start":1630472400,"end":1640926800,"snapshot":9278489,"network":"4","strategies":"[{\\"name\\":\\"ticket\\",\\"params\\":{\\"value\\":100,\\"symbol\\":\\"$\\"}}]","plugins":"{}","metadata":"{}","from":"0xeF8305E140ac520225DAf050e2f71d5fBcC543e7","timestamp":1631432106}}}`; + + // todo: replace with actual peer pk and sk + const wallet = create_wallet(); + const peer_id = wallet.address; + let response = await eip_validation(eip712_doc, peer_id); + + + + // stringify, hexlify, hash and sign response + const resp_str = JSON.stringify(response); + console.log("eip validation response: ", resp_str); + + // just sign the raw message string for a long message + const signed_response = await wallet.signMessage(resp_str); + console.log("signed response: ", signed_response); + + // verify + const address = ethers.utils.verifyMessage(resp_str, signed_response); + console.log("verify signature. peer_id: ", peer_id, " verified addr: ", address, " equal: ", peer_id === address); + + // return (resp_str, signed_response); + + var db = get_db('./data/snapshot.db'); + // crate_table(db); +} + +main(); diff --git a/src/local_db.ts b/src/local_db.ts new file mode 100644 index 0000000..91b4100 --- /dev/null +++ b/src/local_db.ts @@ -0,0 +1,52 @@ +// import { sqlite3, open } from 'sqlite3'; +var sqlite3 = require("sqlite3").verbose(); +import { Response } from './eip_processor'; + + + +// db handler +export async function get_db(db_path: any) { + var db_path = db_path; + + if (db_path === null) { + db_path = ':memory'; + } + + let db = new sqlite3.Database(db_path, (err: any) => { + if (err) { + return console.error("failure to get sqlite: ", err.message); + } + }); + return db; + +} + +export async function crate_table(db: any) { + const stmt = `create table if not exists snapshot.db ( + snapshot_id: integer primary key, + event_address: text, + event_signature: text, + eip712_doc text, + peer_id: text, + timestamp: integer; + eip_validation: boolean; + ts_validation: boolean; + signed_response text + )`; + + db.run(stmt); +} + +export async function insert_event(db: any, eip_obj: any, response_obj: Response, signed_msg: string) { + const stmt = `insert into snapshot.db ( + values (?,?,?,?,?,?,?,?,?) + )`; + const values = [eip_obj.data.message.snapshot, eip_obj.address, eip_obj.sig, JSON.stringify(eip_obj.data), response_obj.peer_id, response_obj.timestamp, response_obj.eip_validation, response_obj.ts_validation, signed_msg]; + db.run(stmt, values, function (err: any) { + if (err) { + return console.log(err.message); + } + // console.log(`A row has been inserted with row id: ${this.lastID}`); + // console.log(`A row has been inserted with row id: ${db.}`); + }); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..909f5f9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,69 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} \ No newline at end of file