diff --git a/.github/workflows/test:chains.yml b/.github/workflows/test:chains.yml deleted file mode 100644 index 4e26b292b..000000000 --- a/.github/workflows/test:chains.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Chain tests - -# INFO: This workflow can't push to the repository -permissions: read-all - -# INFO: Run workflow on changes to the sdks -on: - push: - paths: - - 'sdk/localsdk/multichain/**' - -jobs: - test: - name: XM Chain tests - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Setup Node 18 - uses: actions/setup-node@v3 - with: - node-version: 18.x - - name: Cache dependencies - uses: actions/cache@v3 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-node- - - name: Install dependencies - run: | - yarn install --frozen-lockfile - - name: Run tests - run: | - yarn test:chains diff --git a/.gitignore b/.gitignore index f9c5f3335..56dd4c2b9 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,5 @@ src/features/experimental/fhe/fhe_test.ts yarn.lock publickey publickey +documentation/schemas/.$* +appmap.yml diff --git a/data/genesis.json b/data/genesis.json index c8bd6c4ae..9d843ac06 100644 --- a/data/genesis.json +++ b/data/genesis.json @@ -11,6 +11,10 @@ [ "ccc6ba0c609435a05fdbf236e7df7d60f024ed26c19d8f64b024e6163036247a", "1000000000000000000000000000" + ], + [ + "6f1df0905c986d41bcb01c8b542c0af8263c03ba52a3dd3af9123d99fc8e1067", + "1000000000000000000000000000" ] ], "timestamp": "1692734616000", diff --git a/documentation/Best Practices.md b/documentation/Best Practices.md new file mode 100644 index 000000000..07666cd71 --- /dev/null +++ b/documentation/Best Practices.md @@ -0,0 +1,13 @@ +# Best Practices when coding in this repository + +## Imports +As per `tsconfig.json`, the root of the repository is set to `./`. +For this reason, is easy to keep all the imports sorted and tidy by avoiding relative paths such as `../mymodule.ts` and using directly `src/libs/mymodule.ts` for example. + +## Interfaces +To keep everything ordered, please use the `types` folder in your subdirectory (i.e. `src/libs/blockchain/types`) and import from there. Please avoid defining and exporting interfaces all around the place. + +Classes should have their own file too, while not residing in a dedicated folder. + +## Linting and Formatting +`trunk` linter is used and enabled globally. Configuration should be already shipped. Alternatively, you can use `prettier-eslint`. Please avoid using other formatting tools to avoid huge "false-commits". \ No newline at end of file diff --git a/documentation/code diagrams/Web2.drawio b/documentation/code diagrams/Web2.drawio deleted file mode 100644 index 74c6a6c6a..000000000 --- a/documentation/code diagrams/Web2.drawio +++ /dev/null @@ -1,159 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/documentation/code diagrams/blockchain_diagram.png b/documentation/code diagrams/blockchain_diagram.png new file mode 100644 index 000000000..3b59bdbef Binary files /dev/null and b/documentation/code diagrams/blockchain_diagram.png differ diff --git a/documentation/code diagrams/communications_diagram.png b/documentation/code diagrams/communications_diagram.png new file mode 100644 index 000000000..5b2f2fbb0 Binary files /dev/null and b/documentation/code diagrams/communications_diagram.png differ diff --git a/documentation/code diagrams/consensus_diagram.png b/documentation/code diagrams/consensus_diagram.png new file mode 100644 index 000000000..ae1821e79 Binary files /dev/null and b/documentation/code diagrams/consensus_diagram.png differ diff --git a/documentation/code diagrams/multichain_diagram.png b/documentation/code diagrams/multichain_diagram.png new file mode 100644 index 000000000..a17039f6d Binary files /dev/null and b/documentation/code diagrams/multichain_diagram.png differ diff --git a/documentation/code diagrams/network_diagram.png b/documentation/code diagrams/network_diagram.png new file mode 100644 index 000000000..6ab5cc78c Binary files /dev/null and b/documentation/code diagrams/network_diagram.png differ diff --git a/documentation/code diagrams/web2_diagram.png b/documentation/code diagrams/web2_diagram.png new file mode 100644 index 000000000..57e7df657 Binary files /dev/null and b/documentation/code diagrams/web2_diagram.png differ diff --git a/documentation/faq.docx b/documentation/faq.docx new file mode 100644 index 000000000..d4231639f Binary files /dev/null and b/documentation/faq.docx differ diff --git a/documentation/schemas/.$development.drawio.bkp b/documentation/schemas/.$development.drawio.bkp new file mode 100644 index 000000000..f315ebb38 --- /dev/null +++ b/documentation/schemas/.$development.drawio.bkp @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/schemas/development.drawio b/documentation/schemas/development.drawio new file mode 100644 index 000000000..58b9de10e --- /dev/null +++ b/documentation/schemas/development.drawio @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/schemas/development.drawio.png b/documentation/schemas/development.drawio.png new file mode 100644 index 000000000..df5d304b0 Binary files /dev/null and b/documentation/schemas/development.drawio.png differ diff --git a/documentation/schemas/new_tx_order.drawio b/documentation/schemas/new_tx_order.drawio new file mode 100644 index 000000000..46cc8fa5f --- /dev/null +++ b/documentation/schemas/new_tx_order.drawio @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation/schemas/new_tx_order.drawio.png b/documentation/schemas/new_tx_order.drawio.png new file mode 100644 index 000000000..96299a87d Binary files /dev/null and b/documentation/schemas/new_tx_order.drawio.png differ diff --git a/package.json b/package.json index 59d1bf8b6..5214657c4 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,15 @@ "lint": "prettier --plugin-search-dir . --check . && eslint .", "lint:fix": "eslint . --fix --ext .ts", "prettier-format": "prettier --config .prettierrc.json modules/**/*.ts --write", - "start:bun": "bun -r tsconfig-paths/register src/index.ts", - "start": "tsx -r tsconfig-paths/register src/index.ts", "format": "prettier --plugin-search-dir . --write .", + "start": "tsx -r tsconfig-paths/register src/index.ts", + "start:bun": "bun -r tsconfig-paths/register src/index.ts", + "start:clean": "rm -rf data/chain.db && tsx -r tsconfig-paths/register src/index.ts", + "start:purge": "rm -rf .demos_identity && rm -rf data/chain.db && tsx -r tsconfig-paths/register src/index.ts", "dev": "ts-node-dev --import tsx --respawn --transpile-only src/index.ts", + "upgrade_sdk": "yarn upgrade @kynesyslabs/demosdk --latest", + "upgrade_deps": "yarn upgrade-interactive --latest", + "upgrade_deps:force": "ncu -u && yarn", "test:chains": "jest --testMatch '**/tests/**/*.ts' --testPathIgnorePatterns src/* tests/utils/* tests/**/_template* --verbose" }, "devDependencies": { @@ -42,39 +47,31 @@ "typescript": "^4.9.3" }, "dependencies": { - "@cosmjs/proto-signing": "^0.32.3", - "@cosmjs/stargate": "^0.32.3", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", - "@fortawesome/free-brands-svg-icons": "^6.4.2", - "@fortawesome/free-solid-svg-icons": "^6.4.2", - "@multiversx/sdk-core": "^12.18.0", - "@multiversx/sdk-network-providers": "^2.2.1", - "@multiversx/sdk-wallet": "^4.3.0", - "@solana/web3.js": "^1.78.0", + "@fortawesome/free-brands-svg-icons": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", + "@kynesyslabs/demosdk": "^1.0.13", "@types/express": "^4.17.21", "@types/http-proxy": "^1.17.14", "@types/node-forge": "^1.3.6", - "ace-builds": "^1.31.0", + "ace-builds": "^1.33.0", "argon2": "^0.31.2", "axios": "^1.6.5", "axios-retry": "^3.5.1", "bcrypto": "^5.4.0", - "bip32": "^4.0.0", - "bitcoinjs-lib": "^6.1.3", "buffer": "^6.0.3", "concurrently": "^8.2.1", "crypto-browserify": "^3.12.0", "debounce": "^1.2.1", "dotenv": "^16.3.1", "eiows": "^6.7.2", - "ethers": "^6.11.1", "evm-chains": "^0.2.0", - "express": "^4.18.2", + "express": "^4.19.2", "file-saver": "^2.0.5", "fuse.js": "^6.6.2", "http-proxy": "^1.18.1", "https-browserify": "^1.0.0", - "javascript-time-ago": "^2.5.9", + "javascript-time-ago": "^2.5.10", "js-sha256": "^0.9.0", "lodash.clonedeep": "^4.5.0", "mceliece": "^5.0.4", @@ -83,6 +80,7 @@ "node-forge": "^1.3.1", "node-mceliece": "^1.0.0", "node-seal": "^5.1.3", + "npm-check-updates": "^16.14.18", "nprogress": "^0.2.0", "ntru": "^4.0.4", "object-sizeof": "^2.6.3", @@ -101,7 +99,6 @@ "socket.io-client": "^4.7.2", "sphincs": "^3.0.4", "sqlite3": "^5.1.6", - "stellar-sdk": "^11.1.0", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "superdilithium": "^2.0.6", @@ -112,7 +109,6 @@ "typeorm": "^0.3.17", "uuid": "^9.0.0", "ws": "^8.13.0", - "xrpl": "^2.12.0", "zlib": "^1.0.5" }, "type": "module", diff --git a/sdk/localsdk/multichain/btc.ts b/sdk/localsdk/multichain/btc.ts deleted file mode 100644 index 28ed9ecc3..000000000 --- a/sdk/localsdk/multichain/btc.ts +++ /dev/null @@ -1,72 +0,0 @@ -import BIP32Factory, { BIP32Interface } from "bip32" -import * as bip39 from "bip39" -import * as bitcoin from "bitcoinjs-lib" -import * as ecc from "tiny-secp256k1" - -import defaultChainAsync from "./types/defaultChainAsync" - -const bip32 = BIP32Factory(ecc) - -// NOTE BIP32 implementation follows: -// LINK https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/bip32.spec.ts - -export default class BTC extends defaultChainAsync { - constructor(rpc_url: string) { - super(rpc_url) - this.name = "BTC" - } - - async connect(rpc_url: string): Promise { - throw new Error("Method not implemented.") - } - async disconnect(): Promise { - throw new Error("Method not implemented.") - } - async getBalance(address: string): Promise { - const response = await fetch( - `https://blockchain.info/q/addressbalance/${address}`, - ) - const balance = await response.text() - return balance - } - - pay(receiver: string, amount: string): Promise { - throw new Error("Method not implemented.") - } - async info(account: BIP32Interface): Promise { - let address = bitcoin.payments.p2pkh({ - pubkey: account.publicKey, - }).address! - return JSON.stringify(address) - } - - createWallet(): any { - // TODO Generate mnemonic - let mnemonic - let path = "m/0'/0/0" - let seed = bip39.mnemonicToSeedSync(mnemonic) - let root = bip32.fromSeed(seed) - this.wallet = root.derivePath(path) // REVIEW is this right? - } - - // INFO Accepting base58 encoded private keys like: - // tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK - // NOTE Alternatively you can pass in a mnemonic phrase to generate the private key. - connectWallet(privateKey: string, mnemonic: boolean = false) { - if (!mnemonic) { - this.wallet = bip32.fromBase58(privateKey) - } else { - // Generating the seed and the private key from the mnemonic - let seed = bip39.mnemonicToSeedSync(privateKey) - let node = bip32.fromSeed(seed) - let strng = node.toBase58() - this.wallet = bip32.fromBase58(strng) - } - } - signTransaction(raw_transaction: any): Promise { - throw new Error("Method not implemented.") - } - sendTransaction(signed_transaction: any) { - throw new Error("Method not implemented.") - } -} diff --git a/sdk/localsdk/multichain/configs/ibcProviders.ts b/sdk/localsdk/multichain/configs/ibcProviders.ts index b55d71b3c..8d4a43ad5 100644 --- a/sdk/localsdk/multichain/configs/ibcProviders.ts +++ b/sdk/localsdk/multichain/configs/ibcProviders.ts @@ -1,6 +1,6 @@ export default { cosmos: { mainnet: "", - testnet: "https://rpc.sentry-01.theta-testnet.polypore.xyz" - } + testnet: "https://rpc.sentry-01.theta-testnet.polypore.xyz", + }, } \ No newline at end of file diff --git a/sdk/localsdk/multichain/evm.ts b/sdk/localsdk/multichain/evm.ts deleted file mode 100644 index 46fb5bdef..000000000 --- a/sdk/localsdk/multichain/evm.ts +++ /dev/null @@ -1,234 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ -import { - Contract, - JsonRpcProvider, - parseEther, - TransactionReceipt, - TransactionRequest, - Wallet, -} from "ethers" -import required from "src/utilities/required" - -import defaultChainAsync, { IEVM } from "./types/defaultChainAsync" -import { TransactionResponse } from "./types/multichain" - -export default class EVM extends defaultChainAsync implements IEVM { - // A singleton for each chain_id - private static instances: Map = new Map() - - // Chain properties - provider: JsonRpcProvider = null - wallet: Wallet = null - empty_transaction: TransactionRequest - isEIP1559: boolean = true - - // Specific EVM properties - contracts: Map // Will store all the contracts instances as address: ethers.Contract} - - /** - * The Singleton's constructor should always be private to prevent direct - * construction calls with the `new` operator. - */ - - private constructor( - chain_id: number, - rpc_url: string, - isEIP1559: boolean = true, - ) { - super(rpc_url) - this.name = "evm" - this.isEIP1559 = isEIP1559 - } - - async connect(rpc_url: string) { - console.log("Connecting EVM RPC provider: " + rpc_url) - this.provider = new JsonRpcProvider(rpc_url) - - // INFO: Fetch network data - const network = await this.provider.getNetwork() - - // INFO: Check if network data is truthy - this.connected = Boolean(network.name) - - return this.connected - } - - async disconnect() { - // TODO - } - - createWallet(): any {} - - // INFO Connect a wallet to the EVM provider using a private key - connectWallet(privateKey: string): Wallet { - this.wallet = new Wallet(privateKey, this.provider) - this.wallet.connect(this.provider) - return this.wallet - } - - // INFO Getting a balance for an address - async getBalance(address: string): Promise { - const balance = await this.provider.getBalance(address) - return balance.toString() - } - - // Redirection - async transfer(receiver: string, amount: string): Promise { - return await this.pay(receiver, amount) - } - - // INFO Simply sending an amount to an address - // NOTE Returns the transaction hash as a string - // ANCHOR MVP - async pay(address: string, amount: string) { - required(this.wallet) - let tx = { to: address, value: parseEther(amount) } - let tx_hashed = await this.sendTransaction(tx) - console.log(tx_hashed) - return tx_hashed - } - - async info(): Promise { - let info = "" - // TODO - return info - } - - async getContractInstance(address: string, abi: string): Promise { - console.log(this) - if (!this.provider) { - throw new Error("Provider not connected") - } - let contract = new Contract(address, abi, this.provider) - return contract - } - - // INFO Here we simply return the correct skeleton for a normal transaction - async createRawTransaction(): Promise { - return this.empty_transaction - } - - async signTransaction(raw_transaction: TransactionRequest) { - required(this.wallet) - return await this.wallet.signTransaction(raw_transaction) - } - - async signTransactions(raw_tx: any[], options?: {}): Promise { - throw new Error("Method not implemented.") - } - - // INFO If the wallet is connected, send a transaction - // ANCHOR MVP - async sendTransaction(transaction: TransactionRequest) { - required(this.wallet, "Wallet not connected") - const txResponse = await this.wallet.sendTransaction(transaction) // NOTE It will be signed automatically - return { - result: "success", - hash: txResponse.hash, - } - } - - async sendRawTransaction(raw_transaction: string): Promise { - throw new Error("Method not implemented.") - } - - async sendSignedTransaction( - signed_transaction: string, - ): Promise { - if (!this.provider) { - throw new Error("Provider not connected") - } - const res = await this.provider.broadcastTransaction(signed_transaction) - - return { - result: "success", - hash: res.hash, - } - } - - async waitForReceipt(tx_hash: string): Promise { - return await this.provider.getTransactionReceipt(tx_hash) - } - - // REVIEW Reader for contracts - // ANCHOR MVP - async readFromContract( - contract_instance: Contract, - function_name: string, - args: any, - ): Promise { - return await contract_instance[function_name](...args) - } - - // REVIEW Writer for contracts - async writeToContract( - contract_instance: Contract, - function_name: string, - args: any, - ): Promise { - required(this.wallet) - return await contract_instance[function_name](...args) // NOTE Ensure it is writeable i guess - } - - // SECTION Event listener - async listenForEvent( - event: string, - contract: string, - abi: any[], - ): Promise { - if (!this.provider) { - throw new Error("Provider not connected") - } - let contractInstance = new Contract(contract, abi, this.provider) - // REVIEW THis could work - return contractInstance.on(event, (data: any) => { - ////console.log(data) - // TODO Do something with the data - }) - } - - async listenForAllEvents(contract: string, abi: any[]): Promise { - if (!this.provider) { - throw new Error("Provider not connected") - } - let contractInstance = new Contract(contract, abi, this.provider) - // REVIEW 99% Won't work - return contractInstance.on("*", (data: any) => { - ////console.log(data) - // TODO Do something with the data - }) - } - // !SECTION Event Listener - - /** - * The static method that controls the access to the singleton instance. - * - * This implementation let you subclass the Singleton class while keeping - * just one instance of each subclass around. - */ - - // INFO Getting an instance (if it exists) or false so that we can call createInstance - public static getInstance(chain_id: number): EVM { - if (!EVM.instances[chain_id]) { - return null - } - return EVM.instances[chain_id] - } - - // INFO Creating an instance from a rpc url if not already created - public static createInstance(chain_id: number, rpc_url: string): EVM { - if (!EVM.instances[chain_id]) { - EVM.instances[chain_id] = new EVM(chain_id, rpc_url) - } - return EVM.instances[chain_id] - } -} diff --git a/sdk/localsdk/multichain/ibc.ts b/sdk/localsdk/multichain/ibc.ts deleted file mode 100644 index 7c5181d9f..000000000 --- a/sdk/localsdk/multichain/ibc.ts +++ /dev/null @@ -1,323 +0,0 @@ -import { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx" -import { - DirectSecp256k1HdWallet, - DirectSecp256k1Wallet, -} from "@cosmjs/proto-signing" -import { - SigningStargateClient, - StargateClient, - calculateFee, -} from "@cosmjs/stargate" - -import { IBCTransaction, IPayOptions } from "./types/transfers" -import DefaultChainAsync from "./types/defaultChainAsync" -import required from "src/utilities/required" - -interface IBCConnectWalletOptions { - /** - * The address prefix - */ - prefix: string - - /** - * The the price of a single unit of gas. This is typically a fraction of the smallest fee token unit, such as 0.012utoken - */ - gasPrice: string - - /** - * The multiplier to apply to the estimated gas price. Used to make sure transactions do not run out of gas. - * - * @default 2.0 - */ - multiplier?: number -} - -interface IBCGetBalanceOptions { - /** - * The denomination of the token - */ - denom: string -} - -interface IBCPreparePayOptions extends IBCGetBalanceOptions {} - -interface IBCSignTxOptions extends IBCConnectWalletOptions { - /** - * The private key to override the connected wallet - */ - privateKey?: string -} - -export default class IBC extends DefaultChainAsync { - address: string = "" - chainID: string - declare provider: StargateClient - declare wallet: SigningStargateClient - declare signer: DirectSecp256k1Wallet | DirectSecp256k1HdWallet - - // IBC options - gasPrice: string = "" - multiplier: number = 2.0 - - constructor(rpc_url: string) { - super(rpc_url) - this.name = "ibc" - - // INFO: We can't await here, so we call .setRPC in .create() - // if (rpc_url) { - // this.setRPC(rpc_url); - // } - } - - async setRPC(rpc_url: string) { - this.provider = await StargateClient.connect(rpc_url) - } - - static async create(rpc_url: string): Promise { - const instance = new IBC(rpc_url) - - if (rpc_url) { - // INFO: Set rpc url and ping it. - await instance.setRPC(rpc_url) - await instance.connect() - } - - return instance - } - - async connect() { - try { - const chain_id = await this.provider.getChainId() - this.chainID = chain_id - - this.connected = Boolean(chain_id) - } catch (error) { - this.connected = false - } - - return this.connected - } - - async connectWallet(privateKey: string, options: IBCConnectWalletOptions) { - required(options.prefix, "Address prefix not provided") - required(options.gasPrice, "Gas price not provided") - - // INFO: Store the gasPrice for preparePays and other methods - this.gasPrice = options.gasPrice - - // INFO: Check if privateKey is a mnemonic or a private key - const isPrivateKey = privateKey.split(" ").length === 1 - - // INFO: Create a signer using the appropriate wallet - if (isPrivateKey) { - // TODO: Test this block! - const buffer = Buffer.from(privateKey, "hex") - this.signer = await DirectSecp256k1Wallet.fromKey( - buffer, - options.prefix, - ) - } else { - this.signer = await DirectSecp256k1HdWallet.fromMnemonic( - privateKey, - { - prefix: options.prefix, - }, - ) - } - - // INFO: Store the address as .getAddress can't be async - const wallet_accounts = await this.signer.getAccounts() - const this_account = wallet_accounts.find(account => - account.address.startsWith(options.prefix), - ) - - if (this_account) { - this.address = this_account.address - } else { - throw new Error(`No account found for prefix: ${options.prefix}`) - } - - this.wallet = await SigningStargateClient.connectWithSigner( - this.rpc_url, - this.signer, - ) - return this.wallet - } - - getAddress() { - return this.address - } - - /** - * Get the balance of the address - * @param address The address - * @returns The balance of the address in the specified denomination - */ - async getBalance(address: string, options: IBCGetBalanceOptions) { - required(this.provider, "Provider not connected") - - const coins = await this.provider.getBalance(address, options.denom) - return coins.amount - } - - getEmptyTransaction(): IBCTransaction { - return { - signerAddress: this.getAddress(), - messages: [ - { - typeUrl: "/cosmos.bank.v1beta1.MsgSend", - value: { - fromAddress: this.getAddress(), - toAddress: "", - amount: [{ denom: "", amount: "" }], - }, - }, - ], - // INFO: Fees are to be estimated when filling the tx - fee: null, - memo: "", - } - } - - async pay(receiver: string, amount: string, options: IBCPreparePayOptions) { - // INFO: Call preparePays with a single payment - const tx = await this.multiPay([{ address: receiver, amount }], options) - return tx[0] - } - - /** - * Prepare multiple payments - * @param payments An array of payments - * @param options Specifies the denomination of the token - * @returns An array of signed transactions - */ - async multiPay(payments: IPayOptions[], options: IBCPreparePayOptions) { - // INFO: Create an array of transactions - const txs = payments.map(payment => { - const tx = this.getEmptyTransaction() - - // INFO: Fill the tx - tx.messages[0].value.toAddress = payment.address - tx.messages[0].value.amount[0].amount = payment.amount as string - tx.messages[0].value.amount[0].denom = options.denom - return tx - }) - - // INFO: Estimate the fee for the first tx - const fees = await this.estimateTxFee(txs[0]) - - // INFO: Since all txs here are similar, set same fee for all - txs.forEach(tx => { - tx.fee = fees - }) - - // INFO: Sign and return the txs - return await this.signTransactions(txs) - } - - async signTransaction(tx: IBCTransaction, options?: IBCSignTxOptions) { - // INFO: Call signTransactions with a single tx - const signed_txs = await this.signTransactions([tx], options) - return signed_txs[0] - } - - /** - * Estimate the fee for a transaction - * @param tx The transaction - * @returns The estimate fee for the transaction - */ - private async estimateTxFee(tx: IBCTransaction) { - const signerAddress = this.getAddress() - const gasEstimate = await this.wallet.simulate( - signerAddress, - tx.messages, - tx.memo, - ) - - const gasLimit = Math.round(gasEstimate * this.multiplier) - return calculateFee(gasLimit, this.gasPrice) - } - - async signTransactions( - transactions: IBCTransaction[], - options?: IBCSignTxOptions, - ) { - required(this.wallet, "Wallet not connected") - - if (options?.privateKey) { - const { privateKey, ...connectOptions } = options - await this.connectWallet(privateKey, connectOptions) - } - - // NOTE: Sequence management happens here - // INFO: Get account on network - const address = this.getAddress() - const account = await this.wallet.getAccount(address) - - if (!account) { - throw new Error(`Account ${address} not found`) - } - - // INFO: Store the current sequence - let current_sequence = account.sequence - - const signed_txs = transactions.map(async tx => { - const signerInfo = { - sequence: current_sequence, - accountNumber: account.accountNumber, - chainId: this.chainID, - } - - // INFO: Increment the sequence for next round - current_sequence++ - - // INFO: Sign the tx - const signed_tx = await this.wallet.sign( - tx.signerAddress, - tx.messages, - tx.fee, - tx.memo, - signerInfo, - ) - - // INFO: Convert raw tx to bytes array (Ready for broadcast) - const tx_bytes = TxRaw.encode(signed_tx).finish() - return tx_bytes - }) - - // INFO: Return the signed transactions - return await Promise.all(signed_txs) - } - - async sendTransaction(signed_tx: Uint8Array) { - required(this.wallet, "Wallet not connected") - - const hash = await this.wallet.broadcastTxSync(signed_tx) - - return { - hash, - result: "success", - } - } - - async disconnect() { - this.resetLocals() - this.address = "" - this.gasPrice = "" - return !this.connected - } - - // SECTION: Unimplemented methods - async ibcSend() { - // TODO: Implement IBC send - // REFERENCE: https://github.com/cosmos/cosmjs/blob/33271bc51c/packages/stargate/src/signingstargateclient.ts#L246 - throw new Error("Method not implemented") - } - async info(...args: any): Promise { - throw new Error("Method not implemented.") - } - - async createWallet(password?: string) { - throw new Error("Method not implemented.") - } -} diff --git a/sdk/localsdk/multichain/index.ts b/sdk/localsdk/multichain/index.ts index ef4799085..f648dc545 100644 --- a/sdk/localsdk/multichain/index.ts +++ b/sdk/localsdk/multichain/index.ts @@ -1,10 +1,2 @@ -// REVIEW Why was it here? // import XRPL from './xrpl'; -//export {default as BTC} from './btc'; -export { default as EVM } from "./evm" -export { default as SOLANA } from "./solana" -export { default as XLM } from "./xlm" -export { default as MULTIVERSX } from "./multiversx" -export { default as XRPL } from "./xrpl" export { default as DEMOS } from "./demos" -export { default as IBC } from "./ibc" export { default as multichainCapabilities } from "./types/multichainCapabilities" diff --git a/sdk/localsdk/multichain/multiversx.ts b/sdk/localsdk/multichain/multiversx.ts deleted file mode 100644 index 92f75e974..000000000 --- a/sdk/localsdk/multichain/multiversx.ts +++ /dev/null @@ -1,193 +0,0 @@ -import required from "src/utilities/required" - -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ -import { - Account, - Address, - GasEstimator, - IPlainTransactionObject, - TokenTransfer, - Transaction, - TransferTransactionsFactory, -} from "@multiversx/sdk-core" -import { ApiNetworkProvider } from "@multiversx/sdk-network-providers" -import { INetworkProvider } from "@multiversx/sdk-network-providers/out/interface" -import { Mnemonic, UserSigner, UserWallet } from "@multiversx/sdk-wallet" - -import DefaultChainAsync from "./types/defaultChainAsync" - -export default class MULTIVERSX extends DefaultChainAsync { - declare provider: INetworkProvider - declare wallet: UserSigner - - // TODO: Review the use of #private properties - chainID: string - - constructor(rpcURL: string) { - super(rpcURL) - this.name = "multiversx" - } - - async connect(rpc_url?: string) { - // NOTE We might not need to pass the rpc_url to the provider as it's already set in the constructor - - if (rpc_url) { - this.rpc_url = rpc_url - } - - this.provider = new ApiNetworkProvider(this.rpc_url, { - timeout: 10000, - }) - - const networkConfig = await this.provider.getNetworkConfig() - // NOTE: Chain ID is needed in this.pay() - this.chainID = networkConfig.ChainID - this.connected = Boolean(this.chainID) - - return this.connected - } - - static async create(rpc_url?: string) { - const instance = new MULTIVERSX(rpc_url) - - if (!rpc_url) { - return instance - } - - await instance.connect() - return instance - } - - async disconnect() { - this.resetLocals() - this.chainID = null - } - - createWallet(password: string, addressIndex: number = undefined) { - required(password, "Password is required to encrypt the key file") - - const mnemonics = Mnemonic.generate() - - const words = mnemonics.getWords() - const words_with_index = words.map((word, index) => index + ". " + word) - - const secretKey = mnemonics.deriveKey(addressIndex, password) - const wallet = UserWallet.fromSecretKey({ secretKey, password }) - - const jsonWallet = wallet.toJSON() - - // NOTE: .bech32 is the address property - const walletAddress: string = jsonWallet.bech32 - - // TODO Return downloadable mnemonics & json files - return { - mnemonics: words, - address: walletAddress, - mnemonics_txt: words_with_index.join(""), - wallet_keyfile: JSON.stringify(jsonWallet, null, 2), - } - } - - // @ts-ignore - connectWallet(privateKey: string, password: string) { - // NOTE: privateKey is the keyFile in a JSON string format - // NOTE: the password param is not yet defined in DefaultChainAsync - - required(privateKey, "KeyFile is required to connect to the wallet.") - required(password, "Password is required to decrypt the key file.") - - const keyfile = JSON.parse(privateKey) - this.wallet = UserSigner.fromWallet(keyfile, password) - - return this.wallet - } - - async getBalance(address: string): Promise { - required(address, "address is required to get the balance") - - const Iaddress = new Address(address) - const account = await this.provider.getAccount(Iaddress) - - return account.balance.toString() - } - - async pay(receiver: string, amount: string): Promise { - required(this.provider, "Provider not connected") - - const senderAdress = this.wallet.getAddress() - - // INFO: Sync sender account which is needed to get a nonce - const senderAccount = new Account(senderAdress) - const senderOnNetwork = await this.provider.getAccount(senderAdress) - senderAccount.update(senderOnNetwork) - - const receiverAdress = new Address(receiver) - - // INFO: Gas estimation - const gas = new GasEstimator() - const factory = new TransferTransactionsFactory(gas) - - const transfer = TokenTransfer.egldFromAmount(amount) - const tx = factory.createEGLDTransfer({ - sender: senderAdress, - receiver: receiverAdress, - value: transfer, - chainID: this.chainID, - }) - - tx.setNonce(senderAccount.getNonceThenIncrement()) - - // INFO: tx is the unsigned transaction - // INFO: Return it for signing and broadcast - return tx - } - - info(): Promise { - throw new Error("Method not implemented.") - } - - async signTransaction(transaction: Transaction): Promise { - required(this.wallet, "Wallet not connected") - - const serializedTx = transaction.serializeForSigning() - const signature = await this.wallet.sign(serializedTx) - transaction.applySignature(signature) - - return transaction - } - - async sendTransaction(raw_tx: Transaction | IPlainTransactionObject) { - required(this.provider, "Provider not connected") - let signed_tx: Transaction - - // INFO: raw_tx is a plain object when it comes from the frontend - if (!(raw_tx instanceof Transaction)) { - signed_tx = Transaction.fromPlainObject(raw_tx) - } else { - signed_tx = raw_tx - } - - // INFO: The provider can also send a list of transactions - const tx_hash = await this.provider.sendTransaction( - signed_tx as Transaction, - ) - - return { - result: "success", - hash: tx_hash, - } - } - - async signTransactions(raw_tx: any[], options?: {}): Promise { - throw new Error("Method not implemented.") - } -} diff --git a/sdk/localsdk/multichain/multiversx/test.ts b/sdk/localsdk/multichain/multiversx/test.ts deleted file mode 100644 index e91a6ddb8..000000000 --- a/sdk/localsdk/multichain/multiversx/test.ts +++ /dev/null @@ -1,68 +0,0 @@ -import fs from "node:fs/promises" -import path from "path" -import { fileURLToPath } from "url" - -import MULTIVERSX from "../multiversx" - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const TESTNET_URL = "https://testnet-api.multiversx.com" -const VALID_TESTNET_ADDRESS = - "erd1fsac7hpfzyhzs2ls894579kctfzp8n3hyp6gt5n0ccnd6hp9dpkqd6hg6w" - -async function readKeyfileFromPath(_path: string) { - const fullpath = path.join(__dirname, _path) - - // INFO: Read the keyfile from the path - const file = await fs.readFile(fullpath, { - encoding: "utf-8", - }) - - return file.trim() -} - -export default async function testMultiversx() { - const WALLET_PASSWORD = "password" - - const multiversx = new MULTIVERSX(TESTNET_URL) - - // INFO: Connecting to the network - console.log("starting connection") - await multiversx.connect() - - // INFO: Generating a wallet - const { wallet_keyfile, address } = multiversx.createWallet(WALLET_PASSWORD) - console.log("[MULTIVERSX] GENERATED KEY FILE") - console.log(wallet_keyfile) - - // INFO: Connecting to the wallet - const VALID_KEYFILE = await readKeyfileFromPath("keyfile.json") - const VALID_KEYFILE_PASSWORD = await readKeyfileFromPath( - "keyfile_password.txt", - ) - - multiversx.connectWallet(VALID_KEYFILE, VALID_KEYFILE_PASSWORD) - - // INFO: Getting the balance - const balance = await multiversx.getBalance(VALID_TESTNET_ADDRESS) - console.log("Balance: " + balance) - - // INFO: EGLD Transfer - const tx = await multiversx.pay(address, "1.5") - console.log(tx) - - // INFO: Signing a transaction - const signedTx = await multiversx.signTransaction(tx) - console.log(signedTx) - - // INFO: Sending a transaction - const txHash = await multiversx.sendTransaction(signedTx) - - console.log("SENT TX HASH") - console.log(txHash) - console.log("chain ID before disconnect: ", multiversx.chainID) - - await multiversx.disconnect() - console.log("chaid ID after disconnect: " + multiversx.chainID) -} diff --git a/sdk/localsdk/multichain/solana.ts b/sdk/localsdk/multichain/solana.ts deleted file mode 100644 index 4af73ff61..000000000 --- a/sdk/localsdk/multichain/solana.ts +++ /dev/null @@ -1,114 +0,0 @@ -import required from "src/utilities/required" - -import * as solanaWeb3 from "@solana/web3.js" - -import defaultChainAsync from "./types/defaultChainAsync" - -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -// LINK https://docs.solana.com/developing/clients/javascript-api - -export default class SOLANA extends defaultChainAsync { - private static instance: SOLANA - - wallet: solanaWeb3.Keypair = null - provider: solanaWeb3.Connection = null - - constructor(rpc_url: string) { - super(rpc_url) - this.name = "solana" - } - - async connect(rpc_url: string): Promise { - this.provider = new solanaWeb3.Connection(rpc_url) - // TODO Check connectivity - return true - } - - async disconnect(): Promise { - this.provider = null - // TODO If something is to do, do it here - } - - createWallet(): any {} - - // ANCHOR Public methods - connectWallet(privateKey: string) { - this.wallet = solanaWeb3.Keypair.fromSecretKey( - Buffer.from(privateKey, "hex"), - ) // REVIEW is this ok? - } - - async getBalance(address: string): Promise { - // TODO - return "" - } - - async pay(to: string, amount: string): Promise { - required(this.wallet, "Wallet not connected") - // TODO - return null - } - - async info(): Promise { - let info = "" - // TODO - return info - } - - // INFO Returning an empty raw transaction skeleton - async createRawTransaction(): Promise { - let empty_tx = new solanaWeb3.Transaction() - return empty_tx - } - - // INFO Placeholder compatibility function that is here only for the interface - async signTransaction(raw_transaction: any): Promise { - required(this.wallet, "Wallet not connected") - // LINK https://docs.shyft.to/tutorials/how-to-sign-transactions-on-solana - // NOTE Due to the above, the transaction is signed and sent at the same time. - return raw_transaction - } - - // INFO Sending a transfer transaction on Solana network - sendTransaction({ to, amount }) { - required(this.wallet, "Wallet not connected") - let tx = new solanaWeb3.Transaction() - tx.add( - solanaWeb3.SystemProgram.transfer({ - fromPubkey: this.wallet.publicKey, - toPubkey: to, - lamports: amount * solanaWeb3.LAMPORTS_PER_SOL, - }), - ) - let result = solanaWeb3.sendAndConfirmTransaction(this.provider, tx, [ - this.wallet, - ]) - return result - } - - // ANCHOR Static singleton methods - - static getInstance(): SOLANA | boolean { - if (!SOLANA.instance) { - return false - } - return SOLANA.instance - } - - static createInstance(rpc_url: string): SOLANA { - if (!SOLANA.instance) { - SOLANA.instance = new SOLANA(rpc_url) - } - return SOLANA.instance - } -} diff --git a/sdk/localsdk/multichain/types/transfers.ts b/sdk/localsdk/multichain/types/transfers.ts index dda0dceba..49ef061a5 100644 --- a/sdk/localsdk/multichain/types/transfers.ts +++ b/sdk/localsdk/multichain/types/transfers.ts @@ -1,4 +1,4 @@ -import { MsgSendEncodeObject, StdFee } from '@cosmjs/stargate'; +import { MsgSendEncodeObject, StdFee } from "@cosmjs/stargate" /** * `preparePay` parameters diff --git a/sdk/localsdk/multichain/xlm.ts b/sdk/localsdk/multichain/xlm.ts deleted file mode 100644 index 1f49a9f89..000000000 --- a/sdk/localsdk/multichain/xlm.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -// LINK https://github.com/stellar/js-stellar-sdk/tree/master/docs/reference - -import required from "src/utilities/required" -import * as StellarSdk from "stellar-sdk" - -import Server from "../../../src/libs/network/server" -import defaultChainAsync from "./types/defaultChainAsync" - -// TODO Find a way to make things in the next link much more unified -// LINK https://github.com/stellar/js-stellar-base/blob/master/docs/reference/building-transactions.md -export default class XLM extends defaultChainAsync { - constructor(rpcURL: string) { - super(rpcURL) - this.name = "xlm" - } - - public async connect(rpcURL: string): Promise { - console.log("stellar not yet implemented. check the code") - process.exit(0) - // this.provider = new StellarSdk(rpcURL) // 'https://horizon-testnet.stellar.org' // FIXME - return true - } - - public async disconnect(): Promise { - throw new Error("Method not implemented.") - } - - createWallet(): any {} - - // INFO Loading a keypair from a private key string - connectWallet(privateKey: string) { - this.wallet = StellarSdk.Keypair.fromSecret(privateKey) - } - getBalance(address: string): Promise { - throw new Error("Method not implemented.") - } - pay(receiver: string, amount: string): Promise { - throw new Error("Method not implemented.") - } - info(): Promise { - throw new Error("Method not implemented.") - } - - // REVIEW Signing a pre built transaction - async signTransaction(raw_transaction: any): Promise { - required(this.wallet, "Wallet not connected") - let signed_tx = await raw_transaction.sign(this.wallet) - return signed_tx - } - - sendTransaction(transactions: any) { - throw new Error("Method not implemented.") - } -} diff --git a/sdk/localsdk/multichain/xrpl.ts b/sdk/localsdk/multichain/xrpl.ts deleted file mode 100644 index 61905319e..000000000 --- a/sdk/localsdk/multichain/xrpl.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -import * as xrpl from "xrpl" - -// import WebSocket from "ws" // NOTE tsx compatibility -import DefaultChainAsync from "./types/defaultChainAsync" - -// LINK https://js.xrpl.org/ -// TODO https://xrpl.org/monitor-incoming-payments-with-websocket.html - -export default class XRPL extends DefaultChainAsync { - provider: xrpl.Client = null - wallet: xrpl.Wallet = null - - constructor(rpc_url: string) { - super(rpc_url) // overwrote -> (rpc_url) - this.name = "xrpl" - } - - // SECTION Initializations - - // INFO Connects to a XRP rpc server - public async connect(rpc: string) { - this.provider = new xrpl.Client(rpc, { - connectionTimeout: 10000, - }) - - // INFO Connects to the provider with error handling - let trial_index = 0 - let maxTrials = 3 - - const providerConnect = async () => { - console.log(`[XRPL] ${maxTrials - trial_index} retries left`) - - try { - await this.provider.connect() - console.log( - `[XRPL] Connected to RPC on ${trial_index + 1}th trial`, - ) - - return true - } catch (error) { - console.log("[XRPL] Error connecting to RPC") - console.log(error) - - trial_index++ - if (trial_index == maxTrials) { - // INFO: Return false if we failed to connect - console.log("[XRPL] Failed to connect to RPC") - return false - } - - // INFO: Retry for the Nth time - console.log("[XRPL] Retrying ...") - await providerConnect() - } - - return false - } - - // Listen for connection events - this.provider.on("connected", () => { - console.log("Successfully connected to XRPL.") - this.connected = true - }) - - // Handle disconnection events - this.provider.on("disconnected", async code => { - // Handle the disconnection event (e.g., attempt to reconnect) - console.log( - `Disconnected from XRPL with code: ${code}, attempting to reconnect...`, - ) - this.connected = false - - this.connected = await providerConnect() - }) - - // Handle errors - this.provider.on("error", (errorCode, errorMessage, data) => { - console.log(`XRPL Client Error: ${errorCode}, ${errorMessage}`) - // Handle the error based on errorCode and errorMessage - }) - - // Finally, connect to the provider - this.connected = await providerConnect() - return this.connected - } - - // INFO Manages a clean exit - public async disconnect(): Promise { - await this.provider.disconnect() - this.connected = false - return true - } - - // INFO Connects to a wallet on XRPL - async connectWallet(seed: string) { - this.wallet = xrpl.Wallet.fromSeed(seed) - } - - // INFO Creates a new wallet - async createWallet() { - this.wallet = xrpl.Wallet.generate() - } - - // !SECTION Initializations - - // SECTION Reads - - // INFO Generic account info - async accountInfo(address: string): Promise { - return await this.provider.request({ - command: "account_info", - account: address, - ledger_index: "validated", - }) - } - - // INFO Getting balance for an address (supports both XRP and other tokens) - async getBalance(address: string, multi: boolean = true) { - let response = null - if (multi) { - response = await this.provider.getBalances(address) - } else { - response = await this.provider.getXrpBalance(address) - } - return response - } - - // !SECTION Reads - - // SECTION Writes - - // INFO Sending XRP to an address - // ANCHOR MVP - async pay( - receiver: string, - amount: string, - send: boolean = true, - ): Promise { - // Preparing a payment tx - const prepared = await this.provider.autofill({ - TransactionType: "Payment", - Account: this.wallet.address, - Amount: xrpl.xrpToDrops(amount), - Destination: receiver, - }) - - // FIXME See below - /*const max_ledger = prepared.LastLedgerSequence - console.log("Prepared transaction instructions:", prepared) - console.log("Transaction cost:", xrpl.dropsToXrp(prepared.Fee), "XRP") - console.log("Transaction expires after ledger:", max_ledger) */ - if (send) { - console.log("Sending transaction...") - console.log(prepared) - return await this.sendTransaction(prepared) - } else { - // Just signing the tx - let signed = this.wallet.sign(prepared) - return signed - } - } - - async info(): Promise { - let info = "" - // TODO Implement - return info - } - - async signTransaction(raw_tx: any): Promise { - // Signing the tx - let signed = this.wallet.sign(raw_tx) - console.log("Hash: " + signed.hash) - console.log("Blob: " + signed.tx_blob) - return signed - } - - async signTransactions(raw_tx: any[], options?: {}): Promise { - throw new Error("Method not implemented.") - } - - // INFO Generic sign, send and await (if not specified) a tx - async sendTransaction(signed: any, wait: boolean = false) { - // Sending the tx - console.log("[xrpl] sendtransaction") - - if (wait) { - const res = await this.provider.submitAndWait(signed.tx_blob) - - // NOTE: The return type here might need to change - return { - result: "success", - hash: res.result.hash, - } - } else { - const res = await this.provider.submit(signed.tx_blob) - - return { - result: res.result.accepted ? "success" : "error", - hash: res.result.tx_json.hash, - extra: { - accepted: res.result.accepted, - result: res.result.engine_result, - result_code: res.result.engine_result_code, - result_message: res.result.engine_result_message, - }, - } - } - } - - // !SECTION Writes -} diff --git a/src/features/InstantMessagingProtocol/instantMessagingProtocol.ts b/src/features/InstantMessagingProtocol/instantMessagingProtocol.ts index 9506173bd..f5aaef229 100644 --- a/src/features/InstantMessagingProtocol/instantMessagingProtocol.ts +++ b/src/features/InstantMessagingProtocol/instantMessagingProtocol.ts @@ -1 +1,2 @@ import IMSession from "./types/IMSession" + diff --git a/src/features/experimental/fhe/FHE.ts b/src/features/fhe/FHE.ts similarity index 100% rename from src/features/experimental/fhe/FHE.ts rename to src/features/fhe/FHE.ts diff --git a/src/features/fhe/fhe_test.ts b/src/features/fhe/fhe_test.ts new file mode 100644 index 000000000..085dbbb5d --- /dev/null +++ b/src/features/fhe/fhe_test.ts @@ -0,0 +1,77 @@ +import { cipher } from "node-forge" + +import FHE from "./FHE" + +async function main() { + + // Create a new instance of FHE + // NOTE The resulting instance will be used to perform operations on the encrypted data + // Only this specific instance will be able to decrypt the data it encrypted + const fhe = await FHE.getInstance() + await fhe.config.setParameters() + await fhe.config.createKeysAndEncoders() + + console.log("[+] FHE instance created") + console.log("\n\n[ Math Operations ]") + // Create data to be encrypted + let plainData = 7 + let addStep = 5 + let multiplyStep = 3 + // Encrypt the PlainText + var cipheredData = await fhe.encryption.encryptNumber(plainData) + + console.log("\n[Addition]") + var cipheredAddStep = await fhe.encryption.encryptNumber(addStep) + // Add the CipherText to itself and store it in the destination parameter (itself) + var cipheredAdditionResult = await fhe.math.addNumbers(cipheredData, cipheredAddStep) + // Decrypt the CipherText + var decryptedAdditionResult = await fhe.encryption.decryptNumber(cipheredAdditionResult) + console.log("plainData: ", plainData, "\naddStep: ", addStep, "\ndecryptedAdditionResult: ", decryptedAdditionResult) + + var decryptedData = await fhe.encryption.decryptNumber(cipheredData) + + if (decryptedData !== decryptedAdditionResult) { + console.log("\n[ERROR] The decryptedData is not equal to decryptedAdditionResult") + process.exit(-1) + } + console.log("\n[OK] Now the cipheredData is equal to decryptedAdditionResult: ", decryptedData) + console.log("\n[Multiplication]") + var cipheredMultiplyStep = await fhe.encryption.encryptNumber(multiplyStep) + // Multiply the CipherText to itself and store it in the destination parameter (itself) + var cipheredMultiplicationResult = await fhe.math.multiplyNumbers(cipheredData, cipheredMultiplyStep) + // Decrypt the CipherText + var decryptedMultiplicationResult = await fhe.encryption.decryptNumber(cipheredMultiplicationResult) + console.log("plainData: ", plainData, "\nmultiplyStep: ", multiplyStep, "\ndecryptedMultiplyResult: ", decryptedMultiplicationResult) + + decryptedData = await fhe.encryption.decryptNumber(cipheredData) + if (decryptedData !== decryptedMultiplicationResult) { + console.log("\n[ERROR] The decryptedData is not equal to decryptedMultiplicationResult") + process.exit(-1) + } + console.log("\n[OK] Now the cipheredData is equal to decryptedMultiplicationResult: ", decryptedData) + + console.log("\n[Negate - Flipping the sign of the number]") + // Boolean operations + // Negate the CipherText and store it in the destination parameter (itself) + var cipheredNegateResult = await fhe.math.negate(cipheredData) + // Decrypt the CipherText + var decryptedNegateResult = await fhe.encryption.decryptNumber(cipheredNegateResult) + if (decryptedNegateResult !== -decryptedData) { + console.log("\n[ERROR] The decryptedNegateResult is not equal to -plainData") + process.exit(-1) + } + console.log("\ndecryptedNegateResult: ", decryptedNegateResult) + + decryptedData = await fhe.encryption.decryptNumber(cipheredData) + if (decryptedData !== decryptedNegateResult) { + console.log("\n[ERROR] The decryptedData is not equal to -decryptedNegateResult") + process.exit(-1) + } + + console.log("\n[OK] Now the cipheredData is equal to -decryptedNegateResult: ", decryptedData) + + +} + + +main() \ No newline at end of file diff --git a/src/features/multichain/XMDispatcher.ts b/src/features/multichain/XMDispatcher.ts index c6c44ef44..363aa14d5 100644 --- a/src/features/multichain/XMDispatcher.ts +++ b/src/features/multichain/XMDispatcher.ts @@ -1,7 +1,6 @@ /* eslint-disable no-unused-vars */ import { - DerivableNative, - deriveMempoolOperation, + DerivableNative, deriveMempoolOperation, } from "src/libs/utils/demostdlib/deriveMempoolOperation" // INFO Entry point for multichain requests import { json } from "stream/consumers" @@ -14,25 +13,26 @@ export default class multichainDispatcher { console.log("\n\n") console.log("[XM Script full digest]") console.log(data) + console.log("Stringed to:") console.log(JSON.stringify(data)) console.log("\n\n") console.log("[XMChain Digestion] Processing multichain operation") - console.log(data.multichain_operation) + console.log(data.operations) console.log("\n[XMChain Digestion] Having:") - console.log(Object.keys(data.multichain_operation.operations).length) + console.log(Object.keys(data.operations).length) console.log("operations") console.log("\n===== ANALYSIS ===== \n") console.log("\n===== FUNCTIONS ===== \n") for ( let i = 0; - i < Object.keys(data.multichain_operation.operations).length; + i < Object.keys(data.operations).length; i++ ) { // Named function console.log( "[XMChain Digestion] Found: " + - Object.keys(data.multichain_operation.operations)[i], + Object.keys(data.operations)[i], ) } console.log("\n===== END OF ANALYSIS ===== \n") @@ -60,53 +60,13 @@ export default class multichainDispatcher { console.log("[XM EXECUTE] Successfully executed") console.log(results) - // Inserting in mempool the results - let derivedResults = await multichainDispatcher.toMempool( - script, - results, - true, - ) console.log("[XM EXECUTE] Derived Operation completed successfully") //console.log(derivedOperation) - let overallResult = { - results: results, - derivedOperation: derivedResults[0], // TODO We will want to return the demos tx hash too prolly - } - console.log("[XM EXECUTE] Sending back the result") - console.log(overallResult) - - return overallResult - } - - static async toMempool( - script: XMScript, - results: any[], - insert: boolean = true, - ): Promise { - // We should have a valid, attested request: lets handle it - // NOTE If all the attestations are valid we can create the transaction, insert it and give back the result - // Creating a tx from the completed request if is possible - let jsonNote: DerivableNative = { - from: "0x0", // FIXME Implement this - to: "multichain", // FIXME Implement this more in details - type: "xm", - data: { - script: script, - results: results, - }, - timestamp: Date.now(), - fees: { - networkFee: 0, - rpcFee: 0, - additionalFee: 0, - }, // FIXME Implement this - } - - //console.log(jsonNote) + console.log(results) - return await deriveMempoolOperation(jsonNote, insert) + return results } -} +} \ No newline at end of file diff --git a/src/features/multichain/routines/XMParser.ts b/src/features/multichain/routines/XMParser.ts index 11b3ee33f..e0f193aad 100644 --- a/src/features/multichain/routines/XMParser.ts +++ b/src/features/multichain/routines/XMParser.ts @@ -1,6 +1,6 @@ // INFO In this module is offloaded the parsing of XM requests import * as fs from "fs" -import * as multichain from "sdk/localsdk/multichain" +import * as multichain from "@kynesyslabs/demosdk/xm-localsdk" import { chainIds } from "sdk/localsdk/multichain/configs/chainIds" import { evmProviders } from "sdk/localsdk/multichain/configs/evmProviders" @@ -39,7 +39,8 @@ export interface old_XMScript { } export interface XMScript { - multichain_operation: { [key: string]: IOperation } + operations: { [key: string]: IOperation }, + operations_order: string[] } class XMParser { @@ -79,15 +80,15 @@ class XMParser { // TODO Enforce order for ( let id = 0; - id < Object.keys(fullscript.multichain_operation.operations).length; + id < Object.keys(fullscript.operations).length; id++ ) { try { - name = Object.keys(fullscript.multichain_operation.operations)[ + name = Object.keys(fullscript.operations)[ id ] console.log("[" + name + "] ") - operation = fullscript.multichain_operation.operations[name] + operation = fullscript.operations[name] console.log("[XMParser]: full script operation") console.log(fullscript) console.log("[XMParser]: partial operation") @@ -142,7 +143,7 @@ class XMParser { const result = await handlePayOperation(operation, chainID) // INFO: Adding chain info for debugging purposes - result.chain = `${operation.chain}.${operation.subchain}` + result["chain"] = `${operation.chain}.${operation.subchain}` return result } @@ -160,7 +161,7 @@ class XMParser { // console.log(evmProviders) let providerUrl = evmProviders[operation.chain][operation.subchain] // REVIEW Error handling - let evmInstance = await multichain.EVM.createInstance( + let evmInstance = multichain.EVM.createInstance( chainID, providerUrl, ) // REVIEW We should be connected @@ -168,7 +169,7 @@ class XMParser { `[XM Method] operation.chain: ${operation.chain}, operation.subchain: ${operation.subchain}`, ) console.log(`[XM Method]: providerUrl: ${providerUrl}`) - await evmInstance.connect(providerUrl) + await evmInstance.connect() console.log("params: \n") console.log(operation.task.params) console.log("\n end params: \n") diff --git a/src/features/multichain/routines/executors/pay.ts b/src/features/multichain/routines/executors/pay.ts index 3e022db7c..7cb81b904 100644 --- a/src/features/multichain/routines/executors/pay.ts +++ b/src/features/multichain/routines/executors/pay.ts @@ -1,12 +1,11 @@ -import { multichain } from "sdk/localsdk" +import * as multichain from "@kynesyslabs/demosdk/xm-localsdk" + import { chainProviders } from "sdk/localsdk/multichain/configs/chainProviders" import { evmProviders } from "sdk/localsdk/multichain/configs/evmProviders" import { TransactionResponse } from "sdk/localsdk/multichain/types/multichain" import checkSignedPayloads from "src/utilities/checkSignedPayloads" import { IOperation } from "../XMParser" -import { IBC, MULTIVERSX } from "sdk/localsdk/multichain" -import DefaultChainAsync from "sdk/localsdk/multichain/types/defaultChainAsync" /** * Executes a XM pay operation and returns @@ -60,11 +59,11 @@ export default async function handlePayOperation( break case "egld": - result = await genericJsonRpcPay(MULTIVERSX, rpc_url, operation) + result = await genericJsonRpcPay(multichain.MULTIVERSX, rpc_url, operation) break case "ibc": - result = await genericJsonRpcPay(IBC, rpc_url, operation) + result = await genericJsonRpcPay(multichain.IBC, rpc_url, operation) break default: @@ -87,16 +86,17 @@ export default async function handlePayOperation( * @param operation The operation to be executed */ async function genericJsonRpcPay( - sdk: typeof DefaultChainAsync, + sdk: typeof multichain.IBC | typeof multichain.MULTIVERSX, rpc_url: string, operation: IOperation, ) { console.log([ `[XMScript Parser] Generic JSON RPC Pay on: ${operation.chain}.${operation.subchain}`, ]) - let instance: DefaultChainAsync + let instance: multichain.IBC | multichain.MULTIVERSX try { + // @ts-expect-error instance = await sdk.create(rpc_url) } catch (error) { return { @@ -139,13 +139,9 @@ async function handleEVMPay(chainID: number, operation: IOperation) { let evmInstance = multichain.EVM.getInstance(chainID) if (!evmInstance) { - evmInstance = multichain.EVM.createInstance( - chainID, - evmProviders[operation.chain][operation.subchain], - ) - await evmInstance.connect( - evmProviders[operation.chain][operation.subchain], - ) + const rpc_url = evmProviders[operation.chain][operation.subchain] + evmInstance = multichain.EVM.createInstance(chainID,rpc_url) + await evmInstance.connect() } return await multichain.EVM.getInstance(chainID).sendSignedTransaction( @@ -170,7 +166,7 @@ async function handleXRPLPay( "[XMScript Parser] Ripple Pay: trying to send the payload as a signed transaction...", ) // REVIEW Simulations? let xrplInstance = new multichain.XRPL(rpc_url) - const connected = await xrplInstance.connect(rpc_url) + const connected = await xrplInstance.connect() console.log("CONNECT RETURNED: ", connected) if (!connected) { diff --git a/src/features/experimental/pgp/pgp.ts b/src/features/pgp/pgp.ts similarity index 100% rename from src/features/experimental/pgp/pgp.ts rename to src/features/pgp/pgp.ts diff --git a/src/features/web2/Web2Dispatcher.ts b/src/features/web2/Web2Dispatcher.ts index f6846b454..05c47b84c 100644 --- a/src/features/web2/Web2Dispatcher.ts +++ b/src/features/web2/Web2Dispatcher.ts @@ -1,18 +1,17 @@ // INFO Entry file for handling web2 requests import Web2API, { Web2APIClass } from "src/features/web2/routines/Web2Parser" -import { IWeb2Payload, IWeb2Request } from "src/features/web2/types/Web2Types" -import { Operation } from "src/libs/blockchain/routines/executeOperations" import Cryptography from "src/libs/crypto/cryptography" import Hashing from "src/libs/crypto/hashing" import { - DerivableNative, - deriveMempoolOperation, + DerivableNative, deriveMempoolOperation, } from "src/libs/utils/demostdlib/deriveMempoolOperation" import required from "src/utilities/required" import sharedState from "src/utilities/sharedState" // NOTE Terminal kit for useful logging import terminalkit from "terminal-kit" +import { IWeb2Payload, IWeb2Request, Operation } from "@kynesyslabs/demosdk/types" + const term = terminalkit.terminal // INFO Upon receiving a request from a socket, we @@ -21,9 +20,9 @@ const term = terminalkit.terminal // send back to the client or to the origin rpc the // transaction that will b e granted as web2 result export default async function handleWeb2( - payload: IWeb2Payload, + payload: IWeb2Request, senderSocket: any, -): Promise<[boolean, string]> { +): Promise<[boolean, string | IWeb2Request]> { // Creating the workable interface // TODO Remember that web2 could need to be signed and could need a fee // NOTE From now on, Web2API will reply to instanceName with the same instance @@ -34,7 +33,7 @@ export default async function handleWeb2( //console.log(payload) //process.exit(0) - let request: IWeb2Request = payload.message + let request: IWeb2Request = payload console.log( "[REQUEST FOR WEB2] [+] Found and loaded payload.message as expected...", ) @@ -60,6 +59,7 @@ export default async function handleWeb2( let web2interface = Web2API(null, nameHash, senderSocket, payload) // NOTE We want to wait for the request to be digested before proceeding (see above paragraph) await web2interface.digestedPromise + console.log(web2interface.request.result) // Now result is in web2request.request.result console.log( "[web2Dispatcher] Request digested and promise solved. Registering the instance...", @@ -135,8 +135,6 @@ export default async function handleWeb2( console.log( "[web2Dispatcher] Attestations validated. Deriving a transaction + operation...", ) - let derivedResult = await toMempool(instanceName) - console.log("[web2Dispatcher] Transaction + operation derived.") Web2API("remove", nameHash, senderSocket) @@ -147,31 +145,5 @@ export default async function handleWeb2( // console.log(JSON.stringify(web2interface.request)) // TODO Maybe we should also return derivedResult somehow - return [true, JSON.stringify(web2interface.request)] // , derivedResult -} - -// INFO Derive a valid DEMOS tx and GLS operation from a compatible request -async function toMempool( - instanceName: string, - insert: boolean = true, -): Promise<[string, Operation]> { - // We should have a valid, attested request: lets handle it - let derivedResults: [string, Operation] - let web2Instance: Web2APIClass = Web2API(null, instanceName) - let derivable: DerivableNative = { - from: "web2module", // FIXME Implement this - to: "web2", // FIXME Implement this more in details - type: "web2", - data: web2Instance.request, - timestamp: Date.now(), - fees: { - networkFee: 0, - rpcFee: 0, - additionalFee: 0, - }, // FIXME Implement this - } - // NOTE If all the attestations are valid we can create the transaction, insert it and give back the result - // Deriving an operation and a tx from the web2 request - derivedResults = await deriveMempoolOperation(derivable, insert) - return derivedResults -} + return [true, web2interface.request] // , derivedResult +} \ No newline at end of file diff --git a/src/features/web2/routines/Web2Parser.ts b/src/features/web2/routines/Web2Parser.ts index 89a739626..9614b763b 100644 --- a/src/features/web2/routines/Web2Parser.ts +++ b/src/features/web2/routines/Web2Parser.ts @@ -10,13 +10,9 @@ import sharedState from "src/utilities/sharedState" import terminalkit from "terminal-kit" import { - IParam, - IRawWeb2Request, - IWeb2Attestation, - IWeb2Payload, - IWeb2Request, - IWeb2Result, -} from "../types/Web2Types" + IParam, IRawWeb2Request, IWeb2Attestation, IWeb2Payload, IWeb2Request, IWeb2Result, +} from "@kynesyslabs/demosdk/types" + import post from "./operations/Web2Post" import retrieve from "./operations/Web2Retrieve" @@ -33,7 +29,7 @@ export default function Web2API( command: string = null, named: string = null, sendSock: any = null, - req: IWeb2Payload = null, + req: IWeb2Request = null, ): Web2APIClass { if (command === "remove" && typeof sendSock === "string") { const instanceNameToRemove = sendSock @@ -58,7 +54,7 @@ export class Web2APIClass { static getInstance( named: string = null, sendSock: any = null, - req: IWeb2Payload = null, + req: IWeb2Request = null, ): Web2APIClass { if (!named) { named = String(Web2APIClass.progressive) @@ -84,7 +80,6 @@ export class Web2APIClass { } // NOTE Storing the request here - payload: IWeb2Payload = null request: IWeb2Request = null // NOTE Storing the sender's socket here senderSocket: null @@ -96,18 +91,18 @@ export class Web2APIClass { // SECTION Control methods // INFO Creating a named instance and bootstrapping it - constructor(name: string, sendSock: any, payload: IWeb2Payload = null) { + constructor(name: string, sendSock: any, payload: IWeb2Request = null) { this.name = name this.senderSocket = sendSock - if (!payload.message) { - term.yellow.bold("[Web2API] No request attached. Is this right?") + console.log(payload) + if (!payload.raw) { + term.yellow.bold("[Web2API] No raw request attached. Is this right?") //console.log(payload) // TODO Specify this as a parameter that users can set this.request.raw.minAttestations = 10 this.request.raw.stage.hop_number = 0 } else { - this.payload = payload - this.request = payload.message + this.request = payload } // REVIEW Should be ok anyway // NOTE Not awaiting cause we need to let devs decide when to await with awaitQuorum diff --git a/src/features/web2/routines/operations/Web2Post.ts b/src/features/web2/routines/operations/Web2Post.ts index 12f0cebab..ac82abe7f 100644 --- a/src/features/web2/routines/operations/Web2Post.ts +++ b/src/features/web2/routines/operations/Web2Post.ts @@ -2,7 +2,7 @@ import axios from "axios" import terminalkit from "terminal-kit" // INFO This module is used to retrieve a resource from a raw request -import { IParam, IRawWeb2Request, IWeb2Result } from "../../types/Web2Types" +import { IParam, IRawWeb2Request, IWeb2Result } from "@kynesyslabs/demosdk/types" const term = terminalkit.terminal diff --git a/src/features/web2/routines/operations/Web2Retrieve.ts b/src/features/web2/routines/operations/Web2Retrieve.ts index 7a41b9d0f..7269ad5d9 100644 --- a/src/features/web2/routines/operations/Web2Retrieve.ts +++ b/src/features/web2/routines/operations/Web2Retrieve.ts @@ -2,7 +2,7 @@ import axios from "axios" import terminalkit from "terminal-kit" // INFO This module is used to retrieve a resource from a raw request -import { IParam, IRawWeb2Request, IWeb2Result } from "../../types/Web2Types" +import { IParam, IRawWeb2Request, IWeb2Result } from "@kynesyslabs/demosdk/types" const term = terminalkit.terminal diff --git a/src/features/web2/routines/types/proxyManager.ts b/src/features/web2/routines/types/proxyManager.ts index 0d234e5fa..c06a73420 100644 --- a/src/features/web2/routines/types/proxyManager.ts +++ b/src/features/web2/routines/types/proxyManager.ts @@ -1,12 +1,12 @@ // LINK https://stackoverflow.com/questions/46412934/forward-https-traffic-thru-nginx-without-ssl-certificate // LINK https://github.com/http-party/node-http-proxy +import fs from "fs" // LINK https://github.com/http-party/node-http-proxy?tab=readme-ov-file#https---https import httpProxy from "http-proxy" -import fs from "fs" import Cryptography from "src/libs/crypto/cryptography" import Hashing from "src/libs/crypto/hashing" -import sharedState from "src/utilities/sharedState" import required from "src/utilities/required" +import sharedState from "src/utilities/sharedState" export interface IHTTPSCerts { key: string diff --git a/src/features/web2/types/Web2Types.ts b/src/features/web2/types/Web2Types.ts index d3ab681a5..99aa0dcc1 100644 --- a/src/features/web2/types/Web2Types.ts +++ b/src/features/web2/types/Web2Types.ts @@ -1,68 +1,68 @@ -import forge from "node-forge" +// import forge from "node-forge" -// NOTE -// In case of POST requests, the data is the only parameter thus 'name' is ignored -// In case of GET requests, we can have multiple parameters, thus 'name' is used to identify them -export interface IParam { - name: string // Ignored in POST requests - value: any -} +// // NOTE +// // In case of POST requests, the data is the only parameter thus 'name' is ignored +// // In case of GET requests, we can have multiple parameters, thus 'name' is used to identify them +// export interface IParam { +// name: string // Ignored in POST requests +// value: any +// } -// INFO Properties of a typical request as the client would send it -// NOTE This should be the thing we receive from the handler as a request -// NOTE Basically is the comlink message -export interface IWeb2Payload { - type: "web2Request" - message: IWeb2Request - sender: any - receiver: any - timestamp: any - data: any - extra: any -} +// // INFO Properties of a typical request as the client would send it +// // NOTE This should be the thing we receive from the handler as a request +// // NOTE Basically is the comlink message +// export interface IWeb2Payload { +// type: "web2Request" +// message: IWeb2Request +// sender: any +// receiver: any +// timestamp: any +// data: any +// extra: any +// } -// INFO A web2 result interface -export interface IWeb2Result { - status: number - statusText: string - data: any -} +// // INFO A web2 result interface +// export interface IWeb2Result { +// status: number +// statusText: string +// data: any +// } -// INFO A complete web2 request -export interface IWeb2Request { - raw: IRawWeb2Request - result: any - attestations: {} - hash: string - signature?: forge.pki.ed25519.BinaryBuffer -} +// // INFO A complete web2 request +// export interface IWeb2Request { +// raw: IRawWeb2Request +// result: any +// attestations: {} +// hash: string +// signature?: forge.pki.ed25519.BinaryBuffer +// } -// INFO A request without any attestations or identity data -export interface IRawWeb2Request { - action: string - parameters: IParam[] - requestedParameters: [] | null - method: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" - url: string - headers: any - minAttestations: number - // Handling the various stages of an IWeb2Request - stage: { - // The one that will handle the response too - origin: { - identity: forge.pki.ed25519.BinaryBuffer - connection_url: string - } - // Starting from 0, each attestation it is increased - hop_number: number - } -} +// // INFO A request without any attestations or identity data +// export interface IRawWeb2Request { +// action: string +// parameters: IParam[] +// requestedParameters: [] | null +// method: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" +// url: string +// headers: any +// minAttestations: number +// // Handling the various stages of an IWeb2Request +// stage: { +// // The one that will handle the response too +// origin: { +// identity: forge.pki.ed25519.BinaryBuffer +// connection_url: string +// } +// // Starting from 0, each attestation it is increased +// hop_number: number +// } +// } -// ANCHOR Useful interfaces -export interface IWeb2Attestation { - hash: string - timestamp: number - identity: forge.pki.PublicKey - signature: forge.pki.ed25519.BinaryBuffer - valid: boolean -} +// // ANCHOR Useful interfaces +// export interface IWeb2Attestation { +// hash: string +// timestamp: number +// identity: forge.pki.PublicKey +// signature: forge.pki.ed25519.BinaryBuffer +// valid: boolean +// } diff --git a/src/features/experimental/zk/iZKP/test.ts b/src/features/zk/iZKP/test.ts similarity index 100% rename from src/features/experimental/zk/iZKP/test.ts rename to src/features/zk/iZKP/test.ts diff --git a/src/features/experimental/zk/iZKP/zk.ts b/src/features/zk/iZKP/zk.ts similarity index 100% rename from src/features/experimental/zk/iZKP/zk.ts rename to src/features/zk/iZKP/zk.ts diff --git a/src/features/experimental/zk/iZKP/zkPrimer.ts b/src/features/zk/iZKP/zkPrimer.ts similarity index 100% rename from src/features/experimental/zk/iZKP/zkPrimer.ts rename to src/features/zk/iZKP/zkPrimer.ts diff --git a/src/index.ts b/src/index.ts index 1a3773913..3d05cb4be 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,7 +12,6 @@ KyneSys Labs: https://www.kynesys.xyz/ import "reflect-metadata" -import * as bitcoin from "bitcoinjs-lib" import * as dotenv from "dotenv" import express from "express" //import process from "node:process" diff --git a/src/libs/blockchain/block.ts b/src/libs/blockchain/block.ts index 478c2da08..42e59f7e4 100644 --- a/src/libs/blockchain/block.ts +++ b/src/libs/blockchain/block.ts @@ -11,7 +11,7 @@ KyneSys Labs: https://www.kynesys.xyz/ import { pki } from "node-forge" -import BlockContent from "./types/blocks" +import { BlockContent } from "@kynesyslabs/demosdk/types" // NOTE Block class export default class Block { diff --git a/src/libs/blockchain/chain.ts b/src/libs/blockchain/chain.ts index 70d3ce546..ebe4a3f5c 100644 --- a/src/libs/blockchain/chain.ts +++ b/src/libs/blockchain/chain.ts @@ -18,16 +18,15 @@ import { StatusProperties } from "src/model/entities/StatusProperties" import { Transactions } from "src/model/entities/Transactions" import { MoreThan } from "typeorm" +import { + AddressInfo, Operation, StatusNative as StatusNativeType, + StatusProperties as StatusPropertiesType, TransactionContent, +} from "@kynesyslabs/demosdk/types" + import Hashing from "../crypto/hashing" import Block from "./block" -import { Operation } from "./gls/gls" import manageNative from "./routines/gls_routines/manageNative" import Transaction from "./transaction" -import AddressInfo from "./types/addressInfo" -import RawTransaction from "./types/rawTransaction" -import StatusNativeType from "./types/statusNative" -import StatusPropertiesType from "./types/statusProperties" -import { TransactionContent } from "./types/transactions" export default class Chain { private static instance: Chain diff --git a/src/libs/blockchain/gls/gls.ts b/src/libs/blockchain/gls/gls.ts index a02b0fd98..ebdc6dc39 100644 --- a/src/libs/blockchain/gls/gls.ts +++ b/src/libs/blockchain/gls/gls.ts @@ -52,35 +52,13 @@ import { Validators } from "src/model/entities/Validators" import terminalkit from "terminal-kit" import { LessThanOrEqual } from "typeorm" +import { Operation, OperationRegistrySlot, OperationResult } from "@kynesyslabs/demosdk/types" + import Chain from "../chain" import executeOperations, { Actor } from "../routines/executeOperations" -import { TxFee } from "../types/transactions" const term = terminalkit.terminal -export interface OperationResult { - success: boolean - message: string -} - -export interface Operation { - operator: string - actor: string - params: {} // Documented in the chain itself - hash: string - nonce: number - timestamp: number - status: boolean | "pending" - fees: TxFee -} - -// WIP Making 'operations' registry more stable through db writing or file writing -interface OperationRegistrySlot { - operation: Operation - status: boolean | "pending" - result: OperationResult - timestamp: number -} export class OperationsRegistry { path: string = "data/operations.json" diff --git a/src/libs/blockchain/gls/types/StateChange.ts b/src/libs/blockchain/gls/types/StateChange.ts deleted file mode 100644 index 677ba26bb..000000000 --- a/src/libs/blockchain/gls/types/StateChange.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -import forge from "node-forge" - -interface TokenTransfer { - address: string - amount: number -} - -interface NFTTransfer { - address: string - tokenId: string - amount: number -} - -export default interface StateChange { - // Structure for state change - sender: forge.pki.ed25519.BinaryBuffer - receiver: forge.pki.ed25519.BinaryBuffer - nativeAmount: number - tx_hash: string - token: TokenTransfer - nft: NFTTransfer -} diff --git a/src/libs/blockchain/mempool.ts b/src/libs/blockchain/mempool.ts index 398d08228..0b8eaf38f 100644 --- a/src/libs/blockchain/mempool.ts +++ b/src/libs/blockchain/mempool.ts @@ -21,6 +21,7 @@ import PeerManager from "../peer/PeerManager" import Block from "./block" // INFO Singleton Mempool class import Transaction from "./transaction" +import { ISignature } from "@kynesyslabs/demosdk/types" export interface MempoolData { number: number @@ -91,7 +92,7 @@ export default class Mempool { } // INFO Writing a transaction to the mempool - /* NOTE + /* NOTE Here we should already have cryptographically valid data: adding the transaction to the mempool is the way to flag it for verification and execution at consensus. */ @@ -99,7 +100,15 @@ export default class Mempool { transaction: Transaction, ): Promise { let mempool = await this.getMempool() - console.log("adding transaction, found this mempool:") + console.log("adding transaction with hash " + transaction.hash + " to the mempool") + + // FIXME Debug to remove + let is_coherent = Transaction.isCoherent(transaction) + if (!is_coherent) { + console.error("Transaction in mempool is not coherent") + process.exit(1) + } + //console.log(mempool) mempool.transactions.push(transaction) // REVIEW What if it is empty? @@ -211,7 +220,11 @@ export default class Mempool { "[MEMPOOL VERIFICATION] Verifying the hash of the transaction: " + tx_hash, ) + console.log(JSON.stringify(tx.content)) let calculated_hash = Hashing.sha256(JSON.stringify(tx.content)) + console.log( + "[MEMPOOL VERIFICATION] Calculated hash: " + calculated_hash, + ) if (calculated_hash != tx_hash) { console.log( "[X] [MEMPOOL VERIFICATION] The hash of the transaction is invalid", @@ -223,11 +236,20 @@ export default class Mempool { ) // NOTE Verifying the signature against the verified hash using from as public key console.log("[MEMPOOL VERIFICATION] Verifying the signature") - let { signature } = tx - console.log( - "[MEMPOOL VERIFICATION] Signature: " + - signature.data.toString("hex"), - ) + + let signature = tx.signature // TODO Sometimes there is a nested type / data structure (see below) + // REVIEW Ugly patch for the above TODO + try { + let signature_data = signature.data as unknown as ISignature + if (!signature_data.data || !signature_data.type) { + throw new Error("[*] Signature fix failed successfully!") + } + console.log("[+] Signature fixed successfully!") + signature = signature_data + } catch (error) { + console.log("[+] [MEMPOOL VERIFICATION] Signature did not need to be fixed") + } + console.log( "[MEMPOOL VERIFICATION] Signature: " + signature.data.toString("hex"), diff --git a/src/libs/blockchain/routines/calculateCurrentGas.ts b/src/libs/blockchain/routines/calculateCurrentGas.ts index 1bb8290b2..a5a42b140 100644 --- a/src/libs/blockchain/routines/calculateCurrentGas.ts +++ b/src/libs/blockchain/routines/calculateCurrentGas.ts @@ -8,12 +8,46 @@ import Transaction from "../transaction" // INFO Calculating transaction fees based on the size of the transaction and the status of the chain async function calculateComposedGas(): Promise { let lastblock_basegas: number = await GLS.getGLSLastBlockBaseGas() - // TODO Add something to check congestion - let composed_gas = lastblock_basegas + sharedState.getInstance().rpcFee + // Congestion check + let factor = await adaptGasToCongestion() + let adapted_gas = lastblock_basegas * factor + // Adding the fee for the rpc + // TODO Set limits for the fee + let composed_gas = adapted_gas + sharedState.getInstance().rpcFee // TODO Add dApp fees return composed_gas } +// REVIEW This function is used to adapt the gas to congestion. It increases the gas if needed +async function adaptGasToCongestion(): Promise { + // TODO Get last block and previous last block timestamps + let lastBlockNumber = await Chain.getLastBlockNumber() + // Support for genesis block + if (lastBlockNumber == 0) { + return 0 + } + let previousLastBlockNumber = lastBlockNumber - 1 + // Getting blocks + let lastBlock = await Chain.getBlockByNumber(lastBlockNumber) + let previousLastBlock = await Chain.getBlockByNumber( + previousLastBlockNumber, + ) + // Getting timestamps + let lastBlockTimestamp = lastBlock.content.timestamp + let previousLastBlockTimestamp = previousLastBlock.content.timestamp + // Calculating the difference between the two timestamps + let difference = lastBlockTimestamp - previousLastBlockTimestamp + // Get the block time from the chain status (in seconds, so we multiply by 1000) + let block_time = sharedState.getInstance().block_time * 1000 + // Calculating the factor + let factor: number = 1 + if (difference > block_time) { + let drift = difference - block_time + factor = 1 + (1.5 * drift / block_time) // REVIEW Is this correct? + } + return factor +} + // REVIEW Why is this just a nested call export default async function calculateCurrentGas(payload: any): Promise { let payload_size = sizeOf(payload) diff --git a/src/libs/blockchain/routines/executeTransaction.ts b/src/libs/blockchain/routines/executeNativeTransaction.ts similarity index 95% rename from src/libs/blockchain/routines/executeTransaction.ts rename to src/libs/blockchain/routines/executeNativeTransaction.ts index d8fb7bc68..ee1131474 100644 --- a/src/libs/blockchain/routines/executeTransaction.ts +++ b/src/libs/blockchain/routines/executeNativeTransaction.ts @@ -14,8 +14,9 @@ KyneSys Labs: https://www.kynesys.xyz/ consensus has confirmed the transaction in the block. */ -import GLS, { Operation } from "../gls/gls" +import GLS from "../gls/gls" import Transaction from "../transaction" +import { Operation } from "./executeOperations" /* NOTE @@ -29,7 +30,7 @@ Each block, the nodes execute the Operation objects ordering them by their times */ // INFO Given a transaction, use GLS to see if it is executable and return a result -export default async function executeTransaction( +export default async function executeNativeTransaction( transaction: Transaction, ): Promise<[boolean, string, Operation[]?]> { let success: boolean = true diff --git a/src/libs/blockchain/routines/executeOperations.ts b/src/libs/blockchain/routines/executeOperations.ts index b2267918d..af12800f3 100644 --- a/src/libs/blockchain/routines/executeOperations.ts +++ b/src/libs/blockchain/routines/executeOperations.ts @@ -16,26 +16,27 @@ KyneSys Labs: https://www.kynesys.xyz/ executeSequence is called for each address to execute the operations contained. */ +import { Operation, OperationResult } from "@kynesyslabs/demosdk/types" + import Block from "../block" -import { TxFee } from "../types/transactions" import subOperations from "./subOperations" -export interface OperationResult { - success: boolean - message: string -} - -export interface Operation { - // TODO Add parameters as a property - operator: string - actor: string - params: any - hash: string - nonce: number - timestamp: number - status: boolean | "pending" - fees: TxFee -} +// export interface OperationResult { +// success: boolean +// message: string +// } + +// export interface Operation { +// // TODO Add parameters as a property +// operator: string +// actor: string +// params: any +// hash: string +// nonce: number +// timestamp: number +// status: boolean | "pending" +// fees: TxFee +// } // NOTE The Actor object is designed to represent a single operator and the status of its operations export interface Actor { diff --git a/src/libs/blockchain/routines/gls_routines/assignWeb2.ts b/src/libs/blockchain/routines/gls_routines/assignWeb2.ts index e151c89d8..6626246ce 100644 --- a/src/libs/blockchain/routines/gls_routines/assignWeb2.ts +++ b/src/libs/blockchain/routines/gls_routines/assignWeb2.ts @@ -1,9 +1,11 @@ +import { Operation, OperationResult } from "@kynesyslabs/demosdk/types" + import GLS from "../../gls/gls" -import { Operation, OperationResult } from "../executeOperations" export async function assignWeb2( operation: Operation, ): Promise { + // @ts-expect-error let { address, web2_hash } = operation.params return await GLS.addToGLSWeb2(address, web2_hash) } diff --git a/src/libs/blockchain/routines/gls_routines/assignXM.ts b/src/libs/blockchain/routines/gls_routines/assignXM.ts index 395932f9a..6a1b608dd 100644 --- a/src/libs/blockchain/routines/gls_routines/assignXM.ts +++ b/src/libs/blockchain/routines/gls_routines/assignXM.ts @@ -1,7 +1,9 @@ +import { Operation, OperationResult } from "@kynesyslabs/demosdk/types" + import GLS from "../../gls/gls" -import { Operation, OperationResult } from "../executeOperations" export async function assignXM(operation: Operation): Promise { + // @ts-expect-error let { address, xm_hash } = operation.params return await GLS.addToGLSXM(address, xm_hash) } diff --git a/src/libs/blockchain/routines/gls_routines/manageNative.ts b/src/libs/blockchain/routines/gls_routines/manageNative.ts index aee84d99c..61c7a53fa 100644 --- a/src/libs/blockchain/routines/gls_routines/manageNative.ts +++ b/src/libs/blockchain/routines/gls_routines/manageNative.ts @@ -1,11 +1,11 @@ import Datasource from "src/model/datasource" import { StatusNative } from "src/model/entities/StatusNative" -import Block from "../../block" -import Chain from "../../chain" -import GLS from "../../gls/gls" -import Genesis from "../../types/genesisTypes" -import { Operation, OperationResult } from "../executeOperations" +// import Block from "../../block" +// import Chain from "../../chain" +// import GLS from "../../gls/gls" +// import Genesis from "../../types/genesisTypes" +// import { OperationResult } from "../executeOperations" // TODO Implement other properties of the GLS object to be fetched and set from the database diff --git a/src/libs/blockchain/routines/subOperations.ts b/src/libs/blockchain/routines/subOperations.ts index d5aab9407..7c4622d7f 100644 --- a/src/libs/blockchain/routines/subOperations.ts +++ b/src/libs/blockchain/routines/subOperations.ts @@ -1,11 +1,12 @@ import Datasource from "src/model/datasource" import { Transactions } from "src/model/entities/Transactions" +import { Operation, OperationResult } from "@kynesyslabs/demosdk/types" + import Block from "../block" import Chain from "../chain" import GLS from "../gls/gls" import Genesis from "../types/genesisTypes" -import { Operation, OperationResult } from "./executeOperations" // NOTE Due to the modularity of the code, many routines will be stored in their own modules // TODO Move everything there if possible import glsRoutines from "./gls_routines" diff --git a/src/libs/blockchain/routines/validateTransaction.ts b/src/libs/blockchain/routines/validateTransaction.ts index 843f9bd7b..d83dd937b 100644 --- a/src/libs/blockchain/routines/validateTransaction.ts +++ b/src/libs/blockchain/routines/validateTransaction.ts @@ -9,100 +9,177 @@ KyneSys Labs: https://www.kynesys.xyz/ */ -import { cryptography } from "src/libs/crypto" +import { pki } from "node-forge" +import Chain from "src/libs/blockchain/chain" +import GLS from "src/libs/blockchain/gls/gls" +import calculateCurrentGas from "src/libs/blockchain/routines/calculateCurrentGas" +import executeNativeTransaction from "src/libs/blockchain/routines/executeNativeTransaction" +import Transaction from "src/libs/blockchain/transaction" +import Cryptography from "src/libs/crypto/cryptography" +import Hashing from "src/libs/crypto/hashing" +import sharedState from "src/utilities/sharedState" import terminalkit from "terminal-kit" -import GLS, { Operation } from "../gls/gls" -import Transaction from "../transaction" -import calculateCurrentGas from "./calculateCurrentGas" -import executeTransaction from "./executeTransaction" +import { Operation, ValidityData } from "@kynesyslabs/demosdk/types" const term = terminalkit.terminal -// INFO Cryptographically validate a transaction, calculate gas and see if the execution is valid +// INFO Cryptographically validate a transaction and calculate gas // REVIEW is it overkill to write an interface for the return value? -export default async function validateTransaction( - type: string, - request: any, // Must contain a tx property being a Transaction object -): Promise<[boolean, any]> { - term.yellow("Validating transaction...\n") - +export async function confirmTransaction( + tx: Transaction, // Must contain a tx property being a Transaction object +): Promise { + term.yellow("[Native Tx Validation] Validating transaction...\n") + // Getting the current block number + let reference_block = await Chain.getLastBlockNumber() // Loading identity - const id_ed25519 = await cryptography.load("./.demos_identity") - let publicKey = Buffer.from(id_ed25519.publicKey.toString("hex")) - let privateKey = Buffer.from(id_ed25519.privateKey.toString("hex")) - // Ingesting a transaction so that we have all the methods we need - let tx = new Transaction() - tx.content = request.tx.content - tx.signature = request.tx.signature - // As usual converting buffers to nodejs buffers - if ( - typeof tx.signature === "object" && - request.tx.signature.type === "Buffer" - ) { - tx.signature = Buffer.from(request.tx.signature) as any - console.log("Normalized signature") - } + const id_ed25519 = await Cryptography.load("./.demos_identity") + let publicKey = id_ed25519.publicKey + let privateKey = id_ed25519.privateKey + // REVIEW This should work just fine console.log("Signature: ") console.log(tx.signature) - tx.hash = request.tx.hash - tx.content.transaction_fee = request.tx.content.transaction_fee - console.log("[TX RECEIVED] Examining:\n") + console.log("[Tx Validation] Examining it\n") console.log(tx) - // NOTE Charge the gas for the transaction - let from = tx.content.from.toString("hex") + let validityData: ValidityData = { + data: { + valid: false, + reference_block: reference_block, + message: "", + gas_operation: null, + transaction: tx, + }, + signature: null, + rpc_public_key: publicKey as pki.ed25519.BinaryBuffer, + } + + /* NOTE Charge the gas for the transaction + This includes a check to see if the transaction gas can be paid + by the sender prior to the transaction execution part. + This way, we can avoid committing computations that will be reverted. + */ + let from: string + try { + from = tx.content.from.toString("hex") + console.log( + "[Native Tx Validation] Calculating gas for: " + from + "\n", + ) + } catch (e) { + term.red.bold( + "[Native Tx Validation] [FROM ERROR] No 'from' field found in the transaction\n", + ) + validityData.data.message = + "[Native Tx Validation] [FROM ERROR] No 'from' field found in the transaction\n" + // Hash the validation data + let hash = Hashing.sha256(JSON.stringify(validityData.data)) + // Sign the hash + validityData.signature = Cryptography.sign(hash, privateKey) + return validityData + } let fromBalance = 0 try { fromBalance = await GLS.getGLSNativeBalance(from) } catch (e) { term.red.bold( - "[NATIVE TX] [BALANCE ERROR] No balance found for this address: " + + "[Native Tx Validation] [BALANCE ERROR] No balance found for this address: " + from + "\n", ) - return [ - false, - "[NATIVE TX] [BALANCE ERROR] No balance found for this address: " + - from + - "\n", - ] + validityData.data.message = + "[Native Tx Validation] [BALANCE ERROR] No balance found for this address: " + + from + + "\n" + // Hash the validation data + let hash = Hashing.sha256(JSON.stringify(validityData.data)) + // Sign the hash + validityData.signature = Cryptography.sign(hash, privateKey) + return validityData } // TODO Work on this method - let gasAmount = await calculateCurrentGas(tx) - if (fromBalance < gasAmount) { - return [ - false, - "[NATIVE TX] [BALANCE ERROR] Insufficient balance for gas; required: " + - gasAmount + + let compositeFeeAmount = await calculateCurrentGas(tx) + // FIXME Overriding for testing + if (fromBalance < compositeFeeAmount && sharedState.getInstance().PROD) { + term.red.bold( + "[Native Tx Validation] [BALANCE ERROR] Insufficient balance for gas; required: " + + compositeFeeAmount + "; available: " + fromBalance + "\n" + "\n", - ] + ) + validityData.data.message = + "[Native Tx Validation] [BALANCE ERROR] Insufficient balance for gas; required: " + + compositeFeeAmount + "; available: " + fromBalance + "\n" + + "\n" + // Hash the validation data + let hash = Hashing.sha256(JSON.stringify(validityData.data)) + // Sign the hash + validityData.signature = Cryptography.sign(hash, privateKey) + return validityData } + + // TODO Move gas operation creator to a separate module // NOTE Deducting the gas from the account and assigning the operation to be executed // as child of this transaction let gas_operation: Operation = { operator: "pay_gas", actor: from, - params: { amount: gasAmount.toString() }, + params: { amount: compositeFeeAmount.toString() }, hash: tx.hash, nonce: tx.content.nonce, timestamp: tx.content.timestamp, status: "pending", - fees: tx.content.transaction_fee, + fees: { + network_fee: 0, + rpc_fee: 0, + additional_fee: 0, + }, // This is the gas operation so it doesn't have additional fees } - console.log("[TX RECEIVED] Gas Operation derived\n") + console.log("[Native Tx Validation] Gas Operation derived\n") //console.log(gas_operation) + // Verify tx validity - let verified = Transaction.confirmTx(tx, privateKey, publicKey) // REVIEW Are the buffers ok? + let verified = Transaction.confirmTx(tx, privateKey as pki.ed25519.BinaryBuffer, publicKey as pki.ed25519.BinaryBuffer) // REVIEW Are the buffers ok? if (!verified) { - return [false, "Transaction not verified: " + tx.hash] + term.red.bold( + "[Native Tx Validation] [SIGNATURE ERROR] Transaction signature not verified\n", + ) + validityData.data.message = + "[Native Tx Validation] [SIGNATURE ERROR] Transaction signature not verified\n" + // Hash the validation data + let hash = Hashing.sha256(JSON.stringify(validityData.data)) + // Sign the hash + validityData.signature = Cryptography.sign(hash, privateKey) + return validityData } + console.log("[Native Tx Validation] Transaction validity verified, compiling ValidityData\n") + + // Now that we verified the transaction, we can return its validity data + // TODO Add the relevant info + validityData.data.valid = true + validityData.data.message = + "Transaction verified and ready to be executed\n" + validityData.data.gas_operation = gas_operation + // Hash the validation data + let hash = Hashing.sha256(JSON.stringify(validityData.data)) + // Sign the hash + validityData.signature = Cryptography.sign(hash, privateKey) + + console.log("[Native Tx Validation] Transaction validity data compiled\n") + return validityData +} + +// TODO a verified transaction should be signed by the same rpc that verified it and should be only valid for the current consensus round +export async function broadcastVerifiedNativeTransaction( + validityData: ValidityData, +): Promise<[boolean, string, Operation[]?]> { // REVIEW Execute or Revert the transaction // NOTE executeTransaction returns an array of [success, message, operations] // The operations are the Operation objects that are executed in the GLS after the consensus // has confirmed the transaction in the block. - let execution = await executeTransaction(tx) + + let execution = await executeNativeTransaction( + validityData.data.transaction, + ) if (!execution[0]) { return [false, "Execution failed: " + execution[1]] } @@ -111,7 +188,8 @@ export default async function validateTransaction( // NOTE Now we can save the gas operation as the tx is set to be executed // and the gas will be deducted anyway - GLS.getInstance().operations.push(gas_operation) + console.log("[TX RECEIVED] Gas Operation added to the GLS\n") + GLS.getInstance().operations.push(validityData.data.gas_operation) // Finally, we add all the derived operations to the GLS for (let i = 0; i < execution[2].length; i++) { @@ -120,5 +198,5 @@ export default async function validateTransaction( GLS.getInstance().operations.push(execution[2][i]) console.log("[TX RECEIVED] Operation added to the GLS\n") } - return [true, tx] + return execution } diff --git a/src/libs/blockchain/transaction.ts b/src/libs/blockchain/transaction.ts index c84f00090..a2f3e7fec 100644 --- a/src/libs/blockchain/transaction.ts +++ b/src/libs/blockchain/transaction.ts @@ -18,14 +18,16 @@ NOTE: The fee is locked by the node and released when the block itself is confir */ -import { pki } from "node-forge" +import forge from "node-forge" + +import { + ISignature, RawTransaction, Transaction as ITransaction, TransactionContent, +} from "@kynesyslabs/demosdk/types" import Cryptography from "../crypto/cryptography" import Hashing from "../crypto/hashing" import { compressData, decompressData } from "../utils/demostdlib" import Confirmation from "./types/confirmation" -import RawTransaction from "./types/rawTransaction" -import { TransactionContent } from "./types/transactions" interface TransactionResponse { status: string @@ -34,14 +36,9 @@ interface TransactionResponse { data: {} } -interface Signature { - type: string - data: pki.ed25519.BinaryBuffer -} - -export default class Transaction { +export default class Transaction implements ITransaction { content: TransactionContent - signature: Signature + signature: ISignature hash: string status: string blockNumber: number @@ -69,7 +66,7 @@ export default class Transaction { // INFO Given a transaction, sign it with the private key of the sender public static sign( tx: Transaction, - privateKey: pki.ed25519.BinaryBuffer, + privateKey: forge.pki.ed25519.BinaryBuffer, ): any[] { // Check sanity of the structure of the tx object if (!tx.content) { @@ -119,9 +116,12 @@ export default class Transaction { // INFO Compile a verification for a transaction and spit out the resulting tx static confirmTx( tx: Transaction, - publicKey: pki.ed25519.BinaryBuffer, - privateKey: pki.ed25519.BinaryBuffer, + publicKey: forge.pki.ed25519.BinaryBuffer, + privateKey: forge.pki.ed25519.BinaryBuffer, ) { + console.log(publicKey) + console.log(privateKey) + console.log(tx.signature) let confirmed = this.sanityCheck(tx) && this.isCoherent(tx) && this.structured(tx) if (confirmed) { @@ -140,19 +140,25 @@ export default class Transaction { // INFO Checks the integrity of a transaction public static sanityCheck(tx: Transaction) { + console.log("[sanityCheck] Checking the sanity of the tx with hash: " + tx.hash) + //let tx_content_hash = Hashing.sha256(JSON.stringify(tx.content)) let _result = Cryptography.verify( - JSON.stringify(tx.content), - tx.signature, + tx.hash, + tx.signature.data, tx.content.from, ) + console.log("[sanityCheck] Sanity: " + _result) return _result } // INFO Checking if the tx is coherent to the current state of the blockchain (and the txs pending before it) public static isCoherent(tx: Transaction) { let _result = true + console.log("[isCoherent] Checking the coherence of the tx with hash: " + tx.hash) let _derived_hash = Hashing.sha256(JSON.stringify(tx.content)) - _result = _derived_hash !== tx.hash + console.log("[isCoherent] Derived hash: " + _derived_hash) + _result = (_derived_hash == tx.hash) + console.log("[isCoherent] Coherence: " + _result) return _result } diff --git a/src/libs/blockchain/types/addressInfo.ts b/src/libs/blockchain/types/addressInfo.ts deleted file mode 100644 index f7db74a48..000000000 --- a/src/libs/blockchain/types/addressInfo.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { StatusNative } from "src/model/entities/StatusNative" -import { StatusProperties } from "src/model/entities/StatusProperties" - -export default interface AddressInfo { - native: StatusNative | null - properties: StatusProperties | null -} diff --git a/src/libs/blockchain/types/blocks.ts b/src/libs/blockchain/types/blocks.ts deleted file mode 100644 index 193f9578b..000000000 --- a/src/libs/blockchain/types/blocks.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -import Transactions from "../transaction" - -export default interface BlockContent { - ordered_transactions: string[] - per_address_transactions: Map - web2data: {} // TODO Add Web2 class - previousHash: string - timestamp: number -} diff --git a/src/libs/blockchain/types/confirmation.ts b/src/libs/blockchain/types/confirmation.ts index fa1d7d8a1..4a8117d64 100644 --- a/src/libs/blockchain/types/confirmation.ts +++ b/src/libs/blockchain/types/confirmation.ts @@ -17,4 +17,12 @@ export default class Confirmation { tx_hash_validated: string } signature: forge.pki.ed25519.BinaryBuffer + + constructor() { + this.data = { + validator: null, + tx_hash_validated: null, + } + this.signature = null + } } diff --git a/src/libs/blockchain/types/genesisTypes.ts b/src/libs/blockchain/types/genesisTypes.ts index bcb0b048a..d45a68771 100644 --- a/src/libs/blockchain/types/genesisTypes.ts +++ b/src/libs/blockchain/types/genesisTypes.ts @@ -1,5 +1,7 @@ import { Blocks } from "src/model/entities/Blocks" +import { GenesisArtifact } from "@kynesyslabs/demosdk/types" + // INFO Defining the structure of a valid Genesis block import Chain from "../chain" @@ -9,48 +11,6 @@ import Chain from "../chain" * */ -// SECTION Primitives - -export interface GenesisImmutableProperties { - id: number - name: string - currency: string -} - -export interface GenesisMutableProperties { - minBlocksForValidationOnlineStatus: number -} - -export interface GenesisArtifact { - properties: GenesisImmutableProperties - mutables: GenesisMutableProperties - balances: [[address: string, amount: string]] - timestamp: number - previous_genesis_hash: string - previous_block_hash: string - signature: string - hash: string - number: number -} - -// !SECTION Primitives - -// SECTION Components - -export interface StandardGenesis { - properties: GenesisImmutableProperties - mutables: GenesisMutableProperties - balances: [[address: string, amount: string]] - timestamp: number -} - -export interface forkGenesis extends StandardGenesis { - previous_genesis_hash: string - previous_block_hash: string -} - -// !SECTION Components - // INFO The Genesis class with its methods export default class Genesis { genesisBlock: Blocks // The genesis block diff --git a/src/libs/blockchain/types/rawTransaction.ts b/src/libs/blockchain/types/rawTransaction.ts deleted file mode 100644 index 2b14606b4..000000000 --- a/src/libs/blockchain/types/rawTransaction.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -export default interface RawTransaction { - blockNumber: number - signature: string - status: string - hash: string - content: NonNullable - type: string - from: any - to: any - amount: number - nonce: number - timestamp: number - networkFee: number - rpcFee: number - additionalFee: number -} diff --git a/src/libs/blockchain/types/statusNative.ts b/src/libs/blockchain/types/statusNative.ts deleted file mode 100644 index 77774e6bd..000000000 --- a/src/libs/blockchain/types/statusNative.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default interface statusNative { - address: string - balance: number - nonce: number - tx_list: string -} diff --git a/src/libs/blockchain/types/statusProperties.ts b/src/libs/blockchain/types/statusProperties.ts deleted file mode 100644 index 0669183da..000000000 --- a/src/libs/blockchain/types/statusProperties.ts +++ /dev/null @@ -1,8 +0,0 @@ -export default interface statusNative { - address: string - tokens: string - nfts: string - xm: string - web2: string - other: string -} diff --git a/src/libs/blockchain/types/transactions.ts b/src/libs/blockchain/types/transactions.ts deleted file mode 100644 index 97a845c99..000000000 --- a/src/libs/blockchain/types/transactions.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -import forge, { pki } from "node-forge" - -export interface TxFee { - network_fee: number - rpc_fee: number - additional_fee: number -} - -export interface fromSignature { - type: string - data: pki.ed25519.BinaryBuffer -} - -export interface toSignature { - type: string - data: pki.ed25519.BinaryBuffer -} - -export interface TransactionContent { - type: string - from: forge.pki.ed25519.BinaryBuffer | forge.pki.PublicKey | fromSignature - to: forge.pki.ed25519.BinaryBuffer | forge.pki.PrivateKey | toSignature - amount: number - data: [string, string] // type as string and content in hex string - nonce: number // Increments every time a transaction is sent from the same account - timestamp: number // Is the registered unix timestamp when the transaction was sent the first time - transaction_fee: TxFee // Is the signed message where the sender locks X tokens until the tx is confirmed -} diff --git a/src/libs/communications/comlink.ts b/src/libs/communications/comlink.ts index afa8937f5..90e3cf17d 100644 --- a/src/libs/communications/comlink.ts +++ b/src/libs/communications/comlink.ts @@ -172,11 +172,14 @@ export default class ComLink { // INFO Generic comlink validation function async validateComlink() { var _currentMessage = this.chain.current.currentMessage + console.log(_currentMessage) console.log("[COMLINK VALIDATION 2] Stringifying...") // Check if the current message hash matches the message let stringifiedMessage = JSON.stringify(_currentMessage.bundle.content) + console.log(_currentMessage.bundle.hash) console.log("[COMLINK VALIDATION 2] Hashing...") let _derivedMessageHash = Hashing.sha256(stringifiedMessage) + console.log(_derivedMessageHash) if (!(_derivedMessageHash === _currentMessage.bundle.hash)) { //console.log(_currentMessage) //console.log(stringifiedMessage) diff --git a/src/libs/communications/comlinkUtils.ts b/src/libs/communications/comlinkUtils.ts index d82abd77b..13079fc8f 100644 --- a/src/libs/communications/comlinkUtils.ts +++ b/src/libs/communications/comlinkUtils.ts @@ -9,11 +9,14 @@ KyneSys Labs: https://www.kynesys.xyz/ */ +import { Socket } from "socket.io-client" import sharedState from "src/utilities/sharedState" import sizeOf from "src/utilities/sizeOf" import { json } from "stream/consumers" import terminalkit from "terminal-kit" +import { DefaultEventsMap } from "@socket.io/component-emitter" + import ComLink from "./comlink" const term = terminalkit.terminal @@ -21,9 +24,9 @@ const term = terminalkit.terminal export default class ComLinkUtils { // INFO common comlink digestor static async parseComlink( - request, - peerSocket, - ): Promise<[ComLink, any] | boolean> { + request: ComLink, + peerSocket: Socket, + ): Promise { // We need to check if the message request is valid (is a ComLink object) term.yellow("[COMLINKUTILS] Received comlink\n") // GIving the request the comlink methods @@ -53,7 +56,7 @@ export default class ComLinkUtils { "The request has a current message that is a: " + typeof _comlink_request.chain.current.currentMessage, ) - let type_of_call + let type_of_call: string try { type_of_call = _comlink_request.chain.current.currentMessage.bundle.content @@ -64,7 +67,7 @@ export default class ComLinkUtils { status: "error", message: e, }) - return false + return null } if (!(type_of_call === "nodeCall")) { let valid: any[] @@ -79,7 +82,7 @@ export default class ComLinkUtils { status: "error", message: valid[1], }) - return false + return null } } else { term.green("[COMLINK] nodeCall received (no auth required)\n") @@ -92,17 +95,16 @@ export default class ComLinkUtils { muid: null, message: "No muid specified", }) - return false + return null } console.log("[COMLINK PARSING] MUID: " + request.muid) // Taking the message part - let content - if (!(typeof request.chain.current.currentMessage === "object")) { + /*if (!(typeof request.chain.current.currentMessage === "object")) { content = JSON.parse(request.chain.current.currentMessage).bundle .content - } else { - content = request.chain.current.currentMessage.bundle.content - } + } else { */ + let content = request.chain.current.currentMessage.bundle.content + //} if (!content) { console.log( "[COMLINK PARSING] Eww, no content specified. Erroring back.", @@ -115,18 +117,18 @@ export default class ComLinkUtils { } console.log("[COMLINK PARSING] Content parsed") //console.log(content) - if (!content.message) { + if (!content.message && !content.data) { console.log( - "[COMLINK PARSING] No message specified. Erroring back.", + "[COMLINK PARSING] No message or data specified. Erroring back.", //console.log(content), ) peerSocket.emit("error", { muid: request.muid, message: "Eww...no message specified", }) - return false + return null } console.log("[COMLINK PARSING] Message parsed") - return [_comlink_request, content] + return _comlink_request } } diff --git a/src/libs/communications/transmission.ts b/src/libs/communications/transmission.ts index c08e20824..bf27d0c33 100644 --- a/src/libs/communications/transmission.ts +++ b/src/libs/communications/transmission.ts @@ -11,11 +11,12 @@ KyneSys Labs: https://www.kynesys.xyz/ import forge, { pki } from "node-forge" +import { Bundle } from "@kynesyslabs/demosdk/types" + import Cryptography from "../crypto/cryptography" // INFO This module exposes methods designed to have an unified way of communicate in DEMOS import Hashing from "../crypto/hashing" import { Peer } from "../peer" -import { Bundle } from "./types/transmit" export default class Transmission { bundle: Bundle diff --git a/src/libs/communications/types/transmit.ts b/src/libs/communications/types/transmit.ts deleted file mode 100644 index 408ab7856..000000000 --- a/src/libs/communications/types/transmit.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -import forge, { pki } from "node-forge" - -export interface BundleContent { - type: string - message: string - sender: any // TODO improve interface - receiver: any // TODO improve interface - timestamp: number - data: Buffer - extra: Buffer -} - -export interface Bundle { - content: BundleContent - hash: string - signature: any // TODO improve interface -} diff --git a/src/libs/crypto/cryptography.ts b/src/libs/crypto/cryptography.ts index 33244f843..9a22d03b7 100644 --- a/src/libs/crypto/cryptography.ts +++ b/src/libs/crypto/cryptography.ts @@ -180,7 +180,11 @@ export default class Cryptography { publicKey = Buffer.from(publicKey) // REVIEW Does not work in bun } - console.log("[*] Verifying the signature...") + console.log("[*] Verifying the signature of: " + signed + "\n") + console.log("[*] Using the signature: ") + console.log(signature) + console.log("[*] And the public key: ") + console.log(publicKey) return forge.pki.ed25519.verify({ message: signed, encoding: "utf8", diff --git a/src/libs/network/routines/determineGasForOperation.ts b/src/libs/network/routines/determineGasForOperation.ts index 05ebd3494..e6f0222a9 100644 --- a/src/libs/network/routines/determineGasForOperation.ts +++ b/src/libs/network/routines/determineGasForOperation.ts @@ -7,7 +7,7 @@ export default async function determineGasForOperation( // Calculating byte size of the operation let byte_size = demostdlib.payloadSize(operation) // Getting the base gas from the chain status (GLS) - let base_gas = await calculateCurrentGas() + let base_gas = await calculateCurrentGas(operation) // INFO The gas required for an operation is the base gas multiplied by the byte size let operation_gas = base_gas * byte_size diff --git a/src/libs/network/routines/manageMessages.ts b/src/libs/network/routines/manageMessages.ts new file mode 100644 index 000000000..dc726e884 --- /dev/null +++ b/src/libs/network/routines/manageMessages.ts @@ -0,0 +1,76 @@ +import { pki } from "node-forge" +import { Socket } from "socket.io-client" +// INFO This module manages gasless calls (such as rpc calls, consensus, etc.) +import ComLink from "src/libs/communications/comlink" +import { proofConsensusHandler } from "src/libs/consensus/routines/proofOfConsensus" +// NOTE Terminal kit for useful logging +import terminalkit from "terminal-kit" + +import { BundleContent } from "@kynesyslabs/demosdk/types" + +import ServerHandlers from "../serverHandlers" + +const term = terminalkit.terminal + +export default async function manageMessages( + content: BundleContent, + original_comlink: ComLink, + original_request: any, + id_ed25519: pki.KeyPair, + receiver: Socket, +) { + let extra = null + let require_reply = false + let response = null + + switch (content.type) { + case "proofOfConsensus": + ({ extra, require_reply, response } = + await proofConsensusHandler(content)) + + break + case "consensus": + console.log("[SERVER LISTENER HANDLER]: received consensus request") + console.log( + original_comlink.chain.current.currentMessage.bundle.content + .sender, + ) + ;({ extra, require_reply, response } = + await ServerHandlers.handleConsensusRequest( + original_request, + content, + original_comlink.chain.current.currentMessage.bundle.content + .sender, + )) + break + + case "messages": + ({ extra, require_reply, response } = + await ServerHandlers.handleMessage(content)) + break + + case "storage": + ({ extra, require_reply, response } = + await ServerHandlers.handleStorage()) + break + + case "mempool": + ({ extra, require_reply, response } = + await ServerHandlers.handleMempool(content)) + break + + case "nodeCall": + ({ extra, require_reply, response } = + await ServerHandlers.handleNodeAPI( + content, + receiver, + id_ed25519, + )) + break + + default: + term.red(`[COMLINK INVALID] No known type: ${content.type}\n`) + break + } + return { extra, require_reply, response } +} diff --git a/src/libs/network/routines/nodecalls/getPeerlist.ts b/src/libs/network/routines/nodecalls/getPeerlist.ts index 878f3ceb3..1c57f63e1 100644 --- a/src/libs/network/routines/nodecalls/getPeerlist.ts +++ b/src/libs/network/routines/nodecalls/getPeerlist.ts @@ -1,6 +1,7 @@ -import PeerManager from "../../../peer/PeerManager" import { Peer } from "src/libs/peer" +import PeerManager from "../../../peer/PeerManager" + export default async function getPeerlist(): Promise { console.log("[SERVER] Received getPeerlist") // Getting our current peerlist diff --git a/src/libs/network/securityModule.ts b/src/libs/network/securityModule.ts index 7dc017e30..0c97ab346 100644 --- a/src/libs/network/securityModule.ts +++ b/src/libs/network/securityModule.ts @@ -1,22 +1,5 @@ // TODO Implement this - -export interface ISecurityReport { - state: boolean - code: string - message: string -} - -interface SIResponseRegistry { - prune_interval: number -} - -interface SIComlink { - rate_limit_size: number // How many comlinks can be sent in an interval? // TODO Make it configurable - rate_limit_time: number // How many milliseconds is an interval? // TODO Make it configurable - rate_limit_bin: number // The amount of comlinks sent in the last interval // TODO Make it configurable - rate_limit_timestamp: number // The timestamp of the last interval - checkRateLimits: Function -} +import { ISecurityReport } from "@kynesyslabs/demosdk/types" export const modules = { // SECTION Modules diff --git a/src/libs/network/server.ts b/src/libs/network/server.ts index 9d9643748..dcdac9291 100644 --- a/src/libs/network/server.ts +++ b/src/libs/network/server.ts @@ -10,11 +10,10 @@ KyneSys Labs: https://www.kynesys.xyz/ */ import { Server as ServerType } from "socket.io" +import ServerListeners from "src/libs/network/serverListeners" +import { Peer } from "src/libs/peer" import terminalkit from "terminal-kit" -import { Peer } from "../peer" -import ServerListeners from "./serverListeners" - const term = terminalkit.terminal export default class Server { diff --git a/src/libs/network/serverHandlers.ts b/src/libs/network/serverHandlers.ts index f9d6cf50e..b97e3146a 100644 --- a/src/libs/network/serverHandlers.ts +++ b/src/libs/network/serverHandlers.ts @@ -15,28 +15,37 @@ import multichainDispatcher from "src/features/multichain/XMDispatcher" import handleWeb2 from "src/features/web2/Web2Dispatcher" import Chain from "src/libs/blockchain/chain" import Mempool from "src/libs/blockchain/mempool" +import { + broadcastVerifiedNativeTransaction, confirmTransaction, +} from "src/libs/blockchain/routines/validateTransaction" +import Transaction from "src/libs/blockchain/transaction" +import deriveBlock from "src/libs/consensus/routines/deriveBlock" +import Cryptography from "src/libs/crypto/cryptography" +import Hashing from "src/libs/crypto/hashing" +import eggs from "src/libs/network/routines/eggs" +import getBlockByHash from "src/libs/network/routines/nodecalls/getBlockByHash" +import getBlockByNumber from "src/libs/network/routines/nodecalls/getBlockByNumber" +import getBlockHeaderByHash from "src/libs/network/routines/nodecalls/getBlockHeaderByHash" +import getBlockHeaderByNumber from "src/libs/network/routines/nodecalls/getBlockHeaderByNumber" +import getPeerlist from "src/libs/network/routines/nodecalls/getPeerlist" +import getPreviousHashFromBlockHash from "src/libs/network/routines/nodecalls/getPreviousHashFromBlockHash" +import getPreviousHashFromBlockNumber from "src/libs/network/routines/nodecalls/getPreviousHashFromBlockNumber" +import { normalizeWebBuffers } from "src/libs/network/routines/normalizeWebBuffers" +import Sessions from "src/libs/network/routines/sessionManager" +import { BrowserRequest } from "src/libs/network/serverListeners" import { Peer } from "src/libs/peer" import { Blocks } from "src/model/entities/Blocks" import sharedState from "src/utilities/sharedState" // NOTE Terminal kit for useful logging import terminalkit from "terminal-kit" +import { + AddressInfo, ExecutionResult, IWeb2Payload, IWeb2Request, ValidityData, + XMScript, +} from "@kynesyslabs/demosdk/types" + import GLS from "../blockchain/gls/gls" -import validateTransaction from "../blockchain/routines/validateTransaction" -import Transaction from "../blockchain/transaction" -import AddressInfo from "../blockchain/types/addressInfo" -import deriveBlock from "../consensus/routines/deriveBlock" -import eggs from "./routines/eggs" -import getPreviousHashFromBlockNumber from "./routines/nodecalls/getPreviousHashFromBlockNumber" -import { normalizeWebBuffers } from "./routines/normalizeWebBuffers" -import Sessions from "./routines/sessionManager" -import { BrowserRequest } from "./serverListeners" -import getPeerlist from "./routines/nodecalls/getPeerlist" -import getPreviousHashFromBlockHash from "./routines/nodecalls/getPreviousHashFromBlockHash" -import getBlockHeaderByHash from "./routines/nodecalls/getBlockHeaderByHash" -import getBlockHeaderByNumber from "./routines/nodecalls/getBlockHeaderByNumber" -import getBlockByNumber from "./routines/nodecalls/getBlockByNumber" -import getBlockByHash from "./routines/nodecalls/getBlockByHash" +import { NativePayload, StringifiedPayload, Web2Payload, XMPayload } from "node_modules/@kynesyslabs/demosdk/build/types/blockchain/Transaction" let term = terminalkit.terminal @@ -85,62 +94,183 @@ export default class ServerHandlers { // !SECTION Login On Chain // ANCHOR Comlinks - static async handleTransaction(content: any): Promise { - term.yellow("[handleTransactions] Handling a native DEMOS tx...\n") - let require_reply = true // REVIEW Sure? - let extra: string, response: boolean + static async handleValidateTransaction( + tx: Transaction, + ): Promise { + term.yellow("[handleTransactions] Handling a DEMOS tx...\n") let fname = "[handleTransactions] " term.yellow(fname + "Handling transaction...") // Verify and execute the transaction - let validatedTx: any[] + let validationData: ValidityData try { /* NOTE This workflow goeas as: - * The tx is validated, an operation is created and pushed in the GLS - * An operation for the gas is also pushed in the GLS - * The tx is pushed in the mempool if applicable + * The transaction is validated + * A gas operation is created and is sent back alongside the validation data + * TODO Add signatures to validation data + * The validation data can be used by the client to effectively execute the tx */ //console.log(fname + "Validating transaction...") - validatedTx = await validateTransaction( - content.type, - content.message, - ) + validationData = await confirmTransaction(tx) //console.log(fname + "Fetching result...") } catch (e) { term.red.bold("[TX VALIDATION ERROR] 💀 : ") term.red(e) - validatedTx = [false, JSON.stringify(e)] + validationData = { + data: { + valid: false, + reference_block: null, + message: + "An error occurred while validating the transaction", + gas_operation: null, + transaction: null, + }, + signature: null, + rpc_public_key: null, + } + // Signing and hashing the validation data + let hashedValidationData = Hashing.sha256( + JSON.stringify(validationData.data), + ) + validationData.signature = Cryptography.sign( + hashedValidationData, + sharedState.getInstance().identity.ed25519.privateKey, + ) + } + + term.bold.white(fname + "Transaction handled.") + return validationData + } + + // NOTE This method is used to handle the execution of a transaction + // TODO Better typing for content (must contain validity data, hashing and signature as shown below) + // TODO Either put this into a module or do something to make it more modular + static async handleExecuteTransaction( + validatedData: ValidityData, + senderSocket: any, + ): Promise { + + let fname = "[handleExecuteTransaction] " + let result: ExecutionResult = { + success: true, + response: null, + extra: null, + require_reply: false, } + // NOTE Content should contain validity data and our signature to proceed + // Integrity checks + let ourKey = sharedState.getInstance().identity.ed25519.publicKey + let hexOurKey = ourKey.toString("hex") + let dataKey = validatedData.rpc_public_key + let hexDataKey = Buffer.from(dataKey as Buffer).toString("hex") + let dataSignature = validatedData.signature + let queriedTx = validatedData.data.transaction + console.log("[SERVER] Received transaction for execution: " + queriedTx.hash) + + // We need to have issued the validity data + if (hexDataKey !== hexOurKey) { + term.red.bold(fname + "Invalid validityData signature key (not us) 💀 : ") + + result.success = false + result.response = false + result.extra = "Invalid signature key" + return result - // Returning an appropriate response - if (!validatedTx[0]) { + } + // Also the signature must be valid + let hashedData = Hashing.sha256(JSON.stringify(validatedData.data)) + let signatureValid = Cryptography.verify( + hashedData, + dataSignature, + dataKey, + ) + if (!signatureValid) { + term.red.bold(fname + "Invalid validityData signature 💀 : ") + result.success = false + result.response = false + result.extra = "Invalid signature" + return result + } + // Finally, the block number reference must be valid + let blockNumber = validatedData.data.reference_block + let lastBlockNumber = await Chain.getLastBlockNumber() + if (blockNumber != lastBlockNumber) { + term.red.bold(fname + "Invalid validityData block reference 💀 : ") + result.success = false + result.response = false + result.extra = "Invalid block reference" + return result + } + // REVIEW Is this useful at this point? + if (!validatedData.data.valid) { // An invalid transaction won't even be added to the mempool - term.yellow.bold(fname + "Invalid transaction 💀 : ") - console.log(validatedTx[1]) - extra = "InvalidTransaction 💀: " + validatedTx[1] - response = false - } else { - /* NOTE + term.yellow.bold(fname + "Invalid validityData 💀 : ") + console.log(validatedData.data.message) + result.success = false + result.response = false + result.extra = validatedData.data.message + return result + } + + /* NOTE We just processed the cryptographic validity of the transaction. - We have no idea of its state validity and thus won't modify the GLS, but - it can go into the mempool to be further processed if its cryptographically valid. + We will now try to execute it obtaining valid Operations. */ - term.green.bold(fname + "Valid transaction! ") - //console.log(validatedTx[1]) - console.log(fname + "Adding transaction to mempool...") - // Adding the valid tx to the mempool - Mempool.addTransaction(validatedTx[1]) // Works by writing the registry - extra = validatedTx[1].hash - response = true - //process.exit(0) /* TODO Eliminate this debug line */ + term.green.bold(fname + "Valid validityData! \n") + // REVIEW Switch case for different types of transactions + let tx = validatedData.data.transaction + // Using a payload variable to be able to check types immediately + let payload: XMPayload | Web2Payload | NativePayload | StringifiedPayload + switch (tx.content.type) { + case "crosschainOperation": + case "multichainOperation": + payload = tx.content.data as XMPayload + console.log( + "[Included XM Chainscript]") + console.log(payload[1]) + // TODO Better types on answers + var xm_result = await ServerHandlers.handleXMChainOperation( + payload[1] as XMScript, + ) + // TODO Add result.success handling + result.response = xm_result + break + case "web2Request": + // TODO Better types on answers + payload = tx.content.data as Web2Payload + var web2_result = await ServerHandlers.handleWeb2Request( + payload[1] as IWeb2Request, + senderSocket, + ) + // TODO Add result.success handling + result.response = web2_result + break + case "native": + // REVIEW This still works with the new tx system? + var native_result = await broadcastVerifiedNativeTransaction(validatedData) + // NOTE We add the Transaction to the mempool as it looks valid + if (native_result[0]) { + result.success = true + } + // REVIEW Check if this is ok with types + result.response = native_result + } + // Only if the transaction is valid we add it to the mempool + if (result.success) { + // REVIEW We add the transaction to the mempool + Mempool.addTransaction(queriedTx) // FIXME queriedTx hash mismatch with the expected hash? WHY + /* TODO for the above FIXME + * queriedTx should be identical to above but here is not coherent anymore + */ + // TODO Check if Operation(s) are added to the GLS too } - // TODO Broadcast the tx to the other peers + // TODO Broadcast the tx to the other peers (or maybe not, consensus should take care of it) // Response is then sent back automatically as a reply (with our validation) - term.bold.white(fname + "Transaction handled.") - return { extra, require_reply, response } + // Returning the state of the transaction including operations + return result } // INFO Handling XM Transaction - static async handleXMChainOperation(xmscript: any): Promise { + static async handleXMChainOperation(xmscript: XMScript): Promise { /* NOTE This workflow goeas as: * The XM Operation is validated, executed and verified * when applicable. @@ -162,7 +292,7 @@ export default class ServerHandlers { // INFO This method is used to allow signed data exchanges between peers and clients static async handleXMChainSignedPayload(content: any): Promise { - // TODO Probably to take it out + // TODO Probably to take out } static async handleXMChainStatus(): Promise { @@ -178,8 +308,7 @@ export default class ServerHandlers { // NOTE Theoretically, content should be IWeb2Request compliant // LINK "../../features/web2/types/Web2Request"; static async handleWeb2Request( - request: any, - content: any, + content: IWeb2Request, senderSocket: any, ): Promise { /* NOTE This workflow goeas as: @@ -193,9 +322,9 @@ export default class ServerHandlers { console.log("[SERVER] Received web2Request") //console.log(JSON.stringify(request)) - let extra: any, + let extra: string, require_reply = false - let response: unknown + let response: IWeb2Request // We get our connection string // const currentPeerString = Identity.getInstance().getConnectionString() // NOTE Switched to the new class @@ -208,10 +337,10 @@ export default class ServerHandlers { // Managing the results if (fullResponse[0]) { - response = fullResponse[1] + response = fullResponse[1] as IWeb2Request } else { - response = "error" - extra = fullResponse[1] + response = null + extra = fullResponse[1] as string } return { extra, require_reply, response } } @@ -338,11 +467,12 @@ export default class ServerHandlers { //console.log(typeof data) //console.log(JSON.stringify(content)) switch (content.message) { - case "crosschain_operation": + // NOTE The following commented block of code is vestigial + /*case "crosschain_operation": case "multichain_operation": term.yellow.bold("[SERVER] Received crosschain_operation\n") response = await ServerHandlers.handleXMChainOperation(content) - break // REVIEW Here or in comlinks? + break // REVIEW Here or in comlinks? */ case "getPeerlist": response = await getPeerlist() break diff --git a/src/libs/network/serverListeners.ts b/src/libs/network/serverListeners.ts index 0c21c5534..db51282c1 100644 --- a/src/libs/network/serverListeners.ts +++ b/src/libs/network/serverListeners.ts @@ -1,27 +1,19 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - +import Transaction from "src/libs/blockchain/transaction" import { comlinkUtils } from "src/libs/communications" +import ComLink from "src/libs/communications/comlink" import { cryptography } from "src/libs/crypto" -import { demostdlib } from "../utils" +import manageMessages from "src/libs/network/routines/manageMessages" +import { ISession } from "src/libs/network/routines/sessionManager" +import * as Security from "src/libs/network/securityModule" /* eslint-disable no-extra-semi */ import ServerHandlers from "src/libs/network/serverHandlers" import { Peer } from "src/libs/peer" +import { demostdlib } from "src/libs/utils" import sharedState from "src/utilities/sharedState" // NOTE Terminal kit for useful logging import terminalkit from "terminal-kit" -import { proofConsensusHandler } from "../consensus/routines/proofOfConsensus" -import { ISession } from "./routines/sessionManager" -import * as Security from "./securityModule" +import { BundleContent, ISecurityReport, ValidityData } from "@kynesyslabs/demosdk/types" let term = terminalkit.terminal @@ -75,169 +67,151 @@ export default class ServerListeners { }) } - // NOTE ComLinks are managed "centrally" here so apply securityModule stuff here + // REVIEW ComLinks are managed "centrally" here so apply securityModule stuff here + /* NOTE ComLink and Transactions + Once a comlink is received, we need to parse it and check if it is a transaction or a message. + Basically, a transaction needs gas calculation and validation, then it is sent back to the client that can + either execute it or discard it. + A message is just a message, so it is handled by its type. + */ async comlinkListener() { this.peer.socket.on("comlink", async request => { - term.yellow("[SERVER] Received comlink\n") - const id_ed25519 = sharedState.getInstance().identity.ed25519 - const receiver = this.peer.socket - let parsed_comlink: any // TODO Better typing - // TODO This can be put into securityModule for consistency - try { - // Parsing comlink - parsed_comlink = await comlinkUtils.parseComlink( - request, - this.peer.socket, - ) - if (!parsed_comlink) { - return // TODO Better error handling - } - } catch (error) { - term.red(error) - console.log("Returning") - return // TODO Better error handling - } - let _comlink_request = parsed_comlink[0] - console.log("[serverListeners] ComLink request received and parsed correctly") - //console.log(_comlink_request) - let content = parsed_comlink[1] - console.log("[serverListeners] Received comlink content") - - let extra: any, require_reply: any, response: any - - //let content_data = content.data - //console.log("[DATA INCLUDED IN THE COMLINK]") - //console.log(content_data) - - // NOTE And here we have the real deal - console.log("[serverListeners] content.type: " + content.type) - switch (content.type) { - // TODO We need to calculate the gas cost for operations that require it - case "proofOfConsensus": - ;({ extra, require_reply, response } = - await proofConsensusHandler(content)) - break - - case "tx": - term.yellow.bold("[SERVER] Received tx\n") - ;({ extra, require_reply, response } = - await ServerHandlers.handleTransaction(content)) - break - - case "crosschain_operation": - case "multichain_operation": - console.log( - "[Included XM Chainscript]" + - JSON.stringify(content.message) + - "\n\n", - ) - ;({ extra, require_reply, response } = - await ServerHandlers.handleXMChainOperation( - content.message, - )) - break - - case "web2Request": - ;({ extra, require_reply, response } = - await ServerHandlers.handleWeb2Request( - request, - content, - this.peer.socket, - )) - break - - case "consensus": - console.log( - "[SERVER LISTENER HANDLER]: received consensus request", - ) - console.log( - parsed_comlink[0].chain.current.currentMessage.bundle - .content.sender, - ) - ;({ extra, require_reply, response } = - await ServerHandlers.handleConsensusRequest( - request, - content, - parsed_comlink[0].chain.current.currentMessage - .bundle.content.sender, - )) - break - - case "messages": - ;({ extra, require_reply, response } = - await ServerHandlers.handleMessage(content)) - break - - case "storage": - ;({ extra, require_reply, response } = - await ServerHandlers.handleStorage()) - break - - case "mempool": - ;({ extra, require_reply, response } = - await ServerHandlers.handleMempool(content)) - break - - case "nodeCall": - ;({ extra, require_reply, response } = - await ServerHandlers.handleNodeAPI( - content, - receiver, - id_ed25519, - )) - break - - default: - term.red( - `[COMLINK INVALID] No known type: ${content.type}\n`, - ) - break - } - //console.log("content.message: " + content.message) - //console.log("content.message.action: " + content.message.action) + await this.manageComLink(request) + }) + // TODO See in communications.js and find the best way to validate, check and digest the request + } - // ANCHOR Reply logic - // NOTE unless specified, we now send back the updated comlink as a response - await demostdlib.reply( - _comlink_request, - response, - require_reply, - extra, + // This method is used to check the comlink before processing it + async preflightComLinkChecks(request: any): Promise { + term.yellow("[SERVER] Received comlink\n") + console.log(request) + const id_ed25519 = sharedState.getInstance().identity.ed25519 + const receiver = this.peer.socket + let _comlink_request: ComLink + // TODO This can be put into securityModule for consistency + try { + // Parsing comlink + _comlink_request = await comlinkUtils.parseComlink( + request, + this.peer.socket, ) + if (!_comlink_request) { + return // TODO Better error handling + } + } catch (error) { + term.red(error) + console.log("Returning") + return // TODO Better error handling + } + // We can now extract the comlink and the content to be used in the handlers + console.log( + "[serverListeners] ComLink request received and parsed correctly", + ) + + let content: BundleContent = + _comlink_request.chain.current.currentMessage.bundle.content + return { _comlink_request, content, id_ed25519, receiver } + } - // TODO & REVIEW Call security module for send limiting messages - let secDisabled = false - if (!secDisabled) { - let ts = new Date().getTime() - let securityInterceptor: Security.ISecurityReport = - await Security.modules.communications.comlink.checkRateLimits( - ts, + // Here, we manage the comlink and its content + async manageComLink(request: any) { + // Security and sanity checks + let { _comlink_request, content, id_ed25519, receiver } = + await this.preflightComLinkChecks(request) + //console.log(_comlink_request) + // NOTE Now we have a valid ComLink and we can work with it + console.log("[serverListeners] Received comlink content") + + let extra: any, require_reply: any, response: any + + console.log("[serverListeners] content.type: " + content.type) + console.log("[serverListeners] content.extra: " + content.extra) + + // REVIEW We use the 'extra' field to see if it is a confirmTx request (prior to execution) + // or an broadcastTx request (to execute the transaction after gas cost is calculated). + // Transactions are either gas consuming or not, so we need to check if the transaction + // needs to be validated,executed or treated as a message. + switch (content.extra) { + // ANCHOR Gas consuming transactions + // Validating a tx means that we calculate gas and check if the transaction is valid + // Then we send the validation data to the client that can use it to execute the tx + case "confirmTx": + term.yellow.bold("[SERVER] Received confirmTx\n") + var validityData = + await ServerHandlers.handleValidateTransaction( + content.data as Transaction, ) - if (!securityInterceptor.state) { - switch (securityInterceptor.code) { - case "429": - break - - default: - term.red.bold( - "[COMLINK] [SECURITY INTERCEPTOR] Unknown error: " + - securityInterceptor.code.toString(), - ) - term.red.bold( - "[COMLINK] [SECURITY INTERCEPTOR] Reported:", - ) - console.log(securityInterceptor.message) - break - } + response = validityData + extra = "" + require_reply = false // REVIEW Should we require a reply here? + + // console.log(response) + + break + // Executing a tx means that we execute the transaction and send back the result + // to the client. We first need to check if the tx is actually valid. + case "broadcastTx": + term.yellow.bold("[SERVER] Received broadcastTx\n") + // REVIEW This method needs to actually verify if the transaction is valid + var result = await ServerHandlers.handleExecuteTransaction( + content.data as ValidityData, + this.peer.socket, + ) + // Destructuring the result to get the extra, require_reply and response + ;({ extra, require_reply, response } = result) + break + // ANCHOR Messages + // All the rest of the comlink types do not require extra validation or gas calculation + // They are treated as messages and are handled by their types themselves + // For readability, we call an external function to manage the messages + default: + ;({ extra, require_reply, response } = await manageMessages( + content, + _comlink_request, + request, + id_ed25519, + receiver, + )) + break + } + //console.log("content.message: " + content.message) + //console.log("content.message.action: " + content.message.action) + + // ANCHOR Reply logic + // NOTE unless specified, we now send back the updated comlink as a response + await demostdlib.reply(_comlink_request, response, require_reply, extra) + + // TODO & REVIEW Call security module for send limiting messages + let secDisabled = false + if (!secDisabled) { + let ts = new Date().getTime() + let securityInterceptor: ISecurityReport = + await Security.modules.communications.comlink.checkRateLimits( + ts, + ) + if (!securityInterceptor.state) { + switch (securityInterceptor.code) { + case "429": + break + + default: + term.red.bold( + "[COMLINK] [SECURITY INTERCEPTOR] Unknown error: " + + securityInterceptor.code.toString(), + ) + term.red.bold( + "[COMLINK] [SECURITY INTERCEPTOR] Reported:", + ) + console.log(securityInterceptor.message) + break } } + } - // Sending back the response - console.log("[SERVER] Sending back comlink") - //console.log(JSON.stringify(_comlink_request)) - receiver.emit("comlink_reply", _comlink_request) // reply is managed in the common listeners - }) - // TODO See in communications.js and find the best way to validate, check and digest the request + // Sending back the response + console.log("[SERVER] Sending back comlink") + //console.log(JSON.stringify(_comlink_request)) + receiver.emit("comlink_reply", _comlink_request) // reply is managed in the common listeners } // INFO Register or update a peer identity and connection string diff --git a/src/libs/peer/Peer.ts b/src/libs/peer/Peer.ts index 05f233c5a..32b184c75 100644 --- a/src/libs/peer/Peer.ts +++ b/src/libs/peer/Peer.ts @@ -9,7 +9,7 @@ KyneSys Labs: https://www.kynesys.xyz/ */ -import type { IPeerConfig } from "./types/Peer" +import type { IPeerConfig } from "@kynesyslabs/demosdk/types" import forge from "node-forge" import { Socket } from "socket.io-client" diff --git a/src/libs/peer/types/Peer.ts b/src/libs/peer/types/Peer.ts deleted file mode 100644 index 3c33ef812..000000000 --- a/src/libs/peer/types/Peer.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* LICENSE - -© 2023 by KyneSys Labs, licensed under CC BY-NC-ND 4.0 - -Full license text: https://creativecommons.org/licenses/by-nc-nd/4.0/legalcode -Human readable license: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -KyneSys Labs: https://www.kynesys.xyz/ - -*/ - -import { Socket } from "socket.io-client" - -export interface IPeerConfig { - connectionString?: string - socket?: Socket - identity?: string -} diff --git a/src/utilities/selfPeer.ts b/src/utilities/selfPeer.ts new file mode 100644 index 000000000..98b6aace1 --- /dev/null +++ b/src/utilities/selfPeer.ts @@ -0,0 +1,16 @@ +import * as fs from "fs" + +// NOTE This method is mainly used by index.ts when we generate a new identity and we have no valid peers +// (aka talking to ourselves) +export default async function selfPeer() { + let public_key_file = "publickey" + let public_key = fs.readFileSync(public_key_file, "utf8") + let basic_peer_script = ` + [ + "http://127.0.0.1>53550>` + public_key + `" + ] + ` + let basic_peer_script_file = "demos_peers" + fs.rmSync(basic_peer_script_file) + fs.writeFileSync(basic_peer_script_file, basic_peer_script) +} diff --git a/src/utilities/sharedState.ts b/src/utilities/sharedState.ts index 297af173d..1e30c255f 100644 --- a/src/utilities/sharedState.ts +++ b/src/utilities/sharedState.ts @@ -13,6 +13,8 @@ dotenv.config({ path: "../../.commons" }) export default class sharedState { private static instance: sharedState + block_time: number = 10 // TODO Get it from the genesis (or see Consensus module) + currentTimestamp: number = 0 lastTimestamp: number = 0 diff --git a/tests/chains/_template.test.ts b/tests/chains/_template.test.ts deleted file mode 100644 index fbc8cd735..000000000 --- a/tests/chains/_template.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -// INFO: Duplicate and rename this file to add a new XM test -// import { chainProviders } from "sdk/localsdk/multichain/configs/chainProviders" -// import { CHAIN } from "sdk/localsdk/multichain" - -describe(" CHAIN TESTS", () => { - // TODO: Set correct instance type - let instance: any - - beforeAll(async () => { - // INFO: Connect the RPC and Wallet here - // instance = await CHAIN.create(chainProviders.ibc.testnet); - await instance.connect() - await instance.connectWallet("") - - expect(instance.connected).toBe(true) - }) - - afterAll(async () => { - // INFO: Disconnect from the RPC here - // NOTE: Not needed for non web socket RPCs - await instance.disconnect() - }) - - test("preparePay returns a signed tx", async () => { - // TODO: Test code here - }) - test("A tx is signed with the ledger nonce", async () => { - // TODO: Test code here - }) - - test("Transactions are signed with increasing nonces", async () => { - // TODO: Test code here - }) - test("Transactions are signed in order of appearance", async () => { - // TODO: Test code here - }) -}) diff --git a/tests/chains/ibc.test.ts b/tests/chains/ibc.test.ts deleted file mode 100644 index 1e6ee2cbd..000000000 --- a/tests/chains/ibc.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { defaultRegistryTypes, GasPrice } from "@cosmjs/stargate" -import { decodeTxRaw, Registry } from "@cosmjs/proto-signing" - -import ibcProviders from "sdk/localsdk/multichain/configs/ibcProviders" -import { IBC } from "sdk/localsdk/multichain" -import { getSampleTranfers, verifyNumberOrder } from "../utils" -import { wallets } from "../utils/wallets" - -describe("IBC CHAIN TESTS", () => { - let instance: IBC - const chain_prefix = "cosmos" - const token_denom = "uatom" - const payment_options = { denom: token_denom } - - beforeAll(async () => { - instance = await IBC.create(ibcProviders.cosmos.testnet) - await instance.connectWallet(wallets.ibc.wallet, { - prefix: chain_prefix, - gasPrice: "0.012uatom", - }) - - expect(instance.connected).toBe(true) - }) - - test("preparePay returns a signed tx", async () => { - const address = instance.getAddress() - const tx_bytes = await instance.pay( - address, - "1", - payment_options, - ) - - // INFO: Decode the bytes to get the raw tx - // LINK: https://cosmos.github.io/cosmjs/latest/proto-signing/modules.html#decodeTxRaw - // Ctrl + click decodeTxRaw to see the decoded raw Tx interface - const raw_tx = decodeTxRaw(tx_bytes) - expect(raw_tx.signatures.length == 1).toBe(true) - }) - - test("A tx is signed with the ledger nonce", async () => { - const address = instance.getAddress() - - // INFO: Get the current ledger sequence - const ledger_sequence = (await instance.provider.getAccount(address)) - ?.sequence - - // INFO: Sign and decode the tx - const tx_bytes = await instance.pay( - address, - "1", - payment_options, - ) - const raw_tx = decodeTxRaw(tx_bytes) - - // INFO: Compare the sequences - const tx_sequence = Number(raw_tx.authInfo.signerInfos[0].sequence) - expect(tx_sequence).toEqual(ledger_sequence) - }) - - test("Transactions are signed with increasing nonces", async () => { - const address = instance.getAddress() - const payments = getSampleTranfers(address) - const signed_txs = await instance.multiPay(payments, payment_options) - - // INFO: Get a list of objects containing the sequences - const signer_infos = signed_txs.map(tx_bytes => { - const tx = decodeTxRaw(tx_bytes) - return tx.authInfo.signerInfos[0] - }) - - const is_sorted = verifyNumberOrder(signer_infos, "sequence") - expect(is_sorted).toBe(true) - }) - - test("Transactions are signed in order of appearance", async () => { - const address = instance.getAddress() - const payments = getSampleTranfers(address) - const signed_txs = await instance.multiPay(payments, payment_options) - - const amounts = signed_txs.map(tx_bytes => { - const tx = decodeTxRaw(tx_bytes) - - // INFO: Decode the message to get the amount - // LINK: https://cosmos.github.io/cosmjs/latest/proto-signing/classes/Registry.html - const registry = new Registry(defaultRegistryTypes) - const message = registry.decode(tx.body.messages[0]) - return message["amount"][0] - }) - - const is_sorted = verifyNumberOrder(amounts, "amount") - expect(is_sorted).toBe(true) - }) -}) diff --git a/tests/chains/index.ts b/tests/chains/index.ts deleted file mode 100644 index 815cbd053..000000000 --- a/tests/chains/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -describe("GENERIC CHAIN TESTS", () => { - test("Hi", () => { - expect(1).toBe(1) - }) - - test.todo("Chain has a valid name") - test.todo("Chain connects to the RPC") - test.todo("On connect failure, .connected is false") - test.todo("On disconnect, provider is reset") -}) \ No newline at end of file diff --git a/tests/utils/index.ts b/tests/utils/index.ts deleted file mode 100644 index 330750bff..000000000 --- a/tests/utils/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { IPayOptions } from "sdk/localsdk/multichain/types/transfers"; - -/** - * Verify that keys in a list of items appear in ascending order - * @param items The list of items - * @param key The key to compare - * @returns True if the list is sorted, false otherwise - */ -export function verifyNumberOrder(items: any[], key: string) { - // INFO: Verify that each number is greater than the previous one - return items.every((num, i) => { - const current = Number(num[key]); - const prev = Number(items[i - 1]?.[key]); - return i === 0 || current > prev; - }); -} - -/** - * Generate a list of transaction params for testing - * @param address The address to send the transactions to - * @returns A list of transaction params ready to be passed to preparePays - */ -export function getSampleTranfers(address: string, length: number = 10): IPayOptions[] { - return Array.from({ length }, (_, i) => { - return { - address, - // INFO: Amount is passed to preparePays as a string - amount: `${i + 1}` - }; - }); -} diff --git a/tests/utils/wallets.ts b/tests/utils/wallets.ts deleted file mode 100644 index f06349df0..000000000 --- a/tests/utils/wallets.ts +++ /dev/null @@ -1,43 +0,0 @@ -// INFO: These are testnet wallets for each chain -export const wallets = { - egld: { - wallet: JSON.stringify({ - version: 4, - id: 'c98cefdb-5ea0-404b-96b5-54391fe7cd68', - kind: 'mnemonic', - crypto: { - ciphertext: - '54067052dd60c0fc270329c5e3facfd0d16a5fe618a0ade72eb5797ca529b7b8808439d7d595b1c052e9684681efcb846e373b7f79fa2b6364618bffbc4afe50f20a510f00f7971e05516353ba225fcfe0a5d04d1995fc9e02ab242d60df838acfe59b5140d89adb00f88c121ad1a3cac3809d06bd025fad30fb3035374e8a80b20fb72d777b7b59182c75203f73b49265d8558b003814ea68dced49113d406216', - cipherparams: { iv: '01710d9d92c8b45f2e02079ff3e2b58d' }, - cipher: 'aes-128-ctr', - kdf: 'scrypt', - kdfparams: { - dklen: 32, - salt: '10f68326bdd2755058dda807f9b3347e01b037ae65b439f0b0df1744fe4ff9d2', - n: 4096, - r: 8, - p: 1 - }, - mac: 'dd63b26356692e206f25b7a034babe5a00705bdc7e3acaa2993733f930052489' - } - }), - password: 'e9r#PNK7pB#?f39A' - }, - xrpl: { - wallet: 'sEdToEm3WLYXRmhvs62pd1BuLfxF9WG' - }, - evm: { - // INFO: Sepolia testnet wallet 👇 - wallet: 'e0a00e307c21850cde41b18bae307a492c471b463b60ce5b631fdb80503b23f7' - }, - ibc: { - // INFO: Cosmos chain Id: "theta-testnet-001" - wallet: 'stumble august fancy affair device feed cruise brown dream section fit lift' - } -}; - -export const addresses = { - // Alt account - // INFO: XRPL doesn't accept transfers to own account - xrpl: 'rE57JVcMmoNq6yNbBbiJrCY9sMr4HWiqwf' -}; diff --git a/tsconfig.json b/tsconfig.json index 78bc63c72..cd1fef0c5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,29 +1,31 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "esModuleInterop": true, - "moduleResolution": "node", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "sourceMap": true, - "sourceRoot": "/", - "inlineSources": true, - "outDir": "./dist", - "baseUrl": "./", - "types": ["jest", "node"], - "incremental": true, - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - - /* Linting */ - "skipLibCheck": true, - "strict": true, - "noFallthroughCasesInSwitch": true, - "forceConsistentCasingInFileNames": true - } -} + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "esModuleInterop": true, + "moduleResolution": "Bundler", + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "sourceMap": true, + "sourceRoot": "/", + "inlineSources": true, + "outDir": "./dist", + "baseUrl": "./", + "types": [ + "jest", + "node" + ], + "incremental": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + /* Linting */ + "skipLibCheck": true, + "strict": true, + "noFallthroughCasesInSwitch": true, + "forceConsistentCasingInFileNames": true + } +} \ No newline at end of file