diff --git a/package.json b/package.json index e44b1ff7..380a7b95 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "supertest": "^6.1.3", "ts-node": "^10.5.0", "tslint": "^6.1.3", - "turbo": "^1.0.9", + "turbo": "^1.8.6", "typescript": "^4.6.2" }, "resolutions": { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index eaf1fcd0..1131c9f2 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -12,6 +12,7 @@ import { isErrorResult, isChainLastBlock, SLASH, + getFile, } from "./utils"; import { zencode_exec } from "zenroom"; import { Zencode } from "@restroom-mw/zencode"; @@ -58,6 +59,12 @@ const dispatch = async (req: Request, res: Response, next: NextFunction) => { return; } + if (req.params[0] === "") { + res.contentType('text/plain') + res.send(getFile("sids")); + return; + } + const runHook = (hook: string, args: any) => { try { return getHooks(hook, res, args); diff --git a/packages/core/src/restroom.ts b/packages/core/src/restroom.ts index b51007ea..3dc53aa7 100644 --- a/packages/core/src/restroom.ts +++ b/packages/core/src/restroom.ts @@ -1,5 +1,7 @@ import { ObjectLiteral } from "@restroom-mw/types"; import { DK } from "./types"; +import { getFile } from "./utils"; +import fs from "fs"; /** * @@ -16,9 +18,17 @@ import { DK } from "./types"; export class Restroom { _req: any; _res: any; - constructor(req: any, res: any) { + constructor(req: any, res: any, sids: string[]) { this._req = req; this._res = res; + const sidsFile = getFile("sids"); + if (sidsFile === null) { + fs.appendFileSync(`${process.env.ZENCODE_DIR as string}/sids`, sids.join('\n')); + } else { + const registered = new Set (sidsFile.split("\n")); + sids.map(s => registered.add(s)); + fs.writeFileSync(`${process.env.ZENCODE_DIR as string}/sids`, Array.from(registered).join('\n')); + } } _hook(name: string, promise: (params: Promise) => Promise) { diff --git a/packages/core/tests/index_test.js b/packages/core/tests/index_test.js index 6aaa6f54..eaa48c9d 100644 --- a/packages/core/tests/index_test.js +++ b/packages/core/tests/index_test.js @@ -44,7 +44,9 @@ test("Check that the middleware detects when zenfile and zencontent are missing const res = await request(app).post("/api/missing-zenfile.chain"); t.true( - res.body.exception.includes("Neither zenFile nor zenContent are declared for block id"), + res.body.exception.includes( + "Neither zenFile nor zenContent are declared for block id" + ), res.body.exception ); t.is(res.status, 500); @@ -62,8 +64,10 @@ test("Check that the middleware provide context when debug is on for missing zen test("Check that the middleware provide context when debug is on for gpp sawroom sample", async (t) => { const app = express(); app.use("/api/*", core); - for(const url of ["gpp-sawroom-sample-debug", - "gpp-sawroom-sample-debug-inplace"]) { + for (const url of [ + "gpp-sawroom-sample-debug", + "gpp-sawroom-sample-debug-inplace", + ]) { const res = await request(app).post(`/api/${url}.chain`); t.is(res.body.context.debugEnabled, true); @@ -74,11 +78,15 @@ test("Check that the middleware provide context when debug is on for gpp sawroom test("Check that the middleware detects a duplicated mapping key in yaml blocks", async (t) => { const app = express(); app.use("/api/*", core); - for(const url of ["duplicated-mapping-key", - "duplicated-mapping-key-inplace"]) { + for (const url of [ + "duplicated-mapping-key", + "duplicated-mapping-key-inplace", + ]) { const res = await request(app).post(`/api/${url}.chain`); - t.true(res.body.exception.includes("YAMLException: duplicated mapping key")); + t.true( + res.body.exception.includes("YAMLException: duplicated mapping key") + ); t.is(res.status, 500); } }); @@ -126,11 +134,11 @@ test("Check that the middleware is able to execute for each chain with for each app.use(bodyParser.json()); app.use("/api/*", core); - for(const url of ["gpp-sawroom-sample-foreach", - "gpp-sawroom-sample-foreach-inplace"]) { - const res = await request(app) - .post(`/api/${url}.chain`) - .send(_data); + for (const url of [ + "gpp-sawroom-sample-foreach", + "gpp-sawroom-sample-foreach-inplace", + ]) { + const res = await request(app).post(`/api/${url}.chain`).send(_data); t.is(res.body.myUsers.Pippo.password, "p1pp0"); t.is(res.body.myUsers.Topolino.password, "t0p0l1n0"); diff --git a/packages/core/tests/validations_test.js b/packages/core/tests/validations_test.js index c47d2197..71758561 100644 --- a/packages/core/tests/validations_test.js +++ b/packages/core/tests/validations_test.js @@ -114,7 +114,10 @@ test("validateZen works correctly if forEachObject is undefined", (t) => { validateZen(singleContext, block); }); - t.is(error.message, "Neither zenFile nor zenContent are declared for block id: currentBlock"); + t.is( + error.message, + "Neither zenFile nor zenContent are declared for block id: currentBlock" + ); }); test("validateZen works correctly if I pass both zenContent and zenFile", (t) => { @@ -128,7 +131,10 @@ test("validateZen works correctly if I pass both zenContent and zenFile", (t) => validateZen(singleContext, block); }); - t.is(error.message, "Cannot declare both zenContent and zenFile for block id: currentBlock"); + t.is( + error.message, + "Cannot declare both zenContent and zenFile for block id: currentBlock" + ); }); test("validatePathsInYml works correctly if different paths are present", (t) => { diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index d568c642..353c19b9 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -15,7 +15,7 @@ class Result extends Model { * * @enum {number} */ -const enum ACTIONS { +enum ACTIONS { /** * Given I have a database uri named {} * @param {string} uri the key of the connection string @@ -81,7 +81,7 @@ const enum ACTIONS { export default (req: Request, res: Response, next: NextFunction) => { try { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, Object.values(ACTIONS)); const parse = (o: string) => rr.safeJSONParse(o, `[DATABASE] Error in JSON format "${o}"`) let content: ObjectLiteral = {}; let contentKeys: string[]; diff --git a/packages/ethereum/src/index.ts b/packages/ethereum/src/index.ts index b2bfeef7..73a6755e 100644 --- a/packages/ethereum/src/index.ts +++ b/packages/ethereum/src/index.ts @@ -1,6 +1,6 @@ import { Restroom } from "@restroom-mw/core"; import { ObjectLiteral } from "@restroom-mw/types"; -import { UTF8_DECODER, zencodeNamedParamsOf } from '@restroom-mw/utils'; +import { UTF8_DECODER, zencodeNamedParamsOf } from "@restroom-mw/utils"; import { NextFunction, Request, Response } from "express"; import { @@ -21,26 +21,44 @@ import { READ_OWNER, READ_ASSET, } from "./actions"; -import Web3 from 'web3'; +import Web3 from "web3"; // import * as ERC20_ABI from './erc20_abi.json' require("dotenv").config(); -const ERC20_ABI = require('./erc20_abi.json'); -const ERC721_ABI = require('./erc721_abi.json'); -const ERC721_METADATA_ABI = require('./erc721_metadata_abi.json'); +const ERC20_ABI = require("./erc20_abi.json"); +const ERC721_ABI = require("./erc721_abi.json"); +const ERC721_METADATA_ABI = require("./erc721_metadata_abi.json"); let web3: Web3 = null; -const BLOCKCHAIN = "ethereum" -const ERC721_TRANSFER_EVENT = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" +const BLOCKCHAIN = "ethereum"; +const ERC721_TRANSFER_EVENT = + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"; const validateWeb3 = () => { - if(web3 == null) throw Error("No connection to a client") -} + if (web3 == null) throw Error("No connection to a client"); +}; export default async (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, [ + CONNECT, + NONCE, + GAS_PRICE, + RETRIEVE, + ERC20_0, + ERC20_0_NAMED, + ERC20_1, + ERC20_1_NAMED, + READ_HEAD, + READ_PREVIOUS, + READ_BALANCE, + READ_BALANCE_ARRAY, + BROADCAST, + READ_TOKEN_ID, + READ_OWNER, + READ_ASSET, + ]); // preserve data passed to zenroom also in restroom let rrData: Record = {}; @@ -50,78 +68,87 @@ export default async (req: Request, res: Response, next: NextFunction) => { const input = rr.combineDataKeys(data, keys); const namedParamsOf = zencodeNamedParamsOf(zencode, input); - const callErc20 = async (command: string, contractAddress: string, - variableName: string, args: string[]) => { - if(!web3.utils.isAddress(contractAddress)) { + const callErc20 = async ( + command: string, + contractAddress: string, + variableName: string, + args: string[] + ) => { + if (!web3.utils.isAddress(contractAddress)) { throw new Error(`Not an ethereum address ${contractAddress}`); } const erc20 = new web3.eth.Contract(ERC20_ABI, contractAddress); const fz = (() => { - switch(command) { - case "decimals": return erc20.methods.decimals; - case "name": return erc20.methods.name; - case "symbol": return erc20.methods.symbol; - case "total supply": return erc20.methods.totalSupply; - case "balance": return erc20.methods.balanceOf; - default: return null; + switch (command) { + case "decimals": + return erc20.methods.decimals; + case "name": + return erc20.methods.name; + case "symbol": + return erc20.methods.symbol; + case "total supply": + return erc20.methods.totalSupply; + case "balance": + return erc20.methods.balanceOf; + default: + return null; } })(); - if(!fz) { + if (!fz) { throw new Error(`Unknown function name ${command}`); } const result = await fz(...args).call(); - data[variableName] = result; - } + }; - if(zencode.match(CONNECT)) { - const [ endpoint ] = namedParamsOf(CONNECT) + if (zencode.match(CONNECT)) { + const [endpoint] = namedParamsOf(CONNECT); web3 = new Web3(endpoint); } - if(zencode.match(NONCE)) { + if (zencode.match(NONCE)) { validateWeb3(); - const [ addressInput ] = namedParamsOf(NONCE); + const [addressInput] = namedParamsOf(NONCE); const address = input[addressInput] || addressInput; const nonce = await web3.eth.getTransactionCount(address); data.ethereum_nonce = nonce.toString(); } - if(zencode.match(GAS_PRICE)) { + if (zencode.match(GAS_PRICE)) { validateWeb3(); const gasPrice = await web3.eth.getGasPrice(); data.gas_price = gasPrice.toString(); } - if(zencode.match(RETRIEVE)) { + if (zencode.match(RETRIEVE)) { const chkParams = zencode.chunkedParamsOf(RETRIEVE, 2); for (const params of chkParams) { const tag = input[params[0]]; const variable = params[1]; - const receipt = await web3.eth.getTransactionReceipt("0x" + tag) - if(!receipt) { - throw new Error("Transaction id doesn't exist") + const receipt = await web3.eth.getTransactionReceipt("0x" + tag); + if (!receipt) { + throw new Error("Transaction id doesn't exist"); } - if(!receipt.status) { + if (!receipt.status) { throw new Error("Failed transaction"); } try { const dataRead = receipt.logs[0].data.slice(2); data[variable] = dataRead; - } catch(e) { - throw new Error("Empty transaction") + } catch (e) { + throw new Error("Empty transaction"); } } } - if(zencode.match(ERC20_0_NAMED)) { + if (zencode.match(ERC20_0_NAMED)) { validateWeb3(); const chkParams = zencode.chunkedParamsOf(ERC20_0_NAMED, 3); - for(const params of chkParams) { + for (const params of chkParams) { const command = params[0]; const contractAddress = input[params[1]] || params[1]; const variableName = params[2]; @@ -129,141 +156,154 @@ export default async (req: Request, res: Response, next: NextFunction) => { } } - if(zencode.match(ERC20_0)) { + if (zencode.match(ERC20_0)) { validateWeb3(); const chkParams = zencode.chunkedParamsOf(ERC20_0, 2); - for(const params of chkParams) { + for (const params of chkParams) { const command = params[0]; const contractAddress = input[params[1]] || params[1]; - await callErc20(command, contractAddress, command.replace(" ", "_"), []); + await callErc20( + command, + contractAddress, + command.replace(" ", "_"), + [] + ); } } - if(zencode.match(ERC20_1)) { + if (zencode.match(ERC20_1)) { validateWeb3(); const chkParams = zencode.chunkedParamsOf(ERC20_1, 3); - for(const params of chkParams) { + for (const params of chkParams) { const command = params[0]; const arg = input[params[1]] || params[1]; const contractAddress = input[params[2]] || params[2]; - await callErc20(command, contractAddress, command.replace(" ", "_"), - [ '0x' + arg ]); + await callErc20(command, contractAddress, command.replace(" ", "_"), [ + "0x" + arg, + ]); } } - if(zencode.match(ERC20_1_NAMED)) { + if (zencode.match(ERC20_1_NAMED)) { validateWeb3(); const chkParams = zencode.chunkedParamsOf(ERC20_1_NAMED, 4); - for(const params of chkParams) { + for (const params of chkParams) { const command = params[0]; const arg = input[params[1]] || params[1]; const contractAddress = input[params[2]] || params[2]; const variableName = params[3]; - await callErc20(command, contractAddress, variableName, - [ '0x' + arg ]); + await callErc20(command, contractAddress, variableName, ["0x" + arg]); } } - if(zencode.match(READ_HEAD)) { + if (zencode.match(READ_HEAD)) { const chkParams = zencode.chunkedParamsOf(READ_HEAD, 2); - for(const params of chkParams) { + for (const params of chkParams) { const storage = params[0]; const variableName = params[1]; - if(storage.toLowerCase() == BLOCKCHAIN) { + if (storage.toLowerCase() == BLOCKCHAIN) { const result = await web3.eth.getBlock("latest"); data[variableName] = result.hash.slice(2); } } } - if(zencode.match(READ_PREVIOUS)) { + if (zencode.match(READ_PREVIOUS)) { const chkParams = zencode.chunkedParamsOf(READ_PREVIOUS, 3); - for(const params of chkParams) { + for (const params of chkParams) { const storage = params[0]; - const blockHash = input[params[1]] || data[params[1]] - || params[1]; + const blockHash = input[params[1]] || data[params[1]] || params[1]; const variableName = params[2]; - if(storage.toLowerCase() == BLOCKCHAIN) { + if (storage.toLowerCase() == BLOCKCHAIN) { validateWeb3(); - const result = await web3.eth.getBlock('0x' + blockHash); + const result = await web3.eth.getBlock("0x" + blockHash); data[variableName] = result.parentHash.slice(2); } } } - if(zencode.match(READ_BALANCE)) { + if (zencode.match(READ_BALANCE)) { validateWeb3(); - const [ addressInput ] = namedParamsOf(READ_BALANCE); + const [addressInput] = namedParamsOf(READ_BALANCE); const address = input[addressInput] || addressInput; const balance = await web3.eth.getBalance(address); data.ethereum_balance = balance.toString(); } - if(zencode.match(READ_BALANCE_ARRAY)) { + if (zencode.match(READ_BALANCE_ARRAY)) { validateWeb3(); - for (const [addressesName, balancesName] - of zencode.chunkedParamsOf(READ_BALANCE_ARRAY,2)) { - const addressesInput = input[addressesName] || data[addressesName] - if(!addressesInput) { + for (const [addressesName, balancesName] of zencode.chunkedParamsOf( + READ_BALANCE_ARRAY, + 2 + )) { + const addressesInput = input[addressesName] || data[addressesName]; + if (!addressesInput) { throw new Error(`Could not find ${addressesInput} anywhere`); } - if(!Array.isArray(addressesInput)) { + if (!Array.isArray(addressesInput)) { throw new Error(`${addressesInput} is not an array`); } const balances = await Promise.all( - addressesInput.map(v => web3.eth.getBalance(v))) - data[balancesName] = balances.map(v => - {return {wei_value: v.toString()}}) + addressesInput.map((v) => web3.eth.getBalance(v)) + ); + data[balancesName] = balances.map((v) => { + return { wei_value: v.toString() }; + }); } } - // ERC 721 - if(zencode.match(READ_TOKEN_ID)) { + if (zencode.match(READ_TOKEN_ID)) { validateWeb3(); - const [ txidName ] = namedParamsOf(READ_TOKEN_ID); + const [txidName] = namedParamsOf(READ_TOKEN_ID); const txid = input[txidName] || txidName; - const receipt = await web3.eth.getTransactionReceipt('0x' + txid); + const receipt = await web3.eth.getTransactionReceipt("0x" + txid); const log = receipt.logs.find( - v => v.topics.length > 0 && v.topics[0] === ERC721_TRANSFER_EVENT); - if(!log) { - throw new Error("Token Id not found") + (v) => v.topics.length > 0 && v.topics[0] === ERC721_TRANSFER_EVENT + ); + if (!log) { + throw new Error("Token Id not found"); } data.erc721_id = parseInt(log.topics[3], 16); } - if(zencode.match(READ_OWNER)) { + if (zencode.match(READ_OWNER)) { validateWeb3(); const [tokenName, contractName] = namedParamsOf(READ_OWNER); - const contractAddress = data[contractName] || input[contractName] || contractName; + const contractAddress = + data[contractName] || input[contractName] || contractName; const token = data[tokenName] || input[tokenName] || tokenName; const erc721 = new web3.eth.Contract(ERC721_ABI, contractAddress); - const owner = await erc721.methods.ownerOf(token).call() - data.owner = owner.substring(2) + const owner = await erc721.methods.ownerOf(token).call(); + data.owner = owner.substring(2); } - if(zencode.match(READ_ASSET)) { + if (zencode.match(READ_ASSET)) { validateWeb3(); const [tokenName, contractName] = namedParamsOf(READ_ASSET); - const contractAddress = data[contractName] || input[contractName] || contractName; + const contractAddress = + data[contractName] || input[contractName] || contractName; const token = data[tokenName] || input[tokenName] || tokenName; - const erc721 = new web3.eth.Contract(ERC721_METADATA_ABI, contractAddress); - const asset = await erc721.methods.tokenURI(token).call() - data.asset = asset + const erc721 = new web3.eth.Contract( + ERC721_METADATA_ABI, + contractAddress + ); + const asset = await erc721.methods.tokenURI(token).call(); + data.asset = asset; } }); rr.onSuccess(async (params) => { const { zencode, result } = params; - if(zencode.match(BROADCAST)) { + if (zencode.match(BROADCAST)) { validateWeb3(); const chkParams = zencode.chunkedParamsOf(BROADCAST, 2); - for(const params of chkParams) { + for (const params of chkParams) { const rawtx = result[params[0]]; const tag = params[1]; - if(rawtx && tag) { - const receipt = await web3.eth.sendSignedTransaction('0x' + rawtx); - if(receipt.status) { + if (rawtx && tag) { + const receipt = await web3.eth.sendSignedTransaction("0x" + rawtx); + if (receipt.status) { result[tag] = receipt.transactionHash.substring(2); // Remove 0x } else { throw new Error("Transaction failed"); diff --git a/packages/fabric/src/index.ts b/packages/fabric/src/index.ts index 138f6b7b..bae6706f 100644 --- a/packages/fabric/src/index.ts +++ b/packages/fabric/src/index.ts @@ -100,7 +100,14 @@ const validateStep = (requested: FabricInterop) => { } export default async (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, [ADDRESS, + CONNECT, + CHANNEL, + CONTRACT, + QUERY, + SUBMIT, + STORE, + RETRIEVE]); try { rr.onBefore(async (params) => { diff --git a/packages/files/src/index.ts b/packages/files/src/index.ts index 2a4257b0..600bb16f 100644 --- a/packages/files/src/index.ts +++ b/packages/files/src/index.ts @@ -1,13 +1,13 @@ -import {Restroom} from "@restroom-mw/core"; +import { Restroom } from "@restroom-mw/core"; import axios from "axios"; -import {NextFunction, Request, Response} from "express"; -import fs from 'fs' -import { readdir, stat } from 'node:fs/promises'; -import os from 'os' -import extract from 'extract-zip'; -import path from 'path'; -import {ObjectLiteral} from "@restroom-mw/types"; -import {validateSubdir} from "@restroom-mw/utils" +import { NextFunction, Request, Response } from "express"; +import fs from "fs"; +import { readdir, stat } from "node:fs/promises"; +import os from "os"; +import extract from "extract-zip"; +import path from "path"; +import { ObjectLiteral } from "@restroom-mw/types"; +import { validateSubdir } from "@restroom-mw/utils"; require("dotenv").config(); /** @@ -16,17 +16,16 @@ require("dotenv").config(); * Download a zip file located at the url `myUrl` and extract it at the path * `myFolder` on the server. */ -import {DOWNLOAD} from "./actions"; +import { DOWNLOAD } from "./actions"; /** * `store 'myVariable' in the file 'myFolder'` * * Store the content of the variable `myVariable` in the filesystem at the path * `myFolder` on the server */ -import {STORE_RESULT} from "./actions"; - -import {READ, READ_AND_SAVE, LS} from "./actions"; +import { STORE_RESULT } from "./actions"; +import { READ, READ_AND_SAVE, LS } from "./actions"; /** * Base dir to store data for the user @@ -41,101 +40,112 @@ let input: ObjectLiteral = {}; const validatePath = validateSubdir(FILES_DIR); export default (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, [ + DOWNLOAD, + LS, + READ, + READ_AND_SAVE, + STORE_RESULT, + ]); rr.onBefore(async (params) => { const { zencode, keys, data } = params; input = rr.combineDataKeys(data, keys); - const readFromFile = ( file: string ) => { - let content + const readFromFile = (file: string) => { + let content; const f = input[file] || file; const absoluteFile = path.resolve(path.join(FILES_DIR, f)); validatePath(absoluteFile); try { - content = fs.readFileSync(absoluteFile, 'utf8'); - } catch(e) { + content = fs.readFileSync(absoluteFile, "utf8"); + } catch (e) { // TODO: add not-fatal (warning) log in restroom //throw new Error(`[FILES] error while reading the file ${absoluteFile}`); content = "{}"; } return JSON.parse(content); - } + }; if (zencode.match(READ)) { const params = zencode.paramsOf(READ); - for(const f of params) { + for (const f of params) { Object.assign(data, readFromFile(f)); } - }; + } if (zencode.match(READ_AND_SAVE)) { const chkParams = zencode.chunkedParamsOf(READ_AND_SAVE, 2); - for(const [f, outputVariable] of chkParams) { - data[ outputVariable ] = readFromFile(f); + for (const [f, outputVariable] of chkParams) { + data[outputVariable] = readFromFile(f); } - }; + } if (zencode.match(LS)) { const params = zencode.chunkedParamsOf(LS, 2); - const fileStats: Record = {} - const allLs = (await Promise.all(params.map( - async ([p, name]: string[]) => { - const f = input[p] || p; - validatePath(f); - try { - const content = await readdir(f) - // I am not checking if `name` is used multiple times - fileStats[name] = [] - return content.map((current) => [name, f, current]); - } catch(e) { - throw new Error(`[FILES] error while reading the file ${f}`); - } - }))).flat() + const fileStats: Record = {}; + const allLs = ( + await Promise.all( + params.map(async ([p, name]: string[]) => { + const f = input[p] || p; + validatePath(f); + try { + const content = await readdir(f); + // I am not checking if `name` is used multiple times + fileStats[name] = []; + return content.map((current) => [name, f, current]); + } catch (e) { + throw new Error(`[FILES] error while reading the file ${f}`); + } + }) + ) + ).flat(); // list with all files I want to see the stats of - const allStats = await Promise.all(allLs.map(async ([name, p, current]) => { - const currentFile = path.join(p, current) - const fileStat = await stat(currentFile) - return [name, current, fileStat] - })) - for(const [name, current, currentStat] of allStats) { + const allStats = await Promise.all( + allLs.map(async ([name, p, current]) => { + const currentFile = path.join(p, current); + const fileStat = await stat(currentFile); + return [name, current, fileStat]; + }) + ); + for (const [name, current, currentStat] of allStats) { // see https://unix.stackexchange.com/questions/317855/file-mode-on-macosx#317907 // for the meaning of the mode field fileStats[name].push({ - 'name': current, - 'mode': currentStat.mode.toString(8), - 'dev': currentStat.dev, - 'nlink': currentStat.nlink, - 'uid': currentStat.uid, - 'gid': currentStat.gid, - 'size': currentStat.size, - 'blksize': currentStat.blksize, - 'blocks': currentStat.blocks, - 'atime': currentStat.atime.toISOString(), - 'mtime': currentStat.mtime.toISOString(), - 'ctime': currentStat.ctime.toISOString(), - 'birthtime': currentStat.birthtime.toISOString(), - }) + name: current, + mode: currentStat.mode.toString(8), + dev: currentStat.dev, + nlink: currentStat.nlink, + uid: currentStat.uid, + gid: currentStat.gid, + size: currentStat.size, + blksize: currentStat.blksize, + blocks: currentStat.blocks, + atime: currentStat.atime.toISOString(), + mtime: currentStat.mtime.toISOString(), + ctime: currentStat.ctime.toISOString(), + birthtime: currentStat.birthtime.toISOString(), + }); } - Object.assign(data, fileStats) - + Object.assign(data, fileStats); } input = rr.combineDataKeys(data, keys); }); rr.onSuccess(async (params) => { - const {result, zencode} = params; + const { result, zencode } = params; if (zencode.match(DOWNLOAD)) { const allPassOutputs = zencode.paramsOf(DOWNLOAD); for (let i = 0; i < allPassOutputs.length; i += 2) { - const file = result[allPassOutputs[i]] || - input[allPassOutputs[i]]; - const folder = result[allPassOutputs[i + 1]] || - input[allPassOutputs[i + 1]]; + const file = result[allPassOutputs[i]] || input[allPassOutputs[i]]; + const folder = + result[allPassOutputs[i + 1]] || input[allPassOutputs[i + 1]]; - if(!file) { + if (!file) { throw new Error(`[FILES] url not defined`); } - if(!folder) { - throw new Error(`[FILES] destination folder not defined ${Object.keys(input)}`); + if (!folder) { + throw new Error( + `[FILES] destination folder not defined ${Object.keys(input)}` + ); } try { @@ -143,9 +153,9 @@ export default (req: Request, res: Response, next: NextFunction) => { validatePath(absoluteFolder); const response = await axios.get(file, { - responseType: 'arraybuffer' + responseType: "arraybuffer", }); - const tempdir = fs.mkdtempSync(path.join(os.tmpdir(), 'restroom-')); + const tempdir = fs.mkdtempSync(path.join(os.tmpdir(), "restroom-")); const tempfile = path.join(tempdir, "downloaded"); fs.writeFileSync(tempfile, response.data); await extract(tempfile, { dir: absoluteFolder }); @@ -153,26 +163,27 @@ export default (req: Request, res: Response, next: NextFunction) => { fs.rmdirSync(tempdir); } catch (e) { next(e); - throw new Error(`[FILES] Error sending the result to "${file}": ${e}`); + throw new Error( + `[FILES] Error sending the result to "${file}": ${e}` + ); } } } if (zencode.match(STORE_RESULT)) { const allPassOutputs = zencode.paramsOf(STORE_RESULT); for (let i = 0; i < allPassOutputs.length; i += 2) { - const variable = result[allPassOutputs[i]] || - input[allPassOutputs[i]]; - const file = result[allPassOutputs[i + 1]] || - input[allPassOutputs[i + 1]]; + const variable = result[allPassOutputs[i]] || input[allPassOutputs[i]]; + const file = + result[allPassOutputs[i + 1]] || input[allPassOutputs[i + 1]]; - if(!variable) { + if (!variable) { throw new Error(`[FILES] variable not defined`); } - if(!file) { + if (!file) { throw new Error(`[FILES] destination file not defined`); } - const variableJson = JSON.stringify(variable) + const variableJson = JSON.stringify(variable); try { const absoluteFile = path.resolve(path.join(FILES_DIR, file)); validatePath(absoluteFile); diff --git a/packages/git/src/index.ts b/packages/git/src/index.ts index 5e7f252a..e4cc5e69 100644 --- a/packages/git/src/index.ts +++ b/packages/git/src/index.ts @@ -1,12 +1,12 @@ import { Restroom } from "@restroom-mw/core"; import { NextFunction, Request, Response } from "express"; import { ObjectLiteral } from "@restroom-mw/types"; -import { Action } from "./actions" -import git from 'isomorphic-git' -import http from 'isomorphic-git/http/node' -import path from 'path' -import fs from 'fs' -import {validateSubdir} from "@restroom-mw/utils" +import { Action } from "./actions"; +import git from "isomorphic-git"; +import http from "isomorphic-git/http/node"; +import path from "path"; +import fs from "fs"; +import { validateSubdir } from "@restroom-mw/utils"; export const FILES_DIR = process.env.FILES_DIR; @@ -18,10 +18,10 @@ type ZenroomCommit = { email: string; files: string[]; message: string; -} +}; export default (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, Object.keys(Action)); let input: ObjectLiteral = null; rr.onBefore(async (params: any) => { @@ -30,29 +30,31 @@ export default (req: Request, res: Response, next: NextFunction) => { if (zencode.match(Action.CLONE)) { const args = zencode.chunkedParamsOf(Action.CLONE, 2); - for(const [repoUrlName, repoPathName] of args) { + for (const [repoUrlName, repoPathName] of args) { const repoUrl = input[repoUrlName] || repoUrlName; const repoPath = input[repoPathName] || repoPathName; const absoluteRepo = path.resolve(path.join(FILES_DIR, repoPath)); validatePath(absoluteRepo); - await git.clone({ fs, http, dir: absoluteRepo, url: repoUrl }) + await git.clone({ fs, http, dir: absoluteRepo, url: repoUrl }); } } if (zencode.match(Action.VERIFY)) { const args = zencode.chunkedParamsOf(Action.VERIFY, 1); let errorMsg = null; - await Promise.all(args.map(async ([pathName]: string[]) => { - const repo = input[pathName] || pathName; - const absolutePath = path.resolve(path.join(FILES_DIR, repo)); - validatePath(absolutePath) - return git.findRoot({fs, filepath: absolutePath}).catch( - () => errorMsg = repo - ); - })); + await Promise.all( + args.map(async ([pathName]: string[]) => { + const repo = input[pathName] || pathName; + const absolutePath = path.resolve(path.join(FILES_DIR, repo)); + validatePath(absolutePath); + return git + .findRoot({ fs, filepath: absolutePath }) + .catch(() => (errorMsg = repo)); + }) + ); - if(errorMsg != null) { - throw new Error(`[GIT] ${errorMsg} is not a git repository`) + if (errorMsg != null) { + throw new Error(`[GIT] ${errorMsg} is not a git repository`); } } }); @@ -62,27 +64,36 @@ export default (req: Request, res: Response, next: NextFunction) => { if (zencode.match(Action.COMMIT)) { const [repoPathName] = zencode.paramsOf(Action.COMMIT); - const repoPath = result[repoPathName] - || input[repoPathName] || repoPathName; + const repoPath = + result[repoPathName] || input[repoPathName] || repoPathName; const absoluteRepo = path.resolve(path.join(FILES_DIR, repoPath)); validatePath(absoluteRepo); - const commitDict = result.commit || input.commit - if(!commitDict) { - throw new Error(`[GIT] commit details not found`) + const commitDict = result.commit || input.commit; + if (!commitDict) { + throw new Error(`[GIT] commit details not found`); } const commit = commitDict as ZenroomCommit; - await Promise.all(commit.files.map((file) => { - const absoluteFile = path.resolve(path.join(FILES_DIR, file)); - return git.add({fs, dir: absoluteRepo, - filepath: path.relative(absoluteRepo, absoluteFile)}) - })) + await Promise.all( + commit.files.map((file) => { + const absoluteFile = path.resolve(path.join(FILES_DIR, file)); + return git.add({ + fs, + dir: absoluteRepo, + filepath: path.relative(absoluteRepo, absoluteFile), + }); + }) + ); - await git.commit({fs, dir: absoluteRepo, - message: commit.message, author: { + await git.commit({ + fs, + dir: absoluteRepo, + message: commit.message, + author: { name: commit.author, email: commit.email, - }}) + }, + }); } }); diff --git a/packages/http/src/actions.ts b/packages/http/src/actions.ts index 66ab774b..a066ec3a 100644 --- a/packages/http/src/actions.ts +++ b/packages/http/src/actions.ts @@ -8,4 +8,3 @@ export const PARALLEL_GET_ARRAY = "execute parallel GET to array {} and save the export const PARALLEL_POST = "execute parallel POST with {} to {} and save the result named {} within the object {}"; export const PARALLEL_POST_ARRAY_WITHIN = "execute parallel POST with {} to array {} and save the result named {} within the object {}"; export const PARALLEL_POST_ARRAY = "execute parallel POST with {} to array {} and save the result named {}"; - diff --git a/packages/http/src/index.ts b/packages/http/src/index.ts index 457008bf..1ce9d8c3 100644 --- a/packages/http/src/index.ts +++ b/packages/http/src/index.ts @@ -25,7 +25,15 @@ import { } from "./utils"; export default (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, [EXTERNAL_CONNECTION, + EXTERNAL_OUTPUT, + PARALLEL_GET, + PARALLEL_GET_ARRAY, + PARALLEL_POST, + PARALLEL_POST_ARRAY_WITHIN, + PARALLEL_POST_ARRAY, + PASS_OUTPUT, + POST_AND_SAVE_TO_VARIABLE]); let content: ObjectLiteral = {}; let externalSourceKeys: string[] = []; let parallel_params: { output: string; index: [string , number]}[] = []; diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index b30829b2..dfb5d3fd 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,12 +1,12 @@ -import {Restroom} from "@restroom-mw/core"; -import {NextFunction, Request, Response} from "express"; -import fs from 'fs' -import path from 'path'; -import {ObjectLiteral} from "@restroom-mw/types"; -import {validateSubdir} from "@restroom-mw/utils"; +import { Restroom } from "@restroom-mw/core"; +import { NextFunction, Request, Response } from "express"; +import fs from "fs"; +import path from "path"; +import { ObjectLiteral } from "@restroom-mw/types"; +import { validateSubdir } from "@restroom-mw/utils"; require("dotenv").config(); -import {APPEND, APPEND_NAMED, APPEND_ARRAY} from "./actions"; +import { APPEND, APPEND_NAMED, APPEND_ARRAY } from "./actions"; export const LOGGER_DIR = process.env.LOGGER_DIR; const validatePath = validateSubdir(LOGGER_DIR); @@ -14,7 +14,7 @@ const validatePath = validateSubdir(LOGGER_DIR); let input: ObjectLiteral = {}; export default (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, [APPEND, APPEND_NAMED, APPEND_ARRAY]); rr.onBefore(async (params) => { const { zencode, keys, data } = params; @@ -22,46 +22,46 @@ export default (req: Request, res: Response, next: NextFunction) => { }); rr.onSuccess(async (params) => { - const {result, zencode} = params; + const { result, zencode } = params; const addLog = (sentences: string[], where: string) => { const absolutePath = path.resolve(path.join(LOGGER_DIR, where)); validatePath(absolutePath); - const ws = fs.createWriteStream(absolutePath, {flags: "a"}); - ws.on('error', (error) => { + const ws = fs.createWriteStream(absolutePath, { flags: "a" }); + ws.on("error", (error) => { throw new Error( - `[LOGGER] An error occurred while writing to ${where}\n${error}`) + `[LOGGER] An error occurred while writing to ${where}\n${error}` + ); }); - sentences.forEach( (v) => ws.write(`${v}\n`) ) - } + sentences.forEach((v) => ws.write(`${v}\n`)); + }; if (zencode.match(APPEND)) { const params = zencode.chunkedParamsOf(APPEND, 2); - for(const [ sentence, where ] of params) { - addLog([ result[sentence] || input[sentence] || sentence ], where); + for (const [sentence, where] of params) { + addLog([result[sentence] || input[sentence] || sentence], where); } } if (zencode.match(APPEND_NAMED)) { const params = zencode.chunkedParamsOf(APPEND_NAMED, 2); - for(const [ sentence, pathName ] of params) { + for (const [sentence, pathName] of params) { const logPath = result[pathName] || input[pathName]; - if(!logPath) { - throw new Error( - `[LOGGER] Could not find path to log ${pathName}`) + if (!logPath) { + throw new Error(`[LOGGER] Could not find path to log ${pathName}`); } - addLog([ result[sentence] || input[sentence] || sentence ], logPath); + addLog([result[sentence] || input[sentence] || sentence], logPath); } } if (zencode.match(APPEND_ARRAY)) { const params = zencode.chunkedParamsOf(APPEND_ARRAY, 2); - for(const [arrayName, where] of params) { + for (const [arrayName, where] of params) { const sentences = input[arrayName] || result[arrayName]; - if(!sentences || !Array.isArray(sentences)) { + if (!sentences || !Array.isArray(sentences)) { throw new Error( - `[LOGGER] Could not find sentences array to log ${arrayName}`) + `[LOGGER] Could not find sentences array to log ${arrayName}` + ); } - addLog(sentences, - result[where] || input[where] || where); + addLog(sentences, result[where] || input[where] || where); } } }); diff --git a/packages/planetmint/package.json b/packages/planetmint/package.json index f919655e..f3c0a921 100644 --- a/packages/planetmint/package.json +++ b/packages/planetmint/package.json @@ -28,7 +28,7 @@ "doc": "documentation readme src/** -g -f md -s 'API' --readme-file ../../docs/packages/planetmint.md --shallow", "links": "yarn link", "unlinks": "yarn unlink", - "lint": "eslint .", + "lint": "tslint -c ../../tslint.json src/**/*.ts", "lint:fix": "eslint --fix --ext .js,.jsx .", "watch": "tsc -b tsconfig.json -w", "build": "tsc -b tsconfig.json" diff --git a/packages/planetmint/src/index.ts b/packages/planetmint/src/index.ts index bf1ab4ce..ed8bec40 100644 --- a/packages/planetmint/src/index.ts +++ b/packages/planetmint/src/index.ts @@ -1,14 +1,17 @@ import { Restroom } from "@restroom-mw/core"; import { ObjectLiteral } from "@restroom-mw/types"; -import { zencodeNamedParamsOf } from '@restroom-mw/utils'; +import { zencodeNamedParamsOf } from "@restroom-mw/utils"; -import { TransactionOperations, TransactionUnspentOutput, - TransactionCommon } from "@planetmint/driver" -import { Connection, Transaction } from '@planetmint/driver' -import { Ed25519Sha256 } from 'crypto-conditions' +import { + TransactionOperations, + TransactionUnspentOutput, + TransactionCommon, +} from "@planetmint/driver"; +import { Connection, Transaction } from "@planetmint/driver"; +import { Ed25519Sha256 } from "crypto-conditions"; import { NextFunction, Request, Response } from "express"; -import base58 from 'bs58'; -import { sha3_256 } from 'js-sha3' +import base58 from "bs58"; +import { sha3_256 } from "js-sha3"; import { CONNECT, ASSET, @@ -21,11 +24,11 @@ import { BROADCAST, RETRIEVE, } from "./actions"; -import * as IPFS from 'ipfs-core' -import all from 'it-all' -import { concat as uint8ArrayConcat } from 'uint8arrays/concat' -import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { toString as uint8ArrayToString } from 'uint8arrays/to-string' +import * as IPFS from "ipfs-core"; +import all from "it-all"; +import { concat as uint8ArrayConcat } from "uint8arrays/concat"; +import { fromString as uint8ArrayFromString } from "uint8arrays/from-string"; +import { toString as uint8ArrayToString } from "uint8arrays/to-string"; interface TransactionRetrieved { asset: Record; @@ -34,24 +37,35 @@ interface TransactionRetrieved { require("dotenv").config(); -var ipfs: any = null +var ipfs: any = null; const storeIPFS = async (data: any) => { - const { cid } = await ipfs.add(uint8ArrayFromString(JSON.stringify(data))) - return cid.toString() -} + const { cid } = await ipfs.add(uint8ArrayFromString(JSON.stringify(data))); + return cid.toString(); +}; const readIPFS = async (cid: string) => { - const data = uint8ArrayConcat(await all(ipfs.cat(cid))) - return JSON.parse(uint8ArrayToString(data)) -} + const data = uint8ArrayConcat(await all(ipfs.cat(cid))); + return JSON.parse(uint8ArrayToString(data)); +}; export default async (req: Request, res: Response, next: NextFunction) => { - if(ipfs == null) { - ipfs = await IPFS.create() + if (ipfs == null) { + ipfs = await IPFS.create(); } let connection: Connection = null; let input: ObjectLiteral = null; let rrData: ObjectLiteral = null; - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, [ + CONNECT, + ASSET, + ASSET_METADATA, + ASSET_AMOUNT, + ASSET_AMOUNT_METADATA, + TRANSFER, + TRANSFER_AMOUNT, + SIGNATURE, + BROADCAST, + RETRIEVE, + ]); try { rr.onBefore(async (params) => { @@ -59,180 +73,187 @@ export default async (req: Request, res: Response, next: NextFunction) => { input = rr.combineDataKeys(data, keys); const namedParamsOf = zencodeNamedParamsOf(zencode, input); - if(zencode.match(CONNECT)) { - const [ endpoint ] = namedParamsOf(CONNECT); + if (zencode.match(CONNECT)) { + const [endpoint] = namedParamsOf(CONNECT); // does not perform any http request connection = new Connection(endpoint); } - if(zencode.match(RETRIEVE)) { - const [ id, out ] = namedParamsOf(RETRIEVE); + if (zencode.match(RETRIEVE)) { + const [id, out] = namedParamsOf(RETRIEVE); try { - const receipt = await connection.getTransaction(id) - const assetTx = receipt.assets[0] - let asset: Record = null + const receipt = await connection.getTransaction(id); + const assetTx = receipt.assets[0]; + let asset: Record = null; - if(receipt.operation === "CREATE") { - const cid = (assetTx as {data: string}).data; - asset = await readIPFS(cid) + if (receipt.operation === "CREATE") { + const cid = (assetTx as { data: string }).data; + asset = await readIPFS(cid); } else { - asset = assetTx + asset = assetTx; } - const txResult: TransactionRetrieved = { 'asset': asset }; + const txResult: TransactionRetrieved = { asset: asset }; /*if(receipt.metadata) { txResult.metadata = receipt.metadata; }*/ - data[ out ]= txResult; + data[out] = txResult; } catch (e) { throw new Error(`Transaction not found: ${id}`); } } - const storeAsset = async ( publicKey: string, assetCID: string, - metadataCID: string, amount: string = "1" ) => { + const storeAsset = async ( + publicKey: string, + assetCID: string, + metadataCID: string, + amount: string = "1" + ) => { const eddsaPublicKey = input[publicKey]; - if(!eddsaPublicKey) { + if (!eddsaPublicKey) { throw new Error("Public key not found"); } const tx = Transaction.makeCreateTransaction( - [ assetCID ], + [assetCID], metadataCID, - [ Transaction.makeOutput( - Transaction.makeEd25519Condition( - eddsaPublicKey), - amount) + [ + Transaction.makeOutput( + Transaction.makeEd25519Condition(eddsaPublicKey), + amount + ), ], eddsaPublicKey ); data.planetmint_transaction = - Transaction.serializeTransactionIntoCanonicalString(tx) - } + Transaction.serializeTransactionIntoCanonicalString(tx); + }; - if(zencode.match(ASSET)) { - const [ asset, publicKey ] = zencode.paramsOf(ASSET); - if( !input[ asset ] ) { + if (zencode.match(ASSET)) { + const [asset, publicKey] = zencode.paramsOf(ASSET); + if (!input[asset]) { throw new Error("Asset not found"); } const assetCID = await storeIPFS(input[asset]); - storeAsset(publicKey, assetCID.toString(), null, "1" ); + storeAsset(publicKey, assetCID.toString(), null, "1"); } - if(zencode.match(ASSET_METADATA)) { - const [ asset, metadata, publicKey ] = zencode.paramsOf(ASSET_METADATA); - if( !input[asset] ) { + if (zencode.match(ASSET_METADATA)) { + const [asset, metadata, publicKey] = zencode.paramsOf(ASSET_METADATA); + if (!input[asset]) { throw new Error("Asset not found"); } - if( !input[metadata] ) { + if (!input[metadata]) { throw new Error("Metadata not found"); } const assetCID = await storeIPFS(input[asset]); const metadataCID = await storeIPFS(input[metadata]); - storeAsset(publicKey, assetCID, metadataCID, "1" ); + storeAsset(publicKey, assetCID, metadataCID, "1"); } - if(zencode.match(ASSET_AMOUNT)) { - const [ amount, asset, publicKey ] = zencode.paramsOf(ASSET_AMOUNT); - if( !input[asset] ) { + if (zencode.match(ASSET_AMOUNT)) { + const [amount, asset, publicKey] = zencode.paramsOf(ASSET_AMOUNT); + if (!input[asset]) { throw new Error("Asset not found"); } - if( !input[amount] ) { + if (!input[amount]) { throw new Error("Amount not found"); } const assetCID = await storeIPFS(input[asset]); - storeAsset(publicKey, assetCID, null, input[ amount ]); + storeAsset(publicKey, assetCID, null, input[amount]); } - if(zencode.match(ASSET_AMOUNT_METADATA)) { - const [ amount, asset, metadata, publicKey ] = - zencode.paramsOf(ASSET_AMOUNT_METADATA); - if( !input[asset] ) { + if (zencode.match(ASSET_AMOUNT_METADATA)) { + const [amount, asset, metadata, publicKey] = zencode.paramsOf( + ASSET_AMOUNT_METADATA + ); + if (!input[asset]) { throw new Error("Asset not found"); } - if( !input[metadata] ) { + if (!input[metadata]) { throw new Error("Metadata not found"); } - if( !input[amount] ) { + if (!input[amount]) { throw new Error("Amount not found"); } - storeAsset(publicKey, input[ asset ], input[ metadata ], input[ amount ]); + storeAsset(publicKey, input[asset], input[metadata], input[amount]); } - if(zencode.match(TRANSFER)) { - const [ txidName, recipientName ] = zencode.paramsOf(TRANSFER); + if (zencode.match(TRANSFER)) { + const [txidName, recipientName] = zencode.paramsOf(TRANSFER); const txid = input[txidName]; const recipient = input[recipientName]; - if( !txid ) { + if (!txid) { throw new Error("Transaction id not found"); } - if( !recipient ) { + if (!recipient) { throw new Error("Recipient public key not found"); } - const txIn = await connection.getTransaction(txid) - if( !txIn ) { + const txIn = await connection.getTransaction(txid); + if (!txIn) { throw new Error(`Transaction with id ${txid} not found`); } const tx = Transaction.makeTransferTransaction( - [ { tx: txIn, output_index: 0 } ], - [ Transaction.makeOutput( - Transaction.makeEd25519Condition( - recipient )) - ], + [{ tx: txIn, output_index: 0 }], + [Transaction.makeOutput(Transaction.makeEd25519Condition(recipient))], null ); data.planetmint_transaction = - Transaction.serializeTransactionIntoCanonicalString(tx) + Transaction.serializeTransactionIntoCanonicalString(tx); } - if(zencode.match(TRANSFER_AMOUNT)) { - const [ amountName, txidName, senderName, recipientName ] - = zencode.paramsOf(TRANSFER_AMOUNT); + if (zencode.match(TRANSFER_AMOUNT)) { + const [amountName, txidName, senderName, recipientName] = + zencode.paramsOf(TRANSFER_AMOUNT); const amountStr = input[amountName]; const txid = input[txidName]; const senderPublicKey = input[senderName]; const recipientPublicKey = input[recipientName]; - if( !amountStr ) { + if (!amountStr) { throw new Error("Amount not found"); } - if( !txid ) { + if (!txid) { throw new Error("Transaction id not found"); } - if( !senderPublicKey ) { + if (!senderPublicKey) { throw new Error("Sender public key not found"); } - if( !recipientPublicKey ) { + if (!recipientPublicKey) { throw new Error("Recipient public key not found"); } - const amount = BigInt(amountStr) + const amount = BigInt(amountStr); // Read UTXO - const txs = await - connection.listOutputs(senderPublicKey, false) + const txs = await connection.listOutputs(senderPublicKey, false); // Filter UTXO that refer to the current COIN (asset txid) - const txsUsed = [] - let currentAmount = BigInt(0) - const txsResolved = await Promise.all(txs.map(async (txOld) => { - return { - txOld, - txDict: await connection.getTransaction - (txOld.transaction_id)} - })) + const txsUsed = []; + let currentAmount = BigInt(0); + const txsResolved = await Promise.all( + txs.map(async (txOld) => { + return { + txOld, + txDict: + await connection.getTransaction( + txOld.transaction_id + ), + }; + }) + ); - for(const {txOld, txDict} of txsResolved) { - const asset = txDict.assets[0] as {id: string} + for (const { txOld, txDict } of txsResolved) { + const asset = txDict.assets[0] as { id: string }; const assetId = asset.id ? asset.id : txDict.id; - if(assetId === txid) { + if (assetId === txid) { // Refer to the current coin, we will use it! txsUsed.push({ tx: txDict as TransactionCommon, - output_index: txOld.output_index - } as TransactionUnspentOutput) - currentAmount += BigInt(txDict.outputs[txOld.output_index].amount) + output_index: txOld.output_index, + } as TransactionUnspentOutput); + currentAmount += BigInt(txDict.outputs[txOld.output_index].amount); } // The amount is more than what we want to transfer - if(currentAmount > amount) { + if (currentAmount > amount) { break; } } @@ -240,17 +261,20 @@ export default async (req: Request, res: Response, next: NextFunction) => { // Create the transfer transaction const tx = Transaction.makeTransferTransaction( txsUsed, - [ Transaction.makeOutput( - Transaction.makeEd25519Condition( - senderPublicKey), (currentAmount-amount).toString(10)), + [ Transaction.makeOutput( - Transaction.makeEd25519Condition( - recipientPublicKey), amountStr) + Transaction.makeEd25519Condition(senderPublicKey), + (currentAmount - amount).toString(10) + ), + Transaction.makeOutput( + Transaction.makeEd25519Condition(recipientPublicKey), + amountStr + ), ], null ); data.planetmint_transaction = - Transaction.serializeTransactionIntoCanonicalString(tx) + Transaction.serializeTransactionIntoCanonicalString(tx); } rrData = data; }); @@ -258,57 +282,59 @@ export default async (req: Request, res: Response, next: NextFunction) => { rr.onSuccess(async (params) => { const { zencode, result } = params; - if(zencode.match(SIGNATURE)) { - const [ planetmintTransactionName, publicKeyName ] = zencode.paramsOf(SIGNATURE); - const planetmintTransaction = result[planetmintTransactionName] - || input[planetmintTransactionName]; - if( !planetmintTransaction ) { + if (zencode.match(SIGNATURE)) { + const [planetmintTransactionName, publicKeyName] = + zencode.paramsOf(SIGNATURE); + const planetmintTransaction = + result[planetmintTransactionName] || input[planetmintTransactionName]; + if (!planetmintTransaction) { throw new Error("Planetmint transaction not found"); } - if( !result.planetmint_signatures ) { + if (!result.planetmint_signatures) { throw new Error("planetmint_signatures not found"); } - const publicKeyBS58 = result[publicKeyName] || input[publicKeyName] + const publicKeyBS58 = result[publicKeyName] || input[publicKeyName]; - if( !publicKeyBS58 ) { + if (!publicKeyBS58) { throw new Error("EdDSA public key not provided"); } - const publicKey = base58.decode(publicKeyBS58) - const tx = JSON.parse(planetmintTransaction) as - TransactionCommon; + const publicKey = base58.decode(publicKeyBS58); + const tx = JSON.parse( + planetmintTransaction + ) as TransactionCommon; tx.inputs.forEach((txInput, index) => { - const ed25519Fulfillment = new Ed25519Sha256() - ed25519Fulfillment.setPublicKey(Buffer.from(publicKey)) + const ed25519Fulfillment = new Ed25519Sha256(); + ed25519Fulfillment.setPublicKey(Buffer.from(publicKey)); ed25519Fulfillment.setSignature( - Buffer.from(result.planetmint_signatures[index], 'hex')) - txInput.fulfillment = ed25519Fulfillment.serializeUri() + Buffer.from(result.planetmint_signatures[index], "hex") + ); + txInput.fulfillment = ed25519Fulfillment.serializeUri(); }); const serializedSignedTransaction = - Transaction.serializeTransactionIntoCanonicalString(tx) + Transaction.serializeTransactionIntoCanonicalString(tx); tx.id = sha3_256(serializedSignedTransaction); result.signed_planetmint_transaction = - Transaction.serializeTransactionIntoCanonicalString(tx) + Transaction.serializeTransactionIntoCanonicalString(tx); } - if(zencode.match(BROADCAST)) { - const [ serializedSignedTx ] = zencode.paramsOf(BROADCAST); - if( !result[serializedSignedTx] ) { + if (zencode.match(BROADCAST)) { + const [serializedSignedTx] = zencode.paramsOf(BROADCAST); + if (!result[serializedSignedTx]) { throw new Error("Signed planetmint transaction not found"); } - if( !connection ) { + if (!connection) { throw new Error("Connection not defined"); } const signedTx = JSON.parse(result[serializedSignedTx]); try { - const txResult = await connection.postTransactionCommit(signedTx) + const txResult = await connection.postTransactionCommit(signedTx); result.txid = txResult.id; - } catch(e) { + } catch (e) { throw new Error(`Connection to the node failed: ${e}`); } } - }); next(); } catch (e) { diff --git a/packages/redis/src/index.ts b/packages/redis/src/index.ts index 10248749..01efb432 100644 --- a/packages/redis/src/index.ts +++ b/packages/redis/src/index.ts @@ -63,7 +63,7 @@ enum Action { const REDIS_LUA_FILTER_KEY_AND_GET = "local keys = redis.call('KEYS', '*'..KEYS[1]..'*'); return redis.call('MGET', unpack(keys))" export default (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, Object.values(Action)); let client: any = null; let getRedisClient: () => any; let namedSet: string = null; diff --git a/packages/sawroom/src/index.ts b/packages/sawroom/src/index.ts index 6786167c..ee93482c 100644 --- a/packages/sawroom/src/index.ts +++ b/packages/sawroom/src/index.ts @@ -33,7 +33,18 @@ let sawroomAddress: string = null; let input: ObjectLiteral = {}; export default async (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, [EXECUTE, + READ, + SAVE, + SAWROOM_ADDRESS, + TOKEN, + STORE, + STORE_OUTPUT, + RETRIEVE, + BALANCE, + DEPOSIT, + WITHDRAW, + TRANSFER]); try { rr.onBefore(async (params) => { let { zencode, keys, data } = params; diff --git a/packages/timestamp/src/index.ts b/packages/timestamp/src/index.ts index 23371bb7..35cd9b3b 100644 --- a/packages/timestamp/src/index.ts +++ b/packages/timestamp/src/index.ts @@ -12,7 +12,7 @@ const actions = { }; export default (req: Request, res: Response, next: NextFunction) => { - const rr = new Restroom(req, res); + const rr = new Restroom(req, res, Object.values(actions)); rr.onBefore(async (params: any) => { let { data, zencode } = params; diff --git a/test/db/test.db b/test/db/test.db index a98b0590..5a61b5b2 100644 Binary files a/test/db/test.db and b/test/db/test.db differ