diff --git a/.changeset/real-plums-hammer.md b/.changeset/real-plums-hammer.md new file mode 100644 index 000000000..fdd59a058 --- /dev/null +++ b/.changeset/real-plums-hammer.md @@ -0,0 +1,5 @@ +--- +'@celo/phone-number-privacy-combiner': patch +--- + +Migrated the combiner to gen2 cloud function. diff --git a/.changeset/serious-turtles-switch.md b/.changeset/serious-turtles-switch.md new file mode 100644 index 000000000..1bf23e48d --- /dev/null +++ b/.changeset/serious-turtles-switch.md @@ -0,0 +1,5 @@ +--- +'@celo/phone-number-privacy-combiner': minor +--- + +Migrated the combiner from gen1 to gen2 cloud function. This changeset overwride the previous one. diff --git a/packages/phone-number-privacy/combiner/.env b/packages/phone-number-privacy/combiner/.env index a76483636..2d69d409c 100644 --- a/packages/phone-number-privacy/combiner/.env +++ b/packages/phone-number-privacy/combiner/.env @@ -7,6 +7,4 @@ SERVICE_NAME='odis-combiner' ODIS_BLOCKCHAIN_PROVIDER=https://alfajores-forno.celo-testnet.org CONTEXT_NAME='alfajores' # TODO investigate why these are defined here -NODE_OPTIONS='--require ./dist/tracing.js' -TRACER_ENDPOINT='https://grafana-agent.staging-odis2-centralus.celo-networks-dev.org/api/traces' -TRACING_SERVICE_NAME='odis-combiner-staging' +NODE_OPTIONS='--require ./dist/tracing.js' \ No newline at end of file diff --git a/packages/phone-number-privacy/combiner/.env.celo-pgpnp-mainnet b/packages/phone-number-privacy/combiner/.env.celo-pgpnp-mainnet new file mode 100644 index 000000000..5a1ca429e --- /dev/null +++ b/packages/phone-number-privacy/combiner/.env.celo-pgpnp-mainnet @@ -0,0 +1,29 @@ +TRACER_ENDPOINT='https://grafana-agent.mainnet-odis2-centralus.celo-networks-dev.org/api/traces' +TRACING_SERVICE_NAME='odis-combiner-gen2-mainnet' +MIN_INSTANCES=0 +REQUEST_CONCURRENCY=80 +BLOCKCHAIN_PROVIDER="https://forno.celo.org" +PNP_SERVICE_NAME="odis-combiner-mainnet" +PNP_ENABLED=true +PNP_ODIS_SERVICES_SIGNERS="[{\"url\": \"https://odis.vladiatorlabs.io\"},{\"url\": \"https://mainnet-pgpnp-brazilsouth.azurefd.net\"},{\"url\": \"https://mainnet-pgpnp-eastasia.azurefd.net\"},{\"url\": \"https://phone.chainlayerattestations.com\"},{\"url\": \"https://pnprivacy.wotrust.us\"},{\"url\": \"https://pgpnp.census.works\"},{\"url\": \"https://odis.keyko.rocks\"},{\"url\": \"https://odis.celo.spruceid.xyz\"}]" +PNP_ODIS_SERVICES_TIMEOUT_MILLISECONDS=5000 +PNP_KEYS_CURRENT_VERSION=1 +PNP_KEYS_VERSIONS="[{\"keyVersion\":1,\"threshold\":6,\"polynomial\":\"060000000000000016fade1df2e68418f0c47c6cc5ecab70e2ed4a89c2f63ecadd6ad2e106a962c407e8b75a0d368d1a69e540c7c5634e01a7f2b8c00bea4303bdfdba8f54229ff197bc399a3c16b9a8838258e31022c2bb2a397c6e835d7e86d8c47b5a63e2e30017f865337fd0060497457135173e2b0eaec6f8f14f0cacb17a5d150218e15bd46963ed1b9d56f956f9c4fc692813100042f098b7f70913f671e28ed1c99104b9b740549c42c59212b6671f1e1675674f7e6b6d690a13bd474ab9f0c83cd48e017514ca3874606f6abde2b957c791376e24d55efe6ccc7a1194a685b9589ca873a51c7e77b7b814a76cd9af2aafef500155280fb84efd3219b04312635568788b3393fd45a11f431a7eef8a8fc59ff2bfd4aab744baf9221bf1774653dda61d8193b720f60c627d5a9fec5c2c16a27e948f2f4545b460090303327262ec87f51fbf860f58d5e051d91d5bb869c8912300a9b1c2d922d329c9b7d5179946e049d52ed9b3876f36e5c8b2a47831eb235a51d8d877a284fbe07750449f9654d332808beb9641404188813cddb8ffad906752d71f3f042b583f501b3b7f3906946f9931c598575bf4c8d3e8941168f8cc8e001c092117257bb073db3885dffca5e8dd76b689d395bb5555cf00f9943a9e1ec9939f9d700407330163220f3c15a9420011b8693fb95c635168b6b0a021263b246301343e80161eac44fe79ba657fe59deb9d297ced18d090a8f65dc9c2e0990177f186d7501a2256ac9ecca36743e118f5dd4ce35dc976d38c8679d53cd11b0f11edb45c3473ce848d35875e63b2d100\",\"pubKey\":\"FvreHfLmhBjwxHxsxeyrcOLtSonC9j7K3WrS4QapYsQH6LdaDTaNGmnlQMfFY04Bp/K4wAvqQwO9/bqPVCKf8Ze8OZo8Frmog4JY4xAiwrsqOXxug11+htjEe1pj4uMA\"},{\"keyVersion\":2,\"threshold\":5,\"polynomial\":\"050000000000000016fade1df2e68418f0c47c6cc5ecab70e2ed4a89c2f63ecadd6ad2e106a962c407e8b75a0d368d1a69e540c7c5634e01a7f2b8c00bea4303bdfdba8f54229ff197bc399a3c16b9a8838258e31022c2bb2a397c6e835d7e86d8c47b5a63e2e300399b80040cf5b4b7e016100f02326636696ad6f449cca503bf336c0fec8f7f96d8d410974ca92708a740257029836a00d5b30808af8ab33d6a069656570402339e649a5d4ee2d399768b1598590c3e9deee17d750f480c94f94fb62ad7554b810fb72e1984c0dc2704cf3a6c67d8470104ee5e727b3fc56efb44d53f50ad3d53a18874dc7c3a670c2c34266b33825601fbb1193929115fdebbc99185f2d327904759c18173d1e2abfcaa3db8954f1d41c816a140b86c8d80ba2c2c39faad7080a0068a13aca4767671c13a75735f638d0f0ba8ae2ad650cff7a2f17a89ea7a28699e0e1f232142b2e5f7662c6b582d01b5ac16fb20791462492bed5fa7e28dc2616cf9703f9b4358d8ceb511c7a9cd4054d2d37a8b25f73aa50086c58e723d8145bbd7a6f3024aa7201fa965558e5641839c1b51930bdbc310f9df2894c37e75c4cebe36f4ac5b35c813605b8cbc830078988790de7525fc7d37c44d6ef75b41392b5b117adcfba29f50d6634b331f63cd9ac341744097cd955007b60e86e200\",\"pubKey\":\"FvreHfLmhBjwxHxsxeyrcOLtSonC9j7K3WrS4QapYsQH6LdaDTaNGmnlQMfFY04Bp/K4wAvqQwO9/bqPVCKf8Ze8OZo8Frmog4JY4xAiwrsqOXxug11+htjEe1pj4uMA\"}]" +PNP_FULL_NODE_TIMEOUT_MS=1000 +PNP_FULL_NODE_RETRY_COUNT=5 +PNP_FULL_NODE_DELAY_MS=100 +PNP_SHOULD_AUTHENTICATE=false +PNP_SHOULD_CHECK_QUOTA=false +PNP_SHOULD_MOCK_ACCOUNT_SERVICE=false +PNP_MOCK_DECK=0xbf8a2b73baf8402f8fe906ad3f42b560bf14b39f7df7797ece9e293d6f162188 +DOMAIN_SERVICE_NAME="odis-combiner-mainnet" +DOMAIN_ENABLED=true +DOMAIN_ODIS_SERVICES_SIGNERS="[{\"url\": \"https://odis.vladiatorlabs.io\"},{\"url\": \"https://mainnet-pgpnp-brazilsouth.azurefd.net\"},{\"url\": \"https://phone.chainlayerattestations.com\"},{\"url\": \"https://pnprivacy.wotrust.us\"},{\"url\": \"https://pgpnp.census.works\"},{\"url\": \"https://odis.keyko.rocks\"},{\"url\": \"https://odis.celo.spruceid.xyz\"}]" +DOMAIN_ODIS_SERVICES_TIMEOUT_MILLISECONDS=5000 +DOMAIN_KEYS_CURRENT_VERSION=1 +DOMAIN_KEYS_VERSIONS="[{\"keyVersion\":1,\"threshold\":5,\"polynomial\":\"05000000000000002d7e2d2e2b989bc81e677ced987ee8216cf8a215eddde3d14ddf416c6f513bce8d32b0297e58a888ecca62d22cca3100d2e6ab9d7f049a8fa5b936386f0116a60643c8f604e9431602805a641772e8d0cc800c526dd36d69012ae757c18c250029d97c8a3d4b81e305780b49d511c80dc3009c02b8f651a06c8ec2d5530937a1f7eadf730ad46762a4c089bbd973a000ba77717ec36ebb6fd58904b444a6cde7dd3b3b7ac6fa37f9cd8d00aa67e7cfe81adee5ed45218f7f78b4f8473b564601f4361d228dc6dabf7decd3f61f5bb0ad2c7bd7fe5b7a88054959543e82f4deb08d4fe9af4ac775c9353e038e79f82200863ac9cb7fd6b5fa263eb9d1dead51002607f3eadac153596b671b854715bdb07bee1b0bc8d5178f0dac1b4d00ed0700f46e37135e96604d389f3a323028e29b07f36279e829da00eee1794f3ad6e5dca24eba65a7821755cc464add27c7a601c7e187756e79a5ec3c847f4d91b037fe3cd40590fc1a46b46c2f68c0edcbe5cd7727162a195a711008e4e956eb8a81011b290057cee3f14b9a4198a3e9909cac69a9e7d648fa3dd185794acc4c1e4b994637dca36621d463b42e015115ac2c015fc176d8f143bf99cca654ae95a3101afbdc0c5026f95fbf31af1ac115399f5b6b6d1de09af367745415be9533f8c080\",\"pubKey\":\"LX4tLiuYm8geZ3ztmH7oIWz4ohXt3ePRTd9BbG9RO86NMrApflioiOzKYtIsyjEA0uarnX8Emo+luTY4bwEWpgZDyPYE6UMWAoBaZBdy6NDMgAxSbdNtaQEq51fBjCUA\"}]" +DOMAIN_FULL_NODE_TIMEOUT_MS=1000 +DOMAIN_FULL_NODE_RETRY_COUNT=5 +DOMAIN_FULL_NODE_DELAY_MS=100 +DOMAIN_SHOULD_AUTHENTICATE=false +DOMAIN_SHOULD_CHECK_QUOTA=false diff --git a/packages/phone-number-privacy/combiner/.env.celo-phone-number-privacy b/packages/phone-number-privacy/combiner/.env.celo-phone-number-privacy new file mode 100644 index 000000000..e84366898 --- /dev/null +++ b/packages/phone-number-privacy/combiner/.env.celo-phone-number-privacy @@ -0,0 +1,29 @@ +TRACER_ENDPOINT='https://grafana-agent.alfajores-odis2-centralus.celo-networks-dev.org/api/traces' +TRACING_SERVICE_NAME='odis-combiner-gen2-alfajores' +MIN_INSTANCES=0 +REQUEST_CONCURRENCY=80 +BLOCKCHAIN_PROVIDER="https://alfajores-forno.celo-testnet.org" +PNP_SERVICE_NAME="odis-combiner" +PNP_ENABLED=true +PNP_ODIS_SERVICES_SIGNERS="[{\"url\": \"https://odis-alfajores-signer2.azurefd.net\"},{\"url\": \"https://odis-alfajores-signer3.azurefd.net\"},{\"url\": \"https://odis-alfajores-signer-1-b.azurefd.net\"}]" +PNP_ODIS_SERVICES_TIMEOUT_MILLISECONDS=5000 +PNP_KEYS_CURRENT_VERSION=1 +PNP_KEYS_VERSIONS="[{\"keyVersion\":1,\"threshold\":2,\"polynomial\":\"0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00\",\"pubKey\":\"kPoRxWdEdZ/Nd3uQnp3FJFs54zuiS+ksqvOm9x8vY6KHPG8jrfqysvIRU0wtqYsBKA7SoAsICMBv8C/Fb2ZpDOqhSqvr/sZbZoHmQfvbqrzbtDIPvUIrHgRS0ydJCMsA\"},{\"keyVersion\":2,\"threshold\":2,\"polynomial\":\"0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00\",\"pubKey\":\"kPoRxWdEdZ/Nd3uQnp3FJFs54zuiS+ksqvOm9x8vY6KHPG8jrfqysvIRU0wtqYsBKA7SoAsICMBv8C/Fb2ZpDOqhSqvr/sZbZoHmQfvbqrzbtDIPvUIrHgRS0ydJCMsA\"}]" +PNP_FULL_NODE_TIMEOUT_MS=1000 +PNP_FULL_NODE_RETRY_COUNT=5 +PNP_FULL_NODE_DELAY_MS=100 +PNP_SHOULD_AUTHENTICATE=false +PNP_SHOULD_CHECK_QUOTA=false +PNP_SHOULD_MOCK_ACCOUNT_SERVICE=false +PNP_MOCK_DECK=0xbf8a2b73baf8402f8fe906ad3f42b560bf14b39f7df7797ece9e293d6f162188 +DOMAIN_SERVICE_NAME="odis-combiner" +DOMAIN_ENABLED=true +DOMAIN_ODIS_SERVICES_SIGNERS="[{\"url\": \"https://odis-alfajores-signer2.azurefd.net\"},{\"url\": \"https://odis-alfajores-signer3.azurefd.net\"},{\"url\": \"https://odis-alfajores-signer-1-b.azurefd.net\"}]" +DOMAIN_ODIS_SERVICES_TIMEOUT_MILLISECONDS=5000 +DOMAIN_KEYS_CURRENT_VERSION=1 +DOMAIN_KEYS_VERSIONS="[{\"keyVersion\":1,\"threshold\":2,\"polynomial\":\"0200000000000000f99af1c8fbcb0a15945ff0f23f0e93b86c101f48250c911b4ab4b15004723f93eea98c8ffd4e166535757b46c0522a0167a40224c88ba43c13685bf2f159e63394416cb41432b320e69e3e0810aa8fa1e1b0c7dcc948fc5742f2b8d752b65081f10d83821b4e2cf90b56cc4fc8c98dc00e5f24f2c5b53fa8ad7c2ebd3963c9223cf95209692d267a4f8084edfc0b5f01f7a31d82bf5421c544b6258749c691b79e6f36d9ba963ead6f25b9986b6bcb7d45b5edb33a616af630b4ce17bf552c81\",\"pubKey\":\"+ZrxyPvLChWUX/DyPw6TuGwQH0glDJEbSrSxUARyP5PuqYyP/U4WZTV1e0bAUioBZ6QCJMiLpDwTaFvy8VnmM5RBbLQUMrMg5p4+CBCqj6HhsMfcyUj8V0LyuNdStlCB\"},{\"keyVersion\":2,\"threshold\":2,\"polynomial\":\"0200000000000000f99af1c8fbcb0a15945ff0f23f0e93b86c101f48250c911b4ab4b15004723f93eea98c8ffd4e166535757b46c0522a0167a40224c88ba43c13685bf2f159e63394416cb41432b320e69e3e0810aa8fa1e1b0c7dcc948fc5742f2b8d752b65081f10d83821b4e2cf90b56cc4fc8c98dc00e5f24f2c5b53fa8ad7c2ebd3963c9223cf95209692d267a4f8084edfc0b5f01f7a31d82bf5421c544b6258749c691b79e6f36d9ba963ead6f25b9986b6bcb7d45b5edb33a616af630b4ce17bf552c81\",\"pubKey\":\"+ZrxyPvLChWUX/DyPw6TuGwQH0glDJEbSrSxUARyP5PuqYyP/U4WZTV1e0bAUioBZ6QCJMiLpDwTaFvy8VnmM5RBbLQUMrMg5p4+CBCqj6HhsMfcyUj8V0LyuNdStlCB\"}]" +DOMAIN_FULL_NODE_TIMEOUT_MS=1000 +DOMAIN_FULL_NODE_RETRY_COUNT=5 +DOMAIN_FULL_NODE_DELAY_MS=100 +DOMAIN_SHOULD_AUTHENTICATE=false +DOMAIN_SHOULD_CHECK_QUOTA=false diff --git a/packages/phone-number-privacy/combiner/.env.celo-phone-number-privacy-stg b/packages/phone-number-privacy/combiner/.env.celo-phone-number-privacy-stg new file mode 100644 index 000000000..e1d1349ac --- /dev/null +++ b/packages/phone-number-privacy/combiner/.env.celo-phone-number-privacy-stg @@ -0,0 +1,29 @@ +TRACER_ENDPOINT='https://grafana-agent.staging-odis2-centralus.celo-networks-dev.org/api/traces' +TRACING_SERVICE_NAME='odis-combiner-gen2-staging' +MIN_INSTANCES=0 +REQUEST_CONCURRENCY=80 +BLOCKCHAIN_PROVIDER="https://alfajores-forno.celo-testnet.org" +PNP_SERVICE_NAME="odis_combiner" +PNP_ENABLED=true +PNP_ODIS_SERVICES_SIGNERS="[{\"url\": \"https://staging-pgpnp-signer0.azurefd.net\", \"fallbackUrl\": \"http://52.154.55.35\"},{\"url\": \"https://staging-pgpnp-signer1.azurefd.net\", \"fallbackUrl\": \"http://13.89.116.218\"},{\"url\": \"https://staging-pgpnp-signer2.azurefd.net\", \"fallbackUrl\": \"http://20.84.128.169\"}]" +PNP_ODIS_SERVICES_TIMEOUT_MILLISECONDS=5000 +PNP_KEYS_CURRENT_VERSION=1 +PNP_KEYS_VERSIONS="[{\"keyVersion\":1,\"threshold\":2,\"polynomial\":\"0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00\",\"pubKey\":\"7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA\"},{\"keyVersion\":2,\"threshold\":2,\"polynomial\":\"0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00\",\"pubKey\":\"7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA\"},{\"keyVersion\":3,\"threshold\":2,\"polynomial\":\"0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00\",\"pubKey\":\"7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA\"}]" +PNP_FULL_NODE_TIMEOUT_MS=1000 +PNP_FULL_NODE_RETRY_COUNT=5 +PNP_FULL_NODE_DELAY_MS=100 +PNP_SHOULD_AUTHENTICATE=false +PNP_SHOULD_CHECK_QUOTA=false +PNP_SHOULD_MOCK_ACCOUNT_SERVICE=false +PNP_MOCK_DECK=0xbf8a2b73baf8402f8fe906ad3f42b560bf14b39f7df7797ece9e293d6f162188 +DOMAIN_SERVICE_NAME="odis_combiner" +DOMAIN_ENABLED=true +DOMAIN_ODIS_SERVICES_SIGNERS="[{\"url\": \"https://staging-pgpnp-signer0.azurefd.net\", \"fallbackUrl\": \"http://52.154.55.35\"},{\"url\": \"https://staging-pgpnp-signer1.azurefd.net\", \"fallbackUrl\": \"http://13.89.116.218\"},{\"url\": \"https://staging-pgpnp-signer2.azurefd.net\", \"fallbackUrl\": \"http://20.84.128.169\"}]" +DOMAIN_ODIS_SERVICES_TIMEOUT_MILLISECONDS=5000 +DOMAIN_KEYS_CURRENT_VERSION=1 +DOMAIN_KEYS_VERSIONS="[{\"keyVersion\":1,\"threshold\":2,\"polynomial\":\"0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00\",\"pubKey\":\"7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA\"},{\"keyVersion\":2,\"threshold\":2,\"polynomial\":\"0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00\",\"pubKey\":\"7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA\"},{\"keyVersion\":3,\"threshold\":2,\"polynomial\":\"0200000000000000ec5b161ac167995bd17cc0e9cf3f79369efac1fff5b0f68ad0e83dca207e3fc41b8e20bc155ebb3416a7b3d87364490169032189aa7380c47a0a464864fbe0c106e803197ae4959165e7067b95775cee2c74a78d7a67406764f342e5a4b99a003a510287524c9437b12ebb0bfdc7ea46078b807d1b665966961784bd71c4227c272b01c0fcd19c5b92226c1aac324b010abef36192e8ff3abb25686b3e6707bc747b129c32e572b5850db8446bd8f0af9a3fbf6b579793002b1b68528ca4ac00\",\"pubKey\":\"7FsWGsFnmVvRfMDpzz95Np76wf/1sPaK0Og9yiB+P8QbjiC8FV67NBans9hzZEkBaQMhiapzgMR6CkZIZPvgwQboAxl65JWRZecGe5V3XO4sdKeNemdAZ2TzQuWkuZoA\"}]" +DOMAIN_FULL_NODE_TIMEOUT_MS=1000 +DOMAIN_FULL_NODE_RETRY_COUNT=5 +DOMAIN_FULL_NODE_DELAY_MS=100 +DOMAIN_SHOULD_AUTHENTICATE=false +DOMAIN_SHOULD_CHECK_QUOTA=false diff --git a/packages/phone-number-privacy/combiner/package.json b/packages/phone-number-privacy/combiner/package.json index f641970c3..c8d14cc9d 100644 --- a/packages/phone-number-privacy/combiner/package.json +++ b/packages/phone-number-privacy/combiner/package.json @@ -8,7 +8,7 @@ "main": "dist/index.js", "scripts": { "dev": "yarn build && firebase serve --only functions", - "deploy": "yarn build && firebase deploy --only functions:combiner", + "deploy": "yarn build && firebase deploy --only functions:combinerGen2", "deploy:staging": "yarn deploy --project celo-phone-number-privacy-stg", "deploy:alfajores": "yarn deploy --project celo-phone-number-privacy", "deploy:mainnet": "yarn deploy --project celo-pgpnp-mainnet", @@ -30,6 +30,7 @@ "test:e2e:mainnet": "CONTEXT_NAME=mainnet yarn test:e2e" }, "dependencies": { + "@celo/base": "^5.0.4", "@celo/contractkit": "^5.0.4", "@celo/phone-number-privacy-common": "^3.0.3", "@celo/identity": "^5.0.4", @@ -55,9 +56,8 @@ "express": "^4.17.1", "firebase-admin": "^11.10.1", "firebase-functions": "^4.4.1", - "http-proxy": "^1.18.1", - "stream-array": "^1.1.2", "knex": "^2.1.0", + "lru-cache": "^10.0.1", "node-fetch": "^2.6.9", "pg": "^8.2.1", "uuid": "^7.0.3" @@ -67,8 +67,6 @@ "@celo/utils": "^5.0.4", "@celo/phone-utils": "^5.0.4", "@types/express": "^4.17.6", - "@types/http-proxy": "^1.17.11", - "@types/stream-array": "1.1.1", "@types/supertest": "^2.0.12", "@types/uuid": "^7.0.3", "firebase-functions-test": "^3.1.0", diff --git a/packages/phone-number-privacy/combiner/src/common/combine.ts b/packages/phone-number-privacy/combiner/src/common/combine.ts index 9c8f3f575..aa00c97a1 100644 --- a/packages/phone-number-privacy/combiner/src/common/combine.ts +++ b/packages/phone-number-privacy/combiner/src/common/combine.ts @@ -33,20 +33,6 @@ export async function thresholdCallToSigners( options: ThresholdCallToSignersOptions, processResult: (res: OdisResponse) => Promise = (_) => Promise.resolve(false) ): Promise<{ signerResponses: Array>; maxErrorCode?: number }> { - const obs = new PerformanceObserver((list) => { - // Possible race condition here: if multiple signers take exactly the same - // amount of time, the PerformanceObserver callback may be called twice with - // both entries present. Node 12 doesn't allow for entries to be deleted by name, - // and eliminating the race condition requires a more significant redesign of - // the measurement code. - // This is only used for monitoring purposes, so a rare - // duplicate latency measure for the signer should have minimal impact. - list.getEntries().forEach((entry) => { - logger.info({ latency: entry, signer: entry.name }, 'Signer response latency measured') - }) - }) - obs.observe({ entryTypes: ['measure'], buffered: false }) - const { signers, endpoint, @@ -57,6 +43,31 @@ export async function thresholdCallToSigners( responseSchema, } = options + const obs = new PerformanceObserver((list) => { + // Since moving to a Cloud Run based infrastucture, which allows for + // multiple requests to be processed by the same server instance, + // there was a need to filter the performance observer by entry name. + // + // Without this, the performance observer would incorrectly log requests + // from multiple sessions. + + list.getEntries().forEach((entry) => { + // Filter entries based on signer URL + const matchingSigner = signers.find((signer) => { + const entryName = signer.url + endpoint + `/${request.body.sessionID}` + return entry.name === entryName + }) + + if (matchingSigner) { + logger.info( + { latency: entry, signer: matchingSigner.url + endpoint }, + 'Signer response latency measured' + ) + } + }) + }) + obs.observe({ entryTypes: ['measure'], buffered: false }) + const manualAbort = new AbortController() // @ts-ignore const timeoutSignal = AbortSignal.timeout(requestTimeoutMS) @@ -92,10 +103,11 @@ export async function thresholdCallToSigners( }) if (!signerFetchResult.ok) { + const responseData = await signerFetchResult.json() // used for log based metrics logger.info({ message: 'Received signerFetchResult on unsuccessful signer response', - res: await signerFetchResult.json(), + res: responseData, status: signerFetchResult.status, signer: signer.url, }) @@ -110,6 +122,7 @@ export async function thresholdCallToSigners( logger.warn('Not possible to reach a threshold of signer responses. Failing fast') manualAbort.abort() } + responses.push({ res: responseData, url: signer.url }) return } diff --git a/packages/phone-number-privacy/combiner/src/common/crypto-clients/crypto-client.ts b/packages/phone-number-privacy/combiner/src/common/crypto-clients/crypto-client.ts index c57783a30..a440b1a61 100644 --- a/packages/phone-number-privacy/combiner/src/common/crypto-clients/crypto-client.ts +++ b/packages/phone-number-privacy/combiner/src/common/crypto-clients/crypto-client.ts @@ -39,15 +39,23 @@ export abstract class CryptoClient { `${ErrorMessage.NOT_ENOUGH_PARTIAL_SIGNATURES} ${this.allSignaturesLength}/${threshold}` ) } + const randomID = + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + + const name = `combineBlindedSignatureShares/${randomID}` + const start = `Start ${name}` + const end = `End ${name}` - const start = `Start combineBlindedSignatureShares` - const end = `End combineBlindedSignatureShares` performance.mark(start) const combinedSignature = this._combineBlindedSignatureShares(blindedMessage, logger) performance.mark(end) - performance.measure('combineBlindedSignatureShares', start, end) + performance.measure(name, start, end) + + performance.clearMeasures(name) + performance.clearMarks(start) + performance.clearMarks(end) return combinedSignature } diff --git a/packages/phone-number-privacy/combiner/src/common/io.ts b/packages/phone-number-privacy/combiner/src/common/io.ts index e2df35c25..8e029798a 100644 --- a/packages/phone-number-privacy/combiner/src/common/io.ts +++ b/packages/phone-number-privacy/combiner/src/common/io.ts @@ -90,10 +90,11 @@ export async function fetchSignerResponseWithFallback( }) } - return measureTime(signer.url + signerEndpoint, () => + return measureTime(signer.url + signerEndpoint + `/${request.body.sessionID}`, () => fetchSignerResponse(signer.url + signerEndpoint).catch((err) => { logger.error({ url: signer.url, error: err }, `Signer failed with primary url`) if (signer.fallbackUrl && !isAbortError(err)) { + // TODO should we also be checking isTimeoutError here? logger.warn({ signer }, `Using fallback url to call signer`) return fetchSignerResponse(signer.fallbackUrl + signerEndpoint) } else { @@ -112,5 +113,9 @@ async function measureTime(name: string, fn: () => Promise): Promise { } finally { performance.mark(end) performance.measure(name, start, end) + + performance.clearMeasures(name) + performance.clearMarks(start) + performance.clearMarks(end) } } diff --git a/packages/phone-number-privacy/combiner/src/common/tracing-utils.ts b/packages/phone-number-privacy/combiner/src/common/tracing-utils.ts new file mode 100644 index 000000000..f989c86e5 --- /dev/null +++ b/packages/phone-number-privacy/combiner/src/common/tracing-utils.ts @@ -0,0 +1,21 @@ +import opentelemetry, { SpanStatusCode } from '@opentelemetry/api' + +const tracer = opentelemetry.trace.getTracer('combiner-tracer') + +export function traceAsyncFunction(traceName: string, fn: () => Promise): Promise { + return tracer.startActiveSpan(traceName, async (span) => { + try { + const res = await fn() + span.setStatus({ code: SpanStatusCode.OK }) + return res + } catch (err: any) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: err instanceof Error ? err.message : undefined, + }) + throw err + } finally { + span.end() + } + }) +} diff --git a/packages/phone-number-privacy/combiner/src/common/web3/contracts.ts b/packages/phone-number-privacy/combiner/src/common/web3/contracts.ts new file mode 100644 index 000000000..9629bf321 --- /dev/null +++ b/packages/phone-number-privacy/combiner/src/common/web3/contracts.ts @@ -0,0 +1,18 @@ +import { ContractKit } from '@celo/contractkit' +import { getDataEncryptionKey } from '@celo/phone-number-privacy-common' +import Logger from 'bunyan' +import config from '../../config' + +export async function getDEK(kit: ContractKit, logger: Logger, account: string): Promise { + return getDataEncryptionKey( + account, + kit, + logger, + config.phoneNumberPrivacy.fullNodeTimeoutMs, + config.phoneNumberPrivacy.fullNodeRetryCount, + config.phoneNumberPrivacy.fullNodeRetryDelayMs + ).catch((err) => { + logger.error({ err, account }, 'failed to get on-chain DEK for account') + throw err + }) +} diff --git a/packages/phone-number-privacy/combiner/src/config.ts b/packages/phone-number-privacy/combiner/src/config.ts index d09ea2cd3..de807473e 100644 --- a/packages/phone-number-privacy/combiner/src/config.ts +++ b/packages/phone-number-privacy/combiner/src/config.ts @@ -5,9 +5,38 @@ import { RETRY_DELAY_IN_MS, rootLogger, TestUtils, - toBool, } from '@celo/phone-number-privacy-common' -import * as functions from 'firebase-functions' +import { + blockchainApiKey, + blockchainProvider, + defaultMockDEK, + domainEnabled, + domainFullNodeDelaysMs, + domainFullNodeRetryCount, + domainFullNodeTimeoutMs, + domainKeysCurrentVersion, + domainKeysVersions, + domainOdisServicesSigners, + domainOdisServicesTimeoutMilliseconds, + domainServiceName, + domainShouldAuthenticate, + domainShouldCheckQuota, + pnpEnabled, + pnpFullNodeDelaysMs, + pnpFullNodeRetryCount, + pnpFullNodeTimeoutMs, + pnpKeysCurrentVersion, + pnpKeysVersions, + pnpMockDek, + pnpOdisServicesSigners, + pnpOdisServicesTimeoutMilliseconds, + pnpServiceName, + pnpShouldAuthenticate, + pnpShouldCheckQuota, + pnpShouldMockAccountService, + serviceNameConfig, +} from './utils/firebase-configs' + export function getCombinerVersion(): string { return process.env.npm_package_version ?? require('../package.json').version ?? '0.0.0' } @@ -40,17 +69,16 @@ export interface OdisConfig { fullNodeRetryCount: number fullNodeRetryDelayMs: number shouldAuthenticate: boolean + shouldCheckQuota: boolean + shouldMockAccountService?: boolean + mockDek?: string } -export interface ProxyConfig { - deploymentEnv: string - forwardToGen2: boolean -} + export interface CombinerConfig { serviceName: string blockchain: BlockchainConfig phoneNumberPrivacy: OdisConfig domains: OdisConfig - proxy: ProxyConfig } let config: CombinerConfig @@ -112,6 +140,8 @@ if (DEV_MODE) { fullNodeRetryCount: RETRY_COUNT, fullNodeRetryDelayMs: RETRY_DELAY_IN_MS, shouldAuthenticate: true, + shouldCheckQuota: false, + mockDek: defaultMockDEK, }, domains: { serviceName: defaultServiceName, @@ -147,65 +177,51 @@ if (DEV_MODE) { fullNodeRetryCount: RETRY_COUNT, fullNodeRetryDelayMs: RETRY_DELAY_IN_MS, shouldAuthenticate: true, - }, - proxy: { - forwardToGen2: false, - deploymentEnv: 'local', + shouldCheckQuota: false, }, } } else { - const functionConfig = functions.config() config = { - serviceName: functionConfig.service.name ?? defaultServiceName, + serviceName: serviceNameConfig.value(), blockchain: { - provider: functionConfig.blockchain.provider, - apiKey: functionConfig.blockchain.api_key, + provider: blockchainProvider.value(), + apiKey: blockchainApiKey.value(), }, phoneNumberPrivacy: { - serviceName: functionConfig.pnp.service_name ?? defaultServiceName, - enabled: toBool(functionConfig.pnp.enabled, false), + serviceName: pnpServiceName.value(), + enabled: pnpEnabled.value(), odisServices: { - signers: functionConfig.pnp.odisservices, - timeoutMilliSeconds: functionConfig.pnp.timeout_ms - ? Number(functionConfig.pnp.timeout_ms) - : 5 * 1000, + signers: pnpOdisServicesSigners.value(), + timeoutMilliSeconds: pnpOdisServicesTimeoutMilliseconds.value(), }, keys: { - currentVersion: Number(functionConfig.pnp_keys.current_version), - versions: functionConfig.pnp_keys.versions, + currentVersion: pnpKeysCurrentVersion.value(), + versions: pnpKeysVersions.value(), }, - fullNodeTimeoutMs: Number(functionConfig.pnp.full_node_timeout_ms ?? FULL_NODE_TIMEOUT_IN_MS), - fullNodeRetryCount: Number(functionConfig.pnp.full_node_retry_count ?? RETRY_COUNT), - fullNodeRetryDelayMs: Number( - functionConfig.pnp.full_node_retry_delay_ms ?? RETRY_DELAY_IN_MS - ), - shouldAuthenticate: toBool(functionConfig.pnp.should_authenticate, true), + fullNodeTimeoutMs: pnpFullNodeTimeoutMs.value(), + fullNodeRetryCount: pnpFullNodeRetryCount.value(), + fullNodeRetryDelayMs: pnpFullNodeDelaysMs.value(), + shouldAuthenticate: pnpShouldAuthenticate.value(), + shouldCheckQuota: pnpShouldCheckQuota.value(), + shouldMockAccountService: pnpShouldMockAccountService.value(), + mockDek: pnpMockDek.value(), }, domains: { - serviceName: functionConfig.domains.service_name ?? defaultServiceName, - enabled: toBool(functionConfig.domains.enabled, false), + serviceName: domainServiceName.value(), + enabled: domainEnabled.value(), odisServices: { - signers: functionConfig.domains.odisservices, - timeoutMilliSeconds: functionConfig.domains.timeout_ms - ? Number(functionConfig.domains.timeout_ms) - : 5 * 1000, + signers: domainOdisServicesSigners.value(), + timeoutMilliSeconds: domainOdisServicesTimeoutMilliseconds.value(), }, keys: { - currentVersion: Number(functionConfig.domains_keys.current_version), - versions: functionConfig.domains_keys.versions, + currentVersion: domainKeysCurrentVersion.value(), + versions: domainKeysVersions.value(), }, - fullNodeTimeoutMs: Number( - functionConfig.domains.full_node_timeout_ms ?? FULL_NODE_TIMEOUT_IN_MS - ), // TODO refactor config - domains endpoints don't use full node - fullNodeRetryCount: Number(functionConfig.domains.full_node_retry_count ?? RETRY_COUNT), - fullNodeRetryDelayMs: Number( - functionConfig.domains.full_node_retry_delay_ms ?? RETRY_DELAY_IN_MS - ), - shouldAuthenticate: true, - }, - proxy: { - forwardToGen2: toBool(functionConfig.proxy.forward_to_gen2, false), - deploymentEnv: functionConfig.proxy.deployment_env, + fullNodeTimeoutMs: domainFullNodeTimeoutMs.value(), // TODO refactor config - domains endpoints don't use full node + fullNodeRetryCount: domainFullNodeRetryCount.value(), + fullNodeRetryDelayMs: domainFullNodeDelaysMs.value(), + shouldAuthenticate: domainShouldAuthenticate.value(), + shouldCheckQuota: domainShouldCheckQuota.value(), }, } } diff --git a/packages/phone-number-privacy/combiner/src/index.ts b/packages/phone-number-privacy/combiner/src/index.ts index 1c2e9a31c..512c48369 100644 --- a/packages/phone-number-privacy/combiner/src/index.ts +++ b/packages/phone-number-privacy/combiner/src/index.ts @@ -1,23 +1,18 @@ -import * as functions from 'firebase-functions' +import * as functions from 'firebase-functions/v2/https' import config from './config' -import { startCombiner, startProxy } from './server' +import { startCombiner } from './server' +import { blockchainApiKey, minInstancesConfig, requestConcurency } from './utils/firebase-configs' require('dotenv').config() -export const combiner = functions - .region('us-central1') - .runWith({ - // Keep instances warm for mainnet functions - // Defined check required for running tests vs. deployment - minInstances: functions.config().service ? Number(functions.config().service.min_instances) : 0, - memory: functions.config().service ? functions.config().service.memory : '512MB', - }) - .https.onRequest((req, res) => { - if (config.proxy.forwardToGen2) { - startProxy(req, res, config) - } else { - const app = startCombiner(config) - app(req, res) - } - }) +export const combinerGen2 = functions.onRequest( + { + minInstances: minInstancesConfig, + secrets: [blockchainApiKey], + concurrency: requestConcurency, + memory: '512MiB', + region: 'us-central1', + }, + startCombiner(config) +) export * from './config' diff --git a/packages/phone-number-privacy/combiner/src/pnp/endpoints/quota/action.ts b/packages/phone-number-privacy/combiner/src/pnp/endpoints/quota/action.ts index 3f06d1a87..f8a5bc70d 100644 --- a/packages/phone-number-privacy/combiner/src/pnp/endpoints/quota/action.ts +++ b/packages/phone-number-privacy/combiner/src/pnp/endpoints/quota/action.ts @@ -1,8 +1,8 @@ import { authenticateUser, CombinerEndpoint, - DataEncryptionKeyFetcher, ErrorMessage, + ErrorType, getSignerEndpoint, hasValidAccountParam, isBodyReasonablySized, @@ -16,13 +16,16 @@ import { Signer, thresholdCallToSigners } from '../../../common/combine' import { errorResult, ResultHandler } from '../../../common/handlers' import { getKeyVersionInfo } from '../../../common/io' import { getCombinerVersion, OdisConfig } from '../../../config' +import { NoQuotaCache } from '../../../utils/no-quota-cache' +import { AccountService } from '../../services/account-services' import { logPnpSignerResponseDiscrepancies } from '../../services/log-responses' import { findCombinerQuotaState } from '../../services/threshold-state' export function pnpQuota( signers: Signer[], config: OdisConfig, - dekFetcher: DataEncryptionKeyFetcher + accountService: AccountService, + noQuotaCache: NoQuotaCache ): ResultHandler { return async (request, response) => { const logger = response.locals.logger @@ -31,12 +34,33 @@ export function pnpQuota( return errorResult(400, WarningMessage.INVALID_INPUT) } + const warnings: ErrorType[] = [] if (config.shouldAuthenticate) { - if (!(await authenticateUser(request, logger, dekFetcher))) { + if (!(await authenticateUser(request, logger, accountService.getAccount, warnings))) { return errorResult(401, WarningMessage.UNAUTHENTICATED_USER) } } + const account = request.body.account + if (config.shouldCheckQuota) { + if (noQuotaCache.maximumQuotaReached(account)) { + const quota = noQuotaCache.getTotalQuota(account) + // can exist a race condition between the hasQuota and getTotalQuota but that's highly improbable + if (quota !== undefined) { + return { + status: 200, + body: { + success: true, + version: getCombinerVersion(), + performedQueryCount: quota, + totalQuota: quota, + warnings, + }, + } + } + } + } + // TODO remove this, we shouldn't need keyVersionInfo for non-signing endpoints const keyVersionInfo = getKeyVersionInfo(request, config, logger) @@ -49,13 +73,18 @@ export function pnpQuota( responseSchema: PnpQuotaResponseSchema, shouldCheckKeyVersion: false, }) - const warnings = logPnpSignerResponseDiscrepancies(logger, signerResponses) + warnings.push(...logPnpSignerResponseDiscrepancies(logger, signerResponses)) const { threshold } = keyVersionInfo if (signerResponses.length >= threshold) { try { const quotaStatus = findCombinerQuotaState(keyVersionInfo, signerResponses, warnings) + + if (quotaStatus.performedQueryCount === quotaStatus.totalQuota) { + // Sets that there is not more quota for that account + noQuotaCache.setNoMoreQuota(account, quotaStatus.totalQuota) + } return { status: 200, body: { diff --git a/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/action.ts b/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/action.ts index 9109bfc28..d5002380d 100644 --- a/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/action.ts +++ b/packages/phone-number-privacy/combiner/src/pnp/endpoints/sign/action.ts @@ -1,7 +1,6 @@ import { authenticateUser, CombinerEndpoint, - DataEncryptionKeyFetcher, ErrorMessage, ErrorType, getSignerEndpoint, @@ -21,13 +20,16 @@ import { BLSCryptographyClient } from '../../../common/crypto-clients/bls-crypto import { errorResult, ResultHandler } from '../../../common/handlers' import { getKeyVersionInfo, requestHasSupportedKeyVersion } from '../../../common/io' import { getCombinerVersion, OdisConfig } from '../../../config' +import { NoQuotaCache } from '../../../utils/no-quota-cache' +import { AccountService } from '../../services/account-services' import { logPnpSignerResponseDiscrepancies } from '../../services/log-responses' import { findCombinerQuotaState } from '../../services/threshold-state' export function pnpSign( signers: Signer[], config: OdisConfig, - dekFetcher: DataEncryptionKeyFetcher + accountService: AccountService, + noQuotaCache: NoQuotaCache ): ResultHandler { return async (request, response) => { const logger = response.locals.logger @@ -39,12 +41,24 @@ export function pnpSign( return errorResult(400, WarningMessage.INVALID_KEY_VERSION_REQUEST) } + const warnings: ErrorType[] = [] if (config.shouldAuthenticate) { - if (!(await authenticateUser(request, logger, dekFetcher))) { + if (!(await authenticateUser(request, logger, accountService.getAccount, warnings))) { return errorResult(401, WarningMessage.UNAUTHENTICATED_USER) } } + const account = request.body.account + if (config.shouldCheckQuota) { + if (noQuotaCache.maximumQuotaReached(account)) { + const quota = noQuotaCache.getTotalQuota(account) + // can exist a race condition between the hasQuota and getTotalQuota but that's highly improbable + if (quota !== undefined) { + return errorResult(403, WarningMessage.EXCEEDED_QUOTA) + } + } + } + const keyVersionInfo = getKeyVersionInfo(request, config, logger) const crypto = new BLSCryptographyClient(keyVersionInfo) @@ -83,7 +97,7 @@ export function pnpSign( processResult ) - const warnings = logPnpSignerResponseDiscrepancies(logger, signerResponses) + warnings.push(...logPnpSignerResponseDiscrepancies(logger, signerResponses)) if (crypto.hasSufficientSignatures()) { try { @@ -110,6 +124,15 @@ export function pnpSign( } const errorCode = maxErrorCode ?? 500 + // If the error is 403 it means that we don't have quota for the signer + if (errorCode === 403) { + const exceededResponse = signerResponses.find( + (e) => !e.res.success && e.res.error === WarningMessage.EXCEEDED_QUOTA + ) + if (exceededResponse && exceededResponse.res.totalQuota !== undefined) { + noQuotaCache.setNoMoreQuota(account, exceededResponse.res.totalQuota) + } + } const error = errorCodeToError(errorCode) return errorResult(errorCode, error) } diff --git a/packages/phone-number-privacy/combiner/src/pnp/services/account-services.ts b/packages/phone-number-privacy/combiner/src/pnp/services/account-services.ts new file mode 100644 index 000000000..9c0a4401d --- /dev/null +++ b/packages/phone-number-privacy/combiner/src/pnp/services/account-services.ts @@ -0,0 +1,63 @@ +import { ContractKit } from '@celo/contractkit' +import { ErrorMessage } from '@celo/phone-number-privacy-common' +import Logger from 'bunyan' +import { LRUCache } from 'lru-cache' +import { OdisError, wrapError } from '../../common/error' +import { traceAsyncFunction } from '../../common/tracing-utils' +import { getDEK } from '../../common/web3/contracts' + +export interface AccountService { + getAccount(address: string): Promise +} + +export interface ContractKitAccountServiceOptions { + fullNodeTimeoutMs: number + fullNodeRetryCount: number + fullNodeRetryDelayMs: number +} + +export class CachingAccountService implements AccountService { + private cache: LRUCache + constructor(baseService: AccountService) { + this.cache = new LRUCache({ + max: 500, + ttl: 5 * 1000, // 5 seconds + allowStale: true, + fetchMethod: async (address: string) => { + return baseService.getAccount(address) + }, + }) + } + + getAccount = (address: string): Promise => { + return traceAsyncFunction('CachingAccountService - getAccount', async () => { + const dek = await this.cache.fetch(address) + + if (dek === undefined) { + // TODO decide which error ot use here + throw new OdisError(ErrorMessage.FULL_NODE_ERROR) + } + return dek + }) + } +} + +// tslint:disable-next-line:max-classes-per-file +export class ContractKitAccountService implements AccountService { + constructor(private readonly logger: Logger, private readonly kit: ContractKit) {} + + async getAccount(address: string): Promise { + return traceAsyncFunction('ContractKitAccountService - getAccount', async () => { + return wrapError(getDEK(this.kit, this.logger, address), ErrorMessage.FAILURE_TO_GET_DEK) + }) + } +} + +// tslint:disable-next-line:max-classes-per-file +export class MockAccountService implements AccountService { + constructor(private readonly mockDek: string) {} + + async getAccount(_address: string): Promise { + return this.mockDek + } +} diff --git a/packages/phone-number-privacy/combiner/src/pnp/services/log-responses.ts b/packages/phone-number-privacy/combiner/src/pnp/services/log-responses.ts index 89bd98c26..864d69bac 100644 --- a/packages/phone-number-privacy/combiner/src/pnp/services/log-responses.ts +++ b/packages/phone-number-privacy/combiner/src/pnp/services/log-responses.ts @@ -1,4 +1,5 @@ import { + ErrorType, PnpQuotaRequest, SignMessageRequest, WarningMessage, @@ -13,8 +14,8 @@ import { export function logPnpSignerResponseDiscrepancies( logger: Logger, responses: Array> -): string[] { - const warnings: string[] = [] +): ErrorType[] { + const warnings: ErrorType[] = [] // TODO responses should all already be successes due to CombineAction receiveSuccess // https://github.com/celo-org/celo-monorepo/issues/9826 diff --git a/packages/phone-number-privacy/combiner/src/server.ts b/packages/phone-number-privacy/combiner/src/server.ts index ee4092053..cb7e60c89 100644 --- a/packages/phone-number-privacy/combiner/src/server.ts +++ b/packages/phone-number-privacy/combiner/src/server.ts @@ -4,12 +4,10 @@ import { getContractKitWithAgent, KEY_VERSION_HEADER, loggerMiddleware, - newContractKitFetcher, OdisRequest, rootLogger, } from '@celo/phone-number-privacy-common' import express, { Express, RequestHandler } from 'express' -import httpProxy from 'http-proxy' import { Signer } from './common/combine' import { catchErrorHandler, @@ -26,7 +24,12 @@ import { domainQuota } from './domain/endpoints/quota/action' import { domainSign } from './domain/endpoints/sign/action' import { pnpQuota } from './pnp/endpoints/quota/action' import { pnpSign } from './pnp/endpoints/sign/action' -const streamify = require('stream-array') +import { + CachingAccountService, + ContractKitAccountService, + MockAccountService, +} from './pnp/services/account-services' +import { NoQuotaCache } from './utils/no-quota-cache' require('events').EventEmitter.defaultMaxListeners = 15 @@ -63,13 +66,12 @@ export function startCombiner(config: CombinerConfig, kit?: ContractKit): Expres }) }) - const dekFetcher = newContractKitFetcher( - kit, - logger, - config.phoneNumberPrivacy.fullNodeTimeoutMs, - config.phoneNumberPrivacy.fullNodeRetryCount, - config.phoneNumberPrivacy.fullNodeRetryDelayMs - ) + const baseAccountService = config.phoneNumberPrivacy.shouldMockAccountService + ? new MockAccountService(config.phoneNumberPrivacy.mockDek!) + : new ContractKitAccountService(logger, kit) + + const accountService = new CachingAccountService(baseAccountService) + const noQuotaCache = new NoQuotaCache() const pnpSigners: Signer[] = JSON.parse(config.phoneNumberPrivacy.odisServices.signers) const domainSigners: Signer[] = JSON.parse(config.domains.odisServices.signers) @@ -78,15 +80,21 @@ export function startCombiner(config: CombinerConfig, kit?: ContractKit): Expres app.post( CombinerEndpoint.PNP_QUOTA, - createHandler(phoneNumberPrivacy.enabled, pnpQuota(pnpSigners, phoneNumberPrivacy, dekFetcher)) + createHandler( + phoneNumberPrivacy.enabled, + pnpQuota(pnpSigners, config.phoneNumberPrivacy, accountService, noQuotaCache) + ) ) app.post( CombinerEndpoint.PNP_SIGN, - createHandler(phoneNumberPrivacy.enabled, pnpSign(pnpSigners, phoneNumberPrivacy, dekFetcher)) + createHandler( + phoneNumberPrivacy.enabled, + pnpSign(pnpSigners, config.phoneNumberPrivacy, accountService, noQuotaCache) + ) ) app.post( CombinerEndpoint.DOMAIN_QUOTA_STATUS, - createHandler(domains.enabled, domainQuota(domainSigners, domains)) + createHandler(domains.enabled, domainQuota(domainSigners, config.domains)) ) app.post( CombinerEndpoint.DOMAIN_SIGN, @@ -108,67 +116,3 @@ function createHandler( tracingHandler(meteringHandler(enabled ? resultHandler(action) : disabledHandler)) ) } - -export function startProxy(req: any, res: any, config: CombinerConfig) { - const logger = rootLogger(config.serviceName) - - logger.info({ request: req }, 'Starting proxy.') - - let destinationUrl: string - let rawBodyString: string - let rawBodyJson: any - let rawBodyData: any[] = [] - - const proxy = httpProxy.createProxyServer({ - proxyTimeout: config.phoneNumberPrivacy.odisServices.timeoutMilliSeconds, - }) - - if (req.rawBody) { - // XXX having to strigify and then parse, because simply using `req.rawBody.data` does not work. - rawBodyString = JSON.stringify(req.rawBody) - rawBodyJson = JSON.parse(rawBodyString) - rawBodyData = rawBodyJson.data - } - - switch (config.proxy.deploymentEnv) { - case 'mainnet': - destinationUrl = 'https://us-central1-celo-pgpnp-mainnet.cloudfunctions.net/combinerGen2' - break - - case 'alfajores': - destinationUrl = - 'https://us-central1-celo-phone-number-privacy.cloudfunctions.net/combinerGen2' - break - - case 'staging': - destinationUrl = - 'https://us-central1-celo-phone-number-privacy-stg.cloudfunctions.net/combinerGen2' - break - - default: - throw 'Failed to set destination URL' - } - - logger.info( - { - request: req, - rawBodyData: rawBodyData, - destinationURL: destinationUrl, - }, - 'Proxying request to staging Combiner gen 2.' - ) - - proxy.web(req, res, { - target: destinationUrl, - buffer: streamify(rawBodyData.length != 0 ? [Buffer.from(rawBodyData)] : []), - changeOrigin: true, - }) - - proxy.on('error', (err) => { - logger.error({ err }, 'Error in Proxying request to Combiner.') - res.status(500).json({ - success: false, - error: err, - }) - }) -} diff --git a/packages/phone-number-privacy/combiner/src/utils/firebase-configs.ts b/packages/phone-number-privacy/combiner/src/utils/firebase-configs.ts new file mode 100644 index 000000000..ec26208bc --- /dev/null +++ b/packages/phone-number-privacy/combiner/src/utils/firebase-configs.ts @@ -0,0 +1,88 @@ +import { ensureLeading0x } from '@celo/base' +import { + FULL_NODE_TIMEOUT_IN_MS, + RETRY_COUNT, + RETRY_DELAY_IN_MS, +} from '@celo/phone-number-privacy-common' +import { defineBoolean, defineInt, defineSecret, defineString } from 'firebase-functions/params' + +const defaultServiceName = 'odis-combiner' +export const defaultMockDEK = ensureLeading0x( + 'bf8a2b73baf8402f8fe906ad3f42b560bf14b39f7df7797ece9e293d6f162188' +) + +// function settings +export const minInstancesConfig: any = defineInt('MIN_INSTANCES', { default: 0 }) +export const requestConcurency: any = defineInt('REQUEST_CONCURRENCY', { default: 80 }) + +// Root +export const serviceNameConfig: any = defineString('SERVICE_NAME', { default: defaultServiceName }) + +// Blockchain +export const blockchainProvider: any = defineString('BLOCKCHAIN_PROVIDER') +export const blockchainApiKey: any = defineSecret('BLOCKCHAIN_API_KEY') + +// PNP +export const pnpServiceName: any = defineString('PNP_SERVICE_NAME', { default: defaultServiceName }) +export const pnpEnabled: any = defineBoolean('PNP_ENABLED', { + default: false, + description: '', +}) +export const pnpOdisServicesSigners: any = defineString('PNP_ODIS_SERVICES_SIGNERS') +export const pnpOdisServicesTimeoutMilliseconds: any = defineInt( + 'PNP_ODIS_SERVICES_TIMEOUT_MILLISECONDS', + { + default: 5 * 1000, + } +) +export const pnpKeysCurrentVersion: any = defineInt('PNP_KEYS_CURRENT_VERSION') +export const pnpKeysVersions: any = defineString('PNP_KEYS_VERSIONS') +export const pnpFullNodeTimeoutMs: any = defineInt('PNP_FULL_NODE_TIMEOUT_MS', { + default: FULL_NODE_TIMEOUT_IN_MS, +}) +export const pnpFullNodeRetryCount: any = defineInt('PNP_FULL_NODE_RETRY_COUNT', { + default: RETRY_COUNT, +}) +export const pnpFullNodeDelaysMs: any = defineInt('PNP_FULL_NODE_DELAY_MS', { + default: RETRY_DELAY_IN_MS, +}) +export const pnpShouldAuthenticate: any = defineBoolean('PNP_SHOULD_AUTHENTICATE', { + default: true, +}) +export const pnpShouldCheckQuota: any = defineBoolean('PNP_SHOULD_CHECK_QUOTA', { + default: false, +}) +export const pnpShouldMockAccountService: any = defineBoolean('PNP_SHOULD_MOCK_ACCOUNT_SERVICE', { + default: false, +}) +export const pnpMockDek: any = defineString('PNP_MOCK_DECK', { default: defaultMockDEK }) + +// Domains +export const domainServiceName: any = defineString('DOMAIN_SERVICE_NAME', { + default: defaultServiceName, +}) +export const domainEnabled: any = defineBoolean('DOMAIN_ENABLED', { default: false }) +export const domainOdisServicesSigners: any = defineString('DOMAIN_ODIS_SERVICES_SIGNERS') +export const domainOdisServicesTimeoutMilliseconds: any = defineInt( + 'DOMAIN_ODIS_SERVICES_TIMEOUT_MILLISECONDS', + { + default: 5 * 1000, + } +) +export const domainKeysCurrentVersion: any = defineInt('DOMAIN_KEYS_CURRENT_VERSION') +export const domainKeysVersions: any = defineString('DOMAIN_KEYS_VERSIONS') +export const domainFullNodeTimeoutMs: any = defineInt('DOMAIN_FULL_NODE_TIMEOUT_MS', { + default: FULL_NODE_TIMEOUT_IN_MS, +}) +export const domainFullNodeRetryCount: any = defineInt('DOMAIN_FULL_NODE_RETRY_COUNT', { + default: RETRY_COUNT, +}) +export const domainFullNodeDelaysMs: any = defineInt('DOMAIN_FULL_NODE_DELAY_MS', { + default: RETRY_DELAY_IN_MS, +}) +export const domainShouldAuthenticate: any = defineBoolean('DOMAIN_SHOULD_AUTHENTICATE', { + default: true, +}) +export const domainShouldCheckQuota: any = defineBoolean('DOMAIN_SHOULD_CHECK_QUOTA', { + default: false, +}) diff --git a/packages/phone-number-privacy/combiner/src/utils/no-quota-cache.ts b/packages/phone-number-privacy/combiner/src/utils/no-quota-cache.ts new file mode 100644 index 000000000..bec68d978 --- /dev/null +++ b/packages/phone-number-privacy/combiner/src/utils/no-quota-cache.ts @@ -0,0 +1,31 @@ +import { LRUCache } from 'lru-cache' + +// Stores if an address does not have more quota with its totalQuota number +export class NoQuotaCache { + private cache: LRUCache + constructor(seconds: number = 5) { + this.cache = new LRUCache({ + max: 500, + ttl: seconds * 1000, + allowStale: false, + }) + } + + maximumQuotaReached = (address: string): boolean => { + const quota = this.cache.get(address) + + return quota !== undefined + } + + getTotalQuota = (address: string): number | undefined => { + return this.cache.get(address) + } + + setNoMoreQuota = (address: string, totalQuota: number) => { + const previousQuota = this.cache.get(address) + // Checking if the quotas are not the same to avoid refreshing the ttl + if (!previousQuota || previousQuota < totalQuota) { + this.cache.set(address, totalQuota) + } + } +} diff --git a/packages/phone-number-privacy/combiner/test/end-to-end/resources.ts b/packages/phone-number-privacy/combiner/test/end-to-end/resources.ts index a1a4adf18..abf097a12 100644 --- a/packages/phone-number-privacy/combiner/test/end-to-end/resources.ts +++ b/packages/phone-number-privacy/combiner/test/end-to-end/resources.ts @@ -33,9 +33,11 @@ export const getTestContextName = (): OdisContextName => { export const DEFAULT_FORNO_URL = process.env.ODIS_BLOCKCHAIN_PROVIDER ?? 'https://alfajores-forno.celo-testnet.org' -export const PRIVATE_KEY = '2c63bf6d60b16c8afa13e1069dbe92fef337c23855fff8b27732b3e9c6e7efd4' +export const PRIVATE_KEY = '2c63bf6d60b16c8afa13e1069dbe92fef337c23855fff8b27732b3e9c6e7efd4' // XXX only valid for staging and alfajores export const ACCOUNT_ADDRESS = normalizeAddressWith0x(privateKeyToAddress(PRIVATE_KEY)) // 0x6037800e91eaa703e38bad40c01410bbdf0fea7e +// export const PRIVATE_KEY_NO_QUOTA = +// '2c63bf6d60b16c8afa13e1069dbe92fef337c23855fff8b27732b3e9c6e7efd4' // XXX use this PK on mainnet export const PRIVATE_KEY_NO_QUOTA = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890000000' export const ACCOUNT_ADDRESS_NO_QUOTA = privateKeyToAddress(PRIVATE_KEY_NO_QUOTA) diff --git a/packages/phone-number-privacy/combiner/test/unit/no-quota-cache.test.ts b/packages/phone-number-privacy/combiner/test/unit/no-quota-cache.test.ts new file mode 100644 index 000000000..b8c96b206 --- /dev/null +++ b/packages/phone-number-privacy/combiner/test/unit/no-quota-cache.test.ts @@ -0,0 +1,61 @@ +import { sleep } from '@celo/base' +import { NoQuotaCache } from '../../src/utils/no-quota-cache' + +describe(`NoQuotaCache`, () => { + let noQuotaCache: NoQuotaCache + beforeEach(() => { + noQuotaCache = new NoQuotaCache(1) + }) + + it('should maintain a value', async () => { + noQuotaCache.setNoMoreQuota('ADDRESS1', 10) + + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + expect(noQuotaCache.getTotalQuota('ADDRESS1')).toBe(10) + }) + + it('should return undefined if a key expired', async () => { + noQuotaCache.setNoMoreQuota('ADDRESS1', 10) + + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + await sleep(1100) + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(false) + expect(noQuotaCache.getTotalQuota('ADDRESS1')).toBe(undefined) + }) + + it('should not refresh a key if it saves the same value', async () => { + noQuotaCache.setNoMoreQuota('ADDRESS1', 10) + + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + await sleep(600) + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + noQuotaCache.setNoMoreQuota('ADDRESS1', 10) + await sleep(600) + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(false) + expect(noQuotaCache.getTotalQuota('ADDRESS1')).toBe(undefined) + }) + + it('should not refresh a key if it saves the value is smaller', async () => { + noQuotaCache.setNoMoreQuota('ADDRESS1', 10) + + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + await sleep(600) + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + noQuotaCache.setNoMoreQuota('ADDRESS1', 5) + await sleep(600) + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(false) + expect(noQuotaCache.getTotalQuota('ADDRESS1')).toBe(undefined) + }) + + it('should refresh a key if it saves the value is bigger', async () => { + noQuotaCache.setNoMoreQuota('ADDRESS1', 10) + + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + await sleep(600) + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + noQuotaCache.setNoMoreQuota('ADDRESS1', 20) + await sleep(600) + expect(noQuotaCache.maximumQuotaReached('ADDRESS1')).toBe(true) + expect(noQuotaCache.getTotalQuota('ADDRESS1')).toBe(20) + }) +}) diff --git a/yarn.lock b/yarn.lock index 6fab601ed..48e0e91c6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4217,7 +4217,7 @@ resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== -"@trufflesuite/bigint-buffer@*": +"@trufflesuite/bigint-buffer@1.1.10": version "1.1.10" resolved "https://registry.yarnpkg.com/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.10.tgz#a1d9ca22d3cad1a138b78baaf15543637a3e1692" integrity sha512-pYIQC5EcMmID74t26GCC67946mgTJFiLXOT/BYozgrd4UEY2JHEGLhWi9cMiQCt5BSqFEvKkCHNnoj82SRjiEw== @@ -4537,13 +4537,6 @@ resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.2.tgz#a86e00bbde8950364f8e7846687259ffcd96e8c2" integrity sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg== -"@types/http-proxy@^1.17.11": - version "1.17.12" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.12.tgz#86e849e9eeae0362548803c37a0a1afc616bd96b" - integrity sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw== - dependencies: - "@types/node" "*" - "@types/ioredis4@npm:@types/ioredis@^4.28.10": version "4.28.10" resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff" @@ -4968,13 +4961,6 @@ dependencies: "@types/node" "*" -"@types/stream-array@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@types/stream-array/-/stream-array-1.1.1.tgz#cc720133a6c79d00e6cf12ca268a03bd6f692474" - integrity sha512-25UQG9udcka6vWhJ0fcLf/sPwPGm2fc9bmtprf5feD/aDxjNaopv/UPFWw/296E+Mn2IdFIPT/6FmhkdD1dTTA== - dependencies: - "@types/node" "*" - "@types/superagent@*": version "4.1.19" resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-4.1.19.tgz#33f4fa460fb9e79e5e47a96731725141c667acd0" @@ -5932,7 +5918,7 @@ bip32@^3.1.0: typeforce "^1.11.5" wif "^2.0.6" -"bip39@git+https://github.com/bitcoinjs/bip39.git#d8ea080a18b40f301d4e2219a2991cd2417e83c2", "bip39@https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2": +"bip39@https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2": version "3.0.3" resolved "https://github.com/bitcoinjs/bip39#d8ea080a18b40f301d4e2219a2991cd2417e83c2" dependencies: @@ -5970,7 +5956,7 @@ blakejs@^1.1.0: resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== -"blind-threshold-bls@git+https://github.com/celo-org/blind-threshold-bls-wasm.git#e1e2f8a", "blind-threshold-bls@https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a", "blind-threshold-bls@npm:@celo/blind-threshold-bls@1.0.0-beta": +"blind-threshold-bls@https://github.com/celo-org/blind-threshold-bls-wasm#e1e2f8a", "blind-threshold-bls@npm:@celo/blind-threshold-bls@1.0.0-beta": version "1.0.0-beta" resolved "https://registry.yarnpkg.com/@celo/blind-threshold-bls/-/blind-threshold-bls-1.0.0-beta.tgz#6c46e55c3720d99929d6d34dd3770b1623a09900" integrity sha512-sk9XLvbv0M0TJKJPHPc8FkIRTfP/PiPHeyKXPBTMZBW8URL4pRix9IfcT98zT5sA7hvMDJwgw3p3tM/L6Z1iGw== @@ -6269,11 +6255,6 @@ buffer-reverse@^1.0.1: resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== -buffer-shims@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" - integrity sha512-Zy8ZXMyxIT6RMTeY7OP/bDndfj6bwCan7SS98CEndS6deHwWPpseeHlwarNcBim+etXnF9HBc1non5JgDaJU1g== - buffer-to-arraybuffer@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" @@ -8558,7 +8539,7 @@ eventemitter3@4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== -eventemitter3@^4.0.0, eventemitter3@^4.0.4: +eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -9300,7 +9281,7 @@ fn.name@1.x.x: resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.0.0, follow-redirects@^1.14.0, follow-redirects@^1.15.0: +follow-redirects@^1.14.0, follow-redirects@^1.15.0: version "1.15.3" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a" integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q== @@ -10428,15 +10409,6 @@ http-proxy-agent@^7.0.0: agent-base "^7.1.0" debug "^4.3.4" -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -12036,10 +12008,10 @@ keccak256@^1.0.0: buffer "^6.0.3" keccak "^3.0.2" -keccak@*, keccak@^3.0.0, keccak@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" - integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== +keccak@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" + integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== dependencies: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" @@ -12055,6 +12027,15 @@ keccak@^1.0.2: nan "^2.2.1" safe-buffer "^5.1.0" +keccak@^3.0.0, keccak@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + keytar@^7.3.0: version "7.9.0" resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb" @@ -12213,10 +12194,10 @@ level-transcoder@^1.0.1: buffer "^6.0.3" module-error "^1.0.1" -leveldown@*: - version "6.1.1" - resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-6.1.1.tgz#0f0e480fa88fd807abf94c33cb7e40966ea4b5ce" - integrity sha512-88c+E+Eizn4CkQOBHwqlCJaTNEjGpaEIikn1S+cINc5E9HEvJ77bqY4JY/HxT5u0caWqsc3P3DcFIKBI1vHt+A== +leveldown@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-6.1.0.tgz#7ab1297706f70c657d1a72b31b40323aa612b9ee" + integrity sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w== dependencies: abstract-leveldown "^7.2.0" napi-macros "~2.0.0" @@ -13519,11 +13500,6 @@ node-addon-api@^4.2.0, node-addon-api@^4.3.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== -node-addon-api@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" - integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA== - node-domexception@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" @@ -15472,19 +15448,6 @@ readable-stream@~2.0.0: string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@~2.1.0: - version "2.1.5" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" - integrity sha512-NkXT2AER7VKXeXtJNSaWLpWIhmtSE3K2PguaLEeWr4JILghcIKqoLt1A3wHrnpDC5+ekf8gfk1GKWkFXe4odMw== - dependencies: - buffer-shims "^1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - readdir-glob@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" @@ -15631,11 +15594,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - requizzle@^0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/requizzle/-/requizzle-0.2.4.tgz#319eb658b28c370f0c20f968fa8ceab98c13d27c" @@ -15901,16 +15859,7 @@ scrypt-js@^3.0.0, scrypt-js@^3.0.1: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -secp256k1@*: - version "5.0.0" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-5.0.0.tgz#be6f0c8c7722e2481e9773336d351de8cddd12f7" - integrity sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA== - dependencies: - elliptic "^6.5.4" - node-addon-api "^5.0.0" - node-gyp-build "^4.2.0" - -secp256k1@^4.0.0, secp256k1@^4.0.1: +secp256k1@4.0.3, secp256k1@^4.0.0, secp256k1@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== @@ -16391,13 +16340,6 @@ stoppable@^1.1.0: resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== -stream-array@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/stream-array/-/stream-array-1.1.2.tgz#9e5f7345f2137c30ee3b498b9114e80b52bb7eb5" - integrity sha512-1yWdVsMEm/btiMa2YyHiC3mDrtAqlmNNaDRylx2F7KHhm3C4tA6kSR2V9mpeMthv+ujvbl8Kamyh5xaHHdFvyQ== - dependencies: - readable-stream "~2.1.0" - stream-chain@^2.2.4, stream-chain@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/stream-chain/-/stream-chain-2.2.5.tgz#b30967e8f14ee033c5b9a19bbe8a2cba90ba0d09" @@ -18950,7 +18892,20 @@ yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.3.1, yargs@^17.6.2, yargs@^17.7.1, yargs@^17.7.2: +yargs@^17.3.1: + version "17.7.1" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yargs@^17.6.2, yargs@^17.7.1, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==