UNPKG

godprotocol

Version:

A distributed computing environment for Web 4.0 — integrating AI, decentralisation, and virtual computation.

478 lines (422 loc) 14.7 kB
import create_server from "../utils/create_server.js"; import Account from "./Account.js"; import GDS from "generalised-datastore"; import { hash } from "godprotocol/utils/hash.js"; import { copy_object } from "generalised-datastore/utils/functions.js"; import Clock from "./Clock.js"; class Manager extends Clock { constructor(name, options) { super(); this.name = name; this.options = options; this.trinity = options.trinity; this.accounts = new Object(); this.clock_speed = 0; this.accounts_pairs = new Object(); } swap_result = async (id, thread) => { if (typeof id === "number") { id = thread.results[id]; } return id; }; set_bullet = async (bullet, thread) => { switch (bullet.type) { case "assignment": bullet.value = await this.swap_result(bullet.value, thread); break; case "call": bullet.identifier = await this.swap_result(bullet.identifier, thread); for (let a = 0; a < bullet.arguments.length; a++) { bullet.arguments[a] = await this.swap_result( bullet.arguments[a], thread ); } break; case "return": bullet.output = await this.swap_result(bullet.output, thread); break; case "module": for (let b = 0; b < bullet.body.length; b++) { bullet.body[b] = await this.swap_result(bullet.body[b], thread); } break; case "nest": for (let n = 0; n < bullet.nests.length; n++) { bullet.nests[n] = await this.swap_result(bullet.nests[n], thread); } if (bullet.assignment) { bullet.assignment = await this.swap_result(bullet.assignment, thread); } break; case "twain": for (let key in bullet.value) { let [prop, value] = bullet.value[key]; bullet.value[key][0] = await this.swap_result(prop, thread); bullet.value[key][1] = await this.swap_result(value, thread); } break; case "array": for (let i = 0; i < bullet.value.length; i++) { let val = bullet.value[i]; bullet.value[i] = await this.swap_result(val, thread); } break; case "variable": break; case "try": } return bullet; }; _stack = new Array(); break_count = 0; start_clock = async () => { this.clock = setInterval(async () => { this.clock_running = true; for (let account in this.accounts_pairs) { let account_pair = this.accounts_pairs[account]; let { threads, threadlist, state, current_thread } = account_pair; if (state === "running") continue; let total_loop = 0; while (threadlist.length && total_loop < threadlist.length) { let thread_id = threadlist[account_pair.current_thread]; let thread = threads[thread_id]; if (!thread) { account_pair.current_thread = 0; continue; } let { bullets, pointer, callback, results, chain, spawn, status, _stack, on_pointer, } = thread; if (status && status.start) { if (Date.now() - status.start > status.duration) { thread.status = "active"; } } if (status === "active") { account_pair.current_thread++; account_pair.state = "running"; } else { total_loop++; account_pair.current_thread++; continue; } total_loop = 0; let current_bullet = bullets[pointer], res; if (current_bullet) { if (current_bullet.type === "loop") { _stack.push(current_bullet); } else if (current_bullet.type === "try") { _stack.push(current_bullet); } else if (current_bullet.type === "catch") { let try_conf = _stack.pop(); thread.pointer += try_conf.catch_offset; } else if (current_bullet.type === "endloop") { let loop_conf = _stack.pop(); if (!loop_conf || loop_conf.type !== "loop") { console.log(`ErrorLog: Corrupt stacking`); thread.pointer = bullets.length; } else thread.pointer = loop_conf.conditional_jmp; } else { let account_obj = this.accounts[account]; if (Object.keys(current_bullet).includes("jmp")) { } else res = await account_obj.vm.execute( this.set_bullet(copy_object(current_bullet), thread), { chain, manager: this, thread } ); if (res && res.__interrupt__) { if (res.type === "return") { if (spawn) { let thrd = threads[spawn.thread_id]; thrd.results[spawn.pointer] = res.output; thread.pointer = bullets.length - 1; } else res = res.output; } else if (["break", "continue"].includes(res.__interrupt__)) { let caught; while (_stack.length) { let loop = _stack.slice(-1)[0]; if (loop && loop.type === "loop") { if (res.__interrupt__ === "break") { thread.pointer = loop.conditional_jmp - 1; } else if (res.__interrupt__ === "continue") { thread.pointer = loop.update_jmp - 1; } caught = true; break; } _stack.pop(); } if (!caught) { console.log( `ErrorLog: Loop keywords can only be used inside a loop` ); thread.pointer = bullets.length; } } else { let i = _stack.length - 1, caught; while (_stack.length) { let tstack = _stack[i]; i--; let t_catch = bullets[tstack.catch_index]; if ( !t_catch.objects.length || t_catch.objects.includes(res.payload.type) ) { thread.pointer = tstack.catch_index; if (tstack.alias) await account_obj.vm.error_instance( { location: tstack.location, payload: res.payload }, tstack.alias ); _stack.pop(); caught = true; break; } _stack.pop(); } if (!caught) { console.log(`ErrorLog: ${res.payload.message}`); if (spawn) { let thrd = threads[spawn.thread_id]; thrd.results[spawn.pointer] = res; } thread.pointer = bullets.length; } } } else { if (current_bullet.type === "assignment") { bullets.map((bull) => { if ( bull.type === "variable" && bull.location === current_bullet.location ) { bull._id = res._id; } }); } if ( current_bullet.type === "nest" && current_bullet.assignment ) { if (current_bullet.assignment_location) { bullets.map((bull) => { if ( bull.type === "variable" && bull.location === current_bullet.assignment_location ) { bull._id = res._id; let trd = thread; while (trd.spawn) { let tid = trd.spawn.thread_id; trd = threads[tid]; trd.bullets.map((bl) => { if ( bl.type === "variable" && bl.location === current_bullet.assignment_location ) { bl._id = res._id; } }); } } }); } } if (current_bullet.conditional_jmp) { if (!res.literal) { console.log("Manager.js::SHOUT!"); } let pass = await res.literal(); if (current_bullet.invert) { if (!pass) { thread.pointer = current_bullet.conditional_jmp - 1; } } else { if (!pass) { thread.pointer = current_bullet.conditional_jmp - 1; } } } if (current_bullet.jmp) { thread.pointer = current_bullet.jmp - 1; } } results[pointer] = res; on_pointer && (await on_pointer(pointer, res)); } } thread.pointer++; if (thread.pointer >= bullets.length) { callback && (await callback(res, thread)); delete threads[thread_id]; delete this.accounts[account].vm.envs[thread_id]; account_pair.current_thread--; threadlist.splice(account_pair.current_thread, 1); if (spawn) { let thrd = threads[spawn.thread_id]; if (thrd) { thrd.status = "active"; } } } if (account_pair.current_thread === threadlist.length) { account_pair.current_thread = 0; } account_pair.state = "active"; break; } if (!threadlist.length) { delete this.accounts_pairs[account]; if (!Object.keys(this.accounts_pairs).length) { this.clock_running = false; clearInterval(this.clock); } } } }, this.clock_speed); }; load = async (thread, account, options = {}) => { let { callback, on_pointer, spawn, addr, pointer } = options; let tracker = this.accounts_pairs[account.name]; if (!tracker) { tracker = { threadlist: [], threads: {}, current_thread: 0, }; this.accounts_pairs[account.name] = tracker; } let thread_id = Math.random().toString(); tracker.threadlist.push(thread_id); tracker.threads[thread_id] = { bullets: thread, _id: thread_id, on_pointer, pointer: 0, results: {}, _stack: new Array(), status: "active", addr, chain: account.chain, spawn: spawn ? { thread_id: spawn, pointer } : null, callback, }; if (spawn) { let spawn_thread = tracker.threads[spawn]; spawn_thread.status = thread_id; } if (!this.clock_running) { await this.start_clock(); } return thread_id; }; sync = async () => { this.path = `${this.trinity.game}/${this.name}`; this.hash = hash; this.ds = new GDS(this); await this.ds.sync(); if (this.options.server) { this.server = this.options.server; if (this.options.server.port) this.options.server_domain = `${this.options.server.hostname}:${this.options.server.port}`; else this.options.server_domain = this.options.server.hostname; let handle = create_server({ manager: this, ...this.options.server, app: this.options.app, }); this.handler = async (req, res, cb) => { handle(req, res); if (this.options.oracle && !this.served) { this.served = true; this.oracle = this.options.oracle; if (this.options.init_account) { console.log( "Initiate manager account...", this.options.init_account.name ); await this.add_account(this.options.init_account.name, { password: this.options.init_account.password, init: true, }); cb && cb(); } } }; } }; get_init = async () => { let acc = this.init_account; if (!acc && this.options.init_account) { acc = await this.add_account(this.options.init_account.name, { password: this.options.init_account.password, init: true, }); } return acc; }; get_acc = (options) => { let acc = options.account; if (!acc) { acc = this.options.init_account; if (!acc) return; acc = `Accounts/${acc.name}`; } return acc; }; get_account = async (name, password) => { let acc = this.accounts[name]; if (!acc) { let servers = await this.oracle.read(`Accounts/${name}/.servers`); servers = servers ? JSON.parse(servers) : []; if (servers.length) { acc = await this.add_account(name, { servers, password }); if (!acc) return password ? "Invalid Password" : null; } else return password ? "Account not found." : null; } else if (password) { let res = await acc.verify(password); if (!res) return "Invalid Password"; } return acc; }; add_account = async (name, options = {}) => { let acc = this.accounts[name]; if (!acc) { acc = new Account(name, { ...options, manager: this }); let res = await acc.sync(); if (res) this.accounts[name] = acc; else return; } if (options.init) { this.init_account = acc; } return acc; }; folder = async (address, options = {}) => { let acc; if (options.account) { acc = `Accounts/${options.account}`; } else if (this.options.init_account) { acc = `Accounts/${this.options.init_account.name}`; } address = `${acc}/${address}`; let folder = await this.ds.folder(address, { ...options, account: acc, }); return folder; }; } export default Manager;