diff --git a/Anchor.toml b/Anchor.toml index 29ee695..3dbe094 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -1,4 +1,5 @@ [toolchain] +anchor_version = "0.30.1" [features] resolution = true diff --git a/README.md b/README.md new file mode 100644 index 0000000..11ef46d --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# XEN Blocks Voting Program + +## Installation + +### JS Modules + +```npm i``` + +### Rust Modules + +```anchor build``` + +#### After First Time Build + +1. Run `anchor keys list` +2. Copy program ID +3. Replace Program ID in `./programs/grow_space/src/lib.rs` with the copied string +4. Run `anchor build` again or proceed with tests + +## Configuration + +This project requires configured connection to Xolana network and a funded main wallet. + +Config is set in `Anchor.toml` file. + +## Funding Xolana wallet + +```solana airdrop 2``` + +Note: airdrop faucet is rate-limited to prevent abuse. + +## Running tests + +```anchor test``` + +This is a long-running integration test which creates N worker wallets, fund them from the main wallet, and then +repeatedly attempts to invoke Program Instruction to append voting data for the current block, as well as reward winning +voters for the previous block. + +After the tests are complete, the script prints out stats info about block-voting PDAs and user accounting PDAs. + +## TODOs + +- make global auto-incrementing counter for BlockId diff --git a/app/dist.js b/app/dist.js new file mode 100644 index 0000000..4f706aa --- /dev/null +++ b/app/dist.js @@ -0,0 +1,46 @@ +const anchor = require('@coral-xyz/anchor'); +const { PublicKey } = require('@solana/web3.js'); +const { BN } = require('bn.js'); + +// Get input from command line arguments +const block_id = parseInt(process.argv[2]); + +if (isNaN(block_id)) { + console.error('Please provide a valid block_id.'); + process.exit(1); +} + +const provider = anchor.AnchorProvider.env(); +anchor.setProvider(provider); +const program = anchor.workspace.GrowSpace; + +async function distributeSolToBlock(block_id) { + const uniqueId = new BN(block_id); + + const [pda, bump] = await PublicKey.findProgramAddress( + [Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)], + program.programId + ); + + const [distributionPda] = await PublicKey.findProgramAddress( + [Buffer.from("distribution_account")], + program.programId + ); + + try { + // Distribute SOL to all pubkeys associated with the block_id + const txHash = await program.methods.distributeSol(uniqueId).accounts({ + pdaAccount: pda, + distributionAccount: distributionPda, + systemProgram: anchor.web3.SystemProgram.programId, + }).rpc(); + + console.log(`Distributed SOL. Transaction hash: ${txHash}`); + } catch (err) { + console.error("Failed to distribute SOL", err); + } +} + +// Run the distributeSolToBlock function with the provided block_id +distributeSolToBlock(block_id); + diff --git a/app/read_logs.ts b/app/read_logs.ts new file mode 100644 index 0000000..3834a61 --- /dev/null +++ b/app/read_logs.ts @@ -0,0 +1,33 @@ +import {workspace, web3, AnchorProvider, setProvider,} from "@coral-xyz/anchor"; +import type {Program} from "@coral-xyz/anchor"; +import {GrowSpace} from "../target/types/grow_space"; + +async function main() { + + const provider = AnchorProvider.env(); + setProvider(provider); + const connection = provider.connection; + + const program = workspace.GrowSpace as Program; + + let transactionList = await connection.getSignaturesForAddress( + program.programId, + {limit: 10}, + 'confirmed' + ); + + let signatureList = transactionList.map(transaction => transaction.signature); + let transactionDetails = await connection.getParsedTransactions(signatureList, { + maxSupportedTransactionVersion: 0, + commitment: 'confirmed' + }); + + transactionDetails.forEach((transaction, i) => { + const date = new Date(transaction.blockTime * 1000); + console.log(`Transaction No: ${i + 1}`); + console.log(`Messages:`); + transaction.meta.logMessages.forEach(console.log) + }) +} + +main().catch(console.log) \ No newline at end of file diff --git a/app/server_vote_receiver.js b/app/server_vote_receiver.js new file mode 100644 index 0000000..c61da63 --- /dev/null +++ b/app/server_vote_receiver.js @@ -0,0 +1,142 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const anchor = require('@coral-xyz/anchor'); +const { PublicKey } = require('@solana/web3.js'); +const { BN } = require('bn.js'); + +const app = express(); +app.use(bodyParser.json()); + +const provider = anchor.AnchorProvider.env(); +anchor.setProvider(provider); +const program = anchor.workspace.GrowSpace; + +// Function to check if a PDA account already exists +async function pdaExists(pda) { + const accountInfo = await provider.connection.getAccountInfo(pda); + return accountInfo !== null; +} + +// Endpoint to append data and initialize PDA if needed +app.post('/', async (req, res) => { + const { first_block_id, final_hash, pubkey } = req.body; + console.log(req.body); + const block_id = first_block_id; + console.log(block_id + " " + pubkey + "\n"); + + const uniqueId = new BN(block_id); + const pubkeyObj = new PublicKey(pubkey); + + // define prev block_id + const prevuniqueId = new BN(block_id - 100); + + const [prev_pda, prev_bump] = await PublicKey.findProgramAddress( + [Buffer.from("pda_account"), prevuniqueId.toArrayLike(Buffer, "le", 8)], + program.programId + ); + + // Derive the PDA for the main account + const [pda, bump] = await PublicKey.findProgramAddress( + [Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)], + program.programId + ); + + // Derive the PDA for the user account + const [user_account_pda, user_account_pda_bump] = await PublicKey.findProgramAddress( + [Buffer.from("user_account_pda"), pubkeyObj.toBuffer()], + program.programId + ); + + try { + // Check if the pda_account already exists + const pdaExistsFlag = await pdaExists(pda); + + if (!pdaExistsFlag) { + // Initialize the pda_account if it does not exist + try { + const tx = await program.methods.initializePda(uniqueId, pubkeyObj).accounts({ + pdaAccount: pda, + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }) + .signers([provider.wallet.payer]) + .rpc(); + console.log("Initialization transaction hash:", tx); + console.log("Initialized PDA account public key:", pda.toString(), "with bump:", bump); + console.log("Initialized PDA for user:", user_account_pda.toString()); + } catch (err) { + throw new Error(`Failed to initialize PDA: ${err.message}`); + } + } else { + console.log("PDA already initialized, proceeding to append data."); + } + + try { + console.log("Attempting to append data..."); + + // Append the data + const tx2 = await program.methods.appendData(uniqueId, final_hash, pubkeyObj).accounts({ + pdaAccount: pda, + prevPdaAccount: prev_pda, + userAccountPda: user_account_pda, // Uses init_if_needed so no explicit initialization required + payer: provider.wallet.publicKey, + systemProgram: anchor.web3.SystemProgram.programId, + }) + .signers([provider.wallet.payer]) + .rpc(); + + console.log("Transaction successful, transaction hash:", tx2); + res.status(200).json({ message: "Appended data", pda: pda.toString(), txHash: tx2 }); + + } catch (err) { + console.error("Failed to append data, error details:", err); + res.status(500).json({ error: "Failed to append data", details: err.toString() }); + } + + } catch (err) { + console.error("Failed to process request, error details:", err); + res.status(500).json({ error: "Failed to process request", details: err.toString() }); + } +}); + +// Endpoint to fetch and display data +app.get('/fetch_data/:block_id', async (req, res) => { + const block_id = parseInt(req.params.block_id); + if (isNaN(block_id)) { + return res.status(400).json({ error: "Invalid block_id" }); + } + + const uniqueId = new BN(block_id); + + const [pda] = await PublicKey.findProgramAddress( + [Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)], + program.programId + ); + + try { + console.log(`Fetching data for block_id: ${block_id}, PDA: ${pda.toString()}`); + const account = await program.account.pdaAccount.fetch(pda); + + const blockInfo = { + blockId: block_id, + entries: account.blockIds.map(entry => ({ + blockId: entry.blockId.toString(), + finalHashes: entry.finalHashes.map(hashEntry => ({ + finalHash: Buffer.from(hashEntry.finalHash).toString('utf8'), // Convert finalHash bytes to string + count: parseInt(hashEntry.count, 10), + pubkeys: hashEntry.pubkeys.map(pubkey => pubkey.toString()) + })) + })) + }; + + res.status(200).json(blockInfo); + } catch (err) { + res.status(500).json({ error: "Failed to fetch data", details: err.toString() }); + } +}); + +const PORT = 4444; +app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); +}); + diff --git a/app/test_agg.js b/app/test_agg.js new file mode 100644 index 0000000..332ba04 --- /dev/null +++ b/app/test_agg.js @@ -0,0 +1,72 @@ +const anchor = require('@coral-xyz/anchor'); +const { PublicKey, Transaction } = require('@solana/web3.js'); +const { BN } = require('bn.js'); +const { ComputeBudgetProgram } = require('@solana/web3.js'); + +// Get input from command line arguments +const block_id = parseInt(process.argv[2]); + +if (isNaN(block_id)) { + console.error('Please provide a valid block_id.'); + process.exit(1); +} + +const provider = anchor.AnchorProvider.env(); +anchor.setProvider(provider); +const program = anchor.workspace.GrowSpace; + +async function aggregatePubkeyCounts(blockId) { + const uniqueId = new BN(blockId - 100); + + const uniqueIdBuffer = Buffer.alloc(8); + uniqueIdBuffer.writeBigUInt64LE(BigInt(uniqueId.toString())); + + const [pda] = await PublicKey.findProgramAddress( + [Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)], + program.programId + ); + + const [voterAccountingPda] = await PublicKey.findProgramAddress( + [Buffer.from("accounting")], + program.programId + ); + + try { + const voterAccounting = await program.account.voterAccounting.fetch(voterAccountingPda); + const pdaAccount = await program.account.pdaAccount.fetch(pda); + const transaction = new Transaction(); + + transaction.add( + ComputeBudgetProgram.setComputeUnitLimit({ + units: 1400000, + }) + ); + + transaction.add( + await program.methods.aggregatePubkeyCounts(uniqueId).accounts({ + pdaAccount: pda, + voterAccounting: voterAccountingPda, + systemProgram: anchor.web3.SystemProgram.programId, + }).instruction() + ); + + console.log(`Sending transaction for block ID: ${uniqueId}`); + const txId = await provider.sendAndConfirm(transaction); + console.log(`Transaction sent with ID: ${txId}`); + + // Fetch the updated voter accounting data after the transaction + console.log(`Fetching updated voter accounting data for PDA: ${voterAccountingPda.toString()}`); + const updatedVoterAccounting = await program.account.voterAccounting.fetch(voterAccountingPda); + //console.log("Updated Voter Accounting Data:", updatedVoterAccounting); + + updatedVoterAccounting.pubkeyCounts.forEach((entry, index) => { + console.log(`Updated Record ${index + 1}: Pubkey = ${entry.user.toString()}, Credit = ${entry.credit.toString()}, Debit = ${entry.debit.toString()}, inBlock = ${entry.inblock.toString()}`); + }); + + } catch (err) { + console.error(`Failed to aggregate pubkey counts for block ID: ${uniqueId}`, err); + } +} + +aggregatePubkeyCounts(block_id); + diff --git a/fetch.ts b/fetch.ts new file mode 100644 index 0000000..81cc966 --- /dev/null +++ b/fetch.ts @@ -0,0 +1,36 @@ +import * as anchor from "@coral-xyz/anchor"; +import { Program } from "@coral-xyz/anchor"; +import { GrowSpace } from "../target/types/grow_space"; +import { assert } from "chai"; + +describe("grow_space_combined", () => { + // Configure the client to use the local cluster. + const provider = anchor.AnchorProvider.env(); + anchor.setProvider(provider); + + const program = anchor.workspace.GrowSpace as Program; + + // Static PDA account + const pda = new anchor.web3.PublicKey("69E5vfTiBshW7R28t3ber33wfBHyAbubTvXL3iPWsfcz"); + + it("Fetches and displays data from the static PDA", async () => { + try { + const account = await program.account.pdaAccount.fetch(pda); + + // Print and count the Block IDs + console.log("Block IDs stored in the PDA:"); + account.blockIds.forEach((entry: any, index: number) => { + console.log(`Index ${index}: Block ID ${entry.blockId.toString()}, Final Hashes: ${entry.finalHashes.map((hashEntry: any) => `${hashEntry.finalHash} (count: ${hashEntry.count})`).join(", ")}`); + }); + + // Verify that the values are unique Block IDs + const blockIdsSet = new Set(account.blockIds.map((entry: any) => entry.blockId.toString())); + assert.equal(blockIdsSet.size, account.blockIds.length, "Block IDs should be unique"); + + console.log("Total unique block IDs added: ", account.blockIds.length); + } catch (err) { + console.error("Failed to fetch data from the PDA:", err); + } + }); +}); + diff --git a/package-lock.json b/package-lock.json index 62d47e0..10c83f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "dependencies": { "@coral-xyz/anchor": "^0.30.1", "@project-serum/anchor": "^0.26.0", - "@solana/web3.js": "^1.95.2" + "@solana/web3.js": "^1.95.2", + "express": "^4.19.2" }, "devDependencies": { "@types/bn.js": "^5.1.0", @@ -346,6 +347,19 @@ "dev": true, "license": "ISC" }, + "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==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -448,6 +462,12 @@ "dev": true, "license": "Python-2.0" }, + "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==", + "license": "MIT" + }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -545,6 +565,45 @@ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", "license": "MIT" }, + "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==", + "license": "MIT", + "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/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/borsh": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz", @@ -650,6 +709,34 @@ "node": ">=6.14.2" } }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "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==", + "license": "MIT", + "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/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -797,6 +884,42 @@ "dev": true, "license": "MIT" }, + "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==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -876,6 +999,23 @@ "node": ">=6" } }, + "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==", + "license": "MIT", + "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/delay": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", @@ -888,6 +1028,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -908,6 +1067,12 @@ "tslib": "^2.0.3" } }, + "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==", + "license": "MIT" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -915,6 +1080,36 @@ "dev": true, "license": "MIT" }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -940,6 +1135,12 @@ "node": ">=6" } }, + "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==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -953,12 +1154,78 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "license": "MIT", + "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/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/eyes": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", @@ -992,6 +1259,39 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "license": "MIT", + "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/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1019,6 +1319,24 @@ "flat": "cli.js" } }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1026,6 +1344,15 @@ "dev": true, "license": "ISC" }, + "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==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1046,6 +1373,25 @@ "node": "*" } }, + "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==", + "license": "MIT", + "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/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -1093,6 +1439,18 @@ "node": "*" } }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -1113,6 +1471,54 @@ "node": ">=8" } }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1123,6 +1529,22 @@ "he": "bin/he" } }, + "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==", + "license": "MIT", + "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/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -1132,6 +1554,18 @@ "ms": "^2.0.0" } }, + "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==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -1167,9 +1601,17 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1420,6 +1862,63 @@ "dev": true, "license": "ISC" }, + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", @@ -1519,6 +2018,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -1571,6 +2079,30 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -1619,6 +2151,15 @@ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", "license": "(MIT AND Zlib)" }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -1639,6 +2180,12 @@ "node": ">=0.10.0" } }, + "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==", + "license": "MIT" + }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -1678,6 +2225,34 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "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==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -1688,6 +2263,30 @@ "safe-buffer": "^5.1.0" } }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1796,6 +2395,51 @@ ], "license": "MIT" }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "license": "MIT", + "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/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -1806,6 +2450,62 @@ "randombytes": "^2.1.0" } }, + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "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/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -1837,6 +2537,15 @@ "source-map": "^0.6.0" } }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -1935,6 +2644,15 @@ "node": ">=8.0" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/toml": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", @@ -2096,6 +2814,19 @@ "node": ">=4" } }, + "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==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", @@ -2116,6 +2847,15 @@ "integrity": "sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==", "license": "MIT" }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/utf-8-validate": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", @@ -2130,6 +2870,15 @@ "node": ">=6.14.2" } }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -2146,6 +2895,15 @@ "dev": true, "license": "MIT" }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index a9de120..4aa27ea 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dependencies": { "@coral-xyz/anchor": "^0.30.1", "@project-serum/anchor": "^0.26.0", - "@solana/web3.js": "^1.95.2" + "@solana/web3.js": "^1.95.2", + "express": "^4.19.2" }, "devDependencies": { "@types/bn.js": "^5.1.0", diff --git a/programs/grow_space/Cargo.toml b/programs/grow_space/Cargo.toml index d37e055..a6e63e9 100644 --- a/programs/grow_space/Cargo.toml +++ b/programs/grow_space/Cargo.toml @@ -15,7 +15,6 @@ no-entrypoint = [] no-idl = [] no-log-ix-name = [] idl-build = ["anchor-lang/idl-build"] - init_if_needed = ["anchor-lang/init-if-needed"] [dependencies] diff --git a/programs/grow_space/src/bpf_writer.rs b/programs/grow_space/src/bpf_writer.rs new file mode 100644 index 0000000..7d8758e --- /dev/null +++ b/programs/grow_space/src/bpf_writer.rs @@ -0,0 +1,50 @@ +use solana_program::program_memory::sol_memcpy; +use std::io::{self, Write}; +use std::{cmp, fmt}; + +#[derive(Debug, Default)] +pub struct BpfWriter { + inner: T, + pos: u64, +} + +impl BpfWriter { + pub fn new(inner: T) -> Self { + Self { inner, pos: 0 } + } +} + +impl Write for BpfWriter<&mut [u8]> { + fn write(&mut self, buf: &[u8]) -> io::Result { + if self.pos >= self.inner.len() as u64 { + return Ok(0); + } + + let amt = cmp::min( + self.inner.len().saturating_sub(self.pos as usize), + buf.len(), + ); + sol_memcpy(&mut self.inner[(self.pos as usize)..], buf, amt); + self.pos += amt as u64; + Ok(amt) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + if self.write(buf)? == buf.len() { + Ok(()) + } else { + Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )) + } + } + + fn write_fmt(&mut self, _fmt: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } +} diff --git a/programs/grow_space/src/lib.rs b/programs/grow_space/src/lib.rs index 42fe763..a3e55b0 100644 --- a/programs/grow_space/src/lib.rs +++ b/programs/grow_space/src/lib.rs @@ -1,97 +1,505 @@ +mod bpf_writer; + use anchor_lang::prelude::*; -use anchor_lang::solana_program::program::invoke_signed; -use anchor_lang::solana_program::system_instruction; +use bpf_writer::BpfWriter; +use solana_program::program::invoke; +use solana_program::program::set_return_data; +use solana_program::system_instruction; + +declare_id!("DzDvqRGfLJkFxUB4sBCxS4EuXk2EK62PPpAFfPz6h6p5"); -declare_id!("7KvbAAK7kP72zcdC24vDn9L51TDV8v9he4hNJ3S7ZU51"); +const PDA_ACCOUNT_SEED: &[u8; 11] = b"pda_account"; +const USER_ACCOUNT_PDA_SEED: &[u8; 16] = b"user_account_pda"; +const ACCOUNTING_SEED: &[u8; 10] = b"accounting"; #[program] pub mod grow_space { use super::*; - pub fn initialize_pda(ctx: Context, unique_id: u64) -> Result<()> { - let pda_account = &mut ctx.accounts.pda_account; + pub fn initialize_pda(_ctx: Context, _unique_id: u64) -> Result<()> { + Ok(()) + } + + pub fn aggregate_pubkey_counts( + ctx: Context, + start_block_id: u64, + ) -> Result<()> { + // Look back to previous block_id pda + let (pda, _bump) = Pubkey::find_program_address( + &[PDA_ACCOUNT_SEED, &start_block_id.to_le_bytes()], + ctx.program_id, + ); + msg!("PDA Account for block ID {}: {}", start_block_id, pda); + + // Load the PDA account, this is coming from client + let pda_account = &ctx.accounts.pda_account; + + // Ensure there is at least one BlockEntry and one FinalHashEntry in the first BlockEntry + let first_final_hash_entry = pda_account + .block_ids + .first() + .and_then(|block_entry| block_entry.final_hashes.first()) + .ok_or_else(|| error!(ErrorCode::FinalHashEntryNotFound))?; + + // Convert final_hash bytes to string + let final_hash_str = std::str::from_utf8(&first_final_hash_entry.final_hash) + .map_err(|_| error!(ErrorCode::InvalidUtf8))?; + + // Display the final hash and associated pubkeys from the first BlockEntry + msg!( + "First final hash for the first block_id is {:?}", + final_hash_str + ); + msg!("Total count: {:?}", first_final_hash_entry.pubkeys.len()); + /* + + // Collect all pubkeys from the first final_hash_entry that have an inblock value less than the current start_block_id + // or do not exist in the voter_accounting at all + let all_pubkeys: Vec = first_final_hash_entry.pubkeys.iter() + .filter_map(|&pubkey| { + if ctx.accounts.voter_accounting.pubkey_counts.iter().all(|count| count.user != pubkey || count.inblock < start_block_id) { + Some(pubkey) + } else { + None + } + }) + .collect(); + + if all_pubkeys.len() < 3 { + return Err(error!(ErrorCode::InsufficientPubkeys)); + } + + // Select and write 3 random pubkeys manually + let mut index = (anchor_lang::solana_program::sysvar::clock::Clock::get().unwrap().unix_timestamp % all_pubkeys.len() as i64) as usize; + + for _ in 0..3 { + let pubkey = all_pubkeys[index]; + msg!("Index for pubkey: {} index {}", pubkey, index); + + // Increment the index and use modulo to wrap around if necessary + index = (index + 1) % all_pubkeys.len(); + + // Find or create the user PDA account + let (_user_pda, _user_bump) = Pubkey::find_program_address(&[b"user_pda", pubkey.as_ref()], ctx.program_id); + let user_pda_account = &mut ctx.accounts.user_pda_account; + + // Initialize or update the user PDA account + if user_pda_account.user == Pubkey::default() { + user_pda_account.user = pubkey; + user_pda_account.credit = 1; + user_pda_account.debit = 0; + user_pda_account.inblock = start_block_id; + } else if user_pda_account.inblock < start_block_id { + user_pda_account.credit = 1; + user_pda_account.debit = 0; + user_pda_account.inblock = start_block_id; + } + } + */ - // Initialize the account with an empty vector - pda_account.values = Vec::new(); Ok(()) } - pub fn append_value(ctx: Context, value: u64) -> Result<()> { - let pda_account = &mut ctx.accounts.pda_account; - let payer = &mut ctx.accounts.payer; - - // Calculate new length in bytes - let new_len = (pda_account.values.len() + 1) * 8 + 16; // u64 is 8 bytes when len is zero, pad with 16 - msg!("pda_account.to_account_info().data_len() {}", pda_account.to_account_info().data_len()); - msg!("new_len {}", new_len); - - // Reallocate if needed - if pda_account.to_account_info().data_len() < new_len { - let rent = Rent::get()?; - let new_size = 8 + 256 + new_len; // pad with bytes - let current_balance = **pda_account.to_account_info().lamports.borrow(); - let lamports_needed = rent.minimum_balance(new_size).saturating_sub(current_balance); - msg!("Lamports needed for new size: {}", lamports_needed); - - transfer_lamports( - &payer.to_account_info(), - &pda_account.to_account_info(), - &ctx.accounts.system_program.to_account_info(), - lamports_needed, - )?; + pub fn append_data(ctx: Context, block_id: u64, final_hash: String) -> Result<()> { + let mut pda_account = &mut ctx.accounts.pda_account; + let mut user_account_pda = &mut ctx.accounts.user_account_pda; - pda_account.to_account_info().realloc(new_len + 256, false)?; + if user_account_pda.user == Pubkey::default() { + user_account_pda.user = ctx.accounts.payer.key(); } - pda_account.values.push(value); + msg!( + "block_id: {} for pda: {:?} len: {}", + block_id, + pda_account.key(), + pda_account.to_account_info().data_len() + ); + + // Ensure there is at least one BlockEntry and one FinalHashEntry in the first BlockEntry + if ctx.accounts.prev_pda_account.is_some() { + let prev_pda_account = ctx.accounts.prev_pda_account.clone().unwrap(); + // Log some details about the previous PDA account for debugging + msg!( + "Previous PDA Account Block ID for pda: {:?} Length: {}", + prev_pda_account.key(), + prev_pda_account.block_ids.len() + ); + + // for each BlockEntry of previous block + for entry in prev_pda_account.block_ids.iter() { + // calculate total count of votes + let total_count: u64 = entry + .final_hashes + .iter() + .map(|h| h.pubkeys.len() as u64) + .sum(); + // sort hashes by votes count in reverse order + let mut hashes_sorted = entry.final_hashes.clone(); + hashes_sorted.sort_by_key(|h| std::cmp::Reverse(h.pubkeys.len())); + + // check if the highest score gets over 50% of total votes + if hashes_sorted[0].pubkeys.len() as u64 > total_count / 2 { + // for each voter in the voting vector + for voter in hashes_sorted[0].pubkeys.iter() { + // find voter's PDA + let (voter_pda, _) = Pubkey::find_program_address( + &[USER_ACCOUNT_PDA_SEED, (*voter).as_ref()], + ctx.program_id, + ); + // find account info + for user_account in ctx.remaining_accounts.iter() { + // serialize voter's PDA + if voter_pda == *user_account.key { + let buf: &mut [u8] = + &mut user_account.try_borrow_mut_data().unwrap(); + let mut voter_account: UserAccountPda = + UserAccountPda::try_deserialize(&mut &*buf)?; + + // prevent self-voting + if voter_account.user != ctx.accounts.payer.key() { + // perform accounting on voter's PDA + if voter_account.inblock < block_id { + msg!( + "Eligible voter: {} in-block: {} block: {}", + voter.key(), + voter_account.inblock, + block_id, + ); + voter_account.credit += 1; + voter_account.inblock = block_id; + msg!("Voter's new credit: {}", voter_account.credit); + let mut writer = BpfWriter::new(&mut *buf); + voter_account.try_serialize(&mut writer)?; + } + } + } + } + } + } + } + } + + msg!( + "Dump user_account_pda: {} {} {} {}", + user_account_pda.user, + user_account_pda.credit, + user_account_pda.debit, + user_account_pda.inblock + ); + + // Log the current data size before modification + msg!( + "Current data length allocation: {}", + pda_account.to_account_info().data_len() + ); + + // Convert the final_hash string to bytes and truncate to 64 bits (8 bytes) + let final_hash_bytes: [u8; 8] = { + let mut bytes = final_hash.as_bytes().to_vec(); + bytes.resize(8, 0); // Ensure it has at least 8 bytes + bytes[..8].try_into().expect("slice with incorrect length") + }; + + // Convert truncated bytes back to string for logging + let final_hash_truncated_str = String::from_utf8_lossy(&final_hash_bytes); + + // Log the incoming final_hash string and its truncated byte representation + msg!("Incoming final_hash string: {}", final_hash); + msg!( + "Truncated final_hash bytes as string: {}", + final_hash_truncated_str + ); + + let mut found = false; + let mut add_size: usize = 0; + for block_entry in &mut pda_account.block_ids { + if block_entry.block_id == block_id { + found = true; + let mut hash_found = false; + for hash_entry in &mut block_entry.final_hashes { + if hash_entry.final_hash == final_hash_bytes { + if !hash_entry.pubkeys.contains(ctx.accounts.payer.key) { + hash_entry.pubkeys.push(*ctx.accounts.payer.key); + add_size += 32; + } + hash_found = true; + break; + } + } + if !hash_found { + let final_hashes = &mut block_entry.final_hashes; + add_size += 32 + 8 + 8; + final_hashes.push(FinalHashEntry { + final_hash: final_hash_bytes, + pubkeys: vec![*ctx.accounts.payer.key], + // count: 1, + }); + } + break; + } + } + + if !found { + pda_account.block_ids.push(BlockEntry { + block_id, + final_hashes: vec![FinalHashEntry { + final_hash: final_hash_bytes, + pubkeys: vec![ctx.accounts.payer.key.clone()], + // count: 1, + }], + }); + add_size += 64; + } + msg!("PDA account: {:?}", pda_account.block_ids); + + // Log the new data size after modification + // let current_data_after = calculate_data_size(&pda_account.block_ids); + msg!( + "New length of pda_account.entries: {}", + pda_account.block_ids.len() + ); + // msg!("Data size after in bytes: {}", current_data_after); + msg!( + "Current data length allocation: {} new: {}", + pda_account.to_account_info().data_len(), + add_size + ); + + // Check if the data size exceeds 80% of the allocated space + let data_len = pda_account.to_account_info().data_len(); + let rent = Rent::get()?; + let new_size = data_len + add_size; // + let lamports_needed = rent + .minimum_balance(new_size as usize) + .saturating_sub(pda_account.to_account_info().lamports()); + + if lamports_needed > 0 { + // Transfer lamports to cover the additional rent + invoke( + &system_instruction::transfer( + &ctx.accounts.payer.key(), + &pda_account.to_account_info().key(), + lamports_needed, + ), + &[ + ctx.accounts.payer.to_account_info(), + pda_account.to_account_info(), + ctx.accounts.system_program.to_account_info(), + ], + ) + .expect("Rent payment failed"); + } + + pda_account + .to_account_info() + .realloc(new_size as usize, false) + .expect("Reallocation failed"); + + msg!( + "Reallocated PDA account to new size: {}", + pda_account.to_account_info().data_len() + ); + + Ok(()) + } + + pub fn get_voter_accounting_chunk( + ctx: Context, + offset: u64, + limit: u64, + ) -> Result<()> { + let voter_accounting = &ctx.accounts.voter_accounting; + + let end = + std::cmp::min(offset + limit, voter_accounting.pubkey_counts.len() as u64) as usize; + let chunk = &voter_accounting.pubkey_counts[offset as usize..end]; + + // Serialize the chunk of data + let data = chunk + .try_to_vec() + .map_err(|_| error!(ErrorCode::SerializationError))?; + + // Set the return data + set_return_data(&data); + Ok(()) } } -pub fn transfer_lamports<'info>( - from: &AccountInfo<'info>, - to: &AccountInfo<'info>, - system_program: &AccountInfo<'info>, - lamports: u64, -) -> Result<()> { - invoke_signed( - &system_instruction::transfer( - from.key, - to.key, - lamports, - ), - &[ - from.clone(), - to.clone(), - system_program.clone(), - ], - &[], - )?; - msg!("Transferring {} lamports to PDA account", lamports); - Ok(()) +/* +fn calculate_data_size(entries: &Vec) -> usize { + let mut total_size = 0; + for entry in entries { + // Size of block_id + total_size += 8; + // Size of each FinalHashEntry in final_hashes + for hash_entry in &entry.final_hashes { + total_size += 8 + 8; // Assuming 8 bytes for final_hash and 8 bytes for count + total_size += hash_entry.pubkeys.len() * 32; // Each Pubkey is 32 bytes + } + } + total_size } + */ #[derive(Accounts)] -#[instruction(unique_id: u64)] +#[instruction(_unique_id: u64)] pub struct InitializePDA<'info> { - #[account(init, seeds = [b"pda_account", payer.key.as_ref(), &unique_id.to_le_bytes()], bump, payer = payer, space = 8 + 8 * 2)] // 8 bytes for discriminator + initial space for 10 u64 values + #[account( + init_if_needed, + seeds = [ + PDA_ACCOUNT_SEED, + _unique_id.to_le_bytes().as_ref() + ], + bump, + payer = payer, + space = 8 + PDAAccount::INIT_SPACE, + )] pub pda_account: Account<'info, PDAAccount>, #[account(mut)] pub payer: Signer<'info>, pub system_program: Program<'info, System>, } +/* #[derive(Accounts)] -pub struct AppendValue<'info> { +pub struct PubkeyCount<'info> { + #[account(init_if_needed, seeds = [b"pubkey_count"], bump, payer = payer, space = 16)] + pub pubkey_count_account: Account<'info, CountAccount>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + */ + +#[derive(Accounts)] +#[instruction(block_id: u64, final_hash: String)] +pub struct AppendData<'info> { #[account(mut)] pub pda_account: Account<'info, PDAAccount>, #[account(mut)] + pub prev_pda_account: Option>, + #[account( + init_if_needed, + seeds = [ + USER_ACCOUNT_PDA_SEED, + payer.key().as_ref() + ], + bump, + payer = payer, + space = 8 + UserAccountPda::INIT_SPACE, + // constraint = user_account_pda.user == payer.key() + )] + pub user_account_pda: Box>, + #[account(mut)] + pub payer: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +#[instruction(start_block_id: u64)] +pub struct PerformAccounting<'info> { + #[account(mut)] + pub pda_account: Box>, + #[account( + init_if_needed, + seeds = [ACCOUNTING_SEED], + bump, + payer = payer, + space = 8 + UserAccountPda::INIT_SPACE + )] // Adjust space as needed + pub voter_accounting: Box>, + #[account( + init_if_needed, + seeds = [ + USER_ACCOUNT_PDA_SEED, + payer.key().as_ref() + ], + bump, + payer = payer, + space = 8 + UserAccountPda::INIT_SPACE + )] + pub user_pda_account: Box>, + #[account(mut)] pub payer: Signer<'info>, pub system_program: Program<'info, System>, } +#[derive(Accounts)] +pub struct GetVoterAccounting<'info> { + pub voter_accounting: Box>, +} + +#[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone, InitSpace)] +pub struct BlockEntry { + pub block_id: u64, + #[max_len(0)] + pub final_hashes: Vec, +} + +#[derive(Debug, AnchorSerialize, AnchorDeserialize, Clone, InitSpace)] +pub struct FinalHashEntry { + pub final_hash: [u8; 8], + #[max_len(0)] + pub pubkeys: Vec, + // pub count: u64, +} + +#[account] +#[derive(Debug, InitSpace)] +pub struct UserAccountPda { + pub user: Pubkey, + pub credit: u64, + pub debit: u64, + pub inblock: u64, +} + +#[account] +#[derive(InitSpace, Default)] +pub struct VoterAccounting { + // user, credit, debit + #[max_len(0)] + pub pubkey_counts: Vec, +} + +/* +#[account] +#[derive(InitSpace, Default)] +pub struct UserPDAAccount { + pub user: Pubkey, + pub credit: u64, + pub debit: u64, + pub inblock: u64, +} + + #[account] +#[derive(InitSpace, Default)] +pub struct CountAccount { + pub count: u64, +} + + */ + +#[account] +#[derive(Debug, InitSpace, Default)] pub struct PDAAccount { - pub values: Vec, + #[max_len(0)] + pub block_ids: Vec, + // pub data_size: u32, } +#[error_code] +pub enum ErrorCode { + #[msg("Block entry not found.")] + BlockEntryNotFound, + #[msg("Final hash entry not found.")] + FinalHashEntryNotFound, + #[msg("Invalid UTF-8 sequence.")] + InvalidUtf8, + #[msg("Insufficient pubkeys available.")] + InsufficientPubkeys, + #[msg("Serialization error.")] + SerializationError, + // Add other error codes as needed +} diff --git a/tests/grow_space.ts b/tests/grow_space.ts index 136ff4d..18136b7 100644 --- a/tests/grow_space.ts +++ b/tests/grow_space.ts @@ -1,58 +1,228 @@ -import * as anchor from "@coral-xyz/anchor"; -import { Program } from "@coral-xyz/anchor"; -import { GrowSpace } from "../target/types/grow_space"; -import { assert } from "chai"; -import { BN } from "bn.js"; - -describe("grow_space", () => { - // Configure the client to use the local cluster. - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.GrowSpace as Program; - - let pda: anchor.web3.PublicKey; - let bump: number; - const uniqueId = new BN(Date.now()); // Use a unique identifier and convert to BN - - it("Initializes the PDA", async () => { - [pda, bump] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from("pda_account"), provider.wallet.publicKey.toBuffer(), uniqueId.toArrayLike(Buffer, "le", 8)], - program.programId - ); - - console.log("Initialized PDA account public key:", pda.toString(), "with bump:", bump); - - try { - await program.methods.initializePda(uniqueId).accounts({ - pdaAccount: pda, - payer: provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }) - .signers([provider.wallet.payer]) // Explicitly include the default wallet as a signer - .rpc(); - } catch (err) { - console.error("Failed to initialize PDA:", err); +import {workspace, web3, AnchorProvider, setProvider,} from "@coral-xyz/anchor"; +import type {Program} from "@coral-xyz/anchor"; +import {GrowSpace} from "../target/types/grow_space"; +import {assert} from "chai"; +import {BN} from "bn.js"; +import {ComputeBudgetProgram, Keypair, LAMPORTS_PER_SOL, SystemProgram, Transaction} from "@solana/web3.js"; + +const formatUserPda = (a) => ({ + user: a.user.toString(), + credit: a.credit.toNumber(), + debit: a.debit.toNumber(), + inblock: a.inblock.toNumber() +}); + +describe("grow_space_combined", () => { + // Configure the client to use the local cluster. + const provider = AnchorProvider.env(); + setProvider(provider); + + const program = workspace.GrowSpace as Program; + + const keypairs: Keypair[] = [] + const KEYS = 3; + + const getUserPda = (keypair: Keypair) => { + const [userPda] = web3.PublicKey.findProgramAddressSync( + [Buffer.from("user_account_pda"), keypair.publicKey.toBytes()], + program.programId + ) + return userPda; } - }); - - it("Verifies the PDA is initialized with an empty vector", async () => { - const account = await program.account.pdaAccount.fetch(pda); - assert.deepEqual(account.values, []); - }); - - it("Appends values to the PDA and reallocates if necessary", async () => { - for (let i = 1; i <= 300; i++) { - console.log("Loop: " + i); - await program.methods.appendValue(new anchor.BN(i)) - .accounts({ - pdaAccount: pda, - }) - .rpc(); + + const createAndFundAccount = async () => { + const keypair = web3.Keypair.generate(); + const fundTx = new Transaction().add( + SystemProgram.transfer({ + fromPubkey: provider.wallet.publicKey, + toPubkey: keypair.publicKey, + lamports: 0.005 * LAMPORTS_PER_SOL, + }) + ); + await provider.sendAndConfirm(fundTx, []); + return keypair; } - const account = await program.account.pdaAccount.fetch(pda); - assert.deepEqual(account.values.map((v: any) => v.toNumber()), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); - }); + const printPdaAccountInfo = async (blockId: string) => { + const uniqueId = new BN(parseInt(blockId)); + const [pda] = web3.PublicKey.findProgramAddressSync( + [Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)], + program.programId + ); + + const account = await program.account.pdaAccount.fetch(pda); + + console.log(`Block ID ${blockId} stored in PDA`) + console.log(account); + } + + it("Appends multiple final hashes, including repeats, to random block IDs in the PDA with repeated pubkeys", async () => { + const blockIds = new Set(); + let pda: web3.PublicKey; + let prevPda: web3.PublicKey; + + const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({ + units: 1_400_000 + }); + + for await (const i of Array.from({length: KEYS + 1}, (_, i) => i)) { + const keypair = await createAndFundAccount(); + keypairs.push(keypair) + } + + let randomBlockId = Math.floor(Math.random() * 10_000); + + for await (const i of [0, 1, 2]) { // Limiting to 3 for testing purposes + randomBlockId += Math.floor(Math.random() * 100_000); + blockIds.add(randomBlockId.toString()) + const uniqueId = new BN(randomBlockId); // Use the block ID as the unique ID + console.log("\n\nLoop: " + i + ", Block ID: " + randomBlockId); + + let bump: number; + + // Initialize a new PDA for each block ID + [pda, bump] = web3.PublicKey.findProgramAddressSync( + [Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)], + program.programId + ) + console.log('PDA', pda.toString(), 'prev', prevPda?.toString()) + + try { + const sig = await program.methods.initializePda(uniqueId) + .accounts({ + payer: provider.wallet.publicKey, + }) + .signers([]) + .rpc({commitment: "confirmed", skipPreflight: true}); + console.log("Initialized PDA account public key:", pda.toString(), "with bump:", bump, 'sig:', sig); + } catch (err) { + console.error("Failed to initialize PDA:", err); + } + + // Append repeating final hashes with repeated pubkeys + const repeatingHashes = [`hash_${randomBlockId}_r1`, `hash_${randomBlockId}_r1`, `hash_${randomBlockId}_r1`]; + for await (const j of Array(3).fill(0).map((_, i) => i)) { // Reduced to 3 for testing purposes + for await (const repeatingHash of repeatingHashes) { + try { + const keypair = keypairs[Math.floor(Math.random() * (KEYS + 1))]; + + const [userPda] = web3.PublicKey.findProgramAddressSync( + [Buffer.from("user_account_pda"), keypair.publicKey.toBytes()], + program.programId + ) + + const sig = await program.methods.appendData(uniqueId, repeatingHash) + .accountsPartial({ + pdaAccount: pda, + payer: keypair.publicKey, + userAccountPda: userPda, + // payer: provider.wallet.publicKey, + prevPdaAccount: prevPda || null, + // systemProgram: web3.SystemProgram.programId, + }) + .remainingAccounts([...keypairs.map(k => ({ + pubkey: getUserPda(k), + isSigner: false, + isWritable: true + }))]) + .preInstructions([modifyComputeUnits]) + .signers([keypair]) + .rpc({commitment: "confirmed", skipPreflight: true}); + console.log(" Appending Repeating Final Hash:", repeatingHash, "payer:", keypair.publicKey.toString(), "sig:", sig); + } catch (err) { + console.error(`Failed to append data for Block ID ${randomBlockId}:`, err); + } + } + } + + // Append unique final hashes + for await (const k of [0, 1, 2]) { // Reduced to 3 for testing purposes + randomBlockId += Math.floor(Math.random() * 100_000); + blockIds.add(randomBlockId.toString()) + const uniqueHash = `hash_${randomBlockId}_unique${k}`; + + try { + const keypair = keypairs[Math.floor(Math.random() * (KEYS + 1))]; + + const sig = await program.methods.appendData(new BN(randomBlockId), uniqueHash) + .accountsPartial({ + pdaAccount: pda, + prevPdaAccount: prevPda || null, + payer: keypair.publicKey, + // payer: provider.wallet.publicKey, + //systemProgram: web3.SystemProgram.programId, + }) + .signers([keypair]) + .remainingAccounts([...keypairs.map(k => ({ + pubkey: getUserPda(k), + isSigner: false, + isWritable: true + }))]) + .preInstructions([modifyComputeUnits]) + .rpc({commitment: "confirmed", skipPreflight: true}); + + console.log(" Appending Unique Final Hash:", uniqueHash, "payer:", keypair.publicKey.toString(), "sig:" + sig); + + } catch (err) { + console.error(`Failed to append data for Block ID ${randomBlockId}:`, err); + } + } + + await new Promise(resolve => setTimeout(resolve, 5_000)); + + prevPda = pda; + + } + + console.log('\n\nBlocks', [...blockIds].join(", "), '\n\n'); + // Fetch the PDA and print the stored data + for await (const blockId of [...blockIds]) { + const uniqueId = new BN(parseInt(blockId)); + const [pda] = web3.PublicKey.findProgramAddressSync( + [Buffer.from("pda_account"), uniqueId.toArrayLike(Buffer, "le", 8)], + program.programId + ); + + try { + const account = await program.account.pdaAccount.fetch(pda); + if (account.blockIds.length > 0) { + account.blockIds.forEach((entry: any, index: number) => { + // printPdaAccountInfo(blockId.toString()); + console.log(` Block ID ${entry.blockId.toString()}`); + entry.finalHashes.forEach((hashEntry: any) => { + console.log(` Final Hash: ${Buffer.from(hashEntry.finalHash).toString()} (pubkeys count: ${hashEntry.pubkeys.length})`); + hashEntry.pubkeys.forEach((pubkey: any, pubkeyIndex: number) => { + console.log(` Pubkey ${pubkeyIndex}: ${pubkey.toString()}`); + }); + }); + }); + } else { + console.log(`PDA ${pda}: No blockIds`) + } + } catch (e) { + // console.log(e.message) + } + } + + console.log('\n\n'); + for await (const keypair of keypairs) { + const [userPda] = web3.PublicKey.findProgramAddressSync( + [Buffer.from("user_account_pda"), keypair.publicKey.toBytes()], + program.programId + ) + try { + const userAccount = await program.account.userAccountPda.fetch(userPda); + console.log('user pda', keypair.publicKey.toString(), formatUserPda(userAccount)) + } catch (e) { + console.log(e.message) + } + } + + // Verify that the values are unique Block IDs + const blockIdsSet = new Set(Array.from(blockIds)); + assert.equal(blockIdsSet.size, blockIds.size, "Block IDs should be unique"); + + console.log("Total unique block IDs added: ", blockIds.size); + }); }); diff --git a/yarn.lock b/yarn.lock index fa7fb8a..939fc58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -229,6 +229,14 @@ resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +accepts@~1.3.8: + 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" + acorn-walk@^8.1.1: version "8.3.3" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz" @@ -283,6 +291,11 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + arrify@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" @@ -334,6 +347,24 @@ bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +body-parser@1.20.2: + 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" + borsh@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz" @@ -395,6 +426,22 @@ bufferutil@^4.0.1: dependencies: node-gyp-build "^4.3.0" +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.7: + 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" + camelcase@^6.0.0, camelcase@^6.3.0: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" @@ -474,6 +521,28 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +content-disposition@0.5.4: + 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" + +content-type@~1.0.4, content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== + create-require@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" @@ -491,6 +560,13 @@ crypto-hash@^1.3.0: resolved "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz" integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== +debug@2.6.9: + 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" + debug@4.3.3: version "4.3.3" resolved "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz" @@ -510,11 +586,30 @@ deep-eql@^4.1.3: dependencies: type-detect "^4.0.0" +define-data-property@^1.1.4: + 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" + delay@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + diff@^3.1.0: version "3.5.0" resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" @@ -538,11 +633,33 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +es-define-property@^1.0.0: + 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" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz" @@ -560,11 +677,21 @@ escalade@^3.1.1: resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + escape-string-regexp@4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" @@ -575,6 +702,43 @@ eventemitter3@^5.0.1: resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== +express@^4.19.2: + 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" + eyes@^0.1.8: version "0.1.8" resolved "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" @@ -597,6 +761,19 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" +finalhandler@1.2.0: + 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" + find-up@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" @@ -610,11 +787,26 @@ flat@^5.0.2: resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" @@ -625,6 +817,17 @@ get-func-name@^2.0.1, get-func-name@^2.0.2: resolved "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + 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" + glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" @@ -644,6 +847,13 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +gopd@^1.0.1: + 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" + growl@1.10.5: version "1.10.5" resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" @@ -654,11 +864,46 @@ has-flag@^4.0.0: resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.2: + 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" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0: + 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" + he@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +http-errors@2.0.0: + 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" + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz" @@ -666,6 +911,13 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +iconv-lite@0.4.24: + 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" + ieee754@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" @@ -679,11 +931,16 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ipaddr.js@1.9.1: + 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== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" @@ -822,6 +1079,38 @@ make-error@^1.1.1: resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@~2.1.24, mime-types@~2.1.34: + 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" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + minimatch@^3.0.4: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" @@ -883,6 +1172,11 @@ ms@^2.0.0, ms@2.1.3: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" @@ -893,6 +1187,11 @@ nanoid@3.3.1: resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + no-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" @@ -918,6 +1217,18 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +on-finished@2.4.1: + 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" + once@^1.3.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" @@ -944,6 +1255,11 @@ pako@^2.0.3: resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" @@ -954,6 +1270,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + pathval@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz" @@ -969,6 +1290,21 @@ prettier@^2.6.2: resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +proxy-addr@~2.0.7: + 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" + +qs@6.11.0: + 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" + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" @@ -976,6 +1312,21 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + 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" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" @@ -1009,11 +1360,35 @@ rpc-websockets@^9.0.2: bufferutil "^4.0.1" utf-8-validate "^5.0.2" -safe-buffer@^5.0.1, safe-buffer@^5.1.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +send@0.18.0: + 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" + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz" @@ -1021,6 +1396,43 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" +serve-static@1.15.0: + 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" + +set-function-length@^1.2.1: + 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" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +side-channel@^1.0.4: + 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" + snake-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" @@ -1042,6 +1454,11 @@ source-map@^0.6.0: resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -1109,6 +1526,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + toml@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz" @@ -1181,6 +1603,14 @@ type-detect@^4.0.0, type-detect@^4.1.0: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz" integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== +type-is@~1.6.18: + 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" + typescript@^4.9.5, typescript@>=2.7: version "4.9.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" @@ -1191,6 +1621,11 @@ undici-types@~6.11.1: resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.11.1.tgz" integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ== +unpipe@~1.0.0, unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + utf-8-validate@^5.0.2, utf-8-validate@>=5.0.2: version "5.0.10" resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz" @@ -1198,6 +1633,11 @@ utf-8-validate@^5.0.2, utf-8-validate@>=5.0.2: dependencies: node-gyp-build "^4.3.0" +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" @@ -1208,6 +1648,11 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz"