diff --git a/.eslintignore b/.eslintignore index 32b20f5f6..6fe0c7a2e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ *.js dist/ -.eslintrc \ No newline at end of file +.eslintrc + diff --git a/docs/Ocean Node.postman_collection.json b/docs/Ocean Node.postman_collection.json index c33a605b5..ce8dde7e5 100644 --- a/docs/Ocean Node.postman_collection.json +++ b/docs/Ocean Node.postman_collection.json @@ -41,9 +41,28 @@ "name": "8000 - getPeer", "request": { "method": "GET", - "header": [] - }, - "response": [] + "header": [], + "url": { + "raw": "http://127.0.0.1:8000/getP2pPeer?peerId=16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL", + "protocol": "http", + "host": [ + "127", + "0", + "0", + "1" + ], + "port": "8000", + "path": [ + "getP2pPeer" + ], + "query": [ + { + "key": "peerId", + "value": "16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL" + } + ] + } + } }, { "name": "8000 - advertiseDid", @@ -106,7 +125,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"command\":\"download\",\n \"node\": \"16Uiu2HAkxiemC25d2iZWTkVRQmZr9L9h3RNGnhiUWXEonmsPEC8y\"\n}" + "raw": "{\n \"command\":\"downloadURL\",\n \"node\": \"16Uiu2HAkxiemC25d2iZWTkVRQmZr9L9h3RNGnhiUWXEonmsPEC8y\",\n \"url\": \"http://example.com\",\n \"aes_encrypted_key\": \"0x1234567890abcdef\"\n}" }, "url": { "raw": "http://127.0.0.1:8000/directCommand", @@ -138,7 +157,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"command\":\"download\",\n \"node\": \"16Uiu2HAkvfXgYiFhsHRJvcdtmMs3aopgoRphb5xnXMh3dxCRuuX\"\n}" + "raw": "{\n \"command\":\"downloadURL\",\n \"node\": \"16Uiu2HAkvfXgYiFhsHRJvcdtmMs3aopgoRphb5xnXMh3dxCRuuX\",\n \"url\": \"http://example.com\",\n \"aes_encrypted_key\": \"0x1234567890abcdef\"\n}" }, "url": { "raw": "http://127.0.0.1:8001/directCommand", diff --git a/package-lock.json b/package-lock.json index d8aa10fa2..25646be88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,9 @@ "@multiformats/multiaddr": "^10.2.0", "@types/lodash.clonedeep": "^4.5.7", "aegir": "^37.3.0", + "axios": "^1.5.1", "delay": "^5.0.0", + "eth-crypto": "^2.6.0", "express": "^4.18.2", "hyperdiff": "^2.0.16", "it-pipe": "^3.0.1", @@ -1996,6 +1998,725 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@ethereumjs/common": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.5.tgz", + "integrity": "sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA==", + "dependencies": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.5" + } + }, + "node_modules/@ethereumjs/tx": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz", + "integrity": "sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw==", + "dependencies": { + "@ethereumjs/common": "^2.6.4", + "ethereumjs-util": "^7.1.5" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bignumber/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, "node_modules/@fastify/busboy": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", @@ -3948,6 +4669,14 @@ "yarn": ">=1.9.4" } }, + "node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.4", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz", @@ -4162,6 +4891,14 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.1.tgz", "integrity": "sha512-3YmXzzPAdOTVljVMkTMBdBEvlOLg2cDQaDhnnhT3nT9uDbnJzjWhKlzb+desT12Y7tGqaN6d+AbozcKzyL36Ng==" }, + "node_modules/@types/pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4HCoGwR3221nOc7G0Z/6KgTNGgaaFGkbGrtUJsB+zlKX2LBVjFHHIUkieMBgHHXgBH5Gq6dZHJKdBYdtlhBQvw==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.9", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", @@ -4185,6 +4922,14 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.1.tgz", "integrity": "sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g==" }, + "node_modules/@types/secp256k1": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.5.tgz", + "integrity": "sha512-aIonTBMErtE3T9MxDvTZRzcrT/mCqpEZBw3CCY/i+oG9n57N/+7obBkhFgavUAIrX21bU0LHg1XRgtaLdelBhA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", @@ -5320,6 +6065,11 @@ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, + "node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -5728,6 +6478,11 @@ "has-symbols": "^1.0.3" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -5747,6 +6502,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", + "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.6", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz", @@ -5801,6 +6566,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -5820,6 +6593,11 @@ } ] }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -5851,6 +6629,24 @@ "node": ">=8" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw==", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -5874,6 +6670,16 @@ "node": ">= 6" } }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -6080,11 +6886,29 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/browserslist": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", @@ -6116,6 +6940,24 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -6152,6 +6994,11 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, "node_modules/builtins": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", @@ -6574,6 +7421,15 @@ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -6813,8 +7669,19 @@ "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, "node_modules/commander": { @@ -7222,6 +8089,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -7744,6 +8647,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -8373,6 +9284,20 @@ "ignored": "bin/ignored" } }, + "node_modules/drbg.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", + "integrity": "sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g==", + "optional": true, + "dependencies": { + "browserify-aes": "^1.0.6", + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -8386,6 +9311,52 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/eccrypto": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/eccrypto/-/eccrypto-1.1.6.tgz", + "integrity": "sha512-d78ivVEzu7Tn0ZphUUaL43+jVPKTMPFGtmgtz1D0LrFn7cY3K8CdrvibuLz2AAkHBLKZtR8DMbB2ukRYFk987A==", + "hasInstallScript": true, + "dependencies": { + "acorn": "7.1.1", + "elliptic": "6.5.4", + "es6-promise": "4.2.8", + "nan": "2.14.0" + }, + "optionalDependencies": { + "secp256k1": "3.7.1" + } + }, + "node_modules/eccrypto/node_modules/acorn": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", + "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/eccrypto/node_modules/secp256k1": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.1.tgz", + "integrity": "sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "bindings": "^1.5.0", + "bip66": "^1.1.5", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", + "drbg.js": "^1.0.1", + "elliptic": "^6.4.1", + "nan": "^2.14.0", + "safe-buffer": "^5.1.2" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -8672,6 +9643,20 @@ "node": ">=0.10.0" } }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/email-addresses": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", @@ -9024,6 +10009,11 @@ "es6-symbol": "^3.1.1" } }, + "node_modules/es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, "node_modules/es6-symbol": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", @@ -10314,6 +11304,142 @@ "node": ">= 0.6" } }, + "node_modules/eth-crypto": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eth-crypto/-/eth-crypto-2.6.0.tgz", + "integrity": "sha512-GCX4ffFYRUGgnuWR5qxcZIRQJ1KEqPFiyXU9yVy7s6dtXIMlUXZQ2h+5ID6rFaOHWbpJbjfkC6YdhwtwRYCnug==", + "dependencies": { + "@babel/runtime": "7.20.13", + "@ethereumjs/tx": "3.5.2", + "@types/bn.js": "5.1.1", + "eccrypto": "1.1.6", + "ethereumjs-util": "7.1.5", + "ethers": "5.7.2", + "secp256k1": "5.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/pubkey" + } + }, + "node_modules/eth-crypto/node_modules/@babel/runtime": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/eth-crypto/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereum-cryptography/node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethereumjs-util/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, "node_modules/event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -10341,6 +11467,15 @@ "node": ">=0.8.x" } }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, "node_modules/execa": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-6.1.0.tgz", @@ -10583,6 +11718,12 @@ "moment": "^2.29.1" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "node_modules/filename-reserved-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", @@ -10738,6 +11879,25 @@ "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -10758,6 +11918,19 @@ "node": ">=8.0.0" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/form-data-encoder": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", @@ -11454,6 +12627,60 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hash-base/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasha": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz", @@ -11512,6 +12739,16 @@ "he": "bin/he" } }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/hook-std": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", @@ -12871,6 +14108,11 @@ "node": ">= 0.6.0" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -13012,6 +14254,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/keccak/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -13843,6 +15112,16 @@ "node": ">=6" } }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/mdast-util-find-and-replace": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz", @@ -14875,6 +16154,16 @@ "node": ">=4" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -15250,6 +16539,11 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, + "node_modules/nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, "node_modules/nanoid": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", @@ -15326,6 +16620,11 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, "node_modules/node-emoji": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", @@ -15361,6 +16660,16 @@ "node": ">= 6.13.0" } }, + "node_modules/node-gyp-build": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", + "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -19234,6 +20543,21 @@ "node": "*" } }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -20455,6 +21779,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -21512,6 +22841,31 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/rlp/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -21764,6 +23118,30 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/secp256k1": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz", + "integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/secp256k1/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, "node_modules/semantic-release": { "version": "19.0.5", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-19.0.5.tgz", @@ -22673,11 +24051,28 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index 5e454a8ed..5946b555e 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "npm run clean && npm run build:tsc", "build:swagger": "tsoa spec", "build:tsc": "tsc --sourceMap", + "client": "mkdir -p ./dist/helpers/scripts/output && node dist/helpers/scripts/clientExample.js", "clean": "rm -rf ./dist/ ./doc/ ./.nyc_output", "start": "node --trace-warnings --experimental-specifier-resolution=node dist/index.js", "lint": "eslint --ignore-path .gitignore --ext .ts,.tsx . && npm run type-check", @@ -36,7 +37,9 @@ "@multiformats/multiaddr": "^10.2.0", "@types/lodash.clonedeep": "^4.5.7", "aegir": "^37.3.0", + "axios": "^1.5.1", "delay": "^5.0.0", + "eth-crypto": "^2.6.0", "express": "^4.18.2", "hyperdiff": "^2.0.16", "it-pipe": "^3.0.1", diff --git a/src/components/P2P/downloadHandler.ts b/src/components/P2P/downloadHandler.ts new file mode 100644 index 000000000..1d49d470f --- /dev/null +++ b/src/components/P2P/downloadHandler.ts @@ -0,0 +1,109 @@ +import crypto from 'crypto' +import { DownloadCommand } from '../../utils/constants.js' +import { OceanNodeConfig, P2PCommandResponse } from '../../@types' +import fs from 'fs' +import { P2P_CONSOLE_LOGGER } from '../P2P/index.js' +import * as ethCrypto from 'eth-crypto' +import axios from 'axios' +import { GENERIC_EMOJIS, LOG_LEVELS_STR } from '../../utils/logging/Logger.js' +import { getConfig } from '../../utils/config.js' +export const FILE_ENCRYPTION_ALGORITHM = 'aes-256-cbc' + +/** + * Get the file + * @param fileURL the location of the file + */ +async function getFileFromURL(fileURL: string): Promise { + const response = await axios({ + method: 'get', + url: fileURL, + responseType: 'stream' + }) + + return response.data +} +// No encryption here yet +export async function handleDownloadURLCommand( + task: DownloadCommand +): Promise { + const encryptFile = !!task.aes_encrypted_key + P2P_CONSOLE_LOGGER.logMessage( + 'DownloadCommand requires file encryption? ' + encryptFile, + true + ) + + try { + const inputStream = task.url.startsWith('http') + ? await getFileFromURL(task.url) // remote url + : fs.createReadStream(task.url) // local file + + if (encryptFile) { + // we parse the string into the object again + const encryptedObject = ethCrypto.cipher.parse(task.aes_encrypted_key) + // get the key from configuration + const config: OceanNodeConfig = await getConfig() + const nodePrivateKey = Buffer.from(config.keys.privateKey).toString('hex') + const decrypted = await ethCrypto.decryptWithPrivateKey( + nodePrivateKey, + encryptedObject + ) + const decryptedPayload = JSON.parse(decrypted) + // check signature + // const senderAddress = ethCrypto.recover( + // decryptedPayload.signature, + // ethCrypto.hash.keccak256(decryptedPayload.message) + // ) + // Optional, we can also validate the original address of the sender (the client that created the message) + // this could be part of the /directCommand payload for instance + // console.log( + // 'Got message from ' + senderAddress + ' secrets: ' + decryptedPayload.message + // ) + const secrets = JSON.parse(decryptedPayload.message) + + const cipher = crypto + .createCipheriv( + FILE_ENCRYPTION_ALGORITHM, + Buffer.from(secrets.key, 'hex'), + Buffer.from(secrets.iv, 'hex') + ) + .setAutoPadding(true) + + return { + stream: inputStream.pipe(cipher), + status: { + httpStatus: 200, + headers: { + 'Content-Disposition': "attachment; filename='syslog'", // TODO: the filename must come from somewhere else? + 'Content-Type': 'application/octet-stream', + 'Content-Encoding': 'aesgcm', + 'Transfer-Encoding': 'chunked' + } + } + } + } else { + // Download request is not using encryption! + return { + stream: inputStream, + status: { + httpStatus: 200, + headers: { + 'Content-Disposition': "attachment; filename='syslog'", + 'Content-Type': 'application/octet-stream', + 'Transfer-Encoding': 'chunked' + } + } + } + } + } catch (err) { + P2P_CONSOLE_LOGGER.logMessageWithEmoji( + 'Failure executing downloadURL task: ' + err.message, + true, + GENERIC_EMOJIS.EMOJI_CROSS_MARK, + LOG_LEVELS_STR.LEVEl_ERROR + ) + return { + stream: null, + status: { httpStatus: 501, error: 'Unknown error: ' + err.message } + } + } +} diff --git a/src/components/P2P/handleProtocolCommands.ts b/src/components/P2P/handleProtocolCommands.ts index 35ce31fd1..c870ab2df 100644 --- a/src/components/P2P/handleProtocolCommands.ts +++ b/src/components/P2P/handleProtocolCommands.ts @@ -6,6 +6,11 @@ import StreamConcat from 'stream-concat' // export function handleProtocolCommands (sourceStream:any,sinkStream:any) { import * as fs from 'fs' +import { handleDownloadURLCommand } from './downloadHandler.js' +import { PROTOCOL_COMMANDS } from '../../utils/constants.js' +import { P2PCommandResponse } from '../../@types/OceanNode' + +import { P2P_CONSOLE_LOGGER } from './index.js' class ReadableString extends Readable { private sent = false @@ -25,8 +30,12 @@ class ReadableString extends Readable { } export async function handleProtocolCommands(connection: any) { - console.log('Incoming connection from peer ' + connection.connection.remotePeer) - console.log('Using ' + connection.connection.remoteAddr) + P2P_CONSOLE_LOGGER.logMessage( + 'Incoming connection from peer ' + connection.connection.remotePeer, + true + ) + P2P_CONSOLE_LOGGER.logMessage('Using ' + connection.connection.remoteAddr, true) + let status = null const isError = false let task @@ -45,32 +54,74 @@ export async function handleProtocolCommands(connection: any) { } break } - console.log('Performing task') - console.log(task) + P2P_CONSOLE_LOGGER.logMessage('Performing task: ' + JSON.stringify(task), true) + + let response: P2PCommandResponse = null + try { + switch (task.command) { + case PROTOCOL_COMMANDS.ECHO: + status = { httpStatus: 200 } + break + case PROTOCOL_COMMANDS.DOWNLOAD_URL: + response = await handleDownloadURLCommand(task) + // eslint-disable-next-line prefer-destructuring + status = response.status + sendStream = response.stream + break + default: + status = { httpStatus: 501, error: 'Unknown command' } + break + } + statusStream = new ReadableString(JSON.stringify(status)) + if (sendStream == null) pipe(statusStream, connection.stream.sink) + else { + const combinedStream = new StreamConcat([statusStream, sendStream], { + highWaterMark: JSON.stringify(status).length // important for reading chunks correctly on sink! + }) + pipe(combinedStream, connection.stream.sink) + } + } catch (err) { + console.log('error:') + console.log(err) + } +} +/** + * Use this method to direct calls to the node as node cannot dial into itself + * @param message command message + * @param sink transform function + */ +export async function handleDirectProtocolCommand(message: string, sink: any) { + P2P_CONSOLE_LOGGER.logMessage('Incoming direct command for peer self', true) + let status = null + const task = JSON.parse(message) + // let statusStream + let sendStream = null + let response: P2PCommandResponse = null + + P2P_CONSOLE_LOGGER.logMessage('Performing task: ' + JSON.stringify(task), true) switch (task.command) { - case 'echo': + case PROTOCOL_COMMANDS.ECHO: status = { httpStatus: 200 } - sendStream = connection.stream.source break - case 'download': - sendStream = fs.createReadStream('/var/log/syslog') - // sendStream=fs.createReadStream("/etc/hostname") - status = { - httpStatus: 200, - headers: { - 'Content-Disposition': "attachment; filename='syslog'", - 'Content-Type': 'application/text' - } - } + case PROTOCOL_COMMANDS.DOWNLOAD_URL: + response = await handleDownloadURLCommand(task) + // eslint-disable-next-line prefer-destructuring + status = response.status + sendStream = response.stream break default: status = { httpStatus: 501, error: 'Unknown command' } break } - statusStream = new ReadableString(JSON.stringify(status)) - if (sendStream == null) pipe(statusStream, connection.stream.sink) - else { - const combinedStream = new StreamConcat([statusStream, sendStream]) - pipe(combinedStream, connection.stream.sink) + + const statusStream = new ReadableString(JSON.stringify(status)) + if (sendStream == null) { + pipe(statusStream, sink) + } else { + const combinedStream = new StreamConcat([statusStream, sendStream], { + highWaterMark: JSON.stringify(status).length + // the size of the buffer is important! + }) + pipe(combinedStream, sink) } } diff --git a/src/components/P2P/index.ts b/src/components/P2P/index.ts index 3e7d34f15..d786eafd7 100644 --- a/src/components/P2P/index.ts +++ b/src/components/P2P/index.ts @@ -14,7 +14,8 @@ import { handlePeerJoined, handlePeerLeft, handleSubscriptionCHange, - handleProtocolCommands + handleProtocolCommands, + handleDirectProtocolCommand } from './handlers.js' // import { encoding } from './connection' @@ -47,11 +48,11 @@ import { gossipsub } from '@chainsafe/libp2p-gossipsub' import { cidFromRawString } from '../../utils/index.js' import { Stream, Transform } from 'stream' import { Database } from '../database' -import { AutoDial } from 'libp2p/dist/src/connection-manager/auto-dial' import { OceanNodeConfig } from '../../@types/OceanNode' import { CustomNodeLogger, + GENERIC_EMOJIS, LOGGER_MODULE_NAMES, LOG_LEVELS_STR, defaultConsoleTransport, @@ -277,7 +278,26 @@ export class OceanP2P extends EventEmitter { async getPeerDetails(peerName: string) { try { const peerId = peerIdFromString(peerName) + // Example: for ID 16Uiu2HAkuYfgjXoGcSSLSpRPD6XtUgV71t5RqmTmcqdbmrWY9MJo + // Buffer.from(this._config.keys.publicKey).toString('hex') => 0201cabbabef1cc85218fa2d5bbadfb3425dfc091b311a33e6d9be26f6dcb94668 + // Buffer.from(peerId.publicKey).toString('hex') => 080212210201cabbabef1cc85218fa2d5bbadfb3425dfc091b311a33e6d9be26f6dcb94668 + // 08021221 = > extra 4 bytes at the beginning, but they are important for later + // UPDATE: no need to slice 4 bytes here, actually we need those on client side to verify the node id and perform the encryption of the keys + iv + // See config.ts => getPeerIdFromPrivateKey() + + const pubKey = Buffer.from(peerId.publicKey).toString('hex') // no need to do .subarray(4).toString('hex') const peer = await this._libp2p.peerStore.get(peerId) + + // write the publicKey as well + peer.publicKey = pubKey + // Note: this is a 'compressed' version of the publicKey, we need to decompress it on client side (not working with bellow attempts) + // otherwise the encryption will fail due to public key size mismatch + + // taken from '@libp2p/crypto/keys/secp256k1' decompressPublicKey (cannot import module/function) + // const decompressedKey = secp.ProjectivePoint.fromHex(key.public.bytes).toRawBytes(false) + // Buffer.from(decompressedKey).toString('hex') + // in any case is not working (it crashes here) + return peer } catch (e) { return null @@ -289,7 +309,8 @@ export class OceanP2P extends EventEmitter { message: string, sink: any ): Promise { - console.log('Executing on node ' + peerName + ' task: ' + message) + P2P_CONSOLE_LOGGER.logMessage('SendTo() node ' + peerName + ' task: ' + message, true) + const status: P2PCommandResponse = { status: { httpStatus: 200, error: '' }, stream: null @@ -300,11 +321,19 @@ export class OceanP2P extends EventEmitter { peerId = peerIdFromString(peerName) peer = await this._libp2p.peerStore.get(peerId) } catch (e) { + P2P_CONSOLE_LOGGER.logMessageWithEmoji( + 'Invalid peer (for id): ' + peerId, + true, + GENERIC_EMOJIS.EMOJI_CROSS_MARK, + LOG_LEVELS_STR.LEVEl_ERROR + ) status.status.httpStatus = 404 status.status.error = 'Invalid peer' return status } + let stream + // dial/connect to the target node try { // stream= await this._libp2p.dialProtocol(peer, this._protocol) @@ -329,6 +358,23 @@ export class OceanP2P extends EventEmitter { return status } + // when the target is this node + async sendToSelf(message: string, sink: any): Promise { + const status: P2PCommandResponse = { + status: { httpStatus: 200, error: '' }, + stream: null + } + // direct message to self + // create a writable stream + // const outputStream = new Stream.Writable() + status.stream = new Stream.Writable() + + // read from input stream to output one and move on + await handleDirectProtocolCommand(message, sink) + + return status + } + async _pollPeers() { const node = this._libp2p const newPeers = (await node.services.pubsub.getSubscribers(this._topic)).sort() @@ -386,6 +432,15 @@ export class OceanP2P extends EventEmitter { } return peersFound } + + /** + * Is the message intended for this peer or we need to connect to another one? + * @param targetPeerID the target node id + * @returns true if the message is intended for this peer, false otherwise + */ + isTargetPeerSelf(targetPeerID: string): boolean { + return targetPeerID === this._config.keys.peerId.toString() + } } function encoding(message: any) { diff --git a/src/components/httpRoutes/commands.ts b/src/components/httpRoutes/commands.ts index bdbdba1fa..27911afe8 100644 --- a/src/components/httpRoutes/commands.ts +++ b/src/components/httpRoutes/commands.ts @@ -9,6 +9,8 @@ import { getDefaultLevel } from '../../utils/logging/Logger.js' +import { validateCommandAPIParameters } from './validateCommands.js' + export const broadcastCommandRoute = express.Router() // just use the default logger with default transports @@ -20,7 +22,7 @@ broadcastCommandRoute.post( express.urlencoded({ extended: true }), async (req: Request, res: Response): Promise => { if (!req.query.message) { - res.sendStatus(400) + res.status(400).send('Missing query parameter: "message" is mandatory') return } @@ -36,22 +38,32 @@ directCommandRoute.post( '/directCommand', express.json(), async (req: Request, res: Response): Promise => { - if (!req.body.command || !req.body.node) { - res.sendStatus(400) + const validate = validateCommandAPIParameters(req.body) + if (!validate.valid) { + // 'node' param is not mandatory for 'downloadURL' command for instance: + // https://github.com/oceanprotocol/ocean-node/issues/26 + // https://github.com/oceanprotocol/ocean-node/issues/38 + res.status(validate.status).send(validate.reason) return } + let isBinaryContent = false const sink = async function (source: any) { let first = true for await (const chunk of source) { if (first) { first = false try { - const str = uint8ArrayToString(chunk.subarray()) + const str = uint8ArrayToString(chunk.subarray()) // Obs: we need to specify the length of the subarrays const decoded = JSON.parse(str) + res.status(decoded.httpStatus) if ('headers' in decoded) { res.header(decoded.headers) + // when streaming binary data we cannot convert to plain string, specially if encrypted data + if (str.toLowerCase().includes('application/octet-stream')) { + isBinaryContent = true + } } if (decoded.httpStatus !== 200) { res.write(decoded.error) @@ -64,18 +76,34 @@ directCommandRoute.post( res.end() } } else { - const str = uint8ArrayToString(chunk.subarray()) - res.write(str) + if (isBinaryContent) { + // Binary content, could be encrypted + res.write(chunk.subarray()) + } else { + const str = uint8ArrayToString(chunk.subarray()) + res.write(str) + } } } res.end() } - const status: P2PCommandResponse = await req.oceanNode.node.sendTo( - req.body.node as string, - JSON.stringify(req.body), - sink - ) + logger.logMessage('Sending command : ' + JSON.stringify(req.body), true) + + let status: P2PCommandResponse = null + // send to this peer + if (!req.body.node || req.oceanNode.node.isTargetPeerSelf(req.body.node)) { + // send to this node + status = await req.oceanNode.node.sendToSelf(JSON.stringify(req.body), sink) + } else { + // send to another peer + status = await req.oceanNode.node.sendTo( + req.body.node as string, + JSON.stringify(req.body), + sink + ) + } + if (status.stream == null) { res.status(status.status.httpStatus) res.write(status.status.error) diff --git a/src/components/httpRoutes/validateCommands.ts b/src/components/httpRoutes/validateCommands.ts new file mode 100644 index 000000000..8fe06fe50 --- /dev/null +++ b/src/components/httpRoutes/validateCommands.ts @@ -0,0 +1,50 @@ +import { SUPPORTED_PROTOCOL_COMMANDS, PROTOCOL_COMMANDS } from '../../utils/constants.js' + +export type ValidateParams = { + valid: boolean + reason?: string + status?: number +} +// TODO add others when we add support +export function validateCommandAPIParameters(requestBody: any): ValidateParams { + // eslint-disable-next-line prefer-destructuring + const command: string = requestBody.command as string + + if (!command) { + return { + valid: false, + reason: 'Invalid Request: "command" is mandatory!', + status: 400 + } + } + if (SUPPORTED_PROTOCOL_COMMANDS.includes(command)) { + // downloadURL + if (command === PROTOCOL_COMMANDS.DOWNLOAD_URL) { + // only mandatory is the url + if (!requestBody.url) { + return { + valid: false, + reason: 'Missing required parameter: "url"', + status: 400 + } + } + return { + valid: true + } + // echo + } else if (command === PROTOCOL_COMMANDS.ECHO) { + // nothing special with this one + return { + valid: true + } + } + return { + valid: true + } + } + return { + valid: false, + reason: `Invalid or unrecognized command: "${command}"`, + status: 400 + } +} diff --git a/src/helpers/scripts/clientExample.ts b/src/helpers/scripts/clientExample.ts new file mode 100644 index 000000000..4661cae6e --- /dev/null +++ b/src/helpers/scripts/clientExample.ts @@ -0,0 +1,425 @@ +import axios from 'axios' +import fs from 'fs' + +import crypto from 'crypto' +import * as ethCrypto from 'eth-crypto' + +import * as Digest from 'multiformats/hashes/digest' +import { identity } from 'multiformats/hashes/identity' +import { base58btc } from 'multiformats/bases/base58' +// import { Wallet, ethers } from 'ethers' + +import pkg from 'secp256k1' +import { DownloadCommand } from '../../utils/constants.js' + +// Replace with any other file, works with a local path or URL +// '/var/log/syslog' +const EXAMPLE_FILE = + 'https://ia800909.us.archive.org/13/items/CC_1917_04_16_TheCure/CC_1917_04_16_TheCure_512kb.mp4' +// AES encryption +const FILE_ENCRYPTION_ALGORITHM = 'aes-256-cbc' +const { publicKeyConvert } = pkg +// Decrypt the file after receiving it encrypted? +const DECRYPT_AFTER_RECEIVING_FILE = true + +// ######################################## +/** README/HOWTO + * + * How to run these examples: + * Open a terminal and setup node A (export port and private key) - Node A terminal + * Open a second terminal and setup node B (export port and private key) - Node B terminal + * Open a third terminal - this Client script + * 1) npm run start on terminal A + * 2) npm run start on terminal B + * 3) npm run client + * OBS: when making changes on the client or nodes, we always need to stop them, rebuild npm run build and restart them + * Have fun! + */ +// ######################################## +type P2PNode = { + node_id: string + port: number +} + +// Examples; take the node's private key from your local configuration +// and take the node public key (not needed) from the getP2pPeer API (node details) +// take the port from your local configuration as well +const nodeA: P2PNode = { + node_id: '16Uiu2HAkuYfgjXoGcSSLSpRPD6XtUgV71t5RqmTmcqdbmrWY9MJo', + port: 8000 +} + +const nodeB: P2PNode = { + node_id: '16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL', + port: 8001 +} + +type PeerDetails = { + nodeId: string + publicKey: string + valid: boolean +} + +type FileSecrets = { + privateKey: string // the private key used to decrypt the file + initVector: string // the initialization vector + encryptedKeyAndIV: string // the encrypted private key and iv, used for encrypting the file on Node side, as JSON string +} +// receive the node public key from the peer details, already validated before +/** + * + * @param nodePublicKey the node public key + * @param nodeBPrivateKey the node private key + * @returns + */ +async function createKeyPairForFileEncryption( + nodePublicKey: string +): Promise { + // We could also use Wallet variant + // let wallet = ethers.Wallet.createRandom(); + + // console.log('wallet private: ', wallet.privateKey); + // console.log('wallet public: ', wallet.publicKey) + // console.log('wallet address: ', wallet.address) + + const pair = ethCrypto.createIdentity() + console.log('private key:', pair.privateKey) + console.log('public key:', pair.publicKey) + console.log('address:', pair.address) + + const keyForAESFileEncryption = crypto.randomBytes(32) + const initializationVector = crypto.randomBytes(16) + console.log('keyForAESFileEncryption:', keyForAESFileEncryption.toString('hex')) + + console.log('initializationVector:', initializationVector.toString('hex')) + + // Symmetric key generation, for file encryption + const keySecrets = { + key: keyForAESFileEncryption.toString('hex'), + iv: initializationVector.toString('hex') + } + + const { privateKey } = pair // '0x3634cc4a3d2694a1186a7ce545f149e022eea103cc254d18d08675104bb4b5ac' + const secretMessage = JSON.stringify(keySecrets) // '92ecf27434c6140a5bdc6d62d972d5348e50aa6646db3a09e30ef4acc8616bef' + + console.log('Secret message JSON (before encryption): ' + secretMessage) + + const signature = ethCrypto.sign(privateKey, ethCrypto.hash.keccak256(secretMessage)) + const payload = { + message: secretMessage, + signature + } + + console.log('signature: ', signature) + console.log('payload: ', payload) + + const encryptedPayload = await ethCrypto.encryptWithPublicKey( + nodePublicKey, // by encrypting with target Node publicKey, only target Node can decrypt the payload with his privateKey + JSON.stringify(payload) // we have to stringify the payload before we can encrypt it + ) + + // we convert the object into a smaller string-representation + const encryptedAESKeyAndIV = ethCrypto.cipher.stringify(encryptedPayload) + + console.log('encrypted string/key:', encryptedAESKeyAndIV) + + // we parse the string into the object again + // const encryptedObject = ethCrypto.cipher.parse(encryptedAESKeyAndIV) + + // const decrypted = await ethCrypto.decryptWithPrivateKey(nodePrivateKey, encryptedObject) + // const decryptedPayload = JSON.parse(decrypted) + + // // check signature + // const senderAddress = ethCrypto.recover( + // decryptedPayload.signature, + // ethCrypto.hash.keccak256(decryptedPayload.message) + // ) + + // console.log('Got message from ' + senderAddress + ': ' + decryptedPayload.message) + + return { + privateKey: keySecrets.key, // we need to pass it here because we're generating the key and IV inside the function + initVector: keySecrets.iv, // same + encryptedKeyAndIV: encryptedAESKeyAndIV + } +} +/** + * Decrypt the file stream + * @param inputStream input stream to decrypt + * @param outputStream output stream to write to + * @param usePadding use padding? + * @param privateKey the key to use + * @param initVect the initialization vector + * @returns true if successful + */ +async function decryptFileStream( + inputStream: any, + outputStream: any, + privateKey: string, + initVect: string +): Promise { + const decipher = crypto + .createDecipheriv( + FILE_ENCRYPTION_ALGORITHM, + Buffer.from(privateKey, 'hex'), + Buffer.from(initVect, 'hex') + ) + .setAutoPadding(true) + + // To see what is going on uncomment below + // decipher.on("data", chunk => { + // // console.log("Decrypting a file chunk of size: ", chunk.length) + // }) + + console.log('Decrypting key: ', privateKey) + console.log('Decrypting IV: ', initVect) + + return new Promise((resolve, reject) => { + // input => decryption => output + inputStream.pipe(decipher).pipe(outputStream) + + inputStream.on('end', () => { + resolve(true) + }) + + inputStream.on('error', (err: any) => { + reject(err) + }) + }) +} + +function testEchoCommand(): Promise { + return new Promise((resolve, reject) => { + axios({ + method: 'POST', + url: 'http://127.0.0.1:8000/directCommand', + responseType: 'stream', + data: { + command: 'echo', + // "node": "16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL", + // "16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL",//"16Uiu2HAkuYfgjXoGcSSLSpRPD6XtUgV71t5RqmTmcqdbmrWY9MJo", + url: 'http://example.com' + } + }) + .then(function (response: any) { + console.log('Got response from server...', response.status) + resolve(response.status === 200 ? 'OK' : 'NOK') + }) + .catch((err: any) => { + console.error('Error downloading....', err) + reject(err) + }) + }) +} + +async function testDownloadCommand( + exampleId: number, // this is just to append to file name for testing purposes + nodeHttpPort: number, + nodeId?: string, + fileSecrets?: FileSecrets +): Promise { + const payload: DownloadCommand = { + command: 'downloadURL', + // node: '16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL', // IF not present use own node + // own node A is: "16Uiu2HAkuYfgjXoGcSSLSpRPD6XtUgV71t5RqmTmcqdbmrWY9MJo", + // other node B is: 16Uiu2HAmQU8YmsACkFjkaFqEECLN3Csu6JgoU3hw9EsPmk7i9TFL + url: EXAMPLE_FILE // http://example.com' + // "aes_encrypted_key": encryptedAESKeyAndIV + } + + const useEncryption = fileSecrets && fileSecrets.encryptedKeyAndIV + + if (useEncryption) { + payload.aes_encrypted_key = fileSecrets.encryptedKeyAndIV + } + if (nodeId) { + payload.node = nodeId + } + + return new Promise((resolve, reject) => { + axios({ + method: 'POST', + url: `http://127.0.0.1:${nodeHttpPort}/directCommand`, + responseType: 'stream', + data: payload + }) + .then(function (response: any) { + // console.log('Got response from server...', response.data) + + const fileOutput = './dist/helpers/scripts/output/received_out_' + let suffix = '' + exampleId + if (useEncryption) { + suffix = suffix + (DECRYPT_AFTER_RECEIVING_FILE ? '.decoded' : '.encoded') + } + + // eslint-disable-next-line security/detect-non-literal-fs-filename + const out = fs.createWriteStream(fileOutput + suffix) + + if (useEncryption && DECRYPT_AFTER_RECEIVING_FILE) { + // decrypt the stream and store it decrypted already + // if we want to store it encrypted just do as bellow: + // response.data.pipe(fileOutput) + decryptFileStream( + response.data, + out, + fileSecrets.privateKey, + fileSecrets.initVector + ) + .then(() => { + console.log('File decoded successfully locally!') + console.log('File download complete') + resolve(fileOutput) + }) + .catch((err) => { + console.log('Error decrypting the file: ', err) + }) + } else { + // response.data.on('data', function (chunk: any) { + // console.log('Got chunk: ', chunk); + // }); + + out.on('close', function () { + console.log('Stream Ended! Saved file to path: ', fileOutput) + // will decode the file now + resolve(fileOutput) + }) + + // just write the file output + response.data.pipe(out) + } + }) + .catch((err: any) => { + console.error('Error downloading....', err) + reject(err) + }) + }) +} + +async function getPeerDetails(url: string, nodeId: string): Promise { + const response = await axios.get(url, { + params: { + peerId: nodeId + }, + headers: { + Accept: 'text/json' + } + }) + + const data = await response.data + console.log('Got data from server:', data) + // get the PeerDetails + const details: PeerDetails = { + nodeId: data.id, + publicKey: data.publicKey, + valid: false + } + + // get public key + const hexString: string = details.publicKey + // convert it to byte array + const publicKeyBytes: Uint8Array = Uint8Array.from(Buffer.from(hexString, 'hex')) + + // same hash that is done on peers to get a node identifier (they strip 1st byte) + const multihash: any = await Digest.create(identity.code, publicKeyBytes) + const generatedNodeId: string = base58btc.encode(multihash.bytes).slice(1) + console.log('generatedNodeId', generatedNodeId) + + // confirm that this public key originated this nodeId + const isNodeValid = generatedNodeId === details.nodeId + + console.log('isNodeValid:', isNodeValid) + + // update details info with the verification outcome + details.valid = isNodeValid + + // console.log('Got PeerDetails:', details) + + return details +} + +async function testEncryptionFlow( + exampleId: number, + nodeHttpPort: number, + targetNodeId: string +): Promise { + // get the peer details and validate them to check if public key matches nodeId + + const url = `http://127.0.0.1:${nodeHttpPort}/getP2pPeer` + console.log('url ', url) + console.log('target id ', targetNodeId) + const peerDetails = await getPeerDetails( + `http://127.0.0.1:${nodeHttpPort}/getP2pPeer`, + targetNodeId + ) + console.log('Got Peer details:', peerDetails) + if (peerDetails.valid) { + const compressedPublicKey = peerDetails.publicKey + console.log('Compressed public key:', compressedPublicKey) + const hexArray: Uint8Array = Uint8Array.from(Buffer.from(compressedPublicKey, 'hex')) + // console.log('hexArray:', hexArray) + // console.log('hexArray sliced:', Buffer.from(hexArray.slice(4)).toString('hex')) + const decompressedKeyArray: Uint8Array = publicKeyConvert(hexArray.slice(4), false) + const decompressedPublicKey = Buffer.from(decompressedKeyArray).toString('hex') + console.log('Decompressed public key:', decompressedPublicKey) + + // This DOES NOT WORK!! + // const decompressedPublicKeyWithEthers = ethers.utils.computePublicKey( + // hexArray.slice(4), + // true + // ) + // console.log( + // 'decompressed public key with ethers:', + // Buffer.from(decompressedPublicKeyWithEthers).toString('hex') + // ) + // get the AES key and IV encrypted with node public key + const secrets: FileSecrets = + await createKeyPairForFileEncryption(decompressedPublicKey) + + console.log('Will download now: ') + console.log('exampleId: ', exampleId) + console.log('nodeHttpPort: ', nodeHttpPort) + console.log('targetNodeId: ', targetNodeId) + + // send command to Node + await testDownloadCommand(exampleId, nodeHttpPort, targetNodeId, secrets) + } +} + +const status = await testEchoCommand() +console.log('Echo command status: ', status) + +// In the following examples we always connect to Node A HTTP interface. +// Client = this script +// nodeA = The node we are connecting to via HTTP +// nodeB = The node that will be reached via P2P from nodeA + +// On the 1s case Node A can serve the file directly to the client +// On 2nd case Node A will act as a proxy betweeen Node B and the Client + +// ################# example 1 #################### +// Ex: 1 - Request directly to Node A +// Still goes to the same validation and encryption processes, the only difference is that Node A can serve directly to Client +await testEncryptionFlow(1, nodeA.port, nodeA.node_id) +// ################################################ + +// ################# example 2 #################### +// Ex: 2 - Request file from Node B, via Node A +// The file will be encrypted on node B, using the AES key/IV randomly generated on client +// Client get the peer detaisl and validates the node id agains the public key of the node +// Client encrypts the AES key and IV with node public key, and sends the HTTP request +// Node A forwards the request to Node B, Node B decrypts the AES key and IV with his private key, and encrypts the file +// Node B streams encrypted file to Node A, Node A CANNOT decrypt anything and forwards the request to the initial Client +await testEncryptionFlow(2, nodeA.port, nodeB.node_id) +// ################################################ + +// ################ example 3 ##################### +// On the following example we swap the node roles +// Example 3 (swap nodes) +// We connect to Node B via HTTP and try to get a file from node A, instead +await testEncryptionFlow(3, nodeB.port, nodeA.node_id) +// ############################################### + +// ################# example 4 #################### +// Example 4 - Same as example 1) above but without encryption +await testDownloadCommand(4, nodeA.port, nodeA.node_id) +// ################################################ diff --git a/src/utils/config.ts b/src/utils/config.ts index 9e05c2363..ae248f26f 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -2,6 +2,7 @@ import type { OceanNodeConfig, OceanNodeKeys } from '../@types/OceanNode' import { createFromPrivKey } from '@libp2p/peer-id-factory' import { keys } from '@libp2p/crypto' import { hexStringToByteArray } from '../utils/index.js' +import type { PeerId } from '@libp2p/interface/peer-id' import { CustomNodeLogger, @@ -24,15 +25,22 @@ export async function getPeerIdFromPrivateKey( const key = new keys.supportedKeys.secp256k1.Secp256k1PrivateKey( hexStringToByteArray(privateKey.slice(2)) ) - const id = await createFromPrivKey(key) + const id: PeerId = await createFromPrivKey(key) CONFIG_CONSOLE_LOGGER.logMessageWithEmoji( 'Starting node with peerID:' + id, true, GENERIC_EMOJIS.EMOJI_CHECK_MARK ) + return { peerId: id, - publicKey: (key as any)._publicKey, + publicKey: key.public.bytes, + // Notes: + // using 'key.public.bytes' gives extra 4 bytes: 08021221 + // using (key as any)._publicKey is stripping this same 4 bytes at the beginning: 08021221 + // when getting the peer details with 'peerIdFromString(peerName)' it returns the version with the 4 extra bytes + // and we also need to send that to the client, so he can uncompress the public key correctly and perform the check and the encryption + // so it would make more sense to use this value on the configuration privateKey: (key as any)._key } } diff --git a/src/utils/constants.ts b/src/utils/constants.ts new file mode 100644 index 000000000..12c246fa8 --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1,17 @@ +// Add all the supported commands +export const PROTOCOL_COMMANDS = { + DOWNLOAD_URL: 'downloadURL', + ECHO: 'echo' +} + +export type DownloadCommand = { + command: string + node?: string // if not present it means current node + url: string + aes_encrypted_key?: string // if not present it means download without encryption +} + +export const SUPPORTED_PROTOCOL_COMMANDS: string[] = [ + PROTOCOL_COMMANDS.DOWNLOAD_URL, + PROTOCOL_COMMANDS.ECHO +]