diff --git a/README.md b/README.md index 2289e8f..072934a 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ For a program to custody private data, it must import **`data_custody_protocol.a #### Multiple Custody steps -In case **Custody** step was called more than once for a single `request_id`: +In case **Custody** step was called more than once for a single `custody_hash`: - Between step 3 and step 4, validator bots must call `dcp_core_protocol.aleo/join_shares_as_validator` as many time as there are additional **Custody** step. diff --git a/development/build.sh b/development/build.sh index 098ea03..6374d4a 100644 --- a/development/build.sh +++ b/development/build.sh @@ -18,6 +18,10 @@ cd ./programs/dcp_reconstruct_secret_offchain leo build cd ../.. +cd ./programs/dcp_hash_custody_offchain +leo build +cd ../../../.. + cd examples/nft_marketplace/programs/arc721_example leo build cd ../../../.. diff --git a/development/test.sh b/development/test.sh index adcf02e..d92aa52 100644 --- a/development/test.sh +++ b/development/test.sh @@ -363,7 +363,7 @@ nft_view_record_plaintext=$( --ciphertext $nft_view_record_ciphertext ); -withdraw + withdraw_nft( diff --git a/programs/dcp_hash_custody_offchain/.gitignore b/programs/dcp_hash_custody_offchain/.gitignore new file mode 100644 index 0000000..f721f7f --- /dev/null +++ b/programs/dcp_hash_custody_offchain/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/programs/dcp_hash_custody_offchain/build/README.md b/programs/dcp_hash_custody_offchain/build/README.md new file mode 100644 index 0000000..e1c15e3 --- /dev/null +++ b/programs/dcp_hash_custody_offchain/build/README.md @@ -0,0 +1,15 @@ +# dcp_hash_custody_offchain.aleo + +## Build Guide + +To compile this Aleo program, run: + +```bash +snarkvm build +``` + +To execute this Aleo program, run: + +```bash +snarkvm run hello +``` diff --git a/programs/dcp_hash_custody_offchain/build/main.aleo b/programs/dcp_hash_custody_offchain/build/main.aleo new file mode 100644 index 0000000..5ae4e4b --- /dev/null +++ b/programs/dcp_hash_custody_offchain/build/main.aleo @@ -0,0 +1,12 @@ +program dcp_hash_custody_offchain.aleo; + +struct Custody: + origin as address; + custody_key as field; + threshold as u8; + + +function hash_custody: + input r0 as Custody.private; + hash.bhp256 r0 into r1 as field; + output r1 as field.private; diff --git a/programs/dcp_hash_custody_offchain/build/program.json b/programs/dcp_hash_custody_offchain/build/program.json new file mode 100644 index 0000000..0a92c24 --- /dev/null +++ b/programs/dcp_hash_custody_offchain/build/program.json @@ -0,0 +1,6 @@ +{ + "program": "dcp_hash_custody_offchain.aleo", + "version": "0.0.0", + "description": "", + "license": "MIT" +} \ No newline at end of file diff --git a/programs/dcp_hash_custody_offchain/program.json b/programs/dcp_hash_custody_offchain/program.json new file mode 100644 index 0000000..f7925b0 --- /dev/null +++ b/programs/dcp_hash_custody_offchain/program.json @@ -0,0 +1,7 @@ +{ + "program": "dcp_hash_custody_offchain.aleo", + "version": "0.1.0", + "description": "", + "license": "MIT", + "dependencies": null +} \ No newline at end of file diff --git a/programs/dcp_hash_custody_offchain/src/main.leo b/programs/dcp_hash_custody_offchain/src/main.leo new file mode 100644 index 0000000..aee7c6f --- /dev/null +++ b/programs/dcp_hash_custody_offchain/src/main.leo @@ -0,0 +1,50 @@ + + +program dcp_hash_custody_offchain.aleo { + struct Custody { + origin: address, + custody_key: field, + threshold: u8, + } + + transition hash_custody(custody: Custody) -> field { + return BHP256::hash_to_field(custody); + } +} + + +/* +import { Account, ProgramManager } from '@aleohq/sdk'; + +const programManager = new ProgramManager(); +const account = new Account(); +programManager.setAccount(account); + +const secret_santa_v001_program = `INSERT SOURCE FOR import secret_santa_v001.aleo HERE`; + +function add_gift_tag_bulk_inputs(pairs){ + return ( + "[" + + pairs.map( + ([addr1, addr2]) => `[${addr1}, ${addr2}]` + ).join(", ") + + "]" + ); +} + +async function add_gift_tags(pairs) { + const program = generate_add_gift_tag_function(pairs.length); + const inputs = add_gift_tag_bulk_inputs(pairs); + const executionResponse = await programManager.executeOffline( + program, + "add_gift_tag_bulk", + inputs, + false, + { + "secret_santa_v001.aleo": secret_santa_v001_program + } + ); + const result = executionResponse.getOutputs(); + return result; +} +*/ \ No newline at end of file diff --git a/validators/run-rpc/README.md b/validators/run-rpc/README.md new file mode 100644 index 0000000..5cf15c8 --- /dev/null +++ b/validators/run-rpc/README.md @@ -0,0 +1,6 @@ +# Run RPC + +Either run this RPC or call Leo Wallet's RPC. + +- Necessary to run this RPC to test/dev on Devnet. +- This RPC necessists running Haruka's explorer and exposing the corresponding Postegres database diff --git a/validators/run-rpc/db.js b/validators/run-rpc/db.js new file mode 100644 index 0000000..e5ca83a --- /dev/null +++ b/validators/run-rpc/db.js @@ -0,0 +1,20 @@ +import pg from 'pg'; + +const { Pool, Client } = pg; + +import * as dotenv from 'dotenv'; +dotenv.config(); + + +export const pg_client = new Client({ + user: process.env.POSTGRES_USER, + password: process.env.POSTGRES_PASSWORD, + host: process.env.POSTGRES_HOST, + port: process.env.POSTGRES_PORT, + database: process.env.POSTGRES_DB, + search_path: process.env.POSTGRES_SCHEMA +}) + +await pg_client.connect(); + +await pg_client.query(`SET search_path TO '${process.env.POSTGRES_SCHEMA}';`); diff --git a/validators/run-rpc/methods.js b/validators/run-rpc/methods.js new file mode 100644 index 0000000..6bf173b --- /dev/null +++ b/validators/run-rpc/methods.js @@ -0,0 +1,193 @@ + +import { pg_client } from "./db.js"; + + +export const chainStatus = async () => { + return { + "online": true, + "statusTitle": "Everything is working as expected", + "statusMessage": "There may be some temporary issues with the blockchain, but everything should be working as expected.", + "time": Date.now() + } +} + +export const aleoTransactionsForProgram = async (params) => { + const { + programId, + functionName, + page, + maxTransactions, + } = params; + const sql_query = ` + + WITH transition_inputs AS ( + SELECT + ts.id AS transition_id, + jsonb_agg(i) AS inputs + FROM transition ts + JOIN LATERAL get_transition_inputs(ts.id) i ON true + GROUP BY ts.id + ), + transition_outputs AS ( + SELECT + ts.id AS transition_id, + jsonb_agg(o) AS outputs + FROM transition ts + JOIN LATERAL get_transition_outputs(ts.id) o ON true + GROUP BY ts.id + ), + transitions AS ( + SELECT + ts.transition_id as id, + ts.transaction_execute_id, + ts.function_name as function, + ts.program_id as program, + tsi.inputs, + tso.outputs + FROM transaction_execute te + JOIN transition ts ON te.id = ts.transaction_execute_id + LEFT JOIN transition_inputs tsi ON ts.id = tsi.transition_id + LEFT JOIN transition_outputs tso ON ts.id = tso.transition_id + ) + SELECT + b.height, + b.timestamp as finalizedAt, + ct.type, + t.id as index, + jsonb_build_object( + 'type', ct.type, + 'id', t.original_transaction_id, + 'execution', jsonb_build_object( + 'transitions', jsonb_agg(ots), + 'global_state_root', te.global_state_root, + 'proof', te.proof + ) + ) as transaction + FROM transaction t + JOIN transaction_execute te on te.transaction_id = t.id + JOIN confirmed_transaction ct on t.confirmed_transaction_id = ct.id + JOIN transition ts on te.id = ts.transaction_execute_id + JOIN transitions ots on te.id = ots.transaction_execute_id + JOIN block b on ct.block_id = b.id + WHERE ts.program_id = '${programId}' + AND ts.function_name = '${functionName}' + group by t.id, b.height, b.timestamp, te.global_state_root, te.proof, ct.type + ORDER BY b.height + LIMIT ${maxTransactions} OFFSET ${page}*${maxTransactions}; + `; + const query_res = await pg_client.query(sql_query); + return query_res.rows ? query_res.rows.map(reformat_aleo_transaction) : []; +} + +const reformat_aleo_transaction = (raw_db_tx) => { + const [status, type] = extractFirstTwoWords(raw_db_tx.type); + raw_db_tx.status = status; + raw_db_tx.type = type; + raw_db_tx.transaction.type = type; + const transitions = raw_db_tx?.transaction?.execution?.transitions; + if (raw_db_tx?.transaction?.execution == null || transitions == null) { + return raw_db_tx; + } + raw_db_tx.transaction.execution.transitions + = raw_db_tx.transaction.execution.transitions.map(reformat_aleo_transition); + return raw_db_tx; +} + + +const reformat_aleo_transition = (raw_db_ts) => { + raw_db_ts.inputs = raw_db_ts.inputs || []; + raw_db_ts.outputs = raw_db_ts.outputs || []; + + const inputs = raw_db_ts.inputs.map(reformat_aleo_input); + const outputs = raw_db_ts.outputs.map(reformat_aleo_output); + + return { + ...raw_db_ts, + inputs, + outputs + } +} + + +const reformat_aleo_input = (raw_db_input) => { + if (raw_db_input.type == 'Private') { + return { + type: 'private', + id: raw_db_input.ciphertext_hash, + value: raw_db_input.ciphertext + }; + } + if (raw_db_input.type == 'Public') { + return { + type: 'public', + id: raw_db_input.plaintext_hash, + value: raw_db_input.plaintext, + }; + } + if (raw_db_input.type == 'Record') { + return { + type: 'record', + id: raw_db_input.serial_number, + value: '', + tag: raw_db_input.tag + }; + } + if (raw_db_input.type == 'ExternalRecord') { + return { + type: 'external_record', + id: raw_db_input.commitment, + value: '' + }; + } +} + +const reformat_aleo_output = (raw_db_output) => { + if (raw_db_output.type == 'Private') { + return { + type: 'private', + id: raw_db_output.ciphertext_hash, + value: raw_db_output.ciphertext + }; + } + if (raw_db_output.type == 'Public') { + return { + type: 'public', + id: raw_db_output.plaintext_hash, + value: raw_db_output.plaintext + }; + } + if (raw_db_output.type == 'Record') { + return { + type: 'record', + id: raw_db_output.commitment, + checksum: raw_db_output.checksum, + value: raw_db_output.record_ciphertext + }; + } + if (raw_db_output.type == 'ExternalRecord') { + return { + type: 'external_record', + id: raw_db_output.external_record_commitment, + value: '' + }; + } + if (raw_db_output.type == 'Future') { + return { + type: 'future', + id: raw_db_output.future_hash, + value: null + }; + } +} + + +function extractFirstTwoWords(text) { + const regex = /^([A-Z]?[a-z]+)([A-Z][a-z]*|\s+[A-Za-z]+)/; + const match = text.match(regex); + + if (match) { + // Combine the first and second captured groups, trim any extra spaces. + return [match[1].toLowerCase(), match[2].trim().toLowerCase()]; + } + return ['', '']; +} \ No newline at end of file diff --git a/validators/run-rpc/package-lock.json b/validators/run-rpc/package-lock.json new file mode 100644 index 0000000..a925ad8 --- /dev/null +++ b/validators/run-rpc/package-lock.json @@ -0,0 +1,1183 @@ +{ + "name": "aleo-rpc", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "aleo-rpc", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@aleohq/sdk": "^0.6.9", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "fs.promises.exists": "^1.1.4", + "pg": "^8.12.0" + } + }, + "node_modules/@aleohq/sdk": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@aleohq/sdk/-/sdk-0.6.9.tgz", + "integrity": "sha512-XerwoyQvwYsAWWir7gWWC9i9ZZc7DvbP9Gfkqv92lEKDILe/Ac/PIu7cRlJsRqv09aih8dgVXsKROU303z1PJQ==", + "dependencies": { + "@aleohq/wasm": "^0.6.0", + "comlink": "^4.4.1", + "mime": "^3.0.0", + "sync-request": "^6.1.0" + } + }, + "node_modules/@aleohq/wasm": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/@aleohq/wasm/-/wasm-0.6.9.tgz", + "integrity": "sha512-lDQePcCN+H4uyiCk3Vy22k+vpppogCSMveZk2f3qV3v0H0MCetA1dqFxR3HMZr73n2Yw7B6ZkwTlfotTdcGtPA==" + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==" + }, + "node_modules/@types/qs": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comlink": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.1.tgz", + "integrity": "sha512-+1dlx0aY5Jo1vHy/tSsIGpSkN4tS9rZSW8FIhG0JH/crs9wwweswIo/POr451r7bZww3hFbPAKnTpimzL/mm4Q==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs.promises.exists": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fs.promises.exists/-/fs.promises.exists-1.1.4.tgz", + "integrity": "sha512-lJzUGWbZn8vhGWBedA+RYjB/BeJ+3458ljUfmplqhIeb6ewzTFWNPCR1HCiYCkXV9zxcHz9zXkJzMsEgDLzh3Q==", + "funding": { + "url": "https://github.com/privatenumber/fs.promises.exists?sponsor=1" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pg": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", + "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", + "dependencies": { + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/validators/run-rpc/package.json b/validators/run-rpc/package.json new file mode 100644 index 0000000..7b23634 --- /dev/null +++ b/validators/run-rpc/package.json @@ -0,0 +1,20 @@ +{ + "name": "aleo-rpc", + "version": "1.0.0", + "description": "", + "main": "run.js", + "scripts": { + "start": "node start.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@aleohq/sdk": "^0.6.9", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "fs.promises.exists": "^1.1.4", + "pg": "^8.12.0" + }, + "type": "module" +} diff --git a/validators/run-rpc/start.js b/validators/run-rpc/start.js new file mode 100644 index 0000000..4829550 --- /dev/null +++ b/validators/run-rpc/start.js @@ -0,0 +1,64 @@ +import express from 'express'; +import cors from 'cors'; +import bodyParser from 'body-parser'; +import * as methods from './methods.js'; + +import * as dotenv from 'dotenv'; +dotenv.config(); + + +const app = express(); +app.use(cors()); +app.use(bodyParser.json({ limit: '150kb' })); +app.use(express.urlencoded({ extended: true })); +const port = Number(process.env.RPC_API_PORT || 3000); + +const server = app.listen(port, () => { + console.log('RPC server started on port: ' + port); +}); + + +server.timeout = 60_000; // milliseconds +server.headersTimeout = server.timeout; +server.requestTimeout = server.timeout; + + +const call_rpc_method = async (method_name, params) => { + const method = methods?.[method_name]; + if (method == null) { + throw new Error(`RPC method '${method_name}' not found.`); + } + return await method(params); +} + +const rpc_base = { + jsonrpc: "2.0", + id: 1 +}; + +const try_call_rpc_method = async (rpc_request) => { + const method = rpc_request?.method; + const result = await call_rpc_method(method, rpc_request?.params); + return { + ...rpc_base, + result + }; +} + + +app.post("/", async (req, res) => { + const rpc_request = req.body; + try { + const response = await try_call_rpc_method(rpc_request) + return res.json(response); + } catch (e) { + const response = { + ...rpc_base, + error: e + '' + }; + + return res.status(400).send(response,); + } +}); + + diff --git a/validators/run-validator/.env.example b/validators/run-validator/.env.example index 55fa877..df5c5a8 100644 --- a/validators/run-validator/.env.example +++ b/validators/run-validator/.env.example @@ -1,2 +1,3 @@ PRIVATE_KEY= -RPC_URL=https://testnetbeta.aleorpc.com \ No newline at end of file +RPC_URL=https://testnetbeta.aleorpc.com +REFRESH_PERIOD_S=60 \ No newline at end of file diff --git a/validators/run-validator/config/db.js b/validators/run-validator/config/db.js index d5378fc..e793f50 100644 --- a/validators/run-validator/config/db.js +++ b/validators/run-validator/config/db.js @@ -7,21 +7,35 @@ export const tables = { }, request_records: { name: "request_records", - columns: "(serial_number TEXT PRIMARY KEY, plaintext TEXT, spent INT, custody_key TXT, request_id TXT)" + columns: "(serial_number TEXT PRIMARY KEY, plaintext TEXT, spent INT, owner TXT, custody_hash TXT, to_address TXT, fee_amount TXT, expected_weight TXT, _nonce TXT)" }, share_records: { name: "share_records", - columns: "(serial_number TEXT PRIMARY KEY, plaintext TEXT, spent INT, custody_key TXT)" - }, - requests: { - name: "requests", - columns: "(request_id TEXT PRIMARY KEY, plaintext TEXT, spent INT, custody_key TXT)" - }, - custodies: { - name: "custodies", - columns: "(custody_hash TEXT PRIMARY KEY, custody_key TEXT, spent INT)" + columns: "(serial_number TEXT PRIMARY KEY, plaintext TEXT, custody_hash TXT, spent INT, owner TXT, share TXT, custody TXT, weight TXT, _nonce TXT)" } }; export const db_file_name = "db.sqlite"; + +/* + + +record ValidatorShare { + owner: address, + share: Share, + custody: Custody, + weight: u64 +} + + +record WithdrawRequest { + owner: address, + custody_hash: field, + to: address, + fee_amount: u64, + expected_weight: u64 +} + + +*/ \ No newline at end of file diff --git a/validators/run-validator/config/programs.js b/validators/run-validator/config/programs.js index 77821ec..630c3a0 100644 --- a/validators/run-validator/config/programs.js +++ b/validators/run-validator/config/programs.js @@ -1,10 +1,18 @@ // Programs variable names -export const protocol_transfers_program = "dcp_destination_shares.aleo"; -export const share_record = "ValidatorShare"; -export const request_record = "WithdrawRequest"; - -export const sstv_function = "submit_shares_to_validators"; -export const jsav_function = "join_shares_as_validator"; -export const srtv_function = "submit_requests_to_validators"; -export const prav_function = "process_request_as_validator"; \ No newline at end of file +export const share_record = "dcp_validator_shares.aleo/ValidatorShare"; +export const request_record = "dcp_withdraw_requests.aleo/WithdrawRequest"; + +export const sstv_function = "dcp_validator_shares.aleo/submit_shares_to_validators"; +export const jsav_function = "dcp_validator_shares.aleo/join_shares_as_validator"; +export const srtv_function = "dcp_withdraw_requests.aleo/submit_requests_to_validators"; +export const swr_function = "dcp_withdraw_requests.aleo/spend_withdraw_request"; + + +export const hash_custody_function = "dcp_hash_custody_offchain.aleo/hash_custody"; + + +export const record_data_columns_amounts = { + [share_record]: 5, + [request_record]: 5 +}; diff --git a/validators/run-validator/lib/aleo.js b/validators/run-validator/lib/aleo.js index ee49982..6cde5f9 100644 --- a/validators/run-validator/lib/aleo.js +++ b/validators/run-validator/lib/aleo.js @@ -1,8 +1,23 @@ -import { Account, RecordPlaintext } from '@aleohq/sdk'; +import { + Account, RecordPlaintext, ProgramManager, + initThreadPool, AleoKeyProvider, ProvingKey, VerifyingKey +} from '@aleohq/sdk'; +import { data_dir } from "./path.js"; +import fsExists from 'fs.promises.exists'; +import fs from 'fs.promises'; + +await initThreadPool(); + +const keyProvider = new AleoKeyProvider(); +keyProvider.useCache(true); export const load_aleo_account = async (privateKey) => { const account = new Account({ privateKey }); + + const programManager = new ProgramManager(null, keyProvider); + programManager.setAccount(account); + account.programManager = programManager; return account; } @@ -41,4 +56,83 @@ export const parse_record_plaintext = (plaintext) => { obj[key] = extract_value(value); } return obj; -} \ No newline at end of file +} + + +export const struct_repr = (struct) => { + if (typeof struct === "string") return struct; + const struct_content = Object + .entries( + struct + ).map( + ([key, val]) => ( + `${key}: ${typeof val === "string" ? val : struct_repr(val) + }` + ) + ).join(","); + return `{${struct_content}}`; +} + + +export const execute_program_offchain = async ( + account, + program_source, + program_name, + function_name, + inputs, +) => { + const [ + proving_key, + verifying_key + ] = await load_program_keys(program_name, function_name); + + const executionResponse = await account.programManager.run( + program_source, + function_name, + inputs, + false, + [], + null, + proving_key, + verifying_key + ); + const result = executionResponse.getOutputs(); + return result; +} + + +export const synthetize_program_keys = async ( + account, program_name, program_source, function_name, inputs +) => { + const proving_key_path = `${data_dir}/${program_name}_${function_name}.prover`; + const verifying_key_path = `${data_dir}/${program_name}_${function_name}.verifier`; + + if (await fsExists(proving_key_path) && await fsExists(verifying_key_path)) { + return; + } + const [proving_key, verifying_key] = await account.programManager.synthesizeKeys( + program_source, + function_name, + inputs, + account.privateKey() + ); + await fs.writeFile(proving_key_path, proving_key.toBytes()); + await fs.writeFile(verifying_key_path, verifying_key.toBytes()); +} + + +export const load_program_keys = async (program_name, function_name) => { + const proving_key_path = `${data_dir}/${program_name}_${function_name}.prover`; + const verifying_key_path = `${data_dir}/${program_name}_${function_name}.verifier`; + + const proving_key = ProvingKey.fromBytes( + new Uint8Array(await fs.readFile(proving_key_path)) + ); + const verifying_key = VerifyingKey.fromBytes( + new Uint8Array(await fs.readFile(verifying_key_path)) + ); + + return [proving_key, verifying_key]; +} + + diff --git a/validators/run-validator/lib/db.js b/validators/run-validator/lib/db.js index f513511..cf73ce3 100644 --- a/validators/run-validator/lib/db.js +++ b/validators/run-validator/lib/db.js @@ -3,7 +3,7 @@ import fs from 'fs/promises'; import initSqlJs from 'sql.js'; import { data_dir, create_dir_if_not_exists, } from "./path.js"; import { sql_string } from "./string.js"; -import { tables, db_file_name, db_file_name } from "../config/db.js"; +import { tables, db_file_name } from "../config/db.js"; const db_path = `${data_dir}/${db_file_name}`; @@ -26,7 +26,7 @@ const create_table_if_not_exists = async (db, table) => { } catch (e) { if (e.message.startsWith("no such table:")) { const sql_cmd = `CREATE TABLE ${table.name}${table.columns}`; - console.log(`Executing: '${sql_cmd}'`) + console.log(`Executing: \`${sql_cmd}\``) db.run(sql_cmd); return true; } else { @@ -52,7 +52,7 @@ export const insert_into_table = async (db, table, values) => { values[i] = convert_js_to_sql(values[i]); } const sql_cmd = `INSERT INTO ${table} VALUES (${values.join(",")})`; - console.log(`Executing: '${sql_cmd}'`) + console.log(`Executing: \`${sql_cmd}\``) db.run(sql_cmd); } @@ -67,7 +67,7 @@ export const update_in_table = async (db, table, updates, where) => { .join(","); const where_expr = (where == null) ? "" : ` WHERE ${where}` const sql_cmd = `UPDATE ${table} SET ${updates_expr}${where_expr}`; - console.log(`Executing: '${sql_cmd}'`) + console.log(`Executing: \`${sql_cmd}\``) db.run(sql_cmd); } @@ -75,7 +75,7 @@ export const update_in_table = async (db, table, updates, where) => { export const select_from_table = async (db, table, where) => { const where_expr = (where == null) ? "" : ` WHERE ${where}` const sql_cmd = `SELECT * FROM ${table}${where_expr}`; - console.log(`Executing: '${sql_cmd}'`) + console.log(`Executing: \`${sql_cmd}\``) const result = db.exec(sql_cmd); if (result.length === 0) { return []; diff --git a/validators/run-validator/lib/path.js b/validators/run-validator/lib/path.js index 9a7691e..286ecee 100644 --- a/validators/run-validator/lib/path.js +++ b/validators/run-validator/lib/path.js @@ -1,12 +1,13 @@ import path from 'path'; import { fileURLToPath, } from 'url'; import fsExists from 'fs.promises.exists'; +import fs from 'fs.promises'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); export const data_dir = `${__dirname}/../data`; export const config_dir = `${__dirname}/../config`; - +export const programs_dir = `${__dirname}/../../../programs`; export const create_dir_if_not_exists = async (dir_path) => { const exists = !fsExists(dir_path); diff --git a/validators/run-validator/lib/rpc.js b/validators/run-validator/lib/rpc.js index 70b5b9a..738cee8 100644 --- a/validators/run-validator/lib/rpc.js +++ b/validators/run-validator/lib/rpc.js @@ -12,19 +12,6 @@ export class LiveRpcProvider { this.url = url; } - async from_url(url) { - const instance = new LiveRpcProvider(url); - const status_res = await instance.chainStatus(); - if (!status_res?.online) { - throw new Error( - `RPC unavailable at '${url}'.`, - status_res?.statusTitle, - status_res?.statusMessage - ); - } - return instance; - } - async call_rpc(method, params) { const rawResponse = await fetch( this.url, @@ -41,7 +28,7 @@ export class LiveRpcProvider { ); const response = await rawResponse.json(); if (response.error) { - throw new Error(`RPC API:\n${response.error.message}`); + throw new Error(`RPC API:\n${response.error.message || response.error}`); } return response?.result; } @@ -64,3 +51,16 @@ export class LiveRpcProvider { } } +LiveRpcProvider.from_url = async (url) => { + const instance = new LiveRpcProvider(url); + const status_res = await instance.chainStatus(); + if (!status_res?.online) { + throw new Error( + `RPC unavailable at '${url}'.`, + status_res?.statusTitle, + status_res?.statusMessage + ); + } + return instance; +} + diff --git a/validators/run-validator/lib/sync.js b/validators/run-validator/lib/sync.js index a7f11c5..79f5493 100644 --- a/validators/run-validator/lib/sync.js +++ b/validators/run-validator/lib/sync.js @@ -2,21 +2,29 @@ import { save_db, insert_into_table, select_from_table, update_in_table } from "./db.js" import { remove_whitespaces } from "./string.js" -import { serial_number, parse_record_plaintext } from "./aleo.js" +import { + serial_number, parse_record_plaintext, execute_program_offchain, + struct_repr, synthetize_program_keys +} from "./aleo.js" import { sql_string } from "./string.js"; + import { - protocol_transfers_program, share_record, request_record, sstv_function, jsav_function, srtv_function, - prav_function + swr_function, + hash_custody_function, + record_data_columns_amounts } from "../config/programs.js"; import { tables } from "../config/db.js" import { transaction_per_batch } from "../config/rpc.js" +import { programs_dir } from "./path.js"; +import fs from 'fs.promises'; + const record_tables = { [share_record]: tables.share_records.name, @@ -30,7 +38,7 @@ export const sync_db_with_blockchain = async (rpc_provider, db, account) => { sync_sstv_transitions(rpc_provider, db, account, processed_transitions), sync_jsav_transitions(rpc_provider, db, account, processed_transitions), sync_srtv_transitions(rpc_provider, db, account, processed_transitions), - sync_prav_transitions(rpc_provider, db, account, processed_transitions), + sync_swr_transitions(rpc_provider, db, account, processed_transitions), ]); await update_processed_transitions(db, processed_transitions); @@ -38,20 +46,20 @@ export const sync_db_with_blockchain = async (rpc_provider, db, account) => { } -const load_transactions_page = async (rpc_provider, functionName, page) => ( - await rpc_provider.aleoTransactionsForProgram( +const load_transactions_page = async (rpc_provider, programId, functionName, page) => { + return await rpc_provider.aleoTransactionsForProgram( { - programId: protocol_transfers_program, - functionName: functionName, + programId, + functionName, page, maxTransactions: transaction_per_batch } ) -); +}; -const record_table = (record_name) => { - const table_name = record_tables?.[record_name]; +const record_table = (record_id) => { + const table_name = record_tables?.[record_id]; if (!table_name) { throw new Error("Unknown record name."); } @@ -60,13 +68,14 @@ const record_table = (record_name) => { export const travel_transaction_pages = async ( - rpc_provider, function_name, apply_to_transition, processed_already + rpc_provider, function_id, apply_to_transition, processed_already ) => { let page = Math.floor(processed_already / transaction_per_batch); let starting_index = processed_already % transaction_per_batch; + const [program_id, function_name] = function_id.split("/"); while (true) { const transactions = await load_transactions_page( - rpc_provider, function_name, page + rpc_provider, program_id, function_name, page ); const transitions = transactions.reduce( (acc, transaction, index) => { @@ -76,7 +85,7 @@ export const travel_transaction_pages = async ( const filtered_transitions = transaction .transaction.execution.transitions.filter( (transition) => ( - transition.program === protocol_transfers_program + transition.program === program_id && transition.function === function_name ) ); @@ -96,17 +105,25 @@ export const travel_transaction_pages = async ( } -const get_record_data_columns = (record_name, plaintext) => { - const record_object = parse_record_plaintext(plaintext); - if (record_name == share_record) { - return [record_object.custody_key.slice(0, -"field".length)]; - } - else if (record_name == request_record) { +const record_attributes = (record) => ( + Object.values(record).map(struct_repr) +); + + +const get_record_data_columns = async (account, record_id, plaintext) => { + const record = parse_record_plaintext(plaintext); + if (record_id == share_record) { + const custody = struct_repr(record.custody); + const custody_hash = await hash_custody(account, custody); return [ - record_object.custody_key.slice(0, -"field".length), - record_object.request_id.slice(0, -"field".length) + custody_hash, + ...record_attributes(record) ]; - } else { + } + else if (record_id == request_record) { + return record_attributes(record); + } + else { throw new Error("Unknown record name.") } } @@ -119,8 +136,8 @@ const owned_records_from_outputs = (account, outputs) => ( ); -const retrieve_record_from_serial = async (db, record_name, serial) => { - const table_name = record_table(record_name); +const retrieve_record_from_serial = async (db, record_id, serial) => { + const table_name = record_table(record_id); const where = `serial_number = ${sql_string(serial)}`; const found = await select_from_table(db, table_name, where); return found.length ? found[0] : null; @@ -128,29 +145,32 @@ const retrieve_record_from_serial = async (db, record_name, serial) => { const tag_record_received = async ( - db, account, record_name, record_string + db, account, record_id, record_string ) => { const plaintext = remove_whitespaces(record_string); + const [program_id, record_name] = record_id.split("/"); const serial = serial_number( - account, record_string, protocol_transfers_program, record_name + account, plaintext, program_id, record_name ); - const found = await retrieve_record_from_serial(db, record_name, serial); + const found = await retrieve_record_from_serial(db, record_id, serial); + if (found == null) { - const data_columns = get_record_data_columns(record_name, plaintext); + const data_columns = await get_record_data_columns(account, record_id, plaintext); const values = [serial, plaintext, 0, ...data_columns]; + const table_name = record_table(record_id); await insert_into_table(db, table_name, values); } }; -const tag_record_spent = async (db, record_name, serial) => { - const found = await retrieve_record_from_serial(db, record_name, serial); +const tag_record_spent = async (db, record_id, serial) => { + const found = await retrieve_record_from_serial(db, record_id, serial); + const table_name = record_table(record_id); if (found != null) { - const table_name = record_table(record_name); const where = `serial_number = ${sql_string(serial)}`; await update_in_table(db, table_name, { spent: 1 }, where); } else { - const values = [serial, null, 1]; + const values = [serial, null, 1, ...(new Array(record_data_columns_amounts[record_id]).fill(null))]; await insert_into_table(db, table_name, values); } } @@ -158,10 +178,10 @@ const tag_record_spent = async (db, record_name, serial) => { const retrieve_processed_transitions = async (db) => { const elements = await select_from_table(db, tables.processed_transitions.name); - return !found.length ? null : Object.fromEntries( + return !elements.length ? null : Object.fromEntries( elements.map( (element) => ( - [element.function, element.page] + [element.function, element.amount] ) ) ); @@ -171,10 +191,10 @@ const retrieve_processed_transitions = async (db) => { const load_processed_transitions = async (db) => { const found = await retrieve_processed_transitions(db); return (found != null) ? found : { - sstv_function: 0, - jsav_function: 0, - srtv_function: 0, - prav_function: 0 + [sstv_function]: 0, + [jsav_function]: 0, + [srtv_function]: 0, + [swr_function]: 0 } } @@ -189,9 +209,9 @@ const update_processed_transitions = async (db, processed_transitions) => { const values = [function_name, amount]; await insert_into_table(db, tables.processed_transitions.name, values); } else { - const where = `function = ${function_name}`; - const udpate = { amount }; - await update_in_table(db, tables.processed_transitions.name, udpate, where); + const where = `function = '${function_name}'`; + const update = { amount }; + await update_in_table(db, tables.processed_transitions.name, update, where); } } } @@ -254,24 +274,68 @@ const sync_srtv_transitions = async ( } -const sync_prav_transitions = async ( +const sync_swr_transitions = async ( rpc_provider, db, account, processed_transitions ) => { - const apply_to_prav_transition = async ({ inputs, outputs }) => { + const apply_to_swr_transition = async ({ inputs, outputs }) => { await Promise.all([ - tag_record_spent(db, share_record, inputs[0].id), - tag_record_spent(db, request_record, inputs[1].id) + tag_record_spent(db, request_record, inputs[0].id) ]); }; - processed_transitions[prav_function] = await travel_transaction_pages( + processed_transitions[swr_function] = await travel_transaction_pages( rpc_provider, - prav_function, - apply_to_prav_transition, - processed_transitions[prav_function] + swr_function, + apply_to_swr_transition, + processed_transitions[swr_function] + ); +} + + +const [ + hash_custody_program_id, + hash_custody_function_name +] = hash_custody_function.split("/"); +const hash_custody_program_name = hash_custody_program_id.split(".")[0]; + +const load_hash_custody = async () => { + const hash_custody_program_path = `${programs_dir}/${hash_custody_program_name}/build/main.aleo`; + try { + return await fs.readFile(hash_custody_program_path, "utf-8"); + } + catch (e) { + throw new Error( + `'${hash_custody_program_id}' build not found,` + + `start by executing './development/build.sh' from project root.` + ); + } +} +const hash_custody_program_source = await load_hash_custody(); + + +const hash_custody = async (account, custody) => { + const outputs = await execute_program_offchain( + account, + hash_custody_program_source, + hash_custody_program_name, + hash_custody_function_name, + [custody] ); + return outputs[0]; } +export const synthetize_hash_custody_keys = async (account) => { + return await synthetize_program_keys( + account, + "dcp_hash_custody_offchain", + hash_custody_program_source, + "hash_custody", + ["{origin: aleo1050n3kd4x3spur952m58v56a572uxw8dlnxvmtn9qcjcqnjkty9q0xu8x5,custody_key: 7828field,threshold: 8u8}"] + ) +} + + + /* ---------- Inputs/Outputs of Interest @@ -288,14 +352,13 @@ const sync_prav_transitions = async ( - inputs[1]: ValidatorShare - outputs[0]: ValidatorShare - (dcp_destination_shares.aleo, submit_requests_to_validators) + (dcp_withdraw_requests.aleo, submit_requests_to_validators) - outputs[0]: WithdrawRequest - outputs[1]: WithdrawRequest ... - outputs[15]: WithdrawRequest - (dcp_destination_shares.aleo, process_request_as_validator) - - inputs[0]: ValidatorShare - - inputs[1]: WithdrawRequest + (dcp_withdraw_requests.aleo, spend_withdraw_request) + - inputs[0]: WithdrawRequest */ diff --git a/validators/run-validator/package-lock.json b/validators/run-validator/package-lock.json index bda1f6d..f3d7335 100644 --- a/validators/run-validator/package-lock.json +++ b/validators/run-validator/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@aleohq/sdk": "^0.6.9", "dotenv": "^16.4.5", + "fs.promises": "^0.1.2", "fs.promises.exists": "^1.1.4", "sql.js": "^1.10.3" } @@ -197,6 +198,14 @@ "node": ">= 0.12" } }, + "node_modules/fs.promises": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/fs.promises/-/fs.promises-0.1.2.tgz", + "integrity": "sha512-LNfkXdN6EToumV455EbdJaNxqLmEFqKqNZVIEyI2whtdpIppTsne2fHWgx05s+B2Aif29Lhzdz0AaOXKLvMGsA==", + "engines": { + "node": ">=8.9" + } + }, "node_modules/fs.promises.exists": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/fs.promises.exists/-/fs.promises.exists-1.1.4.tgz", diff --git a/validators/run-validator/package.json b/validators/run-validator/package.json index 9eb4acc..97ac2e5 100644 --- a/validators/run-validator/package.json +++ b/validators/run-validator/package.json @@ -11,8 +11,9 @@ "dependencies": { "@aleohq/sdk": "^0.6.9", "dotenv": "^16.4.5", + "fs.promises": "^0.1.2", "fs.promises.exists": "^1.1.4", "sql.js": "^1.10.3" }, "type": "module" -} \ No newline at end of file +} diff --git a/validators/run-validator/start.js b/validators/run-validator/start.js index a53615d..ee75c94 100644 --- a/validators/run-validator/start.js +++ b/validators/run-validator/start.js @@ -1,12 +1,12 @@ -import { sync_db_with_blockchain } from "./lib/sync.js"; +import { sync_db_with_blockchain, synthetize_hash_custody_keys } from "./lib/sync.js"; import { process_requests } from "./lib/process.js"; import { load_database } from "./lib/db.js"; -import { load_aleo_account } from "./lib/aleo.js"; +import { load_aleo_account, } from "./lib/aleo.js"; import { LiveRpcProvider } from "./lib/rpc.js"; import { private_key_error_msg, private_key_success_msg, refresh_error_msg -} from "../config/errors.js" +} from "./config/errors.js" import 'dotenv/config'; @@ -16,14 +16,13 @@ const load_env = async () => { if (isNaN(refresh_period_s) || refresh_period_s < 0) { throw new Error(refresh_error_msg); } - const rpc_provider = LiveRpcProvider(process.env["RPC_URL"]); + const rpc_provider = await LiveRpcProvider.from_url(process.env["RPC_URL"]); try { - account = await load_aleo_account(process.env["PRIVATE_KEY"]); - address = account.address().to_string(); - console.log(private_key_success_msg, address); + const account = await load_aleo_account(process.env["PRIVATE_KEY"]); + const address = account.address().to_string(); return { account, refresh_period_s, rpc_provider }; - } catch { - throw new Error(private_key_error_msg); + } catch (e) { + throw new Error(private_key_error_msg + " " + e); } }; @@ -37,6 +36,7 @@ const sync_and_process = async (rpc_provider, db, account) => { const main = async () => { const { account, refresh_period_s, rpc_provider } = await load_env(); const db = await load_database(); + await synthetize_hash_custody_keys(account); while (true) { try { await sync_and_process(rpc_provider, db, account); @@ -47,5 +47,4 @@ const main = async () => { } } - -await main(); \ No newline at end of file +await main();